@nika-js/onlymap 0.4.1 → 0.4.3

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 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 518 unit/behavioral tests and 41 Playwright GPU tests.
72
+ Then `npx @nika-js/onlymap init` wires up VS Code IntelliSense and `!`-prefixed manifest snippets for your project. The library ships with 528 unit/behavioral tests and 45 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
 
@@ -81,7 +81,7 @@ A handful of elements, one rule: **attributes are kebab-case versions of deck.gl
81
81
  |---|---|
82
82
  | `<om-map>` | The map. `center`, `zoom`, `pitch`, `bearing`; `basemap` takes a free preset (`positron`, `liberty`, `dark-matter`, `osm`, …), a style URL, or `"none"` (standalone canvas) — and switches **live**; `validate` for a live on-page error panel. |
83
83
  | `<om-layer>` | Any of **34 layer types** by name — all of deck.gl's core, geo, aggregation, and mesh layers (Scatterplot, GeoJson, Arc, Path, Heatmap, Hexagon, Trips, Tile, Tile3D, Scenegraph, …) plus the built-in `PopupLayer` for WebGL badges/labels at scale and the native `COGLayer` for GeoTIFF rasters. `id` required; `label`/`color` feed the legend. |
84
- | `<om-widget>` | UI panels. Built-ins: `legend` (symbology-aware: color scales render as gradient ramps or class ranges, categorical ternaries as discrete palettes), `layer-switcher`, `basemap-switcher`, `lighting`, `zoom-controls`, `undo-redo`, `scale-bar`, `attribution`, `filter`, `draw`, `vega-lite` (live charts). Or write your own inline with HTML + a `<script type="om/widget">`. **Placement is managed**: `position` takes one of 8 logical, RTL-aware slots (`top-start`, `top-center`, `top-end`, `center-start`, `center-end`, `bottom-start`, `bottom-center`, `bottom-end`; legacy corner names alias) — same-slot widgets stack with flush edges and a shared gap, `order` sets in-slot ordering, and `position="manual"` opts out entirely (a plain block you style yourself, even outside the map). Themeable from plain page CSS via custom properties: `om-map { --om-widget-bg: #111827; --om-widget-fg: #f9fafb; }` (also `-muted`, `-border`, `-hover-bg`, `-accent`), plus layout tokens (`--om-widget-inset-x/-y`, `--om-widget-gap-x/-y`, `--om-widget-opacity`, `--om-widget-radius`) or the no-CSS sugar `<om-map widget-style="gap:10 opacity:0.9">`. |
84
+ | `<om-widget>` | UI panels. Built-ins: `legend` (symbology-aware: color scales render as gradient ramps or class ranges, categorical ternaries as discrete palettes), `layer-switcher`, `basemap-switcher`, `lighting`, `zoom-controls`, `undo-redo`, `scale-bar`, `attribution`, `filter`, `draw`, `vega-lite` (live charts). Or write your own inline with HTML + a `<script type="om/widget">`. Adjacent compact button widgets (`zoom-controls`, `undo-redo`, `widgets-toggle`) **auto-cluster** into one control group (opt out per widget with `cluster="false"`), and `<om-map widgets-hidden>` / the `set-widgets-visible` action / `<om-widget type="widgets-toggle">` hide all chrome without destroying it — attribution never hides. **Placement is managed**: `position` takes one of 8 logical, RTL-aware slots (`top-start`, `top-center`, `top-end`, `center-start`, `center-end`, `bottom-start`, `bottom-center`, `bottom-end`; legacy corner names alias) — same-slot widgets stack with flush edges and a shared gap, `order` sets in-slot ordering, and `position="manual"` opts out entirely (a plain block you style yourself, even outside the map). A slot dims automatically while an open popup covers it (`widgets-dim="off"` to disable). Themeable from plain page CSS via custom properties: `om-map { --om-widget-bg: #111827; --om-widget-fg: #f9fafb; }` (also `-muted`, `-border`, `-hover-bg`, `-accent`), plus layout tokens (`--om-widget-inset-x/-y`, `--om-widget-gap-x/-y`, `--om-widget-opacity`, `--om-widget-radius`) or the no-CSS sugar `<om-map widget-style="gap:10 opacity:0.9">`. |
85
85
  | `<om-overlay>` | Rich HTML anchored to a map location — a static `anchor="[lng, lat]"`, the current selection, or a feature's own geometry via `anchor-layer`/`anchor-feature-id`. `{{field}}` interpolates the picked feature, HTML-escaped by default. |
