@nika-js/onlymap 0.4.4 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/bin/onlymapjs.mjs +14 -1
- package/dist/{LercDecode.es-CgN9Gb9e.js → LercDecode.es-B49qauXg.js} +1 -1
- package/dist/{basemap-Bfn5Z__c.js → basemap-nJ_ODY7r.js} +3 -3
- package/dist/elements/om-map.d.ts +40 -0
- package/dist/{index-CnitG1VX.js → index-CeOjfL2p.js} +2 -2
- package/dist/{index-KgO0MBqA.js → index-DSkgI4Oc.js} +7767 -7683
- package/dist/{index-C4cWRigY.js → index-DjlGZov2.js} +1 -1
- package/dist/{index-M8KfTTol.js → index-o_yRv2lD.js} +1 -1
- package/dist/{index-lXrP3rPo.js → index-uDJbHjA9.js} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/layer-registry.d.ts +8 -0
- package/dist/{lerc-l-QFh62d.js → lerc-CaQOmDCP.js} +2 -2
- package/dist/onlymap.standalone.js +12893 -12809
- package/dist/onlymapjs.js +67 -63
- package/dist/programmatic.d.ts +4 -0
- package/dist/{raster-BBA_oI-d.js → raster-DMq8FgiV.js} +2 -2
- package/dist/react.js +162 -161
- package/dist/runtime-core.d.ts +8 -0
- package/dist/testing.d.ts +6 -0
- package/dist/version.d.ts +1 -1
- package/dist/widget-layout.d.ts +20 -0
- package/docs/testing.md +4 -2
- package/llms.txt +1 -1
- package/package.json +2 -2
- package/skills/onlymapjs/SKILL.md +2 -2
- package/skills/onlymapjs/references/syntax.md +3 -3
- package/skills/onlymapjs/references/testing.md +1 -0
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@ Or with no build step at all, straight from a CDN:
|
|
|
69
69
|
|
|
70
70
|
The bare package URL serves `dist/onlymap.standalone.js`, a single-file bundle built for exactly this (jsDelivr too). Use a CDN that serves the package's raw files — **not** a rebundling CDN like esm.sh, which re-splits the bundle into duplicate copies of the deck.gl/luma.gl runtime and breaks every layer's shader compilation.
|
|
71
71
|
|
|
72
|
-
Then `npx @nika-js/onlymap init` wires up VS Code IntelliSense and `!`-prefixed manifest snippets for your project. The library ships with
|
|
72
|
+
Then `npx @nika-js/onlymap init` wires up VS Code IntelliSense and `!`-prefixed manifest snippets for your project. The library ships with 540 unit/behavioral tests and 53 Playwright GPU tests.
|
|
73
73
|
|
|
74
74
|
The [examples](https://github.com/NikaGeospatial/onlymapjs/tree/main/examples) are the best tour: widgets, behaviors & overlays, basemaps, columnar/Arrow data, manual drawing, 3D models, scene lighting (with the native lighting widget), DEM terrain, a live WebSocket ship feed, and a polled driver fleet.
|
|
75
75
|
|
|
@@ -244,7 +244,9 @@ await h.pick({ layer: "quakes", featureId: "q1" }); // same code path as a real
|
|
|
244
244
|
expect(overlay.shadowRoot.textContent).toContain("M 6.5");
|
|
245
245
|
```
|
|
246
246
|
|
|
247
|
-
Plus `OmMap.snapshotIR(html)` to lock down what a manifest *means* in a snapshot test, and `await mapEl.ready` / the `om-map-ready` event to de-flake browser e2e tests.
|
|
247
|
+
Plus `OmMap.snapshotIR(html)` to lock down what a manifest *means* in a snapshot test, and `await mapEl.ready` / the `om-map-ready` event to de-flake browser e2e tests.
|
|
248
|
+
|
|
249
|
+
**Layout, checked like types.** `npx @nika-js/onlymap check-layout <manifest.html>` audits a page's real rendered widget layout in headless Chromium — no screenshots, no eyeballing: it asserts no widgets overlap, each is hit-testable (not painted under the canvas), each sits in its slot, and same-slot edges are flush, across a viewport-width sweep, emitting structured `{severity, element, message, fix}` diagnostics and exiting 0/1. The same audit is importable as `auditLayout(page)` for your own Playwright suite. Full guide: [docs/testing.md](docs/testing.md).
|
|
248
250
|
|
|
249
251
|
## 3D
|
|
250
252
|
|
|
@@ -254,8 +256,8 @@ Plus `OmMap.snapshotIR(html)` to lock down what a manifest *means* in a snapshot
|
|
|
254
256
|
|
|
255
257
|
- **`OmMap.*`** — `validate`, `snapshotIR`, `registerLayer`, `registerWidget`, `registerAction`, `registerSource`, `registerFormat`, `registerBasemap`, `configureBasemap`, `configureData`, `configureTelemetry`, `configureLicense`, `getLayerSchema`
|
|
256
258
|
- **`@nika-js/onlymap/deck`** — the bundled deck.gl classes (`CompositeLayer`, `TileLayer`, …) for building custom layer types: shims must extend the same class hierarchy the core renders with, not a second installed deck.gl copy. Recipe: [docs/custom-layers.md](docs/custom-layers.md)
|
|
257
|
-
- **On a `<om-map>` element** — `ready` (promise), `flyTo(coords, zoom?)`, `setLayerVisible(id, bool)`, `getLayers()`, `emit(action, payload)`, `snapshot(opts?)` (canvas-only PNG of basemap + layers at device pixels — DOM widgets/overlays and provider attribution are NOT captured, so exports must render credits themselves; `{as: "blob"}` for files, default dataURL); the `om-view-changed` event fires once the camera settles (debounced; `detail` = `{longitude, latitude, zoom, pitch, bearing, origin}`, where `origin` is `"user"` for gesture-driven bursts vs `"programmatic"` for API/story moves — the echo-suppression signal for state sync) — the camera-persistence hook; `document.querySelector("om-map")` is fully typed
|
|
258
|
-
- **`MapController`** — the framework-grade programmatic front-end (typed `LayerDescriptor`s → the same reconcile core, no DOM manifest): `setLayers`, `watch`, `emit`, camera methods, `injectPick`, `ready`, `snapshot`, an `onViewChange(view, origin)` option (the `om-view-changed` twin). The React adapter rides it; usable directly from vanilla TS or other frameworks
|
|
259
|
+
- **On a `<om-map>` element** — `ready` (promise), `flyTo(coords, zoom?)`, `setLayerVisible(id, bool)`, `getLayers()`, `emit(action, payload)`, `snapshot(opts?)` (canvas-only PNG of basemap + layers at device pixels — DOM widgets/overlays and provider attribution are NOT captured, so exports must render credits themselves; `{as: "blob"}` for files, default dataURL); the `om-view-changed` event fires once the camera settles (debounced; `detail` = `{longitude, latitude, zoom, pitch, bearing, origin}`, where `origin` is `"user"` for gesture-driven bursts vs `"programmatic"` for API/story moves — the echo-suppression signal for state sync) — the camera-persistence hook; the `om-map-point` event (`detail = {coordinate: [lng,lat]|null, kind: "click"|"hover"}`) fires on every click/hover with the map coordinate, including empty-map clicks picks discard — the hook for custom capture tools the built-in draw widget doesn't cover; the `om-tileset-load` event (`detail = {layerId, tileset}`) surfaces a `Tile3DLayer`'s live deck `Tileset3D` for tools that need the real tileset (e.g. region export), not the IR; `document.querySelector("om-map")` is fully typed
|
|
260
|
+
- **`MapController`** — the framework-grade programmatic front-end (typed `LayerDescriptor`s → the same reconcile core, no DOM manifest): `setLayers`, `watch`, `emit`, camera methods, `injectPick`, `ready`, `snapshot`, an `onViewChange(view, origin)` option (the `om-view-changed` twin), plus `onMapPoint` / `onTilesetLoad` options (the `om-map-point` / `om-tileset-load` twins). The React adapter rides it; usable directly from vanilla TS or other frameworks
|
|
259
261
|
- **`getStore(token)`** — the external-store contract: per-token `{subscribe, getSnapshot}` stores (`viewport`/`selection`/`layers`/`data:<id>`) with cached immutable plain-data snapshots and `origin` tagging — directly consumable by `useSyncExternalStore` (the React adapter's own hooks ride it), MobX autoruns, Redux listeners, Zustand mirrors. ~20-line integration-tested recipes for Redux Toolkit, MobX/mobx-keystone, Zustand, and Jotai: [docs/external-stores.md](docs/external-stores.md)
|
|
260
262
|
- **Testing** — `mountForTest`, and imports are SSR-safe (importing in Node/jsdom never touches browser globals)
|
|
261
263
|
|
package/bin/onlymapjs.mjs
CHANGED
|
@@ -137,8 +137,21 @@ async function checkLayout(manifestArg) {
|
|
|
137
137
|
}
|
|
138
138
|
const context = await browser.newContext({ serviceWorkers: "block" });
|
|
139
139
|
const page = await context.newPage();
|
|
140
|
+
const pageErrors = [];
|
|
141
|
+
page.on("pageerror", (error) => pageErrors.push(String(error).split("\n")[0]));
|
|
142
|
+
page.on("console", (msg) => {
|
|
143
|
+
if (msg.type() === "error") pageErrors.push(msg.text().split("\n")[0]);
|
|
144
|
+
});
|
|
140
145
|
await page.goto(url, { waitUntil: "domcontentloaded" });
|
|
141
|
-
|
|
146
|
+
let diagnostics;
|
|
147
|
+
try {
|
|
148
|
+
diagnostics = await auditLayout(page);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
// The page never produced a map. Its own console/pageerror output is the
|
|
151
|
+
// real cause — surface it so the failure is self-explaining.
|
|
152
|
+
const detail = pageErrors.length ? `\nPage errors:\n ${pageErrors.slice(0, 3).join("\n ")}` : "";
|
|
153
|
+
throw new Error(`${error instanceof Error ? error.message : String(error)}${detail}`);
|
|
154
|
+
}
|
|
142
155
|
if (diagnostics.length === 0) {
|
|
143
156
|
console.log("CLEAN: no widget layout problems at 360, 640, 768, or 1024px.");
|
|
144
157
|
return 0;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as ny, L as sy, M as oy, m as ay, c as ka, z as oc, a as Sd, G as sm, V as ly, W as cy, b as uy, g as hy, d as Vu, f as Ed, e as dy, l as py, u as fy, D as Qf, h as my } from "./index-
|
|
1
|
+
import { C as ny, L as sy, M as oy, m as ay, c as ka, z as oc, a as Sd, G as sm, V as ly, W as cy, b as uy, g as hy, d as Vu, f as Ed, e as dy, l as py, u as fy, D as Qf, h as my } from "./index-DSkgI4Oc.js";
|
|
2
2
|
const Cd = Math.PI / 180, gy = 180 / Math.PI;
|
|
3
3
|
function em(Pe, Q = 0) {
|
|
4
4
|
const me = Math.min(180, Pe) * Cd;
|
|
@@ -25217,7 +25217,7 @@ class Ry {
|
|
|
25217
25217
|
* remount, since `customAttribution` is fixed at construction.
|
|
25218
25218
|
*/
|
|
25219
25219
|
mountAttribution(Q) {
|
|
25220
|
-
if (this.showAttribution && (this.attributionText = Q, this.attribution && (this.map.removeControl(this.attribution), this.attributionElement = void 0), this.attribution = new rm.AttributionControl({ compact: !0, ...Q ? { customAttribution: Q } : {} }), this.map.addControl(this.attribution), this.attributionHost)) {
|
|
25220
|
+
if (this.showAttribution && (this.attributionText = Q, this.attribution && (this.map.removeControl(this.attribution), this.attributionElement?.remove(), this.attributionElement = void 0), this.attribution = new rm.AttributionControl({ compact: !0, ...Q ? { customAttribution: Q } : {} }), this.map.addControl(this.attribution), this.attributionHost)) {
|
|
25221
25221
|
const me = this.map.getContainer().querySelector(".maplibregl-ctrl-attrib");
|
|
25222
25222
|
me && (me.setAttribute("data-om-mandated-chrome-item", "attribution"), me.style.margin = "0", this.attributionHost.appendChild(me), this.attributionElement = me);
|
|
25223
25223
|
}
|
|
@@ -25272,7 +25272,7 @@ class Ry {
|
|
|
25272
25272
|
for (const me of this.pendingCaptureRejects) me(Q);
|
|
25273
25273
|
this.pendingCaptureRejects.clear();
|
|
25274
25274
|
}
|
|
25275
|
-
this.deckCaptures = [], this.attributionElement = void 0, this.overlay.finalize(), this.map.remove();
|
|
25275
|
+
this.deckCaptures = [], this.attributionElement?.remove(), this.attributionElement = void 0, this.overlay.finalize(), this.map.remove();
|
|
25276
25276
|
}
|
|
25277
25277
|
}
|
|
25278
25278
|
export {
|
|
@@ -67,6 +67,8 @@ export declare class OmMapElement extends HTMLElementBase {
|
|
|
67
67
|
private widgetLayer;
|
|
68
68
|
private readonly slotContainers;
|
|
69
69
|
private readonly mandatedChromeHosts;
|
|
70
|
+
/** Slots that host mandated chrome (badge/attribution) — set at host creation, so the collision-dim exemption is a slot-level flag, not a per-flush DOM scan. */
|
|
71
|
+
private readonly slotsWithMandatedChrome;
|
|
70
72
|
private widgetResizeObserver;
|
|
71
73
|
private widgetsFolded;
|
|
72
74
|
private foldPassPending;
|
|
@@ -99,13 +101,22 @@ export declare class OmMapElement extends HTMLElementBase {
|
|
|
99
101
|
* adapters mount/unmount the actual controls inside.
|
|
100
102
|
*/
|
|
101
103
|
private ensureMandatedChromeHost;
|
|
104
|
+
/** True while an internal reparent (slotting/manual-flip) is moving a widget — its transient disconnect must NOT be read as an author removal. */
|
|
105
|
+
private inSlotReparent;
|
|
106
|
+
/** Wrap an internal reparent: suppress history AND flag it as a move so unregisterWidgetInternal keeps the fold/anchor bookkeeping. */
|
|
107
|
+
private reparent;
|
|
102
108
|
slotWidgetInternal(el: HTMLElement, slot: WidgetSlot): void;
|
|
103
109
|
/** Called by <om-widget> when its live `fold` attribute changes. */
|
|
104
110
|
refreshWidgetFoldInternal(): void;
|
|
105
111
|
private startWidgetFoldObserver;
|
|
112
|
+
private foldBreakpointRaw?;
|
|
113
|
+
private foldBreakpointCache;
|
|
114
|
+
/** Cached breakpoint resolution — re-probes only when the token string actually changes, not on every ResizeObserver tick. */
|
|
106
115
|
private foldBreakpointPx;
|
|
107
116
|
private measureWidgetFold;
|
|
108
117
|
private updateWidgetFoldForWidth;
|
|
118
|
+
/** Lift the bottom-end row clear of the bottom drawer toggle while folded. Applied here AND in the fold pass so a lazily-created bottom-end container still gets it. */
|
|
119
|
+
private syncFoldBottomOffset;
|
|
109
120
|
private scheduleFoldPass;
|
|
110
121
|
private shouldFoldWidget;
|
|
111
122
|
private ensureFoldDrawer;
|
|
@@ -172,6 +183,7 @@ export declare class OmMapElement extends HTMLElementBase {
|
|
|
172
183
|
* modeled (headless has no rects; this no-ops there).
|
|
173
184
|
*/
|
|
174
185
|
private dimSlotsAgainstOverlays;
|
|
186
|
+
/** widgets-toggle can sit in any author-chosen slot, so still scan for it (attribution/badge are covered by the slot flag). */
|
|
175
187
|
private slotHasNeverHidesWidget;
|
|
176
188
|
emit(event: string, payload?: Record<string, unknown>): void;
|
|
177
189
|
/** Recenters (and optionally rezooms) the map — instant, not an animated fly. */
|
|
@@ -218,6 +230,20 @@ export declare class OmMapElement extends HTMLElementBase {
|
|
|
218
230
|
*/
|
|
219
231
|
injectPickInternal(selection: Selection | null): void;
|
|
220
232
|
injectDragPickInternal(selection: Selection): void;
|
|
233
|
+
/** Every click/hover map coordinate (spec: "Manual Drawing"): drives the
|
|
234
|
+
* internal draw controller AND fires the public `om-map-point` event, so
|
|
235
|
+
* consumer capture tools the built-in draw widget doesn't cover
|
|
236
|
+
* (rectangle/circle AOIs) can subscribe. The two consumers are
|
|
237
|
+
* DECOUPLED: the event dispatches first (dispatchEvent isolates a
|
|
238
|
+
* throwing listener), then the draw controller runs — so neither a
|
|
239
|
+
* throwing draw session nor a throwing listener can starve the other.
|
|
240
|
+
* Fires at pointer rate on hover (no debounce — vertex capture needs
|
|
241
|
+
* every point); heavy listeners should throttle their own work. */
|
|
242
|
+
private handleMapPoint;
|
|
243
|
+
/** Harness map-point injection (spec: "Consumer Testing Surface") — the
|
|
244
|
+
* same path a real deck click/hover coordinate takes, so the om-map-point
|
|
245
|
+
* event and custom capture tools are testable without a GPU. */
|
|
246
|
+
injectMapPointInternal(coordinate: [number, number] | null, kind?: "click" | "hover"): void;
|
|
221
247
|
/** Harness setView (spec: "Consumer Testing Surface") — the one path that reaches pitch/bearing. */
|
|
222
248
|
setViewInternal(partial: {
|
|
223
249
|
longitude?: number;
|
|
@@ -302,6 +328,20 @@ declare global {
|
|
|
302
328
|
/** "user" if any change in the settled burst came from a canvas gesture; "programmatic" for pure API/action/story moves. */
|
|
303
329
|
origin: "user" | "programmatic";
|
|
304
330
|
}>;
|
|
331
|
+
/** Every click/hover's map coordinate (null when the pointer is off any
|
|
332
|
+
* geometry deck can unproject) — the consumer hook for custom capture
|
|
333
|
+
* tools beyond the built-in draw widget. */
|
|
334
|
+
"om-map-point": CustomEvent<{
|
|
335
|
+
coordinate: [number, number] | null;
|
|
336
|
+
kind: "click" | "hover";
|
|
337
|
+
}>;
|
|
338
|
+
/** A Tile3DLayer finished loading its root tileset — detail carries the
|
|
339
|
+
* authored layer id and the live deck `Tileset3D` for tools (e.g. region
|
|
340
|
+
* export) that need the real tileset, not the IR. */
|
|
341
|
+
"om-tileset-load": CustomEvent<{
|
|
342
|
+
layerId: string;
|
|
343
|
+
tileset: unknown;
|
|
344
|
+
}>;
|
|
305
345
|
}
|
|
306
346
|
}
|
|
307
347
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as E, j as S, k as R, n as N, o as h, p as w, q as O, s as W, r as x, t as I, v as B, w as _, x as P, y as k, A as $, B as z, E as v, F, H as C, I as U, J as p, K as M } from "./index-
|
|
2
|
-
import { N as $e, R as ze, _ as ve, O as Ce, P as Ue, Q as Me, S as je, T as De, U as Je, X as qe, Y as He, Z as Ve, $ as Ge, a0 as Ke, a1 as Qe, a2 as Xe, a3 as Ye, a4 as Ze, a5 as et, a6 as tt, a7 as nt, a8 as rt, a9 as at, aa as st, ab as ot } from "./index-
|
|
1
|
+
import { i as E, j as S, k as R, n as N, o as h, p as w, q as O, s as W, r as x, t as I, v as B, w as _, x as P, y as k, A as $, B as z, E as v, F, H as C, I as U, J as p, K as M } from "./index-DSkgI4Oc.js";
|
|
2
|
+
import { N as $e, R as ze, _ as ve, O as Ce, P as Ue, Q as Me, S as je, T as De, U as Je, X as qe, Y as He, Z as Ve, $ as Ge, a0 as Ke, a1 as Qe, a2 as Xe, a3 as Ye, a4 as Ze, a5 as et, a6 as tt, a7 as nt, a8 as rt, a9 as at, aa as st, ab as ot } from "./index-DSkgI4Oc.js";
|
|
3
3
|
import { g as j, i as D } from "./table-accessors-CYWTzpQI.js";
|
|
4
4
|
async function J(t, e, n = {}, r = {}) {
|
|
5
5
|
const a = E(t), s = S.getWorkerFarm(n), { source: o } = n, c = { name: a, source: o };
|