@nika-js/onlymap 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/.vscode/onlymap.code-snippets +173 -0
- package/LICENSE.md +133 -0
- package/README.md +188 -0
- package/bin/onlymapjs.mjs +182 -0
- package/dist/Arrow.dom-7AXne1TU.js +5233 -0
- package/dist/actions.d.ts +30 -0
- package/dist/attribute-resolution.d.ts +73 -0
- package/dist/basemap-D95W0IIA.js +25203 -0
- package/dist/basemap.d.ts +72 -0
- package/dist/convert-arrow-schema-CvZ3cT5m.js +94 -0
- package/dist/csp-sandbox.d.ts +20 -0
- package/dist/ctx.d.ts +49 -0
- package/dist/d3-bundle.d.ts +218 -0
- package/dist/data-emit.d.ts +10 -0
- package/dist/data-layer.d.ts +150 -0
- package/dist/declarative-filter.d.ts +12 -0
- package/dist/dev-error-panel.d.ts +15 -0
- package/dist/draw-controller.d.ts +51 -0
- package/dist/draw.d.ts +102 -0
- package/dist/effects.d.ts +39 -0
- package/dist/elements/om-behavior.d.ts +13 -0
- package/dist/elements/om-layer.d.ts +11 -0
- package/dist/elements/om-map.d.ts +168 -0
- package/dist/elements/om-overlay.d.ts +82 -0
- package/dist/elements/om-step.d.ts +12 -0
- package/dist/elements/om-story.d.ts +67 -0
- package/dist/elements/om-widget.d.ts +29 -0
- package/dist/env.d.ts +41 -0
- package/dist/expr/cache.d.ts +28 -0
- package/dist/expr/compile.d.ts +27 -0
- package/dist/expr/errors.d.ts +10 -0
- package/dist/expr/full-js-block.d.ts +17 -0
- package/dist/expr/index.d.ts +12 -0
- package/dist/expr/output-type.d.ts +12 -0
- package/dist/expr/scale.d.ts +13 -0
- package/dist/expr/script-block.d.ts +16 -0
- package/dist/expr/whitelist.d.ts +28 -0
- package/dist/field-access.d.ts +24 -0
- package/dist/html-data.d.ts +25 -0
- package/dist/index-2v2NXF_n.js +435 -0
- package/dist/index-BFjXVHly.js +605 -0
- package/dist/index-C9w78NS9.js +4883 -0
- package/dist/index-CU-iOTdr.js +75292 -0
- package/dist/index-D74olQ9w.js +3907 -0
- package/dist/index-Do7hmHwi.js +1457 -0
- package/dist/index.d.ts +76 -0
- package/dist/ir-snapshot.d.ts +54 -0
- package/dist/ir.d.ts +66 -0
- package/dist/layer-registry.d.ts +27 -0
- package/dist/layers/popup-layer.d.ts +65 -0
- package/dist/onlymapjs.css +1 -0
- package/dist/onlymapjs.js +30 -0
- package/dist/onlymapjs.umd.cjs +8618 -0
- package/dist/parse-manifest.d.ts +19 -0
- package/dist/recordbatch-HRu0SMKp.js +4742 -0
- package/dist/runtime-core.d.ts +170 -0
- package/dist/selection.d.ts +31 -0
- package/dist/stats.d.ts +29 -0
- package/dist/table-accessors-DBjWgN0C.js +204 -0
- package/dist/template-interpolation.d.ts +6 -0
- package/dist/testing.d.ts +90 -0
- package/dist/timeline.d.ts +37 -0
- package/dist/validation.d.ts +22 -0
- package/dist/vega-loader.d.ts +22 -0
- package/dist/viewport-filter.d.ts +25 -0
- package/dist/widget-registry.d.ts +23 -0
- package/dist/widget-script.d.ts +1 -0
- package/dist/widgets/built-in.d.ts +1 -0
- package/dist/widgets/dom-utils.d.ts +4 -0
- package/llms.txt +43 -0
- package/onlymapjs.html-data.json +1688 -0
- package/package.json +111 -0
- package/skills/onlymapjs/SKILL.md +61 -0
- package/skills/onlymapjs/agents/openai.yaml +4 -0
- package/skills/onlymapjs/references/patterns.md +213 -0
- package/skills/onlymapjs/references/syntax.md +342 -0
- package/skills/onlymapjs/references/testing.md +126 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev-mode error panel (spec: "Dev-mode error panel (HU4)") — the human-
|
|
3
|
+
* facing counterpart to the agent feedback loop; a renderer of live
|
|
4
|
+
* validation output, no new validation logic. Own shadow DOM (style
|
|
5
|
+
* isolation, like widgets), fixed corner, high z-index, non-blocking (never
|
|
6
|
+
* intercepts map interaction outside its own small footprint), folds to a
|
|
7
|
+
* count badge, expands to the full list, click-to-locate flashes the
|
|
8
|
+
* offending element.
|
|
9
|
+
*/
|
|
10
|
+
import type { ValidationResult } from "./validation";
|
|
11
|
+
export interface ErrorPanelHandle {
|
|
12
|
+
update(result: ValidationResult): void;
|
|
13
|
+
destroy(): void;
|
|
14
|
+
}
|
|
15
|
+
export declare function createErrorPanel(mapEl: HTMLElement): ErrorPanelHandle;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sketch-capture controller (spec: "Manual Drawing", phases D3/D4). The
|
|
3
|
+
* stateful island that bridges the pure DrawSession (src/draw.ts) into the
|
|
4
|
+
* runtime: it consumes map click/hover coordinates (routed from the core's
|
|
5
|
+
* onMapPoint), drives the in-progress preview via the per-frame channel,
|
|
6
|
+
* pushes committed features into the live `draw:<name>` store, toggles
|
|
7
|
+
* double-click-zoom while a tool is active, and saves to a local file.
|
|
8
|
+
*
|
|
9
|
+
* Owned per <om-map> (the tooltip-overlay WeakMap precedent) and driven by
|
|
10
|
+
* `draw-*` actions (src/actions.ts) plus keyboard — so the widget stays pure
|
|
11
|
+
* UI and no "widget injects a layer" capability is needed.
|
|
12
|
+
*/
|
|
13
|
+
import { type DrawMode } from "./draw";
|
|
14
|
+
import type { RuntimeCore } from "./runtime-core";
|
|
15
|
+
export type SaveMode = "download" | "file-system" | "both";
|
|
16
|
+
/** `sketch` → `draw:sketch` (idempotent if already prefixed). */
|
|
17
|
+
export declare function drawUrl(target: string): string;
|
|
18
|
+
export declare class DrawController {
|
|
19
|
+
private readonly mapEl;
|
|
20
|
+
private readonly core;
|
|
21
|
+
private readonly sessions;
|
|
22
|
+
private readonly autosaveKeys;
|
|
23
|
+
private active;
|
|
24
|
+
constructor(mapEl: Element, core: RuntimeCore);
|
|
25
|
+
/** A draw tool is active — om-map routes points here and suppresses selection. */
|
|
26
|
+
isCapturing(): boolean;
|
|
27
|
+
/** Enable localStorage autosave for a target and restore any saved sketch (draw-config action). */
|
|
28
|
+
configure(target: string, opts: {
|
|
29
|
+
autosave?: string;
|
|
30
|
+
}): void;
|
|
31
|
+
setMode(target: string, mode: DrawMode | null): void;
|
|
32
|
+
private deactivate;
|
|
33
|
+
point(coord: [number, number] | null, kind: "click" | "hover"): void;
|
|
34
|
+
/** Close the in-progress line/polygon — from the double-click DOM event or Enter. */
|
|
35
|
+
commit(): void;
|
|
36
|
+
cancel(): void;
|
|
37
|
+
/** Enter closes the shape, Escape cancels it. */
|
|
38
|
+
handleKey(key: string): void;
|
|
39
|
+
/** Remove the most recently drawn shape (v1 "delete last" — no per-feature selection yet). */
|
|
40
|
+
deleteLast(target: string): void;
|
|
41
|
+
clear(target: string): void;
|
|
42
|
+
save(target: string, mode?: SaveMode): Promise<void>;
|
|
43
|
+
private sessionFor;
|
|
44
|
+
private renderPreview;
|
|
45
|
+
private ensurePreviewLayer;
|
|
46
|
+
private removePreviewLayer;
|
|
47
|
+
}
|
|
48
|
+
/** Get-or-create the controller for a map element. */
|
|
49
|
+
export declare function getDrawController(mapEl: Element, core: RuntimeCore): DrawController;
|
|
50
|
+
/** Peek without creating — om-map's hot onMapPoint/selection paths use this. */
|
|
51
|
+
export declare function peekDrawController(mapEl: Element): DrawController | undefined;
|
package/dist/draw.d.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manual drawing — sketch-capture state machine (spec: "Manual Drawing —
|
|
3
|
+
* Sketch Capture", phase D1). PURE and render-free: it turns a sequence of
|
|
4
|
+
* map clicks + mode transitions into RFC 7946 GeoJSON features. The widget
|
|
5
|
+
* (D3) feeds it pointer/keyboard events; the live source (D2) reads its
|
|
6
|
+
* FeatureCollection and pushes commits through the streaming channel. No
|
|
7
|
+
* deck.gl, no DOM, no GL — so the whole capture contract is unit-testable
|
|
8
|
+
* with synthetic coordinates.
|
|
9
|
+
*/
|
|
10
|
+
export type DrawMode = "point" | "line" | "polygon";
|
|
11
|
+
/** [lng, lat] — the map's own coordinate order, same as every accessor. */
|
|
12
|
+
export type Position = [number, number];
|
|
13
|
+
export interface PointGeometry {
|
|
14
|
+
type: "Point";
|
|
15
|
+
coordinates: Position;
|
|
16
|
+
}
|
|
17
|
+
export interface LineGeometry {
|
|
18
|
+
type: "LineString";
|
|
19
|
+
coordinates: Position[];
|
|
20
|
+
}
|
|
21
|
+
export interface PolygonGeometry {
|
|
22
|
+
type: "Polygon";
|
|
23
|
+
coordinates: Position[][];
|
|
24
|
+
}
|
|
25
|
+
export type DrawGeometry = PointGeometry | LineGeometry | PolygonGeometry;
|
|
26
|
+
export interface DrawFeature {
|
|
27
|
+
type: "Feature";
|
|
28
|
+
geometry: DrawGeometry;
|
|
29
|
+
/** RFC 7946 top-level id — the same slot findRowIndexById / anchors read. */
|
|
30
|
+
id: string;
|
|
31
|
+
properties: {
|
|
32
|
+
drawMode: DrawMode;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export interface FeatureCollection {
|
|
36
|
+
type: "FeatureCollection";
|
|
37
|
+
features: DrawFeature[];
|
|
38
|
+
}
|
|
39
|
+
/** The in-progress shape, for the runtime-internal preview layer (D4). */
|
|
40
|
+
export interface DrawPreview {
|
|
41
|
+
mode: DrawMode;
|
|
42
|
+
/** Committed-so-far vertices of the current shape. */
|
|
43
|
+
vertices: Position[];
|
|
44
|
+
/** The live cursor position, for the rubber-band segment (null before first move). */
|
|
45
|
+
cursor: Position | null;
|
|
46
|
+
}
|
|
47
|
+
export interface DrawSessionOpts {
|
|
48
|
+
/** Injectable id generator — a deterministic counter by default (tests assert structure, not entropy). */
|
|
49
|
+
idGen?: () => string;
|
|
50
|
+
/** Fired whenever the COMMITTED feature set changes (commit / delete / clear / seed) — D2 wires this to the store push. */
|
|
51
|
+
onChange?: (fc: FeatureCollection) => void;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* One sketch session. Owns the committed features and the single in-progress
|
|
55
|
+
* shape. Every mutation that changes the committed set fires `onChange` once.
|
|
56
|
+
*/
|
|
57
|
+
export declare class DrawSession {
|
|
58
|
+
private mode;
|
|
59
|
+
private pending;
|
|
60
|
+
private cursor;
|
|
61
|
+
private features;
|
|
62
|
+
private readonly nextId;
|
|
63
|
+
private readonly onChange?;
|
|
64
|
+
constructor(opts?: DrawSessionOpts);
|
|
65
|
+
/** Switch tool (or `null` to deactivate). Discards any in-progress shape. */
|
|
66
|
+
setMode(mode: DrawMode | null): void;
|
|
67
|
+
getMode(): DrawMode | null;
|
|
68
|
+
/**
|
|
69
|
+
* A map click. Point commits immediately (the tool stays active for the
|
|
70
|
+
* next point); line/polygon append a vertex. Returns the committed feature
|
|
71
|
+
* when this click finalized one (point only), else null.
|
|
72
|
+
*/
|
|
73
|
+
click(pos: Position): DrawFeature | null;
|
|
74
|
+
/** Cursor moved — updates the rubber-band target. No committed-set change. */
|
|
75
|
+
move(pos: Position): void;
|
|
76
|
+
/**
|
|
77
|
+
* Finalize the in-progress line/polygon (from double-click / Enter). A
|
|
78
|
+
* trailing duplicate vertex — the second click of a double-click — is
|
|
79
|
+
* dropped first. No-op (shape kept pending) if there aren't enough
|
|
80
|
+
* distinct vertices yet. Returns the committed feature or null.
|
|
81
|
+
*/
|
|
82
|
+
commit(): DrawFeature | null;
|
|
83
|
+
/** Esc — discard the in-progress shape, keep the tool active. */
|
|
84
|
+
cancel(): void;
|
|
85
|
+
/** Delete one committed feature by id. Returns whether anything was removed. */
|
|
86
|
+
delete(id: string): boolean;
|
|
87
|
+
/** Remove the most recently committed feature — the toolbar's "delete last" (v1 has no per-feature selection). */
|
|
88
|
+
deleteLast(): boolean;
|
|
89
|
+
/** Remove every committed feature and any in-progress shape. */
|
|
90
|
+
clear(): void;
|
|
91
|
+
/** Load an existing FeatureCollection to append to (autosave restore / seed `data`). */
|
|
92
|
+
seed(fc: FeatureCollection | {
|
|
93
|
+
features?: unknown;
|
|
94
|
+
} | null | undefined): void;
|
|
95
|
+
toFeatureCollection(): FeatureCollection;
|
|
96
|
+
/** The in-progress shape for the preview layer, or null when nothing is pending. */
|
|
97
|
+
preview(): DrawPreview | null;
|
|
98
|
+
private finalize;
|
|
99
|
+
/** Re-key a seeded feature to this session's id space, preserving its geometry. */
|
|
100
|
+
private adopt;
|
|
101
|
+
private emit;
|
|
102
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effect runner (spec: "Map Stories / Effect verbs", Phase C) — drives the
|
|
3
|
+
* per-frame channel for the verbs that need a clock (`trace`, `pulse`).
|
|
4
|
+
* `fade` is NOT here: it's pure attribute mechanics (opacity + transition),
|
|
5
|
+
* which keeps it DOM-visible and scrub-capturable.
|
|
6
|
+
*
|
|
7
|
+
* One active effect per (core, layer): starting a new one cancels the old
|
|
8
|
+
* (last-write-wins, the library-wide action rule). prefers-reduced-motion
|
|
9
|
+
* jumps straight to the final frame — same contract as camera animation.
|
|
10
|
+
*/
|
|
11
|
+
import type { RuntimeCore } from "./runtime-core";
|
|
12
|
+
/** Pure easing/oscillation math — unit-testable without a clock. */
|
|
13
|
+
export declare function traceProgress(t01: number, t0: number, t1: number): number;
|
|
14
|
+
/** Oscillates 1 → low → 1 per cycle (cosine), ending exactly at 1 for whole cycles. */
|
|
15
|
+
export declare function pulseOpacity(t01: number, cycles: number, low?: number): number;
|
|
16
|
+
export interface LayerEffect {
|
|
17
|
+
/** t01 ∈ [0, 1] → the plain-prop patch for this frame. */
|
|
18
|
+
frame(t01: number): Record<string, unknown>;
|
|
19
|
+
/** What to do at the end: hold the final frame's patch, or clear it. */
|
|
20
|
+
end: "hold" | "clear";
|
|
21
|
+
/** Runs after the end state is applied — phase chaining (trace: draw → reveal → dissolve). */
|
|
22
|
+
onDone?(): void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Per-feature trace geometry (spec: "Effect verbs / per-feature trace") —
|
|
26
|
+
* pure: a GeoJSON-ish feature (or bare geometry / flat row) → the outline
|
|
27
|
+
* path with CUMULATIVE-DISTANCE timestamps, so the draw speed is uniform
|
|
28
|
+
* around the perimeter regardless of vertex spacing. Polygon → outer ring;
|
|
29
|
+
* MultiPolygon → first polygon's outer ring; LineString → the line itself.
|
|
30
|
+
* Returns null for untraceable geometry (points, missing geometry).
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildTraceOutline(feature: unknown): {
|
|
33
|
+
path: [number, number][];
|
|
34
|
+
timestamps: number[];
|
|
35
|
+
total: number;
|
|
36
|
+
} | null;
|
|
37
|
+
export declare function cancelLayerEffect(core: RuntimeCore, layerId: string): void;
|
|
38
|
+
export declare function cancelAllLayerEffects(core: RuntimeCore): void;
|
|
39
|
+
export declare function runLayerEffect(core: RuntimeCore, layerId: string, durationMs: number, effect: LayerEffect): void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <om-behavior> — a config element (spec: "Behavior Engine" — "Declarative
|
|
3
|
+
* event->action wiring"), not a rendered one, same role as <om-layer>: its
|
|
4
|
+
* attributes are read by OmMapElement's pick/lifecycle dispatch, not by
|
|
5
|
+
* this class itself. `on`/`layer`/`action` are the wiring; every OTHER
|
|
6
|
+
* attribute becomes an extra fixed payload key (kebab->camel, like data-emit)
|
|
7
|
+
* merged with the pick-derived payload — e.g. `target`/`anchor-offset` for
|
|
8
|
+
* show-overlay, `template` for show-tooltip.
|
|
9
|
+
*/
|
|
10
|
+
import { HTMLElementBase } from "../env";
|
|
11
|
+
export declare class OmBehaviorElement extends HTMLElementBase {
|
|
12
|
+
connectedCallback(): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <om-layer> — a config/data element, not a rendered one. Its attributes
|
|
3
|
+
* are read directly by parse-manifest.ts on each reconcile; this class only
|
|
4
|
+
* needs to exist so the browser upgrades the tag and keeps it out of layout.
|
|
5
|
+
* (Phase 3+ gives this a real connectedCallback for the per-element registry
|
|
6
|
+
* described in Q5; Phase 1's naive reconciler re-queries the DOM each time.)
|
|
7
|
+
*/
|
|
8
|
+
import { HTMLElementBase } from "../env";
|
|
9
|
+
export declare class OmLayerElement extends HTMLElementBase {
|
|
10
|
+
connectedCallback(): void;
|
|
11
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <om-map> — spec: "Manifest Format / Core elements", "Architecture Layers",
|
|
3
|
+
* "Runtime Context API", "Reactivity — the watch declaration".
|
|
4
|
+
* Light DOM (spec: "DOM Model — Shadow vs. Light"): hosts the deck.gl canvas
|
|
5
|
+
* (inside an internal mount div) and the <om-layer>/<om-widget> children
|
|
6
|
+
* as normal siblings.
|
|
7
|
+
*
|
|
8
|
+
* Reconciler: full reparse per reconcile (still no diff-skipping of unchanged
|
|
9
|
+
* DOM — accessor-level memoization/fingerprinting does the real work, see
|
|
10
|
+
* attribute-resolution.ts / runtime-core.ts), microtask-batched (spec:
|
|
11
|
+
* "MutationObserver burst batching") so N mutations in one synchronous burst
|
|
12
|
+
* collapse into a single reconcile. No basemap adapter yet (Phase 6).
|
|
13
|
+
*
|
|
14
|
+
* Phase 4: also the widget pub-sub hub — maintains the live layer-IR
|
|
15
|
+
* registry (for ctx.layers/data/stats), the current selection, and notifies
|
|
16
|
+
* registered widgets per their `watch` tokens (spec: "Reactivity"). This
|
|
17
|
+
* internal registration API (registerWidgetInternal/unregisterWidgetInternal/
|
|
18
|
+
* emit) is consumed by OmWidgetElement, not part of the public package API.
|
|
19
|
+
*/
|
|
20
|
+
import { HTMLElementBase } from "../env";
|
|
21
|
+
import { type RuntimeContext } from "../ctx";
|
|
22
|
+
import type { LayerIR } from "../ir";
|
|
23
|
+
import type { Selection } from "../selection";
|
|
24
|
+
import type { MapViewport } from "../basemap";
|
|
25
|
+
interface OverlayHost {
|
|
26
|
+
updatePosition(viewport: MapViewport | undefined, selection: Selection | null): void;
|
|
27
|
+
}
|
|
28
|
+
export declare class OmMapElement extends HTMLElementBase {
|
|
29
|
+
private core;
|
|
30
|
+
private observer;
|
|
31
|
+
private keydownHandler?;
|
|
32
|
+
private dblclickHandler?;
|
|
33
|
+
private contextmenuHandler?;
|
|
34
|
+
private mount;
|
|
35
|
+
private reconcilePending;
|
|
36
|
+
private layerIRs;
|
|
37
|
+
private selection;
|
|
38
|
+
private widgets;
|
|
39
|
+
private overlays;
|
|
40
|
+
private overlayFlushPending;
|
|
41
|
+
private errorPanel;
|
|
42
|
+
private runtimeErrors;
|
|
43
|
+
private readyFired;
|
|
44
|
+
private resolveReady;
|
|
45
|
+
readonly ready: Promise<void>;
|
|
46
|
+
private firstReconcileDone;
|
|
47
|
+
private rendererLoadPending;
|
|
48
|
+
private loadDispatched;
|
|
49
|
+
connectedCallback(): void;
|
|
50
|
+
disconnectedCallback(): void;
|
|
51
|
+
registerWidgetInternal(el: Element, watch: string[], notify: (ctx: RuntimeContext) => void): void;
|
|
52
|
+
unregisterWidgetInternal(el: Element): void;
|
|
53
|
+
registerOverlayInternal(overlay: OverlayHost): void;
|
|
54
|
+
unregisterOverlayInternal(overlay: OverlayHost): void;
|
|
55
|
+
/**
|
|
56
|
+
* rAF-batched (spec: "Overlay Renderer" — the callback only stores the
|
|
57
|
+
* latest state and schedules one requestAnimationFrame; every overlay's
|
|
58
|
+
* `viewport.project()` + DOM write happens in that one flush, coalescing
|
|
59
|
+
* multiple viewport/selection changes per frame into one DOM pass).
|
|
60
|
+
*/
|
|
61
|
+
private scheduleOverlayFlush;
|
|
62
|
+
emit(event: string, payload?: Record<string, unknown>): void;
|
|
63
|
+
/** Recenters (and optionally rezooms) the map — instant, not an animated fly. */
|
|
64
|
+
flyTo(coords: [number, number], zoom?: number, opts?: {
|
|
65
|
+
duration?: number;
|
|
66
|
+
curve?: boolean;
|
|
67
|
+
}): void;
|
|
68
|
+
setLayerVisible(id: string, visible: boolean): void;
|
|
69
|
+
/** A snapshot of the current layer IRs — the same shape `ctx.layers` is built from. */
|
|
70
|
+
getLayers(): readonly LayerIR[];
|
|
71
|
+
private notifyWidgets;
|
|
72
|
+
private buildCurrentCtx;
|
|
73
|
+
/** Picked object, normalized to flat — GeoJSON properties lifted; columnar rows materialized from columns at the pick index. */
|
|
74
|
+
private resolvePickedObject;
|
|
75
|
+
/**
|
|
76
|
+
* The single selection path — deck.gl's real picks (via RuntimeCore's
|
|
77
|
+
* onSelectionChange callback) and synthetic harness picks
|
|
78
|
+
* (injectPickInternal) both land here, so flattening, columnar row
|
|
79
|
+
* materialization, widget notification, overlay flush, and behavior
|
|
80
|
+
* dispatch are same-path by construction.
|
|
81
|
+
*
|
|
82
|
+
* Flattening happens here (not in toSelection()) — this is the one place
|
|
83
|
+
* with access to the picked layer's shape, needed for GeoJSON `{{field}}`
|
|
84
|
+
* template interpolation against ctx.selection.object (spec: "Because the
|
|
85
|
+
* picked object is already normalized to flat..."). Columnar layers pick
|
|
86
|
+
* with no object at all (deck.gl's non-iterable data path only reports the
|
|
87
|
+
* index) — materialized here, the one place with both the index and the
|
|
88
|
+
* layer's columns.
|
|
89
|
+
*/
|
|
90
|
+
private handleSelectionChange;
|
|
91
|
+
/**
|
|
92
|
+
* The single drag-pick path — deck.gl's continuous drag picks (via
|
|
93
|
+
* RuntimeCore's onDragPick callback) and synthetic harness drags
|
|
94
|
+
* (injectDragPickInternal) both land here. Deliberately does NOT touch
|
|
95
|
+
* ctx.selection (spec: a widget watching `selection` shouldn't re-render
|
|
96
|
+
* on every drag-move) — it only dispatches `on="drag"` behaviors.
|
|
97
|
+
*/
|
|
98
|
+
private handleDragPick;
|
|
99
|
+
/**
|
|
100
|
+
* Synthetic pick entries for the test harness (spec: "Consumer Testing
|
|
101
|
+
* Surface") — internal-suffix API like registerWidgetInternal, consumed
|
|
102
|
+
* by mountForTest's pick()/clearSelection(), not part of the public
|
|
103
|
+
* authoring surface.
|
|
104
|
+
*/
|
|
105
|
+
injectPickInternal(selection: Selection | null): void;
|
|
106
|
+
injectDragPickInternal(selection: Selection): void;
|
|
107
|
+
/** Harness setView (spec: "Consumer Testing Surface") — the one path that reaches pitch/bearing. */
|
|
108
|
+
setViewInternal(partial: {
|
|
109
|
+
longitude?: number;
|
|
110
|
+
latitude?: number;
|
|
111
|
+
zoom?: number;
|
|
112
|
+
pitch?: number;
|
|
113
|
+
bearing?: number;
|
|
114
|
+
}): void;
|
|
115
|
+
/** Cancels all channel-driven effects (trace/pulse), clears their per-frame patches, and removes runtime temp trace layers — story scrub restore. */
|
|
116
|
+
clearEffectsInternal(): void;
|
|
117
|
+
/** A layer's current per-frame channel patch (tests/e2e assert trace progress through this). */
|
|
118
|
+
getAnimatedPropsInternal(layerId: string): Record<string, unknown> | undefined;
|
|
119
|
+
/** Current camera state (story initial-state capture) — internal-suffix API. */
|
|
120
|
+
getViewStateInternal(): {
|
|
121
|
+
longitude: number;
|
|
122
|
+
latitude: number;
|
|
123
|
+
zoom: number;
|
|
124
|
+
pitch: number;
|
|
125
|
+
bearing: number;
|
|
126
|
+
} | undefined;
|
|
127
|
+
/** Projects a lng/lat through the current viewport (harness pixel derivation) — undefined before the viewport resolves. */
|
|
128
|
+
projectInternal(lngLat: [number, number]): [number, number] | undefined;
|
|
129
|
+
/** Renderer finished async init — defer to the first reconcile if it hasn't run yet (see the field comment). */
|
|
130
|
+
private handleRendererLoad;
|
|
131
|
+
private dispatchRendererLoad;
|
|
132
|
+
/** Fires `om-map-ready` / resolves `.ready` once the first reconcile ran, the renderer is up, and no declared data URL is still loading. */
|
|
133
|
+
private checkReady;
|
|
134
|
+
private pickPayload;
|
|
135
|
+
/**
|
|
136
|
+
* Dispatches to every `<om-behavior on="eventName">` (live-queried, like
|
|
137
|
+
* the toggle-layer action's own `<om-layer>` lookups — behaviors are
|
|
138
|
+
* static config, not something worth maintaining a registry for). Every
|
|
139
|
+
* attribute besides `on`/`layer`/`action` becomes an extra fixed payload
|
|
140
|
+
* key (kebab->camel), merged with the pick-derived payload — e.g.
|
|
141
|
+
* `target`/`anchor-offset` for show-overlay, `template` for show-tooltip.
|
|
142
|
+
*/
|
|
143
|
+
private dispatchToBehaviors;
|
|
144
|
+
/**
|
|
145
|
+
* Microtask-coalesced reconcile (spec: "MutationObserver burst batching").
|
|
146
|
+
* Any number of calls within one synchronous block collapse into a single
|
|
147
|
+
* reconcile() — an LLM streaming several attribute edits in one script
|
|
148
|
+
* shouldn't trigger a reconcile per edit.
|
|
149
|
+
*/
|
|
150
|
+
private scheduleReconcile;
|
|
151
|
+
private reconcile;
|
|
152
|
+
/**
|
|
153
|
+
* Re-runs on every reconcile when the `validate` attribute is present —
|
|
154
|
+
* live editing feedback, not just the `OmMap.validate(htmlString)`
|
|
155
|
+
* pre-flight (which calls `validateManifest` directly, standalone).
|
|
156
|
+
* Runtime errors (the error boundary) are folded in too, so the agent
|
|
157
|
+
* sees one consistent stream regardless of origin. `validate="silent"`
|
|
158
|
+
* still runs validation and fires the event, just skips the on-page panel
|
|
159
|
+
* (spec: "for teams building their own error UI").
|
|
160
|
+
*/
|
|
161
|
+
private publishValidation;
|
|
162
|
+
}
|
|
163
|
+
declare global {
|
|
164
|
+
interface HTMLElementTagNameMap {
|
|
165
|
+
"om-map": OmMapElement;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <om-overlay> — sparse, rich geo-anchored HTML (spec: "Module Breakdown /
|
|
3
|
+
* Overlay Renderer", "Overlay System — Three-Way Split"). Rendered in
|
|
4
|
+
* shadow DOM for style isolation, same as <om-widget>. Inline-only by
|
|
5
|
+
* design — no `type` resolution, no `registerOverlay` (see spec's own
|
|
6
|
+
* rationale: a `<template>` shared across maps, or the future React
|
|
7
|
+
* component, already serve reuse).
|
|
8
|
+
*
|
|
9
|
+
* Positioning is centrally rAF-batched by OmMapElement (one flush handles
|
|
10
|
+
* every registered overlay's `viewport.project()` + DOM write per frame,
|
|
11
|
+
* "coalescing multiple events per frame into one DOM pass") — this class
|
|
12
|
+
* never schedules its own rAF, just exposes `updatePosition()` for the map
|
|
13
|
+
* to call.
|
|
14
|
+
*/
|
|
15
|
+
import { HTMLElementBase } from "../env";
|
|
16
|
+
import type { MapViewport } from "../basemap";
|
|
17
|
+
import type { Selection } from "../selection";
|
|
18
|
+
export declare class OmOverlayElement extends HTMLElementBase {
|
|
19
|
+
private root;
|
|
20
|
+
private mapEl;
|
|
21
|
+
private content;
|
|
22
|
+
/** The authored light-DOM markup, captured once, before any interpolation — the source every interpolation pass re-runs against. */
|
|
23
|
+
private template;
|
|
24
|
+
/**
|
|
25
|
+
* The last selection-derived anchor this overlay actually used — only
|
|
26
|
+
* meaningful (and only ever updated) when a `layer` attribute scopes this
|
|
27
|
+
* overlay to one layer's selections. Without scoping, multiple overlays
|
|
28
|
+
* all sharing the one global `ctx.selection` (a click-shown popup on one
|
|
29
|
+
* layer, a hover-shown tooltip on another) fight over the same value: the
|
|
30
|
+
* MOST RECENT selection wins regardless of which overlay it was meant
|
|
31
|
+
* for, so a click on layer A yanks layer B's already-open overlay to the
|
|
32
|
+
* click point (and vice versa) — visible as the overlay jumping to the
|
|
33
|
+
* wrong place, then fighting deck.gl's own picking for the pointer
|
|
34
|
+
* underneath it. With `layer` set, a non-matching selection (including a
|
|
35
|
+
* null one — hover-out elsewhere) is treated as "not my concern" and
|
|
36
|
+
* simply doesn't update this remembered anchor; only a matching
|
|
37
|
+
* selection moves it, and only an explicit `visible="false"` hides it.
|
|
38
|
+
*/
|
|
39
|
+
private lastAnchor;
|
|
40
|
+
/** Memoized feature anchor — recomputed only when the layer's data reference or the target id changes (live layers swap data). */
|
|
41
|
+
private featureAnchorCache;
|
|
42
|
+
connectedCallback(): void;
|
|
43
|
+
disconnectedCallback(): void;
|
|
44
|
+
/**
|
|
45
|
+
* Replaces the overlay's content template (used by the internal, runtime-
|
|
46
|
+
* managed tooltip overlay the `show-tooltip` action drives — see
|
|
47
|
+
* actions.ts — which clones a different author-referenced `<template>`
|
|
48
|
+
* per call rather than being authored once like a normal `<om-overlay>`).
|
|
49
|
+
*/
|
|
50
|
+
setContent(html: string): void;
|
|
51
|
+
/**
|
|
52
|
+
* `anchor-layer="regions" anchor-feature-id="mission"` — anchored to a
|
|
53
|
+
* FEATURE (bbox center for polygons/lines, the point itself for flat
|
|
54
|
+
* rows) instead of a hardcoded coordinate, so the overlay follows the
|
|
55
|
+
* data. Resolved against the layer's live IR on each flush, memoized by
|
|
56
|
+
* data reference; empty data (still loading) resolves quietly and
|
|
57
|
+
* retries on the next flush.
|
|
58
|
+
*/
|
|
59
|
+
private resolveFeatureAnchor;
|
|
60
|
+
/**
|
|
61
|
+
* Overlays with no interactive content default to `pointer-events: none`
|
|
62
|
+
* — there's nothing in them to click/hover, so letting them sit on top of
|
|
63
|
+
* the deck.gl canvas would only ever cost picking, never gain anything.
|
|
64
|
+
* That matters most for a hover-anchored overlay (any `anchor-from=
|
|
65
|
+
* "selection"` overlay shown from a hover behavior, like the built-in
|
|
66
|
+
* tooltip mechanism): it renders directly over the hovered point, and
|
|
67
|
+
* without this, moving the cursor toward the overlay hands mouse events
|
|
68
|
+
* to this DOM element instead of the canvas underneath — deck.gl loses
|
|
69
|
+
* the hover pick, the overlay hides, the canvas is exposed again, the
|
|
70
|
+
* same pick re-fires, and the cycle repeats as a visible flicker. An
|
|
71
|
+
* overlay WITH interactive content (a `data-emit` close button, say)
|
|
72
|
+
* keeps normal pointer-events so that content stays clickable.
|
|
73
|
+
*/
|
|
74
|
+
private updatePointerEvents;
|
|
75
|
+
/**
|
|
76
|
+
* Called by OmMapElement's batched flush on every viewport/selection
|
|
77
|
+
* change. Resolves the anchor coordinate (`anchor-from="selection"` or a
|
|
78
|
+
* static `anchor="[lng,lat]"`), culls off-screen/behind-camera anchors,
|
|
79
|
+
* and re-interpolates `{{field}}` tokens against the current selection.
|
|
80
|
+
*/
|
|
81
|
+
updatePosition(viewport: MapViewport | undefined, selection: Selection | null): void;
|
|
82
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <om-step> — a config element (spec: "Map Stories / Grammar"), the same
|
|
3
|
+
* citizenship class as <om-behavior>: its attributes are read by the
|
|
4
|
+
* parent <om-story>'s timeline build, never by this class itself. Steps
|
|
5
|
+
* REFERENCE layers/overlays by id and are only valid inside <om-story>;
|
|
6
|
+
* a step that CONTAINS elements is a validation error (the
|
|
7
|
+
* reference-never-contain rule, enforced mechanically).
|
|
8
|
+
*/
|
|
9
|
+
import { HTMLElementBase } from "../env";
|
|
10
|
+
export declare class OmStepElement extends HTMLElementBase {
|
|
11
|
+
connectedCallback(): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <om-story> — the storyboard element (spec: "Map Stories"). A story is a
|
|
3
|
+
* time-sequenced action dispatcher: where <om-behavior> maps event →
|
|
4
|
+
* action, a story maps time → action, through the exact same action-payload
|
|
5
|
+
* contract (mapEl.emit). It owns the clock; nothing owns the story — the
|
|
6
|
+
* player widget is optional chrome that emits story-play/pause/seek.
|
|
7
|
+
*
|
|
8
|
+
* Composability guarantee (spec): steps reference layers/overlays by id and
|
|
9
|
+
* never contain them; delete the story and the map is byte-for-byte the
|
|
10
|
+
* same map.
|
|
11
|
+
*
|
|
12
|
+
* Scrub model (spec, v1): seek(T) = restore captured initial state, then
|
|
13
|
+
* re-fire the FINAL state of every step starting ≤ T (instant — payload
|
|
14
|
+
* duration overridden to 0). Step-granularity; mid-step interpolation is
|
|
15
|
+
* deferred. Initial state is captured on first play from the attributes the
|
|
16
|
+
* built-in-action steps will touch (statically computable from payloads)
|
|
17
|
+
* plus the camera; custom-action steps are replay-only (warned once).
|
|
18
|
+
*/
|
|
19
|
+
import { HTMLElementBase } from "../env";
|
|
20
|
+
export type StoryState = "idle" | "playing" | "paused" | "ended";
|
|
21
|
+
export declare class OmStoryElement extends HTMLElementBase {
|
|
22
|
+
private mapEl;
|
|
23
|
+
private steps;
|
|
24
|
+
private timeline;
|
|
25
|
+
private fired;
|
|
26
|
+
private elapsed;
|
|
27
|
+
private stateValue;
|
|
28
|
+
private raf;
|
|
29
|
+
private lastTimestamp;
|
|
30
|
+
/** Set by the test harness (advanceInternal): the clock is driven manually, never by rAF. */
|
|
31
|
+
private manualClock;
|
|
32
|
+
private captured;
|
|
33
|
+
private capturedCamera;
|
|
34
|
+
private warnedCustomActions;
|
|
35
|
+
private readonly onUserGesture;
|
|
36
|
+
connectedCallback(): void;
|
|
37
|
+
disconnectedCallback(): void;
|
|
38
|
+
get state(): StoryState;
|
|
39
|
+
get currentTime(): number;
|
|
40
|
+
get duration(): number;
|
|
41
|
+
play(): void;
|
|
42
|
+
pause(): void;
|
|
43
|
+
seek(ms: number): void;
|
|
44
|
+
/**
|
|
45
|
+
* Test-harness clock drive (spec: "Consumer Testing Surface" +
|
|
46
|
+
* "Map Stories"): switches this story to a manual clock permanently and
|
|
47
|
+
* advances it deterministically — rAF never runs for a manual story, so
|
|
48
|
+
* headless tests are exact.
|
|
49
|
+
*/
|
|
50
|
+
advanceInternal(ms: number): void;
|
|
51
|
+
private startClock;
|
|
52
|
+
private stopClock;
|
|
53
|
+
private advanceBy;
|
|
54
|
+
private fireDueSteps;
|
|
55
|
+
/** Steps and the timeline are (re)built lazily when idle — mid-play DOM edits apply on the next play/seek. */
|
|
56
|
+
private ensureBuilt;
|
|
57
|
+
private ensureCaptured;
|
|
58
|
+
private restoreInitial;
|
|
59
|
+
private attachInterruptListeners;
|
|
60
|
+
private detachInterruptListeners;
|
|
61
|
+
private tick;
|
|
62
|
+
}
|
|
63
|
+
declare global {
|
|
64
|
+
interface HTMLElementTagNameMap {
|
|
65
|
+
"om-story": OmStoryElement;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <om-widget> — spec: "DOM Model — Shadow vs. Light", "Widget System",
|
|
3
|
+
* "Runtime Context API", "Reactivity — the watch declaration",
|
|
4
|
+
* "Wiring DOM events to emits — two layers".
|
|
5
|
+
*
|
|
6
|
+
* Three tiers, one registry: `type="legend"` etc. resolve against the
|
|
7
|
+
* built-in/registered widget registry; no `type` + a `<script type="om/widget">`
|
|
8
|
+
* child runs as an inline custom widget; no `type` and no script is a purely
|
|
9
|
+
* static widget (Phase 1 behaviour, unchanged).
|
|
10
|
+
*/
|
|
11
|
+
import { HTMLElementBase } from "../env";
|
|
12
|
+
import { type WidgetHost } from "../widget-registry";
|
|
13
|
+
import type { RuntimeContext } from "../ctx";
|
|
14
|
+
export declare class OmWidgetElement extends HTMLElementBase implements WidgetHost {
|
|
15
|
+
root: ShadowRoot;
|
|
16
|
+
state: Record<string, unknown>;
|
|
17
|
+
/** Set by an inline `<script type="om/widget">` block (`this.watch = [...]`), if present. */
|
|
18
|
+
watch?: string[];
|
|
19
|
+
/** Set by an inline `<script type="om/widget">` block (`this.render = (ctx) => {...}`), if present. */
|
|
20
|
+
render?: (ctx: RuntimeContext) => void | Promise<void>;
|
|
21
|
+
private mapEl;
|
|
22
|
+
private renderFn?;
|
|
23
|
+
private watchTokens;
|
|
24
|
+
connectedCallback(): void;
|
|
25
|
+
disconnectedCallback(): void;
|
|
26
|
+
private setUpRenderer;
|
|
27
|
+
$(selector: string): Element | null;
|
|
28
|
+
$$(selector: string): NodeListOf<Element>;
|
|
29
|
+
}
|
package/dist/env.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSR-safe environment guards. The library must be import-safe outside a
|
|
3
|
+
* browser (Next.js server components, Jest/Vitest in node, agent pipelines
|
|
4
|
+
* running pre-flight validation server-side) — the research-validated
|
|
5
|
+
* failure mode is touching browser globals at import time (Mapbox GL's
|
|
6
|
+
* six-years-open #4593). Two rules keep us clean:
|
|
7
|
+
*
|
|
8
|
+
* 1. No module evaluates a browser global at import time. Class
|
|
9
|
+
* declarations `extends HTMLElementBase` (below) instead of
|
|
10
|
+
* `extends HTMLElement`, and element registration goes through
|
|
11
|
+
* `defineElement`, which no-ops outside a browser.
|
|
12
|
+
* 2. Everything that genuinely needs a DOM (rendering, observers,
|
|
13
|
+
* sandbox iframes) touches it inside methods/functions only.
|
|
14
|
+
*
|
|
15
|
+
* Outside a browser the custom elements are inert-but-importable: the
|
|
16
|
+
* classes exist (so types, registries, validation, expression compilation,
|
|
17
|
+
* and IR snapshots all work), they just never register or upgrade.
|
|
18
|
+
*/
|
|
19
|
+
export declare const isBrowser: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Base class for the om-* custom elements. In a browser this IS
|
|
22
|
+
* HTMLElement; in node it's an empty class so the module can evaluate.
|
|
23
|
+
* Constructing an element outside a browser is not supported (nothing
|
|
24
|
+
* registers them there), so the stub never needs real behavior.
|
|
25
|
+
*/
|
|
26
|
+
export declare const HTMLElementBase: typeof HTMLElement;
|
|
27
|
+
/**
|
|
28
|
+
* `customElements.define`, guarded twice: no-op outside a browser, and
|
|
29
|
+
* idempotent under double-import (a second `define` of the same name
|
|
30
|
+
* throws — a real hazard when a CDN tag and a bundled import coexist).
|
|
31
|
+
*/
|
|
32
|
+
export declare function defineElement(name: string, ctor: CustomElementConstructor): void;
|
|
33
|
+
/**
|
|
34
|
+
* Parses a manifest HTML string into a Document — used by the string-input
|
|
35
|
+
* surfaces (`OmMap.validate(html)`, `snapshotIR(html)`). Neither needs a
|
|
36
|
+
* *browser* (no rendering, no custom-element upgrade — they read tags and
|
|
37
|
+
* attributes), but they do need a DOM implementation for DOMParser. In
|
|
38
|
+
* plain node, that means a test DOM: vitest/jest with the happy-dom or
|
|
39
|
+
* jsdom environment both provide it.
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseHtmlDocument(html: string): Document;
|