@siemens/element-ng 51.3.0 → 51.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/fesm2022/siemens-element-ng-breadcrumb.mjs +8 -46
  2. package/fesm2022/siemens-element-ng-breadcrumb.mjs.map +1 -1
  3. package/fesm2022/siemens-element-ng-chat-messages.mjs +2 -2
  4. package/fesm2022/siemens-element-ng-chat-messages.mjs.map +1 -1
  5. package/fesm2022/siemens-element-ng-common.mjs +5 -0
  6. package/fesm2022/siemens-element-ng-common.mjs.map +1 -1
  7. package/fesm2022/siemens-element-ng-dashboard.mjs +2 -3
  8. package/fesm2022/siemens-element-ng-dashboard.mjs.map +1 -1
  9. package/fesm2022/siemens-element-ng-filtered-search.mjs +255 -160
  10. package/fesm2022/siemens-element-ng-filtered-search.mjs.map +1 -1
  11. package/fesm2022/siemens-element-ng-formly.mjs +12 -8
  12. package/fesm2022/siemens-element-ng-formly.mjs.map +1 -1
  13. package/fesm2022/siemens-element-ng-maplibre.mjs +293 -10
  14. package/fesm2022/siemens-element-ng-maplibre.mjs.map +1 -1
  15. package/fesm2022/siemens-element-ng-markdown-extensions-katex.mjs +2 -2
  16. package/fesm2022/siemens-element-ng-markdown-extensions-katex.mjs.map +1 -1
  17. package/fesm2022/siemens-element-ng-popover.mjs +3 -1
  18. package/fesm2022/siemens-element-ng-popover.mjs.map +1 -1
  19. package/fesm2022/siemens-element-ng-shadow-root.mjs +54 -1
  20. package/fesm2022/siemens-element-ng-shadow-root.mjs.map +1 -1
  21. package/fesm2022/siemens-element-ng-theme.mjs +59 -23
  22. package/fesm2022/siemens-element-ng-theme.mjs.map +1 -1
  23. package/fesm2022/siemens-element-ng-tooltip.mjs +82 -10
  24. package/fesm2022/siemens-element-ng-tooltip.mjs.map +1 -1
  25. package/fesm2022/siemens-element-ng-translate.mjs.map +1 -1
  26. package/fesm2022/siemens-element-ng-tree-view.mjs +2 -2
  27. package/fesm2022/siemens-element-ng-tree-view.mjs.map +1 -1
  28. package/maplibre/styles/maplibre-gl.scss +4 -0
  29. package/package.json +7 -7
  30. package/template-i18n.json +6 -0
  31. package/types/siemens-element-ng-breadcrumb.d.ts +1 -1
  32. package/types/siemens-element-ng-common.d.ts +5 -0
  33. package/types/siemens-element-ng-dashboard.d.ts +0 -1
  34. package/types/siemens-element-ng-filtered-search.d.ts +26 -10
  35. package/types/siemens-element-ng-maplibre.d.ts +112 -5
  36. package/types/siemens-element-ng-popover.d.ts +1 -0
  37. package/types/siemens-element-ng-shadow-root.d.ts +16 -1
  38. package/types/siemens-element-ng-theme.d.ts +35 -7
  39. package/types/siemens-element-ng-tooltip.d.ts +14 -1
  40. package/types/siemens-element-ng-translate.d.ts +6 -0
@@ -1,11 +1,17 @@
1
1
  import * as i0 from '@angular/core';
2
- import { assertInInjectionContext, computed, input, output, Component, signal } from '@angular/core';
2
+ import { assertInInjectionContext, computed, input, output, Component, signal, viewChild, Directive, inject, DestroyRef, DOCUMENT, booleanAttribute, numberAttribute, linkedSignal, resource, contentChild, TemplateRef, effect, afterRenderEffect } from '@angular/core';
3
3
  import { injectIsDarkTheme } from '@siemens/element-ng/theme';
4
4
  import { siMapStyle } from '@siemens/map-styles/common';
5
- import { ClusterPointDirective, GeoJSONSourceComponent, MarkersForClustersComponent } from '@maplibre/ngx-maplibre-gl';
5
+ import { GeoJSONSourceComponent, ClusterPointDirective, MarkersForClustersComponent, MapService, PopupComponent } from '@maplibre/ngx-maplibre-gl';
6
6
  import { t, SiTranslatePipe, injectSiTranslateService } from '@siemens/element-translate-ng/translate';