86
86
  | `<om-behavior>` | Declarative interactions: `on="click|hover|drag|load|data-loaded"` → a named action. |
87
87
  | `<om-story>` | A storyboard: `<om-step>` children fire actions on a timeline. Controlled by the `player` widget, behaviors, or `storyEl.play()/pause()/seek()`. |
@@ -19,7 +19,7 @@ async function v(N = {}) {
19
19
  if (n) {
20
20
  const { createRequire: I } = await import(
21
21
  /*webpackIgnore:true*/
22
- "./lerc-filCwJiM.js"
22
+ "./lerc-DEpGyngt.js"
23
23
  ).then((g) => g._);
24
24
  var O = I(import.meta.url);
25
25
  }
@@ -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-BKiyX-jQ.js";
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-DzyVbu_T.js";
2
2
  const Cd = Math.PI / 180, gy = 180 / Math.PI;
3
3
  function em(Pe, Q = 0) {
4
4
  const ge = Math.min(180, Pe) * Cd;
@@ -28,6 +28,15 @@ import type { Selection } from "../selection";
28
28
  import type { MapViewport } from "../basemap";
29
29
  interface OverlayHost {
30
30
  updatePosition(viewport: MapViewport | undefined, selection: Selection | null): void;
31
+ /**
32
+ * Screen rect for collision-dim (spec: "Overlap avoidance") — non-null
33
+ * only when the overlay is VISIBLE and participates in dimming. Returns
34
+ * null when hidden OR when it's transient runtime chrome (the internal
35
+ * hover tooltip / trace / draw preview): those glide constantly and must
36
+ * not flicker widgets. The map reads this off its registered host set
37
+ * rather than re-querying the DOM.
38
+ */
39
+ getVisibleRect(): DOMRect | null;
31
40
  }
32
41
  export declare class OmMapElement extends HTMLElementBase {
33
42
  private core;
@@ -57,7 +66,12 @@ export declare class OmMapElement extends HTMLElementBase {
57
66
  /** Library-owned stacking layer: one absolutely-positioned flex container per managed slot, created lazily. Rendering plumbing, never authored state. */
58
67
  private widgetLayer;
59
68
  private readonly slotContainers;
60
- /** Is this node the widget layer or one of its slot containers? (Their childList churn is plumbing, not manifest edits.) */
69
+ /**
70
+ * Is this node layout plumbing (the widget layer, slot containers, or
71
+ * cluster wrappers)? Their childList churn is rendering, not manifest
72
+ * edits. om-widget elements themselves are NOT plumbing — their attribute
73
+ * edits stay manifest-visible.
74
+ */
61
75
  private isWidgetLayoutPlumbing;
62
76
  /**
63
77
  * Places an <om-widget> into its managed slot container (creating layer/
@@ -71,6 +85,32 @@ export declare class OmMapElement extends HTMLElementBase {
71
85
  /** The widget layer div — lazy, shared by slot containers AND the layout tokens (both lanes put tokens on the layer, never on an authored element). */
72
86
  private ensureWidgetLayer;
73
87
  slotWidgetInternal(el: HTMLElement, slot: WidgetSlot): void;
88
+ private clusterPassPending;
89
+ /** Re-entry guard: the pass's own reparenting fires connect callbacks that call back into scheduleClusterPass — self-inflicted, never re-schedule. */
90
+ private inClusterPass;
91
+ /** Microtask-coalesced: one pass per placement burst (boot slots N widgets). */
92
+ private scheduleClusterPass;
93
+ /**
94
+ * Re-derives cluster wrappers in every slot container: runs of ≥2
95
+ * adjacent compact widgets (in VISUAL order — `order` attr, then DOM)
96
+ * merge into one `data-om-cluster` wrapper div carrying the group's
97
+ * radius/shadow/divider look; the widgets' own shells flatten via the
98
+ * `clustered` class they toggle at render. Unwrap-then-rewrap per pass:
99
+ * wrappers are cheap plumbing divs, and idempotence beats bookkeeping.
100
+ * Members hidden by hide-all are excluded — a hidden widget can't cluster,
101
+ * so a group never leaves a ghost wrapper pill around invisible members.
102
+ * The re-entry guard is inline (the pass's own reparenting fires connect
103
+ * callbacks that call back into scheduleClusterPass — self-inflicted).
104
+ */
105
+ private runClusterPass;
106
+ /**
107
+ * `widgets-hidden` — visibility:hidden + pointer-events:none on every
108
+ * widget EXCEPT attribution and the toggle itself (attribution never
109
+ * hides — license compliance). Never removal: an open listbox or
110
+ * mid-scrub slider survives. Inline styles are history-excluded plumbing.
111
+ */
112
+ private applyWidgetsHidden;
113
+ private applyHiddenToWidget;
74
114
  /** Custom properties applied from widget-style — tracked so an edit reverts removed pairs to their defaults (CSSStyleDeclaration iteration of custom props is not portable). */
75
115
  private appliedWidgetStyleProps;
76
116
  /**
@@ -94,6 +134,17 @@ export declare class OmMapElement extends HTMLElementBase {
94
134
  * multiple viewport/selection changes per frame into one DOM pass).
95
135
  */
96
136
  private scheduleOverlayFlush;
137
+ /**
138
+ * Dynamic overlap avoidance (spec: "Widget Layout Manager / Overlap
139
+ * avoidance") — a slot container whose rect intersects an OPEN
140
+ * `<om-overlay>` popup is dimmed (`--om-widget-opacity-dimmed`, default
141
+ * 0.35) rather than repositioned. Own overlays only (issue Q1: foreign
142
+ * absolutely-positioned DOM is unknowable). `widgets-dim="off"` opts out.
143
+ * Real-geometry only — verified behaviorally in the layout audit, never
144
+ * modeled (headless has no rects; this no-ops there).
145
+ */
146
+ private dimSlotsAgainstOverlays;
147
+ private slotHasNeverHidesWidget;
97
148
  emit(event: string, payload?: Record<string, unknown>): void;
98
149
  /** Recenters (and optionally rezooms) the map — instant, not an animated fly. */
99
150
  flyTo(coords: [number, number], zoom?: number, opts?: {
@@ -78,5 +78,13 @@ export declare class OmOverlayElement extends HTMLElementBase {
78
78
  * static `anchor="[lng,lat]"`), culls off-screen/behind-camera anchors,
79
79
  * and re-interpolates `{{field}}` tokens against the current selection.
80
80
  */
81
+ /**
82
+ * Screen rect for collision-dim (spec: "Overlap avoidance") — null when
83
+ * hidden (via COMPUTED visibility, so author CSS/class hiding counts too,
84
+ * not just our inline write) or when this is transient runtime chrome
85
+ * (`__onlymapjs-` tooltip/trace/draw preview, which glides on hover and
86
+ * must not flicker widgets). Only stable author popups drive dimming.
87
+ */
88
+ getVisibleRect(): DOMRect | null;
81
89
  updatePosition(viewport: MapViewport | undefined, selection: Selection | null): void;
82
90
  }
@@ -15,9 +15,9 @@ interface TagData {
15
15
  }[];
16
16
  }
17
17
  /** Hand-authored action list — validated against hasAction() in html-data.test.ts. */
18
- export declare const BUILTIN_ACTIONS: readonly ["toggle-layer", "zoom-in", "zoom-out", "filter-layer", "highlight-feature", "fade", "pulse", "populate", "trace", "story-play", "story-pause", "story-seek", "fly-to", "zoom-to-feature", "show-overlay", "hide-overlay", "show-tooltip", "hide-tooltip", "draw-mode", "draw-commit", "draw-cancel", "draw-delete", "draw-clear", "draw-config", "draw-save", "set-basemap", "set-lighting", "set-terrain", "undo", "redo"];
18
+ export declare const BUILTIN_ACTIONS: readonly ["toggle-layer", "zoom-in", "zoom-out", "filter-layer", "highlight-feature", "fade", "pulse", "populate", "trace", "story-play", "story-pause", "story-seek", "fly-to", "zoom-to-feature", "show-overlay", "hide-overlay", "show-tooltip", "hide-tooltip", "draw-mode", "draw-commit", "draw-cancel", "draw-delete", "draw-clear", "draw-config", "draw-save", "set-basemap", "set-lighting", "set-terrain", "set-widgets-visible", "undo", "redo"];
19
19
  /** Hand-authored widget types — validated against the widget registry in html-data.test.ts. */
20
- export declare const BUILTIN_WIDGETS: readonly ["legend", "layer-switcher", "zoom-controls", "scale-bar", "attribution", "filter", "vega-lite", "player", "draw", "basemap-switcher", "lighting", "undo-redo"];
20
+ export declare const BUILTIN_WIDGETS: readonly ["legend", "layer-switcher", "zoom-controls", "scale-bar", "attribution", "filter", "vega-lite", "player", "draw", "basemap-switcher", "lighting", "undo-redo", "widgets-toggle"];
21
21
  export declare function buildHtmlCustomData(): {
22
22
  version: number;
23
23
  tags: TagData[];
@@ -1,4 +1,4 @@
1
- import { ai as Bt, ak as Xt, al as Yt, am as Us } from "./index-BKiyX-jQ.js";
1
+ import { ai as Bt, ak as Xt, al as Yt, am as Us } from "./index-DzyVbu_T.js";
2
2
  import { w as gs, t as Hs, f as Ws, m as Qs } from "./mgrs-BY9bIvp4.js";
3
3
  import { c as Xs } from "./convert-arrow-schema-DrAihRf9.js";
4
4
  import { y as Ys, A as bt, a as Ks, z as Vs, R as Zs } from "./recordbatch-Bpc0uxFn.js";
@@ -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-BKiyX-jQ.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-BKiyX-jQ.js";
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-DzyVbu_T.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-DzyVbu_T.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 };
@@ -1,4 +1,4 @@
1
- import { ae as Be, af as le, ag as ce, ah as De, ai as Oe, aj as ke } from "./index-BKiyX-jQ.js";
1
+ import { ae as Be, af as le, ag as ce, ah as De, ai as Oe, aj as ke } from "./index-DzyVbu_T.js";
2
2
  import { y as ve, a as he, z as G, R as J, A as ue, C as Fe, F as je, n as xe, S as Ie, E as Ne } from "./recordbatch-Bpc0uxFn.js";
3
3
  import { g as V, a as fe, b as Le, c as Ue, d as Me, e as qe, m as Ve } from "./table-accessors-CYWTzpQI.js";
4
4
  import { c as $e } from "./convert-arrow-schema-DrAihRf9.js";
@@ -1,4 +1,4 @@
1
- import { ac as _t, ad as Ct } from "./index-BKiyX-jQ.js";
1
+ import { ac as _t, ad as Ct } from "./index-DzyVbu_T.js";
2
2
  function Yt(e, r, t = 2, i, o = "xy") {
3
3
  const s = r && r.length, l = s ? r[0] * t : e.length;
4
4
  let c = Ut(e, 0, l, t, !0, i && i[0], o);