@element-editor/adapter-leaflet 0.0.0-stage → 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.
Files changed (119) hide show
  1. package/AGENTS.md +80 -0
  2. package/LICENSE +21 -0
  3. package/README.md +45 -2
  4. package/dist/ElementEditorControls.d.cts +111 -0
  5. package/dist/ElementEditorControls.d.ts +111 -0
  6. package/dist/adapter-leaflet.css +1 -0
  7. package/dist/components/BoundaryLayer.d.cts +5 -0
  8. package/dist/components/BoundaryLayer.d.ts +5 -0
  9. package/dist/components/DrawingLayer.d.cts +3 -0
  10. package/dist/components/DrawingLayer.d.ts +3 -0
  11. package/dist/components/DrawingPreviewLayer.d.cts +1 -0
  12. package/dist/components/DrawingPreviewLayer.d.ts +1 -0
  13. package/dist/components/FeatureInteractionLayer.d.cts +6 -0
  14. package/dist/components/FeatureInteractionLayer.d.ts +6 -0
  15. package/dist/components/GeometryProcessingPreview.d.cts +1 -0
  16. package/dist/components/GeometryProcessingPreview.d.ts +1 -0
  17. package/dist/components/MeasurePreviewLayer.d.cts +1 -0
  18. package/dist/components/MeasurePreviewLayer.d.ts +1 -0
  19. package/dist/components/MeasurementTooltip.d.cts +1 -0
  20. package/dist/components/MeasurementTooltip.d.ts +1 -0
  21. package/dist/components/PendingPointMarker.d.cts +2 -0
  22. package/dist/components/PendingPointMarker.d.ts +2 -0
  23. package/dist/components/RoutePreviewLayer.d.cts +1 -0
  24. package/dist/components/RoutePreviewLayer.d.ts +1 -0
  25. package/dist/components/SnapController.d.cts +1 -0
  26. package/dist/components/SnapController.d.ts +1 -0
  27. package/dist/components/TextLabelLayer.d.cts +10 -0
  28. package/dist/components/TextLabelLayer.d.ts +10 -0
  29. package/dist/components/TransformControls.d.cts +1 -0
  30. package/dist/components/TransformControls.d.ts +1 -0
  31. package/dist/components/featureLayers/FeatureElements.d.cts +28 -0
  32. package/dist/components/featureLayers/FeatureElements.d.ts +28 -0
  33. package/dist/components/featureLayers/GeometryCollectionLayer.d.cts +13 -0
  34. package/dist/components/featureLayers/GeometryCollectionLayer.d.ts +13 -0
  35. package/dist/components/featureLayers/LineLayer.d.cts +14 -0
  36. package/dist/components/featureLayers/LineLayer.d.ts +14 -0
  37. package/dist/components/featureLayers/MultiLineLayer.d.cts +13 -0
  38. package/dist/components/featureLayers/MultiLineLayer.d.ts +13 -0
  39. package/dist/components/featureLayers/MultiPointLayer.d.cts +12 -0
  40. package/dist/components/featureLayers/MultiPointLayer.d.ts +12 -0
  41. package/dist/components/featureLayers/MultiPolygonLayer.d.cts +13 -0
  42. package/dist/components/featureLayers/MultiPolygonLayer.d.ts +13 -0
  43. package/dist/components/featureLayers/POIMarker.d.cts +13 -0
  44. package/dist/components/featureLayers/POIMarker.d.ts +13 -0
  45. package/dist/components/featureLayers/PolygonLayer.d.cts +14 -0
  46. package/dist/components/featureLayers/PolygonLayer.d.ts +14 -0
  47. package/dist/components/featureLayers/mountQueue.d.cts +41 -0
  48. package/dist/components/featureLayers/mountQueue.d.ts +41 -0
  49. package/dist/components/featureLayers/renderer.d.cts +25 -0
  50. package/dist/components/featureLayers/renderer.d.ts +25 -0
  51. package/dist/components/featureLayers/shared.d.cts +44 -0
  52. package/dist/components/featureLayers/shared.d.ts +44 -0
  53. package/dist/components/featureLayers/useProgressiveMount.d.cts +30 -0
  54. package/dist/components/featureLayers/useProgressiveMount.d.ts +30 -0
  55. package/dist/components/previews/CirclePreview.d.cts +1 -0
  56. package/dist/components/previews/CirclePreview.d.ts +1 -0
  57. package/dist/components/previews/LinePreview.d.cts +1 -0
  58. package/dist/components/previews/LinePreview.d.ts +1 -0
  59. package/dist/components/previews/PolygonPreview.d.cts +1 -0
  60. package/dist/components/previews/PolygonPreview.d.ts +1 -0
  61. package/dist/components/previews/RectPreview.d.cts +1 -0
  62. package/dist/components/previews/RectPreview.d.ts +1 -0
  63. package/dist/components/tools/BoxSelectHandler.d.cts +16 -0
  64. package/dist/components/tools/BoxSelectHandler.d.ts +16 -0
  65. package/dist/components/tools/MapClickHandler.d.cts +6 -0
  66. package/dist/components/tools/MapClickHandler.d.ts +6 -0
  67. package/dist/components/tools/MultiDragHandler.d.cts +9 -0
  68. package/dist/components/tools/MultiDragHandler.d.ts +9 -0
  69. package/dist/components/tools/OsmTraceHandler.d.cts +1 -0
  70. package/dist/components/tools/OsmTraceHandler.d.ts +1 -0
  71. package/dist/components/tools/VertexEditor.d.cts +13 -0
  72. package/dist/components/tools/VertexEditor.d.ts +13 -0
  73. package/dist/contexts/FeatureStyleContext.d.cts +15 -0
  74. package/dist/contexts/FeatureStyleContext.d.ts +15 -0
  75. package/dist/contexts/MapControllerContext.d.cts +2 -0
  76. package/dist/contexts/MapControllerContext.d.ts +2 -0
  77. package/dist/hooks/lastPointer.d.cts +13 -0
  78. package/dist/hooks/lastPointer.d.ts +13 -0
  79. package/dist/hooks/useCursorWhileMode.d.cts +23 -0
  80. package/dist/hooks/useCursorWhileMode.d.ts +23 -0
  81. package/dist/hooks/useDuplicateDrag.d.cts +9 -0
  82. package/dist/hooks/useDuplicateDrag.d.ts +9 -0
  83. package/dist/hooks/useFeatureDrag.d.cts +20 -0
  84. package/dist/hooks/useFeatureDrag.d.ts +20 -0
  85. package/dist/hooks/useMapInteractionMachine.d.cts +13 -0
  86. package/dist/hooks/useMapInteractionMachine.d.ts +13 -0
  87. package/dist/hooks/useModifierDragCursor.d.cts +9 -0
  88. package/dist/hooks/useModifierDragCursor.d.ts +9 -0
  89. package/dist/hooks/usePartDrag.d.cts +23 -0
  90. package/dist/hooks/usePartDrag.d.ts +23 -0
  91. package/dist/index.cjs +52 -0
  92. package/dist/index.d.cts +27 -0
  93. package/dist/index.d.ts +27 -0
  94. package/dist/index.es.js +4246 -0
  95. package/dist/store/StoreContext.d.cts +1 -0
  96. package/dist/store/StoreContext.d.ts +1 -0
  97. package/dist/utils/boxSelect.d.cts +4 -0
  98. package/dist/utils/boxSelect.d.ts +4 -0
  99. package/dist/utils/dragUtils.d.cts +12 -0
  100. package/dist/utils/dragUtils.d.ts +12 -0
  101. package/dist/utils/featureTooltip.d.cts +19 -0
  102. package/dist/utils/featureTooltip.d.ts +19 -0
  103. package/dist/utils/freehandCommitFlag.d.cts +2 -0
  104. package/dist/utils/freehandCommitFlag.d.ts +2 -0
  105. package/dist/utils/freshFeature.d.cts +17 -0
  106. package/dist/utils/freshFeature.d.ts +17 -0
  107. package/dist/utils/geojson.d.cts +3 -0
  108. package/dist/utils/geojson.d.ts +3 -0
  109. package/dist/utils/measureClickFlag.d.cts +2 -0
  110. package/dist/utils/measureClickFlag.d.ts +2 -0
  111. package/dist/utils/modifierDragFlag.d.cts +2 -0
  112. package/dist/utils/modifierDragFlag.d.ts +2 -0
  113. package/dist/utils/pinPath.d.cts +2 -0
  114. package/dist/utils/pinPath.d.ts +2 -0
  115. package/dist/utils/rafThrottle.d.cts +11 -0
  116. package/dist/utils/rafThrottle.d.ts +11 -0
  117. package/dist/utils/snapping.d.cts +25 -0
  118. package/dist/utils/snapping.d.ts +25 -0
  119. package/package.json +98 -3