7
+ import { FocusTrapFactory } from '@angular/cdk/a11y';
8
+ import { NgTemplateOutlet } from '@angular/common';
9
+ import { elementCancel } from '@siemens/element-icons';
10
+ import { addIcons, SiIconComponent } from '@siemens/element-ng/icon';
11
+ import { SiLoadingSpinnerDirective } from '@siemens/element-ng/loading-spinner';
12
+ import { ResizeObserverService } from '@siemens/element-ng/resize-observer';
13
+ import { from, map } from 'rxjs';
7
14
  import { toSignal } from '@angular/core/rxjs-interop';
8
- import { map } from 'rxjs';
9
15
 
10
16
  /**
11
17
  * Copyright (c) Siemens 2016 - 2026
@@ -292,12 +298,17 @@ class SiClusterSourceComponent {
292
298
  /** Stores selection for the projected cluster-popover integration. */
293
299
  selectedCluster = signal(undefined, /* @ts-ignore */
294
300
  ...(ngDevMode ? [{ debugName: "selectedCluster" }] : /* istanbul ignore next */ []));
295
- async selectCluster(feature) {
296
- this.clusterClick.emit(feature);
297
- this.selectedCluster.set({ ...feature, geometry: feature.geometry });
301
+ source = viewChild.required(GeoJSONSourceComponent, /* @ts-ignore */
302
+ ...(ngDevMode ? [{ debugName: "source" }] : /* istanbul ignore next */ []));
303
+ /**
304
+ * Returns a page of original points belonging to a cluster.
305
+ * @see https://maplibre.org/maplibre-gl-js/docs/API/classes/GeoJSONSource/#getclusterleaves
306
+ */
307
+ getClusterLeaves(clusterId, limit, offset) {
308
+ return this.source().getClusterLeaves(clusterId, limit, offset);
298
309
  }
299
310
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SiClusterSourceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
300
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: SiClusterSourceComponent, isStandalone: true, selector: "si-cluster-source", inputs: { sourceId: { classPropertyName: "sourceId", publicName: "sourceId", isSignal: true, isRequired: true, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, groupProperty: { classPropertyName: "groupProperty", publicName: "groupProperty", isSignal: true, isRequired: false, transformFunction: null }, groupColors: { classPropertyName: "groupColors", publicName: "groupColors", isSignal: true, isRequired: false, transformFunction: null }, statusProperty: { classPropertyName: "statusProperty", publicName: "statusProperty", isSignal: true, isRequired: false, transformFunction: null }, clusterRadius: { classPropertyName: "clusterRadius", publicName: "clusterRadius", isSignal: true, isRequired: false, transformFunction: null }, clusterMaxZoom: { classPropertyName: "clusterMaxZoom", publicName: "clusterMaxZoom", isSignal: true, isRequired: false, transformFunction: null }, clusterMinPoints: { classPropertyName: "clusterMinPoints", publicName: "clusterMinPoints", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clusterClick: "clusterClick" }, ngImport: i0, template: "<mgl-geojson-source\n #source\n [id]=\"sourceId()\"\n [data]=\"data()\"\n [cluster]=\"true\"\n [clusterRadius]=\"clusterRadius()\"\n [clusterMaxZoom]=\"clusterMaxZoom()\"\n [clusterMinPoints]=\"clusterMinPoints()\"\n [clusterProperties]=\"clusterConfiguration().clusterProperties\"\n/>\n\n<mgl-markers-for-clusters [source]=\"sourceId()\">\n <ng-template let-feature mglClusterPoint>\n <si-cluster-marker\n [feature]=\"feature\"\n [segments]=\"clusterConfiguration().segments\"\n (markerClick)=\"selectCluster(feature)\"\n />\n </ng-template>\n</mgl-markers-for-clusters>\n\n<ng-content select=\"si-cluster-popover,[siClusterPopoverOutlet]\" />\n", dependencies: [{ kind: "directive", type: ClusterPointDirective, selector: "ng-template[mglClusterPoint]" }, { kind: "component", type: GeoJSONSourceComponent, selector: "mgl-geojson-source", inputs: ["data", "maxzoom", "attribution", "buffer", "tolerance", "cluster", "clusterRadius", "clusterMaxZoom", "clusterMinPoints", "clusterProperties", "lineMetrics", "generateId", "promoteId", "filter"] }, { kind: "component", type: MarkersForClustersComponent, selector: "mgl-markers-for-clusters", inputs: ["source"] }, { kind: "component", type: SiClusterMarkerComponent, selector: "si-cluster-marker", inputs: ["feature", "segments"], outputs: ["markerClick"] }] });
311
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.8", type: SiClusterSourceComponent, isStandalone: true, selector: "si-cluster-source", inputs: { sourceId: { classPropertyName: "sourceId", publicName: "sourceId", isSignal: true, isRequired: true, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, groupProperty: { classPropertyName: "groupProperty", publicName: "groupProperty", isSignal: true, isRequired: false, transformFunction: null }, groupColors: { classPropertyName: "groupColors", publicName: "groupColors", isSignal: true, isRequired: false, transformFunction: null }, statusProperty: { classPropertyName: "statusProperty", publicName: "statusProperty", isSignal: true, isRequired: false, transformFunction: null }, clusterRadius: { classPropertyName: "clusterRadius", publicName: "clusterRadius", isSignal: true, isRequired: false, transformFunction: null }, clusterMaxZoom: { classPropertyName: "clusterMaxZoom", publicName: "clusterMaxZoom", isSignal: true, isRequired: false, transformFunction: null }, clusterMinPoints: { classPropertyName: "clusterMinPoints", publicName: "clusterMinPoints", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clusterClick: "clusterClick" }, viewQueries: [{ propertyName: "source", first: true, predicate: GeoJSONSourceComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<mgl-geojson-source\n #source\n [id]=\"sourceId()\"\n [data]=\"data()\"\n [cluster]=\"true\"\n [clusterRadius]=\"clusterRadius()\"\n [clusterMaxZoom]=\"clusterMaxZoom()\"\n [clusterMinPoints]=\"clusterMinPoints()\"\n [clusterProperties]=\"clusterConfiguration().clusterProperties\"\n/>\n\n<mgl-markers-for-clusters [source]=\"sourceId()\">\n <ng-template let-feature mglClusterPoint>\n <si-cluster-marker\n [feature]=\"feature\"\n [segments]=\"clusterConfiguration().segments\"\n (markerClick)=\"clusterClick.emit(feature)\"\n />\n </ng-template>\n</mgl-markers-for-clusters>\n\n<ng-content />\n", dependencies: [{ kind: "directive", type: ClusterPointDirective, selector: "ng-template[mglClusterPoint]" }, { kind: "component", type: GeoJSONSourceComponent, selector: "mgl-geojson-source", inputs: ["data", "maxzoom", "attribution", "buffer", "tolerance", "cluster", "clusterRadius", "clusterMaxZoom", "clusterMinPoints", "clusterProperties", "lineMetrics", "generateId", "promoteId", "filter"] }, { kind: "component", type: MarkersForClustersComponent, selector: "mgl-markers-for-clusters", inputs: ["source"] }, { kind: "component", type: SiClusterMarkerComponent, selector: "si-cluster-marker", inputs: ["feature", "segments"], outputs: ["markerClick"] }] });
301
312
  }
