@theseam/ui-common 2.0.1-beta.111 → 2.0.1-beta.115
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 +433 -65
- package/fesm2022/theseam-ui-common-google-maps.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-toggle-group.mjs +37 -9
- package/fesm2022/theseam-ui-common-toggle-group.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-utils.mjs +3 -3
- package/fesm2022/theseam-ui-common-utils.mjs.map +1 -1
- package/google-maps/index.d.ts +192 -11
- package/package.json +1 -1
- package/toggle-group/index.d.ts +18 -0
|
@@ -510,6 +510,27 @@ function mergeStyleOptions(target, source) {
|
|
|
510
510
|
}
|
|
511
511
|
return target;
|
|
512
512
|
}
|
|
513
|
+
/**
|
|
514
|
+
* Whether the feature declines to opt OUT of `option`.
|
|
515
|
+
*
|
|
516
|
+
* Resolved per key, not per object: a selected feature consults
|
|
517
|
+
* `styleOptionsSelected` first and falls back to `styleOptions` only for a key
|
|
518
|
+
* `styleOptionsSelected` does not itself mention. Otherwise a feature
|
|
519
|
+
* declaring `styleOptionsSelected` for an unrelated reason, such as a
|
|
520
|
+
* different selected fill colour, would silently lose an unrelated
|
|
521
|
+
* `editable: false` from `styleOptions` the moment it is selected.
|
|
522
|
+
*
|
|
523
|
+
* Only an explicit `false` opts out. Shared by `computeFeatureStyle` and the
|
|
524
|
+
* delete gate in `GoogleMapsService`, so the lock a consumer declares means
|
|
525
|
+
* the same thing to both.
|
|
526
|
+
*/
|
|
527
|
+
function featureAllows(feature, option) {
|
|
528
|
+
const selectedDeclared = isFeatureSelected(feature)
|
|
529
|
+
? getSelectedStyleOptionsDefinedByFeature(feature)
|
|
530
|
+
: undefined;
|
|
531
|
+
const baseDeclared = getStyleOptionsDefinedByFeature(feature);
|
|
532
|
+
return (selectedDeclared?.[option] ?? baseDeclared?.[option]) !== false;
|
|
533
|
+
}
|
|
513
534
|
/**
|
|
514
535
|
* The style for a feature, in a fixed precedence order:
|
|
515
536
|
*
|
|
@@ -537,37 +558,29 @@ function mergeStyleOptions(target, source) {
|
|
|
537
558
|
* feature that opts out of one opts out of the other. This implication runs
|
|
538
559
|
* only one way — `draggable: false` alone does not imply `editable: false`, so
|
|
539
560
|
* a feature may still opt out of dragging while remaining reshapeable via its
|
|
540
|
-
* vertex handles. Resolved through
|
|
541
|
-
*
|
|
542
|
-
*
|
|
543
|
-
*
|
|
561
|
+
* vertex handles. Resolved through `featureAllows`, which the delete gate
|
|
562
|
+
* shares, so a feature that locks `editable: false` in `styleOptions` cannot
|
|
563
|
+
* be dragged even if `styleOptionsSelected` declares `draggable` for an
|
|
564
|
+
* unrelated reason (or not at all).
|
|
544
565
|
*/
|
|
545
566
|
function computeFeatureStyle(feature, context) {
|
|
546
567
|
const selected = isFeatureSelected(feature);
|
|
547
|
-
const baseDeclared = getStyleOptionsDefinedByFeature(feature);
|
|
548
|
-
const selectedDeclared = selected
|
|
549
|
-
? getSelectedStyleOptionsDefinedByFeature(feature)
|
|
550
|
-
: undefined;
|
|
551
568
|
const options = FEATURE_STYLE_OPTIONS_DEFAULT();
|
|
552
569
|
mergeStyleOptions(options, getStyleOptionsDefinedByFeature(feature));
|
|
553
570
|
if (selected) {
|
|
554
571
|
mergeStyleOptions(options, FEATURE_STYLE_OPTIONS_SELECTED());
|
|
555
572
|
mergeStyleOptions(options, getSelectedStyleOptionsDefinedByFeature(feature));
|
|
556
573
|
}
|
|
557
|
-
// Resolved per key, not per object. A feature that opts out in
|
|
558
|
-
// `styleOptions` must stay opted out even when it also declares a
|
|
559
|
-
// `styleOptionsSelected` for unrelated reasons such as colour — otherwise
|
|
560
|
-
// the clamp stops being one-directional and a retired field becomes
|
|
561
|
-
// editable the moment it is selected.
|
|
562
|
-
const wants = (option) => (selectedDeclared?.[option] ?? baseDeclared?.[option]) !== false;
|
|
563
574
|
const editingArmed = context.editingEnabled && context.geometryEditingArmed && selected;
|
|
564
|
-
const wantsEditable =
|
|
575
|
+
const wantsEditable = featureAllows(feature, 'editable');
|
|
565
576
|
options.editable = wantsEditable && editingArmed;
|
|
566
577
|
// editable: false implies draggable: false (see the doc comment above) —
|
|
567
|
-
// the reverse does not hold, so
|
|
568
|
-
//
|
|
569
|
-
options.draggable =
|
|
570
|
-
|
|
578
|
+
// the reverse does not hold, so 'draggable' is still resolved on its own
|
|
579
|
+
// for a feature that opts out of dragging alone.
|
|
580
|
+
options.draggable =
|
|
581
|
+
featureAllows(feature, 'draggable') && wantsEditable && editingArmed;
|
|
582
|
+
options.clickable =
|
|
583
|
+
featureAllows(feature, 'clickable') && context.clicksAllowed;
|
|
571
584
|
return options;
|
|
572
585
|
}
|
|
573
586
|
/**
|
|
@@ -1091,7 +1104,7 @@ class GoogleMapsService {
|
|
|
1091
1104
|
* delay-free discriminator: a stale click's timestamp should predate the
|
|
1092
1105
|
* moment the draw finished. It does not hold up empirically. Driving real
|
|
1093
1106
|
* mouse draws against the live Storybook and logging both the map `click`
|
|
1094
|
-
* listener and `stopDrawing()` (see
|
|
1107
|
+
* listener and `stopDrawing()` (see docs/superpowers/reports/closing-click-report.md)
|
|
1095
1108
|
* caught the echo repeatedly, and every single time its `domEvent.timeStamp`
|
|
1096
1109
|
* was a few milliseconds AFTER `stopDrawing()`'s own timestamp, not before
|
|
1097
1110
|
* — indistinguishable from a genuinely fresh click by timestamp alone.
|
|
@@ -1117,6 +1130,14 @@ class GoogleMapsService {
|
|
|
1117
1130
|
_labelProperty;
|
|
1118
1131
|
_labelsOverlay;
|
|
1119
1132
|
_warnedAboutLabelDisagreement = false;
|
|
1133
|
+
_canDelete;
|
|
1134
|
+
_deleteBlockedSubject = new Subject();
|
|
1135
|
+
/**
|
|
1136
|
+
* A delete that was attempted and refused. Fires only from the three delete
|
|
1137
|
+
* commands, never from their `canDelete*` queries — rendering a menu is not
|
|
1138
|
+
* an attempt.
|
|
1139
|
+
*/
|
|
1140
|
+
deleteBlocked$ = this._deleteBlockedSubject.asObservable();
|
|
1120
1141
|
_selectionSubject = new BehaviorSubject(null);
|
|
1121
1142
|
/**
|
|
1122
1143
|
* `distinctUntilChanged` collapses repeated `null`s. Several paths clear the
|
|
@@ -1171,6 +1192,7 @@ class GoogleMapsService {
|
|
|
1171
1192
|
this._contextMenuTargetSubject.complete();
|
|
1172
1193
|
this._editModeSubject.complete();
|
|
1173
1194
|
this._interactionModeSubject.complete();
|
|
1195
|
+
this._deleteBlockedSubject.complete();
|
|
1174
1196
|
this._groups = undefined;
|
|
1175
1197
|
this._labelsOverlay?.destroy();
|
|
1176
1198
|
this._labelsOverlay = undefined;
|
|
@@ -1226,7 +1248,7 @@ class GoogleMapsService {
|
|
|
1226
1248
|
/**
|
|
1227
1249
|
* Iterates the map's features and removes any that are selected.
|
|
1228
1250
|
*/
|
|
1229
|
-
|
|
1251
|
+
_removeSelection() {
|
|
1230
1252
|
this._assertInitialized();
|
|
1231
1253
|
const mapData = this.googleMap.data;
|
|
1232
1254
|
mapData.forEach((f) => {
|
|
@@ -1236,13 +1258,201 @@ class GoogleMapsService {
|
|
|
1236
1258
|
});
|
|
1237
1259
|
// Every deleted feature was selected, so nothing should still read as
|
|
1238
1260
|
// selected afterward. Re-sync `selection$` and `_focusedFeature` the same
|
|
1239
|
-
// way `
|
|
1261
|
+
// way `_removeFocusedFeature()` and `setData()` already do, rather than
|
|
1240
1262
|
// leaving them pointing at a group that no longer exists. In 'legacy'
|
|
1241
1263
|
// mode nothing consumes selection$ today, and no remaining feature's raw
|
|
1242
1264
|
// selected flag changes here (they were already false), so this is inert
|
|
1243
1265
|
// there.
|
|
1244
1266
|
this.clearSelection();
|
|
1245
1267
|
}
|
|
1268
|
+
/**
|
|
1269
|
+
* The consumer's veto. Consulted for every delete on every path, and for
|
|
1270
|
+
* whether to offer a delete at all.
|
|
1271
|
+
*/
|
|
1272
|
+
setCanDelete(predicate) {
|
|
1273
|
+
this._canDelete = predicate ?? undefined;
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Pair a set of features to remove with the target to consult `canDelete`
|
|
1277
|
+
* with. `null` when there is nothing to delete — distinct from a refusal,
|
|
1278
|
+
* and the reason no honest target exists to report.
|
|
1279
|
+
*
|
|
1280
|
+
* Builds `target.feature` itself rather than delegating the decision to
|
|
1281
|
+
* `_targetFor`. There, `feature: null` means only "no particular feature";
|
|
1282
|
+
* here it is a promise that the whole group is about to cease to exist.
|
|
1283
|
+
* Conflating the two is what let a delete of ONE polygon be consulted as a
|
|
1284
|
+
* group delete — see `ResolvedDeletion.unnamable`.
|
|
1285
|
+
*/
|
|
1286
|
+
_deletionOf(key, removing) {
|
|
1287
|
+
if (key === null || removing.length === 0) {
|
|
1288
|
+
return null;
|
|
1289
|
+
}
|
|
1290
|
+
const resolved = this._registry.groupWithSources(key);
|
|
1291
|
+
if (!resolved) {
|
|
1292
|
+
return null;
|
|
1293
|
+
}
|
|
1294
|
+
// The empty-group invariant: a delete that leaves the group with no
|
|
1295
|
+
// features at all is a group delete, whichever path asked for it — so
|
|
1296
|
+
// `feature: null` always means "this group is about to cease to exist"
|
|
1297
|
+
// and a consumer never has to count features itself. Compared against
|
|
1298
|
+
// `featuresIn`, not `resolved.sources`, because a feature with
|
|
1299
|
+
// unsupported geometry is missing from `sources` but still occupies the
|
|
1300
|
+
// group.
|
|
1301
|
+
const all = this._registry.featuresIn(key);
|
|
1302
|
+
const emptiesGroup = all.every((f) => removing.indexOf(f) !== -1);
|
|
1303
|
+
if (emptiesGroup) {
|
|
1304
|
+
// `feature: null` is honest here whatever `removing` holds: the group
|
|
1305
|
+
// really is going, unsupported-geometry members included. This case is
|
|
1306
|
+
// never at risk, so it is never `unnamable`.
|
|
1307
|
+
return {
|
|
1308
|
+
removing,
|
|
1309
|
+
target: { group: resolved.group, feature: null },
|
|
1310
|
+
unnamable: false,
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
// The group survives, so the target must name the polygon being removed.
|
|
1314
|
+
const index = resolved.sources.indexOf(removing[0]);
|
|
1315
|
+
if (index === -1) {
|
|
1316
|
+
// The feature being removed is absent from `sources`: its geometry is
|
|
1317
|
+
// neither Polygon nor MultiPolygon, so `groupWithSources` dropped it and
|
|
1318
|
+
// it has no emitted GeoJSON counterpart. It has never appeared in any
|
|
1319
|
+
// `group.features` the consumer has seen, so NO value of
|
|
1320
|
+
// `target.feature` describes it — which is exactly why this deletion is
|
|
1321
|
+
// `unnamable` and `_mayDeleteResolved()` refuses it outright whenever a
|
|
1322
|
+
// predicate is set.
|
|
1323
|
+
//
|
|
1324
|
+
// `feature: null` is the least dishonest option available. It overstates
|
|
1325
|
+
// the scope — it reads as "the field is going" when only one polygon
|
|
1326
|
+
// was asked for — but it names the right group, and it is only ever READ
|
|
1327
|
+
// on the `deleteBlocked$` emission that reports the refusal: the
|
|
1328
|
+
// predicate is refused before it is ever consulted with this target, and
|
|
1329
|
+
// with no predicate set nothing observes it at all. A target that only
|
|
1330
|
+
// ever reports a refusal cannot authorise a removal, so the overstatement
|
|
1331
|
+
// is inert. The alternative was emitting nothing, which would leave a
|
|
1332
|
+
// refused delete indistinguishable from a successful one on the design's
|
|
1333
|
+
// only feedback channel.
|
|
1334
|
+
return {
|
|
1335
|
+
removing,
|
|
1336
|
+
target: { group: resolved.group, feature: null },
|
|
1337
|
+
unnamable: true,
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
return {
|
|
1341
|
+
removing,
|
|
1342
|
+
target: {
|
|
1343
|
+
group: resolved.group,
|
|
1344
|
+
feature: resolved.group.features[index],
|
|
1345
|
+
},
|
|
1346
|
+
unnamable: false,
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
/** Resolves the same features `_removeSelection()` would remove. */
|
|
1350
|
+
_selectionDeletion() {
|
|
1351
|
+
this._assertInitialized();
|
|
1352
|
+
const removing = [];
|
|
1353
|
+
this.googleMap.data.forEach((f) => {
|
|
1354
|
+
if (isFeatureSelected(f)) {
|
|
1355
|
+
removing.push(f);
|
|
1356
|
+
}
|
|
1357
|
+
});
|
|
1358
|
+
if (removing.length === 0) {
|
|
1359
|
+
return null;
|
|
1360
|
+
}
|
|
1361
|
+
return this._deletionOf(this._registry.keyOf(removing[0]), removing);
|
|
1362
|
+
}
|
|
1363
|
+
/** Resolves the same features `_removeFocusedFeature()` would remove. */
|
|
1364
|
+
_focusedFeatureDeletion() {
|
|
1365
|
+
const focused = this._focusedFeature;
|
|
1366
|
+
if (focused === null) {
|
|
1367
|
+
return this._selectionDeletion();
|
|
1368
|
+
}
|
|
1369
|
+
return this._deletionOf(this._registry.keyOf(focused), [focused]);
|
|
1370
|
+
}
|
|
1371
|
+
/** Resolves the same features `_removeGroup(key)` would remove. */
|
|
1372
|
+
_groupDeletion(key) {
|
|
1373
|
+
return this._deletionOf(key, this._registry.featuresIn(key));
|
|
1374
|
+
}
|
|
1375
|
+
/**
|
|
1376
|
+
* Whether `removing` may be deleted: the feature-declared lock first, then
|
|
1377
|
+
* the consumer's predicate.
|
|
1378
|
+
*
|
|
1379
|
+
* MUST stay pure. It also answers menu-render questions, which are not
|
|
1380
|
+
* delete attempts — a side effect here would fire on every right-click.
|
|
1381
|
+
*/
|
|
1382
|
+
_mayDelete(removing, target) {
|
|
1383
|
+
// A feature the consumer locked against reshaping must not be removable
|
|
1384
|
+
// by another route: deleting a polygon changes the map's value at least
|
|
1385
|
+
// as much as reshaping it does. Same precedent as editable: false
|
|
1386
|
+
// implying draggable: false in compute-feature-style.ts.
|
|
1387
|
+
if (removing.some((f) => !featureAllows(f, 'editable'))) {
|
|
1388
|
+
return false;
|
|
1389
|
+
}
|
|
1390
|
+
return this._canDelete?.(target) ?? true;
|
|
1391
|
+
}
|
|
1392
|
+
/**
|
|
1393
|
+
* Whether a resolved deletion may proceed. The single gate every query and
|
|
1394
|
+
* every command goes through.
|
|
1395
|
+
*
|
|
1396
|
+
* Wraps `_mayDelete` with the one refusal that cannot be phrased as a
|
|
1397
|
+
* question about a target: an `unnamable` delete. Consulting the predicate
|
|
1398
|
+
* there would mean LYING to it — handing it `feature: null`, which promises
|
|
1399
|
+
* the whole group is about to cease to exist, for a delete that removes one
|
|
1400
|
+
* polygon and leaves the rest standing. A consumer whose rule is "a field
|
|
1401
|
+
* may be deleted, an individual polygon may not" answers `true` to that and
|
|
1402
|
+
* loses a polygon it meant to keep. Fail closed exactly where a consumer's
|
|
1403
|
+
* rule could be subverted.
|
|
1404
|
+
*
|
|
1405
|
+
* Only when a predicate is actually set. With none there is nobody to lie
|
|
1406
|
+
* to, so there is nothing to protect — and refusing unconditionally would
|
|
1407
|
+
* change 'legacy' mode's behaviour for unsupported-geometry features, which
|
|
1408
|
+
* two applications depend on and this work must not drift.
|
|
1409
|
+
*
|
|
1410
|
+
* Pure, like `_mayDelete`: it also answers menu-render questions.
|
|
1411
|
+
*/
|
|
1412
|
+
_mayDeleteResolved(deletion) {
|
|
1413
|
+
if (deletion.unnamable && this._canDelete !== undefined) {
|
|
1414
|
+
return false;
|
|
1415
|
+
}
|
|
1416
|
+
return this._mayDelete(deletion.removing, deletion.target);
|
|
1417
|
+
}
|
|
1418
|
+
/** Whether "Delete Polygon" (or the `Delete` key) may act. */
|
|
1419
|
+
canDeleteFocusedFeature() {
|
|
1420
|
+
if (!this.mapReady) {
|
|
1421
|
+
return false;
|
|
1422
|
+
}
|
|
1423
|
+
const deletion = this._focusedFeatureDeletion();
|
|
1424
|
+
return deletion !== null && this._mayDeleteResolved(deletion);
|
|
1425
|
+
}
|
|
1426
|
+
/** Whether "Delete Field" may act on `key`. */
|
|
1427
|
+
canDeleteGroup(key) {
|
|
1428
|
+
if (!this.mapReady) {
|
|
1429
|
+
return false;
|
|
1430
|
+
}
|
|
1431
|
+
const deletion = this._groupDeletion(key);
|
|
1432
|
+
return deletion !== null && this._mayDeleteResolved(deletion);
|
|
1433
|
+
}
|
|
1434
|
+
/** Whether the legacy "Delete" item (or the `Delete` key) may act. */
|
|
1435
|
+
canDeleteSelection() {
|
|
1436
|
+
if (!this.mapReady) {
|
|
1437
|
+
return false;
|
|
1438
|
+
}
|
|
1439
|
+
const deletion = this._selectionDeletion();
|
|
1440
|
+
return deletion !== null && this._mayDeleteResolved(deletion);
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Remove every selected feature, unless `canDelete` or a feature's own
|
|
1444
|
+
* `editable: false` refuses. A refused delete emits on `deleteBlocked$` and
|
|
1445
|
+
* changes nothing.
|
|
1446
|
+
*/
|
|
1447
|
+
deleteSelection() {
|
|
1448
|
+
this._assertInitialized();
|
|
1449
|
+
const deletion = this._selectionDeletion();
|
|
1450
|
+
if (deletion !== null && !this._mayDeleteResolved(deletion)) {
|
|
1451
|
+
this._deleteBlockedSubject.next(deletion.target);
|
|
1452
|
+
return;
|
|
1453
|
+
}
|
|
1454
|
+
this._removeSelection();
|
|
1455
|
+
}
|
|
1246
1456
|
/**
|
|
1247
1457
|
* Delete every feature in `key`'s group. Backs the grouped "Delete Field"
|
|
1248
1458
|
* context-menu item.
|
|
@@ -1256,7 +1466,7 @@ class GoogleMapsService {
|
|
|
1256
1466
|
* selected, a coincidence this design has already stopped being true once
|
|
1257
1467
|
* before and could again.
|
|
1258
1468
|
*/
|
|
1259
|
-
|
|
1469
|
+
_removeGroup(key) {
|
|
1260
1470
|
this._assertInitialized();
|
|
1261
1471
|
const mapData = this.googleMap.data;
|
|
1262
1472
|
const wasSelected = this._selectionSubject.value?.group.key === key;
|
|
@@ -1278,6 +1488,20 @@ class GoogleMapsService {
|
|
|
1278
1488
|
this._contextMenuTargetSubject.next(null);
|
|
1279
1489
|
}
|
|
1280
1490
|
}
|
|
1491
|
+
/**
|
|
1492
|
+
* Remove every feature in `key`'s group, unless `canDelete` or a feature's
|
|
1493
|
+
* own `editable: false` refuses. A refused delete emits on `deleteBlocked$`
|
|
1494
|
+
* and changes nothing.
|
|
1495
|
+
*/
|
|
1496
|
+
deleteGroup(key) {
|
|
1497
|
+
this._assertInitialized();
|
|
1498
|
+
const deletion = this._groupDeletion(key);
|
|
1499
|
+
if (deletion !== null && !this._mayDeleteResolved(deletion)) {
|
|
1500
|
+
this._deleteBlockedSubject.next(deletion.target);
|
|
1501
|
+
return;
|
|
1502
|
+
}
|
|
1503
|
+
this._removeGroup(key);
|
|
1504
|
+
}
|
|
1281
1505
|
/**
|
|
1282
1506
|
* Whether polygon drawing mode is currently active.
|
|
1283
1507
|
*
|
|
@@ -1635,6 +1859,51 @@ class GoogleMapsService {
|
|
|
1635
1859
|
this._ensureLabelsOverlay();
|
|
1636
1860
|
this._labelsOverlay?.refresh();
|
|
1637
1861
|
}
|
|
1862
|
+
/**
|
|
1863
|
+
* Write `label` into `featureLabelProperty` on every feature in `key`'s
|
|
1864
|
+
* group, without re-adding any data. Returns `false` when no feature
|
|
1865
|
+
* carries `key`, or when no `featureLabelProperty` is configured — there is
|
|
1866
|
+
* then no property to write the label into.
|
|
1867
|
+
*
|
|
1868
|
+
* Unlike a write through the map's `value`, this does NOT go through
|
|
1869
|
+
* `setData()`, so the selection and the viewport are untouched. The
|
|
1870
|
+
* repaint and the value update fall out of the data layer's own
|
|
1871
|
+
* `setproperty` event, which `_initFeatureChangeListeners()` already turns
|
|
1872
|
+
* into a `_labelsOverlay.refresh()` and a `MapValueSource.FeatureChange`
|
|
1873
|
+
* emission.
|
|
1874
|
+
*
|
|
1875
|
+
* It DOES emit a value change, because the label is part of the GeoJSON and
|
|
1876
|
+
* the value genuinely changed. Writing that emitted value straight back
|
|
1877
|
+
* through the `value` input is inert: `MapValueManagerService.setValue`
|
|
1878
|
+
* finds the serialized form identical and returns without emitting.
|
|
1879
|
+
*/
|
|
1880
|
+
setGroupLabel(key, label) {
|
|
1881
|
+
this._assertInitialized();
|
|
1882
|
+
const property = this._labelProperty;
|
|
1883
|
+
if (!property) {
|
|
1884
|
+
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
|
1885
|
+
console.warn(`[seam-google-maps] setGroupLabel("${key}") was called with no ` +
|
|
1886
|
+
`featureLabelProperty configured, so there is no property to ` +
|
|
1887
|
+
`write the label into. Nothing changed.`);
|
|
1888
|
+
}
|
|
1889
|
+
return false;
|
|
1890
|
+
}
|
|
1891
|
+
const features = this._registry.featuresIn(key);
|
|
1892
|
+
if (features.length === 0) {
|
|
1893
|
+
return false;
|
|
1894
|
+
}
|
|
1895
|
+
for (const feature of features) {
|
|
1896
|
+
// `setProperty` raises `setproperty` whether or not the value differs,
|
|
1897
|
+
// and each one costs a full re-serialization of the map's value. A
|
|
1898
|
+
// rename typed character by character would pay that per keystroke per
|
|
1899
|
+
// feature for writes that change nothing.
|
|
1900
|
+
if (feature.getProperty(property) === label) {
|
|
1901
|
+
continue;
|
|
1902
|
+
}
|
|
1903
|
+
feature.setProperty(property, label);
|
|
1904
|
+
}
|
|
1905
|
+
return true;
|
|
1906
|
+
}
|
|
1638
1907
|
_ensureLabelsOverlay() {
|
|
1639
1908
|
this._assertInitialized();
|
|
1640
1909
|
if (this._labelsOverlay) {
|
|
@@ -1886,7 +2155,7 @@ class GoogleMapsService {
|
|
|
1886
2155
|
* `feature` no longer exists — `_applySelection` naturally clears to null
|
|
1887
2156
|
* when the group is now empty, via `groupWithSources`.
|
|
1888
2157
|
*/
|
|
1889
|
-
|
|
2158
|
+
_removeFocusedFeature() {
|
|
1890
2159
|
this._assertInitialized();
|
|
1891
2160
|
const key = this._focusedFeature
|
|
1892
2161
|
? this._registry.keyOf(this._focusedFeature)
|
|
@@ -1897,7 +2166,7 @@ class GoogleMapsService {
|
|
|
1897
2166
|
this._focusedFeature = null;
|
|
1898
2167
|
}
|
|
1899
2168
|
else {
|
|
1900
|
-
this.
|
|
2169
|
+
this._removeSelection();
|
|
1901
2170
|
}
|
|
1902
2171
|
this._applySelection(key, null);
|
|
1903
2172
|
// Same reasoning as deleteGroup(): a context-menu target naming this
|
|
@@ -1907,6 +2176,20 @@ class GoogleMapsService {
|
|
|
1907
2176
|
this._contextMenuTargetSubject.next(null);
|
|
1908
2177
|
}
|
|
1909
2178
|
}
|
|
2179
|
+
/**
|
|
2180
|
+
* Remove the focused polygon — or, with nothing focused, the selection —
|
|
2181
|
+
* unless `canDelete` or a feature's own `editable: false` refuses. A
|
|
2182
|
+
* refused delete emits on `deleteBlocked$` and changes nothing.
|
|
2183
|
+
*/
|
|
2184
|
+
deleteFocusedFeature() {
|
|
2185
|
+
this._assertInitialized();
|
|
2186
|
+
const deletion = this._focusedFeatureDeletion();
|
|
2187
|
+
if (deletion !== null && !this._mayDeleteResolved(deletion)) {
|
|
2188
|
+
this._deleteBlockedSubject.next(deletion.target);
|
|
2189
|
+
return;
|
|
2190
|
+
}
|
|
2191
|
+
this._removeFocusedFeature();
|
|
2192
|
+
}
|
|
1910
2193
|
/** Escape cascades: cancel a draw, then clear selection, then leave edit mode. */
|
|
1911
2194
|
handleEscape() {
|
|
1912
2195
|
if (this.isDrawing()) {
|
|
@@ -1955,7 +2238,7 @@ class GoogleMapsService {
|
|
|
1955
2238
|
// directly. By the time that second delivery arrives (confirmed
|
|
1956
2239
|
// against the live Storybook: consistently a few milliseconds later,
|
|
1957
2240
|
// comfortably within one animation frame — see
|
|
1958
|
-
//
|
|
2241
|
+
// docs/superpowers/reports/closing-click-report.md), `isDrawing()` already reads
|
|
1959
2242
|
// false, so the guard just below cannot tell it apart from a fresh
|
|
1960
2243
|
// click on open map. `_suppressNextMapClick` exists to catch exactly
|
|
1961
2244
|
// that echo; see its doc comment for why a `domEvent.timeStamp`
|
|
@@ -2109,7 +2392,7 @@ class GoogleMapsService {
|
|
|
2109
2392
|
* Arm `_suppressNextMapClick`, and guarantee it cannot linger forever if
|
|
2110
2393
|
* the echo it exists for never arrives — confirmed to be the normal case
|
|
2111
2394
|
* for most real closes, and true of every synthetic/`play()`-driven draw,
|
|
2112
|
-
* per
|
|
2395
|
+
* per docs/superpowers/reports/closing-click-report.md. A `setTimeout` would "solve"
|
|
2113
2396
|
* this by guessing a safe wall-clock delay, which is exactly the kind of
|
|
2114
2397
|
* timing window this fix is trying to avoid introducing. Two
|
|
2115
2398
|
* `requestAnimationFrame` callbacks bound the window instead: the observed
|
|
@@ -2485,6 +2768,56 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
2485
2768
|
args: ['click', ['event']]
|
|
2486
2769
|
}] } });
|
|
2487
2770
|
|
|
2771
|
+
/**
|
|
2772
|
+
* The delete items the feature context menu offers.
|
|
2773
|
+
*
|
|
2774
|
+
* A refused item is not rendered at all. The caller is expected to hide the
|
|
2775
|
+
* menu entirely for an empty list, so a consumer that refuses everything gets
|
|
2776
|
+
* no menu rather than an empty one.
|
|
2777
|
+
*/
|
|
2778
|
+
function buildDeleteMenuItems(context) {
|
|
2779
|
+
const items = [];
|
|
2780
|
+
if (!context.editingEnabled) {
|
|
2781
|
+
return items;
|
|
2782
|
+
}
|
|
2783
|
+
if (context.mode !== 'grouped') {
|
|
2784
|
+
if (context.canDeleteSelection()) {
|
|
2785
|
+
items.push({ label: 'Delete', action: () => context.deleteSelection() });
|
|
2786
|
+
}
|
|
2787
|
+
return items;
|
|
2788
|
+
}
|
|
2789
|
+
const target = context.target;
|
|
2790
|
+
if (!target) {
|
|
2791
|
+
return items;
|
|
2792
|
+
}
|
|
2793
|
+
const key = target.group.key;
|
|
2794
|
+
// On a one-polygon field, "Delete Polygon" and "Delete Field" are the same
|
|
2795
|
+
// act — the first empties the group without saying so. Offer only the one
|
|
2796
|
+
// that names what happens.
|
|
2797
|
+
if (target.group.features.length <= 1) {
|
|
2798
|
+
if (context.canDeleteGroup(key)) {
|
|
2799
|
+
items.push({
|
|
2800
|
+
label: 'Delete Field',
|
|
2801
|
+
action: () => context.deleteGroup(key),
|
|
2802
|
+
});
|
|
2803
|
+
}
|
|
2804
|
+
return items;
|
|
2805
|
+
}
|
|
2806
|
+
if (context.canDeleteFocusedFeature()) {
|
|
2807
|
+
items.push({
|
|
2808
|
+
label: 'Delete Polygon',
|
|
2809
|
+
action: () => context.deleteFocusedFeature(),
|
|
2810
|
+
});
|
|
2811
|
+
}
|
|
2812
|
+
if (context.canDeleteGroup(key)) {
|
|
2813
|
+
items.push({
|
|
2814
|
+
label: 'Delete Field',
|
|
2815
|
+
action: () => context.deleteGroup(key),
|
|
2816
|
+
});
|
|
2817
|
+
}
|
|
2818
|
+
return items;
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2488
2821
|
class TheSeamGoogleMapsApiLoader {
|
|
2489
2822
|
}
|
|
2490
2823
|
|
|
@@ -2720,7 +3053,8 @@ class TheSeamGoogleMapsComponent extends _TheSeamGoogleMapsMixinBase {
|
|
|
2720
3053
|
_fileUploadControlDef = {
|
|
2721
3054
|
component: TheSeamGoogleMapsUploadButtonControlComponent,
|
|
2722
3055
|
data: { label: 'Import Geo File', icon: faFileImport },
|
|
2723
|
-
position: 6 /* google.maps.ControlPosition.LEFT_BOTTOM */,
|
|
3056
|
+
// position: 6 /* google.maps.ControlPosition.LEFT_BOTTOM */,
|
|
3057
|
+
position: 7, // Below top-right fullscreen button
|
|
2724
3058
|
};
|
|
2725
3059
|
_reCenterControlDef = {
|
|
2726
3060
|
component: TheSeamGoogleMapsRecenterButtonControlComponent,
|
|
@@ -2796,9 +3130,37 @@ class TheSeamGoogleMapsComponent extends _TheSeamGoogleMapsMixinBase {
|
|
|
2796
3130
|
newGroupKeyFactory;
|
|
2797
3131
|
/** Preselect a group. Applied on map-ready and after each external value write. */
|
|
2798
3132
|
selectedGroupKey = null;
|
|
3133
|
+
/**
|
|
3134
|
+
* Vetoes a delete. Return `false` to refuse the target.
|
|
3135
|
+
*
|
|
3136
|
+
* `target.feature === null` means the whole group is about to cease to
|
|
3137
|
+
* exist — every polygon of it is going, however the delete was asked for.
|
|
3138
|
+
* A non-null `feature` means one polygon is being removed from a group that
|
|
3139
|
+
* survives.
|
|
3140
|
+
*
|
|
3141
|
+
* **Consulted when no delete is happening.** It decides whether to render
|
|
3142
|
+
* the context-menu items, so it runs on every context-menu open. A
|
|
3143
|
+
* predicate that logs an attempt or flips state will do so on every
|
|
3144
|
+
* right-click. It must also be deterministic for a given target, or the
|
|
3145
|
+
* menu and the `Delete` key can disagree about the same target.
|
|
3146
|
+
*
|
|
3147
|
+
* That is a correctness warning, not a performance one. The context-menu
|
|
3148
|
+
* target changes only on a right-click, a delete, and a value write, so
|
|
3149
|
+
* this runs a handful of times per gesture.
|
|
3150
|
+
*
|
|
3151
|
+
* Passing nothing refuses nothing, which is the behaviour this component
|
|
3152
|
+
* has always had.
|
|
3153
|
+
*/
|
|
3154
|
+
canDelete;
|
|
2799
3155
|
mapReady = new EventEmitter();
|
|
2800
3156
|
selectionChange = new EventEmitter();
|
|
2801
3157
|
featureHoverChange = new EventEmitter();
|
|
3158
|
+
/**
|
|
3159
|
+
* A delete was attempted and refused — in practice, the `Delete` key, since
|
|
3160
|
+
* a refused menu item is never rendered. Emits the target that was refused
|
|
3161
|
+
* so the consumer can explain why; only the consumer knows the reason.
|
|
3162
|
+
*/
|
|
3163
|
+
deleteBlocked = new EventEmitter();
|
|
2802
3164
|
featureContextMenu;
|
|
2803
3165
|
_options = {
|
|
2804
3166
|
mapTypeControl: true,
|
|
@@ -2851,39 +3213,28 @@ class TheSeamGoogleMapsComponent extends _TheSeamGoogleMapsMixinBase {
|
|
|
2851
3213
|
this._googleMaps.hover$
|
|
2852
3214
|
.pipe(skip(1), tap((hover) => this.featureHoverChange.emit(hover)), takeUntil(this._ngUnsubscribe))
|
|
2853
3215
|
.subscribe();
|
|
3216
|
+
// No `skip(1)` here, unlike `selection$` and `hover$`: `deleteBlocked$` is
|
|
3217
|
+
// a plain Subject with no replayed initial value to drop.
|
|
3218
|
+
this._googleMaps.deleteBlocked$
|
|
3219
|
+
.pipe(tap((target) => this.deleteBlocked.emit(target)), takeUntil(this._ngUnsubscribe))
|
|
3220
|
+
.subscribe();
|
|
2854
3221
|
this._contextMenuItems$ = combineLatest([
|
|
2855
3222
|
this._googleMaps.editingEnabled$,
|
|
2856
|
-
// The RIGHT-CLICKED feature's group
|
|
2857
|
-
//
|
|
2858
|
-
// `
|
|
2859
|
-
// `selection$` always agree here — but this is still the one that
|
|
2860
|
-
// names what "Delete Field" is conceptually acting on, independent of
|
|
2861
|
-
// that coincidence. See `contextMenuTarget$`'s doc comment.
|
|
3223
|
+
// The RIGHT-CLICKED feature's group — what "Delete Field" is
|
|
3224
|
+
// conceptually acting on, independent of what happens to be selected.
|
|
3225
|
+
// See `contextMenuTarget$`'s doc comment.
|
|
2862
3226
|
this._googleMaps.contextMenuTarget$,
|
|
2863
|
-
]).pipe(map(([
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
items.push({
|
|
2875
|
-
label: 'Delete Field',
|
|
2876
|
-
action: () => this._googleMaps.deleteGroup(target.group.key),
|
|
2877
|
-
});
|
|
2878
|
-
}
|
|
2879
|
-
return items;
|
|
2880
|
-
}
|
|
2881
|
-
items.push({
|
|
2882
|
-
label: 'Delete',
|
|
2883
|
-
action: () => this._onClickDeleteFeature(),
|
|
2884
|
-
});
|
|
2885
|
-
return items;
|
|
2886
|
-
}), tap((items) => {
|
|
3227
|
+
]).pipe(map(([editingEnabled, target]) => buildDeleteMenuItems({
|
|
3228
|
+
mode: this.interactionMode,
|
|
3229
|
+
editingEnabled,
|
|
3230
|
+
target,
|
|
3231
|
+
canDeleteFocusedFeature: () => this._googleMaps.canDeleteFocusedFeature(),
|
|
3232
|
+
canDeleteGroup: (key) => this._googleMaps.canDeleteGroup(key),
|
|
3233
|
+
canDeleteSelection: () => this._googleMaps.canDeleteSelection(),
|
|
3234
|
+
deleteFocusedFeature: () => this._googleMaps.deleteFocusedFeature(),
|
|
3235
|
+
deleteGroup: (key) => this._googleMaps.deleteGroup(key),
|
|
3236
|
+
deleteSelection: () => this._googleMaps.deleteSelection(),
|
|
3237
|
+
})), tap((items) => {
|
|
2887
3238
|
if (items.length === 0) {
|
|
2888
3239
|
this._googleMaps.setFeatureContextMenu(null);
|
|
2889
3240
|
}
|
|
@@ -2985,6 +3336,9 @@ class TheSeamGoogleMapsComponent extends _TheSeamGoogleMapsMixinBase {
|
|
|
2985
3336
|
if (Object.prototype.hasOwnProperty.call(changes, 'selectedGroupKey')) {
|
|
2986
3337
|
this._applySelectedGroupKey();
|
|
2987
3338
|
}
|
|
3339
|
+
if (Object.prototype.hasOwnProperty.call(changes, 'canDelete')) {
|
|
3340
|
+
this._googleMaps.setCanDelete(this.canDelete);
|
|
3341
|
+
}
|
|
2988
3342
|
if (Object.prototype.hasOwnProperty.call(changes, 'featureLabelProperty')) {
|
|
2989
3343
|
this._googleMaps.setLabelProperty(this.featureLabelProperty);
|
|
2990
3344
|
}
|
|
@@ -3058,9 +3412,6 @@ class TheSeamGoogleMapsComponent extends _TheSeamGoogleMapsMixinBase {
|
|
|
3058
3412
|
this.idleListener?.remove();
|
|
3059
3413
|
});
|
|
3060
3414
|
}
|
|
3061
|
-
_onClickDeleteFeature() {
|
|
3062
|
-
this._googleMaps.deleteSelection();
|
|
3063
|
-
}
|
|
3064
3415
|
// The service delegates below guard on `mapReady` themselves, the same way
|
|
3065
3416
|
// `_applySelectedGroupKey()` already does for the declarative
|
|
3066
3417
|
// `selectedGroupKey` input. Their service-side counterparts call
|
|
@@ -3101,6 +3452,19 @@ class TheSeamGoogleMapsComponent extends _TheSeamGoogleMapsMixinBase {
|
|
|
3101
3452
|
}
|
|
3102
3453
|
return this._googleMaps.getGroups();
|
|
3103
3454
|
}
|
|
3455
|
+
/**
|
|
3456
|
+
* Set a group's label text in place. Returns false when no such group
|
|
3457
|
+
* exists, or when no `featureLabelProperty` is set.
|
|
3458
|
+
*
|
|
3459
|
+
* Does not clear the selection or re-fit the viewport, so it is safe to
|
|
3460
|
+
* call while the user is renaming the very group being displayed.
|
|
3461
|
+
*/
|
|
3462
|
+
setGroupLabel(key, label) {
|
|
3463
|
+
if (!this._googleMaps.mapReady) {
|
|
3464
|
+
return false;
|
|
3465
|
+
}
|
|
3466
|
+
return this._googleMaps.setGroupLabel(key, label);
|
|
3467
|
+
}
|
|
3104
3468
|
setEditMode(enabled) {
|
|
3105
3469
|
this._googleMaps.setEditMode(enabled);
|
|
3106
3470
|
}
|
|
@@ -3108,7 +3472,7 @@ class TheSeamGoogleMapsComponent extends _TheSeamGoogleMapsMixinBase {
|
|
|
3108
3472
|
return this._googleMaps.isEditMode();
|
|
3109
3473
|
}
|
|
3110
3474
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TheSeamGoogleMapsComponent, deps: [{ token: i0.ElementRef }, { token: i1.FocusMonitor }, { token: GoogleMapsService }, { token: MapValueManagerService }, { token: TheSeamGoogleMapsApiLoader }], target: i0.ɵɵFactoryTarget.Component });
|
|
3111
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: TheSeamGoogleMapsComponent, isStandalone: false, selector: "seam-google-maps", inputs: { disabled: "disabled", value: "value", tabIndex: "tabIndex", fileDropEnabled: "fileDropEnabled", fileUploadControlEnabled: "fileUploadControlEnabled", fullscreenControlEnabled: "fullscreenControlEnabled", reCenterControlEnabled: "reCenterControlEnabled", mapTypeControlEnabled: "mapTypeControlEnabled", streetViewControlEnabled: "streetViewControlEnabled", allowDrawingHoleInPolygon: "allowDrawingHoleInPolygon", editingEnabled: "editingEnabled", fileImportHandler: "fileImportHandler", zoom: "zoom", longitude: "longitude", latitude: "latitude", padding: "padding", interactionMode: "interactionMode", featureGroupProperty: "featureGroupProperty", featureLabelProperty: "featureLabelProperty", newGroupKeyFactory: "newGroupKeyFactory", selectedGroupKey: "selectedGroupKey" }, outputs: { mapReady: "mapReady", selectionChange: "selectionChange", featureHoverChange: "featureHoverChange" }, host: { properties: { "attr.disabled": "this._attrDisabled", "attr.tabindex": "this._attrTabIndex" } }, providers: [
|
|
3475
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: TheSeamGoogleMapsComponent, isStandalone: false, selector: "seam-google-maps", inputs: { disabled: "disabled", value: "value", tabIndex: "tabIndex", fileDropEnabled: "fileDropEnabled", fileUploadControlEnabled: "fileUploadControlEnabled", fullscreenControlEnabled: "fullscreenControlEnabled", reCenterControlEnabled: "reCenterControlEnabled", mapTypeControlEnabled: "mapTypeControlEnabled", streetViewControlEnabled: "streetViewControlEnabled", allowDrawingHoleInPolygon: "allowDrawingHoleInPolygon", editingEnabled: "editingEnabled", fileImportHandler: "fileImportHandler", zoom: "zoom", longitude: "longitude", latitude: "latitude", padding: "padding", interactionMode: "interactionMode", featureGroupProperty: "featureGroupProperty", featureLabelProperty: "featureLabelProperty", newGroupKeyFactory: "newGroupKeyFactory", selectedGroupKey: "selectedGroupKey", canDelete: "canDelete" }, outputs: { mapReady: "mapReady", selectionChange: "selectionChange", featureHoverChange: "featureHoverChange", deleteBlocked: "deleteBlocked" }, host: { properties: { "attr.disabled": "this._attrDisabled", "attr.tabindex": "this._attrTabIndex" } }, providers: [
|
|
3112
3476
|
MapValueManagerService,
|
|
3113
3477
|
GoogleMapsService,
|
|
3114
3478
|
{ provide: MAP_CONTROLS_SERVICE, useClass: GoogleMapsControlsService },
|
|
@@ -3117,7 +3481,7 @@ class TheSeamGoogleMapsComponent extends _TheSeamGoogleMapsMixinBase {
|
|
|
3117
3481
|
useExisting: forwardRef(() => TheSeamGoogleMapsComponent),
|
|
3118
3482
|
multi: true,
|
|
3119
3483
|
},
|
|
3120
|
-
], viewQueries: [{ propertyName: "featureContextMenu", first: true, predicate: ["featureContextMenu"], descendants: true, read: MenuComponent, static: true }], exportAs: ["seamGoogleMaps"], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"_gmApiLoaded | async\">\n <google-map\n height=\"100%\"\n width=\"100%\"\n [center]=\"{ lat: latitude, lng: longitude }\"\n [zoom]=\"zoom\"\n [options]=\"_options\"\n (mapInitialized)=\"_onMapReady($any($event))\"\n >\n </google-map>\n</ng-container>\n<seam-menu #featureContextMenu>\n <button\n seamMenuItem\n *ngFor=\"let item of _contextMenuItems$ | async\"\n (click)=\"item.action(
|
|
3484
|
+
], viewQueries: [{ propertyName: "featureContextMenu", first: true, predicate: ["featureContextMenu"], descendants: true, read: MenuComponent, static: true }], exportAs: ["seamGoogleMaps"], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"_gmApiLoaded | async\">\n <google-map\n height=\"100%\"\n width=\"100%\"\n [center]=\"{ lat: latitude, lng: longitude }\"\n [zoom]=\"zoom\"\n [options]=\"_options\"\n (mapInitialized)=\"_onMapReady($any($event))\"\n >\n </google-map>\n</ng-container>\n<seam-menu #featureContextMenu>\n <button\n seamMenuItem\n *ngFor=\"let item of _contextMenuItems$ | async\"\n (click)=\"item.action()\"\n >\n {{ item.label }}\n </button>\n</seam-menu>\n<seam-map-file-drop *ngIf=\"fileDropEnabled\"></seam-map-file-drop>\n\n<seam-map-control\n *ngIf=\"fileUploadControlEnabled\"\n [def]=\"_fileUploadControlDef\"\n></seam-map-control>\n<seam-map-control\n *ngIf=\"reCenterControlEnabled\"\n [def]=\"_reCenterControlDef\"\n></seam-map-control>\n<seam-map-control\n *ngIf=\"editingEnabled\"\n [def]=\"_drawControlDef\"\n></seam-map-control>\n", styles: [":host{display:block;position:relative;height:400px;width:100%}:host.show-focus-border.cdk-focused{outline:0;box-shadow:0 0 0 .2rem #357ebd40}:host.show-focus-border[disabled]{opacity:.65}:host ::ng-deep .seam-map-feature-label{color:#fff;font-size:.75rem;font-weight:600;text-shadow:0 0 3px rgba(0,0,0,.9),0 0 6px rgba(0,0,0,.7);-webkit-user-select:none;user-select:none}\n"], dependencies: [{ kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i6.MenuComponent, selector: "seam-menu", inputs: ["menuClass", "baseWidth", "animationType"], outputs: ["closed"], exportAs: ["seamMenu"] }, { kind: "component", type: i6.MenuItemComponent, selector: "[seamMenuItem]", inputs: ["disabled", "role", "icon", "iconClass", "sublevelIcon", "subLevelIconClass", "badgeText", "badgeTheme"], exportAs: ["seamMenuItem"] }, { kind: "component", type: i7.GoogleMap, selector: "google-map", inputs: ["height", "width", "mapId", "mapTypeId", "center", "zoom", "options"], outputs: ["mapInitialized", "authFailure", "boundsChanged", "centerChanged", "mapClick", "mapDblclick", "mapDrag", "mapDragend", "mapDragstart", "headingChanged", "idle", "maptypeidChanged", "mapMousemove", "mapMouseout", "mapMouseover", "projectionChanged", "mapRightclick", "tilesloaded", "tiltChanged", "zoomChanged"], exportAs: ["googleMap"] }, { kind: "component", type: TheSeamMapFileDropComponent, selector: "seam-map-file-drop" }, { kind: "component", type: TheSeamMapsControlComponent, selector: "seam-map-control", inputs: ["def"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3121
3485
|
}
|
|
3122
3486
|
__decorate([
|
|
3123
3487
|
InputBoolean()
|
|
@@ -3163,7 +3527,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
3163
3527
|
useExisting: forwardRef(() => TheSeamGoogleMapsComponent),
|
|
3164
3528
|
multi: true,
|
|
3165
3529
|
},
|
|
3166
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, exportAs: 'seamGoogleMaps', standalone: false, template: "<ng-container *ngIf=\"_gmApiLoaded | async\">\n <google-map\n height=\"100%\"\n width=\"100%\"\n [center]=\"{ lat: latitude, lng: longitude }\"\n [zoom]=\"zoom\"\n [options]=\"_options\"\n (mapInitialized)=\"_onMapReady($any($event))\"\n >\n </google-map>\n</ng-container>\n<seam-menu #featureContextMenu>\n <button\n seamMenuItem\n *ngFor=\"let item of _contextMenuItems$ | async\"\n (click)=\"item.action(
|
|
3530
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, exportAs: 'seamGoogleMaps', standalone: false, template: "<ng-container *ngIf=\"_gmApiLoaded | async\">\n <google-map\n height=\"100%\"\n width=\"100%\"\n [center]=\"{ lat: latitude, lng: longitude }\"\n [zoom]=\"zoom\"\n [options]=\"_options\"\n (mapInitialized)=\"_onMapReady($any($event))\"\n >\n </google-map>\n</ng-container>\n<seam-menu #featureContextMenu>\n <button\n seamMenuItem\n *ngFor=\"let item of _contextMenuItems$ | async\"\n (click)=\"item.action()\"\n >\n {{ item.label }}\n </button>\n</seam-menu>\n<seam-map-file-drop *ngIf=\"fileDropEnabled\"></seam-map-file-drop>\n\n<seam-map-control\n *ngIf=\"fileUploadControlEnabled\"\n [def]=\"_fileUploadControlDef\"\n></seam-map-control>\n<seam-map-control\n *ngIf=\"reCenterControlEnabled\"\n [def]=\"_reCenterControlDef\"\n></seam-map-control>\n<seam-map-control\n *ngIf=\"editingEnabled\"\n [def]=\"_drawControlDef\"\n></seam-map-control>\n", styles: [":host{display:block;position:relative;height:400px;width:100%}:host.show-focus-border.cdk-focused{outline:0;box-shadow:0 0 0 .2rem #357ebd40}:host.show-focus-border[disabled]{opacity:.65}:host ::ng-deep .seam-map-feature-label{color:#fff;font-size:.75rem;font-weight:600;text-shadow:0 0 3px rgba(0,0,0,.9),0 0 6px rgba(0,0,0,.7);-webkit-user-select:none;user-select:none}\n"] }]
|
|
3167
3531
|
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1.FocusMonitor }, { type: GoogleMapsService }, { type: MapValueManagerService }, { type: TheSeamGoogleMapsApiLoader }], propDecorators: { value: [{
|
|
3168
3532
|
type: Input
|
|
3169
3533
|
}], tabIndex: [{
|
|
@@ -3210,12 +3574,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
3210
3574
|
type: Input
|
|
3211
3575
|
}], selectedGroupKey: [{
|
|
3212
3576
|
type: Input
|
|
3577
|
+
}], canDelete: [{
|
|
3578
|
+
type: Input
|
|
3213
3579
|
}], mapReady: [{
|
|
3214
3580
|
type: Output
|
|
3215
3581
|
}], selectionChange: [{
|
|
3216
3582
|
type: Output
|
|
3217
3583
|
}], featureHoverChange: [{
|
|
3218
3584
|
type: Output
|
|
3585
|
+
}], deleteBlocked: [{
|
|
3586
|
+
type: Output
|
|
3219
3587
|
}], featureContextMenu: [{
|
|
3220
3588
|
type: ViewChild,
|
|
3221
3589
|
args: ['featureContextMenu', { static: true, read: MenuComponent }]
|
|
@@ -3796,5 +4164,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
3796
4164
|
* Generated bundle index. Do not edit.
|
|
3797
4165
|
*/
|
|
3798
4166
|
|
|
3799
|
-
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 };
|
|
4167
|
+
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, buildDeleteMenuItems, computeFeatureHoverStyle, computeFeatureStyle, createFeatureChangeObservable, dataMultiPolygonFromGeoJson, dataPolygonFromGeoJson, featureAllows, geoJsonFeatureFromDataFeature, geoJsonPolygonFromDataFeature, getBoundsWithAllFeatures, getFeatureBounds, getFeatureCenter, getFeaturesCount, getHoveredStyleOptionsDefinedByFeature, getSelectedStyleOptionsDefinedByFeature, getStyleOptionsDefinedByFeature, isAppFeatureProperty, isFeatureSelected, mergeStyleOptions, polygonCoordinates, polygonHasValidPathsLengths, polygonsFromDataFeature, removeAllFeatures, setFeatureSelected, stripAppFeaturePropertiesFromJson };
|
|
3800
4168
|
//# sourceMappingURL=theseam-ui-common-google-maps.mjs.map
|