package/AGENTS.md ADDED
@@ -0,0 +1,80 @@
1
+ # @element-editor/adapter-leaflet: guide for coding agents
2
+
3
+ Adds the Element editing layer (drawing, selection, vertex editing, transform, snapping) to a react-leaflet `MapContainer` you already render. It renders no buttons: toolbars and panels come from `@element-editor/ui`. It is also what the Leaflet drop-in (`@element-editor/react/leaflet`) is built on.
4
+
5
+ Use it when the app already has a Leaflet map. With no existing map, use the drop-in instead (`@element-editor/react`, MapLibre, or `@element-editor/react/leaflet`): it builds the map and the whole UI. The decision table, ref API, events and error codes are in `node_modules/@element-editor/react/AGENTS.md` (when that package is installed) and at https://elementeditor.dev/docs.
6
+
7
+ ## Install and CSS
8
+
9
+ ```bash
10
+ pnpm add @element-editor/adapter-leaflet @element-editor/ui leaflet react-leaflet
11
+ # types/helpers (FeatureCollection, getFeatureCollectionBBox): pnpm add @element-editor/core
12
+ ```
13
+
14
+ Peers: `react`, `react-dom` `^19.2.0`; `leaflet` `^1.9.0`; `react-leaflet` `^5.0.0` (react-leaflet 4 does not work). Keep all `@element-editor/*` packages on one version and list every one you import from in your `package.json`.
15
+
16
+ ```tsx
17
+ import '@element-editor/adapter-leaflet/style.css' // includes leaflet/dist/leaflet.css
18
+ ```
19
+
20
+ ## Example: your map, Element's toolbars
21
+
22
+ `ElementEditorControls` goes inside `MapContainer` (it uses `useMap()`). The `@element-editor/ui` panels go outside `MapContainer`, as siblings in the same positioned `data-elm-theme` wrapper; inside it, Leaflet would treat their clicks and scrolls as map gestures. `FloatingToolbar`, `LayersPanel` and `DataTablePanel` position themselves against that wrapper; `EditingToolbar` and `ActiveDrawingToolbar` render in place, so give them an absolutely positioned wrapper of your own (adding `--elm-table-offset` keeps them above the open table). `zIndex: 0` on the map keeps Leaflet's panes (z-index 400+) under the panels.
23
+
24
+ ```tsx
25
+ import { Suspense, useRef } from 'react'
26
+ import { MapContainer, TileLayer } from 'react-leaflet'
27
+ import {
28
+ ElementEditorUIProvider, GeoStoreProvider, ElementEditorControls, type ElementEditorControlsRef,
29
+ } from '@element-editor/adapter-leaflet'
30
+ import type { FeatureCollection } from '@element-editor/core'
31
+ import { FloatingToolbar, EditingToolbar, ActiveDrawingToolbar, LayersPanel, DataTablePanel } from '@element-editor/ui'
32
+ import '@element-editor/adapter-leaflet/style.css'
33
+
34
+ export default function MyMap({ data }: { data: FeatureCollection | null }) {
35
+ const editorRef = useRef<ElementEditorControlsRef>(null)
36
+
37
+ return (
38
+ <ElementEditorUIProvider config={{ persistKey: 'my-map' }}>
39
+ <GeoStoreProvider>
40
+ <div data-elm-theme="light" style={{ position: 'relative', width: '100%', height: 600, overflow: 'hidden' }}>
41
+ {/* react-leaflet: center is [lat, lng] */}
42
+ <MapContainer center={[48.86, 2.35]} zoom={5} style={{ position: 'absolute', inset: 0, zIndex: 0 }}>
43
+ <TileLayer url="https://tile.openstreetmap.org/{z}/{x}/{y}.png" attribution="&copy; OpenStreetMap contributors" />
44
+ <ElementEditorControls ref={editorRef} initialData={data} onChange={(fc) => console.log(fc)} />
45
+ </MapContainer>
46
+ <FloatingToolbar />
47
+ {/* Not self-positioned: place them yourself (bottom centre here) */}
48
+ <div style={{ position: 'absolute', bottom: 'calc(var(--elm-table-offset, 0px) + 16px)', left: '50%', transform: 'translateX(-50%)', zIndex: 400, display: 'flex', gap: 8 }}>
49
+ <EditingToolbar />
50
+ <ActiveDrawingToolbar />
51
+ </div>
52
+ <LayersPanel />
53
+ <Suspense fallback={null}><DataTablePanel /></Suspense>
54
+ </div>
55
+ </GeoStoreProvider>
56
+ </ElementEditorUIProvider>
57
+ )
58
+ }
59
+ ```
60
+
61
+ `editorRef.current` is an `ElementEditorControlsRef`: the same methods as the drop-in's `ElementEditorRef` (`getFeatureCollection`, `setFeatureCollection`, `addFeature`, `select`, `setTool`, `flyTo`, `fitBounds`, `submit`, `undo`, …) without `getBasemap`/`setBasemap`. `getMap()` returns the `L.Map`. All of them take `[lng, lat]`.
62
+
63
+ ## `ElementEditorControls` props
64
+
65
+ `initialData` (the starting data: applied at mount, or once when it first arrives while the editor is empty and unedited; later changes are ignored, use `setFeatureCollection`; the adapter never moves the map), `schema`, `tools`, `maxFeatures`, `readonly`, `layout` (`'panel' | 'minimal' | 'split'`), `bounds` (bbox `[west, south, east, north]`, `null` clears), `renderer` (`'canvas'` default, or `'svg'`), `getFeatureStyle` (may return Leaflet `PathOptions`), `onReady(map)`, and the callbacks `onChange`, `onSubmit`, `onValidationError`, `onFeatureAdd`, `onFeatureUpdate`, `onFeatureDelete`, `onSelectionChange`, `onFeatureSelect`, `onZoomChange`, `onToolChange`, `onEvent`, `onError`. `routingApiKey` is deprecated (`config.network.routing.apiKey`). There is no `children` prop (unlike the MapLibre adapter).
66
+
67
+ Theme, accent, `config` (network, layout, units, `persistKey`), `onEvent` and `onError` also go on `ElementEditorUIProvider`.
68
+
69
+ ## Gotchas
70
+
71
+ - `[lat, lng]` vs `[lng, lat]`: Leaflet and react-leaflet (`center`, `L.latLng`, `getBounds()`) use `[lat, lng]`; Element's data, `bounds` and ref methods use GeoJSON `[lng, lat]`. Convert at the boundary. `toLeaflet` / `toGeoJSON` are exported for that.
72
+ - `ElementEditorUIProvider` is required (`ElementEditorControls` throws without it). One `GeoStoreProvider` per editor, wrapping the `MapContainer`. Everything (map and panels) inside a `data-elm-theme="light" | "dark"` element; for dark mode also set `ThemeContext` from `@element-editor/ui` to the same value.
73
+ - Without the stylesheet, tiles and markers break silently and the UI is unstyled.
74
+ - react-leaflet's `MapContainer` ignores prop changes after mount (`center`, `zoom`, …). Move the map imperatively: `editorRef.current?.flyTo({ center: [lng, lat], zoom })`, `getMap()`, or `useMap()` in a child.
75
+ - Unselected features are drawn on one shared canvas by default; query feature data through the ref (`getFeatureCollection`, `getSelection`), not through `<path>` elements (or pass `renderer="svg"`).
76
+ - Lazy UI (`DataTablePanel`, `SchemaModal`, `GeoJSONModal`, `SettingsModal`, `ShortcutsModal`, `BooleanOperations`) needs its own `<Suspense>` each.
77
+ - Keep `getFeatureStyle` fast and stable (module level or `useCallback`).
78
+ - Next.js: Leaflet touches `window` on import. Load the whole module that imports `react-leaflet` with `next/dynamic` and `ssr: false`, not only `ElementEditorControls`.
79
+
80
+ Docs: https://elementeditor.dev/docs/quickstart/adapter-leaflet, https://elementeditor.dev/docs/reference/adapter-leaflet, https://elementeditor.dev/docs/guides/custom-ui-with-adapters, https://elementeditor.dev/docs/guides/nextjs. Markdown: append `.md`; everything at once: https://elementeditor.dev/llms-full.txt.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Michal Acler and Element Editor contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,46 @@
1
- # Temporary Holding Version
1
+ # @element-editor/adapter-leaflet
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Plug Element drawing and editing tools (vertex editing, snapping, a data table, a GeoJSON inspector, undo/redo) into an existing Leaflet / react-leaflet map. You keep the map; Element adds the editing layer.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @element-editor/adapter-leaflet @element-editor/ui leaflet react-leaflet
9
+ npm install -D @types/leaflet # TypeScript only
10
+ ```
11
+
12
+ Peer dependencies: `react` and `react-dom` `^19.2` (React 18 is not supported), `react-leaflet` `^5`, `leaflet` `^1.9`. `@element-editor/ui` (the toolbars and panels) comes with the adapter; list it yourself when you import from it, because strict package managers (pnpm) only resolve packages you declare. Keep all `@element-editor/*` packages on the same version.
13
+
14
+ ## Quick start
15
+
16
+ ```tsx
17
+ import { MapContainer, TileLayer } from 'react-leaflet'
18
+ import { ElementEditorUIProvider, GeoStoreProvider, ElementEditorControls } from '@element-editor/adapter-leaflet'
19
+ import '@element-editor/adapter-leaflet/style.css' // includes Leaflet's CSS
20
+
21
+ export default function MyMap() {
22
+ return (
23
+ <ElementEditorUIProvider>
24
+ <GeoStoreProvider>
25
+ <div data-elm-theme="light">
26
+ <MapContainer center={[48.86, 2.35]} zoom={12} style={{ height: 600 }}>
27
+ <TileLayer url="https://tile.openstreetmap.org/{z}/{x}/{y}.png" />
28
+ <ElementEditorControls onChange={(data) => console.log(data)} />
29
+ </MapContainer>
30
+ </div>
31
+ </GeoStoreProvider>
32
+ </ElementEditorUIProvider>
33
+ )
34
+ }
35
+ ```
36
+
37
+ `ElementEditorUIProvider` supplies the contexts the editor needs (and renders its own toast `<Toaster>`); `ElementEditorControls` throws without it. The `data-elm-theme` element scopes the editor's styles (the stylesheet doesn't reset your page) and its keyboard shortcuts.
38
+
39
+ ## Documentation
40
+
41
+ - [Quick start](https://elementeditor.dev/docs/quickstart/adapter-leaflet)
42
+ - [Adapter API reference](https://elementeditor.dev/docs/reference/adapter-leaflet)
43
+ - [Custom UI with adapters](https://elementeditor.dev/docs/guides/custom-ui-with-adapters) · [Next.js](https://elementeditor.dev/docs/guides/nextjs)
44
+ - [All docs](https://elementeditor.dev/docs) · [Changelog](https://github.com/spatibase/element/blob/main/CHANGELOG.md) · [Issues](https://github.com/spatibase/element/issues)
45
+
46
+ MIT licence.
@@ -0,0 +1,111 @@
1
+ import { Map as LeafletMap } from 'leaflet';
2
+ import { BoundsInput, FeatureCollection, FeatureCollectionInput, GeoJSONFeature, GeoJSONFeatureInput, Schema } from '@element-editor/core';
3
+ import { ElementEditorImperativeAPI, ImportResult, ElementEditorEvent, ElementEditorErrorHandler } from '@element-editor/ui';
4
+ import { FeatureRenderer } from './components/FeatureInteractionLayer.cjs';
5
+ import { FeatureStyleFn } from './contexts/FeatureStyleContext.cjs';
6
+ export type DrawingTool = 'polygon' | 'rectangle' | 'circle' | 'line' | 'point' | 'freehand' | 'route' | 'picker' | 'measure' | 'search';
7
+ export type { ImportResult };
8
+ /** Imperative handle exposed via `ref`. Same surface across adapters and `@element-editor/react`; `getMap()` returns the `L.Map`. */
9
+ export type ElementEditorControlsRef = ElementEditorImperativeAPI<LeafletMap>;
10
+ export interface ElementEditorControlsProps {
11
+ /** Pre-populate the editor with an existing FeatureCollection. Applied once on mount. */
12
+ initialData?: FeatureCollectionInput | GeoJSONFeatureInput | null;
13
+ /**
14
+ * Optional single Schema pushed into the store on mount. Safe to pass a
15
+ * stable object — re-renders that keep the same `schema.id` are no-ops.
16
+ */
17
+ schema?: Schema;
18
+ /** Called after every committed change with the full current FeatureCollection. */
19
+ onChange?: (data: FeatureCollection) => void;
20
+ /**
21
+ * Called by `ref.submit()` when all features pass validation. Receives a
22
+ * clean FeatureCollection (stripped of `_validation`).
23
+ */
24
+ onSubmit?: (data: FeatureCollection) => void;
25
+ /**
26
+ * Called by `ref.submit()` when one or more features have validation
27
+ * errors. Blocks `onSubmit` from firing.
28
+ */
29
+ onValidationError?: (errors: Record<string, string[]>) => void;
30
+ /**
31
+ * Receives every message the editor would show the user: `feature:deleted` / `features:deleted`
32
+ * (with `undo`), `feature:duplicated` / `features:duplicated`, `feature:broken`,
33
+ * `selection:copied`, `selection:pasted`, `undo`, `redo`, `error`, `warning` and `success`.
34
+ * When provided, the built-in toasts are suppressed: display them yourself. Tool changes and data
35
+ * commits are not events (use `onToolChange` and `onChange`). Wraps an EventProvider locally so
36
+ * you don't need a separate `<ElementEditorUIProvider onEvent={...}>`.
37
+ */
38
+ onEvent?: (event: ElementEditorEvent) => void;
39
+ /**
40
+ * Called first for every runtime failure with an `ElementEditorError` (`code`, `source`, honest
41
+ * English `message`): basemap, picker, routing, search, import, geometry, clipboard, `maxFeatures`.
42
+ * Return `false` to stop the built-in toast / `onEvent` error event, or a string to show that text
43
+ * instead. Wraps a local EventProvider (with `onEvent`) or hands on to the enclosing one.
44
+ */
45
+ onError?: ElementEditorErrorHandler;
46
+ /**
47
+ * Structural layout hint read by `@element-editor/ui` panels (LayersPanel,
48
+ * DataTablePanel, etc.). `'panel'` keeps the sidebar docked; `'split'`
49
+ * reserves the lower third for the data table; `'minimal'` hides them.
50
+ */
51
+ layout?: 'panel' | 'minimal' | 'split';
52
+ /** Restrict which drawing tools are available. Omit to allow all. */
53
+ tools?: DrawingTool[];
54
+ /** Maximum number of features allowed. Drawing is disabled when reached. */
55
+ maxFeatures?: number;
56
+ /** When true, all editing interactions are disabled. */
57
+ readonly?: boolean;
58
+ /** Called when a feature is added. */
59
+ onFeatureAdd?: (feature: GeoJSONFeature) => void;
60
+ /** Called when a feature's geometry or properties are updated. */
61
+ onFeatureUpdate?: (id: string, feature: GeoJSONFeature) => void;
62
+ /** Called when a feature is deleted. */
63
+ onFeatureDelete?: (id: string) => void;
64
+ /** Called when the selection changes. Fires with the array of selected feature ids. */
65
+ onSelectionChange?: (ids: string[]) => void;
66
+ /**
67
+ * Called with the single selected feature (or `null` when deselected or
68
+ * multi-selected). Convenience variant of `onSelectionChange` that gives
69
+ * you the full GeoJSONFeature for the common single-selection case.
70
+ */
71
+ onFeatureSelect?: (feature: GeoJSONFeature | null) => void;
72
+ /** Called when the map's zoom level changes. */
73
+ onZoomChange?: (zoom: number) => void;
74
+ /** Called with the tool that turned on (as `ref.getTool()` reports it), or `null` when it turned off. */
75
+ onToolChange?: (tool: DrawingTool | null) => void;
76
+ /**
77
+ * Leaflet max-bounds. When set, the user can't pan the map outside this box.
78
+ * A GeoJSON bbox `[west, south, east, north]`. The older `[[south, west], [north, east]]`
79
+ * form still works but is deprecated (dev warning). `null` clears.
80
+ */
81
+ bounds?: BoundsInput | null;
82
+ /** @deprecated Use `config.network.routing.apiKey` (it wins over this). OpenRouteService key for the Route tool. */
83
+ routingApiKey?: string;
84
+ /**
85
+ * Returns Leaflet PathOptions for a given feature (or `undefined`/`null` for the default look),
86
+ * applied to polygons, polylines and point pins (pins use `fillColor`, then `color`).
87
+ * `fillColor` falls back to `color`. Wins over `config.featureColor` per key; the selected /
88
+ * hovered looks win over both. Called on every render — keep it fast (no heavy computation inside).
89
+ *
90
+ * @example
91
+ * getFeatureStyle={(f) => ({
92
+ * color: f.properties.color ?? '#475569',
93
+ * fillOpacity: f.properties.opacity ?? 0.15,
94
+ * })}
95
+ */
96
+ getFeatureStyle?: FeatureStyleFn;
97
+ /**
98
+ * How unselected features are drawn. `'canvas'` (default) paints them on one
99
+ * shared `<canvas>`, which keeps panning and zooming smooth with thousands of
100
+ * features. `'svg'` gives every feature its own `<path>` element (useful if
101
+ * your app styles or queries feature paths via CSS/DOM). Selected features and
102
+ * editing handles are always SVG either way.
103
+ */
104
+ renderer?: FeatureRenderer;
105
+ /**
106
+ * Called once per map, as soon as Element's layers and panes are on it: the place to add your
107
+ * own layers.
108
+ */
109
+ onReady?: (map: LeafletMap) => void;
110
+ }
111
+ export declare const ElementEditorControls: import('react').ForwardRefExoticComponent<ElementEditorControlsProps & import('react').RefAttributes<ElementEditorControlsRef>>;
@@ -0,0 +1,111 @@
1
+ import { Map as LeafletMap } from 'leaflet';
2
+ import { BoundsInput, FeatureCollection, FeatureCollectionInput, GeoJSONFeature, GeoJSONFeatureInput, Schema } from '@element-editor/core';
3
+ import { ElementEditorImperativeAPI, ImportResult, ElementEditorEvent, ElementEditorErrorHandler } from '@element-editor/ui';
4
+ import { FeatureRenderer } from './components/FeatureInteractionLayer.js';
5
+ import { FeatureStyleFn } from './contexts/FeatureStyleContext.js';
6
+ export type DrawingTool = 'polygon' | 'rectangle' | 'circle' | 'line' | 'point' | 'freehand' | 'route' | 'picker' | 'measure' | 'search';
7
+ export type { ImportResult };
8
+ /** Imperative handle exposed via `ref`. Same surface across adapters and `@element-editor/react`; `getMap()` returns the `L.Map`. */
9
+ export type ElementEditorControlsRef = ElementEditorImperativeAPI<LeafletMap>;
10
+ export interface ElementEditorControlsProps {
11
+ /** Pre-populate the editor with an existing FeatureCollection. Applied once on mount. */
12
+ initialData?: FeatureCollectionInput | GeoJSONFeatureInput | null;
13
+ /**
14
+ * Optional single Schema pushed into the store on mount. Safe to pass a
15
+ * stable object — re-renders that keep the same `schema.id` are no-ops.
16
+ */
17
+ schema?: Schema;
18
+ /** Called after every committed change with the full current FeatureCollection. */
19
+ onChange?: (data: FeatureCollection) => void;
20
+ /**
21
+ * Called by `ref.submit()` when all features pass validation. Receives a
22
+ * clean FeatureCollection (stripped of `_validation`).
23
+ */
24
+ onSubmit?: (data: FeatureCollection) => void;
25
+ /**
26
+ * Called by `ref.submit()` when one or more features have validation
27
+ * errors. Blocks `onSubmit` from firing.
28
+ */
29
+ onValidationError?: (errors: Record<string, string[]>) => void;
30
+ /**
31
+ * Receives every message the editor would show the user: `feature:deleted` / `features:deleted`
32
+ * (with `undo`), `feature:duplicated` / `features:duplicated`, `feature:broken`,
33
+ * `selection:copied`, `selection:pasted`, `undo`, `redo`, `error`, `warning` and `success`.
34
+ * When provided, the built-in toasts are suppressed: display them yourself. Tool changes and data
35
+ * commits are not events (use `onToolChange` and `onChange`). Wraps an EventProvider locally so
36
+ * you don't need a separate `<ElementEditorUIProvider onEvent={...}>`.
37
+ */
38
+ onEvent?: (event: ElementEditorEvent) => void;
39
+ /**
40
+ * Called first for every runtime failure with an `ElementEditorError` (`code`, `source`, honest
41
+ * English `message`): basemap, picker, routing, search, import, geometry, clipboard, `maxFeatures`.
42
+ * Return `false` to stop the built-in toast / `onEvent` error event, or a string to show that text
43
+ * instead. Wraps a local EventProvider (with `onEvent`) or hands on to the enclosing one.
44
+ */
45
+ onError?: ElementEditorErrorHandler;
46
+ /**
47
+ * Structural layout hint read by `@element-editor/ui` panels (LayersPanel,
48
+ * DataTablePanel, etc.). `'panel'` keeps the sidebar docked; `'split'`
49
+ * reserves the lower third for the data table; `'minimal'` hides them.
50
+ */
51
+ layout?: 'panel' | 'minimal' | 'split';
52
+ /** Restrict which drawing tools are available. Omit to allow all. */
53
+ tools?: DrawingTool[];
54
+ /** Maximum number of features allowed. Drawing is disabled when reached. */
55
+ maxFeatures?: number;
56
+ /** When true, all editing interactions are disabled. */
57
+ readonly?: boolean;
58
+ /** Called when a feature is added. */
59
+ onFeatureAdd?: (feature: GeoJSONFeature) => void;
60
+ /** Called when a feature's geometry or properties are updated. */
61
+ onFeatureUpdate?: (id: string, feature: GeoJSONFeature) => void;
62
+ /** Called when a feature is deleted. */
63
+ onFeatureDelete?: (id: string) => void;
64
+ /** Called when the selection changes. Fires with the array of selected feature ids. */
65
+ onSelectionChange?: (ids: string[]) => void;
66
+ /**
67
+ * Called with the single selected feature (or `null` when deselected or
68
+ * multi-selected). Convenience variant of `onSelectionChange` that gives
69
+ * you the full GeoJSONFeature for the common single-selection case.
70
+ */
71
+ onFeatureSelect?: (feature: GeoJSONFeature | null) => void;
72
+ /** Called when the map's zoom level changes. */
73
+ onZoomChange?: (zoom: number) => void;
74
+ /** Called with the tool that turned on (as `ref.getTool()` reports it), or `null` when it turned off. */
75
+ onToolChange?: (tool: DrawingTool | null) => void;
76
+ /**
77
+ * Leaflet max-bounds. When set, the user can't pan the map outside this box.
78
+ * A GeoJSON bbox `[west, south, east, north]`. The older `[[south, west], [north, east]]`
79
+ * form still works but is deprecated (dev warning). `null` clears.
80
+ */
81
+ bounds?: BoundsInput | null;
82
+ /** @deprecated Use `config.network.routing.apiKey` (it wins over this). OpenRouteService key for the Route tool. */
83
+ routingApiKey?: string;
84
+ /**
85
+ * Returns Leaflet PathOptions for a given feature (or `undefined`/`null` for the default look),
86
+ * applied to polygons, polylines and point pins (pins use `fillColor`, then `color`).
87
+ * `fillColor` falls back to `color`. Wins over `config.featureColor` per key; the selected /
88
+ * hovered looks win over both. Called on every render — keep it fast (no heavy computation inside).
89
+ *
90
+ * @example
91
+ * getFeatureStyle={(f) => ({
92
+ * color: f.properties.color ?? '#475569',
93
+ * fillOpacity: f.properties.opacity ?? 0.15,
94
+ * })}
95
+ */
96
+ getFeatureStyle?: FeatureStyleFn;
97
+ /**
98
+ * How unselected features are drawn. `'canvas'` (default) paints them on one
99
+ * shared `<canvas>`, which keeps panning and zooming smooth with thousands of
100
+ * features. `'svg'` gives every feature its own `<path>` element (useful if
101
+ * your app styles or queries feature paths via CSS/DOM). Selected features and
102
+ * editing handles are always SVG either way.
103
+ */
104
+ renderer?: FeatureRenderer;
105
+ /**
106
+ * Called once per map, as soon as Element's layers and panes are on it: the place to add your
107
+ * own layers.
108
+ */
109
+ onReady?: (map: LeafletMap) => void;
110
+ }
111
+ export declare const ElementEditorControls: import('react').ForwardRefExoticComponent<ElementEditorControlsProps & import('react').RefAttributes<ElementEditorControlsRef>>;