@nika-js/onlymap 0.3.4 → 0.4.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/dist/onlymapjs.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ay as e, az as r, aA as o, aB as t, aq as i, aC as n, aD as b, aE as l, aF as c, aG as g, aH as m, aI as p, aJ as L, aK as y, ap as T, aL as S, ax as u, W as d, aM as E, aN as M, aO as A, aP as I, aQ as f, aR as x, aS as C, aT as h, aU as B, aV as D, aW as R, aX as w, aY as v, aZ as F, a_ as G, a$ as N, b0 as O, b1 as W, b2 as _, b3 as z, b4 as H, b5 as J, b6 as k, b7 as P, b8 as V, b9 as Y, ba as j, bb as q, bc as K, bd as Q, be as U, bf as X, bg as Z, bh as $, bi as aa, bj as sa, bk as ea } from "./index-BR1HjJoE.js";
1
+ import { ay as e, az as r, aA as o, aB as t, aq as i, aC as n, aD as b, aE as l, aF as c, aG as g, aH as m, aI as p, aJ as L, aK as T, ap as y, aL as S, ax as u, W as d, aM as E, aN as M, aO as A, aP as I, aQ as f, aR as x, aS as C, aT as h, aU as B, aV as D, aW as R, aX as w, aY as v, aZ as F, a_ as G, a$ as N, b0 as O, b1 as W, b2 as _, b3 as k, b4 as z, b5 as H, b6 as J, b7 as P, b8 as V, b9 as Y, ba as j, bb as q, bc as K, bd as Q, be as U, bf as X, bg as Z, bh as $, bi as aa, bj as sa, bk as ea, bl as ra } from "./index-BoxZ3Mec.js";
2
2
  export {
3
3
  e as AccessorCache,
4
4
  r as BitmapLayer,
@@ -13,8 +13,8 @@ export {
13
13
  m as MapController,
14
14
  p as OmMap,
15
15
  L as ScatterplotLayer,
16
- y as ScenegraphLayer,
17
- T as SimpleMeshLayer,
16
+ T as ScenegraphLayer,
17
+ y as SimpleMeshLayer,
18
18
  S as Tile3DLayer,
19
19
  u as TileLayer,
20
20
  d as WebMercatorViewport,
@@ -36,23 +36,24 @@ export {
36
36
  N as getTerrain,
37
37
  O as isBrowser,
38
38
  W as isColumnar,
39
- _ as loadRasterModule,
39
+ _ as isStoreToken,
40
+ k as loadRasterModule,
40
41
  z as mountForTest,
41
42
  H as normalizeData,
42
43
  J as parseDurationMs,
43
- k as parseTerrainAttrs,
44
- P as registerAction,
45
- V as registerBasemap,
46
- Y as registerFormat,
47
- j as registerLayer,
48
- q as registerSource,
49
- K as registerTerrain,
50
- Q as registerWidget,
51
- U as resolveLighting,
52
- X as rowAt,
53
- Z as sandboxHtml,
54
- $ as snapshotIR,
55
- aa as solarAzElDegrees,
56
- sa as validateManifest,
57
- ea as validateManifestString
44
+ P as parseTerrainAttrs,
45
+ V as registerAction,
46
+ Y as registerBasemap,
47
+ j as registerFormat,
48
+ q as registerLayer,
49
+ K as registerSource,
50
+ Q as registerTerrain,
51
+ U as registerWidget,
52
+ X as resolveLighting,
53
+ Z as rowAt,
54
+ $ as sandboxHtml,
55
+ aa as snapshotIR,
56
+ sa as solarAzElDegrees,
57
+ ea as validateManifest,
58
+ ra as validateManifestString
58
59
  };
@@ -18,8 +18,10 @@ import { type CameraOptions } from "./runtime-core";
18
18
  import { type LightingIR, type LightingOptions } from "./scene-lighting";
19
19
  import type { TerrainIR } from "./terrain";
20
20
  import { type SnapshotOptions } from "./snapshot";
21
+ import { type TokenStore, type ViewOrigin, type ViewportSnapshot, type DataVersionSnapshot } from "./external-store";
21
22
  import type { LayerIR } from "./ir";
22
23
  import { type RuntimeContext } from "./ctx";
24
+ import type { LayerMetaSnapshot } from "./ctx";
23
25
  import type { Selection } from "./selection";
24
26
  import type { ValidationEntry } from "./validation";
25
27
  import type { MapViewport } from "./basemap";
@@ -96,9 +98,12 @@ export interface MapControllerOptions {
96
98
  /**
97
99
  * Camera settled (trailing-debounced, the `om-view-changed` twin) — for
98
100
  * camera persistence. Per-frame viewport churn goes to `watch(["viewport"])`;
99
- * this fires once with the destination.
101
+ * this fires once with the destination. `origin` is "user" when ANY change
102
+ * in the settled burst came from a canvas gesture (a drag's inertia tail
103
+ * alone must not relabel the gesture), "programmatic" for pure API/story
104
+ * moves — the echo-suppression signal for store bridges.
100
105
  */
101
- onViewChange?: (view: CameraState) => void;
106
+ onViewChange?: (view: CameraState, origin: ViewOrigin) => void;
102
107
  /** Scene lighting (spec: "Scene Lighting") — the lighting* attributes' programmatic twin; omit for deck's default lights. */
103
108
  lighting?: LightingOptions;
104
109
  }
@@ -131,7 +136,18 @@ export declare class MapController {
131
136
  private commitPending;
132
137
  private destroyed;
133
138
  private firstCommitDone;
134
- private viewChangeTimer;
139
+ private readonly stores;
140
+ /** Origin of the most recent viewport change FRAME — latched through the settle tracker for snapshot reads. */
141
+ private lastViewOrigin;
142
+ /**
143
+ * Shared settle/burst tracker (the `om-view-changed` twin) — ALWAYS armed,
144
+ * onViewChange or not: the burst latch also feeds the viewport snapshot's
145
+ * `origin`, so it must reset on settle even for store-only consumers.
146
+ */
147
+ private readonly viewSettle;
148
+ private onViewChange?;
149
+ /** Per-layer data version counters (`data:<id>` snapshots) — bumped on every data change, monotonic for the controller's lifetime. */
150
+ private readonly dataVersions;
135
151
  private readyFired;
136
152
  private resolveReady;
137
153
  readonly ready: Promise<void>;
@@ -153,6 +169,29 @@ export declare class MapController {
153
169
  */
154
170
  watch(tokens: readonly string[], notify: (ctx: RuntimeContext) => void): () => void;
155
171
  private notifyWatchers;
172
+ /**
173
+ * Per-token `{subscribe, getSnapshot}` store — the framework-free surface
174
+ * for `useSyncExternalStore`, MobX autoruns, Redux listeners, Zustand
175
+ * mirrors (recipes: docs/external-stores.md). Snapshots are cached
176
+ * immutable PLAIN DATA: `getSnapshot()` returns the SAME reference until
177
+ * this token's next change event, and never contains closures — rich
178
+ * reads (`project`, `data()`, `stats()`) stay on `ctx`. Tokens:
179
+ * - "viewport" → ViewportSnapshot (camera + bounds + origin)
180
+ * - "selection" → Selection | null
181
+ * - "layers" → LayerMetaSnapshot[] (what ctx.layers builds)
182
+ * - "data:<layerId>" → {version, rows} — a VERSION STAMP, not the rows
183
+ * (mirroring row arrays into a store per tick is the anti-pattern the
184
+ * Redux performance guidance warns about; fetch rows via ctx.data()).
185
+ * `history` is deliberately NOT a store here — the app owns state on the
186
+ * programmatic front-end. Calling twice for one token returns the
187
+ * identical store object.
188
+ */
189
+ getStore(token: "viewport"): TokenStore<ViewportSnapshot>;
190
+ getStore(token: "selection"): TokenStore<Selection | null>;
191
+ getStore(token: "layers"): TokenStore<LayerMetaSnapshot[]>;
192
+ getStore(token: `data:${string}`): TokenStore<DataVersionSnapshot>;
193
+ getStore(token: string): TokenStore<unknown>;
194
+ private buildViewportSnapshot;
156
195
  /** The RuntimeContext contract (HU5) — identical to what HTML widget scripts receive as `ctx`. */
157
196
  buildCtx(): RuntimeContext;
158
197
  /** Picked object, normalized to flat — same path as OmMapElement (GeoJSON properties lifted; columnar rows materialized). */
@@ -216,6 +255,4 @@ export declare class MapController {
216
255
  /** Projects a lng/lat through the current viewport — undefined before the viewport resolves. */
217
256
  project(lngLat: [number, number]): [number, number] | undefined;
218
257
  destroy(): void;
219
- /** Trailing-debounced onViewChange — see MapControllerOptions.onViewChange. */
220
- private scheduleViewChange;
221
258
  }
@@ -1,5 +1,5 @@
1
1
  import { w as kt, t as Ni, f as Oi, m as Ri } from "./mgrs-BY9bIvp4.js";
2
- import { an as Fi, ao as zi, ap as Ks, aq as Ws, ar as Di, b as Bi, as as Ui, l as es, at as ki, d as qi, au as Ki, av as Wi, aw as Xi, ax as Xs } from "./index-BR1HjJoE.js";
2
+ import { an as Fi, ao as zi, ap as Ks, aq as Ws, ar as Di, b as Bi, as as Ui, l as es, at as ki, d as qi, au as Ki, av as Wi, aw as Xi, ax as Xs } from "./index-BoxZ3Mec.js";
3
3
  function Hi(t, e, s) {
4
4
  const { projectedCorners: i } = e, { topLeft: a, topRight: r, bottomRight: o, bottomLeft: n } = i, h = s(a[0], a[1]), l = s(r[0], r[1]), c = s(o[0], o[1]), u = s(n[0], n[1]), d = [
5
5
  h,
@@ -2377,7 +2377,7 @@ it.set(G.Zstd, () => import("./zstd-jXobGRcq.js").then((t) => t.decode));
2377
2377
  it.set(G.Jpeg, () => Promise.resolve(ke));
2378
2378
  it.set(G.Jpeg6, () => Promise.resolve(ke));
2379
2379
  it.set(G.Webp, () => Promise.resolve(ke));
2380
- it.set(G.Lerc, () => import("./lerc-vBCRLD5s.js").then((t) => t.l).then((t) => t.decode));
2380
+ it.set(G.Lerc, () => import("./lerc-CSc9BX_F.js").then((t) => t.l).then((t) => t.decode));
2381
2381
  async function qe(t, e, s) {
2382
2382
  const i = it.get(e);
2383
2383
  if (!i)
package/dist/react.js CHANGED
@@ -1,40 +1,40 @@
1
- var J = Object.defineProperty;
2
- var Q = (e, t, n) => t in e ? J(e, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : e[t] = n;
3
- var A = (e, t, n) => Q(e, typeof t != "symbol" ? t + "" : t, n);
4
- import { jsxs as U, jsx as C } from "react/jsx-runtime";
5
- import { createContext as X, useContext as Y, forwardRef as Z, useRef as f, useState as k, useCallback as ee, useMemo as I, useLayoutEffect as te, useEffect as g, useImperativeHandle as re } from "react";
6
- import { MapController as ne } from "@nika-js/onlymap";
7
- import { createPortal as B } from "react-dom";
8
- const H = X(null);
1
+ var U = Object.defineProperty;
2
+ var X = (e, t, s) => t in e ? U(e, t, { enumerable: !0, configurable: !0, writable: !0, value: s }) : e[t] = s;
3
+ var A = (e, t, s) => X(e, typeof t != "symbol" ? t + "" : t, s);
4
+ import { jsxs as Y, jsx as P } from "react/jsx-runtime";
5
+ import { createContext as Z, useContext as ee, forwardRef as te, useRef as m, useState as W, useCallback as re, useMemo as T, useLayoutEffect as ne, useEffect as p, useImperativeHandle as se, useSyncExternalStore as H } from "react";
6
+ import { MapController as oe, isStoreToken as le } from "@nika-js/onlymap";
7
+ import { createPortal as G } from "react-dom";
8
+ const J = Z(null);
9
9
  function K(e) {
10
- const t = Y(H);
10
+ const t = ee(J);
11
11
  if (!t) throw new Error(`[onlymapjs] ${e} must be rendered inside <OmMap>.`);
12
12
  return t;
13
13
  }
14
14
  function D(e, t) {
15
15
  if (e === t) return !0;
16
- const n = Object.keys(e ?? {}), r = Object.keys(t ?? {});
17
- return n.length !== r.length ? !1 : n.every((l) => e[l] === t[l]);
16
+ const s = Object.keys(e ?? {}), r = Object.keys(t ?? {});
17
+ return s.length !== r.length ? !1 : s.every((a) => e[a] === t[a]);
18
18
  }
19
- function oe(e, t) {
20
- const { props: n, updateTriggers: r, ...l } = e, { props: a, updateTriggers: u, ...i } = t;
21
- return D(l, i) && D(n, a) && D(r, u);
19
+ function ie(e, t) {
20
+ const { props: s, updateTriggers: r, ...a } = e, { props: u, updateTriggers: c, ...o } = t;
21
+ return D(a, o) && D(s, u) && D(r, c);
22
22
  }
23
- class se {
23
+ class ae {
24
24
  constructor(t) {
25
25
  A(this, "descriptors", /* @__PURE__ */ new Map());
26
26
  this.controller = t;
27
27
  }
28
28
  upsert(t) {
29
- const n = this.descriptors.get(t.id);
30
- n && oe(n, t) || (this.descriptors.set(t.id, t), this.controller.setLayers([...this.descriptors.values()]));
29
+ const s = this.descriptors.get(t.id);
30
+ s && ie(s, t) || (this.descriptors.set(t.id, t), this.controller.setLayers([...this.descriptors.values()]));
31
31
  }
32
32
  remove(t) {
33
33
  this.descriptors.delete(t) && this.controller.setLayers([...this.descriptors.values()]);
34
34
  }
35
35
  }
36
36
  const q = ["top-left", "top-right", "bottom-left", "bottom-right"];
37
- function le(e) {
37
+ function ce(e) {
38
38
  return {
39
39
  position: "absolute",
40
40
  [e.startsWith("top") ? "top" : "bottom"]: 10,
@@ -49,102 +49,103 @@ function le(e) {
49
49
  pointerEvents: "none"
50
50
  };
51
51
  }
52
- function ie(e, t) {
53
- return Object.entries(t).some(([n, r]) => r !== void 0 && e[n] !== r);
52
+ function ue(e, t) {
53
+ return Object.entries(t).some(([s, r]) => r !== void 0 && e[s] !== r);
54
54
  }
55
- const he = Z(function(t, n) {
56
- const { center: r, zoom: l, pitch: a, bearing: u, basemap: i, basemapKey: c, attribution: y, headless: m, className: O, style: z, children: R } = t, d = f(null), [o, T] = k(null), [p, M] = k(null), [j, L] = k({
55
+ const ve = te(function(t, s) {
56
+ const { center: r, zoom: a, pitch: u, bearing: c, basemap: o, basemapKey: l, attribution: d, headless: f, className: w, style: L, children: b } = t, h = m(null), [n, S] = W(null), [g, C] = W(null), [O, E] = W({
57
57
  "top-left": null,
58
58
  "top-right": null,
59
59
  "bottom-left": null,
60
60
  "bottom-right": null
61
- }), v = ee((s) => {
62
- M((h) => h === s ? h : s);
63
- }, []), w = I(
61
+ }), R = re((i) => {
62
+ C((y) => y === i ? y : i);
63
+ }, []), j = T(
64
64
  () => Object.fromEntries(
65
- q.map((s) => [
66
- s,
67
- (h) => {
68
- L((x) => x[s] === h ? x : { ...x, [s]: h });
65
+ q.map((i) => [
66
+ i,
67
+ (y) => {
68
+ E((M) => M[i] === y ? M : { ...M, [i]: y });
69
69
  }
70
70
  ])
71
71
  ),
72
72
  []
73
- ), S = f({ center: r, zoom: l, pitch: a, bearing: u });
74
- S.current = { center: r, zoom: l, pitch: a, bearing: u };
75
- const b = f(t.onReady);
76
- b.current = t.onReady;
77
- const E = f(t.onViewStateChange);
78
- E.current = t.onViewStateChange;
79
- const P = f(t.onRuntimeError);
80
- P.current = t.onRuntimeError;
81
- const N = f(i);
82
- N.current = i;
83
- const V = f(void 0), G = m ? JSON.stringify(m) : "";
84
- te(() => {
85
- if (!d.current) return;
86
- const s = S.current;
87
- V.current = N.current;
88
- const h = new ne(d.current, {
89
- center: s.center,
90
- zoom: s.zoom,
91
- pitch: s.pitch,
92
- bearing: s.bearing,
93
- basemap: N.current,
94
- basemapKey: c,
95
- attribution: y,
96
- headless: m ? m === !0 ? {} : m : void 0,
97
- onRuntimeError: (F) => {
98
- var $;
99
- return ($ = P.current) == null ? void 0 : $.call(P, F);
73
+ ), k = m({ center: r, zoom: a, pitch: u, bearing: c });
74
+ k.current = { center: r, zoom: a, pitch: u, bearing: c };
75
+ const x = m(t.onReady);
76
+ x.current = t.onReady;
77
+ const v = m(t.onViewStateChange);
78
+ v.current = t.onViewStateChange;
79
+ const F = m(t.onRuntimeError);
80
+ F.current = t.onRuntimeError;
81
+ const $ = m(o);
82
+ $.current = o;
83
+ const z = m(void 0), Q = f ? JSON.stringify(f) : "";
84
+ ne(() => {
85
+ if (!h.current) return;
86
+ const i = k.current;
87
+ z.current = $.current;
88
+ const y = new oe(h.current, {
89
+ center: i.center,
90
+ zoom: i.zoom,
91
+ pitch: i.pitch,
92
+ bearing: i.bearing,
93
+ basemap: $.current,
94
+ basemapKey: l,
95
+ attribution: d,
96
+ headless: f ? f === !0 ? {} : f : void 0,
97
+ onRuntimeError: (I) => {
98
+ var _;
99
+ return (_ = F.current) == null ? void 0 : _.call(F, I);
100
100
  }
101
101
  });
102
- T(h);
103
- let x = !0;
104
- return h.ready.then(() => {
105
- var F;
106
- x && ((F = b.current) == null || F.call(b));
102
+ S(y);
103
+ let M = !0;
104
+ return y.ready.then(() => {
105
+ var I;
106
+ M && ((I = x.current) == null || I.call(x));
107
107
  }), () => {
108
- x = !1, T(null), h.destroy();
108
+ M = !1, S(null), y.destroy();
109
109
  };
110
- }, [G]), g(() => {
111
- !o || V.current === i || (V.current = i, o.setBasemap(i ?? null));
112
- }, [o, i]), re(n, () => o, [o]), g(() => {
113
- if (!o) return;
114
- const s = { longitude: r == null ? void 0 : r[0], latitude: r == null ? void 0 : r[1], zoom: l, pitch: a, bearing: u };
115
- ie(o.getViewState(), s) && o.setView(s);
116
- }, [o, r == null ? void 0 : r[0], r == null ? void 0 : r[1], l, a, u]), g(() => {
117
- if (o)
118
- return o.watch(["viewport"], () => {
119
- var s;
120
- return (s = E.current) == null ? void 0 : s.call(E, o.getViewState());
110
+ }, [Q]), p(() => {
111
+ !n || z.current === o || (z.current = o, n.setBasemap(o ?? null));
112
+ }, [n, o]), se(s, () => n, [n]), p(() => {
113
+ if (!n) return;
114
+ const i = { longitude: r == null ? void 0 : r[0], latitude: r == null ? void 0 : r[1], zoom: a, pitch: u, bearing: c };
115
+ ue(n.getViewState(), i) && n.setView(i);
116
+ }, [n, r == null ? void 0 : r[0], r == null ? void 0 : r[1], a, u, c]), p(() => {
117
+ var i;
118
+ if (n)
119
+ return (i = v.current) == null || i.call(v, n.getViewState()), n.getStore("viewport").subscribe(() => {
120
+ var y;
121
+ return (y = v.current) == null ? void 0 : y.call(v, n.getViewState());
121
122
  });
122
- }, [o]);
123
- const W = I(() => o ? new se(o) : null, [o]), _ = I(
124
- () => o && W && p ? { controller: o, layers: W, overlayPane: p, corners: j } : null,
125
- [o, W, p, j]
123
+ }, [n]);
124
+ const N = T(() => n ? new ae(n) : null, [n]), V = T(
125
+ () => n && N && g ? { controller: n, layers: N, overlayPane: g, corners: O } : null,
126
+ [n, N, g, O]
126
127
  );
127
- return /* @__PURE__ */ U("div", { className: O, style: { position: "relative", overflow: "hidden", ...z }, children: [
128
- /* @__PURE__ */ C("div", { ref: d, style: { position: "absolute", inset: 0 } }),
129
- /* @__PURE__ */ C(
128
+ return /* @__PURE__ */ Y("div", { className: w, style: { position: "relative", overflow: "hidden", ...L }, children: [
129
+ /* @__PURE__ */ P("div", { ref: h, style: { position: "absolute", inset: 0 } }),
130
+ /* @__PURE__ */ P(
130
131
  "div",
131
132
  {
132
- ref: v,
133
+ ref: R,
133
134
  style: { position: "absolute", top: 0, left: 0, zIndex: 10, pointerEvents: "none" }
134
135
  }
135
136
  ),
136
- q.map((s) => /* @__PURE__ */ C(
137
+ q.map((i) => /* @__PURE__ */ P(
137
138
  "div",
138
139
  {
139
- "data-om-widget-corner": s,
140
- ref: w[s],
141
- style: le(s)
140
+ "data-om-widget-corner": i,
141
+ ref: j[i],
142
+ style: ce(i)
142
143
  },
143
- s
144
+ i
144
145
  )),
145
- _ && /* @__PURE__ */ C(H.Provider, { value: _, children: R })
146
+ V && /* @__PURE__ */ P(J.Provider, { value: V, children: b })
146
147
  ] });
147
- }), ae = /* @__PURE__ */ new Set([
148
+ }), fe = /* @__PURE__ */ new Set([
148
149
  "id",
149
150
  "type",
150
151
  "data",
@@ -164,11 +165,11 @@ const he = Z(function(t, n) {
164
165
  "onClick",
165
166
  "onHover"
166
167
  ]);
167
- function ge(e) {
168
- const { controller: t, layers: n } = K("OmLayer"), { id: r } = e, l = {};
169
- for (const [c, y] of Object.entries(e))
170
- !ae.has(c) && y !== void 0 && (l[c] = y);
171
- const a = {
168
+ function pe(e) {
169
+ const { controller: t, layers: s } = K("OmLayer"), { id: r } = e, a = {};
170
+ for (const [l, d] of Object.entries(e))
171
+ !fe.has(l) && d !== void 0 && (a[l] = d);
172
+ const u = {
172
173
  id: r,
173
174
  type: e.type,
174
175
  data: e.data,
@@ -185,34 +186,37 @@ function ge(e) {
185
186
  refresh: e.refresh,
186
187
  terrain: e.terrain,
187
188
  updateTriggers: e.updateTriggers,
188
- props: l
189
+ props: a
189
190
  };
190
- g(() => {
191
- n.upsert(a);
192
- }), g(() => () => n.remove(r), [n, r]);
193
- const u = f(e.onClick);
194
- u.current = e.onClick;
195
- const i = f(e.onHover);
196
- return i.current = e.onHover, g(
197
- () => t.watchPicks((c) => {
198
- var y, m, O;
199
- if (c === null) {
200
- (y = i.current) == null || y.call(i, null);
191
+ p(() => {
192
+ s.upsert(u);
193
+ }), p(() => () => s.remove(r), [s, r]);
194
+ const c = m(e.onClick);
195
+ c.current = e.onClick;
196
+ const o = m(e.onHover);
197
+ return o.current = e.onHover, p(
198
+ () => t.watchPicks((l) => {
199
+ var d, f, w;
200
+ if (l === null) {
201
+ (d = o.current) == null || d.call(o, null);
201
202
  return;
202
203
  }
203
- c.layerId === r && (c.type === "click" ? (m = u.current) == null || m.call(u, c) : (O = i.current) == null || O.call(i, c));
204
+ l.layerId === r && (l.type === "click" ? (f = c.current) == null || f.call(c, l) : (w = o.current) == null || w.call(o, l));
204
205
  }),
205
206
  [t, r]
206
207
  ), null;
207
208
  }
208
- function ve({ position: e = "top-left", className: t, style: n, children: r }) {
209
- const { corners: l } = K("OmWidget"), a = l[e];
210
- return a ? B(
211
- /* @__PURE__ */ C("div", { className: t, style: { pointerEvents: "auto", ...n }, children: r }),
212
- a
209
+ function Se({ position: e = "top-left", className: t, style: s, children: r }) {
210
+ const { corners: a } = K("OmWidget"), u = a[e];
211
+ return u ? G(
212
+ /* @__PURE__ */ P("div", { className: t, style: { pointerEvents: "auto", ...s }, children: r }),
213
+ u
213
214
  ) : null;
214
215
  }
215
- const ce = {
216
+ function B(e, t) {
217
+ return !t || (e == null ? void 0 : e.layerId) === t;
218
+ }
219
+ const de = {
216
220
  "top-left": "translate(0, 0)",
217
221
  "top-center": "translate(-50%, 0)",
218
222
  "top-right": "translate(-100%, 0)",
@@ -223,36 +227,52 @@ const ce = {
223
227
  "bottom-center": "translate(-50%, -100%)",
224
228
  "bottom-right": "translate(-100%, -100%)"
225
229
  };
226
- function be(e) {
227
- const { controller: t, overlayPane: n } = K("OmOverlay"), r = f(null), l = f(void 0), [a, u] = k(null), i = f(e);
228
- i.current = e, g(
229
- () => t.registerOverlay((R, d) => {
230
- const o = r.current;
231
- if (!o) return;
232
- const { anchor: T, anchorFrom: p, layer: M, visible: j = !0, anchorOffset: L } = i.current;
233
- let v;
234
- if (p === "selection" ? ((!M || (d == null ? void 0 : d.layerId) === M) && (l.current = (d == null ? void 0 : d.coordinate) ?? void 0, u(d)), v = l.current) : v = T, !j || !v || !R) {
235
- o.style.visibility = "hidden";
230
+ function Oe(e) {
231
+ const { controller: t, overlayPane: s } = K("OmOverlay"), r = m(null), a = m(void 0), u = m(e);
232
+ u.current = e;
233
+ const c = T(() => {
234
+ const b = t.getStore("selection");
235
+ let h, n, S = null;
236
+ return {
237
+ subscribe: b.subscribe,
238
+ getSnapshot: () => {
239
+ const g = b.getSnapshot(), { anchorFrom: C, layer: O } = u.current, E = `${C ?? ""}|${O ?? ""}`;
240
+ return (g !== h || E !== n) && (h = g, n = E, C === "selection" && B(g, O) && (S = g)), S;
241
+ }
242
+ };
243
+ }, [t]), o = H(
244
+ c.subscribe,
245
+ c.getSnapshot,
246
+ c.getSnapshot
247
+ );
248
+ p(
249
+ () => t.registerOverlay((b, h) => {
250
+ const n = r.current;
251
+ if (!n) return;
252
+ const { anchor: S, anchorFrom: g, layer: C, visible: O = !0, anchorOffset: E } = u.current;
253
+ let R;
254
+ if (g === "selection" ? (B(h, C) && (a.current = (h == null ? void 0 : h.coordinate) ?? void 0), R = a.current) : R = S, !O || !R || !b) {
255
+ n.style.visibility = "hidden";
236
256
  return;
237
257
  }
238
- const [w, S, b] = R.project([v[0], v[1], 0]);
239
- if (!(w >= 0 && w <= R.width && S >= 0 && S <= R.height) || b !== void 0 && b < 0) {
240
- o.style.visibility = "hidden";
258
+ const [j, k, x] = b.project([R[0], R[1], 0]);
259
+ if (!(j >= 0 && j <= b.width && k >= 0 && k <= b.height) || x !== void 0 && x < 0) {
260
+ n.style.visibility = "hidden";
241
261
  return;
242
262
  }
243
- o.style.visibility = "visible", o.style.transform = `translate(${w}px, ${S}px) ${ce[L ?? "bottom-center"]}`;
263
+ n.style.visibility = "visible", n.style.transform = `translate(${j}px, ${k}px) ${de[E ?? "bottom-center"]}`;
244
264
  }),
245
265
  [t]
246
- ), g(() => {
266
+ ), p(() => {
247
267
  t.refreshOverlays();
248
268
  });
249
- const { children: c, className: y, style: m, interactive: O = !0 } = e, z = typeof c == "function" ? c(a) : c;
250
- return B(
251
- /* @__PURE__ */ C(
269
+ const { children: l, className: d, style: f, interactive: w = !0 } = e, L = typeof l == "function" ? l(o) : l;
270
+ return G(
271
+ /* @__PURE__ */ P(
252
272
  "div",
253
273
  {
254
274
  ref: r,
255
- className: y,
275
+ className: d,
256
276
  style: {
257
277
  position: "absolute",
258
278
  top: 0,
@@ -260,23 +280,38 @@ function be(e) {
260
280
  visibility: "hidden",
261
281
  // hidden until the first flush positions it — never flashes at the origin
262
282
  willChange: "transform",
263
- pointerEvents: O ? "auto" : "none",
264
- ...m
283
+ pointerEvents: w ? "auto" : "none",
284
+ ...f
265
285
  },
266
- children: z
286
+ children: L
267
287
  }
268
288
  ),
269
- n
289
+ s
270
290
  );
271
291
  }
272
- function Oe(e = []) {
273
- const { controller: t } = K("useOmMap"), n = e.join(" "), r = I(() => n ? n.split(" ") : [], [n]), [l, a] = k(() => t.buildCtx());
274
- return g(() => t.watch(r, a), [t, r]), l;
292
+ function Re(e = []) {
293
+ const { controller: t } = K("useOmMap"), s = e.join(" "), r = T(() => {
294
+ const c = (s ? s.split(" ") : []).flatMap((l) => le(l) ? [t.getStore(l)] : (console.warn(`[onlymapjs] useOmMap: "${l}" is not a watch token on the React front-end — expected viewport, selection, layers, or data:<layerId>.`), []));
295
+ let o;
296
+ return {
297
+ subscribe: (l) => {
298
+ const d = c.map((f) => f.subscribe(l));
299
+ return () => {
300
+ for (const f of d) f();
301
+ };
302
+ },
303
+ getSnapshot: () => {
304
+ const l = c.map((d) => d.getSnapshot());
305
+ return (!o || l.some((d, f) => d !== o[f])) && (o = l), o;
306
+ }
307
+ };
308
+ }, [t, s]), a = H(r.subscribe, r.getSnapshot, r.getSnapshot);
309
+ return T(() => t.buildCtx(), [t, a]);
275
310
  }
276
311
  export {
277
- ge as OmLayer,
278
- he as OmMap,
279
- be as OmOverlay,
280
- ve as OmWidget,
281
- Oe as useOmMap
312
+ pe as OmLayer,
313
+ ve as OmMap,
314
+ Oe as OmOverlay,
315
+ Se as OmWidget,
316
+ Re as useOmMap
282
317
  };
@@ -1,5 +1,6 @@
1
1
  import { type LightingIR } from "./scene-lighting";
2
2
  import { type TerrainIR } from "./terrain";
3
+ import type { ViewOrigin } from "./external-store";
3
4
  import type { LayerIR } from "./ir";
4
5
  import { type Selection } from "./selection";
5
6
  import type { ValidationEntry } from "./validation";
@@ -25,8 +26,14 @@ export interface CameraOptions {
25
26
  curve?: boolean;
26
27
  }
27
28
  export interface RuntimeCoreCallbacks {
28
- /** Fired whenever the view state changes (pan/zoom/pitch/bearing) — a notification, not a value. */
29
- onViewportChange?: () => void;
29
+ /**
30
+ * Fired whenever the view state changes (pan/zoom/pitch/bearing) a
31
+ * notification, not a value. `origin` (spec: "External-Store Contract")
32
+ * distinguishes canvas gestures ("user") from camera APIs / actions /
33
+ * transition frames ("programmatic") — the mapbox `originalEvent`
34
+ * convention, the echo-loop half of two-way store binding.
35
+ */
36
+ onViewportChange?: (origin: ViewOrigin) => void;
30
37
  onSelectionChange?: (selection: Selection | null) => void;
31
38
  /** Fired once the map (deck.gl standalone, or the basemap) has finished its async init (spec: Behavior Engine "load" event). */
32
39
  onLoad?: () => void;
package/dist/version.d.ts CHANGED
@@ -5,4 +5,4 @@
5
5
  * the build rootDir, and a `define` would need repeating across vite/vitest/
6
6
  * vite-node configs.
7
7
  */
8
- export declare const LIBRARY_VERSION = "0.3.4";
8
+ export declare const LIBRARY_VERSION = "0.4.0";