isard 1.0.0 → 1.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.
Files changed (268) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/{basemaps.d.ts → basemap/basemaps.d.ts} +3 -3
  3. package/dist/{basemaps.js → basemap/basemaps.js} +3 -3
  4. package/dist/basemap/basemaps.js.map +1 -0
  5. package/dist/{grid.d.ts → basemap/grid.d.ts} +6 -6
  6. package/dist/{grid.js → basemap/grid.js} +61 -67
  7. package/dist/basemap/grid.js.map +1 -0
  8. package/dist/{mvt.d.ts → basemap/mvt.d.ts} +1 -1
  9. package/dist/{mvt.js → basemap/mvt.js} +6 -6
  10. package/dist/basemap/mvt.js.map +1 -0
  11. package/dist/{pmtiles.d.ts → basemap/pmtiles.d.ts} +16 -10
  12. package/dist/{pmtiles.js → basemap/pmtiles.js} +41 -33
  13. package/dist/basemap/pmtiles.js.map +1 -0
  14. package/dist/{resample.d.ts → basemap/resample.d.ts} +11 -2
  15. package/dist/{resample.js → basemap/resample.js} +18 -16
  16. package/dist/basemap/resample.js.map +1 -0
  17. package/dist/{terrain.d.ts → basemap/terrain.d.ts} +3 -3
  18. package/dist/{terrain.js → basemap/terrain.js} +7 -7
  19. package/dist/basemap/terrain.js.map +1 -0
  20. package/dist/{vectorstyle.d.ts → basemap/vectorstyle.d.ts} +12 -10
  21. package/dist/{vectorstyle.js → basemap/vectorstyle.js} +129 -111
  22. package/dist/basemap/vectorstyle.js.map +1 -0
  23. package/dist/{vectortiles.d.ts → basemap/vectortiles.d.ts} +18 -18
  24. package/dist/{vectortiles.js → basemap/vectortiles.js} +157 -109
  25. package/dist/basemap/vectortiles.js.map +1 -0
  26. package/dist/{wmts.d.ts → basemap/wmts.d.ts} +3 -3
  27. package/dist/{wmts.js → basemap/wmts.js} +19 -14
  28. package/dist/basemap/wmts.js.map +1 -0
  29. package/dist/basemap.worker.js +2 -2
  30. package/dist/basemap.worker.js.map +14 -13
  31. package/dist/bind/layers.d.ts +46 -0
  32. package/dist/bind/layers.js +154 -0
  33. package/dist/bind/layers.js.map +1 -0
  34. package/dist/{crs.d.ts → data/crs.d.ts} +11 -11
  35. package/dist/{crs.js → data/crs.js} +38 -34
  36. package/dist/data/crs.js.map +1 -0
  37. package/dist/{gpx.d.ts → data/gpx.d.ts} +2 -2
  38. package/dist/{gpx.js → data/gpx.js} +20 -20
  39. package/dist/data/gpx.js.map +1 -0
  40. package/dist/{kml.d.ts → data/kml.d.ts} +2 -2
  41. package/dist/{kml.js → data/kml.js} +42 -39
  42. package/dist/data/kml.js.map +1 -0
  43. package/dist/{topojson.d.ts → data/topojson.d.ts} +2 -2
  44. package/dist/{topojson.js → data/topojson.js} +15 -10
  45. package/dist/data/topojson.js.map +1 -0
  46. package/dist/{xml.d.ts → data/xml.d.ts} +5 -5
  47. package/dist/{xml.js → data/xml.js} +19 -18
  48. package/dist/data/xml.js.map +1 -0
  49. package/dist/decoder.worker.js +4 -4
  50. package/dist/decoder.worker.js.map +7 -7
  51. package/dist/{decoders.d.ts → engine/decoders.d.ts} +3 -3
  52. package/dist/{decoders.js → engine/decoders.js} +28 -25
  53. package/dist/engine/decoders.js.map +1 -0
  54. package/dist/{loader.d.ts → engine/loader.d.ts} +1 -1
  55. package/dist/{loader.js → engine/loader.js} +17 -14
  56. package/dist/engine/loader.js.map +1 -0
  57. package/dist/{protocol.d.ts → engine/protocol.d.ts} +1 -1
  58. package/dist/engine/protocol.js +3 -0
  59. package/dist/engine/protocol.js.map +1 -0
  60. package/dist/{requests.d.ts → engine/requests.d.ts} +1 -1
  61. package/dist/{requests.js → engine/requests.js} +19 -16
  62. package/dist/engine/requests.js.map +1 -0
  63. package/dist/engine-loader.d.ts +13 -9
  64. package/dist/engine-loader.js +30 -30
  65. package/dist/engine-loader.js.map +1 -1
  66. package/dist/formats.d.ts +3 -3
  67. package/dist/formats.js +3 -3
  68. package/dist/formats.js.map +1 -1
  69. package/dist/geoplateforme.d.ts +13 -13
  70. package/dist/geoplateforme.js +23 -21
  71. package/dist/geoplateforme.js.map +1 -1
  72. package/dist/index.d.ts +30 -29
  73. package/dist/index.js +18 -18
  74. package/dist/index.js.map +1 -1
  75. package/dist/isard_bg.wasm +0 -0
  76. package/dist/isard_core_bg.wasm +0 -0
  77. package/dist/isard_points_bg.wasm +0 -0
  78. package/dist/{extrusions.d.ts → layers/extrusions.d.ts} +1 -1
  79. package/dist/{extrusions.js → layers/extrusions.js} +10 -5
  80. package/dist/layers/extrusions.js.map +1 -0
  81. package/dist/{features.d.ts → layers/features.d.ts} +10 -10
  82. package/dist/{features.js → layers/features.js} +44 -45
  83. package/dist/layers/features.js.map +1 -0
  84. package/dist/{glyphs.d.ts → layers/glyphs.d.ts} +6 -6
  85. package/dist/{glyphs.js → layers/glyphs.js} +45 -41
  86. package/dist/layers/glyphs.js.map +1 -0
  87. package/dist/{icons.d.ts → layers/icons.d.ts} +10 -10
  88. package/dist/{icons.js → layers/icons.js} +84 -69
  89. package/dist/layers/icons.js.map +1 -0
  90. package/dist/{layers.d.ts → layers/layers.d.ts} +9 -4
  91. package/dist/{layers.js → layers/layers.js} +35 -31
  92. package/dist/layers/layers.js.map +1 -0
  93. package/dist/{panoramas.d.ts → layers/panoramas.d.ts} +3 -3
  94. package/dist/{panoramas.js → layers/panoramas.js} +54 -43
  95. package/dist/layers/panoramas.js.map +1 -0
  96. package/dist/{pointclouds.d.ts → layers/pointclouds.d.ts} +5 -5
  97. package/dist/{pointclouds.js → layers/pointclouds.js} +26 -34
  98. package/dist/layers/pointclouds.js.map +1 -0
  99. package/dist/{symbols.d.ts → layers/symbols.d.ts} +3 -3
  100. package/dist/{symbols.js → layers/symbols.js} +69 -72
  101. package/dist/layers/symbols.js.map +1 -0
  102. package/dist/map/basemap-layers.d.ts +26 -0
  103. package/dist/map/basemap-layers.js +61 -0
  104. package/dist/map/basemap-layers.js.map +1 -0
  105. package/dist/map/canvas.d.ts +17 -0
  106. package/dist/map/canvas.js +56 -0
  107. package/dist/map/canvas.js.map +1 -0
  108. package/dist/map/checks.d.ts +11 -0
  109. package/dist/map/checks.js +39 -0
  110. package/dist/map/checks.js.map +1 -0
  111. package/dist/map/credits.d.ts +17 -0
  112. package/dist/map/credits.js +34 -0
  113. package/dist/map/credits.js.map +1 -0
  114. package/dist/map/engine-settings.d.ts +15 -0
  115. package/dist/map/engine-settings.js +45 -0
  116. package/dist/map/engine-settings.js.map +1 -0
  117. package/dist/map/engines.d.ts +41 -0
  118. package/dist/map/engines.js +77 -0
  119. package/dist/map/engines.js.map +1 -0
  120. package/dist/map/events.d.ts +14 -0
  121. package/dist/map/events.js +46 -0
  122. package/dist/map/events.js.map +1 -0
  123. package/dist/map/frame-loop.d.ts +61 -0
  124. package/dist/map/frame-loop.js +139 -0
  125. package/dist/map/frame-loop.js.map +1 -0
  126. package/dist/map/hover-pick.d.ts +30 -0
  127. package/dist/map/hover-pick.js +56 -0
  128. package/dist/map/hover-pick.js.map +1 -0
  129. package/dist/map/layer-spec.d.ts +42 -0
  130. package/dist/map/layer-spec.js +34 -0
  131. package/dist/map/layer-spec.js.map +1 -0
  132. package/dist/map/layer-stack.d.ts +36 -0
  133. package/dist/map/layer-stack.js +101 -0
  134. package/dist/map/layer-stack.js.map +1 -0
  135. package/dist/map/overlay-host.d.ts +36 -0
  136. package/dist/map/overlay-host.js +101 -0
  137. package/dist/map/overlay-host.js.map +1 -0
  138. package/dist/map/panorama-host.d.ts +5 -0
  139. package/dist/map/panorama-host.js +32 -0
  140. package/dist/map/panorama-host.js.map +1 -0
  141. package/dist/map/raster-source.d.ts +11 -0
  142. package/dist/map/raster-source.js +26 -0
  143. package/dist/map/raster-source.js.map +1 -0
  144. package/dist/map/symbol-atlases.d.ts +26 -0
  145. package/dist/map/symbol-atlases.js +59 -0
  146. package/dist/map/symbol-atlases.js.map +1 -0
  147. package/dist/map/viewshed.d.ts +5 -0
  148. package/dist/map/viewshed.js +28 -0
  149. package/dist/map/viewshed.js.map +1 -0
  150. package/dist/map/workers.d.ts +46 -0
  151. package/dist/map/workers.js +73 -0
  152. package/dist/map/workers.js.map +1 -0
  153. package/dist/map.d.ts +46 -127
  154. package/dist/map.js +183 -698
  155. package/dist/map.js.map +1 -1
  156. package/dist/projections.d.ts +3 -3
  157. package/dist/projections.js +2 -2
  158. package/dist/projections.js.map +1 -1
  159. package/dist/react/Control.d.ts +11 -0
  160. package/dist/react/Control.js +18 -0
  161. package/dist/react/Control.js.map +1 -0
  162. package/dist/react/CustomLayer.d.ts +11 -0
  163. package/dist/react/CustomLayer.js +30 -0
  164. package/dist/react/CustomLayer.js.map +1 -0
  165. package/dist/react/IsardMap.d.ts +43 -0
  166. package/dist/react/IsardMap.js +146 -0
  167. package/dist/react/IsardMap.js.map +1 -0
  168. package/dist/react/Layer.d.ts +42 -0
  169. package/dist/react/Layer.js +74 -0
  170. package/dist/react/Layer.js.map +1 -0
  171. package/dist/react/Marker.d.ts +24 -0
  172. package/dist/react/Marker.js +53 -0
  173. package/dist/react/Marker.js.map +1 -0
  174. package/dist/react/Panel.d.ts +11 -0
  175. package/dist/react/Panel.js +37 -0
  176. package/dist/react/Panel.js.map +1 -0
  177. package/dist/react/Popup.d.ts +18 -0
  178. package/dist/react/Popup.js +60 -0
  179. package/dist/react/Popup.js.map +1 -0
  180. package/dist/react/Swipe.d.ts +9 -0
  181. package/dist/react/Swipe.js +41 -0
  182. package/dist/react/Swipe.js.map +1 -0
  183. package/dist/react/Viewshed.d.ts +5 -0
  184. package/dist/react/Viewshed.js +28 -0
  185. package/dist/react/Viewshed.js.map +1 -0
  186. package/dist/react/context.d.ts +32 -0
  187. package/dist/react/context.js +20 -0
  188. package/dist/react/context.js.map +1 -0
  189. package/dist/react/index.d.ts +26 -0
  190. package/dist/react/index.js +22 -0
  191. package/dist/react/index.js.map +1 -0
  192. package/dist/settings.d.ts +6 -10
  193. package/dist/settings.js +19 -17
  194. package/dist/settings.js.map +1 -1
  195. package/dist/types.d.ts +8 -8
  196. package/dist/types.js +1 -0
  197. package/dist/types.js.map +1 -1
  198. package/dist/{controls.d.ts → ui/controls.d.ts} +1 -1
  199. package/dist/{controls.js → ui/controls.js} +6 -4
  200. package/dist/ui/controls.js.map +1 -0
  201. package/dist/{interaction.d.ts → ui/interaction.d.ts} +9 -5
  202. package/dist/{interaction.js → ui/interaction.js} +9 -8
  203. package/dist/ui/interaction.js.map +1 -0
  204. package/dist/{overlay.d.ts → ui/overlay.d.ts} +2 -2
  205. package/dist/{overlay.js → ui/overlay.js} +18 -20
  206. package/dist/ui/overlay.js.map +1 -0
  207. package/dist/{style.d.ts → ui/style.d.ts} +1 -1
  208. package/dist/{style.js → ui/style.js} +2 -2
  209. package/dist/ui/style.js.map +1 -0
  210. package/dist/{animation.d.ts → util/animation.d.ts} +9 -5
  211. package/dist/{animation.js → util/animation.js} +6 -6
  212. package/dist/util/animation.js.map +1 -0
  213. package/dist/{color.d.ts → util/color.d.ts} +1 -1
  214. package/dist/{color.js → util/color.js} +14 -16
  215. package/dist/util/color.js.map +1 -0
  216. package/dist/{geo.d.ts → util/geo.d.ts} +6 -6
  217. package/dist/{geo.js → util/geo.js} +12 -12
  218. package/dist/util/geo.js.map +1 -0
  219. package/dist/util/guards.d.ts +7 -0
  220. package/dist/util/guards.js +8 -0
  221. package/dist/util/guards.js.map +1 -0
  222. package/dist/util/lru.js.map +1 -0
  223. package/dist/wasm/isard.d.ts +168 -0
  224. package/dist/wasm/isard.js +146 -0
  225. package/dist/wasm/isard_core.d.ts +144 -0
  226. package/dist/wasm/isard_core.js +122 -0
  227. package/dist/{basemap.worker.d.ts → workers/basemap.worker.d.ts} +17 -7
  228. package/dist/workers/basemap.worker.js +658 -0
  229. package/dist/workers/basemap.worker.js.map +1 -0
  230. package/package.json +24 -1
  231. package/vite.js +1 -1
  232. package/dist/animation.js.map +0 -1
  233. package/dist/basemaps.js.map +0 -1
  234. package/dist/color.js.map +0 -1
  235. package/dist/controls.js.map +0 -1
  236. package/dist/crs.js.map +0 -1
  237. package/dist/decoders.js.map +0 -1
  238. package/dist/extrusions.js.map +0 -1
  239. package/dist/features.js.map +0 -1
  240. package/dist/geo.js.map +0 -1
  241. package/dist/glyphs.js.map +0 -1
  242. package/dist/gpx.js.map +0 -1
  243. package/dist/grid.js.map +0 -1
  244. package/dist/icons.js.map +0 -1
  245. package/dist/interaction.js.map +0 -1
  246. package/dist/kml.js.map +0 -1
  247. package/dist/layers.js.map +0 -1
  248. package/dist/loader.js.map +0 -1
  249. package/dist/lru.js.map +0 -1
  250. package/dist/mvt.js.map +0 -1
  251. package/dist/overlay.js.map +0 -1
  252. package/dist/panoramas.js.map +0 -1
  253. package/dist/pmtiles.js.map +0 -1
  254. package/dist/pointclouds.js.map +0 -1
  255. package/dist/protocol.js +0 -2
  256. package/dist/protocol.js.map +0 -1
  257. package/dist/requests.js.map +0 -1
  258. package/dist/resample.js.map +0 -1
  259. package/dist/style.js.map +0 -1
  260. package/dist/symbols.js.map +0 -1
  261. package/dist/terrain.js.map +0 -1
  262. package/dist/topojson.js.map +0 -1
  263. package/dist/vectorstyle.js.map +0 -1
  264. package/dist/vectortiles.js.map +0 -1
  265. package/dist/wmts.js.map +0 -1
  266. package/dist/xml.js.map +0 -1
  267. /package/dist/{lru.d.ts → util/lru.d.ts} +0 -0
  268. /package/dist/{lru.js → util/lru.js} +0 -0
