@nika-js/onlymap 0.1.1 → 0.2.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.
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Programmatic front-end (spec: HU2 — "one core, two front-ends"). Builds
3
+ * the SAME layer IR the DOM manifest front-end (parse-manifest.ts) produces
4
+ * — from typed descriptors instead of <om-layer> elements, with no DOM
5
+ * manifest in between — and drives the same RuntimeCore reconcile.
6
+ * Frameworks ride this front-end (the React adapter, src/react/), so the
7
+ * DOM-ownership collision (a framework re-asserting its virtual DOM vs.
8
+ * this library's MutationObserver) never arises: there is nothing shared
9
+ * to fight over.
10
+ *
11
+ * What deliberately does NOT exist here: behaviors, <script type="om/widget">
12
+ * blocks, manifest validation, stories, and the draw widget — those are
13
+ * DOM/HTML-native authoring surfaces. A framework expresses the same intent
14
+ * through its own event handlers and state (spec: HU2 "the catch"), reading
15
+ * runtime state through the same RuntimeContext contract widgets get (HU5).
16
+ */
17
+ import { type CameraOptions } from "./runtime-core";
18
+ import type { LayerIR } from "./ir";
19
+ import { type RuntimeContext } from "./ctx";
20
+ import type { Selection } from "./selection";
21
+ import type { ValidationEntry } from "./validation";
22
+ import type { MapViewport } from "./basemap";
23
+ /**
24
+ * A layer, declared as a typed value — the programmatic mirror of one
25
+ * <om-layer> element. Accessors are plain JS functions (no expression
26
+ * language, no trust step — first-party code is trusted by definition).
27
+ */
28
+ export interface LayerDescriptor {
29
+ /** Stable identity — same contract as the `id` attribute. Required. */
30
+ id: string;
31
+ /** deck.gl layer class name, e.g. "ScatterplotLayer". */
32
+ type: string;
33
+ /**
34
+ * Inline rows / GeoJSON / columnar data passed by reference, or a URL
35
+ * string — URLs ride the Data Layer exactly like the `data` attribute
36
+ * (format detection, ws(s):// streams, `refresh` polling, draw: stores).
37
+ * Keep inline references STABLE across commits (memoize in the caller):
38
+ * the `data:<id>` watch tokens diff by reference.
39
+ */
40
+ data?: unknown;
41
+ /** Legend metadata (ctx.layers), same as the label/color attributes. */
42
+ label?: string;
43
+ color?: string;
44
+ /** GPU filter (spec: "Filtering") — mirrors filter-field/filter-range/… */
45
+ filterField?: string;
46
+ filterRange?: [number, number];
47
+ filterSoftRange?: [number, number];
48
+ filterCategoryField?: string;
49
+ filterCategories?: unknown[];
50
+ /** Stream options for ws(s):// data URLs (spec: "Live & Streaming Data"). */
51
+ source?: string;
52
+ key?: string;
53
+ flush?: string;
54
+ /** Polling interval for REST snapshot URLs, e.g. "5s". */
55
+ refresh?: string;
56
+ /**
57
+ * deck.gl updateTriggers, passed through per accessor prop. deck ignores
58
+ * accessor FUNCTION identity by design — supply a value here that changes
59
+ * when an accessor's output changes (the deck-idiomatic contract).
60
+ */
61
+ updateTriggers?: Record<string, unknown>;
62
+ /** deck.gl props (camelCase), accessors as plain functions — passed through. */
63
+ props?: Record<string, unknown>;
64
+ }
65
+ /** Camera state, both an input (options/setView) and an output (getViewState). */
66
+ export interface CameraState {
67
+ longitude: number;
68
+ latitude: number;
69
+ zoom: number;
70
+ pitch: number;
71
+ bearing: number;
72
+ }
73
+ export interface MapControllerOptions {
74
+ center?: [number, number];
75
+ zoom?: number;
76
+ pitch?: number;
77
+ bearing?: number;
78
+ /** Same contract as the `basemap` attribute — "maplibre", a style URL, or omit for standalone deck.gl. */
79
+ basemap?: string;
80
+ /** Headless mode (spec: "Consumer Testing Surface") — no renderer, real projection math; for jsdom/happy-dom tests. */
81
+ headless?: {
82
+ width?: number;
83
+ height?: number;
84
+ };
85
+ /** Runtime error boundary — deck.gl-level failures in the structured validation shape. */
86
+ onRuntimeError?: (entry: ValidationEntry) => void;
87
+ }
88
+ /** Descriptor → layer IR: the programmatic counterpart of one parse-manifest.ts loop iteration. */
89
+ export declare function descriptorToIR(desc: LayerDescriptor, onDataLoaded: () => void): LayerIR | null;
90
+ /** rAF-batched overlay positioning hook — same flush contract as the DOM front-end's OverlayHost. */
91
+ export type OverlayPositioner = (viewport: MapViewport | undefined, selection: Selection | null) => void;
92
+ /**
93
+ * Pick stream for framework event handlers. `null` means the pointer left
94
+ * whatever it was hovering (deck reports an empty pick) — the "hover out"
95
+ * signal; clicks never arrive as null.
96
+ */
97
+ export type PickListener = (selection: Selection | null) => void;
98
+ /**
99
+ * The programmatic map instance — owns a RuntimeCore plus the same
100
+ * orchestration OmMapElement provides around it (layer IR registry, watch
101
+ * pub-sub, selection normalization, overlay flush, readiness), minus every
102
+ * DOM-manifest concern. One controller per map container.
103
+ */
104
+ export declare class MapController {
105
+ private core;
106
+ private mount;
107
+ private layerIRs;
108
+ private descriptors;
109
+ private selection;
110
+ private watchers;
111
+ private pickListeners;
112
+ private overlayFns;
113
+ private overlayFlushPending;
114
+ private commitPending;
115
+ private destroyed;
116
+ private firstCommitDone;
117
+ private readyFired;
118
+ private resolveReady;
119
+ readonly ready: Promise<void>;
120
+ constructor(container: HTMLElement, options?: MapControllerOptions);
121
+ /** Replaces the full layer set (z-order = array order) — descriptors are re-resolved on a microtask-coalesced commit. */
122
+ setLayers(descriptors: readonly LayerDescriptor[]): void;
123
+ /** A snapshot of the current layer IRs — the same shape ctx.layers is built from. */
124
+ getLayers(): readonly LayerIR[];
125
+ private scheduleCommit;
126
+ private commit;
127
+ private checkReady;
128
+ /** Readiness as a poll (the promise form is `ready`). */
129
+ isReady(): boolean;
130
+ /**
131
+ * Subscribes to watch tokens ("viewport", "selection", "layers",
132
+ * "data:<layerId>") — `notify` runs immediately with the current context
133
+ * (initial render parity with widget registration), then on every matching
134
+ * change. Returns the unsubscribe function.
135
+ */
136
+ watch(tokens: readonly string[], notify: (ctx: RuntimeContext) => void): () => void;
137
+ private notifyWatchers;
138
+ /** The RuntimeContext contract (HU5) — identical to what HTML widget scripts receive as `ctx`. */
139
+ buildCtx(): RuntimeContext;
140
+ /** Picked object, normalized to flat — same path as OmMapElement (GeoJSON properties lifted; columnar rows materialized). */
141
+ private resolvePickedObject;
142
+ private handleSelectionChange;
143
+ getSelection(): Selection | null;
144
+ /** Every hover/click pick (null = hover left) — the stream framework adapters route to per-layer onClick/onHover handlers. */
145
+ watchPicks(listener: PickListener): () => void;
146
+ /**
147
+ * Synthetic pick injection (spec: "Consumer Testing Surface") — lands on
148
+ * the SAME selection path real deck.gl picks arrive through, so
149
+ * flattening, watcher notification, and overlay flush are same-path by
150
+ * construction.
151
+ */
152
+ injectPick(selection: Selection | null): void;
153
+ /**
154
+ * Registers an rAF-batched positioner: called with the fresh viewport +
155
+ * selection whenever either may have moved (spec: "Overlay Renderer" —
156
+ * one flush per frame across all overlays). Returns unsubscribe.
157
+ */
158
+ registerOverlay(positioner: OverlayPositioner): () => void;
159
+ /**
160
+ * Requests a re-flush of every registered positioner — for state changes
161
+ * a positioner reads from OUTSIDE the flush arguments (framework props
162
+ * like anchor/visible), which the controller can't observe itself.
163
+ * rAF-coalesced, so calling it eagerly is free.
164
+ */
165
+ refreshOverlays(): void;
166
+ private scheduleOverlayFlush;
167
+ /** Dispatches an action (spec: "Action payload contract") — camera moves, pulse/populate effects, custom registered actions. */
168
+ emit(event: string, payload?: Record<string, unknown>): void;
169
+ flyTo(center: [number, number], zoom?: number, opts?: CameraOptions): void;
170
+ flyToBounds(bounds: [[number, number], [number, number]], padding?: number, opts?: CameraOptions): void;
171
+ setView(partial: Partial<CameraState>, opts?: CameraOptions): void;
172
+ getViewState(): CameraState;
173
+ /** Projects a lng/lat through the current viewport — undefined before the viewport resolves. */
174
+ project(lngLat: [number, number]): [number, number] | undefined;
175
+ destroy(): void;
176
+ }
@@ -0,0 +1,25 @@
1
+ import type { MapController, LayerDescriptor } from "@nika-js/onlymap";
2
+ export type WidgetPosition = "top-left" | "top-right" | "bottom-left" | "bottom-right";
3
+ export interface OmMapContextValue {
4
+ controller: MapController;
5
+ layers: LayerRegistry;
6
+ /** The pane <OmOverlay> portals into — absolutely positioned above the canvas, pointer-events: none. */
7
+ overlayPane: HTMLDivElement;
8
+ /** Corner containers <OmWidget> portals into (filled as OmMap's own DOM mounts). */
9
+ corners: Readonly<Record<WidgetPosition, HTMLDivElement | null>>;
10
+ }
11
+ export declare const OmMapContext: import("react").Context<OmMapContextValue | null>;
12
+ export declare function useOmMapContext(caller: string): OmMapContextValue;
13
+ /**
14
+ * Mount-ordered descriptor registry: one entry per <OmLayer>, insertion
15
+ * order = mount order = deck.gl z-order. Every change hands the full set to
16
+ * MapController.setLayers, which microtask-coalesces commits — N layer
17
+ * updates in one render pass cost one reconcile.
18
+ */
19
+ export declare class LayerRegistry {
20
+ private controller;
21
+ private descriptors;
22
+ constructor(controller: MapController);
23
+ upsert(descriptor: LayerDescriptor): void;
24
+ remove(id: string): void;
25
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @nika-js/onlymap/react — the React adapter (spec: HU2/HU5/HU7).
3
+ *
4
+ * Rides the core's programmatic front-end: components build typed layer
5
+ * descriptors → the same IR → the same reconcile core as the HTML manifest.
6
+ * React never renders <om-*> elements, so React's DOM ownership and the
7
+ * core's MutationObserver never meet. React is a peer dependency of this
8
+ * entry only — importing the core package alone never loads it.
9
+ */
10
+ export { OmMap } from "./om-map";
11
+ export type { OmMapProps, OmMapHandle } from "./om-map";
12
+ export { OmLayer } from "./om-layer";
13
+ export type { OmLayerProps } from "./om-layer";
14
+ export { OmWidget } from "./om-widget";
15
+ export type { OmWidgetProps } from "./om-widget";
16
+ export { OmOverlay } from "./om-overlay";
17
+ export type { OmOverlayProps, AnchorOffset } from "./om-overlay";
18
+ export { useOmMap } from "./use-om-map";
19
+ export type { WidgetPosition } from "./context";
20
+ export type { RuntimeContext, Selection, LayerDescriptor, CameraState, MapController } from "@nika-js/onlymap";
@@ -0,0 +1,38 @@
1
+ import type { Selection } from "@nika-js/onlymap";
2
+ export interface OmLayerProps {
3
+ /** Stable identity — same contract as the `id` attribute. */
4
+ id: string;
5
+ /** deck.gl layer class name, e.g. "ScatterplotLayer". */
6
+ type: string;
7
+ /**
8
+ * Inline rows / GeoJSON / columnar data, or a URL string (formats,
9
+ * ws(s):// streams, `refresh` polling — the full Data Layer). Keep inline
10
+ * references stable across renders (useMemo/useState): `data:<id>` watch
11
+ * tokens diff by reference.
12
+ */
13
+ data?: unknown;
14
+ /** Legend metadata (ctx.layers). */
15
+ label?: string;
16
+ color?: string;
17
+ /** GPU filter (DataFilterExtension) — mirrors filter-field/filter-range. */
18
+ filterField?: string;
19
+ filterRange?: [number, number];
20
+ filterSoftRange?: [number, number];
21
+ filterCategoryField?: string;
22
+ filterCategories?: unknown[];
23
+ /** Stream options for ws(s):// data URLs. `streamKey` is the upsert identity field (the `key` attribute — `key` itself is reserved by React). */
24
+ source?: string;
25
+ streamKey?: string;
26
+ flush?: string;
27
+ /** Polling interval for REST snapshot URLs, e.g. "5s". */
28
+ refresh?: string;
29
+ /** deck.gl updateTriggers — supply a changing value when an accessor's OUTPUT changes (deck ignores function identity). */
30
+ updateTriggers?: Record<string, unknown>;
31
+ /** Click pick on this layer (object flattened/materialized, same as ctx.selection). */
32
+ onClick?: (selection: Selection) => void;
33
+ /** Hover pick on this layer; `null` = the pointer left whatever it was hovering. */
34
+ onHover?: (selection: Selection | null) => void;
35
+ /** Everything else passes through as deck.gl props (camelCase): getPosition, radiusMinPixels, visible, opacity, pickable, … */
36
+ [deckProp: string]: unknown;
37
+ }
38
+ export declare function OmLayer(props: OmLayerProps): null;
@@ -0,0 +1,29 @@
1
+ import type { CSSProperties, ReactNode } from "react";
2
+ import { MapController } from "@nika-js/onlymap";
3
+ import type { CameraState, ValidationEntry } from "@nika-js/onlymap";
4
+ export interface OmMapProps {
5
+ /** Initial camera — later prop changes move the camera (instant; use a ref + flyTo for animated moves). */
6
+ center?: [number, number];
7
+ zoom?: number;
8
+ pitch?: number;
9
+ bearing?: number;
10
+ /** Same contract as the `basemap` attribute — "maplibre", a style URL, or omit for standalone deck.gl. Changing it remounts the renderer. */
11
+ basemap?: string;
12
+ /** Headless mode for tests (spec: "Consumer Testing Surface") — no renderer, real projection math under jsdom/happy-dom. */
13
+ headless?: boolean | {
14
+ width?: number;
15
+ height?: number;
16
+ };
17
+ className?: string;
18
+ style?: CSSProperties;
19
+ children?: ReactNode;
20
+ /** Fired once: renderer initialized + first commit ran + no declared data URL still loading (the `.ready` contract). */
21
+ onReady?: () => void;
22
+ /** Fired on every camera change (pan/zoom/pitch/bearing). */
23
+ onViewStateChange?: (view: CameraState) => void;
24
+ /** Runtime error boundary — deck.gl-level failures in the structured validation shape. */
25
+ onRuntimeError?: (entry: ValidationEntry) => void;
26
+ }
27
+ /** The imperative surface (`ref`) — the MapController itself: flyTo, setView, emit, getLayers, injectPick, ready. */
28
+ export type OmMapHandle = MapController;
29
+ export declare const OmMap: import("react").ForwardRefExoticComponent<OmMapProps & import("react").RefAttributes<MapController>>;
@@ -0,0 +1,27 @@
1
+ import type { CSSProperties, ReactNode } from "react";
2
+ import type { Selection } from "@nika-js/onlymap";
3
+ export type AnchorOffset = "top-left" | "top-center" | "top-right" | "center-left" | "center" | "center-right" | "bottom-left" | "bottom-center" | "bottom-right";
4
+ export interface OmOverlayProps {
5
+ /** Static anchor [lng, lat]. */
6
+ anchor?: [number, number];
7
+ /** Dynamic anchor: follow the current selection (click/hover picks). */
8
+ anchorFrom?: "selection";
9
+ /** Scope a selection-anchored overlay to one layer's picks — non-matching selections don't move or re-render it. */
10
+ layer?: string;
11
+ visible?: boolean;
12
+ /** Attachment point relative to the anchor (default bottom-center: content sits above the point). */
13
+ anchorOffset?: AnchorOffset;
14
+ /**
15
+ * Set false for hover-following content (tooltips): a hoverable overlay
16
+ * sits over the picked point and would steal the pointer from the canvas,
17
+ * flickering the pick on/off (same rationale as the HTML overlay's
18
+ * automatic pointer-events handling — explicit here since JSX
19
+ * interactivity isn't detectable).
20
+ */
21
+ interactive?: boolean;
22
+ className?: string;
23
+ style?: CSSProperties;
24
+ /** Static JSX, or a function of the tracked selection (spec: HU7's `{sel => <QuakeCard feature={sel.object} />}`). */
25
+ children?: ReactNode | ((selection: Selection | null) => ReactNode);
26
+ }
27
+ export declare function OmOverlay(props: OmOverlayProps): ReactNode;
@@ -0,0 +1,9 @@
1
+ import type { CSSProperties, ReactNode } from "react";
2
+ import { type WidgetPosition } from "./context";
3
+ export interface OmWidgetProps {
4
+ position?: WidgetPosition;
5
+ className?: string;
6
+ style?: CSSProperties;
7
+ children?: ReactNode;
8
+ }
9
+ export declare function OmWidget({ position, className, style, children }: OmWidgetProps): ReactNode;
@@ -0,0 +1,2 @@
1
+ import type { RuntimeContext } from "@nika-js/onlymap";
2
+ export declare function useOmMap(watch?: readonly string[]): RuntimeContext;
package/dist/react.js ADDED
@@ -0,0 +1,272 @@
1
+ var q = Object.defineProperty;
2
+ var H = (e, t, n) => t in e ? q(e, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : e[t] = n;
3
+ var D = (e, t, n) => H(e, typeof t != "symbol" ? t + "" : t, n);
4
+ import { jsxs as G, jsx as w } from "react/jsx-runtime";
5
+ import { createContext as J, useContext as B, forwardRef as Q, useRef as d, useState as p, useCallback as U, useMemo as j, useLayoutEffect as X, useImperativeHandle as Y, useEffect as O } from "react";
6
+ import { MapController as Z } from "@nika-js/onlymap";
7
+ import { createPortal as $ } from "react-dom";
8
+ const A = J(null);
9
+ function F(e) {
10
+ const t = B(A);
11
+ if (!t) throw new Error(`[onlymapjs] ${e} must be rendered inside <OmMap>.`);
12
+ return t;
13
+ }
14
+ function L(e, t) {
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]);
18
+ }
19
+ function ee(e, t) {
20
+ const { props: n, updateTriggers: r, ...l } = e, { props: a, updateTriggers: u, ...c } = t;
21
+ return L(l, c) && L(n, a) && L(r, u);
22
+ }
23
+ class te {
24
+ constructor(t) {
25
+ D(this, "descriptors", /* @__PURE__ */ new Map());
26
+ this.controller = t;
27
+ }
28
+ upsert(t) {
29
+ const n = this.descriptors.get(t.id);
30
+ n && ee(n, t) || (this.descriptors.set(t.id, t), this.controller.setLayers([...this.descriptors.values()]));
31
+ }
32
+ remove(t) {
33
+ this.descriptors.delete(t) && this.controller.setLayers([...this.descriptors.values()]);
34
+ }
35
+ }
36
+ const _ = ["top-left", "top-right", "bottom-left", "bottom-right"];
37
+ function re(e) {
38
+ return {
39
+ position: "absolute",
40
+ [e.startsWith("top") ? "top" : "bottom"]: 10,
41
+ [e.endsWith("left") ? "left" : "right"]: 10,
42
+ display: "flex",
43
+ flexDirection: "column",
44
+ gap: 8,
45
+ alignItems: e.endsWith("left") ? "flex-start" : "flex-end",
46
+ zIndex: 20,
47
+ // The container never intercepts the map's pointer events; each
48
+ // <OmWidget> shell re-enables its own.
49
+ pointerEvents: "none"
50
+ };
51
+ }
52
+ function ne(e, t) {
53
+ return Object.entries(t).some(([n, r]) => r !== void 0 && e[n] !== r);
54
+ }
55
+ const fe = Q(function(t, n) {
56
+ const { center: r, zoom: l, pitch: a, bearing: u, basemap: c, headless: i, className: y, style: x, children: C } = t, E = d(null), [s, f] = p(null), [h, I] = p(null), [P, k] = p({
57
+ "top-left": null,
58
+ "top-right": null,
59
+ "bottom-left": null,
60
+ "bottom-right": null
61
+ }), z = U((o) => {
62
+ I((m) => m === o ? m : o);
63
+ }, []), K = j(
64
+ () => Object.fromEntries(
65
+ _.map((o) => [
66
+ o,
67
+ (m) => {
68
+ k((R) => R[o] === m ? R : { ...R, [o]: m });
69
+ }
70
+ ])
71
+ ),
72
+ []
73
+ ), g = d({ center: r, zoom: l, pitch: a, bearing: u });
74
+ g.current = { center: r, zoom: l, pitch: a, bearing: u };
75
+ const v = d(t.onReady);
76
+ v.current = t.onReady;
77
+ const b = d(t.onViewStateChange);
78
+ b.current = t.onViewStateChange;
79
+ const S = d(t.onRuntimeError);
80
+ S.current = t.onRuntimeError;
81
+ const N = i ? JSON.stringify(i) : "";
82
+ X(() => {
83
+ if (!E.current) return;
84
+ const o = g.current, m = new Z(E.current, {
85
+ center: o.center,
86
+ zoom: o.zoom,
87
+ pitch: o.pitch,
88
+ bearing: o.bearing,
89
+ basemap: c,
90
+ headless: i ? i === !0 ? {} : i : void 0,
91
+ onRuntimeError: (M) => {
92
+ var W;
93
+ return (W = S.current) == null ? void 0 : W.call(S, M);
94
+ }
95
+ });
96
+ f(m);
97
+ let R = !0;
98
+ return m.ready.then(() => {
99
+ var M;
100
+ R && ((M = v.current) == null || M.call(v));
101
+ }), () => {
102
+ R = !1, f(null), m.destroy();
103
+ };
104
+ }, [c, N]), Y(n, () => s, [s]), O(() => {
105
+ if (!s) return;
106
+ const o = { longitude: r == null ? void 0 : r[0], latitude: r == null ? void 0 : r[1], zoom: l, pitch: a, bearing: u };
107
+ ne(s.getViewState(), o) && s.setView(o);
108
+ }, [s, r == null ? void 0 : r[0], r == null ? void 0 : r[1], l, a, u]), O(() => {
109
+ if (s)
110
+ return s.watch(["viewport"], () => {
111
+ var o;
112
+ return (o = b.current) == null ? void 0 : o.call(b, s.getViewState());
113
+ });
114
+ }, [s]);
115
+ const T = j(() => s ? new te(s) : null, [s]), V = j(
116
+ () => s && T && h ? { controller: s, layers: T, overlayPane: h, corners: P } : null,
117
+ [s, T, h, P]
118
+ );
119
+ return /* @__PURE__ */ G("div", { className: y, style: { position: "relative", overflow: "hidden", ...x }, children: [
120
+ /* @__PURE__ */ w("div", { ref: E, style: { position: "absolute", inset: 0 } }),
121
+ /* @__PURE__ */ w(
122
+ "div",
123
+ {
124
+ ref: z,
125
+ style: { position: "absolute", top: 0, left: 0, zIndex: 10, pointerEvents: "none" }
126
+ }
127
+ ),
128
+ _.map((o) => /* @__PURE__ */ w(
129
+ "div",
130
+ {
131
+ "data-om-widget-corner": o,
132
+ ref: K[o],
133
+ style: re(o)
134
+ },
135
+ o
136
+ )),
137
+ V && /* @__PURE__ */ w(A.Provider, { value: V, children: C })
138
+ ] });
139
+ }), oe = /* @__PURE__ */ new Set([
140
+ "id",
141
+ "type",
142
+ "data",
143
+ "label",
144
+ "color",
145
+ "filterField",
146
+ "filterRange",
147
+ "filterSoftRange",
148
+ "filterCategoryField",
149
+ "filterCategories",
150
+ "source",
151
+ "streamKey",
152
+ "flush",
153
+ "refresh",
154
+ "updateTriggers",
155
+ "onClick",
156
+ "onHover"
157
+ ]);
158
+ function de(e) {
159
+ const { controller: t, layers: n } = F("OmLayer"), { id: r } = e, l = {};
160
+ for (const [i, y] of Object.entries(e))
161
+ !oe.has(i) && y !== void 0 && (l[i] = y);
162
+ const a = {
163
+ id: r,
164
+ type: e.type,
165
+ data: e.data,
166
+ label: e.label,
167
+ color: e.color,
168
+ filterField: e.filterField,
169
+ filterRange: e.filterRange,
170
+ filterSoftRange: e.filterSoftRange,
171
+ filterCategoryField: e.filterCategoryField,
172
+ filterCategories: e.filterCategories,
173
+ source: e.source,
174
+ key: e.streamKey,
175
+ flush: e.flush,
176
+ refresh: e.refresh,
177
+ updateTriggers: e.updateTriggers,
178
+ props: l
179
+ };
180
+ O(() => {
181
+ n.upsert(a);
182
+ }), O(() => () => n.remove(r), [n, r]);
183
+ const u = d(e.onClick);
184
+ u.current = e.onClick;
185
+ const c = d(e.onHover);
186
+ return c.current = e.onHover, O(
187
+ () => t.watchPicks((i) => {
188
+ var y, x, C;
189
+ if (i === null) {
190
+ (y = c.current) == null || y.call(c, null);
191
+ return;
192
+ }
193
+ i.layerId === r && (i.type === "click" ? (x = u.current) == null || x.call(u, i) : (C = c.current) == null || C.call(c, i));
194
+ }),
195
+ [t, r]
196
+ ), null;
197
+ }
198
+ function ye({ position: e = "top-left", className: t, style: n, children: r }) {
199
+ const { corners: l } = F("OmWidget"), a = l[e];
200
+ return a ? $(
201
+ /* @__PURE__ */ w("div", { className: t, style: { pointerEvents: "auto", ...n }, children: r }),
202
+ a
203
+ ) : null;
204
+ }
205
+ const se = {
206
+ "top-left": "translate(0, 0)",
207
+ "top-center": "translate(-50%, 0)",
208
+ "top-right": "translate(-100%, 0)",
209
+ "center-left": "translate(0, -50%)",
210
+ center: "translate(-50%, -50%)",
211
+ "center-right": "translate(-100%, -50%)",
212
+ "bottom-left": "translate(0, -100%)",
213
+ "bottom-center": "translate(-50%, -100%)",
214
+ "bottom-right": "translate(-100%, -100%)"
215
+ };
216
+ function he(e) {
217
+ const { controller: t, overlayPane: n } = F("OmOverlay"), r = d(null), l = d(void 0), [a, u] = p(null), c = d(e);
218
+ c.current = e, O(
219
+ () => t.registerOverlay((s, f) => {
220
+ const h = r.current;
221
+ if (!h) return;
222
+ const { anchor: I, anchorFrom: P, layer: k, visible: z = !0, anchorOffset: K } = c.current;
223
+ let g;
224
+ if (P === "selection" ? ((!k || (f == null ? void 0 : f.layerId) === k) && (l.current = (f == null ? void 0 : f.coordinate) ?? void 0, u(f)), g = l.current) : g = I, !z || !g || !s) {
225
+ h.style.visibility = "hidden";
226
+ return;
227
+ }
228
+ const [v, b, S] = s.project([g[0], g[1], 0]);
229
+ if (!(v >= 0 && v <= s.width && b >= 0 && b <= s.height) || S !== void 0 && S < 0) {
230
+ h.style.visibility = "hidden";
231
+ return;
232
+ }
233
+ h.style.visibility = "visible", h.style.transform = `translate(${v}px, ${b}px) ${se[K ?? "bottom-center"]}`;
234
+ }),
235
+ [t]
236
+ ), O(() => {
237
+ t.refreshOverlays();
238
+ });
239
+ const { children: i, className: y, style: x, interactive: C = !0 } = e, E = typeof i == "function" ? i(a) : i;
240
+ return $(
241
+ /* @__PURE__ */ w(
242
+ "div",
243
+ {
244
+ ref: r,
245
+ className: y,
246
+ style: {
247
+ position: "absolute",
248
+ top: 0,
249
+ left: 0,
250
+ visibility: "hidden",
251
+ // hidden until the first flush positions it — never flashes at the origin
252
+ willChange: "transform",
253
+ pointerEvents: C ? "auto" : "none",
254
+ ...x
255
+ },
256
+ children: E
257
+ }
258
+ ),
259
+ n
260
+ );
261
+ }
262
+ function me(e = []) {
263
+ const { controller: t } = F("useOmMap"), n = e.join(" "), r = j(() => n ? n.split(" ") : [], [n]), [l, a] = p(() => t.buildCtx());
264
+ return O(() => t.watch(r, a), [t, r]), l;
265
+ }
266
+ export {
267
+ de as OmLayer,
268
+ fe as OmMap,
269
+ he as OmOverlay,
270
+ ye as OmWidget,
271
+ me as useOmMap
272
+ };