@nika-js/onlymap 0.2.0 → 0.2.1
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 +7 -5
- package/dist/{basemap-C0RKcLaL.js → basemap-COurZNDH.js} +2126 -2105
- package/dist/basemap-registry.d.ts +49 -0
- package/dist/basemap.d.ts +22 -2
- package/dist/html-data.d.ts +2 -2
- package/dist/{index-CZf9WlZe.js → index-COu-3-gN.js} +1 -1
- package/dist/{index-DeEur5Xk.js → index-CgyAD98B.js} +1 -1
- package/dist/{index-DnvxPnDF.js → index-CvfuISOc.js} +11661 -11466
- package/dist/{index-BDJ9hHXv.js → index-D-X8KPA1.js} +1 -1
- package/dist/{index-GTGi85ZO.js → index-DuvXK95V.js} +2 -2
- package/dist/index.d.ts +5 -0
- package/dist/onlymapjs.js +20 -17
- package/dist/onlymapjs.umd.cjs +277 -271
- package/dist/programmatic.d.ts +11 -1
- package/dist/react/om-map.d.ts +5 -1
- package/dist/react.js +136 -128
- package/dist/runtime-core.d.ts +34 -1
- package/dist/widget-registry.d.ts +2 -0
- package/docs/basemaps.md +89 -0
- package/docs/react.md +1 -1
- package/llms.txt +3 -3
- package/onlymapjs.html-data.json +57 -1
- package/package.json +1 -1
- package/skills/onlymapjs/SKILL.md +14 -2
- package/skills/onlymapjs/references/react.md +68 -0
- package/skills/onlymapjs/references/syntax.md +5 -1
package/onlymapjs.html-data.json
CHANGED
|
@@ -23,10 +23,53 @@
|
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
"name": "basemap",
|
|
26
|
-
"description": "Basemap
|
|
26
|
+
"description": "Basemap: a registered preset name, \"maplibre\" (demo style), a style URL, or \"none\" (standalone canvas).",
|
|
27
27
|
"values": [
|
|
28
28
|
{
|
|
29
29
|
"name": "maplibre"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"name": "none"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "liberty"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "bright"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "positron"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "dark-matter"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"name": "voyager"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"name": "osm"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "maptiler-streets"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"name": "maptiler-dataviz"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "maptiler-satellite"
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "basemap-key",
|
|
65
|
+
"description": "Provider API key for keyed basemap presets (maptiler-*) — publishable origin-restricted key; or call OmMap.configureBasemap once."
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "attribution",
|
|
69
|
+
"description": "Set \"false\" to opt out of the compact attribution control shown with basemaps.",
|
|
70
|
+
"values": [
|
|
71
|
+
{
|
|
72
|
+
"name": "false"
|
|
30
73
|
}
|
|
31
74
|
]
|
|
32
75
|
},
|
|
@@ -1358,6 +1401,9 @@
|
|
|
1358
1401
|
},
|
|
1359
1402
|
{
|
|
1360
1403
|
"name": "draw-save"
|
|
1404
|
+
},
|
|
1405
|
+
{
|
|
1406
|
+
"name": "set-basemap"
|
|
1361
1407
|
}
|
|
1362
1408
|
]
|
|
1363
1409
|
},
|
|
@@ -1405,6 +1451,9 @@
|
|
|
1405
1451
|
},
|
|
1406
1452
|
{
|
|
1407
1453
|
"name": "draw"
|
|
1454
|
+
},
|
|
1455
|
+
{
|
|
1456
|
+
"name": "basemap-switcher"
|
|
1408
1457
|
}
|
|
1409
1458
|
]
|
|
1410
1459
|
},
|
|
@@ -1483,6 +1532,10 @@
|
|
|
1483
1532
|
"name": "autosave",
|
|
1484
1533
|
"description": "Draw widget: localStorage key — mirrors the sketch and restores it on reload."
|
|
1485
1534
|
},
|
|
1535
|
+
{
|
|
1536
|
+
"name": "options",
|
|
1537
|
+
"description": "Basemap-switcher: space-separated preset names to offer (default: every keyless registered preset)."
|
|
1538
|
+
},
|
|
1486
1539
|
{
|
|
1487
1540
|
"name": "title",
|
|
1488
1541
|
"description": "Widget heading."
|
|
@@ -1613,6 +1666,9 @@
|
|
|
1613
1666
|
},
|
|
1614
1667
|
{
|
|
1615
1668
|
"name": "draw-save"
|
|
1669
|
+
},
|
|
1670
|
+
{
|
|
1671
|
+
"name": "set-basemap"
|
|
1616
1672
|
}
|
|
1617
1673
|
]
|
|
1618
1674
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nika-js/onlymap",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Interactive WebGL maps from declarative HTML — a custom-element manifest drives deck.gl: rendering, data loading, live updates, picking, widgets, and validation. No build step.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"publishConfig": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: onlymapjs
|
|
3
|
-
description: Build, edit, debug, or review OnlyMapJS declarative HTML maps and dashboards. Use when a user asks for an interactive map, deck.gl-style visualization, geospatial dashboard, live fleet/telemetry map, choropleth, popup/tooltip map, map story/tour, manual drawing/sketch map, 3D map assets, or help with OnlyMapJS syntax, validation, widgets, data formats, testing, or publishing examples.
|
|
3
|
+
description: Build, edit, debug, or review OnlyMapJS declarative HTML maps and dashboards, or React maps via the @nika-js/onlymap/react adapter. Use when a user asks for an interactive map, deck.gl-style visualization, geospatial dashboard, live fleet/telemetry map, choropleth, popup/tooltip map, map story/tour, manual drawing/sketch map, 3D map assets, a React map component, or help with OnlyMapJS syntax, validation, widgets, data formats, testing, or publishing examples.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# OnlyMapJS
|
|
@@ -23,7 +23,17 @@ Use OnlyMapJS as a declarative HTML map library. Write custom elements such as `
|
|
|
23
23
|
</script>
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
For no-build CDN examples, use a published module URL such as `https://esm.sh/@nika-js/onlymap@0.
|
|
26
|
+
For no-build CDN examples, use a published module URL such as `https://esm.sh/@nika-js/onlymap@0.2.0`.
|
|
27
|
+
|
|
28
|
+
## React Projects
|
|
29
|
+
|
|
30
|
+
In a React codebase, do NOT render `om-*` elements from JSX — React and the library would contend over the same DOM. Use the first-party adapter instead:
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import { OmMap, OmLayer, OmWidget, OmOverlay, useOmMap } from "@nika-js/onlymap/react";
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The adapter inverts several HTML-manifest rules: props are camelCase deck.gl props, accessors are plain JS functions (`getFillColor={d => ...}` — no `$field` expression language, no `js` opt-in), and interactions are `onClick`/`onHover` handlers plus React state, not `<om-behavior>` or state-mutating actions. Load `references/react.md` before writing React map code.
|
|
27
37
|
|
|
28
38
|
## Required References
|
|
29
39
|
|
|
@@ -31,6 +41,7 @@ Load the smallest reference needed for the task:
|
|
|
31
41
|
|
|
32
42
|
- `references/syntax.md` — element vocabulary, attributes, data formats, accessors, actions, widgets, overlays, drawing, 3D, built-in layer types.
|
|
33
43
|
- `references/patterns.md` — copyable manifest patterns for common map requests.
|
|
44
|
+
- `references/react.md` — the React adapter: components, the useOmMap hook, HTML-vs-React rule differences, testing.
|
|
34
45
|
- `references/testing.md` — validation, snapshot, headless harness, and browser testing workflow.
|
|
35
46
|
|
|
36
47
|
## Non-Negotiable Syntax Rules
|
|
@@ -50,6 +61,7 @@ Load the smallest reference needed for the task:
|
|
|
50
61
|
- Sparse rich HTML at one geographic location -> `<om-overlay>`.
|
|
51
62
|
- Many labels/badges -> `<om-layer type="PopupLayer">`.
|
|
52
63
|
- Guided tour or narrative sequence -> `<om-story>` with `<om-step>` siblings that reference existing layers/overlays by id.
|
|
64
|
+
- Basemap choice or user-switchable basemaps -> `basemap` presets (`positron`, `liberty`, `dark-matter`, `osm`, ...) + `<om-widget type="basemap-switcher">`; MapTiler custom styles via a style URL or `basemap-key`.
|
|
53
65
|
- Live entity updates -> `wss://` stream with `key` and optional `source` decoder.
|
|
54
66
|
- REST snapshot that changes over time -> `refresh="5s"`.
|
|
55
67
|
- User sketching -> `data="draw:sketch"` layer plus `<om-widget type="draw" target="sketch">`.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# React adapter — `@nika-js/onlymap/react`
|
|
2
|
+
|
|
3
|
+
Real components over the same engine as the HTML manifest. React never renders `om-*` elements — components feed a typed programmatic front-end, so there is no DOM contention with the library's reconciler. React ≥ 18 (`npm install @nika-js/onlymap react react-dom`).
|
|
4
|
+
|
|
5
|
+
## Rules that INVERT the HTML-manifest rules
|
|
6
|
+
|
|
7
|
+
- Props are **camelCase deck.gl props** (`getFillColor`, `radiusMinPixels`), not kebab-case attributes.
|
|
8
|
+
- Accessors are **plain JS functions**: `getFillColor={d => d.mag > 6 ? [214,40,40] : [252,191,73]}`. Never use `$field` expressions or `scale()` strings here; never add a `js` attribute (there is none).
|
|
9
|
+
- Interactions are **event handlers + state**, not `<om-behavior>`. Toggle a layer by rendering `visible={false}`; filter by changing `filterRange`. The state-mutating actions (`toggle-layer`, `show-overlay`, `fade`, story/draw actions) are not dispatched on this path — a console warning points back to props. Camera actions (`fly-to`, `zoom-to-feature`, `zoom-in`/`-out`), `pulse`, `populate`, and custom `OmMap.registerAction` handlers work via `emit`.
|
|
10
|
+
- Keep inline `data` references **stable across renders** (`useMemo`/`useState`) — data reactivity diffs by reference. deck.gl ignores accessor function identity; pass `updateTriggers={{ getFillColor: theme }}` when an accessor's *output* changes.
|
|
11
|
+
- Stories and the draw widget are HTML-manifest-only for now.
|
|
12
|
+
|
|
13
|
+
## Complete example
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { useState } from "react";
|
|
17
|
+
import { OmMap, OmLayer, OmWidget, OmOverlay, useOmMap } from "@nika-js/onlymap/react";
|
|
18
|
+
|
|
19
|
+
function QuakeMap() {
|
|
20
|
+
const [visible, setVisible] = useState(true);
|
|
21
|
+
return (
|
|
22
|
+
<OmMap center={[-119, 36]} zoom={5} basemap="maplibre" style={{ height: "100vh" }}>
|
|
23
|
+
<OmLayer id="quakes" type="ScatterplotLayer" data="/quakes.csv" label="Earthquakes"
|
|
24
|
+
visible={visible} pickable
|
|
25
|
+
getPosition={(d) => [d.longitude, d.latitude]}
|
|
26
|
+
getFillColor={(d) => (d.mag >= 6 ? [214, 40, 40] : [252, 191, 73])}
|
|
27
|
+
radiusMinPixels={3}
|
|
28
|
+
onClick={(sel) => console.log(sel.object)} />
|
|
29
|
+
<OmWidget position="top-left"><StatsPanel onToggle={() => setVisible(!visible)} /></OmWidget>
|
|
30
|
+
<OmOverlay anchorFrom="selection" layer="quakes">
|
|
31
|
+
{(sel) => sel && <div className="card">M{sel.object.mag} — {sel.object.place}</div>}
|
|
32
|
+
</OmOverlay>
|
|
33
|
+
</OmMap>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function StatsPanel({ onToggle }) {
|
|
38
|
+
const ctx = useOmMap(["viewport", "data:quakes"]); // watch tokens re-render the component
|
|
39
|
+
return (
|
|
40
|
+
<div className="panel">
|
|
41
|
+
{ctx.stats("quakes", "mag").count} quakes at z{ctx.viewport.zoom.toFixed(1)}
|
|
42
|
+
<button onClick={onToggle}>Toggle</button>
|
|
43
|
+
<button onClick={() => ctx.emit("fly-to", { center: [140, 38], zoom: 4, duration: 1200 })}>Japan</button>
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Component surface
|
|
50
|
+
|
|
51
|
+
- **`<OmMap>`** — `center`/`zoom`/`pitch`/`bearing` (initial; later changes move the camera, unchanged props never fight user panning), `basemap`, `headless`, `onReady`, `onViewStateChange`, `onRuntimeError`. Give it a size via `style`/`className`. `ref` exposes the imperative `MapController` handle: `flyTo`, `setView`, `emit`, `getLayers`, `getSelection`, `injectPick`, `ready` (promise), `project`.
|
|
52
|
+
- **`<OmLayer>`** — `id` + `type` (any registered deck.gl layer type) + deck props. `data`: stable inline reference or URL string (full Data Layer: CSV/Arrow/Shapefile/KML formats, `ws(s)://` streams via `source`/`streamKey`/`flush`, `refresh` polling). `label`/`color` feed `ctx.layers`; `filterField`/`filterRange` = GPU filter; `onClick`/`onHover` receive the flattened picked object (`onHover(null)` = pointer left).
|
|
53
|
+
- **`<OmWidget>`** — positioning shell: `position="top-left|top-right|bottom-left|bottom-right"` + arbitrary JSX. Widgets sharing a corner stack.
|
|
54
|
+
- **`<OmOverlay>`** — geo-anchored HTML with managed projection/tracking/culling. `anchor={[lng, lat]}` or `anchorFrom="selection"` (+ `layer` to scope which picks move it); children may be `(selection) => JSX`; `anchorOffset` (default `bottom-center`); `interactive={false}` for hover-following tooltips.
|
|
55
|
+
- **`useOmMap(watch?)`** — the same `ctx` contract HTML widget scripts get, typed: `layers`, `viewport`, `selection`, `emit`, `data()`, `dataInViewport()`, `stats()`. Watch tokens: `"viewport"`, `"selection"`, `"layers"`, `"data:<layerId>"`.
|
|
56
|
+
|
|
57
|
+
## Testing (no browser, no GPU)
|
|
58
|
+
|
|
59
|
+
`<OmMap headless>` runs real projection math under jsdom/happy-dom:
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
const ref = createRef<OmMapHandle>();
|
|
63
|
+
render(<OmMap ref={ref} headless><OmLayer id="pts" type="ScatterplotLayer" data={rows} getPosition={p} /></OmMap>);
|
|
64
|
+
await ref.current!.ready;
|
|
65
|
+
ref.current!.injectPick({ layerId: "pts", object: rows[0], index: 0, coordinate: [0, 0], pixel: [0, 0], type: "click" });
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`injectPick` rides the same selection path as real GPU picks — `onClick`, `useOmMap(["selection"])`, and selection-anchored overlays all react. For browser e2e, expose the `ref` handle (e.g. on `window`) as the readiness/projection probe — a React page has no `<om-map>` element to await.
|
|
@@ -33,7 +33,8 @@ Common attributes:
|
|
|
33
33
|
- `zoom="11"`
|
|
34
34
|
- `pitch="55"`
|
|
35
35
|
- `bearing="20"`
|
|
36
|
-
- `basemap
|
|
36
|
+
- `basemap` — a free preset (`liberty`, `bright`, `positron`, `dark-matter`, `voyager`, `osm`; keyed `maptiler-streets|dataviz|satellite` with `basemap-key="…"` or `OmMap.configureBasemap({ maptilerKey })`), `maplibre` (bare demo style), a MapLibre style URL, or `none` (standalone canvas). The attribute is live: writing it switches the basemap in place (camera and layers survive). Register more with `OmMap.registerBasemap(name, { style })`.
|
|
37
|
+
- `attribution="false"` to opt out of the automatic provider-attribution control (only if you render equivalent credits yourself)
|
|
37
38
|
- `validate` to show live validation errors during authoring
|
|
38
39
|
- `headless width="800" height="600"` for test harness use
|
|
39
40
|
|
|
@@ -178,6 +179,7 @@ Built-ins:
|
|
|
178
179
|
- `draw`
|
|
179
180
|
- `vega-lite`
|
|
180
181
|
- `player`
|
|
182
|
+
- `basemap-switcher` — radio list of presets; `options="positron dark-matter osm"` (default: every keyless registered preset)
|
|
181
183
|
|
|
182
184
|
Positions: `top-left`, `top-right`, `bottom-left`, `bottom-right`.
|
|
183
185
|
|
|
@@ -187,6 +189,7 @@ Examples:
|
|
|
187
189
|
<om-widget type="legend" position="bottom-right" title="Layers" interactive></om-widget>
|
|
188
190
|
<om-widget type="filter" layer="quakes" field="magnitude" position="top-left"></om-widget>
|
|
189
191
|
<om-widget type="draw" target="sketch" modes="point line polygon" save="both"></om-widget>
|
|
192
|
+
<om-widget type="basemap-switcher" options="positron dark-matter liberty osm" position="top-right"></om-widget>
|
|
190
193
|
```
|
|
191
194
|
|
|
192
195
|
Custom widget:
|
|
@@ -254,6 +257,7 @@ Common built-in actions:
|
|
|
254
257
|
- `highlight-feature`
|
|
255
258
|
- `zoom-to-feature`
|
|
256
259
|
- `filter-layer`
|
|
260
|
+
- `set-basemap` — payload `{ basemap }`; writes the `<om-map basemap>` attribute
|
|
257
261
|
- `zoom-in`, `zoom-out`
|
|
258
262
|
- `fly-to`
|
|
259
263
|
- story actions: `story-play`, `story-pause`, `story-seek`
|