@texuli/blocks 0.0.0-stage → 0.1.0-next.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/README.md +77 -2
- package/dist/code/CodeBlockView.svelte +135 -0
- package/dist/code/CodeBlockView.svelte.d.ts +5 -0
- package/dist/code/check.d.ts +15 -0
- package/dist/code/check.js +14 -0
- package/dist/code/code.css +201 -0
- package/dist/code/code.css.d.ts +1 -0
- package/dist/code/engine-entry.d.ts +10 -0
- package/dist/code/engine-entry.js +10 -0
- package/dist/code/engine.d.ts +18 -0
- package/dist/code/engine.js +18 -0
- package/dist/code/family-engine.d.ts +23 -0
- package/dist/code/family-engine.js +4 -0
- package/dist/code/family.d.ts +7 -0
- package/dist/code/family.js +5 -0
- package/dist/code/grammars.d.ts +12 -0
- package/dist/code/grammars.js +83 -0
- package/dist/code/highlight.d.ts +15 -0
- package/dist/code/highlight.js +53 -0
- package/dist/code/index.d.ts +13 -0
- package/dist/code/index.js +12 -0
- package/dist/code/labels.d.ts +27 -0
- package/dist/code/labels.js +24 -0
- package/dist/code/languages/bash.d.ts +3 -0
- package/dist/code/languages/bash.js +48 -0
- package/dist/code/languages/css.d.ts +10 -0
- package/dist/code/languages/css.js +67 -0
- package/dist/code/languages/html.d.ts +6 -0
- package/dist/code/languages/html.js +35 -0
- package/dist/code/languages/javascript.d.ts +3 -0
- package/dist/code/languages/javascript.js +113 -0
- package/dist/code/languages/json.d.ts +3 -0
- package/dist/code/languages/json.js +24 -0
- package/dist/code/languages/python.d.ts +3 -0
- package/dist/code/languages/python.js +35 -0
- package/dist/code/languages/shared.d.ts +22 -0
- package/dist/code/languages/shared.js +37 -0
- package/dist/code/languages/sql.d.ts +3 -0
- package/dist/code/languages/sql.js +32 -0
- package/dist/code/languages/yaml.d.ts +7 -0
- package/dist/code/languages/yaml.js +36 -0
- package/dist/code/renderers.d.ts +5 -0
- package/dist/code/renderers.js +5 -0
- package/dist/code/token-styles.d.ts +25 -0
- package/dist/code/token-styles.js +51 -0
- package/dist/code/tokenize-code.d.ts +17 -0
- package/dist/code/tokenize-code.js +34 -0
- package/dist/code/tokenizer.d.ts +76 -0
- package/dist/code/tokenizer.js +171 -0
- package/dist/core/catalog/entries.d.ts +10 -0
- package/dist/core/catalog/entries.js +589 -0
- package/dist/core/catalog/index.d.ts +4 -0
- package/dist/core/catalog/index.js +2 -0
- package/dist/core/catalog/types.d.ts +36 -0
- package/dist/core/catalog/types.js +1 -0
- package/dist/core/catalog/validate.d.ts +15 -0
- package/dist/core/catalog/validate.js +46 -0
- package/dist/core/formula/evaluate.d.ts +14 -0
- package/dist/core/formula/evaluate.js +181 -0
- package/dist/core/formula/index.d.ts +8 -0
- package/dist/core/formula/index.js +6 -0
- package/dist/core/formula/parse.d.ts +19 -0
- package/dist/core/formula/parse.js +452 -0
- package/dist/core/formula/types.d.ts +76 -0
- package/dist/core/formula/types.js +7 -0
- package/dist/core/index.d.ts +14 -0
- package/dist/core/index.js +13 -0
- package/dist/core/limits.d.ts +27 -0
- package/dist/core/limits.js +26 -0
- package/dist/core/safety.d.ts +30 -0
- package/dist/core/safety.js +110 -0
- package/dist/core/schema/blocks.d.ts +138 -0
- package/dist/core/schema/blocks.js +181 -0
- package/dist/core/schema/code-family.d.ts +4 -0
- package/dist/core/schema/code-family.js +75 -0
- package/dist/core/schema/line-spec.d.ts +7 -0
- package/dist/core/schema/line-spec.js +19 -0
- package/dist/core/schema/media-family.d.ts +4 -0
- package/dist/core/schema/media-family.js +260 -0
- package/dist/core/schema/parse-family.d.ts +14 -0
- package/dist/core/schema/parse-family.js +37 -0
- package/dist/core/schema/primitives.d.ts +73 -0
- package/dist/core/schema/primitives.js +140 -0
- package/dist/core/schema/text-family.d.ts +4 -0
- package/dist/core/schema/text-family.js +121 -0
- package/dist/core/schema/vocabulary.d.ts +20 -0
- package/dist/core/schema/vocabulary.js +33 -0
- package/dist/core/text.d.ts +31 -0
- package/dist/core/text.js +139 -0
- package/dist/core/untrusted.d.ts +20 -0
- package/dist/core/untrusted.js +377 -0
- package/dist/css-modules.d.ts +1 -0
- package/dist/css.d.ts +1 -0
- package/dist/data/ChartBlockView.svelte +77 -0
- package/dist/data/ChartBlockView.svelte.d.ts +10 -0
- package/dist/data/DataBlockView.svelte +109 -0
- package/dist/data/DataBlockView.svelte.d.ts +5 -0
- package/dist/data/check.d.ts +10 -0
- package/dist/data/check.js +9 -0
- package/dist/data/engine.d.ts +18 -0
- package/dist/data/engine.js +10 -0
- package/dist/data/family.d.ts +6 -0
- package/dist/data/family.js +4 -0
- package/dist/data/index.d.ts +9 -0
- package/dist/data/index.js +8 -0
- package/dist/data/labels.d.ts +8 -0
- package/dist/data/labels.js +8 -0
- package/dist/data/renderers.d.ts +2 -0
- package/dist/data/renderers.js +2 -0
- package/dist/data/schema.d.ts +84 -0
- package/dist/data/schema.js +385 -0
- package/dist/diagram/BoardDiagram.svelte +48 -0
- package/dist/diagram/BoardDiagram.svelte.d.ts +8 -0
- package/dist/diagram/DiagramFamily.svelte +46 -0
- package/dist/diagram/DiagramFamily.svelte.d.ts +10 -0
- package/dist/diagram/FlowDiagram.svelte +108 -0
- package/dist/diagram/FlowDiagram.svelte.d.ts +8 -0
- package/dist/diagram/TerminalDiagram.svelte +38 -0
- package/dist/diagram/TerminalDiagram.svelte.d.ts +8 -0
- package/dist/diagram/TreeDiagram.svelte +149 -0
- package/dist/diagram/TreeDiagram.svelte.d.ts +8 -0
- package/dist/diagram/diagram.css +287 -0
- package/dist/diagram/diagram.css.d.ts +1 -0
- package/dist/diagram/engine.d.ts +16 -0
- package/dist/diagram/engine.js +14 -0
- package/dist/diagram/family.d.ts +7 -0
- package/dist/diagram/family.js +5 -0
- package/dist/diagram/index.d.ts +10 -0
- package/dist/diagram/index.js +9 -0
- package/dist/diagram/layout.d.ts +26 -0
- package/dist/diagram/layout.js +165 -0
- package/dist/diagram/schema.d.ts +74 -0
- package/dist/diagram/schema.js +358 -0
- package/dist/diagram/tones.d.ts +6 -0
- package/dist/diagram/tones.js +16 -0
- package/dist/embed/EmbedApp.svelte +138 -0
- package/dist/embed/EmbedApp.svelte.d.ts +17 -0
- package/dist/embed/EmbedBlockView.svelte +32 -0
- package/dist/embed/EmbedBlockView.svelte.d.ts +5 -0
- package/dist/embed/LinkPreview.svelte +48 -0
- package/dist/embed/LinkPreview.svelte.d.ts +10 -0
- package/dist/embed/engine.d.ts +30 -0
- package/dist/embed/engine.js +17 -0
- package/dist/embed/family.d.ts +7 -0
- package/dist/embed/family.js +5 -0
- package/dist/embed/index.d.ts +5 -0
- package/dist/embed/index.js +3 -0
- package/dist/embed/renderers.d.ts +3 -0
- package/dist/embed/renderers.js +3 -0
- package/dist/embed/sandbox.d.ts +86 -0
- package/dist/embed/sandbox.js +114 -0
- package/dist/embed/schema.d.ts +30 -0
- package/dist/embed/schema.js +217 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +15 -0
- package/dist/interactive/CalculatorBlock.svelte +61 -0
- package/dist/interactive/CalculatorBlock.svelte.d.ts +10 -0
- package/dist/interactive/ChecklistBlock.svelte +44 -0
- package/dist/interactive/ChecklistBlock.svelte.d.ts +10 -0
- package/dist/interactive/InteractiveBlockView.svelte +31 -0
- package/dist/interactive/InteractiveBlockView.svelte.d.ts +5 -0
- package/dist/interactive/PollBlock.svelte +111 -0
- package/dist/interactive/PollBlock.svelte.d.ts +10 -0
- package/dist/interactive/QuizBlock.svelte +75 -0
- package/dist/interactive/QuizBlock.svelte.d.ts +10 -0
- package/dist/interactive/check.d.ts +10 -0
- package/dist/interactive/check.js +9 -0
- package/dist/interactive/engine.d.ts +19 -0
- package/dist/interactive/engine.js +12 -0
- package/dist/interactive/family.d.ts +6 -0
- package/dist/interactive/family.js +4 -0
- package/dist/interactive/index.d.ts +13 -0
- package/dist/interactive/index.js +13 -0
- package/dist/interactive/interactive.css +97 -0
- package/dist/interactive/interactive.css.d.ts +1 -0
- package/dist/interactive/labels.d.ts +16 -0
- package/dist/interactive/labels.js +16 -0
- package/dist/interactive/renderers.d.ts +9 -0
- package/dist/interactive/renderers.js +9 -0
- package/dist/interactive/schema.d.ts +76 -0
- package/dist/interactive/schema.js +324 -0
- package/dist/map/MapBlockView.svelte +465 -0
- package/dist/map/MapBlockView.svelte.d.ts +5 -0
- package/dist/map/check.d.ts +11 -0
- package/dist/map/check.js +6 -0
- package/dist/map/engine.d.ts +19 -0
- package/dist/map/engine.js +8 -0
- package/dist/map/family.d.ts +7 -0
- package/dist/map/family.js +5 -0
- package/dist/map/index.d.ts +11 -0
- package/dist/map/index.js +10 -0
- package/dist/map/labels.d.ts +27 -0
- package/dist/map/labels.js +21 -0
- package/dist/map/renderers.d.ts +2 -0
- package/dist/map/renderers.js +2 -0
- package/dist/map/schema.d.ts +41 -0
- package/dist/map/schema.js +176 -0
- package/dist/map/view-model.d.ts +55 -0
- package/dist/map/view-model.js +168 -0
- package/dist/media/MediaBlockView.svelte +258 -0
- package/dist/media/MediaBlockView.svelte.d.ts +5 -0
- package/dist/media/check.d.ts +14 -0
- package/dist/media/check.js +13 -0
- package/dist/media/engine.d.ts +18 -0
- package/dist/media/engine.js +10 -0
- package/dist/media/family.d.ts +7 -0
- package/dist/media/family.js +5 -0
- package/dist/media/index.d.ts +11 -0
- package/dist/media/index.js +10 -0
- package/dist/media/labels.d.ts +7 -0
- package/dist/media/labels.js +12 -0
- package/dist/media/renderers.d.ts +5 -0
- package/dist/media/renderers.js +5 -0
- package/dist/media/resolve.d.ts +6 -0
- package/dist/media/resolve.js +15 -0
- package/dist/structure/NestedBlocks.svelte +65 -0
- package/dist/structure/NestedBlocks.svelte.d.ts +11 -0
- package/dist/structure/SafeBody.svelte +13 -0
- package/dist/structure/SafeBody.svelte.d.ts +7 -0
- package/dist/structure/StructureFamily.svelte +157 -0
- package/dist/structure/StructureFamily.svelte.d.ts +5 -0
- package/dist/structure/check.d.ts +14 -0
- package/dist/structure/check.js +27 -0
- package/dist/structure/engine.d.ts +18 -0
- package/dist/structure/engine.js +18 -0
- package/dist/structure/family.d.ts +7 -0
- package/dist/structure/family.js +5 -0
- package/dist/structure/index.d.ts +11 -0
- package/dist/structure/index.js +10 -0
- package/dist/structure/labels.d.ts +11 -0
- package/dist/structure/labels.js +11 -0
- package/dist/structure/renderers.d.ts +5 -0
- package/dist/structure/renderers.js +5 -0
- package/dist/structure/schema.d.ts +71 -0
- package/dist/structure/schema.js +327 -0
- package/dist/structure/structure.css +182 -0
- package/dist/structure/structure.css.d.ts +1 -0
- package/dist/text/RichText.svelte +169 -0
- package/dist/text/RichText.svelte.d.ts +13 -0
- package/dist/text/TextBlockView.svelte +177 -0
- package/dist/text/TextBlockView.svelte.d.ts +5 -0
- package/dist/text/check.d.ts +14 -0
- package/dist/text/check.js +13 -0
- package/dist/text/engine.d.ts +17 -0
- package/dist/text/engine.js +10 -0
- package/dist/text/family.d.ts +7 -0
- package/dist/text/family.js +5 -0
- package/dist/text/index.d.ts +12 -0
- package/dist/text/index.js +11 -0
- package/dist/text/labels.d.ts +22 -0
- package/dist/text/labels.js +29 -0
- package/dist/text/markdown.d.ts +80 -0
- package/dist/text/markdown.js +361 -0
- package/dist/text/renderers.d.ts +6 -0
- package/dist/text/renderers.js +6 -0
- package/package.json +167 -4
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cx } from 'texuli/cx';
|
|
3
|
+
import { checkMapBlock } from './check.js';
|
|
4
|
+
import type { MapBlockViewProps } from './family.js';
|
|
5
|
+
import { formatMapLabel, resolveMapBlockLabels } from './labels.js';
|
|
6
|
+
import {
|
|
7
|
+
applyMapAction,
|
|
8
|
+
basemapFeatures,
|
|
9
|
+
basemapFor,
|
|
10
|
+
clearFeatureLabels,
|
|
11
|
+
fitPoints,
|
|
12
|
+
type MapKeyAction,
|
|
13
|
+
mapKeyAction,
|
|
14
|
+
mapSizeFor,
|
|
15
|
+
markerLabelFlips,
|
|
16
|
+
placePoints,
|
|
17
|
+
routeMeters,
|
|
18
|
+
routePath,
|
|
19
|
+
routeStops,
|
|
20
|
+
toneClass,
|
|
21
|
+
} from './view-model.js';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Map block: points and an optional route on an offline, fictional basemap.
|
|
25
|
+
* Renders only what `checkMapBlock` accepts; anything else becomes an inert
|
|
26
|
+
* notice. The SVG is decorative: the labeled map region (arrow keys pan,
|
|
27
|
+
* + and − zoom), the controls and the place list carry the meaning.
|
|
28
|
+
*/
|
|
29
|
+
let { block, labels, locale, headingLevel = 3, oninvalid, onselect }: MapBlockViewProps = $props();
|
|
30
|
+
|
|
31
|
+
const check = $derived(checkMapBlock(block));
|
|
32
|
+
const text = $derived(resolveMapBlockLabels(labels));
|
|
33
|
+
const uid = $props.id();
|
|
34
|
+
|
|
35
|
+
$effect(() => {
|
|
36
|
+
if (!check.ok) oninvalid?.(check.issues);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const map = $derived(check.ok ? check.block : null);
|
|
40
|
+
const basemap = $derived(map ? basemapFor(map) : null);
|
|
41
|
+
/** Canvas width in CSS px; the SVG draws at 1:1 so map text keeps its size. */
|
|
42
|
+
let canvasWidth = $state(0);
|
|
43
|
+
const size = $derived(mapSizeFor(canvasWidth));
|
|
44
|
+
const home = $derived(map && basemap ? fitPoints(map.points, basemap, size) : null);
|
|
45
|
+
let moved = $state<typeof home>(null);
|
|
46
|
+
const view = $derived(moved ?? home);
|
|
47
|
+
let selectedId = $state<string | undefined>();
|
|
48
|
+
|
|
49
|
+
// New block data starts from its own framing.
|
|
50
|
+
$effect.pre(() => {
|
|
51
|
+
void home;
|
|
52
|
+
moved = null;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const features = $derived(basemap && view ? basemapFeatures(basemap, view, size) : []);
|
|
56
|
+
const placed = $derived(map && view ? placePoints(map.points, view, size) : []);
|
|
57
|
+
const stops = $derived(map ? routeStops(map) : []);
|
|
58
|
+
const route = $derived(view ? routePath(stops, view, size) : undefined);
|
|
59
|
+
const selected = $derived(placed.find((point) => point.id === selectedId));
|
|
60
|
+
const showLabels = $derived(placed.length <= 12);
|
|
61
|
+
/** Basemap labels that stay clear of the marker labels shown. */
|
|
62
|
+
const labeled = $derived(
|
|
63
|
+
clearFeatureLabels(
|
|
64
|
+
features,
|
|
65
|
+
placed.filter((point) => showLabels || point.id === selectedId),
|
|
66
|
+
size
|
|
67
|
+
)
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const distance = $derived.by(() => {
|
|
71
|
+
const meters = routeMeters(stops);
|
|
72
|
+
const km = meters >= 1000;
|
|
73
|
+
return new Intl.NumberFormat(locale, {
|
|
74
|
+
style: 'unit',
|
|
75
|
+
unit: km ? 'kilometer' : 'meter',
|
|
76
|
+
maximumFractionDigits: km ? 1 : 0,
|
|
77
|
+
}).format(km ? meters / 1000 : meters);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
function act(action: MapKeyAction) {
|
|
81
|
+
if (!view || !home) return;
|
|
82
|
+
moved = action === 'reset' ? null : applyMapAction(view, action, home, size);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function onkeydown(event: KeyboardEvent) {
|
|
86
|
+
if (event.altKey || event.ctrlKey || event.metaKey) return;
|
|
87
|
+
const action = mapKeyAction(event.key);
|
|
88
|
+
if (!action) return;
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
act(action);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function select(id: string) {
|
|
94
|
+
selectedId = id;
|
|
95
|
+
onselect?.(id);
|
|
96
|
+
}
|
|
97
|
+
</script>
|
|
98
|
+
|
|
99
|
+
{#if !check.ok || !map || !basemap}
|
|
100
|
+
<p class="tx-map-invalid" role="note" data-tx-block="invalid">{text.invalid}</p>
|
|
101
|
+
{:else}
|
|
102
|
+
<div class={cx('tx-map')} data-tx-block="map" role="group" aria-labelledby={`${uid}-name`}>
|
|
103
|
+
{#if map.title}
|
|
104
|
+
<svelte:element this={`h${headingLevel}`} class="tx-map-title">{map.title}</svelte:element>
|
|
105
|
+
{/if}
|
|
106
|
+
|
|
107
|
+
<div class="tx-map-controls" role="group" aria-label={text.controls}>
|
|
108
|
+
<button type="button" aria-label={text.zoomIn} onclick={() => act('zoom-in')}><span aria-hidden="true">+</span></button>
|
|
109
|
+
<button type="button" aria-label={text.zoomOut} onclick={() => act('zoom-out')}><span aria-hidden="true">−</span></button>
|
|
110
|
+
<button type="button" onclick={() => act('reset')}>{text.reset}</button>
|
|
111
|
+
</div>
|
|
112
|
+
<div class="tx-map-frame">
|
|
113
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex, a11y_no_noninteractive_element_interactions -- the map surface is an ARIA application: focusable, arrow keys pan, + and − zoom. -->
|
|
114
|
+
<div
|
|
115
|
+
class="tx-map-canvas"
|
|
116
|
+
role="application"
|
|
117
|
+
tabindex="0"
|
|
118
|
+
aria-roledescription={text.roleDescription}
|
|
119
|
+
aria-label={map.label}
|
|
120
|
+
aria-describedby={`${uid}-hint`}
|
|
121
|
+
{onkeydown}
|
|
122
|
+
bind:clientWidth={canvasWidth}
|
|
123
|
+
data-tx-map-canvas
|
|
124
|
+
>
|
|
125
|
+
<span id={`${uid}-name`} class="tx-map-visually-hidden">{map.label}</span>
|
|
126
|
+
<svg viewBox={`0 0 ${size.width} ${size.height}`} aria-hidden="true" focusable="false">
|
|
127
|
+
<rect class="water" width={size.width} height={size.height} />
|
|
128
|
+
{#each features as feature (feature.id)}
|
|
129
|
+
<path class={`feature ${feature.kind}`} d={feature.d} />
|
|
130
|
+
{/each}
|
|
131
|
+
{#each labeled as feature (feature.id)}
|
|
132
|
+
{#if feature.label}
|
|
133
|
+
<text class="feature-label" x={feature.label.x} y={feature.label.y}>{feature.label.text}</text>
|
|
134
|
+
{/if}
|
|
135
|
+
{/each}
|
|
136
|
+
{#if route}
|
|
137
|
+
<path class="route" d={route} data-tx-map-route />
|
|
138
|
+
{/if}
|
|
139
|
+
{#each placed as point (point.id)}
|
|
140
|
+
{#if point.visible}
|
|
141
|
+
<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -- pointer shortcut; the place list is the keyboard path. -->
|
|
142
|
+
<g class={cx('marker', toneClass(point.tone), point.id === selectedId && 'selected')} onclick={() => select(point.id)} data-tx-map-marker={point.id}>
|
|
143
|
+
<circle cx={point.x} cy={point.y} r={point.id === selectedId ? 9 : 7} />
|
|
144
|
+
{#if showLabels || point.id === selectedId}
|
|
145
|
+
{@const flip = markerLabelFlips(point, size)}
|
|
146
|
+
<text
|
|
147
|
+
class="marker-label"
|
|
148
|
+
x={flip ? point.x - 12 : point.x + 12}
|
|
149
|
+
y={point.y + 4}
|
|
150
|
+
text-anchor={flip ? 'end' : 'start'}
|
|
151
|
+
>{point.label}</text>
|
|
152
|
+
{/if}
|
|
153
|
+
</g>
|
|
154
|
+
{/if}
|
|
155
|
+
{/each}
|
|
156
|
+
</svg>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
<p id={`${uid}-hint`} class="tx-map-hint">{text.keyboardHint}</p>
|
|
160
|
+
<p class="tx-map-attribution">{basemap.attribution}</p>
|
|
161
|
+
|
|
162
|
+
<div class="tx-map-panel">
|
|
163
|
+
<div>
|
|
164
|
+
<p class="tx-map-panel-title" id={`${uid}-places`}>{text.places}</p>
|
|
165
|
+
<ul
|
|
166
|
+
class="tx-map-places"
|
|
167
|
+
role="list"
|
|
168
|
+
aria-labelledby={`${uid}-places`}
|
|
169
|
+
data-tx-map-place-list=""
|
|
170
|
+
>
|
|
171
|
+
{#each placed as point, index (point.id)}
|
|
172
|
+
<li
|
|
173
|
+
role="listitem"
|
|
174
|
+
aria-setsize={placed.length}
|
|
175
|
+
aria-posinset={index + 1}
|
|
176
|
+
aria-current={point.id === selectedId ? 'true' : undefined}
|
|
177
|
+
>
|
|
178
|
+
<button
|
|
179
|
+
type="button"
|
|
180
|
+
aria-pressed={point.id === selectedId}
|
|
181
|
+
onclick={() => select(point.id)}
|
|
182
|
+
data-tx-map-place={point.id}
|
|
183
|
+
>
|
|
184
|
+
<span class={cx('swatch', toneClass(point.tone))} aria-hidden="true"></span>
|
|
185
|
+
<span class="place-copy">
|
|
186
|
+
<span class="place-label">{point.label}</span>
|
|
187
|
+
{#if point.description}<span class="place-detail">{point.description}</span>{/if}
|
|
188
|
+
{#if !point.visible}<span class="place-detail">({text.offMap})</span>{/if}
|
|
189
|
+
</span>
|
|
190
|
+
</button>
|
|
191
|
+
</li>
|
|
192
|
+
{/each}
|
|
193
|
+
</ul>
|
|
194
|
+
</div>
|
|
195
|
+
{#if stops.length >= 2}
|
|
196
|
+
<div>
|
|
197
|
+
<p class="tx-map-panel-title" id={`${uid}-route`}>{text.route}</p>
|
|
198
|
+
<p class="tx-map-route-summary" data-tx-map-route-summary>
|
|
199
|
+
{formatMapLabel(text.routeSummary, { count: stops.length, distance })}
|
|
200
|
+
</p>
|
|
201
|
+
<ol class="tx-map-stops" aria-labelledby={`${uid}-route`}>
|
|
202
|
+
{#each stops as stop, index (`${stop.id}-${index}`)}
|
|
203
|
+
<li>{stop.label}</li>
|
|
204
|
+
{/each}
|
|
205
|
+
</ol>
|
|
206
|
+
</div>
|
|
207
|
+
{/if}
|
|
208
|
+
</div>
|
|
209
|
+
<p class="tx-map-selection" aria-live="polite">
|
|
210
|
+
{#if selected}{formatMapLabel(text.selected, { label: selected.label })}{#if selected.description}. {selected.description}{/if}{/if}
|
|
211
|
+
</p>
|
|
212
|
+
</div>
|
|
213
|
+
{/if}
|
|
214
|
+
|
|
215
|
+
<style>
|
|
216
|
+
@layer tx.components {
|
|
217
|
+
.tx-map {
|
|
218
|
+
--map-water: color-mix(in srgb, var(--tx-primary, #2563eb) 16%, var(--tx-background, Canvas));
|
|
219
|
+
--map-land: color-mix(in srgb, var(--tx-muted, #e5e7eb) 70%, var(--tx-background, Canvas));
|
|
220
|
+
display: grid;
|
|
221
|
+
gap: 0.75rem;
|
|
222
|
+
color: var(--tx-foreground, CanvasText);
|
|
223
|
+
}
|
|
224
|
+
.tx-map-title {
|
|
225
|
+
margin: 0;
|
|
226
|
+
font-size: 1.125rem;
|
|
227
|
+
line-height: 1.3;
|
|
228
|
+
}
|
|
229
|
+
.tx-map-frame {
|
|
230
|
+
position: relative;
|
|
231
|
+
overflow: hidden;
|
|
232
|
+
border: 1px solid var(--tx-border, currentColor);
|
|
233
|
+
border-radius: var(--tx-radius, 0.5rem);
|
|
234
|
+
background: var(--map-water);
|
|
235
|
+
}
|
|
236
|
+
.tx-map-canvas {
|
|
237
|
+
display: block;
|
|
238
|
+
}
|
|
239
|
+
.tx-map-canvas:focus-visible {
|
|
240
|
+
outline: var(--tx-focus-ring-width, 2px) solid var(--tx-ring, Highlight);
|
|
241
|
+
outline-offset: calc(-1 * var(--tx-focus-ring-width, 2px));
|
|
242
|
+
}
|
|
243
|
+
svg {
|
|
244
|
+
display: block;
|
|
245
|
+
inline-size: 100%;
|
|
246
|
+
block-size: auto;
|
|
247
|
+
}
|
|
248
|
+
.water {
|
|
249
|
+
fill: var(--map-water);
|
|
250
|
+
}
|
|
251
|
+
.feature {
|
|
252
|
+
vector-effect: non-scaling-stroke;
|
|
253
|
+
}
|
|
254
|
+
.feature.land {
|
|
255
|
+
fill: var(--map-land);
|
|
256
|
+
stroke: color-mix(in srgb, var(--tx-foreground, CanvasText) 20%, transparent);
|
|
257
|
+
}
|
|
258
|
+
.feature.water {
|
|
259
|
+
fill: var(--map-water);
|
|
260
|
+
}
|
|
261
|
+
.feature.park {
|
|
262
|
+
fill: color-mix(in srgb, var(--tx-success, #16a34a) 30%, var(--map-land));
|
|
263
|
+
}
|
|
264
|
+
.feature.district {
|
|
265
|
+
fill: color-mix(in srgb, var(--tx-background, Canvas) 55%, transparent);
|
|
266
|
+
stroke: color-mix(in srgb, var(--tx-foreground, CanvasText) 15%, transparent);
|
|
267
|
+
}
|
|
268
|
+
.feature.road {
|
|
269
|
+
fill: none;
|
|
270
|
+
stroke: color-mix(in srgb, var(--tx-foreground, CanvasText) 35%, transparent);
|
|
271
|
+
stroke-width: 2;
|
|
272
|
+
stroke-linecap: round;
|
|
273
|
+
}
|
|
274
|
+
.feature.rail {
|
|
275
|
+
fill: none;
|
|
276
|
+
stroke: color-mix(in srgb, var(--tx-foreground, CanvasText) 45%, transparent);
|
|
277
|
+
stroke-width: 1.2;
|
|
278
|
+
stroke-dasharray: 4 3;
|
|
279
|
+
}
|
|
280
|
+
.feature-label,
|
|
281
|
+
.marker-label {
|
|
282
|
+
fill: var(--tx-foreground, CanvasText);
|
|
283
|
+
paint-order: stroke;
|
|
284
|
+
stroke: var(--tx-background, Canvas);
|
|
285
|
+
stroke-width: 3px;
|
|
286
|
+
stroke-linejoin: round;
|
|
287
|
+
pointer-events: none;
|
|
288
|
+
}
|
|
289
|
+
.feature-label {
|
|
290
|
+
font-size: 11px;
|
|
291
|
+
}
|
|
292
|
+
.marker-label {
|
|
293
|
+
font-size: 13px;
|
|
294
|
+
font-weight: 600;
|
|
295
|
+
}
|
|
296
|
+
.route {
|
|
297
|
+
fill: none;
|
|
298
|
+
stroke: var(--tx-primary, #2563eb);
|
|
299
|
+
stroke-width: 3;
|
|
300
|
+
stroke-dasharray: 8 5;
|
|
301
|
+
stroke-linecap: round;
|
|
302
|
+
stroke-linejoin: round;
|
|
303
|
+
}
|
|
304
|
+
.marker {
|
|
305
|
+
cursor: pointer;
|
|
306
|
+
}
|
|
307
|
+
.marker circle {
|
|
308
|
+
fill: var(--marker, var(--tx-primary, #2563eb));
|
|
309
|
+
stroke: var(--tx-background, Canvas);
|
|
310
|
+
stroke-width: 2.5;
|
|
311
|
+
}
|
|
312
|
+
.marker.selected circle {
|
|
313
|
+
stroke: var(--tx-foreground, CanvasText);
|
|
314
|
+
stroke-width: 3;
|
|
315
|
+
}
|
|
316
|
+
.tone-accent {
|
|
317
|
+
--marker: var(--tx-primary, #2563eb);
|
|
318
|
+
}
|
|
319
|
+
.tone-neutral {
|
|
320
|
+
--marker: var(--tx-muted-foreground, #4b5563);
|
|
321
|
+
}
|
|
322
|
+
.tone-info {
|
|
323
|
+
--marker: var(--tx-info, #0284c7);
|
|
324
|
+
}
|
|
325
|
+
.tone-success {
|
|
326
|
+
--marker: var(--tx-success, #16a34a);
|
|
327
|
+
}
|
|
328
|
+
.tone-warning {
|
|
329
|
+
--marker: var(--tx-warning, #d97706);
|
|
330
|
+
}
|
|
331
|
+
.tone-danger {
|
|
332
|
+
--marker: var(--tx-destructive, #dc2626);
|
|
333
|
+
}
|
|
334
|
+
.tx-map-controls {
|
|
335
|
+
display: flex;
|
|
336
|
+
gap: 0.25rem;
|
|
337
|
+
justify-content: flex-end;
|
|
338
|
+
}
|
|
339
|
+
button {
|
|
340
|
+
min-block-size: 2rem;
|
|
341
|
+
min-inline-size: 2rem;
|
|
342
|
+
padding-block: 0.25rem;
|
|
343
|
+
padding-inline: 0.5rem;
|
|
344
|
+
border: 1px solid var(--tx-border-strong, currentColor);
|
|
345
|
+
border-radius: var(--tx-radius, 0.375rem);
|
|
346
|
+
background: var(--tx-background, Canvas);
|
|
347
|
+
color: var(--tx-foreground, CanvasText);
|
|
348
|
+
font: inherit;
|
|
349
|
+
font-size: 0.875rem;
|
|
350
|
+
cursor: pointer;
|
|
351
|
+
}
|
|
352
|
+
button:focus-visible {
|
|
353
|
+
outline: var(--tx-focus-ring-width, 2px) solid var(--tx-ring, Highlight);
|
|
354
|
+
outline-offset: var(--tx-focus-ring-offset, 2px);
|
|
355
|
+
}
|
|
356
|
+
.tx-map-hint,
|
|
357
|
+
.tx-map-attribution,
|
|
358
|
+
.tx-map-selection {
|
|
359
|
+
margin: 0;
|
|
360
|
+
font-size: 0.8125rem;
|
|
361
|
+
}
|
|
362
|
+
.tx-map-hint,
|
|
363
|
+
.tx-map-attribution {
|
|
364
|
+
color: var(--tx-muted-foreground, CanvasText);
|
|
365
|
+
}
|
|
366
|
+
.tx-map-selection:empty {
|
|
367
|
+
display: none;
|
|
368
|
+
}
|
|
369
|
+
.tx-map-panel {
|
|
370
|
+
display: grid;
|
|
371
|
+
gap: 1rem;
|
|
372
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
|
|
373
|
+
}
|
|
374
|
+
.tx-map-panel-title {
|
|
375
|
+
margin: 0 0 0.375rem;
|
|
376
|
+
font-weight: 600;
|
|
377
|
+
}
|
|
378
|
+
.tx-map-places,
|
|
379
|
+
.tx-map-stops {
|
|
380
|
+
margin: 0;
|
|
381
|
+
padding-inline-start: 0;
|
|
382
|
+
}
|
|
383
|
+
.tx-map-places {
|
|
384
|
+
display: grid;
|
|
385
|
+
gap: 0.25rem;
|
|
386
|
+
list-style: none;
|
|
387
|
+
}
|
|
388
|
+
.tx-map-places button {
|
|
389
|
+
display: flex;
|
|
390
|
+
gap: 0.5rem;
|
|
391
|
+
align-items: flex-start;
|
|
392
|
+
inline-size: 100%;
|
|
393
|
+
border-color: transparent;
|
|
394
|
+
text-align: start;
|
|
395
|
+
}
|
|
396
|
+
.tx-map-places button[aria-pressed='true'] {
|
|
397
|
+
border-color: var(--tx-border-strong, currentColor);
|
|
398
|
+
background: var(--tx-muted, Canvas);
|
|
399
|
+
}
|
|
400
|
+
.tx-map-stops {
|
|
401
|
+
padding-inline-start: 1.5rem;
|
|
402
|
+
}
|
|
403
|
+
.swatch {
|
|
404
|
+
flex: none;
|
|
405
|
+
inline-size: 0.75rem;
|
|
406
|
+
block-size: 0.75rem;
|
|
407
|
+
margin-block-start: 0.25rem;
|
|
408
|
+
border-radius: 50%;
|
|
409
|
+
background: var(--marker, var(--tx-primary, #2563eb));
|
|
410
|
+
}
|
|
411
|
+
.place-copy {
|
|
412
|
+
display: grid;
|
|
413
|
+
}
|
|
414
|
+
.place-label {
|
|
415
|
+
font-weight: 600;
|
|
416
|
+
}
|
|
417
|
+
.place-detail {
|
|
418
|
+
color: var(--tx-muted-foreground, CanvasText);
|
|
419
|
+
font-size: 0.8125rem;
|
|
420
|
+
}
|
|
421
|
+
.tx-map-route-summary {
|
|
422
|
+
margin: 0 0 0.25rem;
|
|
423
|
+
}
|
|
424
|
+
.tx-map-visually-hidden {
|
|
425
|
+
position: absolute;
|
|
426
|
+
inline-size: 1px;
|
|
427
|
+
block-size: 1px;
|
|
428
|
+
overflow: hidden;
|
|
429
|
+
clip-path: inset(50%);
|
|
430
|
+
white-space: nowrap;
|
|
431
|
+
}
|
|
432
|
+
.tx-map-invalid {
|
|
433
|
+
padding: 0.5rem 0.75rem;
|
|
434
|
+
border: 1px dashed currentColor;
|
|
435
|
+
border-radius: 0.375rem;
|
|
436
|
+
font-size: 0.875rem;
|
|
437
|
+
}
|
|
438
|
+
@media (forced-colors: active) {
|
|
439
|
+
.feature.land,
|
|
440
|
+
.feature.park,
|
|
441
|
+
.feature.district,
|
|
442
|
+
.water,
|
|
443
|
+
.feature.water {
|
|
444
|
+
fill: Canvas;
|
|
445
|
+
stroke: CanvasText;
|
|
446
|
+
}
|
|
447
|
+
.feature.road,
|
|
448
|
+
.feature.rail,
|
|
449
|
+
.route {
|
|
450
|
+
stroke: CanvasText;
|
|
451
|
+
}
|
|
452
|
+
.marker circle,
|
|
453
|
+
.swatch {
|
|
454
|
+
fill: Highlight;
|
|
455
|
+
background: Highlight;
|
|
456
|
+
stroke: Canvas;
|
|
457
|
+
}
|
|
458
|
+
.feature-label,
|
|
459
|
+
.marker-label {
|
|
460
|
+
fill: CanvasText;
|
|
461
|
+
stroke: Canvas;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FieldIssue } from '../core/schema/primitives.js';
|
|
2
|
+
import { type MapBlock } from './schema.js';
|
|
3
|
+
export type MapBlockCheck = {
|
|
4
|
+
ok: true;
|
|
5
|
+
block: MapBlock;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
issues: FieldIssue[];
|
|
9
|
+
};
|
|
10
|
+
/** The only way data reaches the map renderer. */
|
|
11
|
+
export declare function checkMapBlock(value: unknown): MapBlockCheck;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { parseMapBlock } from "./schema.js";
|
|
2
|
+
/** The only way data reaches the map renderer. */
|
|
3
|
+
export function checkMapBlock(value) {
|
|
4
|
+
const parsed = parseMapBlock(value);
|
|
5
|
+
return parsed.success ? { ok: true, block: parsed.data } : { ok: false, issues: parsed.issues };
|
|
6
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@texuli/blocks/map/engine` — Svelte-free map parsers, limits, labels and view-model.
|
|
3
|
+
*/
|
|
4
|
+
import type { FieldIssue } from '../core/schema/primitives.js';
|
|
5
|
+
import type { MapBlockLabelOverrides } from './labels.js';
|
|
6
|
+
export { checkMapBlock, type MapBlockCheck } from './check.js';
|
|
7
|
+
export { DEFAULT_MAP_BLOCK_LABELS, formatMapLabel, type MapBlockLabelOverrides, type MapBlockLabels, resolveMapBlockLabels, } from './labels.js';
|
|
8
|
+
export { isMapBlock, MAP_BASEMAPS, MAP_LIMITS, type MapBasemapId, type MapBlock, type MapPoint, parseMapBlock, } from './schema.js';
|
|
9
|
+
export * from './view-model.js';
|
|
10
|
+
export declare const MAP_BLOCK_TYPES: readonly ["map"];
|
|
11
|
+
export type MapFamilyBlockType = (typeof MAP_BLOCK_TYPES)[number];
|
|
12
|
+
export interface MapBlockViewProps {
|
|
13
|
+
block: unknown;
|
|
14
|
+
labels?: MapBlockLabelOverrides;
|
|
15
|
+
locale?: string;
|
|
16
|
+
headingLevel?: 2 | 3 | 4 | 5 | 6;
|
|
17
|
+
oninvalid?: (issues: FieldIssue[]) => void;
|
|
18
|
+
onselect?: (pointId: string) => void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@texuli/blocks/map/engine` — Svelte-free map parsers, limits, labels and view-model.
|
|
3
|
+
*/
|
|
4
|
+
export { checkMapBlock } from "./check.js";
|
|
5
|
+
export { DEFAULT_MAP_BLOCK_LABELS, formatMapLabel, resolveMapBlockLabels, } from "./labels.js";
|
|
6
|
+
export { isMapBlock, MAP_BASEMAPS, MAP_LIMITS, parseMapBlock, } from "./schema.js";
|
|
7
|
+
export * from "./view-model.js";
|
|
8
|
+
export const MAP_BLOCK_TYPES = ['map'];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Component } from 'svelte';
|
|
2
|
+
import type { MapBlockViewProps } from './engine.js';
|
|
3
|
+
export { MAP_BLOCK_TYPES, type MapBlockViewProps, type MapFamilyBlockType, } from './engine.js';
|
|
4
|
+
/** Lazily loads the map block renderer (its own chunk). */
|
|
5
|
+
export declare function loadMapFamily(): Promise<{
|
|
6
|
+
default: Component<MapBlockViewProps>;
|
|
7
|
+
}>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/// <reference path="../css-modules.d.ts" />
|
|
2
|
+
/**
|
|
3
|
+
* `@texuli/blocks` map family (T-blocks-map): points and an optional route on
|
|
4
|
+
* an offline, fictional basemap. No Svelte or geo engine is imported eagerly:
|
|
5
|
+
* the renderer loads through `loadMapFamily()` (its own chunk) or from
|
|
6
|
+
* `@texuli/blocks/map/renderers`.
|
|
7
|
+
*/
|
|
8
|
+
export { checkMapBlock, type MapBlockCheck } from './check.js';
|
|
9
|
+
export { loadMapFamily, MAP_BLOCK_TYPES, type MapBlockViewProps, type MapFamilyBlockType, } from './family.js';
|
|
10
|
+
export { DEFAULT_MAP_BLOCK_LABELS, formatMapLabel, type MapBlockLabelOverrides, type MapBlockLabels, resolveMapBlockLabels, } from './labels.js';
|
|
11
|
+
export { isMapBlock, MAP_BASEMAPS, MAP_LIMITS, type MapBasemapId, type MapBlock, type MapPoint, parseMapBlock, } from './schema.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@texuli/blocks` map family (T-blocks-map): points and an optional route on
|
|
3
|
+
* an offline, fictional basemap. No Svelte or geo engine is imported eagerly:
|
|
4
|
+
* the renderer loads through `loadMapFamily()` (its own chunk) or from
|
|
5
|
+
* `@texuli/blocks/map/renderers`.
|
|
6
|
+
*/
|
|
7
|
+
export { checkMapBlock } from "./check.js";
|
|
8
|
+
export { loadMapFamily, MAP_BLOCK_TYPES, } from "./family.js";
|
|
9
|
+
export { DEFAULT_MAP_BLOCK_LABELS, formatMapLabel, resolveMapBlockLabels, } from "./labels.js";
|
|
10
|
+
export { isMapBlock, MAP_BASEMAPS, MAP_LIMITS, parseMapBlock, } from "./schema.js";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every string the map block renders that does not come from the block.
|
|
3
|
+
* Hosts pass translations; English is the default. `{count}`, `{distance}`
|
|
4
|
+
* and `{label}` are replaced at render time.
|
|
5
|
+
*/
|
|
6
|
+
export interface MapBlockLabels {
|
|
7
|
+
invalid: string;
|
|
8
|
+
/** `aria-roledescription` of the interactive map surface. */
|
|
9
|
+
roleDescription: string;
|
|
10
|
+
zoomIn: string;
|
|
11
|
+
zoomOut: string;
|
|
12
|
+
reset: string;
|
|
13
|
+
controls: string;
|
|
14
|
+
/** Visible hint under the map. */
|
|
15
|
+
keyboardHint: string;
|
|
16
|
+
places: string;
|
|
17
|
+
route: string;
|
|
18
|
+
/** Route summary, e.g. "4 stops · 3.2 km". */
|
|
19
|
+
routeSummary: string;
|
|
20
|
+
selected: string;
|
|
21
|
+
offMap: string;
|
|
22
|
+
}
|
|
23
|
+
export type MapBlockLabelOverrides = Partial<MapBlockLabels>;
|
|
24
|
+
export declare const DEFAULT_MAP_BLOCK_LABELS: Readonly<MapBlockLabels>;
|
|
25
|
+
export declare function resolveMapBlockLabels(overrides?: MapBlockLabelOverrides): MapBlockLabels;
|
|
26
|
+
/** Replaces `{name}` placeholders; unknown names stay as written. */
|
|
27
|
+
export declare function formatMapLabel(template: string, values: Record<string, string | number>): string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const DEFAULT_MAP_BLOCK_LABELS = Object.freeze({
|
|
2
|
+
invalid: 'This block could not be shown.',
|
|
3
|
+
roleDescription: 'map',
|
|
4
|
+
zoomIn: 'Zoom in',
|
|
5
|
+
zoomOut: 'Zoom out',
|
|
6
|
+
reset: 'Reset view',
|
|
7
|
+
controls: 'Map controls',
|
|
8
|
+
keyboardHint: 'Focus the map, then use the arrow keys to pan and + or − to zoom.',
|
|
9
|
+
places: 'Places',
|
|
10
|
+
route: 'Route',
|
|
11
|
+
routeSummary: '{count} stops · {distance}',
|
|
12
|
+
selected: 'Selected: {label}',
|
|
13
|
+
offMap: 'outside the current view',
|
|
14
|
+
});
|
|
15
|
+
export function resolveMapBlockLabels(overrides = {}) {
|
|
16
|
+
return { ...DEFAULT_MAP_BLOCK_LABELS, ...overrides };
|
|
17
|
+
}
|
|
18
|
+
/** Replaces `{name}` placeholders; unknown names stay as written. */
|
|
19
|
+
export function formatMapLabel(template, values) {
|
|
20
|
+
return template.replace(/\{(\w+)\}/g, (match, name) => name in values ? String(values[name]) : match);
|
|
21
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type BlockBase, type ParseResult } from '../core/schema/primitives.js';
|
|
2
|
+
import { type BlockTone } from '../core/schema/vocabulary.js';
|
|
3
|
+
/**
|
|
4
|
+
* Map block (T-blocks-map): points on an offline, fictional basemap. A map
|
|
5
|
+
* block never loads tiles, geocodes or touches the network; coordinates are
|
|
6
|
+
* plain numbers on a simulated map. Owned validator: strict keys, finite
|
|
7
|
+
* numbers, unique ids, bounded sizes.
|
|
8
|
+
*/
|
|
9
|
+
/** Fictional basemaps that ship with `@texuli/data/geo`. */
|
|
10
|
+
export declare const MAP_BASEMAPS: readonly ["port-meridian"];
|
|
11
|
+
export type MapBasemapId = (typeof MAP_BASEMAPS)[number];
|
|
12
|
+
export declare const MAP_LIMITS: Readonly<{
|
|
13
|
+
points: 50;
|
|
14
|
+
route: 20;
|
|
15
|
+
label: 120;
|
|
16
|
+
pointLabel: 80;
|
|
17
|
+
description: 200;
|
|
18
|
+
/** Web Mercator stops being finite at the poles. */
|
|
19
|
+
maxLatitude: 85.06;
|
|
20
|
+
}>;
|
|
21
|
+
export type MapPoint = {
|
|
22
|
+
id: string;
|
|
23
|
+
label: string;
|
|
24
|
+
lng: number;
|
|
25
|
+
lat: number;
|
|
26
|
+
description?: string;
|
|
27
|
+
tone?: BlockTone;
|
|
28
|
+
};
|
|
29
|
+
export type MapBlock = BlockBase & {
|
|
30
|
+
type: 'map';
|
|
31
|
+
/** Accessible name of the map (required). */
|
|
32
|
+
label: string;
|
|
33
|
+
title?: string;
|
|
34
|
+
basemap?: MapBasemapId;
|
|
35
|
+
points: MapPoint[];
|
|
36
|
+
/** Point ids joined by a route line, in order. */
|
|
37
|
+
route?: string[];
|
|
38
|
+
};
|
|
39
|
+
/** Validates untrusted data as a map block. */
|
|
40
|
+
export declare function parseMapBlock(value: unknown, path?: (string | number)[]): ParseResult<MapBlock>;
|
|
41
|
+
export declare function isMapBlock(value: unknown): value is MapBlock;
|