@@ -0,0 +1,43 @@
1
+ /** `<IsardMap>`: the map, created when it mounts, destroyed when it unmounts, its options and events as props */
2
+ import { type CSSProperties, type ReactNode } from 'react';
3
+ import { GeoMap } from '../map.ts';
4
+ import type { GeoMapOptions, LonLat, MapEvents } from '../types.ts';
5
+ import type { Camera } from '../wasm/isard.js';
6
+ /** The props of the map's events: `onClick` for 'click', `onMoveEnd` for 'moveend'… */
7
+ export interface MapEventProps {
8
+ onMoveStart?: (camera: Camera) => void;
9
+ onMove?: (camera: Camera) => void;
10
+ onMoveEnd?: (camera: Camera) => void;
11
+ onResize?: (event: MapEvents['resize']) => void;
12
+ onHover?: (event: MapEvents['hover']) => void;
13
+ onHoverFeature?: (event: MapEvents['hoverfeature']) => void;
14
+ onClick?: (event: MapEvents['click']) => void;
15
+ onRender?: (event: MapEvents['render']) => void;
16
+ onIdle?: () => void;
17
+ onLeave?: () => void;
18
+ }
19
+ export interface IsardMapProps extends Omit<GeoMapOptions, keyof Camera>, MapEventProps {
20
+ /** The camera at creation, then left to the user's gestures */
21
+ center?: LonLat;
22
+ zoom?: number;
23
+ pitch?: number;
24
+ bearing?: number;
25
+ /** A camera the map follows: set again whenever it changes (with `onMove` to keep it in a state) */
26
+ camera?: Partial<Camera>;
27
+ /** What `onHoverFeature` picks: within `radius` CSS pixels, among `layers` (default every layer) */
28
+ hoverPicking?: GeoMap['hoverPicking'];
29
+ /** The map, once created */
30
+ onLoad?: (map: GeoMap) => void;
31
+ /** The map's creation failed (no WebGL2, the engine not found) */
32
+ onError?: (error: unknown) => void;
33
+ id?: string;
34
+ className?: string;
35
+ style?: CSSProperties;
36
+ /** Layers, markers, popups, panels, effects; drawn once the map is created */
37
+ children?: ReactNode;
38
+ }
39
+ /**
40
+ * A map filling its element (give it a height). Created once mounted, destroyed when unmounted; the options read at
41
+ * creation only (`body`, `preset`, `interactive`, the URLs) need a new `key` to take another value
42
+ */
43
+ export declare const IsardMap: import("react").ForwardRefExoticComponent<IsardMapProps & import("react").RefAttributes<GeoMap | undefined>>;
@@ -0,0 +1,146 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /** `<IsardMap>`: the map, created when it mounts, destroyed when it unmounts, its options and events as props */
3
+ import { forwardRef, useEffect, useImperativeHandle, useRef, useState, } from 'react';
4
+ import { LayerOrder } from "../bind/layers.js";
5
+ import { GeoMap } from "../map.js";
6
+ import { defined, MapContext } from "./context.js";
7
+ const EVENT_PROPS = {
8
+ onMoveStart: 'movestart',
9
+ onMove: 'move',
10
+ onMoveEnd: 'moveend',
11
+ onResize: 'resize',
12
+ onHover: 'hover',
13
+ onHoverFeature: 'hoverfeature',
14
+ onClick: 'click',
15
+ onRender: 'render',
16
+ onIdle: 'idle',
17
+ onLeave: 'leave',
18
+ };
19
+ /** The props that are not options of the map */
20
+ const NOT_OPTIONS = [...Object.keys(EVENT_PROPS), 'onLoad', 'onError', 'hoverPicking', 'id', 'className', 'style'];
21
+ const isEventProp = (key) => key in EVENT_PROPS;
22
+ /** Options the map takes again after its creation, and how */
23
+ const LIVE_OPTIONS = {
24
+ basemap: (map, p) => {
25
+ if (p.basemap)
26
+ map.setBasemap(p.basemap, p.hillshade);
27
+ },
28
+ hillshade: (map, p) => {
29
+ if (p.basemap)
30
+ map.setBasemap(p.basemap, p.hillshade);
31
+ },
32
+ terrain: (map, p) => map.setTerrain(p.terrain ?? false),
33
+ minZoom: (map, p) => map.setMinZoom(p.minZoom ?? 0),
34
+ maxZoom: (map, p) => map.setMaxZoom(p.maxZoom ?? 22),
35
+ maxPitch: (map, p) => map.setMaxPitch(p.maxPitch ?? 70),
36
+ atmosphere: (map, p) => map.setAtmosphere(p.atmosphere),
37
+ projection: (map, p) => map.setProjection(p.projection ?? 'globe'),
38
+ loading: (map, p) => map.setLoading(p.loading ?? 'immediate'),
39
+ sky: (map, p) => map.setSky(p.sky ?? true),
40
+ background: (map, p) => {
41
+ if (p.background)
42
+ map.setBackground(p.background);
43
+ },
44
+ };
45
+ const isLiveOption = (key) => key in LIVE_OPTIONS;
46
+ const optionsOf = (props) => Object.fromEntries(Object.entries(props).filter(([key]) => !NOT_OPTIONS.includes(key)));
47
+ const sameCamera = (a, b) => (b.center === undefined || (a.center[0] === b.center[0] && a.center[1] === b.center[1])) &&
48
+ (b.zoom === undefined || a.zoom === b.zoom) &&
49
+ (b.pitch === undefined || a.pitch === b.pitch) &&
50
+ (b.bearing === undefined || a.bearing === b.bearing);
51
+ /** What the components of a new map share */
52
+ const bindingOf = (map) => {
53
+ const viewsheds = new Map();
54
+ return {
55
+ map,
56
+ order: new LayerOrder(),
57
+ viewsheds,
58
+ applyViewsheds: () => map.setViewshed(viewsheds.size > 0 ? [...viewsheds.values()] : undefined),
59
+ };
60
+ };
61
+ /**
62
+ * A map filling its element (give it a height). Created once mounted, destroyed when unmounted; the options read at
63
+ * creation only (`body`, `preset`, `interactive`, the URLs) need a new `key` to take another value
64
+ */
65
+ export const IsardMap = forwardRef(function IsardMap(props, ref) {
66
+ const element = useRef(null);
67
+ const latest = useRef(props);
68
+ const applied = useRef(props);
69
+ const [binding, setBinding] = useState();
70
+ const map = binding?.map;
71
+ const { camera, hoverPicking } = props;
72
+ latest.current = props;
73
+ useImperativeHandle(ref, () => map, [map]);
74
+ // Created once: StrictMode's second mount and an unmount before the engine is loaded drop the map they started
75
+ useEffect(() => {
76
+ const container = element.current;
77
+ if (!container)
78
+ return;
79
+ const created = { cancelled: false };
80
+ const { camera, center, zoom, pitch, bearing, children: _, ...options } = latest.current;
81
+ GeoMap.create(container, { ...optionsOf(options), ...defined({ center, zoom, pitch, bearing }), ...camera }).then((map) => {
82
+ if (created.cancelled) {
83
+ map.destroy();
84
+ return;
85
+ }
86
+ created.map = map;
87
+ setBinding(bindingOf(map));
88
+ latest.current.onLoad?.(map);
89
+ }, (error) => {
90
+ if (created.cancelled)
91
+ return;
92
+ if (latest.current.onError)
93
+ latest.current.onError(error);
94
+ else
95
+ console.error(error);
96
+ });
97
+ return () => {
98
+ created.cancelled = true;
99
+ created.map?.destroy();
100
+ setBinding(undefined);
101
+ };
102
+ }, []);
103
+ // The events, each listened to once: the props may give another function at each render
104
+ useEffect(() => {
105
+ if (!map)
106
+ return;
107
+ const offs = Object.keys(EVENT_PROPS)
108
+ .filter(isEventProp)
109
+ .map((prop) => map.on(EVENT_PROPS[prop], (event) => latest.current[prop]?.(event)));
110
+ return () => {
111
+ offs.forEach((off) => {
112
+ off();
113
+ });
114
+ };
115
+ }, [map]);
116
+ // The options taken again, compared with what the map was given last
117
+ useEffect(() => {
118
+ if (!map)
119
+ return;
120
+ const last = applied.current;
121
+ const done = new Set();
122
+ applied.current = props;
123
+ Object.keys(LIVE_OPTIONS)
124
+ .filter(isLiveOption)
125
+ .forEach((key) => {
126
+ const apply = LIVE_OPTIONS[key];
127
+ const changed = !Object.is(last[key], props[key]);
128
+ if (!apply || done.has(apply) || !changed)
129
+ return;
130
+ done.add(apply);
131
+ apply(map, props);
132
+ });
133
+ });
134
+ // What `onHoverFeature` picks, given to the map whenever the prop changes
135
+ useEffect(() => {
136
+ if (map && hoverPicking)
137
+ map.hoverPicking = hoverPicking;
138
+ }, [map, hoverPicking]);
139
+ // A controlled camera: set when the prop changes, unless the map is already there (a gesture reported by `onMove`)
140
+ useEffect(() => {
141
+ if (map && camera && !sameCamera(map.getCamera(), camera))
142
+ map.setCamera(camera);
143
+ }, [map, camera]);
144
+ return (_jsx("div", { ref: element, id: props.id, className: props.className, style: props.style, children: binding ? _jsx(MapContext.Provider, { value: binding, children: props.children }) : null }));
145
+ });
146
+ //# sourceMappingURL=IsardMap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IsardMap.js","sourceRoot":"","sources":["../../src/react/IsardMap.tsx"],"names":[],"mappings":"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","sourcesContent":["/** `<IsardMap>`: the map, created when it mounts, destroyed when it unmounts, its options and events as props */\n\nimport {\n type CSSProperties,\n forwardRef,\n type ReactNode,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from 'react';\nimport { LayerOrder } from '../bind/layers.ts';\nimport { GeoMap } from '../map.ts';\nimport type { GeoMapOptions, LonLat, MapEvents, ViewshedOptions } from '../types.ts';\nimport type { Camera } from '../wasm/isard.js';\nimport { type Binding, defined, MapContext } from './context.ts';\n\n/** The props of the map's events: `onClick` for 'click', `onMoveEnd` for 'moveend'… */\nexport interface MapEventProps {\n onMoveStart?: (camera: Camera) => void;\n onMove?: (camera: Camera) => void;\n onMoveEnd?: (camera: Camera) => void;\n onResize?: (event: MapEvents['resize']) => void;\n onHover?: (event: MapEvents['hover']) => void;\n onHoverFeature?: (event: MapEvents['hoverfeature']) => void;\n onClick?: (event: MapEvents['click']) => void;\n onRender?: (event: MapEvents['render']) => void;\n onIdle?: () => void;\n onLeave?: () => void;\n}\n\nconst EVENT_PROPS = {\n onMoveStart: 'movestart',\n onMove: 'move',\n onMoveEnd: 'moveend',\n onResize: 'resize',\n onHover: 'hover',\n onHoverFeature: 'hoverfeature',\n onClick: 'click',\n onRender: 'render',\n onIdle: 'idle',\n onLeave: 'leave',\n} as const satisfies Record<keyof MapEventProps, keyof MapEvents>;\n\nexport interface IsardMapProps extends Omit<GeoMapOptions, keyof Camera>, MapEventProps {\n /** The camera at creation, then left to the user's gestures */\n center?: LonLat;\n zoom?: number;\n pitch?: number;\n bearing?: number;\n /** A camera the map follows: set again whenever it changes (with `onMove` to keep it in a state) */\n camera?: Partial<Camera>;\n /** What `onHoverFeature` picks: within `radius` CSS pixels, among `layers` (default every layer) */\n hoverPicking?: GeoMap['hoverPicking'];\n /** The map, once created */\n onLoad?: (map: GeoMap) => void;\n /** The map's creation failed (no WebGL2, the engine not found) */\n onError?: (error: unknown) => void;\n id?: string;\n className?: string;\n style?: CSSProperties;\n /** Layers, markers, popups, panels, effects; drawn once the map is created */\n children?: ReactNode;\n}\n\n/** The props that are not options of the map */\nconst NOT_OPTIONS = [...Object.keys(EVENT_PROPS), 'onLoad', 'onError', 'hoverPicking', 'id', 'className', 'style'];\n\nconst isEventProp = (key: string): key is keyof typeof EVENT_PROPS => key in EVENT_PROPS;\n\n/** Options the map takes again after its creation, and how */\nconst LIVE_OPTIONS: { [K in keyof GeoMapOptions]?: (map: GeoMap, props: IsardMapProps) => void } = {\n basemap: (map, p) => {\n if (p.basemap) map.setBasemap(p.basemap, p.hillshade);\n },\n hillshade: (map, p) => {\n if (p.basemap) map.setBasemap(p.basemap, p.hillshade);\n },\n terrain: (map, p) => map.setTerrain(p.terrain ?? false),\n minZoom: (map, p) => map.setMinZoom(p.minZoom ?? 0),\n maxZoom: (map, p) => map.setMaxZoom(p.maxZoom ?? 22),\n maxPitch: (map, p) => map.setMaxPitch(p.maxPitch ?? 70),\n atmosphere: (map, p) => map.setAtmosphere(p.atmosphere),\n projection: (map, p) => map.setProjection(p.projection ?? 'globe'),\n loading: (map, p) => map.setLoading(p.loading ?? 'immediate'),\n sky: (map, p) => map.setSky(p.sky ?? true),\n background: (map, p) => {\n if (p.background) map.setBackground(p.background);\n },\n};\n\nconst isLiveOption = (key: string): key is keyof typeof LIVE_OPTIONS => key in LIVE_OPTIONS;\n\nconst optionsOf = (props: Partial<IsardMapProps>): GeoMapOptions =>\n Object.fromEntries(Object.entries(props).filter(([key]) => !NOT_OPTIONS.includes(key))) as GeoMapOptions;\n\nconst sameCamera = (a: Camera, b: Partial<Camera>): boolean =>\n (b.center === undefined || (a.center[0] === b.center[0] && a.center[1] === b.center[1])) &&\n (b.zoom === undefined || a.zoom === b.zoom) &&\n (b.pitch === undefined || a.pitch === b.pitch) &&\n (b.bearing === undefined || a.bearing === b.bearing);\n\n/** What the components of a new map share */\nconst bindingOf = (map: GeoMap): Binding => {\n const viewsheds = new Map<object, ViewshedOptions>();\n\n return {\n map,\n order: new LayerOrder(),\n viewsheds,\n applyViewsheds: () => map.setViewshed(viewsheds.size > 0 ? [...viewsheds.values()] : undefined),\n };\n};\n\n/**\n * A map filling its element (give it a height). Created once mounted, destroyed when unmounted; the options read at\n * creation only (`body`, `preset`, `interactive`, the URLs) need a new `key` to take another value\n */\nexport const IsardMap = forwardRef<GeoMap | undefined, IsardMapProps>(function IsardMap(props, ref) {\n const element = useRef<HTMLDivElement>(null);\n const latest = useRef(props);\n const applied = useRef<IsardMapProps>(props);\n const [binding, setBinding] = useState<Binding>();\n const map = binding?.map;\n const { camera, hoverPicking } = props;\n\n latest.current = props;\n useImperativeHandle(ref, () => map, [map]);\n\n // Created once: StrictMode's second mount and an unmount before the engine is loaded drop the map they started\n useEffect(() => {\n const container = element.current;\n\n if (!container) return;\n\n const created: { map?: GeoMap; cancelled: boolean } = { cancelled: false };\n const { camera, center, zoom, pitch, bearing, children: _, ...options } = latest.current;\n\n GeoMap.create(container, { ...optionsOf(options), ...defined({ center, zoom, pitch, bearing }), ...camera }).then(\n (map) => {\n if (created.cancelled) {\n map.destroy();\n\n return;\n }\n\n created.map = map;\n setBinding(bindingOf(map));\n latest.current.onLoad?.(map);\n },\n (error: unknown) => {\n if (created.cancelled) return;\n if (latest.current.onError) latest.current.onError(error);\n else console.error(error);\n },\n );\n\n return () => {\n created.cancelled = true;\n created.map?.destroy();\n setBinding(undefined);\n };\n }, []);\n\n // The events, each listened to once: the props may give another function at each render\n useEffect(() => {\n if (!map) return;\n\n const offs = Object.keys(EVENT_PROPS)\n .filter(isEventProp)\n .map((prop) =>\n map.on(EVENT_PROPS[prop], (event) => (latest.current[prop] as ((e: unknown) => void) | undefined)?.(event)),\n );\n\n return () => {\n offs.forEach((off) => {\n off();\n });\n };\n }, [map]);\n\n // The options taken again, compared with what the map was given last\n useEffect(() => {\n if (!map) return;\n\n const last = applied.current;\n const done = new Set<(map: GeoMap, props: IsardMapProps) => void>();\n\n applied.current = props;\n Object.keys(LIVE_OPTIONS)\n .filter(isLiveOption)\n .forEach((key) => {\n const apply = LIVE_OPTIONS[key];\n const changed = !Object.is(last[key], props[key]);\n\n if (!apply || done.has(apply) || !changed) return;\n done.add(apply);\n apply(map, props);\n });\n });\n\n // What `onHoverFeature` picks, given to the map whenever the prop changes\n useEffect(() => {\n if (map && hoverPicking) map.hoverPicking = hoverPicking;\n }, [map, hoverPicking]);\n\n // A controlled camera: set when the prop changes, unless the map is already there (a gesture reported by `onMove`)\n useEffect(() => {\n if (map && camera && !sameCamera(map.getCamera(), camera)) map.setCamera(camera);\n }, [map, camera]);\n\n return (\n <div ref={element} id={props.id} className={props.className} style={props.style}>\n {binding ? <MapContext.Provider value={binding}>{props.children}</MapContext.Provider> : null}\n </div>\n );\n});\n"]}
@@ -0,0 +1,42 @@
1
+ /** `<Layer>`: a layer added when it mounts, updated by the least its props need, removed when it unmounts, in its
2
+ * declared order */
3
+ import { type ReactNode, type Ref } from 'react';
4
+ import { type BoundLayerSpec } from '../bind/layers.ts';
5
+ import type { ExtrusionLayer } from '../layers/extrusions.ts';
6
+ import type { CircleLayer, FillLayer, LineLayer } from '../layers/features.ts';
7
+ import type { GroundLayer, ModelLayer, NetworkLayer, RasterLayer, Tiles3dLayer } from '../layers/layers.ts';
8
+ import type { PointCloudLayer } from '../layers/pointclouds.ts';
9
+ import type { SymbolLayer } from '../layers/symbols.ts';
10
+ import type { LayerSpec } from '../map.ts';
11
+ /** The handle of a layer by its type */
12
+ export interface LayerHandles<P = Record<string, unknown>> {
13
+ lines: LineLayer<P>;
14
+ circles: CircleLayer<P>;
15
+ fills: FillLayer<P>;
16
+ symbols: SymbolLayer<P>;
17
+ extrusions: ExtrusionLayer<P>;
18
+ raster: RasterLayer;
19
+ ground: GroundLayer;
20
+ network: NetworkLayer;
21
+ tiles3d: Tiles3dLayer;
22
+ pointcloud: PointCloudLayer;
23
+ models: ModelLayer;
24
+ }
25
+ export type LayerType = LayerSpec['type'];
26
+ export type LayerProps<P = Record<string, unknown>, T extends LayerType = LayerType> = Extract<BoundLayerSpec<P>, {
27
+ type: T;
28
+ }> & {
29
+ /** Its place among the layers of the components: from the bottom up, the same order by mounting (default 0) */
30
+ order?: number;
31
+ /** The layer, once created and each time it is made anew */
32
+ onLayer?: (layer: LayerHandles<P>[T]) => void;
33
+ ref?: Ref<LayerHandles<P>[T] | undefined>;
34
+ };
35
+ /**
36
+ * A layer: the options of its `add…` method with its `type` (`map.add`), and `values` for the layers colored by
37
+ * values. A change of data, style, filter, values or instances updates it; a change of what is set at creation (its
38
+ * URL, grid, nodes…) makes it anew, in its place. Data, functions and arrays are compared by identity: keep them in a
39
+ * memo or a state. The ref and `onLayer` give its handle, typed by its `type`; the properties of its features are
40
+ * inferred from its data or its style functions (`<Layer<Building> …>` to name them)
41
+ */
42
+ export declare const Layer: <P = Record<string, unknown>, T extends LayerType = LayerType>(props: LayerProps<P, T>) => ReactNode;
@@ -0,0 +1,74 @@
1
+ /** `<Layer>`: a layer added when it mounts, updated by the least its props need, removed when it unmounts, in its
2
+ * declared order */
3
+ import { forwardRef, useContext, useEffect, useImperativeHandle, useRef, useState, } from 'react';
4
+ import { addBoundLayer, applyLayerChange, layerChange, rebuildBoundLayer, } from "../bind/layers.js";
5
+ import { MapContext, SwipeContext } from "./context.js";
6
+ const LayerComponent = forwardRef(function Layer(props, ref) {
7
+ const binding = useContext(MapContext);
8
+ const swipe = useContext(SwipeContext);
9
+ const { order = 0, onLayer, ...rest } = props;
10
+ const spec = rest;
11
+ const mounted = useRef(undefined);
12
+ const latest = useRef({ spec, order, onLayer });
13
+ const [layer, setLayer] = useState();
14
+ latest.current = { spec, order, onLayer };
15
+ useImperativeHandle(ref, () => layer, [layer]);
16
+ // Added once the map is there, removed when unmounted (or when the map goes)
17
+ useEffect(() => {
18
+ if (!binding)
19
+ return;
20
+ const { spec, order, onLayer } = latest.current;
21
+ const created = addBoundLayer(binding.map, spec);
22
+ binding.order.place(created, order);
23
+ mounted.current = { layer: created, spec, order };
24
+ swipe?.set(created);
25
+ setLayer(created);
26
+ onLayer?.(created);
27
+ return () => {
28
+ const current = mounted.current;
29
+ mounted.current = undefined;
30
+ if (!current)
31
+ return;
32
+ binding.order.remove(current.layer);
33
+ swipe?.unset(current.layer);
34
+ if (binding.map.hasLayer(current.layer))
35
+ current.layer.remove();
36
+ };
37
+ }, [binding, swipe]);
38
+ // Each render: what changed sent, or the layer made anew in its place
39
+ useEffect(() => {
40
+ const current = mounted.current;
41
+ if (!binding || !current)
42
+ return;
43
+ const change = layerChange(current.spec, spec);
44
+ current.spec = spec;
45
+ if (change?.rebuild) {
46
+ const old = current.layer;
47
+ const fresh = rebuildBoundLayer(binding.map, old, spec);
48
+ binding.order.place(fresh, order, old);
49
+ swipe?.unset(old);
50
+ swipe?.set(fresh);
51
+ current.layer = fresh;
52
+ current.order = order;
53
+ setLayer(fresh);
54
+ latest.current.onLayer?.(fresh);
55
+ return;
56
+ }
57
+ if (change)
58
+ applyLayerChange(current.layer, spec, change);
59
+ if (current.order !== order) {
60
+ current.order = order;
61
+ binding.order.place(current.layer, order, current.layer);
62
+ }
63
+ });
64
+ return null;
65
+ });
66
+ /**
67
+ * A layer: the options of its `add…` method with its `type` (`map.add`), and `values` for the layers colored by
68
+ * values. A change of data, style, filter, values or instances updates it; a change of what is set at creation (its
69
+ * URL, grid, nodes…) makes it anew, in its place. Data, functions and arrays are compared by identity: keep them in a
70
+ * memo or a state. The ref and `onLayer` give its handle, typed by its `type`; the properties of its features are
71
+ * inferred from its data or its style functions (`<Layer<Building> …>` to name them)
72
+ */
73
+ export const Layer = LayerComponent;
74
+ //# sourceMappingURL=Layer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Layer.js","sourceRoot":"","sources":["../../src/react/Layer.tsx"],"names":[],"mappings":"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","sourcesContent":["/** `<Layer>`: a layer added when it mounts, updated by the least its props need, removed when it unmounts, in its\n * declared order */\n\nimport {\n forwardRef,\n type ReactNode,\n type Ref,\n useContext,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from 'react';\nimport {\n addBoundLayer,\n applyLayerChange,\n type BoundLayerSpec,\n layerChange,\n rebuildBoundLayer,\n} from '../bind/layers.ts';\nimport type { ExtrusionLayer } from '../layers/extrusions.ts';\nimport type { CircleLayer, FillLayer, LineLayer } from '../layers/features.ts';\nimport type {\n GroundLayer,\n Layer as LayerHandle,\n ModelLayer,\n NetworkLayer,\n RasterLayer,\n Tiles3dLayer,\n} from '../layers/layers.ts';\nimport type { PointCloudLayer } from '../layers/pointclouds.ts';\nimport type { SymbolLayer } from '../layers/symbols.ts';\nimport type { LayerSpec } from '../map.ts';\nimport { MapContext, SwipeContext } from './context.ts';\n\n/** The handle of a layer by its type */\nexport interface LayerHandles<P = Record<string, unknown>> {\n lines: LineLayer<P>;\n circles: CircleLayer<P>;\n fills: FillLayer<P>;\n symbols: SymbolLayer<P>;\n extrusions: ExtrusionLayer<P>;\n raster: RasterLayer;\n ground: GroundLayer;\n network: NetworkLayer;\n tiles3d: Tiles3dLayer;\n pointcloud: PointCloudLayer;\n models: ModelLayer;\n}\n\nexport type LayerType = LayerSpec['type'];\n\nexport type LayerProps<P = Record<string, unknown>, T extends LayerType = LayerType> = Extract<\n BoundLayerSpec<P>,\n { type: T }\n> & {\n /** Its place among the layers of the components: from the bottom up, the same order by mounting (default 0) */\n order?: number;\n /** The layer, once created and each time it is made anew */\n onLayer?: (layer: LayerHandles<P>[T]) => void;\n ref?: Ref<LayerHandles<P>[T] | undefined>;\n};\n\ninterface Mounted {\n layer: LayerHandle;\n spec: BoundLayerSpec;\n order: number;\n}\n\nconst LayerComponent = forwardRef<LayerHandle | undefined, Omit<LayerProps, 'ref'>>(function Layer(props, ref) {\n const binding = useContext(MapContext);\n const swipe = useContext(SwipeContext);\n const { order = 0, onLayer, ...rest } = props;\n const spec = rest as BoundLayerSpec;\n const mounted = useRef<Mounted>(undefined);\n const latest = useRef({ spec, order, onLayer });\n const [layer, setLayer] = useState<LayerHandle>();\n\n latest.current = { spec, order, onLayer };\n useImperativeHandle(ref, () => layer, [layer]);\n\n // Added once the map is there, removed when unmounted (or when the map goes)\n useEffect(() => {\n if (!binding) return;\n\n const { spec, order, onLayer } = latest.current;\n const created = addBoundLayer(binding.map, spec);\n\n binding.order.place(created, order);\n mounted.current = { layer: created, spec, order };\n swipe?.set(created);\n setLayer(created);\n onLayer?.(created as never);\n\n return () => {\n const current = mounted.current;\n\n mounted.current = undefined;\n if (!current) return;\n binding.order.remove(current.layer);\n swipe?.unset(current.layer);\n if (binding.map.hasLayer(current.layer)) current.layer.remove();\n };\n }, [binding, swipe]);\n\n // Each render: what changed sent, or the layer made anew in its place\n useEffect(() => {\n const current = mounted.current;\n\n if (!binding || !current) return;\n\n const change = layerChange(current.spec, spec);\n\n current.spec = spec;\n if (change?.rebuild) {\n const old = current.layer;\n const fresh = rebuildBoundLayer(binding.map, old, spec);\n\n binding.order.place(fresh, order, old);\n swipe?.unset(old);\n swipe?.set(fresh);\n current.layer = fresh;\n current.order = order;\n setLayer(fresh);\n latest.current.onLayer?.(fresh as never);\n\n return;\n }\n\n if (change) applyLayerChange(current.layer, spec, change);\n if (current.order !== order) {\n current.order = order;\n binding.order.place(current.layer, order, current.layer);\n }\n });\n\n return null;\n});\n\n/**\n * A layer: the options of its `add…` method with its `type` (`map.add`), and `values` for the layers colored by\n * values. A change of data, style, filter, values or instances updates it; a change of what is set at creation (its\n * URL, grid, nodes…) makes it anew, in its place. Data, functions and arrays are compared by identity: keep them in a\n * memo or a state. The ref and `onLayer` give its handle, typed by its `type`; the properties of its features are\n * inferred from its data or its style functions (`<Layer<Building> …>` to name them)\n */\nexport const Layer = LayerComponent as <P = Record<string, unknown>, T extends LayerType = LayerType>(\n props: LayerProps<P, T>,\n) => ReactNode;\n"]}
@@ -0,0 +1,24 @@
1
+ /** `<Marker>`: an element at a position, the default pin or JSX, its `<Popup>` opened by its click */
2
+ import { type ReactNode } from 'react';
3
+ import type { LonLat, Point } from '../types.ts';
4
+ import { type Anchor } from '../ui/overlay.ts';
5
+ export interface MarkerProps {
6
+ lonLat: LonLat;
7
+ /** Metres above the ground, its stem down to `base` (what it stands on: a roof, a mesh) */
8
+ height?: number;
9
+ base?: number;
10
+ draggable?: boolean;
11
+ offset?: Point;
12
+ /** Which part of the element sits on the position (default 'bottom' for the pin, 'center' with an icon) */
13
+ anchor?: Anchor;
14
+ className?: string;
15
+ /** The marker's look, in place of the default pin; the element changes with it */
16
+ icon?: ReactNode;
17
+ onDragStart?: (lonLat: LonLat) => void;
18
+ onDrag?: (lonLat: LonLat) => void;
19
+ onDragEnd?: (lonLat: LonLat) => void;
20
+ /** A `<Popup>` opened by a click on the marker */
21
+ children?: ReactNode;
22
+ }
23
+ /** A marker at a position, its look the default pin or the `icon` given */
24
+ export declare const Marker: (props: MarkerProps) => ReactNode;
@@ -0,0 +1,53 @@
1
+ import { jsxs as _jsxs } from "react/jsx-runtime";
2
+ /** `<Marker>`: an element at a position, the default pin or JSX, its `<Popup>` opened by its click */
3
+ import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
4
+ import { createPortal } from 'react-dom';
5
+ import { Marker as MarkerHandle } from "../ui/overlay.js";
6
+ import { defined, MarkerContext, useMap } from "./context.js";
7
+ /** A marker at a position, its look the default pin or the `icon` given */
8
+ export const Marker = (props) => {
9
+ const map = useMap();
10
+ const latest = useRef(props);
11
+ const { anchor, className, lonLat, height = 0, base = 0, draggable = false, offset } = props;
12
+ const custom = props.icon !== undefined;
13
+ const element = useMemo(() => (custom ? document.createElement('div') : undefined), [custom]);
14
+ const marker = useMemo(() => new MarkerHandle({ ...defined({ anchor, className, element }) }), [anchor, className, element]);
15
+ const context = useMemo(() => ({ marker, lonLat }), [marker, lonLat]);
16
+ latest.current = props;
17
+ // On the map once it is there, its drag events forwarded to the latest props; off the map when unmounted
18
+ useEffect(() => {
19
+ if (!map)
20
+ return;
21
+ const offs = [
22
+ marker.on('dragstart', (e) => latest.current.onDragStart?.(e.lonLat)),
23
+ marker.on('drag', (e) => latest.current.onDrag?.(e.lonLat)),
24
+ marker.on('dragend', (e) => latest.current.onDragEnd?.(e.lonLat)),
25
+ ];
26
+ marker.addTo(map);
27
+ return () => {
28
+ offs.forEach((off) => {
29
+ off();
30
+ });
31
+ marker.remove();
32
+ };
33
+ }, [map, marker]);
34
+ // Its position, before the browser paints (no frame at the old place)
35
+ useLayoutEffect(() => {
36
+ marker.setLonLat(lonLat);
37
+ }, [marker, lonLat[0], lonLat[1]]);
38
+ // Its height above what it stands on
39
+ useLayoutEffect(() => {
40
+ marker.setHeight(height, base);
41
+ }, [marker, height, base]);
42
+ // Draggable or not
43
+ useLayoutEffect(() => {
44
+ marker.setDraggable(draggable);
45
+ }, [marker, draggable]);
46
+ // Its shift from the anchor
47
+ useLayoutEffect(() => {
48
+ if (offset)
49
+ marker.setOffset(offset);
50
+ }, [marker, offset?.[0], offset?.[1]]);
51
+ return (_jsxs(MarkerContext.Provider, { value: context, children: [element ? createPortal(props.icon, element) : null, props.children] }));
52
+ };
53
+ //# sourceMappingURL=Marker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Marker.js","sourceRoot":"","sources":["../../src/react/Marker.tsx"],"names":[],"mappings":"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","sourcesContent":["/** `<Marker>`: an element at a position, the default pin or JSX, its `<Popup>` opened by its click */\n\nimport { type ReactNode, useEffect, useLayoutEffect, useMemo, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport type { LonLat, Point } from '../types.ts';\nimport { type Anchor, Marker as MarkerHandle } from '../ui/overlay.ts';\nimport { defined, MarkerContext, useMap } from './context.ts';\n\nexport interface MarkerProps {\n lonLat: LonLat;\n /** Metres above the ground, its stem down to `base` (what it stands on: a roof, a mesh) */\n height?: number;\n base?: number;\n draggable?: boolean;\n offset?: Point;\n /** Which part of the element sits on the position (default 'bottom' for the pin, 'center' with an icon) */\n anchor?: Anchor;\n className?: string;\n /** The marker's look, in place of the default pin; the element changes with it */\n icon?: ReactNode;\n onDragStart?: (lonLat: LonLat) => void;\n onDrag?: (lonLat: LonLat) => void;\n onDragEnd?: (lonLat: LonLat) => void;\n /** A `<Popup>` opened by a click on the marker */\n children?: ReactNode;\n}\n\n/** A marker at a position, its look the default pin or the `icon` given */\nexport const Marker = (props: MarkerProps): ReactNode => {\n const map = useMap();\n const latest = useRef(props);\n const { anchor, className, lonLat, height = 0, base = 0, draggable = false, offset } = props;\n const custom = props.icon !== undefined;\n const element = useMemo(() => (custom ? document.createElement('div') : undefined), [custom]);\n const marker = useMemo(\n () => new MarkerHandle({ ...defined({ anchor, className, element }) }),\n [anchor, className, element],\n );\n const context = useMemo(() => ({ marker, lonLat }), [marker, lonLat]);\n\n latest.current = props;\n\n // On the map once it is there, its drag events forwarded to the latest props; off the map when unmounted\n useEffect(() => {\n if (!map) return;\n\n const offs = [\n marker.on('dragstart', (e) => latest.current.onDragStart?.(e.lonLat)),\n marker.on('drag', (e) => latest.current.onDrag?.(e.lonLat)),\n marker.on('dragend', (e) => latest.current.onDragEnd?.(e.lonLat)),\n ];\n\n marker.addTo(map);\n\n return () => {\n offs.forEach((off) => {\n off();\n });\n marker.remove();\n };\n }, [map, marker]);\n\n // Its position, before the browser paints (no frame at the old place)\n useLayoutEffect(() => {\n marker.setLonLat(lonLat);\n }, [marker, lonLat[0], lonLat[1]]);\n // Its height above what it stands on\n useLayoutEffect(() => {\n marker.setHeight(height, base);\n }, [marker, height, base]);\n // Draggable or not\n useLayoutEffect(() => {\n marker.setDraggable(draggable);\n }, [marker, draggable]);\n // Its shift from the anchor\n useLayoutEffect(() => {\n if (offset) marker.setOffset(offset);\n }, [marker, offset?.[0], offset?.[1]]);\n\n return (\n <MarkerContext.Provider value={context}>\n {element ? createPortal(props.icon, element) : null}\n {props.children}\n </MarkerContext.Provider>\n );\n};\n"]}
@@ -0,0 +1,11 @@
1
+ /** `<Panel>`: JSX standing in 3D at a position */
2
+ import { type ReactNode } from 'react';
3
+ import type { LonLat } from '../types.ts';
4
+ import { type PanelOptions } from '../ui/overlay.ts';
5
+ export interface PanelProps extends Omit<PanelOptions, 'element'> {
6
+ lonLat: LonLat;
7
+ /** What stands in 3D: a video, an iframe, a sign */
8
+ children?: ReactNode;
9
+ }
10
+ /** JSX standing in 3D at a position, `width` metres wide, facing `heading` */
11
+ export declare const Panel: (props: PanelProps) => ReactNode;
@@ -0,0 +1,37 @@
1
+ /** `<Panel>`: JSX standing in 3D at a position */
2
+ import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
3
+ import { createPortal } from 'react-dom';
4
+ import { Panel as PanelHandle } from "../ui/overlay.js";
5
+ import { defined, useMap } from "./context.js";
6
+ /** JSX standing in 3D at a position, `width` metres wide, facing `heading` */
7
+ export const Panel = (props) => {
8
+ const map = useMap();
9
+ const { className, lonLat, width, heading, pitch, height } = props;
10
+ // The width at creation: the placement is set again by the effect below
11
+ const initialWidth = useRef(width);
12
+ const element = useMemo(() => document.createElement('div'), []);
13
+ const panel = useMemo(() => new PanelHandle({ element, width: initialWidth.current, ...defined({ className }) }), [element, className]);
14
+ // On the map once it is there; off it when unmounted
15
+ useEffect(() => {
16
+ if (!map)
17
+ return;
18
+ panel.addTo(map);
19
+ return () => {
20
+ panel.remove();
21
+ };
22
+ }, [map, panel]);
23
+ // Its position, before the browser paints
24
+ useLayoutEffect(() => {
25
+ panel.setLonLat(lonLat);
26
+ }, [panel, lonLat[0], lonLat[1]]);
27
+ // Its size and orientation: width, heading, pitch, height
28
+ useLayoutEffect(() => {
29
+ panel.setPlacement(defined({ width, heading, pitch, height }));
30
+ }, [panel, width, heading, pitch, height]);
31
+ // Its content may change its CSS size
32
+ useLayoutEffect(() => {
33
+ panel.resize();
34
+ });
35
+ return createPortal(props.children, element);
36
+ };
37
+ //# sourceMappingURL=Panel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Panel.js","sourceRoot":"","sources":["../../src/react/Panel.tsx"],"names":[],"mappings":"AAAA,kDAAkD;AAElD,OAAO,EAAkB,SAAS,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,EAAE,KAAK,IAAI,WAAW,EAAqB,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAQ/C,8EAA8E;AAC9E,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,KAAiB,EAAa,EAAE;IACpD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IACnE,wEAAwE;IACxE,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,IAAI,WAAW,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,EAC1F,CAAC,OAAO,EAAE,SAAS,CAAC,CACrB,CAAC;IAEF,qDAAqD;IACrD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAEjB,OAAO,GAAG,EAAE;YACV,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;IAEjB,0CAA0C;IAC1C,eAAe,CAAC,GAAG,EAAE;QACnB,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClC,0DAA0D;IAC1D,eAAe,CAAC,GAAG,EAAE;QACnB,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IACjE,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3C,sCAAsC;IACtC,eAAe,CAAC,GAAG,EAAE;QACnB,KAAK,CAAC,MAAM,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;IAEH,OAAO,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC,CAAC","sourcesContent":["/** `<Panel>`: JSX standing in 3D at a position */\n\nimport { type ReactNode, useEffect, useLayoutEffect, useMemo, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport type { LonLat } from '../types.ts';\nimport { Panel as PanelHandle, type PanelOptions } from '../ui/overlay.ts';\nimport { defined, useMap } from './context.ts';\n\nexport interface PanelProps extends Omit<PanelOptions, 'element'> {\n lonLat: LonLat;\n /** What stands in 3D: a video, an iframe, a sign */\n children?: ReactNode;\n}\n\n/** JSX standing in 3D at a position, `width` metres wide, facing `heading` */\nexport const Panel = (props: PanelProps): ReactNode => {\n const map = useMap();\n const { className, lonLat, width, heading, pitch, height } = props;\n // The width at creation: the placement is set again by the effect below\n const initialWidth = useRef(width);\n const element = useMemo(() => document.createElement('div'), []);\n const panel = useMemo(\n () => new PanelHandle({ element, width: initialWidth.current, ...defined({ className }) }),\n [element, className],\n );\n\n // On the map once it is there; off it when unmounted\n useEffect(() => {\n if (!map) return;\n panel.addTo(map);\n\n return () => {\n panel.remove();\n };\n }, [map, panel]);\n\n // Its position, before the browser paints\n useLayoutEffect(() => {\n panel.setLonLat(lonLat);\n }, [panel, lonLat[0], lonLat[1]]);\n // Its size and orientation: width, heading, pitch, height\n useLayoutEffect(() => {\n panel.setPlacement(defined({ width, heading, pitch, height }));\n }, [panel, width, heading, pitch, height]);\n // Its content may change its CSS size\n useLayoutEffect(() => {\n panel.resize();\n });\n\n return createPortal(props.children, element);\n};\n"]}
@@ -0,0 +1,18 @@
1
+ /** `<Popup>`: a box of JSX content at a position or on a `<Marker>` */
2
+ import { type ReactNode } from 'react';
3
+ import type { LonLat } from '../types.ts';
4
+ import { type PopupOptions } from '../ui/overlay.ts';
5
+ export interface PopupProps extends PopupOptions {
6
+ /** Its position; in a `<Marker>`, the marker's */
7
+ lonLat?: LonLat;
8
+ /** Metres above the ground (in a `<Marker>`, the marker's) */
9
+ height?: number;
10
+ /** Open or closed, when the application decides; else open when mounted alone, on the marker's click in a marker */
11
+ open?: boolean;
12
+ onOpen?: () => void;
13
+ /** Closed by its button, a click on the map, or the marker's click */
14
+ onClose?: () => void;
15
+ children?: ReactNode;
16
+ }
17
+ /** A popup of JSX content, at a position or on a `<Marker>` */
18
+ export declare const Popup: (props: PopupProps) => ReactNode;
@@ -0,0 +1,60 @@
1
+ /** `<Popup>`: a box of JSX content at a position or on a `<Marker>` */
2
+ import { useContext, useEffect, useLayoutEffect, useMemo, useRef } from 'react';
3
+ import { createPortal } from 'react-dom';
4
+ import { Popup as PopupHandle } from "../ui/overlay.js";
5
+ import { defined, MarkerContext, useMap } from "./context.js";
6
+ /** A popup of JSX content, at a position or on a `<Marker>` */
7
+ export const Popup = (props) => {
8
+ const map = useMap();
9
+ const inMarker = useContext(MarkerContext);
10
+ const latest = useRef(props);
11
+ const { anchor, closeButton, closeOnClick, maxWidth, className, height, open, offset } = props;
12
+ const marker = inMarker?.marker;
13
+ const lonLat = props.lonLat ?? inMarker?.lonLat;
14
+ const opened = open ?? !marker;
15
+ const content = useMemo(() => document.createElement('div'), []);
16
+ const popup = useMemo(() => new PopupHandle(defined({ anchor, closeButton, closeOnClick, maxWidth, className })).setDOMContent(content), [anchor, closeButton, closeOnClick, maxWidth, className, content]);
17
+ latest.current = props;
18
+ // Its open and close events forwarded to the latest props, and tied to the marker around (its click opens it); removed when unmounted
19
+ useEffect(() => {
20
+ if (!map)
21
+ return;
22
+ const offs = [
23
+ popup.on('open', () => latest.current.onOpen?.()),
24
+ popup.on('close', () => latest.current.onClose?.()),
25
+ ];
26
+ if (marker)
27
+ marker.setPopup(popup);
28
+ return () => {
29
+ offs.forEach((off) => {
30
+ off();
31
+ });
32
+ if (marker?.getPopup() === popup)
33
+ marker.setPopup(undefined);
34
+ popup.remove();
35
+ };
36
+ }, [map, marker, popup]);
37
+ // Its shift from the anchor
38
+ useLayoutEffect(() => {
39
+ if (offset)
40
+ popup.setOffset(offset);
41
+ }, [popup, offset?.[0], offset?.[1]]);
42
+ // Its position, height and, when `open` decides or it stands alone, open or closed; before the browser paints
43
+ useLayoutEffect(() => {
44
+ if (!map)
45
+ return;
46
+ if (lonLat)
47
+ popup.setLonLat(lonLat);
48
+ if (height !== undefined)
49
+ popup.setHeight(height);
50
+ // In a marker without `open`: its click decides
51
+ if (open === undefined && marker)
52
+ return;
53
+ if (opened && !popup.isOpen())
54
+ popup.addTo(map);
55
+ if (!opened && popup.isOpen())
56
+ popup.remove();
57
+ }, [map, marker, popup, lonLat?.[0], lonLat?.[1], height, open, opened]);
58
+ return createPortal(props.children, content);
59
+ };
60
+ //# sourceMappingURL=Popup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Popup.js","sourceRoot":"","sources":["../../src/react/Popup.tsx"],"names":[],"mappings":"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","sourcesContent":["/** `<Popup>`: a box of JSX content at a position or on a `<Marker>` */\n\nimport { type ReactNode, useContext, useEffect, useLayoutEffect, useMemo, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport type { LonLat } from '../types.ts';\nimport { Popup as PopupHandle, type PopupOptions } from '../ui/overlay.ts';\nimport { defined, MarkerContext, useMap } from './context.ts';\n\nexport interface PopupProps extends PopupOptions {\n /** Its position; in a `<Marker>`, the marker's */\n lonLat?: LonLat;\n /** Metres above the ground (in a `<Marker>`, the marker's) */\n height?: number;\n /** Open or closed, when the application decides; else open when mounted alone, on the marker's click in a marker */\n open?: boolean;\n onOpen?: () => void;\n /** Closed by its button, a click on the map, or the marker's click */\n onClose?: () => void;\n children?: ReactNode;\n}\n\n/** A popup of JSX content, at a position or on a `<Marker>` */\nexport const Popup = (props: PopupProps): ReactNode => {\n const map = useMap();\n const inMarker = useContext(MarkerContext);\n const latest = useRef(props);\n const { anchor, closeButton, closeOnClick, maxWidth, className, height, open, offset } = props;\n const marker = inMarker?.marker;\n const lonLat = props.lonLat ?? inMarker?.lonLat;\n const opened = open ?? !marker;\n const content = useMemo(() => document.createElement('div'), []);\n const popup = useMemo(\n () => new PopupHandle(defined({ anchor, closeButton, closeOnClick, maxWidth, className })).setDOMContent(content),\n [anchor, closeButton, closeOnClick, maxWidth, className, content],\n );\n\n latest.current = props;\n\n // Its open and close events forwarded to the latest props, and tied to the marker around (its click opens it); removed when unmounted\n useEffect(() => {\n if (!map) return;\n\n const offs = [\n popup.on('open', () => latest.current.onOpen?.()),\n popup.on('close', () => latest.current.onClose?.()),\n ];\n\n if (marker) marker.setPopup(popup);\n\n return () => {\n offs.forEach((off) => {\n off();\n });\n if (marker?.getPopup() === popup) marker.setPopup(undefined);\n popup.remove();\n };\n }, [map, marker, popup]);\n\n // Its shift from the anchor\n useLayoutEffect(() => {\n if (offset) popup.setOffset(offset);\n }, [popup, offset?.[0], offset?.[1]]);\n // Its position, height and, when `open` decides or it stands alone, open or closed; before the browser paints\n useLayoutEffect(() => {\n if (!map) return;\n if (lonLat) popup.setLonLat(lonLat);\n if (height !== undefined) popup.setHeight(height);\n // In a marker without `open`: its click decides\n if (open === undefined && marker) return;\n if (opened && !popup.isOpen()) popup.addTo(map);\n if (!opened && popup.isOpen()) popup.remove();\n }, [map, marker, popup, lonLat?.[0], lonLat?.[1], height, open, opened]);\n\n return createPortal(props.children, content);\n};\n"]}
@@ -0,0 +1,9 @@
1
+ /** `<Swipe>`: the `<Layer>` in it on one side of a vertical line only */
2
+ import { type ReactNode } from 'react';
3
+ import type { SwipeOptions } from '../types.ts';
4
+ export interface SwipeProps extends Omit<SwipeOptions, 'layers'> {
5
+ /** The `<Layer>` shown on one side of the line only */
6
+ children?: ReactNode;
7
+ }
8
+ /** The `<Layer>` in it shown on one side of a vertical line only, at `position` CSS pixels from the left */
9
+ export declare const Swipe: (props: SwipeProps) => ReactNode;