@element-editor/ui 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.
- package/AGENTS.md +65 -0
- package/LICENSE +21 -0
- package/README.md +46 -2
- package/dist/BooleanOperations-WXcmW9Vy.cjs +1 -0
- package/dist/BooleanOperations-oF0WybQP.js +111 -0
- package/dist/DataTable-CNfB01y2.cjs +2 -0
- package/dist/DataTable-DNmjUwka.js +510 -0
- package/dist/DataTablePanel-BknCxC1K.cjs +1 -0
- package/dist/DataTablePanel-rWdaQubk.js +208 -0
- package/dist/EditToolbarBtn-SZIsnIn3.js +378 -0
- package/dist/EditToolbarBtn-dpCmKnzk.cjs +3 -0
- package/dist/FeatureEditModal-BF-fGhVD.js +77 -0
- package/dist/FeatureEditModal-CM6BObUO.cjs +1 -0
- package/dist/FeatureList-CQb2kTrV.js +520 -0
- package/dist/FeatureList-Csr-anLA.cjs +1 -0
- package/dist/FeatureRowContextMenu-BLt7VlUU.js +130 -0
- package/dist/FeatureRowContextMenu-Gwq3OMEA.cjs +1 -0
- package/dist/GeoJSONModal-BPFWeaLZ.js +886 -0
- package/dist/GeoJSONModal-iwUcvrg7.cjs +9 -0
- package/dist/SchemaEditor-BEsKUe2p.js +293 -0
- package/dist/SchemaEditor-qkoYMg0C.cjs +1 -0
- package/dist/SchemaModal-9nNrgorr.js +95 -0
- package/dist/SchemaModal-GrtgA7cH.cjs +1 -0
- package/dist/SettingsModal-6_qmRyCi.cjs +1 -0
- package/dist/SettingsModal-DTP0Xx_Z.js +237 -0
- package/dist/ShortcutsModal-CdyQlrqK.cjs +1 -0
- package/dist/ShortcutsModal-DwxZu71W.js +299 -0
- package/dist/components/ActiveDrawingToolbar.d.cts +1 -0
- package/dist/components/ActiveDrawingToolbar.d.ts +1 -0
- package/dist/components/BasemapPicker.d.cts +26 -0
- package/dist/components/BasemapPicker.d.ts +26 -0
- package/dist/components/BooleanOperations.d.cts +1 -0
- package/dist/components/BooleanOperations.d.ts +1 -0
- package/dist/components/BufferPanel.d.cts +1 -0
- package/dist/components/BufferPanel.d.ts +1 -0
- package/dist/components/DataTable.d.cts +9 -0
- package/dist/components/DataTable.d.ts +9 -0
- package/dist/components/DataTablePanel.d.cts +10 -0
- package/dist/components/DataTablePanel.d.ts +10 -0
- package/dist/components/EditingToolbar.d.cts +1 -0
- package/dist/components/EditingToolbar.d.ts +1 -0
- package/dist/components/ElementEditorErrorBoundary.d.cts +28 -0
- package/dist/components/ElementEditorErrorBoundary.d.ts +28 -0
- package/dist/components/ElementEditorUIProvider.d.cts +50 -0
- package/dist/components/ElementEditorUIProvider.d.ts +50 -0
- package/dist/components/FeatureMapContextMenu.d.cts +23 -0
- package/dist/components/FeatureMapContextMenu.d.ts +23 -0
- package/dist/components/FitBoundsButton.d.cts +6 -0
- package/dist/components/FitBoundsButton.d.ts +6 -0
- package/dist/components/FloatingToolbar.d.cts +3 -0
- package/dist/components/FloatingToolbar.d.ts +3 -0
- package/dist/components/LocateButton.d.cts +11 -0
- package/dist/components/LocateButton.d.ts +11 -0
- package/dist/components/ReadonlyBanner.d.cts +1 -0
- package/dist/components/ReadonlyBanner.d.ts +1 -0
- package/dist/components/ShortcutsModal.d.cts +1 -0
- package/dist/components/ShortcutsModal.d.ts +1 -0
- package/dist/components/SimplifyPanel.d.cts +1 -0
- package/dist/components/SimplifyPanel.d.ts +1 -0
- package/dist/components/SmoothPanel.d.cts +1 -0
- package/dist/components/SmoothPanel.d.ts +1 -0
- package/dist/components/UndoRedoToolbar.d.cts +20 -0
- package/dist/components/UndoRedoToolbar.d.ts +20 -0
- package/dist/components/VertexSelectionBar.d.cts +12 -0
- package/dist/components/VertexSelectionBar.d.ts +12 -0
- package/dist/components/ZoomIndicator.d.cts +15 -0
- package/dist/components/ZoomIndicator.d.ts +15 -0
- package/dist/components/dialogs/DynamicFieldRenderer.d.cts +11 -0
- package/dist/components/dialogs/DynamicFieldRenderer.d.ts +11 -0
- package/dist/components/dialogs/SettingsModal.d.cts +1 -0
- package/dist/components/dialogs/SettingsModal.d.ts +1 -0
- package/dist/components/lazyParts.d.cts +12 -0
- package/dist/components/lazyParts.d.ts +12 -0
- package/dist/components/sidebar/FeatureEditModal.d.cts +6 -0
- package/dist/components/sidebar/FeatureEditModal.d.ts +6 -0
- package/dist/components/sidebar/FeatureList.d.cts +4 -0
- package/dist/components/sidebar/FeatureList.d.ts +4 -0
- package/dist/components/sidebar/FeatureRow.d.cts +38 -0
- package/dist/components/sidebar/FeatureRow.d.ts +38 -0
- package/dist/components/sidebar/FeatureRowContextMenu.d.cts +12 -0
- package/dist/components/sidebar/FeatureRowContextMenu.d.ts +12 -0
- package/dist/components/sidebar/FeatureSidePanel.d.cts +8 -0
- package/dist/components/sidebar/FeatureSidePanel.d.ts +8 -0
- package/dist/components/sidebar/GeoJSONModal.d.cts +5 -0
- package/dist/components/sidebar/GeoJSONModal.d.ts +5 -0
- package/dist/components/sidebar/GroupRow.d.cts +21 -0
- package/dist/components/sidebar/GroupRow.d.ts +21 -0
- package/dist/components/sidebar/HideLayersToggle.d.cts +2 -0
- package/dist/components/sidebar/HideLayersToggle.d.ts +2 -0
- package/dist/components/sidebar/LayersPanel.d.cts +5 -0
- package/dist/components/sidebar/LayersPanel.d.ts +5 -0
- package/dist/components/sidebar/PartSubRow.d.cts +9 -0
- package/dist/components/sidebar/PartSubRow.d.ts +9 -0
- package/dist/components/sidebar/SchemaEditor.d.cts +4 -0
- package/dist/components/sidebar/SchemaEditor.d.ts +4 -0
- package/dist/components/sidebar/SchemaModal.d.cts +5 -0
- package/dist/components/sidebar/SchemaModal.d.ts +5 -0
- package/dist/components/sidebar/codeDock.d.cts +21 -0
- package/dist/components/sidebar/codeDock.d.ts +21 -0
- package/dist/components/ui/Button.d.cts +9 -0
- package/dist/components/ui/Button.d.ts +9 -0
- package/dist/components/ui/CloseButton.d.cts +6 -0
- package/dist/components/ui/CloseButton.d.ts +6 -0
- package/dist/components/ui/ContextMenu.d.cts +28 -0
- package/dist/components/ui/ContextMenu.d.ts +28 -0
- package/dist/components/ui/EditToolbarBtn.d.cts +13 -0
- package/dist/components/ui/EditToolbarBtn.d.ts +13 -0
- package/dist/components/ui/ElementToaster.d.cts +3 -0
- package/dist/components/ui/ElementToaster.d.ts +3 -0
- package/dist/components/ui/GeometryTypeIcon.d.cts +7 -0
- package/dist/components/ui/GeometryTypeIcon.d.ts +7 -0
- package/dist/components/ui/IconButton.d.cts +28 -0
- package/dist/components/ui/IconButton.d.ts +28 -0
- package/dist/components/ui/Input.d.cts +14 -0
- package/dist/components/ui/Input.d.ts +14 -0
- package/dist/components/ui/JsonCode.d.cts +4 -0
- package/dist/components/ui/JsonCode.d.ts +4 -0
- package/dist/components/ui/Kbd.d.cts +16 -0
- package/dist/components/ui/Kbd.d.ts +16 -0
- package/dist/components/ui/NoFeaturesEmptyState.d.cts +1 -0
- package/dist/components/ui/NoFeaturesEmptyState.d.ts +1 -0
- package/dist/components/ui/SearchDropdown.d.cts +28 -0
- package/dist/components/ui/SearchDropdown.d.ts +28 -0
- package/dist/components/ui/ToolButton.d.cts +19 -0
- package/dist/components/ui/ToolButton.d.ts +19 -0
- package/dist/components/ui/Tooltip.d.cts +12 -0
- package/dist/components/ui/Tooltip.d.ts +12 -0
- package/dist/components/ui/panelHeader.d.cts +2 -0
- package/dist/components/ui/panelHeader.d.ts +2 -0
- package/dist/components/ui/resizeEdge.d.cts +4 -0
- package/dist/components/ui/resizeEdge.d.ts +4 -0
- package/dist/contexts/ElementEditorConfigContext.d.cts +351 -0
- package/dist/contexts/ElementEditorConfigContext.d.ts +351 -0
- package/dist/contexts/EventContext.d.cts +69 -0
- package/dist/contexts/EventContext.d.ts +69 -0
- package/dist/contexts/MapControllerContext.d.cts +24 -0
- package/dist/contexts/MapControllerContext.d.ts +24 -0
- package/dist/contexts/ShortcutConfigContext.d.cts +16 -0
- package/dist/contexts/ShortcutConfigContext.d.ts +16 -0
- package/dist/contexts/ThemeContext.d.cts +12 -0
- package/dist/contexts/ThemeContext.d.ts +12 -0
- package/dist/element-editor-ui.cjs +1 -0
- package/dist/element-editor-ui.es.js +102 -0
- package/dist/geometryWorker-CCwg4tNC.cjs +1 -0
- package/dist/geometryWorker-DgQCFSmD.js +25 -0
- package/dist/hooks/dragShield.d.cts +7 -0
- package/dist/hooks/dragShield.d.ts +7 -0
- package/dist/hooks/themeInputs.d.cts +14 -0
- package/dist/hooks/themeInputs.d.ts +14 -0
- package/dist/hooks/useAccentPalette.d.cts +15 -0
- package/dist/hooks/useAccentPalette.d.ts +15 -0
- package/dist/hooks/useAccentStyle.d.cts +39 -0
- package/dist/hooks/useAccentStyle.d.ts +39 -0
- package/dist/hooks/useAdapterControls.d.cts +280 -0
- package/dist/hooks/useAdapterControls.d.ts +280 -0
- package/dist/hooks/useBalancedWrap.d.cts +8 -0
- package/dist/hooks/useBalancedWrap.d.ts +8 -0
- package/dist/hooks/useContextMenu.d.cts +11 -0
- package/dist/hooks/useContextMenu.d.ts +11 -0
- package/dist/hooks/useDataChangeSubscription.d.cts +9 -0
- package/dist/hooks/useDataChangeSubscription.d.ts +9 -0
- package/dist/hooks/useDragWindow.d.cts +9 -0
- package/dist/hooks/useDragWindow.d.ts +9 -0
- package/dist/hooks/useFocusTrap.d.cts +7 -0
- package/dist/hooks/useFocusTrap.d.ts +7 -0
- package/dist/hooks/useGeoJSONDrop.d.cts +40 -0
- package/dist/hooks/useGeoJSONDrop.d.ts +40 -0
- package/dist/hooks/useGeoJSONExport.d.cts +69 -0
- package/dist/hooks/useGeoJSONExport.d.ts +69 -0
- package/dist/hooks/useGeoJSONFileImport.d.cts +24 -0
- package/dist/hooks/useGeoJSONFileImport.d.ts +24 -0
- package/dist/hooks/useInlineRename.d.cts +26 -0
- package/dist/hooks/useInlineRename.d.ts +26 -0
- package/dist/hooks/useJumpToFeature.d.cts +3 -0
- package/dist/hooks/useJumpToFeature.d.ts +3 -0
- package/dist/hooks/useKeyboardShortcuts.d.cts +9 -0
- package/dist/hooks/useKeyboardShortcuts.d.ts +9 -0
- package/dist/hooks/useNominatim.d.cts +24 -0
- package/dist/hooks/useNominatim.d.ts +24 -0
- package/dist/hooks/usePanelOffset.d.cts +14 -0
- package/dist/hooks/usePanelOffset.d.ts +14 -0
- package/dist/hooks/useResizablePanel.d.cts +4 -0
- package/dist/hooks/useResizablePanel.d.ts +4 -0
- package/dist/hooks/useRootTokens.d.cts +27 -0
- package/dist/hooks/useRootTokens.d.ts +27 -0
- package/dist/hooks/useSearchState.d.cts +23 -0
- package/dist/hooks/useSearchState.d.ts +23 -0
- package/dist/hooks/useServiceAttribution.d.cts +13 -0
- package/dist/hooks/useServiceAttribution.d.ts +13 -0
- package/dist/hooks/useWindowEscape.d.cts +25 -0
- package/dist/hooks/useWindowEscape.d.ts +25 -0
- package/dist/hooks/useWindowFocus.d.cts +4 -0
- package/dist/hooks/useWindowFocus.d.ts +4 -0
- package/dist/hooks/useWorkerGeometryPreview.d.cts +22 -0
- package/dist/hooks/useWorkerGeometryPreview.d.ts +22 -0
- package/dist/index-BKdDZ4Y9.cjs +1 -0
- package/dist/index-CtZterPc.js +3165 -0
- package/dist/index.d.cts +89 -0
- package/dist/index.d.ts +89 -0
- package/dist/internal.cjs +1 -0
- package/dist/internal.d.cts +16 -0
- package/dist/internal.d.ts +16 -0
- package/dist/internal.es.js +42 -0
- package/dist/primitives.cjs +1 -0
- package/dist/primitives.d.cts +19 -0
- package/dist/primitives.d.ts +19 -0
- package/dist/primitives.es.js +17 -0
- package/dist/store/StoreContext.d.cts +17 -0
- package/dist/store/StoreContext.d.ts +17 -0
- package/dist/types/events.d.cts +95 -0
- package/dist/types/events.d.ts +95 -0
- package/dist/useAccentStyle-CPo2XqAn.js +412 -0
- package/dist/useAccentStyle-hsGvyHZc.cjs +1 -0
- package/dist/useAdapterControls-BBObhzVX.cjs +1 -0
- package/dist/useAdapterControls-DVjkwRZ_.js +691 -0
- package/dist/useContextMenu-DJ2Cti85.js +10 -0
- package/dist/useContextMenu-DbxMvb1s.cjs +1 -0
- package/dist/useFocusTrap-BBjPCfDR.cjs +1 -0
- package/dist/useFocusTrap-CKxKT8De.js +25 -0
- package/dist/useJumpToFeature-Bdz2hqOC.js +16 -0
- package/dist/useJumpToFeature-LwjxGzfy.cjs +1 -0
- package/dist/useWindowFocus-D-_yEm_V.cjs +1 -0
- package/dist/useWindowFocus-DGAWA1au.js +80 -0
- package/dist/utils/booleanValue.d.cts +4 -0
- package/dist/utils/booleanValue.d.ts +4 -0
- package/dist/utils/clipboardError.d.cts +3 -0
- package/dist/utils/clipboardError.d.ts +3 -0
- package/dist/utils/download.d.cts +42 -0
- package/dist/utils/download.d.ts +42 -0
- package/dist/utils/geojsonDocument.d.cts +168 -0
- package/dist/utils/geojsonDocument.d.ts +168 -0
- package/dist/utils/geometryWorkerClient.d.cts +6 -0
- package/dist/utils/geometryWorkerClient.d.ts +6 -0
- package/dist/utils/layerOrder.d.cts +9 -0
- package/dist/utils/layerOrder.d.ts +9 -0
- package/dist/utils/lazyWithPreload.d.cts +24 -0
- package/dist/utils/lazyWithPreload.d.ts +24 -0
- package/dist/utils/lineSmooth.d.cts +7 -0
- package/dist/utils/lineSmooth.d.ts +7 -0
- package/dist/utils/platform.d.cts +2 -0
- package/dist/utils/platform.d.ts +2 -0
- package/dist/workers/geometryWorker.d.cts +1 -0
- package/dist/workers/geometryWorker.d.ts +1 -0
- package/package.json +133 -3
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { FeatureCollection, FeatureCollectionInput, FeaturePatch, FlyToOptions, GeoJSONFeature, GeoJSONFeatureInput, Schema } from '@element-editor/core';
|
|
2
|
+
import { DrawTool } from '../contexts/ElementEditorConfigContext.js';
|
|
3
|
+
/**
|
|
4
|
+
* `useAdapterControls` — single source of truth for the editor-shell wiring
|
|
5
|
+
* shared by `@element-editor/adapter-leaflet`, `@element-editor/adapter-maplibre`, and
|
|
6
|
+
* `@element-editor/react`.
|
|
7
|
+
*
|
|
8
|
+
* Owns:
|
|
9
|
+
* - prop → store sync (readonly / tools / maxFeatures / layout; network-disabled tools)
|
|
10
|
+
* - one-time mounts (initialData, schema)
|
|
11
|
+
* - data-change subscription (onChange / per-feature callbacks / selection)
|
|
12
|
+
* - onZoomChange + onFeatureSelect subscriptions
|
|
13
|
+
* - the FC-shaped imperative API (return value — caller wires it through
|
|
14
|
+
* `useImperativeHandle`)
|
|
15
|
+
*
|
|
16
|
+
* Caller still owns: bounds / maxBounds (different APIs across map libs),
|
|
17
|
+
* the map-bridge wiring, the layer tree, and any wrapper-only concerns
|
|
18
|
+
* (slots, tile layer, theme persistence, dev affordances).
|
|
19
|
+
*/
|
|
20
|
+
export interface ImportResult {
|
|
21
|
+
/** Features skipped due to unsupported geometry type. */
|
|
22
|
+
skipped: number;
|
|
23
|
+
/** Features skipped because their id collided with an existing feature. */
|
|
24
|
+
duplicates: number;
|
|
25
|
+
/** True if the import was capped by `maxFeatures` / internal safety limits. */
|
|
26
|
+
truncated: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Options of the ref's data-changing methods (`addFeature`, `updateFeature(s)`, `removeFeatures`).
|
|
30
|
+
*/
|
|
31
|
+
export interface MutationOptions {
|
|
32
|
+
/**
|
|
33
|
+
* Default `true`: the change is one undo step, like the same edit made by the user.
|
|
34
|
+
* `false` (e.g. applying a change from your server): no undo step, and the change is also
|
|
35
|
+
* applied to every undo/redo snapshot, so undoing an earlier user edit doesn't revert it.
|
|
36
|
+
*/
|
|
37
|
+
undoable?: boolean;
|
|
38
|
+
}
|
|
39
|
+
/** Options of `ref.setFeatureCollection`. */
|
|
40
|
+
export interface SetFeatureCollectionOptions {
|
|
41
|
+
/**
|
|
42
|
+
* Default `false`: the load fires `onChange` (and `onFeatureAdd` / `onFeatureUpdate` /
|
|
43
|
+
* `onFeatureDelete`) like any other change. `true`: those callbacks don't fire for this load,
|
|
44
|
+
* so data you just fetched from your backend isn't saved straight back. `onSelectionChange`
|
|
45
|
+
* still fires when the load clears a selection.
|
|
46
|
+
*/
|
|
47
|
+
silent?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Default `true`: the load is one undo step (undo restores the previous data). `false`: no undo
|
|
50
|
+
* step, and the undo/redo history is cleared, so the user can't undo back past the loaded data
|
|
51
|
+
* (what `initialData` does).
|
|
52
|
+
*/
|
|
53
|
+
undoable?: boolean;
|
|
54
|
+
}
|
|
55
|
+
/** What `ref.validate()` returns. */
|
|
56
|
+
export interface ValidationReport {
|
|
57
|
+
/** `true` when no feature has a validation error. */
|
|
58
|
+
valid: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Error messages per feature id, only for features that have errors (e.g.
|
|
61
|
+
* `'Missing required property: Owner'`): the same object `onValidationError` receives.
|
|
62
|
+
*/
|
|
63
|
+
errors: Record<string, string[]>;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Imperative handle of `<ElementEditor ref>` and both adapters' `<ElementEditorControls ref>`.
|
|
67
|
+
* Coordinates are GeoJSON order, `[lng, lat]`, unless a method says otherwise.
|
|
68
|
+
* Map methods (`flyTo`, `setZoom`, `fitBounds`, `select`'s `fit`) do nothing until the map has mounted.
|
|
69
|
+
*
|
|
70
|
+
* Data-changing methods go through the same store actions as the UI: `maxFeatures`, schema
|
|
71
|
+
* validation, undo and the `onChange` / `onFeature*` / `onSelectionChange` callbacks behave as
|
|
72
|
+
* for the user's own edits (callbacks fire synchronously, before the method returns). They work
|
|
73
|
+
* in `readonly` mode too (the host app is in charge); only `setTool` is refused there.
|
|
74
|
+
*
|
|
75
|
+
* `TMap` is the engine's map type: `maplibregl.Map` on `@element-editor/react` and
|
|
76
|
+
* `@element-editor/adapter-maplibre`, `L.Map` on `@element-editor/react/leaflet` and
|
|
77
|
+
* `@element-editor/adapter-leaflet`.
|
|
78
|
+
*/
|
|
79
|
+
export interface ElementEditorImperativeAPI<TMap = unknown> {
|
|
80
|
+
/**
|
|
81
|
+
* Snapshot of the current data as a FeatureCollection: every feature (hidden ones included,
|
|
82
|
+
* internal validation state stripped) plus `metadata.schemas` / `metadata.groups`.
|
|
83
|
+
*/
|
|
84
|
+
getFeatureCollection: () => FeatureCollection;
|
|
85
|
+
/**
|
|
86
|
+
* One feature by id, as `getFeatureCollection()` would return it (hidden features included), or
|
|
87
|
+
* `null` for an unknown id.
|
|
88
|
+
*/
|
|
89
|
+
getFeature: (id: string) => GeoJSONFeature | null;
|
|
90
|
+
/**
|
|
91
|
+
* Replaces all data with `data` (like `importGeoJSON(data, 'replace')`). Plain GeoJSON is fine:
|
|
92
|
+
* missing ids get UUIDs, no `metadata` means the default schema. Features without a usable
|
|
93
|
+
* geometry (`geometry: null`, unknown types) are skipped and reported through `onError` /
|
|
94
|
+
* the `error` event.
|
|
95
|
+
*
|
|
96
|
+
* By default this fires `onChange` (and `onFeatureAdd` / `onFeatureDelete`) like any change and is
|
|
97
|
+
* one undo step. When you load data from your backend and save it in `onChange`, pass
|
|
98
|
+
* `{ silent: true }` so the load isn't saved straight back (see `SetFeatureCollectionOptions`).
|
|
99
|
+
*
|
|
100
|
+
* ```ts
|
|
101
|
+
* const fc = await (await fetch('/api/map')).json()
|
|
102
|
+
* ref.current?.setFeatureCollection(fc, { silent: true, undoable: false })
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
setFeatureCollection: (data: FeatureCollectionInput, options?: SetFeatureCollectionOptions) => void;
|
|
106
|
+
/** The current data serialised as a GeoJSON string (what "Export" downloads). */
|
|
107
|
+
exportGeoJSON: () => string;
|
|
108
|
+
/** Triggers a browser download of the current data. Only `'geojson'` is supported. */
|
|
109
|
+
exportAs: (format: 'geojson') => void;
|
|
110
|
+
/**
|
|
111
|
+
* Imports a FeatureCollection (or anything parseable as one). `'replace'` (default) swaps the
|
|
112
|
+
* data, `'merge'` appends. It is one undo step (undo restores the previous features, schemas and
|
|
113
|
+
* groups; no file backup). Returns counts of skipped (unsupported
|
|
114
|
+
* geometry) / duplicate (repeated id) features and whether the import was truncated
|
|
115
|
+
* (`maxFeatures` or the internal 50,000-feature limit).
|
|
116
|
+
*/
|
|
117
|
+
importGeoJSON: (raw: FeatureCollectionInput | unknown, mode?: 'replace' | 'merge') => ImportResult;
|
|
118
|
+
/**
|
|
119
|
+
* Updates many features in one commit: one store update, one `onChange`, and `onFeatureUpdate`
|
|
120
|
+
* once per feature whose geometry or properties changed. Each `patch` may set `properties`
|
|
121
|
+
* (replaces the whole object), `geometry`, `schemaId` or `hidden`; features are re-validated
|
|
122
|
+
* against their schema. The batch is one undo step (see `MutationOptions.undoable`). Unknown ids
|
|
123
|
+
* and malformed patches are skipped (with a development warning).
|
|
124
|
+
*
|
|
125
|
+
* ```ts
|
|
126
|
+
* ref.current.updateFeatures(ids.map((id) => ({ id, patch: { properties: { ...props(id), status: 'done' } } })))
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
updateFeatures: (updates: ReadonlyArray<{
|
|
130
|
+
id: string;
|
|
131
|
+
patch: FeaturePatch;
|
|
132
|
+
}>, options?: MutationOptions) => void;
|
|
133
|
+
/**
|
|
134
|
+
* Adds a feature and returns it as stored (with its `id`; a missing id gets a UUID), or `null`
|
|
135
|
+
* when nothing was added: the geometry isn't valid GeoJSON, the id is already used (both warn
|
|
136
|
+
* in development), or `maxFeatures` is reached (reported like a user add). The feature goes on
|
|
137
|
+
* top of the layer list; the selection is left alone. `groupId` is kept only if that group exists.
|
|
138
|
+
* Fires `onFeatureAdd` and `onChange`.
|
|
139
|
+
*/
|
|
140
|
+
addFeature: (feature: GeoJSONFeatureInput, options?: MutationOptions) => GeoJSONFeature | null;
|
|
141
|
+
/**
|
|
142
|
+
* Updates one feature. `patch` may set `properties` (replaces the whole object), `geometry`,
|
|
143
|
+
* `schemaId` or `hidden`; other keys are ignored. The feature is re-validated against its schema
|
|
144
|
+
* (schema errors don't block the write). Returns `false`, changing nothing, for an unknown id or
|
|
145
|
+
* a malformed patch (both warn in development). Fires `onFeatureUpdate` and `onChange`.
|
|
146
|
+
*/
|
|
147
|
+
updateFeature: (id: string, patch: FeaturePatch, options?: MutationOptions) => boolean;
|
|
148
|
+
/**
|
|
149
|
+
* Removes the features with these ids in one undo step and returns how many were removed
|
|
150
|
+
* (unknown ids are skipped, with a development warning). Removed ids leave the selection.
|
|
151
|
+
* Fires `onFeatureDelete` per feature and `onChange` once.
|
|
152
|
+
*/
|
|
153
|
+
removeFeatures: (ids: string[], options?: MutationOptions) => number;
|
|
154
|
+
/** Ids of the selected features (a copy). */
|
|
155
|
+
getSelection: () => string[];
|
|
156
|
+
/**
|
|
157
|
+
* Replaces the selection with these features (`[]` clears it) and fires `onSelectionChange`.
|
|
158
|
+
* Unknown ids, group ids and hidden features (or features in a hidden group) are left out, with a
|
|
159
|
+
* development warning. `fit: true` also fits the map to the selected features.
|
|
160
|
+
*/
|
|
161
|
+
select: (ids: string[], options?: {
|
|
162
|
+
fit?: boolean;
|
|
163
|
+
}) => void;
|
|
164
|
+
/**
|
|
165
|
+
* Turns a drawing tool on, as its toolbar button does (an unfinished shape of another tool is
|
|
166
|
+
* discarded); `null` turns the active tool off. Returns `false`, changing nothing, when the tool
|
|
167
|
+
* isn't available: unknown or `'search'`, left out by `tools`, `maxFeatures` reached (tools that
|
|
168
|
+
* add features), or `readonly` (every tool but `'measure'`, which the readonly toolbar keeps).
|
|
169
|
+
*/
|
|
170
|
+
setTool: (tool: DrawTool | null) => boolean;
|
|
171
|
+
/**
|
|
172
|
+
* The drawing tool that is on (as `setTool` takes it), or `null` when none is. Shape operations
|
|
173
|
+
* (cut, slice, simplify, smooth, buffer) aren't tools: `null` while they run.
|
|
174
|
+
*/
|
|
175
|
+
getTool: () => DrawTool | null;
|
|
176
|
+
/** `true` when `undo()` would change something (there is an edit to undo). */
|
|
177
|
+
canUndo: () => boolean;
|
|
178
|
+
/** `true` when `redo()` would change something (there is an undone edit to redo). */
|
|
179
|
+
canRedo: () => boolean;
|
|
180
|
+
/**
|
|
181
|
+
* Validates every feature against its schema (required fields, number `min` / `max`) and its
|
|
182
|
+
* geometry (zero-area polygons), and returns the result. Changes nothing and calls no callback:
|
|
183
|
+
* use it to check before your own save, or to show errors. `submit()` runs the same check.
|
|
184
|
+
*
|
|
185
|
+
* ```ts
|
|
186
|
+
* const { valid, errors } = ref.current!.validate()
|
|
187
|
+
* if (!valid) ref.current!.select(Object.keys(errors), { fit: true })
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
validate: () => ValidationReport;
|
|
191
|
+
/**
|
|
192
|
+
* The underlying map: `maplibregl.Map` or `L.Map` (see `TMap`), or `null` until the map has
|
|
193
|
+
* mounted (the MapLibre drop-in creates it once its basemap style has loaded).
|
|
194
|
+
*
|
|
195
|
+
* On a MapLibre map, switch basemaps with the `basemap` prop or `setBasemap`: they keep your
|
|
196
|
+
* layers. After your own `map.setStyle()` Element restores its own layers; re-add yours on `style.load`.
|
|
197
|
+
*/
|
|
198
|
+
getMap: () => TMap | null;
|
|
199
|
+
/**
|
|
200
|
+
* Animates the map to a centre and zoom.
|
|
201
|
+
*
|
|
202
|
+
* - `flyTo({ center: [lng, lat], zoom? })` — preferred; GeoJSON order. Zoom defaults to 15.
|
|
203
|
+
* - `flyTo([lat, lng], zoom?)` — deprecated `[lat, lng]` tuple form; still works, warns once
|
|
204
|
+
* in development.
|
|
205
|
+
*/
|
|
206
|
+
flyTo: {
|
|
207
|
+
(options: FlyToOptions): void;
|
|
208
|
+
/** @deprecated `[lat, lng]` order. Use `flyTo({ center: [lng, lat], zoom })`. */
|
|
209
|
+
(latlng: [number, number], zoom?: number): void;
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* Sets the zoom immediately (no animation), clamped to the map's min/max zoom.
|
|
213
|
+
* Returns `false` when the map is not mounted yet (nothing happened), `true` otherwise.
|
|
214
|
+
*/
|
|
215
|
+
setZoom: (zoom: number) => boolean;
|
|
216
|
+
/**
|
|
217
|
+
* Animates the map to fit a box, with padding, never zooming in past 16. Accepts:
|
|
218
|
+
*
|
|
219
|
+
* - a GeoJSON bbox `[minLng, minLat, maxLng, maxLat]` (= `[west, south, east, north]`), or
|
|
220
|
+
* - an array of `[lng, lat]` points (fits their bounding box).
|
|
221
|
+
*
|
|
222
|
+
* Empty input or input without finite coordinates is ignored.
|
|
223
|
+
*/
|
|
224
|
+
fitBounds: (target: [number, number][] | [number, number, number, number]) => void;
|
|
225
|
+
/**
|
|
226
|
+
* Validates every feature (as `validate()` does), then submits. Returns `false` if any feature
|
|
227
|
+
* has validation errors: `onValidationError` is called with them (keyed by feature id) and
|
|
228
|
+
* `onSubmit` is not. Returns `true` when the data is valid: `onSubmit` (if set) is called
|
|
229
|
+
* with the current FeatureCollection.
|
|
230
|
+
*/
|
|
231
|
+
submit: () => boolean;
|
|
232
|
+
/** Undoes the last edit (no-op when there is nothing to undo; see `canUndo`). */
|
|
233
|
+
undo: () => void;
|
|
234
|
+
/** Redoes the last undone edit (no-op when there is nothing to redo; see `canRedo`). */
|
|
235
|
+
redo: () => void;
|
|
236
|
+
/** Deselects all features. */
|
|
237
|
+
clearSelection: () => void;
|
|
238
|
+
}
|
|
239
|
+
export interface UseAdapterControlsOptions {
|
|
240
|
+
/**
|
|
241
|
+
* Plain GeoJSON is fine (see `FeatureCollectionInput`); a single Feature is wrapped in a collection.
|
|
242
|
+
* Applied on mount. A value that arrives later (`null` / `undefined` → data, e.g. after a fetch) is
|
|
243
|
+
* applied once too, if the editor is still empty and unedited; any other change is ignored, with a
|
|
244
|
+
* development warning.
|
|
245
|
+
*/
|
|
246
|
+
initialData?: FeatureCollectionInput | GeoJSONFeatureInput | null;
|
|
247
|
+
schema?: Schema;
|
|
248
|
+
layout?: 'panel' | 'minimal' | 'split';
|
|
249
|
+
readonly?: boolean;
|
|
250
|
+
tools?: DrawTool[] | null;
|
|
251
|
+
maxFeatures?: number;
|
|
252
|
+
/**
|
|
253
|
+
* The map engine; the adapters pass it. With it, the tools `config.network` turns off are
|
|
254
|
+
* synced to `uiStore.unavailableTools` (the Picker needs Overpass only on Leaflet).
|
|
255
|
+
*/
|
|
256
|
+
engine?: 'leaflet' | 'maplibre';
|
|
257
|
+
onChange?: (data: FeatureCollection) => void;
|
|
258
|
+
onSubmit?: (data: FeatureCollection) => void;
|
|
259
|
+
onValidationError?: (errors: Record<string, string[]>) => void;
|
|
260
|
+
onFeatureAdd?: (feature: GeoJSONFeature) => void;
|
|
261
|
+
onFeatureUpdate?: (id: string, feature: GeoJSONFeature) => void;
|
|
262
|
+
onFeatureDelete?: (id: string) => void;
|
|
263
|
+
onSelectionChange?: (ids: string[]) => void;
|
|
264
|
+
onFeatureSelect?: (feature: GeoJSONFeature | null) => void;
|
|
265
|
+
onZoomChange?: (zoom: number) => void;
|
|
266
|
+
/** Called with the tool that turned on (as `ref.getTool()` reports it), or `null` when it turned off. */
|
|
267
|
+
onToolChange?: (tool: DrawTool | null) => void;
|
|
268
|
+
/**
|
|
269
|
+
* @internal Called after `initialData` was applied: `late` is true for data that arrived after
|
|
270
|
+
* mount. The drop-in uses it to fit the late data.
|
|
271
|
+
*/
|
|
272
|
+
onInitialDataApplied?: (data: FeatureCollectionInput, late: boolean) => void;
|
|
273
|
+
}
|
|
274
|
+
/** The tools `network` turns off: no routing → Route, no geocoder → search, no Overpass → Picker on Leaflet (MapLibre still picks from the vector tiles). */
|
|
275
|
+
export declare function networkUnavailableTools(network: {
|
|
276
|
+
routing?: unknown;
|
|
277
|
+
nominatim?: unknown;
|
|
278
|
+
overpass?: unknown;
|
|
279
|
+
}, engine: 'leaflet' | 'maplibre'): string[];
|
|
280
|
+
export declare function useAdapterControls<TMap = unknown>(opts: UseAdapterControlsOptions): ElementEditorImperativeAPI<TMap>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* `text-wrap: balance` for a `flex-wrap` toolbar: when its buttons don't fit
|
|
4
|
+
* on one row, it's narrowed to the least width that still needs no more rows,
|
|
5
|
+
* so a narrow screen shows 4 + 4 buttons rather than 7 + a lone Delete.
|
|
6
|
+
* Re-runs when the room around it or `key` (what the toolbar holds) changes.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useBalancedWrap(ref: RefObject<HTMLElement | null>, key: unknown): void;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* `text-wrap: balance` for a `flex-wrap` toolbar: when its buttons don't fit
|
|
4
|
+
* on one row, it's narrowed to the least width that still needs no more rows,
|
|
5
|
+
* so a narrow screen shows 4 + 4 buttons rather than 7 + a lone Delete.
|
|
6
|
+
* Re-runs when the room around it or `key` (what the toolbar holds) changes.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useBalancedWrap(ref: RefObject<HTMLElement | null>, key: unknown): void;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { StoreApi } from 'zustand';
|
|
2
|
+
import { DataStore, DataChangeCallbacks } from '@element-editor/core';
|
|
3
|
+
/**
|
|
4
|
+
* React wrapper around `subscribeToDataChanges`. Owns the callback ref
|
|
5
|
+
* plumbing so hosts can pass a fresh object each render without resubscribing.
|
|
6
|
+
*
|
|
7
|
+
* The subscription only tears down when `store` itself changes.
|
|
8
|
+
*/
|
|
9
|
+
export declare function useDataChangeSubscription(store: StoreApi<DataStore>, callbacks: DataChangeCallbacks): void;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { StoreApi } from 'zustand';
|
|
2
|
+
import { DataStore, DataChangeCallbacks } from '@element-editor/core';
|
|
3
|
+
/**
|
|
4
|
+
* React wrapper around `subscribeToDataChanges`. Owns the callback ref
|
|
5
|
+
* plumbing so hosts can pass a fresh object each render without resubscribing.
|
|
6
|
+
*
|
|
7
|
+
* The subscription only tears down when `store` itself changes.
|
|
8
|
+
*/
|
|
9
|
+
export declare function useDataChangeSubscription(store: StoreApi<DataStore>, callbacks: DataChangeCallbacks): void;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Traps keyboard focus within a container element.
|
|
4
|
+
* Tab and Shift+Tab cycle through focusable elements without leaving the container.
|
|
5
|
+
* Automatically focuses the first focusable element on mount.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useFocusTrap(containerRef: RefObject<HTMLElement | null>, enabled?: boolean): void;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Traps keyboard focus within a container element.
|
|
4
|
+
* Tab and Shift+Tab cycle through focusable elements without leaving the container.
|
|
5
|
+
* Automatically focuses the first focusable element on mount.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useFocusTrap(containerRef: RefObject<HTMLElement | null>, enabled?: boolean): void;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { DragEvent as ReactDragEvent, ChangeEvent } from 'react';
|
|
2
|
+
import { FeatureCollection, ParseGeoJSONError } from '@element-editor/core';
|
|
3
|
+
export type GeoJSONDropError = {
|
|
4
|
+
kind: 'wrong-type';
|
|
5
|
+
file: File;
|
|
6
|
+
} | ParseGeoJSONError;
|
|
7
|
+
export interface UseGeoJSONDropOptions {
|
|
8
|
+
/** Called with the parsed FeatureCollection + original File handle. */
|
|
9
|
+
onImport: (fc: FeatureCollection, file: File) => void;
|
|
10
|
+
/** Called for any reject reason (wrong MIME/extension, too big, malformed, not a FC). */
|
|
11
|
+
onError?: (err: GeoJSONDropError) => void;
|
|
12
|
+
/** Max accepted byte length of the file. Defaults to core's DEFAULT_MAX_IMPORT_BYTES (100 MB). */
|
|
13
|
+
maxBytes?: number;
|
|
14
|
+
/** File extensions + MIME types to accept. Defaults to `.geojson`, `.json`, and their MIME equivalents. */
|
|
15
|
+
accept?: string[];
|
|
16
|
+
/** When true, drag/drop + file picker become no-ops and `isDragActive` stays false. */
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface UseGeoJSONDropReturn {
|
|
20
|
+
getRootProps: () => {
|
|
21
|
+
onDragEnter: (e: ReactDragEvent) => void;
|
|
22
|
+
onDragOver: (e: ReactDragEvent) => void;
|
|
23
|
+
onDragLeave: (e: ReactDragEvent) => void;
|
|
24
|
+
onDrop: (e: ReactDragEvent) => void;
|
|
25
|
+
};
|
|
26
|
+
getInputProps: () => {
|
|
27
|
+
type: 'file';
|
|
28
|
+
accept: string;
|
|
29
|
+
style: {
|
|
30
|
+
display: 'none';
|
|
31
|
+
};
|
|
32
|
+
ref: (el: HTMLInputElement | null) => void;
|
|
33
|
+
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
34
|
+
};
|
|
35
|
+
isDragActive: boolean;
|
|
36
|
+
isProcessing: boolean;
|
|
37
|
+
/** Programmatically open the native file picker. No-op when `disabled`. */
|
|
38
|
+
open: () => void;
|
|
39
|
+
}
|
|
40
|
+
export declare function useGeoJSONDrop(options: UseGeoJSONDropOptions): UseGeoJSONDropReturn;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { DragEvent as ReactDragEvent, ChangeEvent } from 'react';
|
|
2
|
+
import { FeatureCollection, ParseGeoJSONError } from '@element-editor/core';
|
|
3
|
+
export type GeoJSONDropError = {
|
|
4
|
+
kind: 'wrong-type';
|
|
5
|
+
file: File;
|
|
6
|
+
} | ParseGeoJSONError;
|
|
7
|
+
export interface UseGeoJSONDropOptions {
|
|
8
|
+
/** Called with the parsed FeatureCollection + original File handle. */
|
|
9
|
+
onImport: (fc: FeatureCollection, file: File) => void;
|
|
10
|
+
/** Called for any reject reason (wrong MIME/extension, too big, malformed, not a FC). */
|
|
11
|
+
onError?: (err: GeoJSONDropError) => void;
|
|
12
|
+
/** Max accepted byte length of the file. Defaults to core's DEFAULT_MAX_IMPORT_BYTES (100 MB). */
|
|
13
|
+
maxBytes?: number;
|
|
14
|
+
/** File extensions + MIME types to accept. Defaults to `.geojson`, `.json`, and their MIME equivalents. */
|
|
15
|
+
accept?: string[];
|
|
16
|
+
/** When true, drag/drop + file picker become no-ops and `isDragActive` stays false. */
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface UseGeoJSONDropReturn {
|
|
20
|
+
getRootProps: () => {
|
|
21
|
+
onDragEnter: (e: ReactDragEvent) => void;
|
|
22
|
+
onDragOver: (e: ReactDragEvent) => void;
|
|
23
|
+
onDragLeave: (e: ReactDragEvent) => void;
|
|
24
|
+
onDrop: (e: ReactDragEvent) => void;
|
|
25
|
+
};
|
|
26
|
+
getInputProps: () => {
|
|
27
|
+
type: 'file';
|
|
28
|
+
accept: string;
|
|
29
|
+
style: {
|
|
30
|
+
display: 'none';
|
|
31
|
+
};
|
|
32
|
+
ref: (el: HTMLInputElement | null) => void;
|
|
33
|
+
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
34
|
+
};
|
|
35
|
+
isDragActive: boolean;
|
|
36
|
+
isProcessing: boolean;
|
|
37
|
+
/** Programmatically open the native file picker. No-op when `disabled`. */
|
|
38
|
+
open: () => void;
|
|
39
|
+
}
|
|
40
|
+
export declare function useGeoJSONDrop(options: UseGeoJSONDropOptions): UseGeoJSONDropReturn;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { FeatureCollection } from '@element-editor/core';
|
|
2
|
+
/**
|
|
3
|
+
* Headless primitive for exporting the current Element state as GeoJSON.
|
|
4
|
+
* Symmetric to {@link useGeoJSONDrop}: the caller supplies a way to read the
|
|
5
|
+
* current FeatureCollection, the hook handles serialization, filename, Blob
|
|
6
|
+
* construction, and the programmatic anchor-click download.
|
|
7
|
+
*
|
|
8
|
+
* Three return shapes because different apps want different things:
|
|
9
|
+
* - `exportToFile()` — trigger a browser download (the 99% path).
|
|
10
|
+
* - `exportToString()` — POST the text to a backend, copy to clipboard, etc.
|
|
11
|
+
* - `exportToBlob()` — wrap in FormData, upload as multipart, pipe to a
|
|
12
|
+
* Worker, hand off to `navigator.share`, etc.
|
|
13
|
+
*
|
|
14
|
+
* All three share the same serialization and error handling.
|
|
15
|
+
*/
|
|
16
|
+
export type GeoJSONExportError = {
|
|
17
|
+
kind: 'empty';
|
|
18
|
+
} | {
|
|
19
|
+
kind: 'serialization-error';
|
|
20
|
+
cause: Error;
|
|
21
|
+
};
|
|
22
|
+
export interface UseGeoJSONExportOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Returns the FeatureCollection to export. Read lazily on every call so
|
|
25
|
+
* the hook doesn't re-render when features mutate. Typical usage:
|
|
26
|
+
* `() => ref.current?.getFeatureCollection()`.
|
|
27
|
+
*/
|
|
28
|
+
getFeatureCollection: () => FeatureCollection | null | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* File name (without extension) or a function of the FC. Default: "features".
|
|
31
|
+
* The `.geojson` extension is appended automatically.
|
|
32
|
+
*/
|
|
33
|
+
filename?: string | ((fc: FeatureCollection) => string);
|
|
34
|
+
/**
|
|
35
|
+
* Called after the user has CONFIRMED the save (Chromium's
|
|
36
|
+
* `showSaveFilePicker` resolved with a chosen path). Great for toasts.
|
|
37
|
+
*
|
|
38
|
+
* Note: in Firefox / Safari the legacy `<a download>` flow has no
|
|
39
|
+
* confirmation signal, so `onExport` is NOT called there — a pre-mature
|
|
40
|
+
* "exported!" toast for an action the user may have cancelled in the OS
|
|
41
|
+
* Save dialog is worse than no toast. See {@link saveGeoJSONFile}.
|
|
42
|
+
*/
|
|
43
|
+
onExport?: (fc: FeatureCollection, file: {
|
|
44
|
+
name: string;
|
|
45
|
+
bytes: number;
|
|
46
|
+
}) => void;
|
|
47
|
+
/** Called on empty FC or serialization errors. */
|
|
48
|
+
onError?: (err: GeoJSONExportError) => void;
|
|
49
|
+
/** Pretty-print indent. Default 2. Pass 0 for compact output. */
|
|
50
|
+
indent?: number;
|
|
51
|
+
/** When true, export methods become no-ops. Useful for readonly modes. */
|
|
52
|
+
disabled?: boolean;
|
|
53
|
+
}
|
|
54
|
+
export interface UseGeoJSONExportReturn {
|
|
55
|
+
/**
|
|
56
|
+
* Serialize, build a Blob, and trigger a browser download. Returns the
|
|
57
|
+
* suggested filename immediately (`null` only when there's nothing to
|
|
58
|
+
* export). Fires `onExport` ONLY after the user confirms the save in the
|
|
59
|
+
* OS dialog (Chromium); Firefox / Safari fall back to the legacy
|
|
60
|
+
* `<a download>` flow silently — see `onExport` doc above for why.
|
|
61
|
+
* Fires `onError({ kind: 'empty' })` when the FC has no features.
|
|
62
|
+
*/
|
|
63
|
+
exportToFile: () => string | null;
|
|
64
|
+
/** Return the serialized GeoJSON text, or `null` if the FC is empty. */
|
|
65
|
+
exportToString: () => string | null;
|
|
66
|
+
/** Return a `Blob` of the serialized GeoJSON, or `null` if the FC is empty. */
|
|
67
|
+
exportToBlob: () => Blob | null;
|
|
68
|
+
}
|
|
69
|
+
export declare function useGeoJSONExport(options: UseGeoJSONExportOptions): UseGeoJSONExportReturn;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { FeatureCollection } from '@element-editor/core';
|
|
2
|
+
/**
|
|
3
|
+
* Headless primitive for exporting the current Element state as GeoJSON.
|
|
4
|
+
* Symmetric to {@link useGeoJSONDrop}: the caller supplies a way to read the
|
|
5
|
+
* current FeatureCollection, the hook handles serialization, filename, Blob
|
|
6
|
+
* construction, and the programmatic anchor-click download.
|
|
7
|
+
*
|
|
8
|
+
* Three return shapes because different apps want different things:
|
|
9
|
+
* - `exportToFile()` — trigger a browser download (the 99% path).
|
|
10
|
+
* - `exportToString()` — POST the text to a backend, copy to clipboard, etc.
|
|
11
|
+
* - `exportToBlob()` — wrap in FormData, upload as multipart, pipe to a
|
|
12
|
+
* Worker, hand off to `navigator.share`, etc.
|
|
13
|
+
*
|
|
14
|
+
* All three share the same serialization and error handling.
|
|
15
|
+
*/
|
|
16
|
+
export type GeoJSONExportError = {
|
|
17
|
+
kind: 'empty';
|
|
18
|
+
} | {
|
|
19
|
+
kind: 'serialization-error';
|
|
20
|
+
cause: Error;
|
|
21
|
+
};
|
|
22
|
+
export interface UseGeoJSONExportOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Returns the FeatureCollection to export. Read lazily on every call so
|
|
25
|
+
* the hook doesn't re-render when features mutate. Typical usage:
|
|
26
|
+
* `() => ref.current?.getFeatureCollection()`.
|
|
27
|
+
*/
|
|
28
|
+
getFeatureCollection: () => FeatureCollection | null | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* File name (without extension) or a function of the FC. Default: "features".
|
|
31
|
+
* The `.geojson` extension is appended automatically.
|
|
32
|
+
*/
|
|
33
|
+
filename?: string | ((fc: FeatureCollection) => string);
|
|
34
|
+
/**
|
|
35
|
+
* Called after the user has CONFIRMED the save (Chromium's
|
|
36
|
+
* `showSaveFilePicker` resolved with a chosen path). Great for toasts.
|
|
37
|
+
*
|
|
38
|
+
* Note: in Firefox / Safari the legacy `<a download>` flow has no
|
|
39
|
+
* confirmation signal, so `onExport` is NOT called there — a pre-mature
|
|
40
|
+
* "exported!" toast for an action the user may have cancelled in the OS
|
|
41
|
+
* Save dialog is worse than no toast. See {@link saveGeoJSONFile}.
|
|
42
|
+
*/
|
|
43
|
+
onExport?: (fc: FeatureCollection, file: {
|
|
44
|
+
name: string;
|
|
45
|
+
bytes: number;
|
|
46
|
+
}) => void;
|
|
47
|
+
/** Called on empty FC or serialization errors. */
|
|
48
|
+
onError?: (err: GeoJSONExportError) => void;
|
|
49
|
+
/** Pretty-print indent. Default 2. Pass 0 for compact output. */
|
|
50
|
+
indent?: number;
|
|
51
|
+
/** When true, export methods become no-ops. Useful for readonly modes. */
|
|
52
|
+
disabled?: boolean;
|
|
53
|
+
}
|
|
54
|
+
export interface UseGeoJSONExportReturn {
|
|
55
|
+
/**
|
|
56
|
+
* Serialize, build a Blob, and trigger a browser download. Returns the
|
|
57
|
+
* suggested filename immediately (`null` only when there's nothing to
|
|
58
|
+
* export). Fires `onExport` ONLY after the user confirms the save in the
|
|
59
|
+
* OS dialog (Chromium); Firefox / Safari fall back to the legacy
|
|
60
|
+
* `<a download>` flow silently — see `onExport` doc above for why.
|
|
61
|
+
* Fires `onError({ kind: 'empty' })` when the FC has no features.
|
|
62
|
+
*/
|
|
63
|
+
exportToFile: () => string | null;
|
|
64
|
+
/** Return the serialized GeoJSON text, or `null` if the FC is empty. */
|
|
65
|
+
exportToString: () => string | null;
|
|
66
|
+
/** Return a `Blob` of the serialized GeoJSON, or `null` if the FC is empty. */
|
|
67
|
+
exportToBlob: () => Blob | null;
|
|
68
|
+
}
|
|
69
|
+
export declare function useGeoJSONExport(options: UseGeoJSONExportOptions): UseGeoJSONExportReturn;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ErrorCode } from '@element-editor/core';
|
|
2
|
+
type ImportFn = (raw: unknown, mode: 'replace' | 'merge') => {
|
|
3
|
+
skipped: number;
|
|
4
|
+
duplicates: number;
|
|
5
|
+
};
|
|
6
|
+
/** Reads, parses and imports (replace) a GeoJSON file. Exported for tests; the UI uses `useGeoJSONFileImport`. */
|
|
7
|
+
export declare function importGeoJSONFromFile(file: File, importFn: ImportFn, countAfter: () => number): Promise<{
|
|
8
|
+
ok: true;
|
|
9
|
+
count: number;
|
|
10
|
+
skipped: number;
|
|
11
|
+
duplicates: number;
|
|
12
|
+
} | {
|
|
13
|
+
ok: false;
|
|
14
|
+
code: ErrorCode;
|
|
15
|
+
reason: string;
|
|
16
|
+
cause?: unknown;
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* The one user-initiated file import (Cmd+O, dropping a file on the map): replaces the data in one
|
|
20
|
+
* undo step (`maxFeatures` truncates and reports), fits the map to what was imported, and reports
|
|
21
|
+
* the result as events (toasts). Refused in readonly.
|
|
22
|
+
*/
|
|
23
|
+
export declare function useGeoJSONFileImport(): (file: File) => Promise<void>;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ErrorCode } from '@element-editor/core';
|
|
2
|
+
type ImportFn = (raw: unknown, mode: 'replace' | 'merge') => {
|
|
3
|
+
skipped: number;
|
|
4
|
+
duplicates: number;
|
|
5
|
+
};
|
|
6
|
+
/** Reads, parses and imports (replace) a GeoJSON file. Exported for tests; the UI uses `useGeoJSONFileImport`. */
|
|
7
|
+
export declare function importGeoJSONFromFile(file: File, importFn: ImportFn, countAfter: () => number): Promise<{
|
|
8
|
+
ok: true;
|
|
9
|
+
count: number;
|
|
10
|
+
skipped: number;
|
|
11
|
+
duplicates: number;
|
|
12
|
+
} | {
|
|
13
|
+
ok: false;
|
|
14
|
+
code: ErrorCode;
|
|
15
|
+
reason: string;
|
|
16
|
+
cause?: unknown;
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* The one user-initiated file import (Cmd+O, dropping a file on the map): replaces the data in one
|
|
20
|
+
* undo step (`maxFeatures` truncates and reports), fits the map to what was imported, and reports
|
|
21
|
+
* the result as events (toasts). Refused in readonly.
|
|
22
|
+
*/
|
|
23
|
+
export declare function useGeoJSONFileImport(): (file: File) => Promise<void>;
|
|
24
|
+
export {};
|