@theseam/ui-common 2.0.1-beta.110 → 2.0.1-beta.111
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/fesm2022/theseam-ui-common-google-maps.mjs +1632 -161
- package/fesm2022/theseam-ui-common-google-maps.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-story-helpers.mjs +55 -2
- package/fesm2022/theseam-ui-common-story-helpers.mjs.map +1 -1
- package/google-maps/index.d.ts +426 -13
- package/package.json +1 -1
- package/story-helpers/index.d.ts +4 -2
package/google-maps/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { Observable } from 'rxjs';
|
|
|
7
7
|
import { CanDisableCtor, CanDisable } from '@theseam/ui-common/core';
|
|
8
8
|
import * as i10 from '@theseam/ui-common/menu';
|
|
9
9
|
import { MenuComponent } from '@theseam/ui-common/menu';
|
|
10
|
+
import { Feature, Polygon, MultiPolygon } from 'geojson';
|
|
10
11
|
import { ComponentType } from '@theseam/ui-common/models';
|
|
11
12
|
import * as i11 from '@theseam/ui-common/icon';
|
|
12
13
|
import { SeamIcon } from '@theseam/ui-common/icon';
|
|
@@ -16,12 +17,50 @@ import * as i7 from '@angular/common';
|
|
|
16
17
|
import * as i8 from '@angular/cdk/observers';
|
|
17
18
|
import * as i9 from '@theseam/ui-common/shared';
|
|
18
19
|
import * as i13 from '@angular/google-maps';
|
|
19
|
-
|
|
20
|
+
|
|
21
|
+
/** A set of features that belong to the same logical thing, such as one field. */
|
|
22
|
+
interface TheSeamMapFeatureGroup {
|
|
23
|
+
/**
|
|
24
|
+
* `properties[featureGroupProperty]`.
|
|
25
|
+
*
|
|
26
|
+
* Falls back to a map-assigned key for features that carry no group value —
|
|
27
|
+
* always the case when no group property is configured, and for imported
|
|
28
|
+
* features the consumer did not assign one to. An assigned key is
|
|
29
|
+
* session-scoped: it does not survive an external value write, so it must not
|
|
30
|
+
* be persisted or held across a write and passed back to `selectGroup()`.
|
|
31
|
+
*/
|
|
32
|
+
key: string;
|
|
33
|
+
features: Feature<Polygon | MultiPolygon>[];
|
|
34
|
+
}
|
|
35
|
+
/** A group, plus the individual feature an interaction landed on. */
|
|
36
|
+
interface TheSeamMapGroupTarget {
|
|
37
|
+
group: TheSeamMapFeatureGroup;
|
|
38
|
+
/**
|
|
39
|
+
* The polygon the interaction landed on. For a selection this is the one
|
|
40
|
+
* `Delete` acts on; for a hover, the one under the cursor.
|
|
41
|
+
*
|
|
42
|
+
* A reference into `group.features`, not a copy.
|
|
43
|
+
*/
|
|
44
|
+
feature: Feature<Polygon | MultiPolygon> | null;
|
|
45
|
+
}
|
|
20
46
|
|
|
21
47
|
declare abstract class TheSeamGoogleMapsApiLoader {
|
|
22
48
|
abstract load(): Observable<void>;
|
|
23
49
|
}
|
|
24
50
|
|
|
51
|
+
interface FeatureGroupRegistryOptions {
|
|
52
|
+
/**
|
|
53
|
+
* Name of the GeoJSON property that groups features. When unset, every
|
|
54
|
+
* feature is its own group.
|
|
55
|
+
*/
|
|
56
|
+
groupProperty?: string | null;
|
|
57
|
+
/** Generates the key written for a newly created group. */
|
|
58
|
+
newGroupKeyFactory?: () => string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Which interaction model a map uses. */
|
|
62
|
+
type TheSeamMapInteractionMode = 'legacy' | 'grouped';
|
|
63
|
+
|
|
25
64
|
declare enum MapValueSource {
|
|
26
65
|
Input = "input",
|
|
27
66
|
FeatureChange = "featurechange"
|
|
@@ -51,6 +90,26 @@ declare class GoogleMapsService implements OnDestroy {
|
|
|
51
90
|
private readonly _editingEnabledSubject;
|
|
52
91
|
private _terraDraw?;
|
|
53
92
|
private _terraDrawReady;
|
|
93
|
+
private _pendingStartDrawing;
|
|
94
|
+
/**
|
|
95
|
+
* One-shot suppression for the map `click` listener, armed the instant a
|
|
96
|
+
* draw finishes (see `_armMapClickSuppression()` for why, and its call
|
|
97
|
+
* site in `_onDrawFinished()`).
|
|
98
|
+
*
|
|
99
|
+
* `domEvent.timeStamp` was tried first, since it is a `DOMHighResTimeStamp`
|
|
100
|
+
* on the same origin as `performance.now()` and looks like a clean,
|
|
101
|
+
* delay-free discriminator: a stale click's timestamp should predate the
|
|
102
|
+
* moment the draw finished. It does not hold up empirically. Driving real
|
|
103
|
+
* mouse draws against the live Storybook and logging both the map `click`
|
|
104
|
+
* listener and `stopDrawing()` (see .superpowers/closing-click-report.md)
|
|
105
|
+
* caught the echo repeatedly, and every single time its `domEvent.timeStamp`
|
|
106
|
+
* was a few milliseconds AFTER `stopDrawing()`'s own timestamp, not before
|
|
107
|
+
* — indistinguishable from a genuinely fresh click by timestamp alone.
|
|
108
|
+
* Google appears to stamp its own synthetic `click` at the moment IT
|
|
109
|
+
* dispatches, not at the original pointer event's time, so there is no
|
|
110
|
+
* "stale-looking" timestamp to compare against.
|
|
111
|
+
*/
|
|
112
|
+
private _suppressNextMapClick;
|
|
54
113
|
private readonly _drawingSubject;
|
|
55
114
|
readonly drawing$: Observable<boolean>;
|
|
56
115
|
private _featureContextMenu;
|
|
@@ -58,6 +117,47 @@ declare class GoogleMapsService implements OnDestroy {
|
|
|
58
117
|
private _baseLatLng?;
|
|
59
118
|
private _padding?;
|
|
60
119
|
private _allowDrawingHoleInPolygon;
|
|
120
|
+
private _model;
|
|
121
|
+
private readonly _interactionModeSubject;
|
|
122
|
+
readonly interactionMode$: Observable<TheSeamMapInteractionMode>;
|
|
123
|
+
private _groups?;
|
|
124
|
+
private _groupOptions;
|
|
125
|
+
private _focusedFeature;
|
|
126
|
+
private _styleFn?;
|
|
127
|
+
private _labelProperty;
|
|
128
|
+
private _labelsOverlay?;
|
|
129
|
+
private _warnedAboutLabelDisagreement;
|
|
130
|
+
private readonly _selectionSubject;
|
|
131
|
+
/**
|
|
132
|
+
* `distinctUntilChanged` collapses repeated `null`s. Several paths clear the
|
|
133
|
+
* selection defensively — `setData`, `clearSelection`, the declarative
|
|
134
|
+
* `selectedGroupKey` application on map-ready — and each would otherwise
|
|
135
|
+
* emit its own `null` to consumers before the user has touched anything.
|
|
136
|
+
*
|
|
137
|
+
* Non-null targets are rebuilt on every change, so reference equality never
|
|
138
|
+
* suppresses a real one, including re-selecting the same group with a
|
|
139
|
+
* different focused polygon.
|
|
140
|
+
*/
|
|
141
|
+
readonly selection$: Observable<TheSeamMapGroupTarget | null>;
|
|
142
|
+
private readonly _hoverSubject;
|
|
143
|
+
/** Same reasoning as `selection$`; `mouseout` repeats `null` freely. */
|
|
144
|
+
readonly hover$: Observable<TheSeamMapGroupTarget | null>;
|
|
145
|
+
/**
|
|
146
|
+
* The group of the feature a `contextmenu` event last landed on. Kept
|
|
147
|
+
* distinct from `selection$` even though `allowsContextMenu()` now requires
|
|
148
|
+
* the right-clicked feature's group to already be the selection — so, for
|
|
149
|
+
* as long as the menu stays open, the two are provably the same group.
|
|
150
|
+
* Collapsing onto `selection$` would re-couple "what the menu acts on" to
|
|
151
|
+
* "whatever is currently selected", which is exactly the coupling this
|
|
152
|
+
* subject was introduced to break when the menu could still open for a
|
|
153
|
+
* non-selected group. That rule has already moved twice; keeping this
|
|
154
|
+
* separate means a future loosening of `allowsContextMenu()` does not have
|
|
155
|
+
* to re-invent it.
|
|
156
|
+
*/
|
|
157
|
+
private readonly _contextMenuTargetSubject;
|
|
158
|
+
readonly contextMenuTarget$: Observable<TheSeamMapGroupTarget | null>;
|
|
159
|
+
private readonly _editModeSubject;
|
|
160
|
+
readonly editMode$: Observable<boolean>;
|
|
61
161
|
private _fileInputHandler;
|
|
62
162
|
googleMap?: google.maps.Map;
|
|
63
163
|
readonly mapReady$: Observable<boolean>;
|
|
@@ -77,26 +177,192 @@ declare class GoogleMapsService implements OnDestroy {
|
|
|
77
177
|
* Iterates the map's features and removes any that are selected.
|
|
78
178
|
*/
|
|
79
179
|
deleteSelection(): void;
|
|
80
|
-
/**
|
|
180
|
+
/**
|
|
181
|
+
* Delete every feature in `key`'s group. Backs the grouped "Delete Field"
|
|
182
|
+
* context-menu item.
|
|
183
|
+
*
|
|
184
|
+
* `allowsContextMenu()` now requires the right-clicked feature's group to
|
|
185
|
+
* already be the selection, so `key` here is always the selected group's
|
|
186
|
+
* key by the time this runs — making this call equivalent in practice to
|
|
187
|
+
* `deleteSelection()`. Kept as its own method anyway: it names the thing
|
|
188
|
+
* the menu item actually acts on (the group the menu opened for) rather
|
|
189
|
+
* than relying on the coincidence that it currently matches whatever is
|
|
190
|
+
* selected, a coincidence this design has already stopped being true once
|
|
191
|
+
* before and could again.
|
|
192
|
+
*/
|
|
193
|
+
deleteGroup(key: string): void;
|
|
194
|
+
/**
|
|
195
|
+
* Whether polygon drawing mode is currently active.
|
|
196
|
+
*
|
|
197
|
+
* Reads `_drawingSubject` — set by `startDrawing()`/`stopDrawing()`, which
|
|
198
|
+
* this service fully controls — rather than asking Terra Draw's own
|
|
199
|
+
* `getMode()`. The two are kept in lockstep by every call to those two
|
|
200
|
+
* methods, so this is not a behaviour change; it exists so a stuck Terra
|
|
201
|
+
* Draw mode can never silently disagree with, and disarm, the rest of this
|
|
202
|
+
* service's drawing-state bookkeeping (F3's `geometryEditingArmed`
|
|
203
|
+
* included). `stopDrawing()` still always calls `_terraDraw.setMode('static')`
|
|
204
|
+
* to make Terra Draw itself release its cursor override and any
|
|
205
|
+
* in-progress geometry, regardless of which source `isDrawing()` reads.
|
|
206
|
+
*/
|
|
81
207
|
isDrawing(): boolean;
|
|
82
208
|
/** Enter polygon drawing mode. */
|
|
83
209
|
startDrawing(): void;
|
|
210
|
+
/**
|
|
211
|
+
* Clear any selection when entering drawing mode, but only in 'legacy'
|
|
212
|
+
* mode. There, a selected feature and the shape being drawn are unrelated,
|
|
213
|
+
* so leaving the old one visibly selected would read as one shape. In
|
|
214
|
+
* 'grouped' mode they are related: the selected group is exactly the
|
|
215
|
+
* target the drawn polygon will join, so it keeps its selected styling —
|
|
216
|
+
* `GroupedInteractionModel.featureFlags()` disarms its edit handles for
|
|
217
|
+
* the duration instead (F3), so they don't compete with Terra Draw for
|
|
218
|
+
* pointer events.
|
|
219
|
+
*
|
|
220
|
+
* Split out of `startDrawing()` so a recreate-in-flight (see
|
|
221
|
+
* `_pendingStartDrawing`) can defer this until the new instance's `ready`
|
|
222
|
+
* event actually fires, instead of entering drawing mode against an
|
|
223
|
+
* instance that isn't ready yet.
|
|
224
|
+
*/
|
|
225
|
+
private _enterDrawingMode;
|
|
84
226
|
/**
|
|
85
227
|
* Cancel any in-progress drawing and leave drawing mode. Switching to the
|
|
86
228
|
* `static` mode clears an unfinished line.
|
|
87
229
|
*/
|
|
88
230
|
stopDrawing(): void;
|
|
231
|
+
/**
|
|
232
|
+
* Whether the active Terra Draw mode has actually placed a coordinate.
|
|
233
|
+
* Terra Draw's own mode state machine transitions from `'started'`
|
|
234
|
+
* (armed, no coordinate committed yet) to `'drawing'` the instant the
|
|
235
|
+
* first vertex lands (`TerraDrawPolyLineMode`'s internal `setDrawing()`,
|
|
236
|
+
* confirmed against `terra-draw`'s own source). Read directly from Terra
|
|
237
|
+
* Draw's state rather than inferred from this service's own flags, so it
|
|
238
|
+
* reflects whether the adapter's pointer capture was actually
|
|
239
|
+
* engaged — the thing terra-draw#710 is actually about — rather than
|
|
240
|
+
* merely whether this service thought a draw was armed.
|
|
241
|
+
*/
|
|
242
|
+
private _hasPlacedVertex;
|
|
243
|
+
/**
|
|
244
|
+
* Stop (if running) and drop the current `TerraDraw` instance. `TerraDraw#
|
|
245
|
+
* stop()` deregisters its adapter, which for the Google Maps adapter's
|
|
246
|
+
* `isolatedData: true` mode calls `this._data.setMap(null)` and drops the
|
|
247
|
+
* reference — so this never leaves an orphaned or duplicate Data layer
|
|
248
|
+
* behind, across any number of calls.
|
|
249
|
+
*/
|
|
250
|
+
private _disposeTerraDraw;
|
|
251
|
+
/**
|
|
252
|
+
* Dispose the current `TerraDraw` instance (and its adapter) and build a
|
|
253
|
+
* fresh one in its place. This is the terra-draw#710 recovery: the old
|
|
254
|
+
* instance's listeners die with it (its adapter is deregistered by
|
|
255
|
+
* `stop()`, so nothing further reaches it), and `_createTerraDraw()` wires
|
|
256
|
+
* up brand new `ready`/`finish` listeners on the new instance — never the
|
|
257
|
+
* stale closures from a previous instance.
|
|
258
|
+
*/
|
|
259
|
+
private _recreateTerraDraw;
|
|
89
260
|
private _initTerraDraw;
|
|
261
|
+
/**
|
|
262
|
+
* Build and start a new `TerraDraw` instance with its Google Maps adapter,
|
|
263
|
+
* wiring up this instance's own `ready`/`finish` listeners. Called both by
|
|
264
|
+
* `_initTerraDraw()` (first-ever init, guarded against running twice) and
|
|
265
|
+
* by `_recreateTerraDraw()` (every terra-draw#710 recovery afterward) —
|
|
266
|
+
* the guard against accidental double-init only lives in `_initTerraDraw()`
|
|
267
|
+
* itself, so recreation is free to call this as many times as needed.
|
|
268
|
+
*/
|
|
269
|
+
private _createTerraDraw;
|
|
90
270
|
addControl(element: HTMLElement, position: google.maps.ControlPosition): void;
|
|
91
271
|
setData(data: any): Promise<void>;
|
|
92
272
|
reCenterOnFeatures(): void;
|
|
93
273
|
allowDrawingHoleInPolygon(allow: boolean): void;
|
|
94
274
|
setFileInputHandler(handler: ((file: File) => void) | undefined | null): void;
|
|
95
275
|
getFileInputHandler(): ((file: File) => void) | undefined | null;
|
|
276
|
+
setInteractionMode(mode: TheSeamMapInteractionMode): void;
|
|
277
|
+
setGroupOptions(options: FeatureGroupRegistryOptions): void;
|
|
278
|
+
/**
|
|
279
|
+
* Name of the GeoJSON property to render as a per-group label. `undefined`
|
|
280
|
+
* removes labels entirely (and, until now set, means the overlay is never
|
|
281
|
+
* even constructed).
|
|
282
|
+
*/
|
|
283
|
+
setLabelProperty(property: string | undefined): void;
|
|
284
|
+
private _ensureLabelsOverlay;
|
|
285
|
+
private _buildLabels;
|
|
286
|
+
private _warnAboutLabelDisagreement;
|
|
287
|
+
isEditMode(): boolean;
|
|
288
|
+
setEditMode(enabled: boolean): void;
|
|
289
|
+
/**
|
|
290
|
+
* Re-run the style callback for every feature.
|
|
291
|
+
*
|
|
292
|
+
* Google re-evaluates the callback whenever the style is set, so handing
|
|
293
|
+
* back the same stored function is enough to repaint after a mode change.
|
|
294
|
+
*/
|
|
295
|
+
private _refreshStyles;
|
|
296
|
+
private get _registry();
|
|
297
|
+
private _interactionContext;
|
|
298
|
+
/**
|
|
299
|
+
* Find an existing feature that fully contains `polygon`, restricted to a
|
|
300
|
+
* group when `groupKey` is given and to features `accept` returns true for
|
|
301
|
+
* when given. Matches any part of a MultiPolygon.
|
|
302
|
+
*
|
|
303
|
+
* `accept` is applied DURING iteration — a rejected candidate is skipped,
|
|
304
|
+
* not treated as ending the search — so legacy mode's Polygon-only filter
|
|
305
|
+
* can let a later Polygon candidate still match.
|
|
306
|
+
*/
|
|
307
|
+
private _findContainingFeature;
|
|
308
|
+
private _applySelection;
|
|
309
|
+
/**
|
|
310
|
+
* Pair a group with the emitted GeoJSON for one of its `Data.Feature`s.
|
|
311
|
+
*
|
|
312
|
+
* Indexes into the returned source array rather than `featuresIn()`, because
|
|
313
|
+
* a feature with unsupported geometry is dropped from the emitted list and
|
|
314
|
+
* would shift every index after it.
|
|
315
|
+
*/
|
|
316
|
+
private _targetFor;
|
|
317
|
+
selectGroup(key: string | null): boolean;
|
|
318
|
+
clearSelection(): void;
|
|
319
|
+
getGroups(): TheSeamMapFeatureGroup[];
|
|
320
|
+
private _boundsForGroup;
|
|
321
|
+
fitGroup(key: string, padding?: number | google.maps.Padding): boolean;
|
|
322
|
+
panToGroup(key: string): boolean;
|
|
323
|
+
/**
|
|
324
|
+
* Delete only the polygon the last interaction landed on.
|
|
325
|
+
*
|
|
326
|
+
* Re-applies the selection's group key afterward (with no focused feature)
|
|
327
|
+
* rather than leaving `selection$` holding a `TheSeamMapGroupTarget` whose
|
|
328
|
+
* `feature` no longer exists — `_applySelection` naturally clears to null
|
|
329
|
+
* when the group is now empty, via `groupWithSources`.
|
|
330
|
+
*/
|
|
331
|
+
deleteFocusedFeature(): void;
|
|
332
|
+
/** Escape cascades: cancel a draw, then clear selection, then leave edit mode. */
|
|
333
|
+
handleEscape(): void;
|
|
334
|
+
/**
|
|
335
|
+
* `startDrawing()` raw-deselects every feature (via `setFeatureSelected`)
|
|
336
|
+
* without touching `_selectionSubject`, so that `onDrawFinished` can still
|
|
337
|
+
* read `getSelectedKey()` for the group being drawn into. When a draw ends
|
|
338
|
+
* WITHOUT producing a finished feature — cancelled by `Escape` or
|
|
339
|
+
* `setEditMode(false)`, or a `finish` event with no valid geometry — nothing
|
|
340
|
+
* else re-applies those raw flags, so the map renders the selection as gone
|
|
341
|
+
* while `selection$`/`getSelectedKey()` still report it. Call this at every
|
|
342
|
+
* such stopping point to bring the two back in sync.
|
|
343
|
+
*
|
|
344
|
+
* Not called from the successful-finish path in `_onDrawFinished`: that
|
|
345
|
+
* path calls `_applySelection` itself with the new/joined selection, and
|
|
346
|
+
* doing it here first would only add a redundant, momentarily-stale
|
|
347
|
+
* `selectionChange` emission ahead of the real one.
|
|
348
|
+
*/
|
|
349
|
+
private _reapplyCurrentSelection;
|
|
96
350
|
private _initFeatureStyling;
|
|
97
351
|
setFeatureHoveredStyleOverride(feature: google.maps.Data.Feature): void;
|
|
98
|
-
private _mergeStyleOptions;
|
|
99
352
|
private _initFeatureChangeListeners;
|
|
353
|
+
/**
|
|
354
|
+
* Establish `feature` as what the context menu is about to open for:
|
|
355
|
+
* `_focusedFeature` (what "Delete Polygon" acts on) and
|
|
356
|
+
* `contextMenuTarget$` (what "Delete Field" is gated on and acts on) both
|
|
357
|
+
* follow it. Shared by the `contextmenu` mouse listener and
|
|
358
|
+
* `openContextMenu()`'s keyboard path so the two establish the target
|
|
359
|
+
* identically — the menu's target must always be the feature the menu was
|
|
360
|
+
* opened for, on either path. Before this, only the mouse listener set
|
|
361
|
+
* `contextMenuTarget$`, so pressing the `ContextMenu` key could open a menu
|
|
362
|
+
* over whatever was CURRENTLY selected while still offering "Delete Field"
|
|
363
|
+
* for whatever a PRIOR right-click had targeted.
|
|
364
|
+
*/
|
|
365
|
+
private _setContextMenuTarget;
|
|
100
366
|
hasSelectedFeature(): boolean;
|
|
101
367
|
getSelectedFeature(): google.maps.Data.Feature | null;
|
|
102
368
|
openContextMenu(): void;
|
|
@@ -105,6 +371,21 @@ declare class GoogleMapsService implements OnDestroy {
|
|
|
105
371
|
getGeoJson(removeAppProperties?: boolean): Promise<object>;
|
|
106
372
|
/** Asserts that the map has been initialized. */
|
|
107
373
|
private _assertInitialized;
|
|
374
|
+
/**
|
|
375
|
+
* Arm `_suppressNextMapClick`, and guarantee it cannot linger forever if
|
|
376
|
+
* the echo it exists for never arrives — confirmed to be the normal case
|
|
377
|
+
* for most real closes, and true of every synthetic/`play()`-driven draw,
|
|
378
|
+
* per .superpowers/closing-click-report.md. A `setTimeout` would "solve"
|
|
379
|
+
* this by guessing a safe wall-clock delay, which is exactly the kind of
|
|
380
|
+
* timing window this fix is trying to avoid introducing. Two
|
|
381
|
+
* `requestAnimationFrame` callbacks bound the window instead: the observed
|
|
382
|
+
* echo arrives a few milliseconds after `stopDrawing()`, comfortably
|
|
383
|
+
* inside a single frame, so two frames is generous headroom without
|
|
384
|
+
* guessing at a duration — and two real user actions (even fast automated
|
|
385
|
+
* ones) are never going to land within two animation frames of each other,
|
|
386
|
+
* so an unrelated later click is never at risk of being swallowed.
|
|
387
|
+
*/
|
|
388
|
+
private _armMapClickSuppression;
|
|
108
389
|
private _onDrawFinished;
|
|
109
390
|
/**
|
|
110
391
|
* Normalize a finished Terra Draw geometry into a Polygon. A Polygon (the
|
|
@@ -113,12 +394,6 @@ declare class GoogleMapsService implements OnDestroy {
|
|
|
113
394
|
* they drew is discarded. Any other geometry type yields undefined.
|
|
114
395
|
*/
|
|
115
396
|
private _toDrawnPolygon;
|
|
116
|
-
/**
|
|
117
|
-
* Find an existing Polygon feature that fully contains the drawn polygon, so
|
|
118
|
-
* the drawing can be applied as a cutout. Returns the existing feature
|
|
119
|
-
* instance (never a copy).
|
|
120
|
-
*/
|
|
121
|
-
private _getPossibleExteriorFeature;
|
|
122
397
|
static ɵfac: i0.ɵɵFactoryDeclaration<GoogleMapsService, never>;
|
|
123
398
|
static ɵprov: i0.ɵɵInjectableDeclaration<GoogleMapsService>;
|
|
124
399
|
}
|
|
@@ -164,6 +439,7 @@ declare class TheSeamGoogleMapsComponent extends _TheSeamGoogleMapsMixinBase imp
|
|
|
164
439
|
static ngAcceptInputType_streetViewControlEnabled: BooleanInput;
|
|
165
440
|
static ngAcceptInputType_allowDrawingHoleInPolygon: BooleanInput;
|
|
166
441
|
static ngAcceptInputType_editingEnabled: BooleanInput;
|
|
442
|
+
static ngAcceptInputType_selectedGroupKey: string | null;
|
|
167
443
|
private readonly _changeDetectorRef;
|
|
168
444
|
private readonly _ngUnsubscribe;
|
|
169
445
|
readonly _gmApiLoaded: Observable<boolean>;
|
|
@@ -198,7 +474,32 @@ declare class TheSeamGoogleMapsComponent extends _TheSeamGoogleMapsMixinBase imp
|
|
|
198
474
|
longitude: number;
|
|
199
475
|
latitude: number;
|
|
200
476
|
padding: number | google.maps.Padding | undefined;
|
|
477
|
+
/**
|
|
478
|
+
* Which interaction model the map uses.
|
|
479
|
+
*
|
|
480
|
+
* `'legacy'` is the single-boundary behaviour this component has always had
|
|
481
|
+
* and is the default, so existing consumers are unaffected. `'grouped'`
|
|
482
|
+
* separates selection from geometry editing; see the design doc.
|
|
483
|
+
*/
|
|
484
|
+
interactionMode: TheSeamMapInteractionMode;
|
|
485
|
+
/**
|
|
486
|
+
* Name of the GeoJSON property that groups features into one logical thing,
|
|
487
|
+
* such as a field. Features sharing a value select, style, and edit together.
|
|
488
|
+
* Unset means every feature is its own group.
|
|
489
|
+
*/
|
|
490
|
+
featureGroupProperty: string | undefined;
|
|
491
|
+
/** Name of the GeoJSON property holding a group's label text. */
|
|
492
|
+
featureLabelProperty: string | undefined;
|
|
493
|
+
/**
|
|
494
|
+
* Generates the key written to `featureGroupProperty` for a newly drawn
|
|
495
|
+
* group. Consumer-supplied so the format is one the app recognises.
|
|
496
|
+
*/
|
|
497
|
+
newGroupKeyFactory: (() => string) | undefined;
|
|
498
|
+
/** Preselect a group. Applied on map-ready and after each external value write. */
|
|
499
|
+
selectedGroupKey: string | null;
|
|
201
500
|
mapReady: EventEmitter<google.maps.Map | undefined>;
|
|
501
|
+
selectionChange: EventEmitter<TheSeamMapGroupTarget | null>;
|
|
502
|
+
featureHoverChange: EventEmitter<TheSeamMapGroupTarget | null>;
|
|
202
503
|
featureContextMenu: MenuComponent;
|
|
203
504
|
_options: {
|
|
204
505
|
mapTypeControl: boolean;
|
|
@@ -212,6 +513,7 @@ declare class TheSeamGoogleMapsComponent extends _TheSeamGoogleMapsMixinBase imp
|
|
|
212
513
|
ngOnInit(): void;
|
|
213
514
|
ngOnDestroy(): void;
|
|
214
515
|
ngOnChanges(changes: SimpleChanges): void;
|
|
516
|
+
private _applySelectedGroupKey;
|
|
215
517
|
writeValue(value: MapValue): void;
|
|
216
518
|
registerOnChange(fn: any): void;
|
|
217
519
|
registerOnTouched(fn: any): void;
|
|
@@ -224,8 +526,18 @@ declare class TheSeamGoogleMapsComponent extends _TheSeamGoogleMapsMixinBase imp
|
|
|
224
526
|
private _getHostElement;
|
|
225
527
|
_onMapReady(theMap: google.maps.Map): void;
|
|
226
528
|
_onClickDeleteFeature(): void;
|
|
529
|
+
/** Select a group by key. Returns false when no such group exists. */
|
|
530
|
+
selectGroup(key: string): boolean;
|
|
531
|
+
clearSelection(): void;
|
|
532
|
+
/** Fit the viewport to a group. Returns false when no such group exists. */
|
|
533
|
+
fitGroup(key: string, padding?: number | google.maps.Padding): boolean;
|
|
534
|
+
/** Pan to a group's centre. Returns false when no such group exists. */
|
|
535
|
+
panToGroup(key: string): boolean;
|
|
536
|
+
getGroups(): TheSeamMapFeatureGroup[];
|
|
537
|
+
setEditMode(enabled: boolean): void;
|
|
538
|
+
isEditMode(): boolean;
|
|
227
539
|
static ɵfac: i0.ɵɵFactoryDeclaration<TheSeamGoogleMapsComponent, never>;
|
|
228
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<TheSeamGoogleMapsComponent, "seam-google-maps", ["seamGoogleMaps"], { "disabled": { "alias": "disabled"; "required": false; }; "value": { "alias": "value"; "required": false; }; "tabIndex": { "alias": "tabIndex"; "required": false; }; "fileDropEnabled": { "alias": "fileDropEnabled"; "required": false; }; "fileUploadControlEnabled": { "alias": "fileUploadControlEnabled"; "required": false; }; "fullscreenControlEnabled": { "alias": "fullscreenControlEnabled"; "required": false; }; "reCenterControlEnabled": { "alias": "reCenterControlEnabled"; "required": false; }; "mapTypeControlEnabled": { "alias": "mapTypeControlEnabled"; "required": false; }; "streetViewControlEnabled": { "alias": "streetViewControlEnabled"; "required": false; }; "allowDrawingHoleInPolygon": { "alias": "allowDrawingHoleInPolygon"; "required": false; }; "editingEnabled": { "alias": "editingEnabled"; "required": false; }; "fileImportHandler": { "alias": "fileImportHandler"; "required": false; }; "zoom": { "alias": "zoom"; "required": false; }; "longitude": { "alias": "longitude"; "required": false; }; "latitude": { "alias": "latitude"; "required": false; }; "padding": { "alias": "padding"; "required": false; }; }, { "mapReady": "mapReady"; }, never, never, false, never>;
|
|
540
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<TheSeamGoogleMapsComponent, "seam-google-maps", ["seamGoogleMaps"], { "disabled": { "alias": "disabled"; "required": false; }; "value": { "alias": "value"; "required": false; }; "tabIndex": { "alias": "tabIndex"; "required": false; }; "fileDropEnabled": { "alias": "fileDropEnabled"; "required": false; }; "fileUploadControlEnabled": { "alias": "fileUploadControlEnabled"; "required": false; }; "fullscreenControlEnabled": { "alias": "fullscreenControlEnabled"; "required": false; }; "reCenterControlEnabled": { "alias": "reCenterControlEnabled"; "required": false; }; "mapTypeControlEnabled": { "alias": "mapTypeControlEnabled"; "required": false; }; "streetViewControlEnabled": { "alias": "streetViewControlEnabled"; "required": false; }; "allowDrawingHoleInPolygon": { "alias": "allowDrawingHoleInPolygon"; "required": false; }; "editingEnabled": { "alias": "editingEnabled"; "required": false; }; "fileImportHandler": { "alias": "fileImportHandler"; "required": false; }; "zoom": { "alias": "zoom"; "required": false; }; "longitude": { "alias": "longitude"; "required": false; }; "latitude": { "alias": "latitude"; "required": false; }; "padding": { "alias": "padding"; "required": false; }; "interactionMode": { "alias": "interactionMode"; "required": false; }; "featureGroupProperty": { "alias": "featureGroupProperty"; "required": false; }; "featureLabelProperty": { "alias": "featureLabelProperty"; "required": false; }; "newGroupKeyFactory": { "alias": "newGroupKeyFactory"; "required": false; }; "selectedGroupKey": { "alias": "selectedGroupKey"; "required": false; }; }, { "mapReady": "mapReady"; "selectionChange": "selectionChange"; "featureHoverChange": "featureHoverChange"; }, never, never, false, never>;
|
|
229
541
|
}
|
|
230
542
|
|
|
231
543
|
/**
|
|
@@ -542,7 +854,8 @@ declare class TheSeamGoogleMapsModule {
|
|
|
542
854
|
}
|
|
543
855
|
|
|
544
856
|
declare enum AppFeaturePropertyName {
|
|
545
|
-
IsSelected = "__app__isSelected"
|
|
857
|
+
IsSelected = "__app__isSelected",
|
|
858
|
+
GroupKey = "__app__groupKey"
|
|
546
859
|
}
|
|
547
860
|
declare function isAppFeatureProperty(propertyName: string): propertyName is AppFeaturePropertyName;
|
|
548
861
|
declare function isFeatureSelected(feature: google.maps.Data.Feature): boolean;
|
|
@@ -560,6 +873,34 @@ declare function dataPolygonFromGeoJson(polygon: Polygon): google.maps.Data.Poly
|
|
|
560
873
|
* Returns undefined for non-Polygon geometries. Does not mutate the feature.
|
|
561
874
|
*/
|
|
562
875
|
declare function geoJsonPolygonFromDataFeature(feature: google.maps.Data.Feature): Polygon | undefined;
|
|
876
|
+
/** Build a google.maps.Data.MultiPolygon from a GeoJSON MultiPolygon. */
|
|
877
|
+
declare function dataMultiPolygonFromGeoJson(multiPolygon: MultiPolygon): google.maps.Data.MultiPolygon;
|
|
878
|
+
/**
|
|
879
|
+
* Every Polygon part of a feature, as closed GeoJSON. A Polygon feature yields
|
|
880
|
+
* one; a MultiPolygon yields one per part; anything else yields none.
|
|
881
|
+
*
|
|
882
|
+
* Callers that need to match a drawn shape against existing geometry must use
|
|
883
|
+
* this rather than `geoJsonPolygonFromDataFeature`, which sees only Polygons
|
|
884
|
+
* and silently ignores MultiPolygon features.
|
|
885
|
+
*/
|
|
886
|
+
declare function polygonsFromDataFeature(feature: google.maps.Data.Feature): Polygon[];
|
|
887
|
+
/**
|
|
888
|
+
* Cut `hole` into whichever part of `feature`'s geometry fully contains it.
|
|
889
|
+
*
|
|
890
|
+
* Mutates the EXISTING feature (via `setGeometry`) rather than replacing it,
|
|
891
|
+
* so its identity and properties survive. A single-part Polygon feature stays
|
|
892
|
+
* a Polygon; a MultiPolygon feature stays a MultiPolygon with only the
|
|
893
|
+
* containing part modified. Returns whether a containing part was found —
|
|
894
|
+
* when none is, the feature is left untouched.
|
|
895
|
+
*/
|
|
896
|
+
declare function applyHoleToFeature(feature: google.maps.Data.Feature, hole: Polygon): boolean;
|
|
897
|
+
/**
|
|
898
|
+
* Read a feature as a GeoJSON Feature, geometry and properties together.
|
|
899
|
+
*
|
|
900
|
+
* `excludeProperty` filters properties out by name — used to keep internal
|
|
901
|
+
* `__app__` bookkeeping out of anything handed to a consumer.
|
|
902
|
+
*/
|
|
903
|
+
declare function geoJsonFeatureFromDataFeature(feature: google.maps.Data.Feature, excludeProperty?: (name: string) => boolean): Feature<Polygon | MultiPolygon> | undefined;
|
|
563
904
|
declare function getBoundsWithAllFeatures(data: google.maps.Data): google.maps.LatLngBounds;
|
|
564
905
|
declare function getFeatureBounds(feature: google.maps.Data.Feature): google.maps.LatLngBounds;
|
|
565
906
|
declare function getFeatureCenter(feature: google.maps.Data.Feature): google.maps.LatLng;
|
|
@@ -570,6 +911,78 @@ declare function getFeaturesCount(data: google.maps.Data): number;
|
|
|
570
911
|
*/
|
|
571
912
|
declare function createFeatureChangeObservable(data: google.maps.Data, ngZone: NgZone): Observable<void>;
|
|
572
913
|
|
|
914
|
+
declare const FEATURE_STYLE_OPTIONS_DEFAULT: () => google.maps.Data.StyleOptions;
|
|
915
|
+
declare const FEATURE_STYLE_OPTIONS_SELECTED: () => google.maps.Data.StyleOptions;
|
|
916
|
+
declare const FEATURE_STYLE_OVERRIDE_OPTIONS_HOVERED: () => google.maps.Data.StyleOptions;
|
|
917
|
+
/**
|
|
918
|
+
* Style options a feature may set through its GeoJSON properties.
|
|
919
|
+
*
|
|
920
|
+
* `editable`, `draggable`, and `clickable` are here so a feature can opt OUT —
|
|
921
|
+
* a retired field stays unreshapeable while its neighbours do not. They cannot
|
|
922
|
+
* opt in: the clamp in `computeFeatureStyle` runs last and only ever narrows.
|
|
923
|
+
*/
|
|
924
|
+
declare const SUPPORTED_PROPERTY_STYLE_OPTIONS: (keyof google.maps.Data.StyleOptions)[];
|
|
925
|
+
/** What the interaction model and the global gate permit for a feature. */
|
|
926
|
+
interface TheSeamMapFeatureStyleContext {
|
|
927
|
+
/** The `editingEnabled` input. */
|
|
928
|
+
editingEnabled: boolean;
|
|
929
|
+
/** Whether the model currently arms geometry editing at all. */
|
|
930
|
+
geometryEditingArmed: boolean;
|
|
931
|
+
/** Whether the model lets this feature receive clicks. */
|
|
932
|
+
clicksAllowed: boolean;
|
|
933
|
+
}
|
|
934
|
+
/** Merge only the supported subset of one options object into another. */
|
|
935
|
+
declare function mergeStyleOptions(target: google.maps.Data.StyleOptions, source: google.maps.Data.StyleOptions | undefined): google.maps.Data.StyleOptions;
|
|
936
|
+
/**
|
|
937
|
+
* The style for a feature, in a fixed precedence order:
|
|
938
|
+
*
|
|
939
|
+
* defaults
|
|
940
|
+
* -> properties.styleOptions
|
|
941
|
+
* -> selected defaults (when selected)
|
|
942
|
+
* -> properties.styleOptionsSelected (when selected)
|
|
943
|
+
* -> interaction clamp (always last)
|
|
944
|
+
*
|
|
945
|
+
* That chain governs the *visual* options (colours, etc.) only. The opt-out
|
|
946
|
+
* flags (`editable`/`draggable`/`clickable`) resolve separately, per key: a
|
|
947
|
+
* selected feature consults `styleOptionsSelected` first and falls back to
|
|
948
|
+
* `styleOptions` only for a key `styleOptionsSelected` doesn't itself
|
|
949
|
+
* mention — never a whole-object fallback. Otherwise a feature that declares
|
|
950
|
+
* `styleOptionsSelected` for an unrelated reason, such as a different
|
|
951
|
+
* selected fill colour, would silently lose an unrelated `editable: false`
|
|
952
|
+
* from `styleOptions` the moment it's selected.
|
|
953
|
+
*
|
|
954
|
+
* The clamp is one-directional. A feature can only narrow what the context
|
|
955
|
+
* permits, never widen it, so no property in an uploaded file can arm editing
|
|
956
|
+
* when the mode says no.
|
|
957
|
+
*
|
|
958
|
+
* A declared `editable: false` also implies `draggable: false`: moving a
|
|
959
|
+
* polygon changes the map's value exactly as much as reshaping it does, so a
|
|
960
|
+
* feature that opts out of one opts out of the other. This implication runs
|
|
961
|
+
* only one way — `draggable: false` alone does not imply `editable: false`, so
|
|
962
|
+
* a feature may still opt out of dragging while remaining reshapeable via its
|
|
963
|
+
* vertex handles. Resolved through the same per-key `wants()` lookup as
|
|
964
|
+
* `editable` itself, so a feature that locks `editable: false` in
|
|
965
|
+
* `styleOptions` cannot be dragged even if `styleOptionsSelected` declares
|
|
966
|
+
* `draggable` for an unrelated reason (or not at all).
|
|
967
|
+
*/
|
|
968
|
+
declare function computeFeatureStyle(feature: google.maps.Data.Feature, context: TheSeamMapFeatureStyleContext): google.maps.Data.StyleOptions;
|
|
969
|
+
/**
|
|
970
|
+
* The hover override, merged with anything the feature declares for hover.
|
|
971
|
+
*
|
|
972
|
+
* `mergeStyleOptions` is shared with `computeFeatureStyle`, and
|
|
973
|
+
* `SUPPORTED_PROPERTY_STYLE_OPTIONS` includes `editable`/`draggable` so a
|
|
974
|
+
* feature can opt OUT of them via `styleOptions`/`styleOptionsSelected`. But
|
|
975
|
+
* this override is applied via `overrideStyle`, with no clamp downstream at
|
|
976
|
+
* all — unlike `computeFeatureStyle`, where the interaction clamp runs last
|
|
977
|
+
* and narrows whatever came before it. So if a consumer's
|
|
978
|
+
* `styleOptionsHovered` declared `editable` or `draggable`, it would apply
|
|
979
|
+
* unclamped: the one path where the "clamp is one-directional and always
|
|
980
|
+
* runs last" guarantee would not hold. A hover override has no business
|
|
981
|
+
* touching either, so both are stripped unconditionally, regardless of what
|
|
982
|
+
* `styleOptionsHovered` declares.
|
|
983
|
+
*/
|
|
984
|
+
declare function computeFeatureHoverStyle(feature: google.maps.Data.Feature): google.maps.Data.StyleOptions;
|
|
985
|
+
|
|
573
986
|
declare class GoogleMapsContextMenu {
|
|
574
987
|
private readonly _map;
|
|
575
988
|
private readonly _menu;
|
|
@@ -665,5 +1078,5 @@ declare class TheSeamNoopGoogleMapsAPILoader extends TheSeamGoogleMapsApiLoader
|
|
|
665
1078
|
static ɵprov: i0.ɵɵInjectableDeclaration<TheSeamNoopGoogleMapsAPILoader>;
|
|
666
1079
|
}
|
|
667
1080
|
|
|
668
|
-
export { AppFeaturePropertyName, GoogleMapsContextMenu, GoogleMapsControlsService, GoogleMapsService, MAP_CONTROLS_SERVICE, MAP_CONTROL_DATA, MapControlRef, MapValueManagerService, MapValueSource, SEAM_GOOGLE_PLACES_AUTOCOMPLETE_DEFAULT_OPTIONS, THESEAM_LAZY_MAPS_API_CONFIG, TheSeamGoogleMapsApiLoader, TheSeamGoogleMapsComponent, TheSeamGoogleMapsModule, TheSeamGoogleMapsPlacesAutoCompleteComponent, TheSeamGoogleMapsPlacesAutocompleteDirective, TheSeamGoogleMapsRecenterButtonControlComponent, TheSeamGoogleMapsUploadButtonControlComponent, TheSeamLazyMapsApiLoader, TheSeamMapFileDropComponent, TheSeamMapsControlComponent, TheSeamNoopGoogleMapsAPILoader, createFeatureChangeObservable, dataPolygonFromGeoJson, geoJsonPolygonFromDataFeature, getBoundsWithAllFeatures, getFeatureBounds, getFeatureCenter, getFeaturesCount, getHoveredStyleOptionsDefinedByFeature, getSelectedStyleOptionsDefinedByFeature, getStyleOptionsDefinedByFeature, isAppFeatureProperty, isFeatureSelected, polygonCoordinates, polygonHasValidPathsLengths, removeAllFeatures, setFeatureSelected, stripAppFeaturePropertiesFromJson };
|
|
669
|
-
export type { GoogleMapsRecenterButtonControlData, GoogleMapsUploadButtonControlData, MapControl, MapControlsService, MapValue, MapValueChange, TheSeamLazyMapsApiLoaderConfig };
|
|
1081
|
+
export { AppFeaturePropertyName, FEATURE_STYLE_OPTIONS_DEFAULT, FEATURE_STYLE_OPTIONS_SELECTED, FEATURE_STYLE_OVERRIDE_OPTIONS_HOVERED, GoogleMapsContextMenu, GoogleMapsControlsService, GoogleMapsService, MAP_CONTROLS_SERVICE, MAP_CONTROL_DATA, MapControlRef, MapValueManagerService, MapValueSource, SEAM_GOOGLE_PLACES_AUTOCOMPLETE_DEFAULT_OPTIONS, SUPPORTED_PROPERTY_STYLE_OPTIONS, THESEAM_LAZY_MAPS_API_CONFIG, TheSeamGoogleMapsApiLoader, TheSeamGoogleMapsComponent, TheSeamGoogleMapsModule, TheSeamGoogleMapsPlacesAutoCompleteComponent, TheSeamGoogleMapsPlacesAutocompleteDirective, TheSeamGoogleMapsRecenterButtonControlComponent, TheSeamGoogleMapsUploadButtonControlComponent, TheSeamLazyMapsApiLoader, TheSeamMapFileDropComponent, TheSeamMapsControlComponent, TheSeamNoopGoogleMapsAPILoader, applyHoleToFeature, computeFeatureHoverStyle, computeFeatureStyle, createFeatureChangeObservable, dataMultiPolygonFromGeoJson, dataPolygonFromGeoJson, geoJsonFeatureFromDataFeature, geoJsonPolygonFromDataFeature, getBoundsWithAllFeatures, getFeatureBounds, getFeatureCenter, getFeaturesCount, getHoveredStyleOptionsDefinedByFeature, getSelectedStyleOptionsDefinedByFeature, getStyleOptionsDefinedByFeature, isAppFeatureProperty, isFeatureSelected, mergeStyleOptions, polygonCoordinates, polygonHasValidPathsLengths, polygonsFromDataFeature, removeAllFeatures, setFeatureSelected, stripAppFeaturePropertiesFromJson };
|
|
1082
|
+
export type { GoogleMapsRecenterButtonControlData, GoogleMapsUploadButtonControlData, MapControl, MapControlsService, MapValue, MapValueChange, TheSeamLazyMapsApiLoaderConfig, TheSeamMapFeatureGroup, TheSeamMapFeatureStyleContext, TheSeamMapGroupTarget, TheSeamMapInteractionMode };
|
package/package.json
CHANGED
package/story-helpers/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HandlerFunction } from 'storybook/actions';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { ModuleWithProviders, Injector, TemplateRef, InjectionToken, ComponentRef } from '@angular/core';
|
|
3
|
+
import { ModuleWithProviders, Injector, TemplateRef, InjectionToken, ComponentRef, Type } from '@angular/core';
|
|
4
4
|
import * as i2 from '@angular/common';
|
|
5
5
|
import * as i3 from '@angular/router';
|
|
6
6
|
import * as i4 from '@theseam/ui-common/scrollbar';
|
|
@@ -194,5 +194,7 @@ declare const INJECTOR_TO_ARGS_PROPERTY_NAME = "__getInjector";
|
|
|
194
194
|
declare const addInjectorGetterToArgs: (argName?: string) => DecoratorFunction<AngularRenderer>;
|
|
195
195
|
declare const getInjectorFromArgs: (args: any, argName?: string) => Injector;
|
|
196
196
|
|
|
197
|
-
|
|
197
|
+
declare function getComponentInstance<T>(element: Element | null | undefined, componentType?: Type<T>): T | null;
|
|
198
|
+
|
|
199
|
+
export { INJECTOR_TO_ARGS_PROPERTY_NAME, STORY_INITIAL_ROUTE_URL, StoryEmptyComponent, StoryEmptyWithRouteComponent, StoryHelperComponentsModule, StoryInitialRouteModule, StoryInitialRouteService, StoryModalContainerComponent, StoryPreferencesAccessorService, addInjectorGetterToArgs, argsToTpl, buttonTypeArgType, getComponentInstance, getInjectorFromArgs, onStoryBootstrappedTrigger, routeButton, routesArgType, sizeArgType, storyInitialRouteFactory, storyModalDecorator, themeArgType, themeWithOutlineArgType };
|
|
198
200
|
export type { ArgsTplOptions, ArgsTplParts, StoryModalDecoratorOptions };
|