302
313
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SiClusterSourceComponent, decorators: [{
303
314
  type: Component,
@@ -306,8 +317,280 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
306
317
  GeoJSONSourceComponent,
307
318
  MarkersForClustersComponent,
308
319
  SiClusterMarkerComponent
309
- ], template: "<mgl-geojson-source\n #source\n [id]=\"sourceId()\"\n [data]=\"data()\"\n [cluster]=\"true\"\n [clusterRadius]=\"clusterRadius()\"\n [clusterMaxZoom]=\"clusterMaxZoom()\"\n [clusterMinPoints]=\"clusterMinPoints()\"\n [clusterProperties]=\"clusterConfiguration().clusterProperties\"\n/>\n\n<mgl-markers-for-clusters [source]=\"sourceId()\">\n <ng-template let-feature mglClusterPoint>\n <si-cluster-marker\n [feature]=\"feature\"\n [segments]=\"clusterConfiguration().segments\"\n (markerClick)=\"selectCluster(feature)\"\n />\n </ng-template>\n</mgl-markers-for-clusters>\n\n<ng-content select=\"si-cluster-popover,[siClusterPopoverOutlet]\" />\n" }]
310
- }], propDecorators: { sourceId: [{ type: i0.Input, args: [{ isSignal: true, alias: "sourceId", required: true }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], groupProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupProperty", required: false }] }], groupColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupColors", required: false }] }], statusProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "statusProperty", required: false }] }], clusterRadius: [{ type: i0.Input, args: [{ isSignal: true, alias: "clusterRadius", required: false }] }], clusterMaxZoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "clusterMaxZoom", required: false }] }], clusterMinPoints: [{ type: i0.Input, args: [{ isSignal: true, alias: "clusterMinPoints", required: false }] }], clusterClick: [{ type: i0.Output, args: ["clusterClick"] }] } });
320
+ ], template: "<mgl-geojson-source\n #source\n [id]=\"sourceId()\"\n [data]=\"data()\"\n [cluster]=\"true\"\n [clusterRadius]=\"clusterRadius()\"\n [clusterMaxZoom]=\"clusterMaxZoom()\"\n [clusterMinPoints]=\"clusterMinPoints()\"\n [clusterProperties]=\"clusterConfiguration().clusterProperties\"\n/>\n\n<mgl-markers-for-clusters [source]=\"sourceId()\">\n <ng-template let-feature mglClusterPoint>\n <si-cluster-marker\n [feature]=\"feature\"\n [segments]=\"clusterConfiguration().segments\"\n (markerClick)=\"clusterClick.emit(feature)\"\n />\n </ng-template>\n</mgl-markers-for-clusters>\n\n<ng-content />\n" }]
321
+ }], propDecorators: { sourceId: [{ type: i0.Input, args: [{ isSignal: true, alias: "sourceId", required: true }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], groupProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupProperty", required: false }] }], groupColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupColors", required: false }] }], statusProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "statusProperty", required: false }] }], clusterRadius: [{ type: i0.Input, args: [{ isSignal: true, alias: "clusterRadius", required: false }] }], clusterMaxZoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "clusterMaxZoom", required: false }] }], clusterMinPoints: [{ type: i0.Input, args: [{ isSignal: true, alias: "clusterMinPoints", required: false }] }], clusterClick: [{ type: i0.Output, args: ["clusterClick"] }], source: [{ type: i0.ViewChild, args: [i0.forwardRef(() => GeoJSONSourceComponent), { isSignal: true }] }] } });
322
+
323
+ /**
324
+ * Copyright (c) Siemens 2016 - 2026
325
+ * SPDX-License-Identifier: MIT
326
+ */
327
+ /**
328
+ * Template for custom location content inside `si-cluster-popover`.
329
+ *
330
+ * Receives a {@link ClusterPopoverContext} with all locations loaded so far. Loading feedback,
331
+ * error recovery, and the "Load more" button are handled by the popover.
332
+ *
333
+ * @experimental
334
+ */
335
+ class SiClusterPopoverTemplateDirective {
336
+ /** @internal */
337
+ static ngTemplateContextGuard(directive, context) {
338
+ return true;
339
+ }
340
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SiClusterPopoverTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
341
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: SiClusterPopoverTemplateDirective, isStandalone: true, selector: "[siClusterPopoverTemplate]", ngImport: i0 });
342
+ }
343
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SiClusterPopoverTemplateDirective, decorators: [{
344
+ type: Directive,
345
+ args: [{
346
+ selector: '[siClusterPopoverTemplate]'
347
+ }]
348
+ }] });
349
+
350
+ /**
351
+ * Copyright (c) Siemens 2016 - 2026
352
+ * SPDX-License-Identifier: MIT
353
+ */
354
+ /**
355
+ * Optional popup with incrementally loaded locations projected into a `si-cluster-source`.
356
+ *
357
+ * @experimental
358
+ */
359
+ class SiClusterPopoverComponent {
360
+ static idCounter = 0;
361
+ destroyRef = inject(DestroyRef);
362
+ focusTrapFactory = inject(FocusTrapFactory);
363
+ document = inject(DOCUMENT);
364
+ source = inject(SiClusterSourceComponent);
365
+ mapService = inject(MapService);
366
+ resizeObserver = inject(ResizeObserverService);
367
+ /**
368
+ * Maximum width passed to the MapLibre popup.
369
+ * @defaultValue '320px'
370
+ */
371
+ maxWidth = input('320px', /* @ts-ignore */
372
+ ...(ngDevMode ? [{ debugName: "maxWidth" }] : /* istanbul ignore next */ []));
373
+ /**
374
+ * Whether the popup closes when the map moves.
375
+ * @defaultValue true
376
+ */
377
+ closeOnMove = input(true, { ...(ngDevMode ? { debugName: "closeOnMove" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
378
+ /**
379
+ * Number of locations loaded initially and with each "Load more" action.
380
+ * @defaultValue 8
381
+ */
382
+ pageSize = input(8, { ...(ngDevMode ? { debugName: "pageSize" } : /* istanbul ignore next */ {}), transform: numberAttribute });
383
+ /**
384
+ * Feature property displayed in the default list.
385
+ * @defaultValue 'name'
386
+ */
387
+ labelProperty = input('name', /* @ts-ignore */
388
+ ...(ngDevMode ? [{ debugName: "labelProperty" }] : /* istanbul ignore next */ []));
389
+ /**
390
+ * Feature property displayed as the description in the default list.
391
+ * @defaultValue 'description'
392
+ */
393
+ descriptionProperty = input('description', /* @ts-ignore */
394
+ ...(ngDevMode ? [{ debugName: "descriptionProperty" }] : /* istanbul ignore next */ []));
395
+ dialog = viewChild('dialog', /* @ts-ignore */
396
+ ...(ngDevMode ? [{ debugName: "dialog" }] : /* istanbul ignore next */ []));
397
+ scrollRegion = viewChild('scrollRegion', /* @ts-ignore */
398
+ ...(ngDevMode ? [{ debugName: "scrollRegion" }] : /* istanbul ignore next */ []));
399
+ batchButton = viewChild('batchButton', /* @ts-ignore */
400
+ ...(ngDevMode ? [{ debugName: "batchButton" }] : /* istanbul ignore next */ []));
401
+ selectedCluster = linkedSignal(() => {
402
+ this.source.sourceId();
403
+ this.source.data();
404
+ this.source.groupProperty();
405
+ this.source.groupColors();
406
+ this.source.statusProperty();
407
+ this.source.clusterRadius();
408
+ this.source.clusterMaxZoom();
409
+ this.source.clusterMinPoints();
410
+ return undefined;
411
+ }, /* @ts-ignore */
412
+ ...(ngDevMode ? [{ debugName: "selectedCluster" }] : /* istanbul ignore next */ []));
413
+ batchFocusRequested = linkedSignal(() => {
414
+ this.selectedCluster();
415
+ this.pageSize();
416
+ return false;
417
+ }, /* @ts-ignore */
418
+ ...(ngDevMode ? [{ debugName: "batchFocusRequested" }] : /* istanbul ignore next */ []));
419
+ offset = linkedSignal(() => {
420
+ this.selectedCluster();
421
+ this.pageSize();
422
+ return 0;
423
+ }, /* @ts-ignore */
424
+ ...(ngDevMode ? [{ debugName: "offset" }] : /* istanbul ignore next */ []));
425
+ displayedFeatures = linkedSignal({ ...(ngDevMode ? { debugName: "displayedFeatures" } : /* istanbul ignore next */ {}), source: () => ({
426
+ cluster: this.selectedCluster(),
427
+ limit: this.pageSize(),
428
+ offset: this.offset(),
429
+ features: this.leaves.hasValue() ? this.leaves.value() : undefined
430
+ }),
431
+ computation: (current, previous) => {
432
+ if (!current.cluster) {
433
+ return [];
434
+ }
435
+ const retainedFeatures = current.cluster === previous?.source.cluster && current.limit === previous.source.limit
436
+ ? previous.value
437
+ : [];
438
+ return current.features
439
+ ? [...retainedFeatures.slice(0, current.offset), ...current.features]
440
+ : retainedFeatures;
441
+ } });
442
+ titleId = `__si-cluster-popover-title-${SiClusterPopoverComponent.idCounter++}`;
443
+ popupCoordinates = linkedSignal(() => this.selectedCluster()?.geometry.coordinates, /* @ts-ignore */
444
+ ...(ngDevMode ? [{ debugName: "popupCoordinates" }] : /* istanbul ignore next */ []));
445
+ availableBlockSize = signal(undefined, /* @ts-ignore */
446
+ ...(ngDevMode ? [{ debugName: "availableBlockSize" }] : /* istanbul ignore next */ []));
447
+ leaves = resource({ ...(ngDevMode ? { debugName: "leaves" } : /* istanbul ignore next */ {}), params: () => {
448
+ const cluster = this.selectedCluster();
449
+ return cluster
450
+ ? {
451
+ cluster,
452
+ limit: this.pageSize(),
453
+ offset: this.offset()
454
+ }
455
+ : undefined;
456
+ },
457
+ loader: ({ params }) => this.source.getClusterLeaves(Number(params.cluster.properties?.cluster_id), params.limit, params.offset) });
458
+ context = computed(() => {
459
+ const features = this.displayedFeatures();
460
+ const cluster = this.selectedCluster();
461
+ if (!cluster) {
462
+ return undefined;
463
+ }
464
+ return {
465
+ $implicit: features,
466
+ features,
467
+ total: Number(cluster.properties?.point_count),
468
+ loading: this.leaves.isLoading(),
469
+ error: this.leaves.error(),
470
+ cluster,
471
+ close: this.close
472
+ };
473
+ }, /* @ts-ignore */
474
+ ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
475
+ template = contentChild(SiClusterPopoverTemplateDirective, { ...(ngDevMode ? { debugName: "template" } : /* istanbul ignore next */ {}), read: TemplateRef });
476
+ clusterPopoverTitle = t(() => $localize `:@@SI_MAP_CLUSTER.POPOVER_TITLE:Cluster with {{count}} locations`);
477
+ locationsLabel = t(() => $localize `:@@SI_MAP_CLUSTER.LOCATIONS:Cluster locations`);
478
+ errorText = t(() => $localize `:@@SI_MAP_CLUSTER.ERROR:Could not load locations.`);
479
+ loadMoreText = t(() => $localize `:@@SI_MAP_CLUSTER.LOAD_MORE:Load more`);
480
+ retryText = t(() => $localize `:@@SI_MAP_CLUSTER.RETRY:Retry`);
481
+ closeText = t(() => $localize `:@@SI_MAP_CLUSTER.CLOSE:Close`);
482
+ icons = addIcons({ elementCancel });
483
+ close = () => this.selectedCluster.set(undefined);
484
+ loadBatch = () => {
485
+ if (this.leaves.isLoading()) {
486
+ return;
487
+ }
488
+ // Safari does not focus buttons on click.
489
+ this.batchButton()?.nativeElement.focus({ preventScroll: true });
490
+ this.batchFocusRequested.set(true);
491
+ if (this.leaves.error()) {
492
+ this.leaves.reload();
493
+ }
494
+ else {
495
+ this.offset.set(this.displayedFeatures().length);
496
+ }
497
+ };
498
+ constructor() {
499
+ const subscription = this.source.clusterClick.subscribe(cluster => {
500
+ this.selectedCluster.set(cluster);
501
+ });
502
+ this.destroyRef.onDestroy(() => subscription.unsubscribe());
503
+ effect(onCleanup => {
504
+ const dialog = this.dialog()?.nativeElement;
505
+ const cluster = this.selectedCluster();
506
+ if (!dialog || !cluster) {
507
+ return;
508
+ }
509
+ const focusTrap = this.focusTrapFactory.create(dialog);
510
+ const focusSubscription = from(focusTrap.focusFirstTabbableElementWhenReady()).subscribe();
511
+ const map = this.mapService.mapInstance;
512
+ const mapContainer = map.getContainer();
513
+ const updateAvailableSpace = () => {
514
+ const [longitude, latitude] = cluster.geometry.coordinates;
515
+ const { y } = map.project([longitude, latitude]);
516
+ const height = mapContainer.clientHeight;
517
+ this.availableBlockSize.set(Math.min(height, Math.max(y, height - y)));
518
+ };
519
+ updateAvailableSpace();
520
+ const mapResizeSubscription = this.resizeObserver
521
+ .observe(mapContainer, 0)
522
+ .subscribe(updateAvailableSpace);
523
+ const dialogResizeSubscription = this.resizeObserver.observe(dialog, 0).subscribe(() => {
524
+ // A new coordinate reference refreshes MapLibre's popup anchor.
525
+ this.popupCoordinates.update(coordinates => (coordinates ? [...coordinates] : undefined));
526
+ });
527
+ const moveSubscription = map.on('move', updateAvailableSpace);
528
+ onCleanup(() => {
529
+ mapResizeSubscription.unsubscribe();
530
+ dialogResizeSubscription.unsubscribe();
531
+ moveSubscription.unsubscribe();
532
+ focusSubscription.unsubscribe();
533
+ focusTrap.destroy();
534
+ });
535
+ });
536
+ afterRenderEffect({
537
+ earlyRead: () => {
538
+ if (!this.batchFocusRequested()) {
539
+ return undefined;
540
+ }
541
+ const loading = this.leaves.isLoading();
542
+ const button = this.batchButton()?.nativeElement;
543
+ const active = this.document.activeElement;
544
+ return {
545
+ loading,
546
+ button: active === button ? button : undefined,
547
+ scrollRegion: !button && active === this.document.body
548
+ ? this.scrollRegion()?.nativeElement
549
+ : undefined
550
+ };
551
+ },
552
+ write: state => {
553
+ const request = state();
554
+ if (!request || request.loading) {
555
+ return;
556
+ }
557
+ this.batchFocusRequested.set(false);
558
+ if (request.button) {
559
+ request.button.scrollIntoView({ block: 'nearest', inline: 'nearest' });
560
+ }
561
+ else {
562
+ request.scrollRegion?.focus({ preventScroll: true });
563
+ }
564
+ }
565
+ });
566
+ effect(onCleanup => {
567
+ if (!this.selectedCluster()) {
568
+ return;
569
+ }
570
+ const trigger = this.document.activeElement;
571
+ onCleanup(() => {
572
+ const active = this.document.activeElement;
573
+ if (trigger instanceof HTMLElement &&
574
+ trigger.isConnected &&
575
+ (active === this.document.body || this.dialog()?.nativeElement.contains(active))) {
576
+ trigger.focus();
577
+ }
578
+ });
579
+ });
580
+ }
581
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SiClusterPopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
582
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: SiClusterPopoverComponent, isStandalone: true, selector: "si-cluster-popover", inputs: { maxWidth: { classPropertyName: "maxWidth", publicName: "maxWidth", isSignal: true, isRequired: false, transformFunction: null }, closeOnMove: { classPropertyName: "closeOnMove", publicName: "closeOnMove", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, labelProperty: { classPropertyName: "labelProperty", publicName: "labelProperty", isSignal: true, isRequired: false, transformFunction: null }, descriptionProperty: { classPropertyName: "descriptionProperty", publicName: "descriptionProperty", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "template", first: true, predicate: SiClusterPopoverTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }], viewQueries: [{ propertyName: "dialog", first: true, predicate: ["dialog"], descendants: true, isSignal: true }, { propertyName: "scrollRegion", first: true, predicate: ["scrollRegion"], descendants: true, isSignal: true }, { propertyName: "batchButton", first: true, predicate: ["batchButton"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (context(); as context) {\n <mgl-popup\n [maxWidth]=\"maxWidth()\"\n [lngLat]=\"popupCoordinates()\"\n [closeOnMove]=\"closeOnMove()\"\n [focusAfterOpen]=\"false\"\n [closeButton]=\"false\"\n (popupClose)=\"context.close()\"\n >\n <section\n #dialog\n class=\"cluster-popover d-flex flex-column\"\n role=\"dialog\"\n [attr.aria-labelledby]=\"titleId\"\n [style.--_si-cluster-popover-available-block-size.px]=\"availableBlockSize()\"\n (keydown.escape)=\"$event.stopPropagation(); context.close()\"\n >\n <div class=\"popover-header flex-shrink-0\">\n <strong class=\"flex-grow-1\" [id]=\"titleId\">\n {{ clusterPopoverTitle | translate: { count: context.total } }}\n </strong>\n <button\n type=\"button\"\n class=\"btn btn-circle btn-sm btn-ghost ms-2\"\n [attr.aria-label]=\"closeText | translate\"\n (click)=\"context.close()\"\n >\n <si-icon [icon]=\"icons.elementCancel\" />\n </button>\n </div>\n <div\n class=\"cluster-popover-body d-flex flex-column p-2\"\n [siLoading]=\"context.loading\"\n [blocking]=\"true\"\n >\n <div\n #scrollRegion\n class=\"cluster-popover-scroll overflow-auto\"\n role=\"region\"\n tabindex=\"0\"\n [attr.aria-label]=\"locationsLabel | translate\"\n >\n <div [inert]=\"context.loading\" [attr.aria-busy]=\"context.loading\">\n @if (template(); as customTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"customTemplate\"\n [ngTemplateOutletContext]=\"context\"\n />\n } @else {\n <ul class=\"list-unstyled mb-0\">\n @for (feature of context.features; track feature.id ?? $index) {\n <li class=\"list-item p-4\">\n <div class=\"list-item-title pt-0\">\n {{ feature.properties?.[labelProperty()] }}\n </div>\n <div class=\"list-item-description mb-0\">\n {{ feature.properties?.[descriptionProperty()] }}\n </div>\n </li>\n }\n </ul>\n }\n </div>\n @if (context.error && !context.loading) {\n <p role=\"alert\" class=\"mt-4 mb-0\">{{ errorText | translate }}</p>\n }\n @if (\n context.error ||\n (context.features.length > 0 && context.features.length < context.total)\n ) {\n <button\n #batchButton\n type=\"button\"\n class=\"btn btn-link d-flex w-100 justify-content-center p-4 focus-inside\"\n [class.disabled]=\"context.loading\"\n [attr.aria-disabled]=\"context.loading\"\n (click)=\"loadBatch()\"\n >\n {{ (context.error ? retryText : loadMoreText) | translate }}\n </button>\n }\n </div>\n </div>\n </section>\n </mgl-popup>\n}\n", styles: [".cluster-popover{min-inline-size:200px;max-block-size:calc(var(--_si-cluster-popover-available-block-size, 100dvb) - var(--si-sys-sizing-spacing-y-60) * 2)}.cluster-popover-body,.cluster-popover-scroll{min-block-size:0}.cluster-popover-scroll{overscroll-behavior-block:contain}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: PopupComponent, selector: "mgl-popup", inputs: ["closeButton", "closeOnClick", "closeOnMove", "focusAfterOpen", "anchor", "className", "maxWidth", "subpixelPositioning", "feature", "lngLat", "marker", "offset"], outputs: ["popupClose", "popupOpen"] }, { kind: "component", type: SiIconComponent, selector: "si-icon", inputs: ["icon"] }, { kind: "directive", type: SiLoadingSpinnerDirective, selector: "[siLoading]", inputs: ["siLoading", "blocking", "initialDelay"] }, { kind: "pipe", type: SiTranslatePipe, name: "translate" }] });
583
+ }
584
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SiClusterPopoverComponent, decorators: [{
585
+ type: Component,
586
+ args: [{ selector: 'si-cluster-popover', imports: [
587
+ NgTemplateOutlet,
588
+ PopupComponent,
589
+ SiIconComponent,
590
+ SiLoadingSpinnerDirective,
591
+ SiTranslatePipe
592
+ ], template: "@if (context(); as context) {\n <mgl-popup\n [maxWidth]=\"maxWidth()\"\n [lngLat]=\"popupCoordinates()\"\n [closeOnMove]=\"closeOnMove()\"\n [focusAfterOpen]=\"false\"\n [closeButton]=\"false\"\n (popupClose)=\"context.close()\"\n >\n <section\n #dialog\n class=\"cluster-popover d-flex flex-column\"\n role=\"dialog\"\n [attr.aria-labelledby]=\"titleId\"\n [style.--_si-cluster-popover-available-block-size.px]=\"availableBlockSize()\"\n (keydown.escape)=\"$event.stopPropagation(); context.close()\"\n >\n <div class=\"popover-header flex-shrink-0\">\n <strong class=\"flex-grow-1\" [id]=\"titleId\">\n {{ clusterPopoverTitle | translate: { count: context.total } }}\n </strong>\n <button\n type=\"button\"\n class=\"btn btn-circle btn-sm btn-ghost ms-2\"\n [attr.aria-label]=\"closeText | translate\"\n (click)=\"context.close()\"\n >\n <si-icon [icon]=\"icons.elementCancel\" />\n </button>\n </div>\n <div\n class=\"cluster-popover-body d-flex flex-column p-2\"\n [siLoading]=\"context.loading\"\n [blocking]=\"true\"\n >\n <div\n #scrollRegion\n class=\"cluster-popover-scroll overflow-auto\"\n role=\"region\"\n tabindex=\"0\"\n [attr.aria-label]=\"locationsLabel | translate\"\n >\n <div [inert]=\"context.loading\" [attr.aria-busy]=\"context.loading\">\n @if (template(); as customTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"customTemplate\"\n [ngTemplateOutletContext]=\"context\"\n />\n } @else {\n <ul class=\"list-unstyled mb-0\">\n @for (feature of context.features; track feature.id ?? $index) {\n <li class=\"list-item p-4\">\n <div class=\"list-item-title pt-0\">\n {{ feature.properties?.[labelProperty()] }}\n </div>\n <div class=\"list-item-description mb-0\">\n {{ feature.properties?.[descriptionProperty()] }}\n </div>\n </li>\n }\n </ul>\n }\n </div>\n @if (context.error && !context.loading) {\n <p role=\"alert\" class=\"mt-4 mb-0\">{{ errorText | translate }}</p>\n }\n @if (\n context.error ||\n (context.features.length > 0 && context.features.length < context.total)\n ) {\n <button\n #batchButton\n type=\"button\"\n class=\"btn btn-link d-flex w-100 justify-content-center p-4 focus-inside\"\n [class.disabled]=\"context.loading\"\n [attr.aria-disabled]=\"context.loading\"\n (click)=\"loadBatch()\"\n >\n {{ (context.error ? retryText : loadMoreText) | translate }}\n </button>\n }\n </div>\n </div>\n </section>\n </mgl-popup>\n}\n", styles: [".cluster-popover{min-inline-size:200px;max-block-size:calc(var(--_si-cluster-popover-available-block-size, 100dvb) - var(--si-sys-sizing-spacing-y-60) * 2)}.cluster-popover-body,.cluster-popover-scroll{min-block-size:0}.cluster-popover-scroll{overscroll-behavior-block:contain}\n"] }]
593
+ }], ctorParameters: () => [], propDecorators: { maxWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxWidth", required: false }] }], closeOnMove: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnMove", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], labelProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelProperty", required: false }] }], descriptionProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "descriptionProperty", required: false }] }], dialog: [{ type: i0.ViewChild, args: ['dialog', { isSignal: true }] }], scrollRegion: [{ type: i0.ViewChild, args: ['scrollRegion', { isSignal: true }] }], batchButton: [{ type: i0.ViewChild, args: ['batchButton', { isSignal: true }] }], template: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SiClusterPopoverTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }] } });
311
594
 
312
595
  const createDefaultLocale = () => ({
313
596
  'AttributionControl.ToggleAttribution': t(() => $localize `:@@SI_MAP.TOGGLE_ATTRIBUTION:Toggle attribution`),
@@ -397,5 +680,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
397
680
  * Generated bundle index. Do not edit.
398
681
  */
399
682
 
400
- export { SiClusterSourceComponent, SiStatusMarkerComponent, injectSiMapStyle, injectSiMapTranslations };
683
+ export { SiClusterPopoverComponent, SiClusterPopoverTemplateDirective, SiClusterSourceComponent, SiStatusMarkerComponent, injectSiMapStyle, injectSiMapTranslations };
401
684
  //# sourceMappingURL=siemens-element-ng-maplibre.mjs.map