geonetwork-ui 2.11.0-dev.ca7fb6b05 → 2.11.0-dev.cf22e64cf

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 (68) hide show
  1. package/fesm2022/geonetwork-ui.mjs +1736 -1379
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +221 -105
  4. package/index.d.ts.map +1 -1
  5. package/package.json +2 -1
  6. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +120 -34
  7. package/src/libs/common/domain/src/lib/model/search/filter.model.ts +24 -2
  8. package/src/libs/feature/map/src/index.ts +0 -2
  9. package/src/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.html +1 -0
  10. package/src/libs/feature/map/src/lib/feature-map.module.ts +1 -8
  11. package/src/libs/feature/notifications/src/lib/notifications.service.ts +6 -4
  12. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +8 -3
  13. package/src/libs/feature/router/src/lib/default/state/query-params.utils.ts +1 -1
  14. package/src/libs/feature/search/src/index.ts +2 -0
  15. package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +12 -4
  16. package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +63 -19
  17. package/src/libs/feature/{map/src/lib → search/src/lib/geocoding}/geocoding.service.ts +16 -7
  18. package/src/libs/feature/search/src/lib/location-search/location-search.component.html +8 -0
  19. package/src/libs/feature/search/src/lib/location-search/location-search.component.ts +40 -0
  20. package/src/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts +19 -8
  21. package/src/libs/feature/search/src/lib/state/reducer.ts +1 -4
  22. package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +15 -1
  23. package/src/libs/feature/search/src/lib/utils/service/fields.ts +61 -2
  24. package/src/libs/ui/elements/src/lib/service-capabilities/service-capabilities.component.ts +2 -1
  25. package/src/libs/ui/inputs/src/index.ts +2 -0
  26. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.html +10 -1
  27. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts +8 -0
  28. package/src/libs/ui/inputs/src/lib/date-adapter.providers.ts +30 -0
  29. package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +3 -24
  30. package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.css +37 -3
  31. package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.html +135 -16
  32. package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.ts +166 -29
  33. package/src/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.ts +3 -7
  34. package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.css +1 -0
  35. package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.html +20 -7
  36. package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.ts +46 -5
  37. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +7 -4
  38. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.ts +12 -9
  39. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.model.ts +2 -2
  40. package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html +111 -0
  41. package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts +221 -0
  42. package/src/libs/ui/layout/src/lib/block-list/block-list.component.css +1 -0
  43. package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +2 -0
  44. package/src/libs/util/app-config/src/lib/app-config.ts +23 -0
  45. package/src/libs/util/app-config/src/lib/fixtures.ts +16 -1
  46. package/src/libs/util/app-config/src/lib/model.ts +15 -1
  47. package/src/libs/util/shared/src/index.ts +1 -0
  48. package/src/libs/util/shared/src/lib/autofocus.directive.ts +26 -0
  49. package/src/libs/util/shared/src/lib/links/link-utils.ts +19 -10
  50. package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
  51. package/src/libs/util/shared/src/lib/utils/file.ts +15 -0
  52. package/src/libs/util/shared/src/lib/utils/geojson.ts +24 -0
  53. package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
  54. package/tailwind.base.css +5 -0
  55. package/translations/de.json +19 -3
  56. package/translations/en.json +18 -2
  57. package/translations/es.json +17 -1
  58. package/translations/fr.json +23 -7
  59. package/translations/it.json +17 -1
  60. package/translations/nl.json +17 -1
  61. package/translations/pt.json +17 -1
  62. package/translations/sk.json +17 -1
  63. package/src/libs/feature/map/src/lib/geocoding/geocoding.component.html +0 -41
  64. package/src/libs/feature/map/src/lib/geocoding/geocoding.component.ts +0 -112
  65. package/src/libs/feature/map/src/lib/style/index.ts +0 -2
  66. package/src/libs/feature/map/src/lib/style/map-style.fixtures.ts +0 -47
  67. package/src/libs/feature/map/src/lib/style/map-style.service.ts +0 -108
  68. /package/src/libs/{feature/map/src/lib/geocoding/geocoding.component.css → ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.css} +0 -0
@@ -6,9 +6,9 @@ import GeoJSON from 'ol/format/GeoJSON.js';
6
6
  import { parse as parse$1 } from 'ol/xml.js';
7
7
  import { format } from 'date-fns/format';
8
8
  import { Namespace, Literal, lit, parse as parse$2, sym, BlankNode, graph } from 'rdflib';
9
- import { lastValueFrom, fromEvent, startWith, map as map$2, shareReplay, filter, pairwise, of, switchMap, Subject, forkJoin, combineLatest, from, exhaustMap, throwError, takeLast, firstValueFrom, merge, BehaviorSubject, timer, ReplaySubject, Subscription, first, distinctUntilChanged as distinctUntilChanged$1, animationFrameScheduler, debounceTime as debounceTime$1, Observable, buffer, tap as tap$2, combineLatestWith, take as take$1, catchError as catchError$1, takeUntil, EMPTY, mergeMap as mergeMap$1, withLatestFrom as withLatestFrom$1 } from 'rxjs';
9
+ import { lastValueFrom, fromEvent, startWith, map as map$2, shareReplay, filter, pairwise, of, switchMap, Subject, forkJoin, combineLatest, from, exhaustMap, throwError, takeLast, firstValueFrom, merge, BehaviorSubject, timer, ReplaySubject, Subscription, first, distinctUntilChanged as distinctUntilChanged$1, animationFrameScheduler, debounceTime as debounceTime$1, Observable, buffer, tap as tap$2, combineLatestWith, take as take$1, catchError as catchError$1, EMPTY, mergeMap as mergeMap$1, withLatestFrom as withLatestFrom$1, takeUntil } from 'rxjs';
10
10
  import * as i0 from '@angular/core';
11
- import { InjectionToken, inject, Injectable, NgModule, Injector, provideAppInitializer, makeEnvironmentProviders, ElementRef, HostListener, Input, Directive, Renderer2, DestroyRef, EventEmitter, Output, ViewChild, ChangeDetectionStrategy, Component, ViewEncapsulation, ChangeDetectorRef, HostBinding, ViewContainerRef, TemplateRef, ViewChildren, ContentChild, input, output, signal, viewChild, computed, ContentChildren, NgZone, ComponentFactoryResolver, afterNextRender, viewChildren } from '@angular/core';
11
+ import { InjectionToken, inject, Injectable, NgModule, Injector, provideAppInitializer, makeEnvironmentProviders, ElementRef, afterNextRender, booleanAttribute, Input, Directive, HostListener, Renderer2, DestroyRef, EventEmitter, Output, ViewChild, ChangeDetectionStrategy, Component, ViewEncapsulation, ChangeDetectorRef, HostBinding, ViewContainerRef, TemplateRef, ViewChildren, ContentChild, input, output, signal, viewChild, computed, ContentChildren, NgZone, ComponentFactoryResolver, viewChildren } from '@angular/core';
12
12
  import { HttpClient, HttpHeaders, HttpParams, HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi, HttpEventType } from '@angular/common/http';
13
13
  import * as i4 from '@ngx-translate/core';
14
14
  import { TranslateLoader, TranslateCompiler, TranslateDefaultParser, TranslateParser, TranslateService, provideTranslateService, TranslateDirective, TranslatePipe, TranslateModule } from '@ngx-translate/core';
@@ -18,7 +18,7 @@ import { map as map$1, shareReplay as shareReplay$1, catchError, tap as tap$1, f
18
18
  import { lt, valid, coerce, satisfies, ltr } from 'semver';
19
19
  import chroma from 'chroma-js';
20
20
  import * as i1$1 from '@angular/common';
21
- import { Location, CommonModule, NgClass, NgTemplateOutlet, DatePipe, AsyncPipe } from '@angular/common';
21
+ import { Location, CommonModule, NgTemplateOutlet, NgClass, AsyncPipe } from '@angular/common';
22
22
  import { formatDistance } from 'date-fns/formatDistance';
23
23
  import { Scroll, NavigationEnd, Router, RouteReuseStrategy } from '@angular/router';
24
24
  import { WmtsEndpoint, WmsEndpoint, WfsEndpoint, OgcApiEndpoint, sharedFetch, useCache, StacEndpoint, TmsEndpoint } from '@camptocamp/ogc-client';
@@ -26,7 +26,6 @@ import { getLength } from 'ol/sphere.js';
26
26
  import { LineString } from 'ol/geom.js';
27
27
  import * as i1 from '@ngrx/store';
28
28
  import { createAction, props, createReducer, on, createFeatureSelector, createSelector, Store, select, StoreModule } from '@ngrx/store';
29
- import { queryDataGouvFr, queryGeonames, queryGeoadmin } from '@geospatial-sdk/geocoding';
30
29
  import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
31
30
  import { FeaturesClickEventType, FeaturesHoverEventType, MapClickEventType, MapExtentChangeEventType, SourceLoadErrorType, computeMapContextDiff, createViewFromLayer } from '@geospatial-sdk/core';
32
31
  import { createMapFromContext, applyContextDiffToMap, listen } from '@geospatial-sdk/openlayers';
@@ -37,14 +36,11 @@ import { createLegendFromLayer } from '@geospatial-sdk/legend';
37
36
  import { mouseOnly, noModifierKeys, primaryAction, platformModifierKeyOnly } from 'ol/events/condition.js';
38
37
  import { defaults, DragPan, MouseWheelZoom } from 'ol/interaction.js';
39
38
  import { extend } from 'ol/extent.js';
40
- import * as TOML from '@ltd/j-toml';
41
- import { Style, Stroke, Fill, Circle } from 'ol/style.js';
42
- import CircleStyle from 'ol/style/Circle.js';
43
39
  import EmblaCarousel from 'embla-carousel';
44
- import { iconoirNavArrowLeft, iconoirNavArrowRight, iconoirLongArrowDownLeft, iconoirSearch, iconoirCalendar, iconoirArrowUp, iconoirLink, iconoirFramePlusIn, iconoirCloudUpload, iconoirReduce, iconoirNavArrowUp, iconoirNavArrowDown, iconoirExpand, iconoirSettings, iconoirDownload, iconoirPlus, iconoirBin, iconoirMediaImageXmark, iconoirMediaImage, iconoirAppWindow, iconoirCode, iconoirDatabase, iconoirAppleWallet, iconoirBank, iconoirList, iconoirTranslate, iconoirLock, iconoirUser, iconoirArrowLeft, iconoirLightBulbOn, iconoirImport, iconoirBadgeCheck, iconoirSystemShut, iconoirCircle, iconoirCheckCircle, iconoirAttachment, iconoirRefresh } from '@ng-icons/iconoir';
40
+ import { iconoirNavArrowLeft, iconoirNavArrowRight, iconoirLongArrowDownLeft, iconoirSearch, iconoirCalendar, iconoirArrowUp, iconoirLink, iconoirFramePlusIn, iconoirCloudUpload, iconoirUpload, iconoirTrash, iconoirSquareDashed, iconoirCheckCircle, iconoirReduce, iconoirNavArrowUp, iconoirNavArrowDown, iconoirExpand, iconoirSettings, iconoirDownload, iconoirPlus, iconoirBin, iconoirMediaImageXmark, iconoirMediaImage, iconoirAppWindow, iconoirCode, iconoirDatabase, iconoirAppleWallet, iconoirBank, iconoirList, iconoirTranslate, iconoirLock, iconoirUser, iconoirArrowLeft, iconoirLightBulbOn, iconoirImport, iconoirBadgeCheck, iconoirSystemShut, iconoirCircle, iconoirAttachment, iconoirRefresh } from '@ng-icons/iconoir';
45
41
  import { MatButtonModule } from '@angular/material/button';
46
- import * as i1$8 from '@angular/cdk/overlay';
47
- import { OverlayContainer, ScrollStrategyOptions, OverlayModule, CdkConnectedOverlay, ScrollDispatcher, Overlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
42
+ import * as i1$7 from '@angular/cdk/overlay';
43
+ import { ScrollStrategyOptions, OverlayModule, CdkConnectedOverlay, ScrollDispatcher, Overlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
48
44
  import * as i1$3 from '@angular/forms';
49
45
  import { UntypedFormControl, ReactiveFormsModule, FormsModule } from '@angular/forms';
50
46
  import * as i1$2 from '@angular/material/autocomplete';
@@ -52,16 +48,18 @@ import { MatAutocompleteModule, MatAutocompleteTrigger, MatAutocomplete } from '
52
48
  import tippy from 'tippy.js';
53
49
  import * as i2 from '@angular/material/progress-spinner';
54
50
  import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
55
- import { matClose, matContentCopy, matExpandLess, matExpandMore, matSearch, matStarBorder, matStar, matRemove, matAdd, matChevronRight, matChevronLeft, matArrowForwardIos, matArrowBackIos, matMoodBad, matQuestionMark, matFace, matZoomOutMap, matOpenInNew, matPersonOutline, matMailOutline, matWarningAmber, matCheck, matCheckCircleOutline, matWarning, matMoreVert } from '@ng-icons/material-icons/baseline';
51
+ import { matClose, matContentCopy, matExpandMore, matExpandLess, matSearch, matStarBorder, matStar, matRemove, matAdd, matChevronRight, matChevronLeft, matArrowForwardIos, matArrowBackIos, matMoodBad, matQuestionMark, matFace, matZoomOutMap, matOpenInNew, matPersonOutline, matMailOutline, matWarningAmber, matCheck, matCheckCircleOutline, matWarning, matMoreVert } from '@ng-icons/material-icons/baseline';
56
52
  import * as i1$4 from '@angular/material/checkbox';
57
53
  import { MatCheckboxModule } from '@angular/material/checkbox';
58
54
  import * as i1$5 from '@angular/material/tooltip';
59
55
  import { MatTooltipModule } from '@angular/material/tooltip';
60
- import { DateAdapter, MatNativeDateModule, MAT_DATE_LOCALE } from '@angular/material/core';
56
+ import { MAT_DATE_LOCALE, MAT_DATE_FORMATS, DateAdapter } from '@angular/material/core';
57
+ import { MAT_DATE_FNS_FORMATS, DateFnsAdapter } from '@angular/material-date-fns-adapter';
58
+ import { enUS } from 'date-fns/locale/en-US';
61
59
  import * as i1$6 from '@angular/material/datepicker';
62
60
  import { MatDatepickerModule } from '@angular/material/datepicker';
63
- import * as i1$7 from 'ngx-dropzone';
64
- import { NgxDropzoneModule } from 'ngx-dropzone';
61
+ import * as i1$8 from 'ngx-dropzone';
62
+ import { NgxDropzoneModule, NgxDropzoneComponent } from 'ngx-dropzone';
65
63
  import * as i1$9 from '@angular/material/button-toggle';
66
64
  import { MatButtonToggleModule } from '@angular/material/button-toggle';
67
65
  import { moveItemInArray, CdkDropList, CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop';
@@ -72,6 +70,7 @@ import { MatTabsModule } from '@angular/material/tabs';
72
70
  import * as i2$2 from '@ngrx/effects';
73
71
  import { Actions, createEffect, ofType, EffectsModule } from '@ngrx/effects';
74
72
  import { valid as valid$1 } from 'geojson-validation';
73
+ import { queryGeoplateforme, queryGeonames, queryGeoadmin } from '@geospatial-sdk/geocoding';
75
74
  import { TemplatePortal } from '@angular/cdk/portal';
76
75
  import { parse as parse$3 } from 'marked';
77
76
  import * as i2$3 from '@angular/cdk/scrolling';
@@ -99,6 +98,7 @@ import { TextFieldModule } from '@angular/cdk/text-field';
99
98
  import * as i2$5 from '@ngrx/router-store';
100
99
  import { getRouterSelectors, routerReducer, StoreRouterConnectingModule, FullRouterStateSerializer } from '@ngrx/router-store';
101
100
  import { navigation } from '@ngrx/router-store/data-persistence';
101
+ import * as TOML from '@ltd/j-toml';
102
102
 
103
103
  class MetadataMapperContext {
104
104
  }
@@ -19467,10 +19467,10 @@ class DateService {
19467
19467
  const dateObj = this.getDateObject(date);
19468
19468
  return { locale, dateObj };
19469
19469
  }
19470
- async getDateLocale() {
19470
+ async getDateFnsLocale() {
19471
19471
  const lang = this.translateService.getCurrentLang() || DEFAULT_LANGUAGE;
19472
19472
  const locales = await this.dateLocales;
19473
- return locales[lang];
19473
+ return locales[lang] ?? locales[DEFAULT_LANGUAGE];
19474
19474
  }
19475
19475
  formatDate(date, options) {
19476
19476
  const { locale, dateObj } = this.getLocaleAndDate(date);
@@ -19483,7 +19483,7 @@ class DateService {
19483
19483
  async formatRelativeDateTime(date) {
19484
19484
  const dateObj = this.getDateObject(date);
19485
19485
  const now = new Date();
19486
- const locale = await this.getDateLocale();
19486
+ const locale = await this.getDateFnsLocale();
19487
19487
  return formatDistance(dateObj, now, {
19488
19488
  addSuffix: true,
19489
19489
  locale: locale,
@@ -19518,6 +19518,18 @@ function propagateToDocumentOnly(event) {
19518
19518
  }, 0);
19519
19519
  }
19520
19520
 
19521
+ function isFileExtensionValid(fileName, acceptedExtensions) {
19522
+ return acceptedExtensions.some((ext) => fileName.toLowerCase().endsWith(ext));
19523
+ }
19524
+ function readFileAsText(file) {
19525
+ return new Promise((resolve, reject) => {
19526
+ const reader = new FileReader();
19527
+ reader.onload = () => resolve(reader.result);
19528
+ reader.onerror = () => reject(reader.error);
19529
+ reader.readAsText(file);
19530
+ });
19531
+ }
19532
+
19521
19533
  function formatUserInfo(userInfo, displayCount = false) {
19522
19534
  const infos = (typeof userInfo === 'string' ? userInfo : '').split('|');
19523
19535
  const count = displayCount ? ` ${infos[3].split(' ')[1]}` : '';
@@ -19577,6 +19589,24 @@ function getGeometryFromGeoJSON(data) {
19577
19589
  }
19578
19590
  return null;
19579
19591
  }
19592
+ function isBoundingBox(value) {
19593
+ return (Array.isArray(value) &&
19594
+ value.length === 4 &&
19595
+ value.every((item) => typeof item === 'number'));
19596
+ }
19597
+ /**
19598
+ * Checks that a bounding box's coordinates stay within the valid
19599
+ * longitude (-180/180) and latitude (-90/90) ranges.
19600
+ */
19601
+ function isBoundingBoxWithinWorldExtent(bbox) {
19602
+ const [minX, minY, maxX, maxY] = bbox;
19603
+ return (minX >= -180 &&
19604
+ maxX <= 180 &&
19605
+ minY >= -90 &&
19606
+ maxY <= 90 &&
19607
+ minX <= maxX &&
19608
+ minY <= maxY);
19609
+ }
19580
19610
  function getGeometryBoundingBox(geometry) {
19581
19611
  // use the bounding box if specified in the GeoJSON object
19582
19612
  if (geometry.bbox) {
@@ -20245,7 +20275,7 @@ function getLinkLabel(link) {
20245
20275
  const label = link.description || ('name' in link ? link.name : '');
20246
20276
  return format ? `${label} (${format})` : label;
20247
20277
  }
20248
- async function getLayers(url, serviceProtocol) {
20278
+ async function getLayers(url, serviceProtocol, deep = false) {
20249
20279
  switch (serviceProtocol) {
20250
20280
  case 'ogcFeatures': {
20251
20281
  const layers = await new OgcApiEndpoint(url).allCollections;
@@ -20253,7 +20283,10 @@ async function getLayers(url, serviceProtocol) {
20253
20283
  }
20254
20284
  case 'wfs': {
20255
20285
  const endpointWfs = await new WfsEndpoint(url).isReady();
20256
- const featureTypes = await endpointWfs.getFeatureTypes();
20286
+ const featureTypes = endpointWfs.getFeatureTypes();
20287
+ if (!deep) {
20288
+ return featureTypes;
20289
+ }
20257
20290
  const layers = (await Promise.allSettled(featureTypes.map((collection) => {
20258
20291
  return endpointWfs.getFeatureTypeFull(collection.name);
20259
20292
  })))
@@ -20263,12 +20296,14 @@ async function getLayers(url, serviceProtocol) {
20263
20296
  }
20264
20297
  case 'wms': {
20265
20298
  const endpointWms = await new WmsEndpoint(url).isReady();
20266
- const layers = (await endpointWms
20299
+ const layersSummary = endpointWms
20267
20300
  .getLayers()
20268
20301
  .flatMap(wmsLayerFlatten)
20269
- .filter((l) => l.name)).map((collection) => {
20270
- return endpointWms.getLayerByName(collection.name);
20271
- });
20302
+ .filter((l) => l.name);
20303
+ if (!deep) {
20304
+ return layersSummary;
20305
+ }
20306
+ const layers = layersSummary.map((collection) => endpointWms.getLayerByName(collection.name));
20272
20307
  return layers;
20273
20308
  }
20274
20309
  case 'wmts': {
@@ -20362,6 +20397,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
20362
20397
  }]
20363
20398
  }] });
20364
20399
 
20400
+ class AutofocusDirective {
20401
+ constructor() {
20402
+ this.el = inject(ElementRef);
20403
+ this.injector = inject(Injector);
20404
+ }
20405
+ set autofocus(active) {
20406
+ if (!active)
20407
+ return;
20408
+ afterNextRender(() => this.el.nativeElement.focus(), {
20409
+ injector: this.injector,
20410
+ });
20411
+ }
20412
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AutofocusDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
20413
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "20.3.19", type: AutofocusDirective, isStandalone: true, selector: "[gnUiAutofocus]", inputs: { autofocus: ["gnUiAutofocus", "autofocus", booleanAttribute] }, ngImport: i0 }); }
20414
+ }
20415
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AutofocusDirective, decorators: [{
20416
+ type: Directive,
20417
+ args: [{
20418
+ selector: '[gnUiAutofocus]',
20419
+ standalone: true,
20420
+ }]
20421
+ }], propDecorators: { autofocus: [{
20422
+ type: Input,
20423
+ args: [{ alias: 'gnUiAutofocus', transform: booleanAttribute }]
20424
+ }] } });
20425
+
20365
20426
  class ImageFallbackDirective {
20366
20427
  constructor() {
20367
20428
  this.el = inject(ElementRef);
@@ -20389,7 +20450,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
20389
20450
  }] } });
20390
20451
 
20391
20452
  var name = "geonetwork-ui";
20392
- var version = "2.11.0-dev.ca7fb6b05";
20453
+ var version = "2.11.0-dev.cf22e64cf";
20393
20454
  var engines = {
20394
20455
  node: ">=24"
20395
20456
  };
@@ -20410,6 +20471,7 @@ var peerDependencies = {
20410
20471
  "@angular/core": "19.x || 20.x || 21.x",
20411
20472
  "@angular/forms": "19.x || 20.x || 21.x",
20412
20473
  "@angular/material": "19.x || 20.x || 21.x",
20474
+ "@angular/material-date-fns-adapter": "19.x || 20.x",
20413
20475
  "@angular/platform-browser": "19.x || 20.x || 21.x",
20414
20476
  "@angular/platform-browser-dynamic": "19.x || 20.x || 21.x",
20415
20477
  "@angular/router": "19.x || 20.x || 21.x",
@@ -20651,8 +20713,8 @@ class ElasticsearchService {
20651
20713
  if (!payload.runtime_mappings)
20652
20714
  payload.runtime_mappings = {};
20653
20715
  payload.runtime_mappings[fieldName] = {
20654
- type: 'keyword',
20655
- script: this.runtimeFields[fieldName],
20716
+ type: this.runtimeFields[fieldName].type,
20717
+ script: this.runtimeFields[fieldName].script,
20656
20718
  };
20657
20719
  };
20658
20720
  const lookForField = (node) => {
@@ -20672,6 +20734,12 @@ class ElasticsearchService {
20672
20734
  (node[runtimeField] === 'asc' || node[runtimeField] === 'desc')) {
20673
20735
  addMapping(runtimeField);
20674
20736
  }
20737
+ if (runtimeField in node &&
20738
+ typeof node[runtimeField] === 'object' &&
20739
+ node[runtimeField] !== null &&
20740
+ ('gte' in node[runtimeField] || 'lte' in node[runtimeField])) {
20741
+ addMapping(runtimeField);
20742
+ }
20675
20743
  if ('query' in node &&
20676
20744
  typeof node.query === 'string' &&
20677
20745
  node.query.indexOf(runtimeField + ':') > -1) {
@@ -20688,8 +20756,8 @@ class ElasticsearchService {
20688
20756
  lookForField(payload.query);
20689
20757
  return payload;
20690
20758
  }
20691
- registerRuntimeField(fieldName, expression) {
20692
- this.runtimeFields[fieldName] = expression;
20759
+ registerRuntimeField(fieldName, expression, type = 'keyword') {
20760
+ this.runtimeFields[fieldName] = { script: expression, type };
20693
20761
  }
20694
20762
  getMetadataByIdsPayload(uuids) {
20695
20763
  return {
@@ -20806,7 +20874,62 @@ class ElasticsearchService {
20806
20874
  isCurrentSearchLang() {
20807
20875
  return this.metadataLang === 'current';
20808
20876
  }
20809
- filtersToQuery(filters) {
20877
+ findSpatialFilterExtent(filters) {
20878
+ if (typeof filters === 'string') {
20879
+ return undefined;
20880
+ }
20881
+ return Object.values(filters).find(isBoundingBox);
20882
+ }
20883
+ // builds a query matching records whose dates intersect the filter range
20884
+ buildDateRangeQuery(searchField, dateRange) {
20885
+ // fields ending with 'DateRange' maps as ES range type
20886
+ if (searchField.endsWith('DateRange')) {
20887
+ return {
20888
+ range: {
20889
+ [searchField]: {
20890
+ ...(dateRange.start && { gte: formatDate(dateRange.start) }),
20891
+ ...(dateRange.end && { lte: formatDate(dateRange.end) }),
20892
+ format: 'yyyy-MM-dd',
20893
+ relation: 'intersects',
20894
+ },
20895
+ },
20896
+ };
20897
+ }
20898
+ const minField = `${searchField}Min`;
20899
+ const maxField = `${searchField}Max`;
20900
+ if (minField in this.runtimeFields && maxField in this.runtimeFields) {
20901
+ // min/max runtime fields form an interval per record; match if it intersects the filter range
20902
+ const filter = [
20903
+ dateRange.start && {
20904
+ range: {
20905
+ [maxField]: {
20906
+ gte: formatDate(dateRange.start),
20907
+ format: 'yyyy-MM-dd',
20908
+ },
20909
+ },
20910
+ },
20911
+ dateRange.end && {
20912
+ range: {
20913
+ [minField]: {
20914
+ lte: formatDate(dateRange.end),
20915
+ format: 'yyyy-MM-dd',
20916
+ },
20917
+ },
20918
+ },
20919
+ ].filter(Boolean);
20920
+ return { bool: { filter } };
20921
+ }
20922
+ return {
20923
+ range: {
20924
+ [searchField]: {
20925
+ ...(dateRange.start && { gte: formatDate(dateRange.start) }),
20926
+ ...(dateRange.end && { lte: formatDate(dateRange.end) }),
20927
+ format: 'yyyy-MM-dd',
20928
+ },
20929
+ },
20930
+ };
20931
+ }
20932
+ filtersToQuery(filters, spatialFilterExtent = this.findSpatialFilterExtent(filters)) {
20810
20933
  const addQuote = (key) => (/^\/.+\/$/.test(key) ? key : `"${key}"`);
20811
20934
  const makeQuery = (filter) => {
20812
20935
  if (typeof filter === 'string') {
@@ -20825,7 +20948,9 @@ class ElasticsearchService {
20825
20948
  ? filters
20826
20949
  : Object.keys(filters)
20827
20950
  .filter((fieldname) => fieldname !== 'gn-ui-crossFieldFilter')
20951
+ .filter((fieldname) => !isBoundingBox(filters[fieldname]))
20828
20952
  .filter((fieldname) => !isDateRange(filters[fieldname]))
20953
+ .filter((fieldname) => !Array.isArray(filters[fieldname]))
20829
20954
  .filter((fieldname) => filters[fieldname] &&
20830
20955
  JSON.stringify(filters[fieldname]) !== '{}')
20831
20956
  .map((fieldname) => `${fieldname}:(${makeQuery(filters[fieldname])})`)
@@ -20833,31 +20958,26 @@ class ElasticsearchService {
20833
20958
  if (filters['gn-ui-crossFieldFilter']) {
20834
20959
  queryString = `${queryString} AND (${filters['gn-ui-crossFieldFilter']})`;
20835
20960
  }
20836
- const queryRange = Object.entries(filters)
20837
- .filter(([, value]) => isDateRange(value))
20838
- .map(([searchField, dateRange]) => {
20839
- return {
20840
- searchField,
20841
- dateRange,
20842
- };
20843
- })[0];
20961
+ const queryRanges = Object.entries(filters).filter(([, value]) => isDateRange(value));
20844
20962
  const queryParts = [
20845
20963
  queryString && {
20846
20964
  query_string: {
20847
20965
  query: queryString,
20848
20966
  },
20849
20967
  },
20850
- queryRange &&
20851
- queryRange.dateRange && {
20852
- range: {
20853
- [queryRange.searchField]: {
20854
- ...(queryRange.dateRange.start && {
20855
- gte: formatDate(queryRange.dateRange.start),
20856
- }),
20857
- ...(queryRange.dateRange.end && {
20858
- lte: formatDate(queryRange.dateRange.end),
20859
- }),
20860
- format: 'yyyy-MM-dd',
20968
+ ...queryRanges.map(([searchField, dateRange]) => this.buildDateRangeQuery(searchField, dateRange)),
20969
+ spatialFilterExtent && {
20970
+ geo_shape: {
20971
+ geom: {
20972
+ shape: {
20973
+ type: 'envelope',
20974
+ // spatialFilterExtent is [minX, minY, maxX, maxY]; envelope coordinates are [top-left, bottom-right]
20975
+ coordinates: [
20976
+ [spatialFilterExtent[0], spatialFilterExtent[3]],
20977
+ [spatialFilterExtent[2], spatialFilterExtent[1]],
20978
+ ],
20979
+ },
20980
+ relation: 'intersects',
20861
20981
  },
20862
20982
  },
20863
20983
  },
@@ -20887,7 +21007,9 @@ class ElasticsearchService {
20887
21007
  },
20888
21008
  });
20889
21009
  }
20890
- const queryFilters = this.filtersToQuery(fieldSearchFilters);
21010
+ // a spatial extent filter takes precedence over the preference geometry for boosting
21011
+ const spatialFilterExtent = this.findSpatialFilterExtent(fieldSearchFilters);
21012
+ const queryFilters = this.filtersToQuery(fieldSearchFilters, spatialFilterExtent);
20891
21013
  if (queryFilters) {
20892
21014
  filter.push(...queryFilters);
20893
21015
  }
@@ -20898,7 +21020,10 @@ class ElasticsearchService {
20898
21020
  },
20899
21021
  });
20900
21022
  }
20901
- if (geometry) {
21023
+ const boostGeometry = spatialFilterExtent
21024
+ ? bboxToPolygon(spatialFilterExtent)
21025
+ : geometry;
21026
+ if (boostGeometry) {
20902
21027
  // boosts applied using the filter geometry:
20903
21028
  // * records completely within the geometry receive a boost of 5
20904
21029
  // * records intersecting the geometry receive a boost of 2
@@ -20907,7 +21032,7 @@ class ElasticsearchService {
20907
21032
  should.push({
20908
21033
  geo_shape: {
20909
21034
  geom: {
20910
- shape: geometry,
21035
+ shape: boostGeometry,
20911
21036
  relation: 'within',
20912
21037
  },
20913
21038
  boost: 5.0,
@@ -20915,7 +21040,7 @@ class ElasticsearchService {
20915
21040
  }, {
20916
21041
  geo_shape: {
20917
21042
  geom: {
20918
- shape: geometry,
21043
+ shape: boostGeometry,
20919
21044
  relation: 'intersects',
20920
21045
  },
20921
21046
  boost: 2.0,
@@ -20924,7 +21049,7 @@ class ElasticsearchService {
20924
21049
  // this will boost the results variably depending on their distance from the given geometry
20925
21050
  // note: this takes into account the `location` field of a record; this is generally the center of all spatial extents
20926
21051
  // combined, and thus the actual size/coverage of the record spatial extent isn't relevant here
20927
- const bbox = getGeometryBoundingBox(geometry);
21052
+ const bbox = getGeometryBoundingBox(boostGeometry);
20928
21053
  const center = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2];
20929
21054
  const northToCenter = new LineString([
20930
21055
  [center[0], bbox[3]],
@@ -22514,48 +22639,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
22514
22639
  type: Injectable
22515
22640
  }] });
22516
22641
 
22517
- const GEOCODING_PROVIDER = new InjectionToken('geocoding-provider');
22518
- class GeocodingService {
22519
- constructor() {
22520
- this.provider = inject(GEOCODING_PROVIDER);
22521
- }
22522
- query(text) {
22523
- let queryObservable;
22524
- switch (this.provider[0]) {
22525
- case 'geoadmin':
22526
- queryObservable = from(queryGeoadmin(text, this.provider[1]));
22527
- break;
22528
- case 'geonames':
22529
- queryObservable = from(queryGeonames(text, this.provider[1]));
22530
- break;
22531
- case 'data-gouv-fr':
22532
- queryObservable = from(queryDataGouvFr(text, this.provider[1]));
22533
- break;
22534
- default:
22535
- return throwError(() => new Error(`Unsupported geocoding provider: ${this.provider[0]}`));
22536
- }
22537
- return queryObservable.pipe(catchError((error) => throwError(error)));
22538
- }
22539
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
22540
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingService, providedIn: 'root' }); }
22541
- }
22542
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingService, decorators: [{
22543
- type: Injectable,
22544
- args: [{
22545
- providedIn: 'root',
22546
- }]
22547
- }] });
22548
-
22549
22642
  class FeatureMapModule {
22550
22643
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FeatureMapModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
22551
22644
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.19", ngImport: i0, type: FeatureMapModule, imports: [i1.StoreFeatureModule] }); }
22552
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FeatureMapModule, providers: [
22553
- MapFacade,
22554
- {
22555
- provide: GEOCODING_PROVIDER,
22556
- useValue: ['geonames', { maxRows: 5 }],
22557
- },
22558
- ], imports: [StoreModule.forFeature(MAP_FEATURE_KEY, mapReducer)] }); }
22645
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FeatureMapModule, providers: [MapFacade], imports: [StoreModule.forFeature(MAP_FEATURE_KEY, mapReducer)] }); }
22559
22646
  }
22560
22647
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FeatureMapModule, decorators: [{
22561
22648
  type: NgModule,
@@ -22563,13 +22650,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
22563
22650
  imports: [
22564
22651
  StoreModule.forFeature(MAP_FEATURE_KEY, mapReducer),
22565
22652
  ],
22566
- providers: [
22567
- MapFacade,
22568
- {
22569
- provide: GEOCODING_PROVIDER,
22570
- useValue: ['geonames', { maxRows: 5 }],
22571
- },
22572
- ],
22653
+ providers: [MapFacade],
22573
22654
  }]
22574
22655
  }] });
22575
22656
 
@@ -22588,6 +22669,8 @@ const VECTOR_STYLE_DEFAULT = new InjectionToken('vectorStyleDefault', {
22588
22669
  const DEFAULT_BASEMAP_LAYER = {
22589
22670
  type: 'maplibre-style',
22590
22671
  styleUrl: `https://basemaps.cartocdn.com/gl/positron-gl-style/style.json`,
22672
+ clickable: false,
22673
+ hoverable: false,
22591
22674
  };
22592
22675
  const DEFAULT_VIEW = {
22593
22676
  center: [0, 15],
@@ -23010,1058 +23093,352 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
23010
23093
  }]
23011
23094
  }] });
23012
23095
 
23013
- const flatten = (base, obj, keepLevel = false) => Object.keys(obj).reduce((prev, curr) => {
23014
- const path = base ? `${base}.${curr}` : curr;
23015
- const val = obj[curr];
23016
- if (keepLevel) {
23017
- return { ...prev, [path]: flatten('', val) };
23096
+ class AnchorLinkDirective {
23097
+ constructor() {
23098
+ this.changeDetector = inject(ChangeDetectorRef);
23099
+ this.disabled = false;
23100
+ this.observer = new MutationObserver(() => {
23101
+ this.refreshDisabledState();
23102
+ });
23103
+ this.inView = false;
23104
+ this.initialized = false;
23018
23105
  }
23019
- else if (typeof val === 'object' && !Array.isArray(val)) {
23020
- return {
23021
- ...prev,
23022
- ...flatten(path, val),
23023
- };
23106
+ get elementClass() {
23107
+ if (this.disabled) {
23108
+ return this.disabledClass;
23109
+ }
23110
+ if (this.inView) {
23111
+ return `${this.inViewClass} ${this.enabledClass}`;
23112
+ }
23113
+ else {
23114
+ return `${this.outOfViewClass} ${this.enabledClass}`;
23115
+ }
23024
23116
  }
23025
- else {
23026
- return { ...prev, [path]: val };
23117
+ ngOnInit() {
23118
+ this.observer.observe(document.body, {
23119
+ childList: true,
23120
+ subtree: true,
23121
+ });
23122
+ this.refreshDisabledState();
23027
23123
  }
23028
- }, {});
23029
- const checkKeys = (input, mandatory, optional) => {
23030
- const keys = Object.keys(input);
23031
- const missing = mandatory.filter((key) => keys.indexOf(key) === -1);
23032
- const unrecognized = keys.filter((key) => mandatory.indexOf(key) === -1 && optional.indexOf(key) === -1);
23033
- return {
23034
- missing,
23035
- unrecognized,
23036
- };
23037
- };
23038
- /**
23039
- * Only keeps recognized keys, flatten nested values (to allow having "." in keys),
23040
- * pushes warnings and errors to the given arrays; returns null in case of error
23041
- */
23042
- function parseConfigSection(fullConfigObj, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors) {
23043
- if (typeof fullConfigObj[sectionName] !== 'object') {
23044
- if (mandatoryKeys.length === 0)
23045
- return null;
23046
- outErrors.push(`The [${sectionName}] mandatory section is missing.`);
23047
- return null;
23124
+ ngAfterViewChecked() {
23125
+ if (!this.initialized && !this.disabled) {
23126
+ const target = document.getElementById(this.targetId);
23127
+ if (target) {
23128
+ this.initializeIntersectionObserver(target);
23129
+ this.initialized = true;
23130
+ }
23131
+ }
23048
23132
  }
23049
- const sectionConf = fullConfigObj[sectionName];
23050
- const keysCheck = checkKeys(sectionConf, mandatoryKeys, optionalKeys);
23051
- if (keysCheck.missing.length) {
23052
- // note: this is not thrown to allow merging several Errors down the line
23053
- outErrors.push(`In the [${sectionName}] section: ${keysCheck.missing.join(', ')}`);
23054
- return null;
23133
+ initializeIntersectionObserver(target) {
23134
+ this.intersectionObserver = new IntersectionObserver((entries) => {
23135
+ entries.forEach((entry) => {
23136
+ this.inView = entry.isIntersecting;
23137
+ this.changeDetector.detectChanges();
23138
+ });
23139
+ }, {
23140
+ root: null,
23141
+ rootMargin: '-30% 0% -60% 0%',
23142
+ });
23143
+ this.intersectionObserver.observe(target);
23055
23144
  }
23056
- else if (keysCheck.unrecognized.length) {
23057
- outWarnings.push(`In the [${sectionName}] section: ${keysCheck.unrecognized.join(', ')}`);
23058
- keysCheck.unrecognized.forEach((key) => delete sectionConf[key]);
23145
+ ngOnDestroy() {
23146
+ this.observer.disconnect();
23147
+ if (this.intersectionObserver) {
23148
+ this.intersectionObserver.disconnect();
23149
+ }
23059
23150
  }
23060
- return sectionConf;
23151
+ refreshDisabledState() {
23152
+ const targetNotPresent = !document.getElementById(this.targetId);
23153
+ if (targetNotPresent !== this.disabled) {
23154
+ this.disabled = targetNotPresent;
23155
+ this.changeDetector.detectChanges();
23156
+ }
23157
+ }
23158
+ scrollToTarget() {
23159
+ const target = document.getElementById(this.targetId);
23160
+ if (!target)
23161
+ return;
23162
+ target.scrollIntoView({
23163
+ behavior: 'smooth',
23164
+ block: 'start',
23165
+ });
23166
+ }
23167
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AnchorLinkDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
23168
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: AnchorLinkDirective, isStandalone: true, selector: "[gnUiAnchorLink]", inputs: { targetId: ["gnUiAnchorLink", "targetId"], disabledClass: ["gnUiAnchorLinkDisabledClass", "disabledClass"], enabledClass: ["gnUiAnchorLinkEnabledClass", "enabledClass"], inViewClass: ["gnUiAnchorLinkInViewClass", "inViewClass"], outOfViewClass: ["gnUiAnchorLinkOutOfViewClass", "outOfViewClass"] }, host: { listeners: { "click": "scrollToTarget()" }, properties: { "class": "this.elementClass" } }, ngImport: i0 }); }
23061
23169
  }
23062
- /**
23063
- * Same as parseConfigSection but keeps all keys, recognized or not
23064
- * Also the first level is the language code (e.g. 'en')
23065
- * This is optional
23066
- */
23067
- function parseTranslationsConfigSection(fullConfigObj, sectionName) {
23068
- if (typeof fullConfigObj[sectionName] !== 'object') {
23069
- return {};
23170
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AnchorLinkDirective, decorators: [{
23171
+ type: Directive,
23172
+ args: [{
23173
+ selector: '[gnUiAnchorLink]',
23174
+ standalone: true,
23175
+ }]
23176
+ }], propDecorators: { targetId: [{
23177
+ type: Input,
23178
+ args: ['gnUiAnchorLink']
23179
+ }], disabledClass: [{
23180
+ type: Input,
23181
+ args: ['gnUiAnchorLinkDisabledClass']
23182
+ }], enabledClass: [{
23183
+ type: Input,
23184
+ args: ['gnUiAnchorLinkEnabledClass']
23185
+ }], inViewClass: [{
23186
+ type: Input,
23187
+ args: ['gnUiAnchorLinkInViewClass']
23188
+ }], outOfViewClass: [{
23189
+ type: Input,
23190
+ args: ['gnUiAnchorLinkOutOfViewClass']
23191
+ }], elementClass: [{
23192
+ type: HostBinding,
23193
+ args: ['class']
23194
+ }], scrollToTarget: [{
23195
+ type: HostListener,
23196
+ args: ['click']
23197
+ }] } });
23198
+
23199
+ class PaginationDotsComponent {
23200
+ constructor() {
23201
+ this.containerClass = '';
23070
23202
  }
23071
- return flatten('', fullConfigObj[sectionName], true);
23203
+ // 1-based
23204
+ get steps() {
23205
+ return Array.from({ length: this.listComponent.pagesCount }, (_, i) => i + 1);
23206
+ }
23207
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PaginationDotsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23208
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: PaginationDotsComponent, isStandalone: true, selector: "gn-ui-pagination-dots", inputs: { listComponent: "listComponent", containerClass: "containerClass" }, ngImport: i0, template: "@if (listComponent.pagesCount > 1) {\n <div\n class=\"overflow-hidden flex flex-row justify-center gap-[14px] p-1\"\n [ngClass]=\"containerClass\"\n >\n @for (step of steps; track step) {\n <button\n type=\"button\"\n class=\"pagination-dot\"\n (click)=\"listComponent.goToPage(step)\"\n [ngClass]=\"\n listComponent.currentPage === step ? 'bg-primary' : 'bg-gray-400'\n \"\n ></button>\n }\n </div>\n}\n", styles: [".pagination-dot{width:6px;height:6px;border-radius:6px;position:relative;flex-shrink:0}.pagination-dot:after{content:\"\";position:absolute;left:-7px;top:-7px;width:20px;height:20px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], viewProviders: [
23209
+ provideIcons({
23210
+ iconoirNavArrowRight,
23211
+ iconoirNavArrowLeft,
23212
+ }),
23213
+ ] }); }
23072
23214
  }
23073
- /**
23074
- * Same as parseConfigSection but handles an array of configs; this is
23075
- * considered optional; returns null in case of error in at least one of
23076
- * the sections
23077
- */
23078
- function parseMultiConfigSection(fullConfigObj, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors) {
23079
- const sections = fullConfigObj[sectionName];
23080
- if (!sections) {
23081
- return [];
23215
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PaginationDotsComponent, decorators: [{
23216
+ type: Component,
23217
+ args: [{ selector: 'gn-ui-pagination-dots', standalone: true, imports: [CommonModule], viewProviders: [
23218
+ provideIcons({
23219
+ iconoirNavArrowRight,
23220
+ iconoirNavArrowLeft,
23221
+ }),
23222
+ ], template: "@if (listComponent.pagesCount > 1) {\n <div\n class=\"overflow-hidden flex flex-row justify-center gap-[14px] p-1\"\n [ngClass]=\"containerClass\"\n >\n @for (step of steps; track step) {\n <button\n type=\"button\"\n class=\"pagination-dot\"\n (click)=\"listComponent.goToPage(step)\"\n [ngClass]=\"\n listComponent.currentPage === step ? 'bg-primary' : 'bg-gray-400'\n \"\n ></button>\n }\n </div>\n}\n", styles: [".pagination-dot{width:6px;height:6px;border-radius:6px;position:relative;flex-shrink:0}.pagination-dot:after{content:\"\";position:absolute;left:-7px;top:-7px;width:20px;height:20px}\n"] }]
23223
+ }], propDecorators: { listComponent: [{
23224
+ type: Input
23225
+ }], containerClass: [{
23226
+ type: Input
23227
+ }] } });
23228
+
23229
+ class CarouselComponent {
23230
+ constructor() {
23231
+ this.changeDetector = inject(ChangeDetectorRef);
23232
+ this.containerClass = '';
23233
+ this.stepsContainerClass = 'w-full bottom-0 top-auto';
23234
+ this.currentStepChange = new EventEmitter();
23235
+ this.steps = [];
23236
+ this.currentStep = 0;
23237
+ this.refreshSteps = () => {
23238
+ this.steps = this.emblaApi.scrollSnapList();
23239
+ this.currentStep = this.emblaApi.selectedScrollSnap();
23240
+ this.currentStepChange.emit(this.currentStep);
23241
+ this.changeDetector.detectChanges();
23242
+ };
23082
23243
  }
23083
- if (!Array.isArray(sections)) {
23084
- outErrors.push(`Expected [[${sectionName}]] to be an array of sections.`);
23085
- return null;
23244
+ // Paginable API
23245
+ get isFirstPage() {
23246
+ return this.currentStep === 0;
23086
23247
  }
23087
- const result = sections.map((section) => parseConfigSection({ [sectionName]: section }, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors));
23088
- if (result.some((r) => r === null)) {
23089
- return null;
23248
+ get isLastPage() {
23249
+ return this.currentStep === this.steps.length - 1;
23090
23250
  }
23091
- return result;
23092
- }
23093
- function checkMetadataLanguage(parsedConfigSection, outWarnings) {
23094
- const lang2 = toLang2(parsedConfigSection.metadata_language.toLowerCase());
23095
- if (lang2?.length !== 2) {
23096
- outWarnings.push(`In the [global] section: metadata_language = "${parsedConfigSection.metadata_language}" is not a recognized ISO 639 language code`);
23251
+ get currentPage() {
23252
+ return this.currentStep + 1; // this is 1-based
23097
23253
  }
23098
- return parsedConfigSection;
23099
- }
23100
- function checkNewRecordDefaultLanguage(parsedConfigSection, outWarnings) {
23101
- const lang2 = toLang2(parsedConfigSection.new_record_default_language.toLowerCase());
23102
- if (!(lang2 in LANG_2_TO_3_MAPPER)) {
23103
- outWarnings.push(`In the [editing] section: new_record_default_language = "${parsedConfigSection.new_record_default_language}" is not a recognized ISO 639 language code`);
23104
- return {
23105
- ...parsedConfigSection,
23106
- new_record_default_language: undefined,
23107
- };
23254
+ get pagesCount() {
23255
+ return this.steps.length;
23108
23256
  }
23109
- return {
23110
- ...parsedConfigSection,
23111
- new_record_default_language: lang2,
23112
- };
23113
- }
23114
- function checkNewRecordStandard(parsedConfigSection, outWarnings) {
23115
- const standard = parsedConfigSection.new_record_standard;
23116
- const normalizedStandard = typeof standard === 'string' ? standard.trim().toLowerCase() : null;
23117
- if (normalizedStandard === 'iso19139' ||
23118
- normalizedStandard === 'iso19115-3') {
23119
- return {
23120
- ...parsedConfigSection,
23121
- new_record_standard: normalizedStandard,
23122
- };
23257
+ goToPage(stepIndex) {
23258
+ this.emblaApi.scrollTo(stepIndex - 1); // this is 0-based
23123
23259
  }
23124
- outWarnings.push(`In the [editing] section: new_record_standard = "${standard}" is not a supported metadata standard`);
23125
- return {
23126
- ...parsedConfigSection,
23127
- new_record_standard: undefined,
23128
- };
23260
+ goToPrevPage() {
23261
+ if (this.isFirstPage)
23262
+ return;
23263
+ this.emblaApi.scrollPrev();
23264
+ }
23265
+ goToNextPage() {
23266
+ if (this.isLastPage)
23267
+ return;
23268
+ this.emblaApi.scrollNext();
23269
+ }
23270
+ ngAfterViewInit() {
23271
+ this.emblaApi = EmblaCarousel(this.carouselOverflowContainer.nativeElement, {
23272
+ duration: 15,
23273
+ });
23274
+ this.emblaApi
23275
+ .on('init', this.refreshSteps)
23276
+ .on('reInit', this.refreshSteps)
23277
+ .on('select', this.refreshSteps);
23278
+ }
23279
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CarouselComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23280
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: CarouselComponent, isStandalone: true, selector: "gn-ui-carousel", inputs: { containerClass: "containerClass", stepsContainerClass: "stepsContainerClass" }, outputs: { currentStepChange: "currentStepChange" }, viewQueries: [{ propertyName: "carouselOverflowContainer", first: true, predicate: ["carouselOverflowContainer"], descendants: true }], ngImport: i0, template: "<div #carouselOverflowContainer class=\"w-full block\">\n <div class=\"carousel-container flex\" [ngClass]=\"containerClass\">\n <ng-content></ng-content>\n </div>\n</div>\n<gn-ui-pagination-dots\n [containerClass]=\"'absolute ' + stepsContainerClass\"\n [listComponent]=\"this\"\n></gn-ui-pagination-dots>\n", styles: [":host{position:relative}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: PaginationDotsComponent, selector: "gn-ui-pagination-dots", inputs: ["listComponent", "containerClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
23129
23281
  }
23282
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CarouselComponent, decorators: [{
23283
+ type: Component,
23284
+ args: [{ selector: 'gn-ui-carousel', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, PaginationDotsComponent], template: "<div #carouselOverflowContainer class=\"w-full block\">\n <div class=\"carousel-container flex\" [ngClass]=\"containerClass\">\n <ng-content></ng-content>\n </div>\n</div>\n<gn-ui-pagination-dots\n [containerClass]=\"'absolute ' + stepsContainerClass\"\n [listComponent]=\"this\"\n></gn-ui-pagination-dots>\n", styles: [":host{position:relative}\n"] }]
23285
+ }], propDecorators: { carouselOverflowContainer: [{
23286
+ type: ViewChild,
23287
+ args: ['carouselOverflowContainer']
23288
+ }], containerClass: [{
23289
+ type: Input
23290
+ }], stepsContainerClass: [{
23291
+ type: Input
23292
+ }], currentStepChange: [{
23293
+ type: Output
23294
+ }] } });
23130
23295
 
23131
- /**
23132
- * This loader extends the default one based on JSON files but also loads custom translations
23133
- * defined in the app configuration
23134
- * Implements a fallback on default lang if translated labels are empty
23135
- */
23136
- class FileWithOverridesTranslateLoader extends FileTranslateLoader {
23137
- getTranslation(lang) {
23138
- const baseLang = lang.substring(0, 2); // removing the right part of e.g. en_EN
23139
- return super.getTranslation(baseLang).pipe(map$1((translations) => ({
23140
- ...translations,
23141
- ...getCustomTranslations(baseLang),
23142
- })));
23296
+ class ProgressBarComponent {
23297
+ constructor() {
23298
+ this.value = 0;
23299
+ this.type = 'default';
23300
+ }
23301
+ get progress() {
23302
+ return this.value > 0 ? (this.value < 100 ? this.value : 100) : 0;
23303
+ }
23304
+ get color() {
23305
+ switch (this.type) {
23306
+ case 'default':
23307
+ return {
23308
+ outerBar: 'bg-gray-200',
23309
+ innerBar: 'bg-gray-100',
23310
+ text: 'text-gray-900',
23311
+ };
23312
+ case 'primary':
23313
+ return {
23314
+ outerBar: 'bg-primary',
23315
+ innerBar: 'bg-primary-lighter',
23316
+ text: 'text-white',
23317
+ };
23318
+ case 'secondary':
23319
+ return {
23320
+ outerBar: 'bg-secondary',
23321
+ innerBar: 'bg-secondary-lighter',
23322
+ text: 'text-white',
23323
+ };
23324
+ case 'light':
23325
+ return {
23326
+ outerBar: 'bg-primary-white',
23327
+ innerBar: 'bg-primary',
23328
+ text: 'text-main',
23329
+ };
23330
+ }
23143
23331
  }
23332
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProgressBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23333
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: ProgressBarComponent, isStandalone: true, selector: "gn-ui-progress-bar", inputs: { value: "value", type: "type" }, ngImport: i0, template: "@switch (type) {\n <!-- Light Theme -->\n @case ('light') {\n <div class=\"flex items-center relative\">\n <div\n class=\"flex-shrink-0 {{ color.text }} font-medium mr-2\n text-opacity-100 !text-slate-800\"\n data-cy=\"progressPercentage\"\n >\n {{ progress }}%\n </div>\n <div class=\"flex-grow h-[4px] w-[52px] {{ color.outerBar }} rounded-full\">\n <div\n [style.width.%]=\"progress\"\n class=\"{{ color.innerBar }} transition-width duration-500\n ease-in-out rounded-full shadow-sm w-[52px] h-[4px]\"\n ></div>\n </div>\n </div>\n }\n <!-- Default / Primary / Secondary Themes -->\n @default {\n <div class=\"flex h-full {{ color.outerBar }} rounded-t-lg rounded-b-lg\">\n <div\n [style.width.%]=\"progress\"\n class=\"flex {{ color.innerBar }} my-1 mx-1 transition-width\n duration-500 ease-in-out rounded-t-md rounded-b-md shadow-xl\"\n >\n <div\n class=\"flex items-center pl-2 py-1 {{ color.text }} font-bold text-4\"\n >\n {{ progress }}%\n </div>\n </div>\n </div>\n }\n}\n", styles: [".font-bold{font-weight:var(--progress-bar-font-weight, \"bold\")}\n"] }); }
23144
23334
  }
23335
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProgressBarComponent, decorators: [{
23336
+ type: Component,
23337
+ args: [{ selector: 'gn-ui-progress-bar', standalone: true, imports: [], template: "@switch (type) {\n <!-- Light Theme -->\n @case ('light') {\n <div class=\"flex items-center relative\">\n <div\n class=\"flex-shrink-0 {{ color.text }} font-medium mr-2\n text-opacity-100 !text-slate-800\"\n data-cy=\"progressPercentage\"\n >\n {{ progress }}%\n </div>\n <div class=\"flex-grow h-[4px] w-[52px] {{ color.outerBar }} rounded-full\">\n <div\n [style.width.%]=\"progress\"\n class=\"{{ color.innerBar }} transition-width duration-500\n ease-in-out rounded-full shadow-sm w-[52px] h-[4px]\"\n ></div>\n </div>\n </div>\n }\n <!-- Default / Primary / Secondary Themes -->\n @default {\n <div class=\"flex h-full {{ color.outerBar }} rounded-t-lg rounded-b-lg\">\n <div\n [style.width.%]=\"progress\"\n class=\"flex {{ color.innerBar }} my-1 mx-1 transition-width\n duration-500 ease-in-out rounded-t-md rounded-b-md shadow-xl\"\n >\n <div\n class=\"flex items-center pl-2 py-1 {{ color.text }} font-bold text-4\"\n >\n {{ progress }}%\n </div>\n </div>\n </div>\n }\n}\n", styles: [".font-bold{font-weight:var(--progress-bar-font-weight, \"bold\")}\n"] }]
23338
+ }], propDecorators: { value: [{
23339
+ type: Input
23340
+ }], type: [{
23341
+ type: Input
23342
+ }] } });
23145
23343
 
23146
- const MISSING_CONFIG_ERROR = `Application configuration was not initialized correctly.
23147
- This error might show up in case of an invalid/malformed configuration file.
23148
-
23149
- Note: make sure that you have called \`loadAppConfig\` from '../../../../../libs/util/app-config/src' before starting the Angular application.`;
23150
- let globalConfig = null;
23151
- function getGlobalConfig() {
23152
- if (globalConfig === null)
23153
- throw new Error(MISSING_CONFIG_ERROR);
23154
- return globalConfig;
23155
- }
23156
- let themeConfig = null;
23157
- function getThemeConfig() {
23158
- if (themeConfig === null)
23159
- throw new Error(MISSING_CONFIG_ERROR);
23160
- return themeConfig;
23161
- }
23162
- let mapConfig = null;
23163
- function getOptionalMapConfig() {
23164
- return mapConfig;
23165
- }
23166
- let searchConfig = null;
23167
- function getOptionalSearchConfig() {
23168
- return searchConfig;
23169
- }
23170
- let editorConfig = null;
23171
- function getOptionalEditorConfig() {
23172
- return editorConfig;
23173
- }
23174
- let metadataQualityConfig = null;
23175
- function getMetadataQualityConfig() {
23176
- return (metadataQualityConfig ||
23177
- {
23178
- ENABLED: false,
23344
+ class PopoverComponent {
23345
+ constructor() {
23346
+ this.viewContainerRef = inject(ViewContainerRef);
23347
+ this.renderer = inject(Renderer2);
23348
+ }
23349
+ getContent() {
23350
+ if (this.content instanceof TemplateRef) {
23351
+ if (this.view) {
23352
+ this.view.destroy();
23353
+ }
23354
+ this.view = this.viewContainerRef.createEmbeddedView(this.content);
23355
+ this.view.detectChanges();
23356
+ const wrapper = this.renderer.createElement('div'); // Create a wrapper div
23357
+ this.view.rootNodes.forEach((node) => {
23358
+ this.renderer.appendChild(wrapper, node); // Append each root node to the wrapper
23359
+ });
23360
+ return wrapper;
23361
+ }
23362
+ return this.content;
23363
+ }
23364
+ ngAfterViewInit() {
23365
+ this.tippyInstance = tippy(this.popoverContent.nativeElement, {
23366
+ content: this.getContent(),
23367
+ allowHTML: true,
23368
+ theme: this.theme,
23179
23369
  });
23180
- }
23181
- let customTranslations = null;
23182
- function getCustomTranslations(langCode) {
23183
- if (customTranslations === null)
23184
- throw new Error(MISSING_CONFIG_ERROR);
23185
- return langCode in customTranslations ? customTranslations[langCode] : {};
23186
- }
23187
- let appConfigLoaded = false;
23188
- function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
23189
- console.log(`[geonetwork-ui] Loading application configuration from ${configUrl}`);
23190
- return fetch(configUrl)
23191
- .then((resp) => {
23192
- if (!resp.ok)
23193
- throw new Error('Configuration file could not be loaded');
23194
- return resp.text();
23195
- })
23196
- .then((conf) => {
23197
- let parsed;
23198
- try {
23199
- parsed = TOML.parse(conf, { joiner: '\n', bigint: false });
23200
- }
23201
- catch (e) {
23202
- throw new Error(`An error occurred when parsing the configuration file: ${e.message}`);
23203
- }
23204
- const errors = [];
23205
- const warnings = [];
23206
- let parsedGlobalSection = parseConfigSection(parsed, 'global', ['geonetwork4_api_url'], [
23207
- 'datahub_url',
23208
- 'edit_url_template',
23209
- 'proxy_path',
23210
- 'metadata_language',
23211
- 'login_url',
23212
- 'logout_url',
23213
- 'settings_url',
23214
- 'web_component_embedder_url',
23215
- 'languages',
23216
- 'contact_email',
23217
- 'reuse_form_url',
23218
- ], warnings, errors);
23219
- if (parsedGlobalSection?.metadata_language) {
23220
- parsedGlobalSection = checkMetadataLanguage(parsedGlobalSection, warnings);
23221
- }
23222
- globalConfig =
23223
- parsedGlobalSection === null
23224
- ? null
23225
- : {
23226
- GN4_API_URL: parsedGlobalSection.geonetwork4_api_url,
23227
- DATAHUB_URL: parsedGlobalSection.datahub_url,
23228
- EDIT_URL_TEMPLATE: parsedGlobalSection.edit_url_template,
23229
- PROXY_PATH: parsedGlobalSection.proxy_path,
23230
- METADATA_LANGUAGE: parsedGlobalSection.metadata_language
23231
- ? parsedGlobalSection.metadata_language.toLowerCase()
23232
- : undefined,
23233
- LOGIN_URL: parsedGlobalSection.login_url,
23234
- LOGOUT_URL: parsedGlobalSection.logout_url,
23235
- SETTINGS_URL: parsedGlobalSection.settings_url,
23236
- WEB_COMPONENT_EMBEDDER_URL: parsedGlobalSection.web_component_embedder_url,
23237
- LANGUAGES: parsedGlobalSection.languages,
23238
- CONTACT_EMAIL: parsedGlobalSection.contact_email,
23239
- REUSE_FORM_URL: parsedGlobalSection.reuse_form_url,
23240
- };
23241
- const parsedLayersSections = parseMultiConfigSection(parsed, 'map_layer', ['type'], ['name', 'url', 'data', 'styleUrl', 'accessToken'], warnings, errors);
23242
- const parsedMapSection = parseConfigSection(parsed, 'map', [], [
23243
- 'max_zoom',
23244
- 'do_not_tile_wms',
23245
- 'max_extent',
23246
- 'max_feature_count',
23247
- 'baselayer',
23248
- 'do_not_use_default_basemap',
23249
- 'external_viewer_url_template',
23250
- 'external_viewer_open_new_tab',
23251
- ], warnings, errors);
23252
- mapConfig =
23253
- parsedMapSection === null || parsedLayersSections === null
23254
- ? null
23255
- : {
23256
- MAX_ZOOM: parsedMapSection.max_zoom,
23257
- DO_NOT_TILE_WMS: parsedMapSection.do_not_tile_wms,
23258
- MAX_EXTENT: parsedMapSection.max_extent,
23259
- MAX_FEATURE_COUNT: parsedMapSection.max_feature_count,
23260
- EXTERNAL_VIEWER_URL_TEMPLATE: parsedMapSection.external_viewer_url_template,
23261
- EXTERNAL_VIEWER_OPEN_NEW_TAB: parsedMapSection.external_viewer_open_new_tab,
23262
- DO_NOT_USE_DEFAULT_BASEMAP: !!parsedMapSection.do_not_use_default_basemap,
23263
- MAP_LAYERS: parsedLayersSections.map((map_layer) => ({
23264
- TYPE: map_layer.type,
23265
- URL: map_layer.url,
23266
- NAME: map_layer.name,
23267
- DATA: map_layer.data,
23268
- STYLE_URL: map_layer.styleUrl,
23269
- ACCESS_TOKEN: map_layer.accessToken,
23270
- })),
23271
- };
23272
- const parsedThemeSection = parseConfigSection(parsed, 'theme', ['primary_color', 'secondary_color', 'main_color', 'background_color'], [
23273
- 'header_foreground_color',
23274
- 'main_font',
23275
- 'title_font',
23276
- 'fonts_stylesheet_url',
23277
- 'thumbnail_placeholder',
23278
- 'header_background',
23279
- 'favicon',
23280
- ], warnings, errors);
23281
- themeConfig =
23282
- parsedThemeSection === null
23283
- ? null
23284
- : {
23285
- PRIMARY_COLOR: parsedThemeSection.primary_color,
23286
- SECONDARY_COLOR: parsedThemeSection.secondary_color,
23287
- MAIN_COLOR: parsedThemeSection.main_color,
23288
- BACKGROUND_COLOR: parsedThemeSection.background_color,
23289
- HEADER_FOREGROUND_COLOR: parsedThemeSection.header_foreground_color,
23290
- THUMBNAIL_PLACEHOLDER: parsedThemeSection.thumbnail_placeholder,
23291
- HEADER_BACKGROUND: parsedThemeSection.header_background,
23292
- TITLE_FONT: parsedThemeSection.title_font,
23293
- MAIN_FONT: parsedThemeSection.main_font,
23294
- FONTS_STYLESHEET_URL: parsedThemeSection.fonts_stylesheet_url,
23295
- FAVICON: parsedThemeSection.favicon,
23296
- };
23297
- const parsedSearchSection = parseConfigSection(parsed, 'search', [], [
23298
- 'record_kind_quick_filter',
23299
- 'filter_geometry_data',
23300
- 'filter_geometry_url',
23301
- 'do_not_use_default_search_preset',
23302
- 'search_preset',
23303
- 'advanced_filters',
23304
- 'limit',
23305
- ], warnings, errors);
23306
- const parsedSearchParams = parseMultiConfigSection(parsed, 'search_preset', ['name'], ['sort', 'filters'], warnings, errors);
23307
- searchConfig =
23308
- parsedSearchSection === null
23309
- ? null
23310
- : {
23311
- RECORD_KIND_QUICK_FILTER: parsedSearchSection.record_kind_quick_filter,
23312
- FILTER_GEOMETRY_DATA: parsedSearchSection.filter_geometry_data,
23313
- FILTER_GEOMETRY_URL: parsedSearchSection.filter_geometry_url,
23314
- DO_NOT_USE_DEFAULT_SEARCH_PRESET: !!parsedSearchSection.do_not_use_default_search_preset,
23315
- SEARCH_PRESET: parsedSearchParams.map((param) => ({
23316
- sort: param.sort,
23317
- name: param.name,
23318
- filters: param.filters,
23319
- })),
23320
- ADVANCED_FILTERS: parsedSearchSection.advanced_filters,
23321
- LIMIT: parsedSearchSection.limit,
23322
- };
23323
- const parsedMetadataQualitySection = parseConfigSection(parsed, 'metadata-quality', [], ['enabled'], warnings, errors);
23324
- metadataQualityConfig =
23325
- parsedMetadataQualitySection === null
23326
- ? null
23327
- : {
23328
- ENABLED: parsedMetadataQualitySection.enabled,
23329
- SORTABLE: parsedMetadataQualitySection.sortable,
23330
- };
23331
- let parsedEditingSection = parseConfigSection(parsed, 'editing', [], ['new_record_default_language', 'new_record_standard'], warnings, errors);
23332
- if (parsedEditingSection !== null &&
23333
- parsedEditingSection.new_record_default_language !== undefined) {
23334
- parsedEditingSection = checkNewRecordDefaultLanguage(parsedEditingSection, warnings);
23370
+ }
23371
+ ngOnChanges(changes) {
23372
+ if (changes['theme']) {
23373
+ this.theme = changes['theme'].currentValue;
23374
+ if (this.tippyInstance) {
23375
+ this.tippyInstance.setProps({ theme: this.theme });
23376
+ }
23335
23377
  }
23336
- if (parsedEditingSection !== null &&
23337
- parsedEditingSection.new_record_standard !== undefined) {
23338
- parsedEditingSection = checkNewRecordStandard(parsedEditingSection, warnings);
23378
+ if (changes['content']) {
23379
+ this.content = changes['content'].currentValue;
23380
+ if (this.tippyInstance) {
23381
+ this.tippyInstance.setContent(this.getContent());
23382
+ }
23339
23383
  }
23340
- editorConfig =
23341
- parsedEditingSection === null
23342
- ? null
23343
- : {
23344
- NEW_RECORD_DEFAULT_LANGUAGE: parsedEditingSection.new_record_default_language,
23345
- NEW_RECORD_STANDARD: parsedEditingSection.new_record_standard,
23346
- };
23347
- customTranslations = parseTranslationsConfigSection(parsed, 'translations');
23348
- if (errors.length) {
23349
- throw new Error(`One or more mandatory settings were missing from the configuration file.
23350
- ${errors.join('\n')}`);
23384
+ }
23385
+ ngOnDestroy() {
23386
+ if (this.tippyInstance) {
23387
+ this.tippyInstance.destroy();
23351
23388
  }
23352
- else if (warnings.length) {
23353
- console.warn(`One or more unexpected settings were encountered in the configuration file.
23354
- ${warnings.join('\n')}`);
23389
+ if (this.view) {
23390
+ this.view.destroy();
23355
23391
  }
23356
- appConfigLoaded = true;
23357
- });
23358
- }
23359
- function isConfigLoaded() {
23360
- return appConfigLoaded;
23361
- }
23362
- function _reset() {
23363
- globalConfig = null;
23364
- themeConfig = null;
23365
- editorConfig = null;
23366
- customTranslations = null;
23367
- }
23368
- const TRANSLATE_WITH_OVERRIDES_CONFIG = {
23369
- compiler: {
23370
- provide: TranslateCompiler,
23371
- useClass: TranslateMessageFormatCompiler,
23372
- },
23373
- loader: [
23374
- {
23375
- provide: TRANSLATE_HTTP_LOADER_CONFIG,
23376
- useValue: {
23377
- prefix: './assets/i18n/',
23378
- suffix: '.json',
23379
- },
23380
- },
23381
- {
23382
- provide: TranslateLoader,
23383
- useClass: FileWithOverridesTranslateLoader,
23384
- deps: [HttpClient, TRANSLATE_HTTP_LOADER_CONFIG],
23385
- },
23386
- ],
23387
- };
23388
-
23389
- const okAppConfigFixture = () => `
23390
- [global]
23391
- geonetwork4_api_url = "/geonetwork/srv/api"
23392
- proxy_path = "/proxy/?url="
23393
- metadata_language = "fre"
23394
- login_url = "/cas/login?service="
23395
- logout_url = "/geonetwork/signout"
23396
- settings_url = "/geonetwork/settings"
23397
- web_component_embedder_url = "/datahub/wc-embedder.html"
23398
-
23399
- [map]
23400
- max_zoom = 10
23401
- max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855]
23402
- do_not_use_default_basemap = false
23403
- external_viewer_url_template = 'https://example.com/myviewer?'
23404
- external_viewer_open_new_tab = true
23405
- [[map_layer]]
23406
- type = "wms"
23407
- url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
23408
- name = "masque_hdf_ign_carto_latin1"
23409
- [[map_layer]]
23410
- type = "wfs"
23411
- url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
23412
- name = "masque_hdf_ign_carto_latin1"
23413
-
23414
- [theme]
23415
- primary_color = "#093564"
23416
- secondary_color = "#c2e9dc"
23417
- main_color = "#212029" # All-purpose text color
23418
- background_color = "#fdfbff"
23419
- thumbnail_placeholder = 'assets/img/placeholder.svg'
23420
- header_background = 'teal'
23421
- header_foreground_color = "#872e2e"
23422
- main_font = 'sans-serif'
23423
- title_font = 'serif'
23424
- fonts_stylesheet_url = "https://fonts.googleapis.com/css2?family=Open+Sans"
23425
-
23426
- [search]
23427
- record_kind_quick_filter = false
23428
- filter_geometry_url = 'https://my.domain.org/geom.json'
23429
- do_not_use_default_search_preset = false
23430
- advanced_filters = ['publicationYear', 'documentStandard', 'inspireKeyword', 'topic', 'license']
23431
-
23432
- [[search_preset]]
23433
- sort = "-createDate"
23434
- name = 'filterByOrgs'
23435
- filters.q = 'Carto'
23436
- filters.organisation = ['Org1', 'Org2']
23437
- filters.format = ['ESRI Shapefile']
23438
- filters.topic = ['boundaries']
23439
- filters.publicationYear = ['2023', '2022']
23440
- filters.spatial = ['yes']
23441
-
23442
- [translations.en]
23443
- "my.first.key" = 'First label.'
23444
- "my.second.key" = """
23445
- Second label,
23446
- on two lines."""
23447
-
23448
- [translations.de]
23449
- my.first.key = 'Erste Etikett.'
23450
-
23451
- [translations.fr]
23452
- my.sample.text = "Un bon exemple de texte."
23453
- "my.quoted.text" = 'du texte entre guillements.'
23454
- `;
23455
- const appConfigWithTranslationFixture = () => okAppConfigFixture();
23456
- const minimalAppConfigFixture = () => `
23457
- [global]
23458
- geonetwork4_api_url = "/geonetwork/srv/api"
23459
- proxy_path = "/proxy/?url="
23460
-
23461
- [theme]
23462
- primary_color = "#093564"
23463
- secondary_color = "#c2e9dc"
23464
- main_color = "#212029" # All-purpose text color
23465
- background_color = "#fdfbff"
23466
- `;
23467
- const mapConfigFixture = () => ({
23468
- MAX_ZOOM: 10,
23469
- MAX_EXTENT: [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855],
23470
- DO_NOT_USE_DEFAULT_BASEMAP: false,
23471
- DO_NOT_TILE_WMS: false,
23472
- EXTERNAL_VIEWER_URL_TEMPLATE: 'https://example.com/myviewer/#/?actions=[{"type":"CATALOG:ADD_LAYERS_FROM_CATALOGS","layers":["${layer_name}"],"sources":[{"url":"${service_url}","type":"${service_type}"}]}]',
23473
- EXTERNAL_VIEWER_OPEN_NEW_TAB: true,
23474
- MAP_LAYERS: [
23475
- {
23476
- TYPE: 'xyz',
23477
- URL: 'https://some-basemap-server',
23478
- },
23479
- {
23480
- TYPE: 'wms',
23481
- URL: 'https://some-wms-server',
23482
- NAME: 'some_layername',
23483
- },
23484
- {
23485
- TYPE: 'wfs',
23486
- URL: 'https://some-wfs-server',
23487
- NAME: 'some_layername',
23488
- },
23489
- ],
23490
- });
23491
- const malformedConfigFixture = () => `
23492
- {
23493
- "I thought": "we were still doing json"
23392
+ }
23393
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23394
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: PopoverComponent, isStandalone: true, selector: "gn-ui-popover", inputs: { content: "content", theme: "theme" }, viewQueries: [{ propertyName: "popoverContent", first: true, predicate: ["popoverContent"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<span #popoverContent>\n <ng-content></ng-content>\n</span>\n", styles: [""] }); }
23494
23395
  }
23495
- `;
23496
- const missingMandatoryConfigFixture = () => `
23497
- [theme]
23498
- primary_color = "#093564"
23499
- secondary_color = "#c2e9dc"
23500
- background_color = "#fdfbff"
23501
- main_font = 'sans-serif'
23502
- `;
23503
- const wrongLanguageCodeConfigFixture = () => `
23504
- [global]
23505
- geonetwork4_api_url = "/geonetwork/srv/api"
23506
- proxy_path = "/proxy/?url="
23507
- metadata_language = "abc"
23508
-
23509
- [map]
23510
-
23511
- [theme]
23512
- primary_color = "#093564"
23513
- secondary_color = "#c2e9dc"
23514
- main_color = "#212029" # All-purpose text color
23515
- background_color = "#fdfbff"
23516
- `;
23517
- const unrecognizedKeysConfigFixture = () => `
23518
- [global]
23519
- geonetwork4_api_url = "/geonetwork/srv/api"
23520
- proxy_path = "/proxy/?url="
23521
- metadata_language = "fre"
23522
- another_path = '/whatever'
23523
-
23524
- [map]
23525
- max_zoom = 10
23526
- max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855]
23527
- do_not_use_default_basemap = false
23528
- external_viewer_url_template = 'https://example.com/myviewer?'
23529
- external_viewer_open_new_tab = true
23530
- another_zoom = 15
23531
- [[map_layer]]
23532
- type = "wms"
23533
- url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
23534
- name = "masque_hdf_ign_carto_latin1"
23535
- [[map_layer]]
23536
- type = "wfs"
23537
- url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
23538
- name = "masque_hdf_ign_carto_latin1"
23539
- another_layer = "wrong layer definition"
23540
-
23541
- [theme]
23542
- primary_color = "#093564"
23543
- secondary_color = "#c2e9dc"
23544
- main_color = "#212029" # All-purpose text color
23545
- background_color = "#fdfbff"
23546
- thumbnail_placeholder = 'assets/img/placeholder.svg'
23547
- header_background = 'teal'
23548
- header_foreground_color = "#872e2e"
23549
- main_font = 'sans-serif'
23550
- title_font = 'serif'
23551
- another_color = 'red'
23552
- `;
23396
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopoverComponent, decorators: [{
23397
+ type: Component,
23398
+ args: [{ selector: 'gn-ui-popover', standalone: true, imports: [], template: "<span #popoverContent>\n <ng-content></ng-content>\n</span>\n" }]
23399
+ }], propDecorators: { popoverContent: [{
23400
+ type: ViewChild,
23401
+ args: ['popoverContent', { static: false }]
23402
+ }], content: [{
23403
+ type: Input
23404
+ }], theme: [{
23405
+ type: Input
23406
+ }] } });
23553
23407
 
23554
- function getMapContextLayerFromConfig(config) {
23555
- switch (config.TYPE) {
23556
- case 'wms':
23557
- return {
23558
- type: 'wms',
23559
- url: config.URL,
23560
- name: config.NAME,
23561
- };
23562
- case 'wfs':
23563
- return {
23564
- type: 'wfs',
23565
- url: config.URL,
23566
- featureType: config.NAME,
23567
- };
23568
- case 'xyz':
23569
- return {
23570
- type: config.TYPE,
23571
- url: config.URL,
23572
- };
23573
- case 'geojson':
23574
- return {
23575
- type: config.TYPE,
23576
- ...(config.DATA ? { data: config.DATA } : { url: config.URL }),
23577
- };
23578
- case 'maplibre-style':
23579
- return {
23580
- type: config.TYPE,
23581
- styleUrl: config.STYLE_URL,
23582
- accessToken: config.ACCESS_TOKEN,
23583
- };
23584
- }
23408
+ class LoadingMaskComponent {
23409
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LoadingMaskComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23410
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: LoadingMaskComponent, isStandalone: true, selector: "gn-ui-loading-mask", inputs: { message: "message" }, ngImport: i0, template: "<div class=\"h-full flex flex-col justify-center items-center relative backdrop\">\n <div class=\"absolute background bg-white inset-0\"></div>\n <mat-spinner [diameter]=\"28\" class=\"relative\"></mat-spinner>\n <span class=\"text-sm text-gray-700 mt-3 relative\">{{ message }}</span>\n</div>\n", styles: ["::ng-deep .mat-spinner circle{stroke:var(--color-gray-700);opacity:.5}.backdrop{background-color:#fff6}@supports (-webkit-backdrop-filter: blur()) or (backdrop-filter: blur()){.backdrop{background-color:transparent;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}}.background{opacity:.7}\n"], dependencies: [{ kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i2.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
23585
23411
  }
23412
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LoadingMaskComponent, decorators: [{
23413
+ type: Component,
23414
+ args: [{ selector: 'gn-ui-loading-mask', changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatProgressSpinnerModule], standalone: true, template: "<div class=\"h-full flex flex-col justify-center items-center relative backdrop\">\n <div class=\"absolute background bg-white inset-0\"></div>\n <mat-spinner [diameter]=\"28\" class=\"relative\"></mat-spinner>\n <span class=\"text-sm text-gray-700 mt-3 relative\">{{ message }}</span>\n</div>\n", styles: ["::ng-deep .mat-spinner circle{stroke:var(--color-gray-700);opacity:.5}.backdrop{background-color:#fff6}@supports (-webkit-backdrop-filter: blur()) or (backdrop-filter: blur()){.backdrop{background-color:transparent;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}}.background{opacity:.7}\n"] }]
23415
+ }], propDecorators: { message: [{
23416
+ type: Input
23417
+ }] } });
23586
23418
 
23587
- class MapStyleService {
23588
- constructor() {
23589
- this.styles = {
23590
- default: this.createStyleFunction(this.createGeometryStyles({
23591
- color: isConfigLoaded() ? getThemeConfig().PRIMARY_COLOR : 'blue',
23592
- })),
23593
- defaultHL: this.createStyleFunction(this.createGeometryStyles({
23594
- color: isConfigLoaded() ? getThemeConfig().SECONDARY_COLOR : 'red',
23595
- isFocused: true,
23596
- })),
23597
- };
23598
- }
23599
- createGeometryStyles(options) {
23600
- const { color, isFocused } = options;
23601
- const zIndex = isFocused ? 10 : undefined;
23602
- return {
23603
- polygon: new Style({
23604
- fill: new Fill({
23605
- color: this.computeTransparentFillColor(color),
23606
- }),
23607
- stroke: new Stroke({
23608
- color: 'white',
23609
- width: 2,
23610
- }),
23611
- zIndex,
23612
- }),
23613
- point: new Style({
23614
- image: new CircleStyle({
23615
- fill: new Fill({
23616
- color,
23617
- }),
23618
- stroke: new Stroke({
23619
- color: 'white',
23620
- width: isFocused ? 3 : 2,
23621
- }),
23622
- radius: isFocused ? 8 : 7,
23623
- }),
23624
- zIndex,
23625
- }),
23626
- line: [
23627
- new Style({
23628
- stroke: new Stroke({
23629
- color: 'white',
23630
- width: isFocused ? 8 : 6,
23631
- }),
23632
- zIndex,
23633
- }),
23634
- new Style({
23635
- stroke: new Stroke({
23636
- color,
23637
- width: isFocused ? 3 : 2,
23638
- }),
23639
- zIndex,
23640
- }),
23641
- ],
23642
- };
23643
- }
23644
- createStyleFunction(styleByGeometryType) {
23645
- return (feature) => {
23646
- const geometryType = feature?.getGeometry()?.getType();
23647
- switch (geometryType) {
23648
- case 'LinearRing':
23649
- case 'LineString':
23650
- case 'MultiLineString':
23651
- return styleByGeometryType.line;
23652
- case 'Point':
23653
- case 'MultiPoint':
23654
- return styleByGeometryType.point;
23655
- case 'Circle':
23656
- case 'Polygon':
23657
- case 'MultiPolygon':
23658
- return styleByGeometryType.polygon;
23659
- default:
23660
- return styleByGeometryType.point;
23661
- }
23662
- };
23663
- }
23664
- computeTransparentFillColor(color, alpha = 0.25) {
23665
- return chroma(color).alpha(alpha).css();
23666
- }
23667
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: MapStyleService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
23668
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: MapStyleService, providedIn: 'root' }); }
23419
+ class ColorScaleComponent {
23420
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ColorScaleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23421
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: ColorScaleComponent, isStandalone: true, selector: "gn-ui-color-scale", ngImport: i0, template: "<div class=\"flex flex-row items-center\">\n <div class=\"text-xs text-gray-700 font-bold m-2 w-32 text-right\">primary</div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-black\"\n title=\"primary-black\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-darkest\"\n title=\"primary-darkest\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-darker\"\n title=\"primary-darker\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary border-2 border-primary-darkest\"\n title=\"primary\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-lighter\"\n title=\"primary-lighter\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-lightest\"\n title=\"primary-lightest\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-white\"\n title=\"primary-white\"\n ></div>\n</div>\n<div class=\"flex flex-row\">\n <div class=\"text-xs text-gray-700 font-bold m-2 w-32 text-right\">\n secondary\n </div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-black\"\n title=\"secondary-black\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-darkest\"\n title=\"secondary-darkest\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-darker\"\n title=\"secondary-darker\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary border-2 border-secondary-darkest\"\n title=\"secondary\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-lighter\"\n title=\"secondary-lighter\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-lightest\"\n title=\"secondary-lightest\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-white\"\n title=\"secondary-white\"\n ></div>\n</div>\n<div class=\"flex flex-row\">\n <div class=\"text-xs text-gray-700 font-bold m-2 w-32 text-right\">main</div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-main\" title=\"main\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-900\" title=\"gray-900\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-800\" title=\"gray-800\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-700\" title=\"gray-700\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-600\" title=\"gray-600\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-500\" title=\"gray-500\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-400\" title=\"gray-400\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-300\" title=\"gray-300\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-200\" title=\"gray-200\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-100\" title=\"gray-100\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-50\" title=\"gray-50\"></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-background\"\n title=\"background\"\n ></div>\n <div class=\"text-xs text-gray-700 font-bold m-2 w-32\">background</div>\n</div>\n", styles: [""] }); }
23669
23422
  }
23670
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: MapStyleService, decorators: [{
23671
- type: Injectable,
23672
- args: [{
23673
- providedIn: 'root',
23674
- }]
23423
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ColorScaleComponent, decorators: [{
23424
+ type: Component,
23425
+ args: [{ selector: 'gn-ui-color-scale', standalone: true, template: "<div class=\"flex flex-row items-center\">\n <div class=\"text-xs text-gray-700 font-bold m-2 w-32 text-right\">primary</div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-black\"\n title=\"primary-black\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-darkest\"\n title=\"primary-darkest\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-darker\"\n title=\"primary-darker\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary border-2 border-primary-darkest\"\n title=\"primary\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-lighter\"\n title=\"primary-lighter\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-lightest\"\n title=\"primary-lightest\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-white\"\n title=\"primary-white\"\n ></div>\n</div>\n<div class=\"flex flex-row\">\n <div class=\"text-xs text-gray-700 font-bold m-2 w-32 text-right\">\n secondary\n </div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-black\"\n title=\"secondary-black\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-darkest\"\n title=\"secondary-darkest\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-darker\"\n title=\"secondary-darker\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary border-2 border-secondary-darkest\"\n title=\"secondary\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-lighter\"\n title=\"secondary-lighter\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-lightest\"\n title=\"secondary-lightest\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-white\"\n title=\"secondary-white\"\n ></div>\n</div>\n<div class=\"flex flex-row\">\n <div class=\"text-xs text-gray-700 font-bold m-2 w-32 text-right\">main</div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-main\" title=\"main\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-900\" title=\"gray-900\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-800\" title=\"gray-800\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-700\" title=\"gray-700\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-600\" title=\"gray-600\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-500\" title=\"gray-500\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-400\" title=\"gray-400\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-300\" title=\"gray-300\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-200\" title=\"gray-200\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-100\" title=\"gray-100\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-50\" title=\"gray-50\"></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-background\"\n title=\"background\"\n ></div>\n <div class=\"text-xs text-gray-700 font-bold m-2 w-32\">background</div>\n</div>\n" }]
23675
23426
  }] });
23676
23427
 
23677
- const colorPrimary = 'blue';
23678
- const colorSecondary = 'red';
23679
- const colorStroke = 'white';
23680
- const defaultMapStyleFixture = () => new Style({
23681
- image: new Circle({
23682
- fill: new Fill({
23683
- color: colorPrimary,
23684
- }),
23685
- stroke: new Stroke({
23686
- color: colorStroke,
23687
- width: 2,
23688
- }),
23689
- radius: 5,
23690
- }),
23691
- fill: new Fill({
23692
- color: 'rgba(0,0,255,0.25)',
23693
- }),
23694
- stroke: new Stroke({
23695
- color: colorStroke,
23696
- width: 2,
23697
- }),
23698
- });
23699
- const defaultMapStyleHlFixture = () => new Style({
23700
- image: new Circle({
23701
- fill: new Fill({
23702
- color: colorSecondary,
23703
- }),
23704
- stroke: new Stroke({
23705
- color: colorStroke,
23706
- width: 3,
23707
- }),
23708
- radius: 6,
23709
- }),
23710
- fill: new Fill({
23711
- color: 'rgba(255,0,0,0.25)',
23712
- }),
23713
- stroke: new Stroke({
23714
- color: colorSecondary,
23715
- width: 3,
23716
- }),
23717
- });
23718
-
23719
- class AnchorLinkDirective {
23428
+ class PopupAlertComponent {
23720
23429
  constructor() {
23721
23430
  this.changeDetector = inject(ChangeDetectorRef);
23722
- this.disabled = false;
23723
- this.observer = new MutationObserver(() => {
23724
- this.refreshDisabledState();
23725
- });
23726
- this.inView = false;
23727
- this.initialized = false;
23431
+ this.type = 'info';
23432
+ this.position = 'top';
23433
+ this.expanded = false;
23434
+ this.timeout = null;
23728
23435
  }
23729
- get elementClass() {
23730
- if (this.disabled) {
23731
- return this.disabledClass;
23732
- }
23733
- if (this.inView) {
23734
- return `${this.inViewClass} ${this.enabledClass}`;
23735
- }
23736
- else {
23737
- return `${this.outOfViewClass} ${this.enabledClass}`;
23738
- }
23436
+ get showDuration() {
23437
+ const chars = this.content.nativeElement.innerHTML.length;
23438
+ return Math.max(3000, chars * 20);
23739
23439
  }
23740
23440
  ngOnInit() {
23741
- this.observer.observe(document.body, {
23742
- childList: true,
23743
- subtree: true,
23744
- });
23745
- this.refreshDisabledState();
23746
- }
23747
- ngAfterViewChecked() {
23748
- if (!this.initialized && !this.disabled) {
23749
- const target = document.getElementById(this.targetId);
23750
- if (target) {
23751
- this.initializeIntersectionObserver(target);
23752
- this.initialized = true;
23753
- }
23754
- }
23755
- }
23756
- initializeIntersectionObserver(target) {
23757
- this.intersectionObserver = new IntersectionObserver((entries) => {
23758
- entries.forEach((entry) => {
23759
- this.inView = entry.isIntersecting;
23760
- this.changeDetector.detectChanges();
23761
- });
23762
- }, {
23763
- root: null,
23764
- rootMargin: '-30% 0% -60% 0%',
23765
- });
23766
- this.intersectionObserver.observe(target);
23767
- }
23768
- ngOnDestroy() {
23769
- this.observer.disconnect();
23770
- if (this.intersectionObserver) {
23771
- this.intersectionObserver.disconnect();
23772
- }
23773
- }
23774
- refreshDisabledState() {
23775
- const targetNotPresent = !document.getElementById(this.targetId);
23776
- if (targetNotPresent !== this.disabled) {
23777
- this.disabled = targetNotPresent;
23778
- this.changeDetector.detectChanges();
23779
- }
23780
- }
23781
- scrollToTarget() {
23782
- const target = document.getElementById(this.targetId);
23783
- if (!target)
23784
- return;
23785
- target.scrollIntoView({
23786
- behavior: 'smooth',
23787
- block: 'start',
23788
- });
23789
- }
23790
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AnchorLinkDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
23791
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.19", type: AnchorLinkDirective, isStandalone: true, selector: "[gnUiAnchorLink]", inputs: { targetId: ["gnUiAnchorLink", "targetId"], disabledClass: ["gnUiAnchorLinkDisabledClass", "disabledClass"], enabledClass: ["gnUiAnchorLinkEnabledClass", "enabledClass"], inViewClass: ["gnUiAnchorLinkInViewClass", "inViewClass"], outOfViewClass: ["gnUiAnchorLinkOutOfViewClass", "outOfViewClass"] }, host: { listeners: { "click": "scrollToTarget()" }, properties: { "class": "this.elementClass" } }, ngImport: i0 }); }
23792
- }
23793
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AnchorLinkDirective, decorators: [{
23794
- type: Directive,
23795
- args: [{
23796
- selector: '[gnUiAnchorLink]',
23797
- standalone: true,
23798
- }]
23799
- }], propDecorators: { targetId: [{
23800
- type: Input,
23801
- args: ['gnUiAnchorLink']
23802
- }], disabledClass: [{
23803
- type: Input,
23804
- args: ['gnUiAnchorLinkDisabledClass']
23805
- }], enabledClass: [{
23806
- type: Input,
23807
- args: ['gnUiAnchorLinkEnabledClass']
23808
- }], inViewClass: [{
23809
- type: Input,
23810
- args: ['gnUiAnchorLinkInViewClass']
23811
- }], outOfViewClass: [{
23812
- type: Input,
23813
- args: ['gnUiAnchorLinkOutOfViewClass']
23814
- }], elementClass: [{
23815
- type: HostBinding,
23816
- args: ['class']
23817
- }], scrollToTarget: [{
23818
- type: HostListener,
23819
- args: ['click']
23820
- }] } });
23821
-
23822
- class PaginationDotsComponent {
23823
- constructor() {
23824
- this.containerClass = '';
23825
- }
23826
- // 1-based
23827
- get steps() {
23828
- return Array.from({ length: this.listComponent.pagesCount }, (_, i) => i + 1);
23829
- }
23830
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PaginationDotsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23831
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: PaginationDotsComponent, isStandalone: true, selector: "gn-ui-pagination-dots", inputs: { listComponent: "listComponent", containerClass: "containerClass" }, ngImport: i0, template: "@if (listComponent.pagesCount > 1) {\n <div\n class=\"overflow-hidden flex flex-row justify-center gap-[14px] p-1\"\n [ngClass]=\"containerClass\"\n >\n @for (step of steps; track step) {\n <button\n type=\"button\"\n class=\"pagination-dot\"\n (click)=\"listComponent.goToPage(step)\"\n [ngClass]=\"\n listComponent.currentPage === step ? 'bg-primary' : 'bg-gray-400'\n \"\n ></button>\n }\n </div>\n}\n", styles: [".pagination-dot{width:6px;height:6px;border-radius:6px;position:relative;flex-shrink:0}.pagination-dot:after{content:\"\";position:absolute;left:-7px;top:-7px;width:20px;height:20px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], viewProviders: [
23832
- provideIcons({
23833
- iconoirNavArrowRight,
23834
- iconoirNavArrowLeft,
23835
- }),
23836
- ] }); }
23837
- }
23838
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PaginationDotsComponent, decorators: [{
23839
- type: Component,
23840
- args: [{ selector: 'gn-ui-pagination-dots', standalone: true, imports: [CommonModule], viewProviders: [
23841
- provideIcons({
23842
- iconoirNavArrowRight,
23843
- iconoirNavArrowLeft,
23844
- }),
23845
- ], template: "@if (listComponent.pagesCount > 1) {\n <div\n class=\"overflow-hidden flex flex-row justify-center gap-[14px] p-1\"\n [ngClass]=\"containerClass\"\n >\n @for (step of steps; track step) {\n <button\n type=\"button\"\n class=\"pagination-dot\"\n (click)=\"listComponent.goToPage(step)\"\n [ngClass]=\"\n listComponent.currentPage === step ? 'bg-primary' : 'bg-gray-400'\n \"\n ></button>\n }\n </div>\n}\n", styles: [".pagination-dot{width:6px;height:6px;border-radius:6px;position:relative;flex-shrink:0}.pagination-dot:after{content:\"\";position:absolute;left:-7px;top:-7px;width:20px;height:20px}\n"] }]
23846
- }], propDecorators: { listComponent: [{
23847
- type: Input
23848
- }], containerClass: [{
23849
- type: Input
23850
- }] } });
23851
-
23852
- class CarouselComponent {
23853
- constructor() {
23854
- this.changeDetector = inject(ChangeDetectorRef);
23855
- this.containerClass = '';
23856
- this.stepsContainerClass = 'w-full bottom-0 top-auto';
23857
- this.currentStepChange = new EventEmitter();
23858
- this.steps = [];
23859
- this.currentStep = 0;
23860
- this.refreshSteps = () => {
23861
- this.steps = this.emblaApi.scrollSnapList();
23862
- this.currentStep = this.emblaApi.selectedScrollSnap();
23863
- this.currentStepChange.emit(this.currentStep);
23864
- this.changeDetector.detectChanges();
23865
- };
23866
- }
23867
- // Paginable API
23868
- get isFirstPage() {
23869
- return this.currentStep === 0;
23870
- }
23871
- get isLastPage() {
23872
- return this.currentStep === this.steps.length - 1;
23873
- }
23874
- get currentPage() {
23875
- return this.currentStep + 1; // this is 1-based
23876
- }
23877
- get pagesCount() {
23878
- return this.steps.length;
23879
- }
23880
- goToPage(stepIndex) {
23881
- this.emblaApi.scrollTo(stepIndex - 1); // this is 0-based
23882
- }
23883
- goToPrevPage() {
23884
- if (this.isFirstPage)
23885
- return;
23886
- this.emblaApi.scrollPrev();
23887
- }
23888
- goToNextPage() {
23889
- if (this.isLastPage)
23890
- return;
23891
- this.emblaApi.scrollNext();
23892
- }
23893
- ngAfterViewInit() {
23894
- this.emblaApi = EmblaCarousel(this.carouselOverflowContainer.nativeElement, {
23895
- duration: 15,
23896
- });
23897
- this.emblaApi
23898
- .on('init', this.refreshSteps)
23899
- .on('reInit', this.refreshSteps)
23900
- .on('select', this.refreshSteps);
23901
- }
23902
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CarouselComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23903
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: CarouselComponent, isStandalone: true, selector: "gn-ui-carousel", inputs: { containerClass: "containerClass", stepsContainerClass: "stepsContainerClass" }, outputs: { currentStepChange: "currentStepChange" }, viewQueries: [{ propertyName: "carouselOverflowContainer", first: true, predicate: ["carouselOverflowContainer"], descendants: true }], ngImport: i0, template: "<div #carouselOverflowContainer class=\"w-full block\">\n <div class=\"carousel-container flex\" [ngClass]=\"containerClass\">\n <ng-content></ng-content>\n </div>\n</div>\n<gn-ui-pagination-dots\n [containerClass]=\"'absolute ' + stepsContainerClass\"\n [listComponent]=\"this\"\n></gn-ui-pagination-dots>\n", styles: [":host{position:relative}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: PaginationDotsComponent, selector: "gn-ui-pagination-dots", inputs: ["listComponent", "containerClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
23904
- }
23905
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CarouselComponent, decorators: [{
23906
- type: Component,
23907
- args: [{ selector: 'gn-ui-carousel', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, PaginationDotsComponent], template: "<div #carouselOverflowContainer class=\"w-full block\">\n <div class=\"carousel-container flex\" [ngClass]=\"containerClass\">\n <ng-content></ng-content>\n </div>\n</div>\n<gn-ui-pagination-dots\n [containerClass]=\"'absolute ' + stepsContainerClass\"\n [listComponent]=\"this\"\n></gn-ui-pagination-dots>\n", styles: [":host{position:relative}\n"] }]
23908
- }], propDecorators: { carouselOverflowContainer: [{
23909
- type: ViewChild,
23910
- args: ['carouselOverflowContainer']
23911
- }], containerClass: [{
23912
- type: Input
23913
- }], stepsContainerClass: [{
23914
- type: Input
23915
- }], currentStepChange: [{
23916
- type: Output
23917
- }] } });
23918
-
23919
- class ProgressBarComponent {
23920
- constructor() {
23921
- this.value = 0;
23922
- this.type = 'default';
23923
- }
23924
- get progress() {
23925
- return this.value > 0 ? (this.value < 100 ? this.value : 100) : 0;
23926
- }
23927
- get color() {
23928
- switch (this.type) {
23929
- case 'default':
23930
- return {
23931
- outerBar: 'bg-gray-200',
23932
- innerBar: 'bg-gray-100',
23933
- text: 'text-gray-900',
23934
- };
23935
- case 'primary':
23936
- return {
23937
- outerBar: 'bg-primary',
23938
- innerBar: 'bg-primary-lighter',
23939
- text: 'text-white',
23940
- };
23941
- case 'secondary':
23942
- return {
23943
- outerBar: 'bg-secondary',
23944
- innerBar: 'bg-secondary-lighter',
23945
- text: 'text-white',
23946
- };
23947
- case 'light':
23948
- return {
23949
- outerBar: 'bg-primary-white',
23950
- innerBar: 'bg-primary',
23951
- text: 'text-main',
23952
- };
23953
- }
23954
- }
23955
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProgressBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23956
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: ProgressBarComponent, isStandalone: true, selector: "gn-ui-progress-bar", inputs: { value: "value", type: "type" }, ngImport: i0, template: "@switch (type) {\n <!-- Light Theme -->\n @case ('light') {\n <div class=\"flex items-center relative\">\n <div\n class=\"flex-shrink-0 {{ color.text }} font-medium mr-2\n text-opacity-100 !text-slate-800\"\n data-cy=\"progressPercentage\"\n >\n {{ progress }}%\n </div>\n <div class=\"flex-grow h-[4px] w-[52px] {{ color.outerBar }} rounded-full\">\n <div\n [style.width.%]=\"progress\"\n class=\"{{ color.innerBar }} transition-width duration-500\n ease-in-out rounded-full shadow-sm w-[52px] h-[4px]\"\n ></div>\n </div>\n </div>\n }\n <!-- Default / Primary / Secondary Themes -->\n @default {\n <div class=\"flex h-full {{ color.outerBar }} rounded-t-lg rounded-b-lg\">\n <div\n [style.width.%]=\"progress\"\n class=\"flex {{ color.innerBar }} my-1 mx-1 transition-width\n duration-500 ease-in-out rounded-t-md rounded-b-md shadow-xl\"\n >\n <div\n class=\"flex items-center pl-2 py-1 {{ color.text }} font-bold text-4\"\n >\n {{ progress }}%\n </div>\n </div>\n </div>\n }\n}\n", styles: [".font-bold{font-weight:var(--progress-bar-font-weight, \"bold\")}\n"] }); }
23957
- }
23958
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProgressBarComponent, decorators: [{
23959
- type: Component,
23960
- args: [{ selector: 'gn-ui-progress-bar', standalone: true, imports: [], template: "@switch (type) {\n <!-- Light Theme -->\n @case ('light') {\n <div class=\"flex items-center relative\">\n <div\n class=\"flex-shrink-0 {{ color.text }} font-medium mr-2\n text-opacity-100 !text-slate-800\"\n data-cy=\"progressPercentage\"\n >\n {{ progress }}%\n </div>\n <div class=\"flex-grow h-[4px] w-[52px] {{ color.outerBar }} rounded-full\">\n <div\n [style.width.%]=\"progress\"\n class=\"{{ color.innerBar }} transition-width duration-500\n ease-in-out rounded-full shadow-sm w-[52px] h-[4px]\"\n ></div>\n </div>\n </div>\n }\n <!-- Default / Primary / Secondary Themes -->\n @default {\n <div class=\"flex h-full {{ color.outerBar }} rounded-t-lg rounded-b-lg\">\n <div\n [style.width.%]=\"progress\"\n class=\"flex {{ color.innerBar }} my-1 mx-1 transition-width\n duration-500 ease-in-out rounded-t-md rounded-b-md shadow-xl\"\n >\n <div\n class=\"flex items-center pl-2 py-1 {{ color.text }} font-bold text-4\"\n >\n {{ progress }}%\n </div>\n </div>\n </div>\n }\n}\n", styles: [".font-bold{font-weight:var(--progress-bar-font-weight, \"bold\")}\n"] }]
23961
- }], propDecorators: { value: [{
23962
- type: Input
23963
- }], type: [{
23964
- type: Input
23965
- }] } });
23966
-
23967
- class PopoverComponent {
23968
- constructor() {
23969
- this.viewContainerRef = inject(ViewContainerRef);
23970
- this.renderer = inject(Renderer2);
23971
- }
23972
- getContent() {
23973
- if (this.content instanceof TemplateRef) {
23974
- if (this.view) {
23975
- this.view.destroy();
23976
- }
23977
- this.view = this.viewContainerRef.createEmbeddedView(this.content);
23978
- this.view.detectChanges();
23979
- const wrapper = this.renderer.createElement('div'); // Create a wrapper div
23980
- this.view.rootNodes.forEach((node) => {
23981
- this.renderer.appendChild(wrapper, node); // Append each root node to the wrapper
23982
- });
23983
- return wrapper;
23984
- }
23985
- return this.content;
23986
- }
23987
- ngAfterViewInit() {
23988
- this.tippyInstance = tippy(this.popoverContent.nativeElement, {
23989
- content: this.getContent(),
23990
- allowHTML: true,
23991
- theme: this.theme,
23992
- });
23993
- }
23994
- ngOnChanges(changes) {
23995
- if (changes['theme']) {
23996
- this.theme = changes['theme'].currentValue;
23997
- if (this.tippyInstance) {
23998
- this.tippyInstance.setProps({ theme: this.theme });
23999
- }
24000
- }
24001
- if (changes['content']) {
24002
- this.content = changes['content'].currentValue;
24003
- if (this.tippyInstance) {
24004
- this.tippyInstance.setContent(this.getContent());
24005
- }
24006
- }
24007
- }
24008
- ngOnDestroy() {
24009
- if (this.tippyInstance) {
24010
- this.tippyInstance.destroy();
24011
- }
24012
- if (this.view) {
24013
- this.view.destroy();
24014
- }
24015
- }
24016
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24017
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: PopoverComponent, isStandalone: true, selector: "gn-ui-popover", inputs: { content: "content", theme: "theme" }, viewQueries: [{ propertyName: "popoverContent", first: true, predicate: ["popoverContent"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<span #popoverContent>\n <ng-content></ng-content>\n</span>\n", styles: [""] }); }
24018
- }
24019
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopoverComponent, decorators: [{
24020
- type: Component,
24021
- args: [{ selector: 'gn-ui-popover', standalone: true, imports: [], template: "<span #popoverContent>\n <ng-content></ng-content>\n</span>\n" }]
24022
- }], propDecorators: { popoverContent: [{
24023
- type: ViewChild,
24024
- args: ['popoverContent', { static: false }]
24025
- }], content: [{
24026
- type: Input
24027
- }], theme: [{
24028
- type: Input
24029
- }] } });
24030
-
24031
- class LoadingMaskComponent {
24032
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LoadingMaskComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24033
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: LoadingMaskComponent, isStandalone: true, selector: "gn-ui-loading-mask", inputs: { message: "message" }, ngImport: i0, template: "<div class=\"h-full flex flex-col justify-center items-center relative backdrop\">\n <div class=\"absolute background bg-white inset-0\"></div>\n <mat-spinner [diameter]=\"28\" class=\"relative\"></mat-spinner>\n <span class=\"text-sm text-gray-700 mt-3 relative\">{{ message }}</span>\n</div>\n", styles: ["::ng-deep .mat-spinner circle{stroke:var(--color-gray-700);opacity:.5}.backdrop{background-color:#fff6}@supports (-webkit-backdrop-filter: blur()) or (backdrop-filter: blur()){.backdrop{background-color:transparent;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}}.background{opacity:.7}\n"], dependencies: [{ kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i2.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
24034
- }
24035
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LoadingMaskComponent, decorators: [{
24036
- type: Component,
24037
- args: [{ selector: 'gn-ui-loading-mask', changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatProgressSpinnerModule], standalone: true, template: "<div class=\"h-full flex flex-col justify-center items-center relative backdrop\">\n <div class=\"absolute background bg-white inset-0\"></div>\n <mat-spinner [diameter]=\"28\" class=\"relative\"></mat-spinner>\n <span class=\"text-sm text-gray-700 mt-3 relative\">{{ message }}</span>\n</div>\n", styles: ["::ng-deep .mat-spinner circle{stroke:var(--color-gray-700);opacity:.5}.backdrop{background-color:#fff6}@supports (-webkit-backdrop-filter: blur()) or (backdrop-filter: blur()){.backdrop{background-color:transparent;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}}.background{opacity:.7}\n"] }]
24038
- }], propDecorators: { message: [{
24039
- type: Input
24040
- }] } });
24041
-
24042
- class ColorScaleComponent {
24043
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ColorScaleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24044
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: ColorScaleComponent, isStandalone: true, selector: "gn-ui-color-scale", ngImport: i0, template: "<div class=\"flex flex-row items-center\">\n <div class=\"text-xs text-gray-700 font-bold m-2 w-32 text-right\">primary</div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-black\"\n title=\"primary-black\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-darkest\"\n title=\"primary-darkest\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-darker\"\n title=\"primary-darker\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary border-2 border-primary-darkest\"\n title=\"primary\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-lighter\"\n title=\"primary-lighter\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-lightest\"\n title=\"primary-lightest\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-white\"\n title=\"primary-white\"\n ></div>\n</div>\n<div class=\"flex flex-row\">\n <div class=\"text-xs text-gray-700 font-bold m-2 w-32 text-right\">\n secondary\n </div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-black\"\n title=\"secondary-black\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-darkest\"\n title=\"secondary-darkest\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-darker\"\n title=\"secondary-darker\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary border-2 border-secondary-darkest\"\n title=\"secondary\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-lighter\"\n title=\"secondary-lighter\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-lightest\"\n title=\"secondary-lightest\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-white\"\n title=\"secondary-white\"\n ></div>\n</div>\n<div class=\"flex flex-row\">\n <div class=\"text-xs text-gray-700 font-bold m-2 w-32 text-right\">main</div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-main\" title=\"main\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-900\" title=\"gray-900\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-800\" title=\"gray-800\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-700\" title=\"gray-700\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-600\" title=\"gray-600\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-500\" title=\"gray-500\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-400\" title=\"gray-400\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-300\" title=\"gray-300\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-200\" title=\"gray-200\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-100\" title=\"gray-100\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-50\" title=\"gray-50\"></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-background\"\n title=\"background\"\n ></div>\n <div class=\"text-xs text-gray-700 font-bold m-2 w-32\">background</div>\n</div>\n", styles: [""] }); }
24045
- }
24046
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ColorScaleComponent, decorators: [{
24047
- type: Component,
24048
- args: [{ selector: 'gn-ui-color-scale', standalone: true, template: "<div class=\"flex flex-row items-center\">\n <div class=\"text-xs text-gray-700 font-bold m-2 w-32 text-right\">primary</div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-black\"\n title=\"primary-black\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-darkest\"\n title=\"primary-darkest\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-darker\"\n title=\"primary-darker\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary border-2 border-primary-darkest\"\n title=\"primary\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-lighter\"\n title=\"primary-lighter\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-lightest\"\n title=\"primary-lightest\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-primary-white\"\n title=\"primary-white\"\n ></div>\n</div>\n<div class=\"flex flex-row\">\n <div class=\"text-xs text-gray-700 font-bold m-2 w-32 text-right\">\n secondary\n </div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-black\"\n title=\"secondary-black\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-darkest\"\n title=\"secondary-darkest\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-darker\"\n title=\"secondary-darker\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary border-2 border-secondary-darkest\"\n title=\"secondary\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-lighter\"\n title=\"secondary-lighter\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-lightest\"\n title=\"secondary-lightest\"\n ></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-secondary-white\"\n title=\"secondary-white\"\n ></div>\n</div>\n<div class=\"flex flex-row\">\n <div class=\"text-xs text-gray-700 font-bold m-2 w-32 text-right\">main</div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-main\" title=\"main\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-900\" title=\"gray-900\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-800\" title=\"gray-800\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-700\" title=\"gray-700\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-600\" title=\"gray-600\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-500\" title=\"gray-500\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-400\" title=\"gray-400\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-300\" title=\"gray-300\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-200\" title=\"gray-200\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-100\" title=\"gray-100\"></div>\n <div class=\"shadow-md rounded w-6 h-6 m-2 bg-gray-50\" title=\"gray-50\"></div>\n <div\n class=\"shadow-md rounded w-6 h-6 m-2 bg-background\"\n title=\"background\"\n ></div>\n <div class=\"text-xs text-gray-700 font-bold m-2 w-32\">background</div>\n</div>\n" }]
24049
- }] });
24050
-
24051
- class PopupAlertComponent {
24052
- constructor() {
24053
- this.changeDetector = inject(ChangeDetectorRef);
24054
- this.type = 'info';
24055
- this.position = 'top';
24056
- this.expanded = false;
24057
- this.timeout = null;
24058
- }
24059
- get showDuration() {
24060
- const chars = this.content.nativeElement.innerHTML.length;
24061
- return Math.max(3000, chars * 20);
24062
- }
24063
- ngOnInit() {
24064
- this.expandAndClose();
23441
+ this.expandAndClose();
24065
23442
  }
24066
23443
  expand() {
24067
23444
  this.expanded = true;
@@ -24376,7 +23753,7 @@ class AutocompleteComponent {
24376
23753
  this.enterBtnPosition = event.target['value'].length * 8 + 80;
24377
23754
  }
24378
23755
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AutocompleteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24379
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: AutocompleteComponent, isStandalone: true, selector: "gn-ui-autocomplete", inputs: { placeholder: "placeholder", enterButton: "enterButton", action: "action", value: "value", clearOnSelection: "clearOnSelection", preventCompleteOnSelection: "preventCompleteOnSelection", autoFocus: "autoFocus", minCharacterCount: "minCharacterCount", allowSubmit: "allowSubmit", forceTrackPosition: "forceTrackPosition", displayWithFn: "displayWithFn" }, outputs: { itemSelected: "itemSelected", inputSubmitted: "inputSubmitted", inputCleared: "inputCleared", isSearchActive: "isSearchActive" }, providers: [
23756
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: AutocompleteComponent, isStandalone: true, selector: "gn-ui-autocomplete", inputs: { placeholder: "placeholder", enterButton: "enterButton", action: "action", value: "value", clearOnSelection: "clearOnSelection", preventCompleteOnSelection: "preventCompleteOnSelection", autoFocus: "autoFocus", minCharacterCount: "minCharacterCount", allowSubmit: "allowSubmit", forceTrackPosition: "forceTrackPosition", displayWithTemplate: "displayWithTemplate", displayWithFn: "displayWithFn" }, outputs: { itemSelected: "itemSelected", inputSubmitted: "inputSubmitted", inputCleared: "inputCleared", isSearchActive: "isSearchActive" }, providers: [
24380
23757
  provideIcons({
24381
23758
  iconoirSearch,
24382
23759
  matClose,
@@ -24385,7 +23762,7 @@ class AutocompleteComponent {
24385
23762
  provideNgIconsConfig({
24386
23763
  size: '1.75rem',
24387
23764
  }),
24388
- ], viewQueries: [{ propertyName: "triggerRef", first: true, predicate: MatAutocompleteTrigger, descendants: true }, { propertyName: "autocomplete", first: true, predicate: MatAutocomplete, descendants: true }, { propertyName: "inputRef", first: true, predicate: ["searchInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<span class=\"w-full inline-block relative\">\n @if (!allowSubmit) {\n <div\n class=\"absolute inset-y-[--icon-padding] left-[--icon-padding] w-[--icon-width] pointer-events-none\"\n >\n <ng-icon name=\"iconoirSearch\" class=\"text-primary search\"></ng-icon>\n </div>\n }\n <div class=\"flex flex-row\">\n <input\n #searchInput\n type=\"text\"\n class=\"gn-ui-text-input\"\n (input)=\"handleInput($event)\"\n [placeholder]=\"placeholder\"\n [formControl]=\"control\"\n [matAutocomplete]=\"auto\"\n (keyup.enter)=\"handleSearch()\"\n [ngClass]=\"{\n 'text-primary': searchActive && enterButton,\n 'text-gray-900': !searchActive,\n 'px-[--icon-width]': !allowSubmit,\n }\"\n />\n @if (searchInput.value && displayEnterBtn) {\n <gn-ui-button\n type=\"gray\"\n extraClass=\"w-32 h-8 !opacity-100\"\n [ngStyle]=\"{ left: enterBtnPosition + 'px' }\"\n class=\"absolute\"\n disabled=\"true\"\n >\n <ng-icon\n name=\"iconoirLongArrowDownLeft\"\n class=\"!w-4 text-gray-900 font-bold\"\n ></ng-icon>\n <span translate class=\"text-bold text-gray-900 font-bold\">\n Enter to search\n </span>\n </gn-ui-button>\n }\n </div>\n @if (searchInput.value) {\n <gn-ui-button\n type=\"primary\"\n [extraClass]=\"getExtraClass()\"\n data-test=\"clear-btn\"\n aria-label=\"Clear\"\n (buttonClick)=\"clear()\"\n >\n <ng-icon class=\"text-white\" name=\"matClose\"></ng-icon>\n </gn-ui-button>\n }\n @if (allowSubmit) {\n <gn-ui-button\n type=\"light\"\n extraClass=\"border-0 border-l-[1px] border-gray-300 hover:border-gray-500 text-primary hover:text-primary-darkest focus:text-primary-darkest absolute inset-y-[--icon-padding] right-[--icon-padding]\"\n aria-label=\"Trigger search\"\n data-test=\"autocomplete-submit-btn\"\n (buttonClick)=\"handleSearch()\"\n >\n <ng-icon name=\"iconoirSearch\"></ng-icon>\n </gn-ui-button>\n }\n\n @if (error) {\n <gn-ui-popup-alert\n class=\"absolute mt-2 w-full top-[100%] left-0\"\n icon=\"matErrorOutlineOutline\"\n position=\"top\"\n type=\"warning\"\n >\n <span translate>search.autocomplete.error</span>\n {{ error }}\n </gn-ui-popup-alert>\n }\n</span>\n\n<mat-autocomplete\n #auto=\"matAutocomplete\"\n (optionSelected)=\"handleSelection($event)\"\n [displayWith]=\"displayWithFnInternal\"\n>\n @for (suggestion of suggestions$ | async; track suggestion) {\n <mat-option [value]=\"suggestion\" class=\"p-2 suggestion text-start\">\n {{ displayWithFnInternal(suggestion) }}\n </mat-option>\n }\n</mat-autocomplete>\n", styles: ["span{--icon-padding: var(--gn-ui-text-input-border-size, 2px);--icon-width: calc(var(--gn-ui-text-input-padding, .6em) * 2 + 1.25em)}span>*:not(gn-ui-popup-alert){font-size:var(--gn-ui-text-input-font-size, 1em)}input:placeholder-shown{text-overflow:ellipsis}gn-ui-button{--gn-ui-button-rounded: 0;--gn-ui-button-width: var(--icon-width);--gn-ui-button-padding: 0;font-size:1em}ng-icon.search{margin-top:.6rem;margin-left:.6rem}gn-ui-button:last-of-type{--gn-ui-button-rounded: 0 var(--gn-ui-text-input-rounded, .25em) var(--gn-ui-text-input-rounded, .25em) 0}.mat-mdc-option.suggestion.mat-mdc-option-active{background-color:var(--color-primary-lightest)}\n"], dependencies: [{ kind: "component", type: PopupAlertComponent, selector: "gn-ui-popup-alert", inputs: ["icon", "type", "position"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i1$2.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i1$2.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i1$2.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
23765
+ ], viewQueries: [{ propertyName: "triggerRef", first: true, predicate: MatAutocompleteTrigger, descendants: true }, { propertyName: "autocomplete", first: true, predicate: MatAutocomplete, descendants: true }, { propertyName: "inputRef", first: true, predicate: ["searchInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<span class=\"w-full inline-block relative\">\n @if (!allowSubmit) {\n <div\n class=\"absolute inset-y-[--icon-padding] left-[--icon-padding] w-[--icon-width] pointer-events-none\"\n >\n <ng-icon name=\"iconoirSearch\" class=\"text-primary search\"></ng-icon>\n </div>\n }\n <div class=\"flex flex-row\">\n <input\n #searchInput\n type=\"text\"\n class=\"gn-ui-text-input\"\n (input)=\"handleInput($event)\"\n [placeholder]=\"placeholder\"\n [formControl]=\"control\"\n [matAutocomplete]=\"auto\"\n (keyup.enter)=\"handleSearch()\"\n [ngClass]=\"{\n 'text-primary': searchActive && enterButton,\n 'text-gray-900': !searchActive,\n 'px-[--icon-width]': !allowSubmit,\n }\"\n />\n @if (searchInput.value && displayEnterBtn) {\n <gn-ui-button\n type=\"gray\"\n extraClass=\"w-32 h-8 !opacity-100\"\n [ngStyle]=\"{ left: enterBtnPosition + 'px' }\"\n class=\"absolute\"\n disabled=\"true\"\n >\n <ng-icon\n name=\"iconoirLongArrowDownLeft\"\n class=\"!w-4 text-gray-900 font-bold\"\n ></ng-icon>\n <span translate class=\"text-bold text-gray-900 font-bold\">\n Enter to search\n </span>\n </gn-ui-button>\n }\n </div>\n @if (searchInput.value) {\n <gn-ui-button\n type=\"primary\"\n [extraClass]=\"getExtraClass()\"\n data-test=\"clear-btn\"\n aria-label=\"Clear\"\n (buttonClick)=\"clear()\"\n >\n <ng-icon class=\"text-white\" name=\"matClose\"></ng-icon>\n </gn-ui-button>\n }\n @if (allowSubmit) {\n <gn-ui-button\n type=\"light\"\n extraClass=\"border-0 border-l-[1px] border-gray-300 hover:border-gray-500 text-primary hover:text-primary-darkest focus:text-primary-darkest absolute inset-y-[--icon-padding] right-[--icon-padding]\"\n aria-label=\"Trigger search\"\n data-test=\"autocomplete-submit-btn\"\n (buttonClick)=\"handleSearch()\"\n >\n <ng-icon name=\"iconoirSearch\"></ng-icon>\n </gn-ui-button>\n }\n\n @if (error) {\n <gn-ui-popup-alert\n class=\"absolute mt-2 w-full top-[100%] left-0\"\n icon=\"matErrorOutlineOutline\"\n position=\"top\"\n type=\"warning\"\n >\n <span translate>search.autocomplete.error</span>\n {{ error }}\n </gn-ui-popup-alert>\n }\n</span>\n\n<mat-autocomplete\n #auto=\"matAutocomplete\"\n (optionSelected)=\"handleSelection($event)\"\n [displayWith]=\"displayWithFnInternal\"\n>\n @for (suggestion of suggestions$ | async; track suggestion) {\n <mat-option [value]=\"suggestion\" class=\"p-2 suggestion text-start\">\n @if (displayWithTemplate) {\n <ng-container\n *ngTemplateOutlet=\"\n displayWithTemplate;\n context: { $implicit: suggestion }\n \"\n ></ng-container>\n } @else {\n {{ displayWithFnInternal(suggestion) }}\n }\n </mat-option>\n }\n</mat-autocomplete>\n", styles: ["span{--icon-padding: var(--gn-ui-text-input-border-size, 2px);--icon-width: calc(var(--gn-ui-text-input-padding, .6em) * 2 + 1.25em)}span>*:not(gn-ui-popup-alert){font-size:var(--gn-ui-text-input-font-size, 1em)}input:placeholder-shown{text-overflow:ellipsis}gn-ui-button{--gn-ui-button-rounded: 0;--gn-ui-button-width: var(--icon-width);--gn-ui-button-padding: 0;font-size:1em}ng-icon.search{margin-top:.6rem;margin-left:.6rem}gn-ui-button:last-of-type{--gn-ui-button-rounded: 0 var(--gn-ui-text-input-rounded, .25em) var(--gn-ui-text-input-rounded, .25em) 0}.mat-mdc-option.suggestion.mat-mdc-option-active{background-color:var(--color-primary-lightest)}\n"], dependencies: [{ kind: "component", type: PopupAlertComponent, selector: "gn-ui-popup-alert", inputs: ["icon", "type", "position"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i1$2.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i1$2.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i1$2.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
24389
23766
  }
24390
23767
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AutocompleteComponent, decorators: [{
24391
23768
  type: Component,
@@ -24406,7 +23783,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
24406
23783
  provideNgIconsConfig({
24407
23784
  size: '1.75rem',
24408
23785
  }),
24409
- ], template: "<span class=\"w-full inline-block relative\">\n @if (!allowSubmit) {\n <div\n class=\"absolute inset-y-[--icon-padding] left-[--icon-padding] w-[--icon-width] pointer-events-none\"\n >\n <ng-icon name=\"iconoirSearch\" class=\"text-primary search\"></ng-icon>\n </div>\n }\n <div class=\"flex flex-row\">\n <input\n #searchInput\n type=\"text\"\n class=\"gn-ui-text-input\"\n (input)=\"handleInput($event)\"\n [placeholder]=\"placeholder\"\n [formControl]=\"control\"\n [matAutocomplete]=\"auto\"\n (keyup.enter)=\"handleSearch()\"\n [ngClass]=\"{\n 'text-primary': searchActive && enterButton,\n 'text-gray-900': !searchActive,\n 'px-[--icon-width]': !allowSubmit,\n }\"\n />\n @if (searchInput.value && displayEnterBtn) {\n <gn-ui-button\n type=\"gray\"\n extraClass=\"w-32 h-8 !opacity-100\"\n [ngStyle]=\"{ left: enterBtnPosition + 'px' }\"\n class=\"absolute\"\n disabled=\"true\"\n >\n <ng-icon\n name=\"iconoirLongArrowDownLeft\"\n class=\"!w-4 text-gray-900 font-bold\"\n ></ng-icon>\n <span translate class=\"text-bold text-gray-900 font-bold\">\n Enter to search\n </span>\n </gn-ui-button>\n }\n </div>\n @if (searchInput.value) {\n <gn-ui-button\n type=\"primary\"\n [extraClass]=\"getExtraClass()\"\n data-test=\"clear-btn\"\n aria-label=\"Clear\"\n (buttonClick)=\"clear()\"\n >\n <ng-icon class=\"text-white\" name=\"matClose\"></ng-icon>\n </gn-ui-button>\n }\n @if (allowSubmit) {\n <gn-ui-button\n type=\"light\"\n extraClass=\"border-0 border-l-[1px] border-gray-300 hover:border-gray-500 text-primary hover:text-primary-darkest focus:text-primary-darkest absolute inset-y-[--icon-padding] right-[--icon-padding]\"\n aria-label=\"Trigger search\"\n data-test=\"autocomplete-submit-btn\"\n (buttonClick)=\"handleSearch()\"\n >\n <ng-icon name=\"iconoirSearch\"></ng-icon>\n </gn-ui-button>\n }\n\n @if (error) {\n <gn-ui-popup-alert\n class=\"absolute mt-2 w-full top-[100%] left-0\"\n icon=\"matErrorOutlineOutline\"\n position=\"top\"\n type=\"warning\"\n >\n <span translate>search.autocomplete.error</span>\n {{ error }}\n </gn-ui-popup-alert>\n }\n</span>\n\n<mat-autocomplete\n #auto=\"matAutocomplete\"\n (optionSelected)=\"handleSelection($event)\"\n [displayWith]=\"displayWithFnInternal\"\n>\n @for (suggestion of suggestions$ | async; track suggestion) {\n <mat-option [value]=\"suggestion\" class=\"p-2 suggestion text-start\">\n {{ displayWithFnInternal(suggestion) }}\n </mat-option>\n }\n</mat-autocomplete>\n", styles: ["span{--icon-padding: var(--gn-ui-text-input-border-size, 2px);--icon-width: calc(var(--gn-ui-text-input-padding, .6em) * 2 + 1.25em)}span>*:not(gn-ui-popup-alert){font-size:var(--gn-ui-text-input-font-size, 1em)}input:placeholder-shown{text-overflow:ellipsis}gn-ui-button{--gn-ui-button-rounded: 0;--gn-ui-button-width: var(--icon-width);--gn-ui-button-padding: 0;font-size:1em}ng-icon.search{margin-top:.6rem;margin-left:.6rem}gn-ui-button:last-of-type{--gn-ui-button-rounded: 0 var(--gn-ui-text-input-rounded, .25em) var(--gn-ui-text-input-rounded, .25em) 0}.mat-mdc-option.suggestion.mat-mdc-option-active{background-color:var(--color-primary-lightest)}\n"] }]
23786
+ ], template: "<span class=\"w-full inline-block relative\">\n @if (!allowSubmit) {\n <div\n class=\"absolute inset-y-[--icon-padding] left-[--icon-padding] w-[--icon-width] pointer-events-none\"\n >\n <ng-icon name=\"iconoirSearch\" class=\"text-primary search\"></ng-icon>\n </div>\n }\n <div class=\"flex flex-row\">\n <input\n #searchInput\n type=\"text\"\n class=\"gn-ui-text-input\"\n (input)=\"handleInput($event)\"\n [placeholder]=\"placeholder\"\n [formControl]=\"control\"\n [matAutocomplete]=\"auto\"\n (keyup.enter)=\"handleSearch()\"\n [ngClass]=\"{\n 'text-primary': searchActive && enterButton,\n 'text-gray-900': !searchActive,\n 'px-[--icon-width]': !allowSubmit,\n }\"\n />\n @if (searchInput.value && displayEnterBtn) {\n <gn-ui-button\n type=\"gray\"\n extraClass=\"w-32 h-8 !opacity-100\"\n [ngStyle]=\"{ left: enterBtnPosition + 'px' }\"\n class=\"absolute\"\n disabled=\"true\"\n >\n <ng-icon\n name=\"iconoirLongArrowDownLeft\"\n class=\"!w-4 text-gray-900 font-bold\"\n ></ng-icon>\n <span translate class=\"text-bold text-gray-900 font-bold\">\n Enter to search\n </span>\n </gn-ui-button>\n }\n </div>\n @if (searchInput.value) {\n <gn-ui-button\n type=\"primary\"\n [extraClass]=\"getExtraClass()\"\n data-test=\"clear-btn\"\n aria-label=\"Clear\"\n (buttonClick)=\"clear()\"\n >\n <ng-icon class=\"text-white\" name=\"matClose\"></ng-icon>\n </gn-ui-button>\n }\n @if (allowSubmit) {\n <gn-ui-button\n type=\"light\"\n extraClass=\"border-0 border-l-[1px] border-gray-300 hover:border-gray-500 text-primary hover:text-primary-darkest focus:text-primary-darkest absolute inset-y-[--icon-padding] right-[--icon-padding]\"\n aria-label=\"Trigger search\"\n data-test=\"autocomplete-submit-btn\"\n (buttonClick)=\"handleSearch()\"\n >\n <ng-icon name=\"iconoirSearch\"></ng-icon>\n </gn-ui-button>\n }\n\n @if (error) {\n <gn-ui-popup-alert\n class=\"absolute mt-2 w-full top-[100%] left-0\"\n icon=\"matErrorOutlineOutline\"\n position=\"top\"\n type=\"warning\"\n >\n <span translate>search.autocomplete.error</span>\n {{ error }}\n </gn-ui-popup-alert>\n }\n</span>\n\n<mat-autocomplete\n #auto=\"matAutocomplete\"\n (optionSelected)=\"handleSelection($event)\"\n [displayWith]=\"displayWithFnInternal\"\n>\n @for (suggestion of suggestions$ | async; track suggestion) {\n <mat-option [value]=\"suggestion\" class=\"p-2 suggestion text-start\">\n @if (displayWithTemplate) {\n <ng-container\n *ngTemplateOutlet=\"\n displayWithTemplate;\n context: { $implicit: suggestion }\n \"\n ></ng-container>\n } @else {\n {{ displayWithFnInternal(suggestion) }}\n }\n </mat-option>\n }\n</mat-autocomplete>\n", styles: ["span{--icon-padding: var(--gn-ui-text-input-border-size, 2px);--icon-width: calc(var(--gn-ui-text-input-padding, .6em) * 2 + 1.25em)}span>*:not(gn-ui-popup-alert){font-size:var(--gn-ui-text-input-font-size, 1em)}input:placeholder-shown{text-overflow:ellipsis}gn-ui-button{--gn-ui-button-rounded: 0;--gn-ui-button-width: var(--icon-width);--gn-ui-button-padding: 0;font-size:1em}ng-icon.search{margin-top:.6rem;margin-left:.6rem}gn-ui-button:last-of-type{--gn-ui-button-rounded: 0 var(--gn-ui-text-input-rounded, .25em) var(--gn-ui-text-input-rounded, .25em) 0}.mat-mdc-option.suggestion.mat-mdc-option-active{background-color:var(--color-primary-lightest)}\n"] }]
24410
23787
  }], propDecorators: { placeholder: [{
24411
23788
  type: Input
24412
23789
  }], enterButton: [{
@@ -24444,6 +23821,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
24444
23821
  }], inputRef: [{
24445
23822
  type: ViewChild,
24446
23823
  args: ['searchInput']
23824
+ }], displayWithTemplate: [{
23825
+ type: Input
24447
23826
  }], displayWithFn: [{
24448
23827
  type: Input
24449
23828
  }] } });
@@ -24576,12 +23955,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
24576
23955
  type: Input
24577
23956
  }] } });
24578
23957
 
23958
+ function provideLocalizedDateAdapter() {
23959
+ return [
23960
+ { provide: MAT_DATE_LOCALE, useValue: enUS },
23961
+ { provide: MAT_DATE_FORMATS, useValue: MAT_DATE_FNS_FORMATS },
23962
+ {
23963
+ provide: DateAdapter,
23964
+ useFactory: () => {
23965
+ const dateService = inject(DateService);
23966
+ const adapter = new DateFnsAdapter();
23967
+ dateService
23968
+ .getDateFnsLocale()
23969
+ .then((locale) => adapter.setLocale(locale));
23970
+ return adapter;
23971
+ },
23972
+ },
23973
+ ];
23974
+ }
23975
+
24579
23976
  class DatePickerComponent {
24580
23977
  constructor() {
24581
- this.dateAdapter = inject(DateAdapter);
24582
- this.translate = inject(TranslateService);
24583
23978
  this.dateChange = new EventEmitter();
24584
- this.dateAdapter.setLocale(this.translate.getCurrentLang());
24585
23979
  }
24586
23980
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24587
23981
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: DatePickerComponent, isStandalone: true, selector: "gn-ui-date-picker", inputs: { date: "date" }, outputs: { dateChange: "dateChange" }, providers: [
@@ -24589,30 +23983,19 @@ class DatePickerComponent {
24589
23983
  provideNgIconsConfig({
24590
23984
  size: '1.5rem',
24591
23985
  }),
24592
- {
24593
- provide: MAT_DATE_LOCALE,
24594
- useFactory: (locale) => locale,
24595
- },
24596
- ], ngImport: i0, template: "<span class=\"w-full inline-block relative\">\n <input\n class=\"gn-ui-text-input pr-[var(--text-padding)]\"\n [matDatepicker]=\"picker\"\n [value]=\"date\"\n (dateChange)=\"dateChange.emit($event.value)\"\n />\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n data-cy=\"date-picker-button\"\n extraClass=\"h-full\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-datepicker #picker></mat-datepicker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"], dependencies: [{ kind: "ngmodule", type: MatNativeDateModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i1$6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i1$6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
23986
+ provideLocalizedDateAdapter(),
23987
+ ], ngImport: i0, template: "<span class=\"w-full inline-block relative\">\n <input\n class=\"gn-ui-text-input pr-[var(--text-padding)]\"\n [matDatepicker]=\"picker\"\n [value]=\"date\"\n (dateChange)=\"dateChange.emit($event.value)\"\n />\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n data-cy=\"date-picker-button\"\n extraClass=\"h-full\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-datepicker #picker></mat-datepicker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"], dependencies: [{ kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i1$6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i1$6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
24597
23988
  }
24598
23989
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatePickerComponent, decorators: [{
24599
23990
  type: Component,
24600
- args: [{ selector: 'gn-ui-date-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
24601
- MatNativeDateModule,
24602
- MatDatepickerModule,
24603
- ButtonComponent,
24604
- NgIconComponent,
24605
- ], providers: [
23991
+ args: [{ selector: 'gn-ui-date-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [MatDatepickerModule, ButtonComponent, NgIconComponent], providers: [
24606
23992
  provideIcons({ iconoirCalendar }),
24607
23993
  provideNgIconsConfig({
24608
23994
  size: '1.5rem',
24609
23995
  }),
24610
- {
24611
- provide: MAT_DATE_LOCALE,
24612
- useFactory: (locale) => locale,
24613
- },
23996
+ provideLocalizedDateAdapter(),
24614
23997
  ], template: "<span class=\"w-full inline-block relative\">\n <input\n class=\"gn-ui-text-input pr-[var(--text-padding)]\"\n [matDatepicker]=\"picker\"\n [value]=\"date\"\n (dateChange)=\"dateChange.emit($event.value)\"\n />\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n data-cy=\"date-picker-button\"\n extraClass=\"h-full\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-datepicker #picker></mat-datepicker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"] }]
24615
- }], ctorParameters: () => [], propDecorators: { date: [{
23998
+ }], propDecorators: { date: [{
24616
23999
  type: Input
24617
24000
  }], dateChange: [{
24618
24001
  type: Output
@@ -24620,58 +24003,159 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
24620
24003
 
24621
24004
  class DateRangeDropdownComponent {
24622
24005
  constructor() {
24623
- this.overlayContainer = inject(OverlayContainer);
24006
+ this.scrollStrategies = inject(ScrollStrategyOptions);
24007
+ this.dateAdapter = inject(DateAdapter);
24008
+ this.dateFormats = inject(MAT_DATE_FORMATS);
24624
24009
  this.cdr = inject(ChangeDetectorRef);
24625
- this.startDateChange = new EventEmitter();
24626
- this.endDateChange = new EventEmitter();
24627
- this.isPickerDisplayed = false;
24010
+ this.dateRange = {};
24011
+ this.dateRangeChange = new EventEmitter();
24012
+ this.overlayPositions = [
24013
+ {
24014
+ originX: 'start',
24015
+ originY: 'bottom',
24016
+ overlayX: 'start',
24017
+ overlayY: 'top',
24018
+ offsetY: 8,
24019
+ },
24020
+ {
24021
+ originX: 'start',
24022
+ originY: 'top',
24023
+ overlayX: 'start',
24024
+ overlayY: 'bottom',
24025
+ offsetY: -8,
24026
+ },
24027
+ ];
24028
+ this.scrollStrategy = this.scrollStrategies.reposition();
24029
+ this.overlayOpen = false;
24030
+ this.expandedBound = 'start';
24031
+ this.invalidBounds = { start: false, end: false };
24032
+ // redraw when the lazily loaded UI locale reaches the adapter
24033
+ this.dateAdapter.localeChanges
24034
+ .pipe(takeUntilDestroyed())
24035
+ .subscribe(() => this.cdr.markForCheck());
24628
24036
  }
24629
- ngAfterViewChecked() {
24630
- this.checkPickerOverlay();
24037
+ get startDate() {
24038
+ return this.dateRange.start;
24631
24039
  }
24632
- checkPickerOverlay() {
24633
- const overlayContainerElement = this.overlayContainer.getContainerElement();
24634
- setTimeout(() => {
24635
- this.isPickerDisplayed =
24636
- overlayContainerElement.querySelector('.mat-datepicker-content') !==
24637
- null;
24638
- this.cdr.detectChanges();
24639
- }, 200); // FIXME: find a better way to deal with animation delay
24040
+ get endDate() {
24041
+ return this.dateRange.end;
24042
+ }
24043
+ get selectedDatesCount() {
24044
+ return (this.startDate ? 1 : 0) + (this.endDate ? 1 : 0);
24045
+ }
24046
+ openOverlay() {
24047
+ this.expandedBound = 'start';
24048
+ this.overlayOpen = true;
24049
+ }
24050
+ closeOverlay() {
24051
+ this.overlayOpen = false;
24052
+ }
24053
+ toggleBound(bound) {
24054
+ this.expandedBound = this.expandedBound === bound ? null : bound;
24055
+ }
24056
+ formatDate(date) {
24057
+ return this.dateAdapter.format(date, this.dateFormats.display.dateInput);
24058
+ }
24059
+ onDateInput(bound, event) {
24060
+ const typedText = event.target.value.trim();
24061
+ const date = typedText
24062
+ ? this.dateAdapter.parse(typedText, this.dateFormats.parse.dateInput)
24063
+ : null;
24064
+ if (!this.isAcceptable(bound, typedText, date)) {
24065
+ // keep the text as typed and flag it; the filter stays on its last value
24066
+ this.invalidBounds = { ...this.invalidBounds, [bound]: true };
24067
+ return;
24068
+ }
24069
+ if (bound === 'start')
24070
+ this.setStartDate(date);
24071
+ else
24072
+ this.setEndDate(date);
24073
+ }
24074
+ normalizeDateInput(bound, event) {
24075
+ if (this.invalidBounds[bound])
24076
+ return;
24077
+ const date = bound === 'start' ? this.startDate : this.endDate;
24078
+ const input = event.target;
24079
+ input.value = date ? this.formatDate(date) : '';
24080
+ }
24081
+ isAcceptable(bound, typedText, date) {
24082
+ if (!typedText)
24083
+ return true;
24084
+ if (!date || !this.dateAdapter.isValid(date))
24085
+ return false;
24086
+ return bound === 'start'
24087
+ ? !this.endDate || date <= this.endDate
24088
+ : !this.startDate || date >= this.startDate;
24089
+ }
24090
+ setStartDate(date) {
24091
+ this.invalidBounds = { ...this.invalidBounds, start: false };
24092
+ this.applyDateRange({ ...this.dateRange, start: date });
24093
+ this.expandedBound = 'end';
24094
+ }
24095
+ setEndDate(date) {
24096
+ this.invalidBounds = { ...this.invalidBounds, end: false };
24097
+ this.applyDateRange({ ...this.dateRange, end: date });
24098
+ this.expandedBound = null;
24099
+ }
24100
+ clearDates(event) {
24101
+ this.invalidBounds = { start: false, end: false };
24102
+ this.expandedBound = 'start';
24103
+ this.applyDateRange({});
24104
+ propagateToDocumentOnly(event);
24105
+ }
24106
+ applyDateRange(dateRange) {
24107
+ this.dateRange = {
24108
+ ...(dateRange.start && { start: dateRange.start }),
24109
+ ...(dateRange.end && { end: dateRange.end }),
24110
+ };
24111
+ this.dateRangeChange.emit(this.dateRange);
24640
24112
  }
24641
24113
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangeDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24642
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: DateRangeDropdownComponent, isStandalone: true, selector: "gn-ui-date-range-dropdown", inputs: { title: "title", startDate: "startDate", endDate: "endDate" }, outputs: { startDateChange: "startDateChange", endDateChange: "endDateChange" }, providers: [
24114
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DateRangeDropdownComponent, isStandalone: true, selector: "gn-ui-date-range-dropdown", inputs: { title: "title", dateRange: "dateRange" }, outputs: { dateRangeChange: "dateRangeChange" }, providers: [
24643
24115
  provideIcons({
24644
- matExpandMore,
24116
+ iconoirCalendar,
24117
+ matClose,
24645
24118
  matExpandLess,
24119
+ matExpandMore,
24646
24120
  }),
24647
- ], viewQueries: [{ propertyName: "picker", first: true, predicate: ["picker"], descendants: true }], ngImport: i0, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n (buttonClick)=\"picker.open()\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n </div>\n <ng-icon\n [name]=\"isPickerDisplayed ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n<!--date range input is just present for output events and hidden from the DOM-->\n<mat-date-range-input [rangePicker]=\"picker\">\n <input\n matStartDate\n [value]=\"startDate\"\n (dateInput)=\"startDateChange.emit($event.value)\"\n />\n <input\n matEndDate\n [value]=\"endDate\"\n (dateInput)=\"endDateChange.emit($event.value)\"\n />\n</mat-date-range-input>\n<mat-date-range-picker #picker></mat-date-range-picker>\n", styles: [":host .mat-date-range-input-container{display:none}\n"], dependencies: [{ kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "ngmodule", type: MatNativeDateModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i1$6.MatDateRangeInput, selector: "mat-date-range-input", inputs: ["rangePicker", "required", "dateFilter", "min", "max", "disabled", "separator", "comparisonStart", "comparisonEnd"], exportAs: ["matDateRangeInput"] }, { kind: "directive", type: i1$6.MatStartDate, selector: "input[matStartDate]", outputs: ["dateChange", "dateInput"] }, { kind: "directive", type: i1$6.MatEndDate, selector: "input[matEndDate]", outputs: ["dateChange", "dateInput"] }, { kind: "component", type: i1$6.MatDateRangePicker, selector: "mat-date-range-picker", exportAs: ["matDateRangePicker"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }] }); }
24121
+ provideNgIconsConfig({
24122
+ size: '1.5rem',
24123
+ }),
24124
+ provideLocalizedDateAdapter(),
24125
+ ], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }], ngImport: i0, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"openOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (selectedDatesCount) {\n <div\n class=\"gn-ui-multiselect-counter shrink-0 rounded-full font-bold text-[12px] w-5 h-5 flex items-center justify-center mr-1 selected-count\"\n >\n {{ selectedDatesCount }}\n </div>\n }\n </div>\n <button\n class=\"h-6 w-6 flex items-center justify-center\"\n data-test=\"dropdown-clear\"\n >\n @if (selectedDatesCount && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"clearDates($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n data-test=\"dropdown-toggle\"\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"w-[296px] flex flex-col gap-2 bg-white border border-gray-400 rounded px-4 py-3 shadow-lg text-main text-[16px] leading-[24px]\"\n data-test=\"date-range-panel\"\n >\n <div class=\"flex flex-col justify-center border-b border-gray-300 pb-1\">\n <ng-container\n [ngTemplateOutlet]=\"boundRow\"\n [ngTemplateOutletContext]=\"{\n bound: 'start',\n label: 'daterange.filter.from',\n date: startDate,\n }\"\n ></ng-container>\n @if (expandedBound === 'start') {\n <div class=\"calendar\" animate.enter=\"expand\" animate.leave=\"collapse\">\n <mat-calendar\n data-test=\"start-date-calendar\"\n [selected]=\"startDate\"\n [startAt]=\"startDate ?? endDate\"\n [maxDate]=\"endDate\"\n (selectedChange)=\"setStartDate($event)\"\n ></mat-calendar>\n </div>\n }\n </div>\n <div class=\"flex flex-col justify-center\">\n <ng-container\n [ngTemplateOutlet]=\"boundRow\"\n [ngTemplateOutletContext]=\"{\n bound: 'end',\n label: 'daterange.filter.to',\n date: endDate,\n }\"\n ></ng-container>\n @if (expandedBound === 'end') {\n <div class=\"calendar\" animate.enter=\"expand\" animate.leave=\"collapse\">\n <mat-calendar\n data-test=\"end-date-calendar\"\n [selected]=\"endDate\"\n [startAt]=\"endDate ?? startDate\"\n [minDate]=\"startDate\"\n (selectedChange)=\"setEndDate($event)\"\n ></mat-calendar>\n </div>\n }\n </div>\n </div>\n</ng-template>\n\n<ng-template #boundRow let-bound=\"bound\" let-label=\"label\" let-date=\"date\">\n <div class=\"flex items-center gap-1 w-full\">\n <span class=\"w-[80px] shrink-0\" [translate]=\"label\"></span>\n <div\n class=\"flex-1 min-w-0 flex items-center gap-1 border rounded-lg px-2 py-1 transition-colors\"\n [class]=\"\n invalidBounds[bound]\n ? 'border-red-400 text-red-800 focus-within:border-red-800 hover:border-red-800'\n : 'border-gray-400 focus-within:border-main hover:border-main'\n \"\n >\n <ng-icon\n name=\"iconoirCalendar\"\n class=\"shrink-0\"\n aria-hidden=\"true\"\n ></ng-icon>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n autocomplete=\"off\"\n [gnUiAutofocus]=\"bound === expandedBound\"\n class=\"min-w-0 grow bg-transparent focus:outline-none\"\n [value]=\"date ? formatDate(date) : ''\"\n [placeholder]=\"'daterange.filter.datePlaceholder' | translate\"\n [attr.aria-label]=\"label | translate\"\n [attr.aria-invalid]=\"invalidBounds[bound] || null\"\n [attr.data-test]=\"bound + '-date-input'\"\n (change)=\"onDateInput(bound, $event)\"\n (blur)=\"normalizeDateInput(bound, $event)\"\n (click)=\"expandedBound !== bound && toggleBound(bound)\"\n />\n </div>\n <div class=\"shrink-0 flex items-center justify-end\">\n <gn-ui-button\n type=\"light\"\n [style.--gn-ui-button-padding]=\"0\"\n [style.--gn-ui-button-width]=\"'24px'\"\n [style.--gn-ui-button-height]=\"'24px'\"\n [attr.aria-expanded]=\"expandedBound === bound\"\n [attr.data-test]=\"bound + '-date-toggle'\"\n (buttonClick)=\"toggleBound(bound)\"\n >\n <ng-icon\n [name]=\"expandedBound === bound ? 'matExpandLess' : 'matExpandMore'\"\n ></ng-icon>\n </gn-ui-button>\n </div>\n </div>\n</ng-template>\n", styles: ["mat-calendar{width:100%}.calendar{display:grid;grid-template-rows:1fr}.calendar>mat-calendar{min-height:0;overflow:hidden}.calendar.expand{animation:open .2s ease-out}.calendar.collapse{animation:open .2s ease-in reverse}@keyframes open{0%{grid-template-rows:0fr;opacity:0}to{grid-template-rows:1fr;opacity:1}}@media (prefers-reduced-motion: reduce){.calendar.expand,.calendar.collapse{animation-duration:1ms}}\n"], dependencies: [{ kind: "directive", type: AutofocusDirective, selector: "[gnUiAutofocus]", inputs: ["gnUiAutofocus"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i1$6.MatCalendar, selector: "mat-calendar", inputs: ["headerComponent", "startAt", "startView", "selected", "minDate", "maxDate", "dateFilter", "dateClass", "comparisonStart", "comparisonEnd", "startDateAccessibleName", "endDateAccessibleName"], outputs: ["selectedChange", "yearSelected", "monthSelected", "viewChanged", "_userSelection", "_userDragDrop"], exportAs: ["matCalendar"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
24648
24126
  }
24649
24127
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangeDropdownComponent, decorators: [{
24650
24128
  type: Component,
24651
24129
  args: [{ selector: 'gn-ui-date-range-dropdown', standalone: true, imports: [
24652
- NgIconComponent,
24653
- MatNativeDateModule,
24654
- MatDatepickerModule,
24130
+ AutofocusDirective,
24655
24131
  ButtonComponent,
24132
+ MatDatepickerModule,
24133
+ NgIcon,
24134
+ NgTemplateOutlet,
24135
+ OverlayModule,
24136
+ TranslateDirective,
24137
+ TranslatePipe,
24656
24138
  ], providers: [
24657
24139
  provideIcons({
24658
- matExpandMore,
24140
+ iconoirCalendar,
24141
+ matClose,
24659
24142
  matExpandLess,
24143
+ matExpandMore,
24660
24144
  }),
24661
- ], template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n (buttonClick)=\"picker.open()\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n </div>\n <ng-icon\n [name]=\"isPickerDisplayed ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n<!--date range input is just present for output events and hidden from the DOM-->\n<mat-date-range-input [rangePicker]=\"picker\">\n <input\n matStartDate\n [value]=\"startDate\"\n (dateInput)=\"startDateChange.emit($event.value)\"\n />\n <input\n matEndDate\n [value]=\"endDate\"\n (dateInput)=\"endDateChange.emit($event.value)\"\n />\n</mat-date-range-input>\n<mat-date-range-picker #picker></mat-date-range-picker>\n", styles: [":host .mat-date-range-input-container{display:none}\n"] }]
24662
- }], propDecorators: { title: [{
24663
- type: Input
24664
- }], startDate: [{
24145
+ provideNgIconsConfig({
24146
+ size: '1.5rem',
24147
+ }),
24148
+ provideLocalizedDateAdapter(),
24149
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"openOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (selectedDatesCount) {\n <div\n class=\"gn-ui-multiselect-counter shrink-0 rounded-full font-bold text-[12px] w-5 h-5 flex items-center justify-center mr-1 selected-count\"\n >\n {{ selectedDatesCount }}\n </div>\n }\n </div>\n <button\n class=\"h-6 w-6 flex items-center justify-center\"\n data-test=\"dropdown-clear\"\n >\n @if (selectedDatesCount && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"clearDates($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n data-test=\"dropdown-toggle\"\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"w-[296px] flex flex-col gap-2 bg-white border border-gray-400 rounded px-4 py-3 shadow-lg text-main text-[16px] leading-[24px]\"\n data-test=\"date-range-panel\"\n >\n <div class=\"flex flex-col justify-center border-b border-gray-300 pb-1\">\n <ng-container\n [ngTemplateOutlet]=\"boundRow\"\n [ngTemplateOutletContext]=\"{\n bound: 'start',\n label: 'daterange.filter.from',\n date: startDate,\n }\"\n ></ng-container>\n @if (expandedBound === 'start') {\n <div class=\"calendar\" animate.enter=\"expand\" animate.leave=\"collapse\">\n <mat-calendar\n data-test=\"start-date-calendar\"\n [selected]=\"startDate\"\n [startAt]=\"startDate ?? endDate\"\n [maxDate]=\"endDate\"\n (selectedChange)=\"setStartDate($event)\"\n ></mat-calendar>\n </div>\n }\n </div>\n <div class=\"flex flex-col justify-center\">\n <ng-container\n [ngTemplateOutlet]=\"boundRow\"\n [ngTemplateOutletContext]=\"{\n bound: 'end',\n label: 'daterange.filter.to',\n date: endDate,\n }\"\n ></ng-container>\n @if (expandedBound === 'end') {\n <div class=\"calendar\" animate.enter=\"expand\" animate.leave=\"collapse\">\n <mat-calendar\n data-test=\"end-date-calendar\"\n [selected]=\"endDate\"\n [startAt]=\"endDate ?? startDate\"\n [minDate]=\"startDate\"\n (selectedChange)=\"setEndDate($event)\"\n ></mat-calendar>\n </div>\n }\n </div>\n </div>\n</ng-template>\n\n<ng-template #boundRow let-bound=\"bound\" let-label=\"label\" let-date=\"date\">\n <div class=\"flex items-center gap-1 w-full\">\n <span class=\"w-[80px] shrink-0\" [translate]=\"label\"></span>\n <div\n class=\"flex-1 min-w-0 flex items-center gap-1 border rounded-lg px-2 py-1 transition-colors\"\n [class]=\"\n invalidBounds[bound]\n ? 'border-red-400 text-red-800 focus-within:border-red-800 hover:border-red-800'\n : 'border-gray-400 focus-within:border-main hover:border-main'\n \"\n >\n <ng-icon\n name=\"iconoirCalendar\"\n class=\"shrink-0\"\n aria-hidden=\"true\"\n ></ng-icon>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n autocomplete=\"off\"\n [gnUiAutofocus]=\"bound === expandedBound\"\n class=\"min-w-0 grow bg-transparent focus:outline-none\"\n [value]=\"date ? formatDate(date) : ''\"\n [placeholder]=\"'daterange.filter.datePlaceholder' | translate\"\n [attr.aria-label]=\"label | translate\"\n [attr.aria-invalid]=\"invalidBounds[bound] || null\"\n [attr.data-test]=\"bound + '-date-input'\"\n (change)=\"onDateInput(bound, $event)\"\n (blur)=\"normalizeDateInput(bound, $event)\"\n (click)=\"expandedBound !== bound && toggleBound(bound)\"\n />\n </div>\n <div class=\"shrink-0 flex items-center justify-end\">\n <gn-ui-button\n type=\"light\"\n [style.--gn-ui-button-padding]=\"0\"\n [style.--gn-ui-button-width]=\"'24px'\"\n [style.--gn-ui-button-height]=\"'24px'\"\n [attr.aria-expanded]=\"expandedBound === bound\"\n [attr.data-test]=\"bound + '-date-toggle'\"\n (buttonClick)=\"toggleBound(bound)\"\n >\n <ng-icon\n [name]=\"expandedBound === bound ? 'matExpandLess' : 'matExpandMore'\"\n ></ng-icon>\n </gn-ui-button>\n </div>\n </div>\n</ng-template>\n", styles: ["mat-calendar{width:100%}.calendar{display:grid;grid-template-rows:1fr}.calendar>mat-calendar{min-height:0;overflow:hidden}.calendar.expand{animation:open .2s ease-out}.calendar.collapse{animation:open .2s ease-in reverse}@keyframes open{0%{grid-template-rows:0fr;opacity:0}to{grid-template-rows:1fr;opacity:1}}@media (prefers-reduced-motion: reduce){.calendar.expand,.calendar.collapse{animation-duration:1ms}}\n"] }]
24150
+ }], ctorParameters: () => [], propDecorators: { title: [{
24665
24151
  type: Input
24666
- }], endDate: [{
24152
+ }], dateRange: [{
24667
24153
  type: Input
24668
- }], startDateChange: [{
24154
+ }], dateRangeChange: [{
24669
24155
  type: Output
24670
- }], endDateChange: [{
24671
- type: Output
24672
- }], picker: [{
24156
+ }], overlayOrigin: [{
24673
24157
  type: ViewChild,
24674
- args: ['picker']
24158
+ args: ['overlayOrigin']
24675
24159
  }] } });
24676
24160
 
24677
24161
  class DateRangePickerComponent {
@@ -24685,20 +24169,17 @@ class DateRangePickerComponent {
24685
24169
  provideNgIconsConfig({
24686
24170
  size: '1.5rem',
24687
24171
  }),
24688
- ], ngImport: i0, template: "<span class=\"w-full inline-block relative\">\n <mat-date-range-input [rangePicker]=\"picker\" class=\"gn-ui-text-input\">\n <input\n matStartDate\n placeholder=\"Start date\"\n [value]=\"startDate\"\n (dateInput)=\"startDateChange.emit($event.value)\"\n />\n <input\n matEndDate\n placeholder=\"End date\"\n [value]=\"endDate\"\n (dateInput)=\"endDateChange.emit($event.value)\"\n />\n </mat-date-range-input>\n\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n extraClass=\"h-full\"\n data-cy=\"date-picker-button\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-date-range-picker #picker></mat-date-range-picker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"], dependencies: [{ kind: "ngmodule", type: MatNativeDateModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i1$6.MatDateRangeInput, selector: "mat-date-range-input", inputs: ["rangePicker", "required", "dateFilter", "min", "max", "disabled", "separator", "comparisonStart", "comparisonEnd"], exportAs: ["matDateRangeInput"] }, { kind: "directive", type: i1$6.MatStartDate, selector: "input[matStartDate]", outputs: ["dateChange", "dateInput"] }, { kind: "directive", type: i1$6.MatEndDate, selector: "input[matEndDate]", outputs: ["dateChange", "dateInput"] }, { kind: "component", type: i1$6.MatDateRangePicker, selector: "mat-date-range-picker", exportAs: ["matDateRangePicker"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
24172
+ provideLocalizedDateAdapter(),
24173
+ ], ngImport: i0, template: "<span class=\"w-full inline-block relative\">\n <mat-date-range-input [rangePicker]=\"picker\" class=\"gn-ui-text-input\">\n <input\n matStartDate\n placeholder=\"Start date\"\n [value]=\"startDate\"\n (dateInput)=\"startDateChange.emit($event.value)\"\n />\n <input\n matEndDate\n placeholder=\"End date\"\n [value]=\"endDate\"\n (dateInput)=\"endDateChange.emit($event.value)\"\n />\n </mat-date-range-input>\n\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n extraClass=\"h-full\"\n data-cy=\"date-picker-button\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-date-range-picker #picker></mat-date-range-picker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"], dependencies: [{ kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i1$6.MatDateRangeInput, selector: "mat-date-range-input", inputs: ["rangePicker", "required", "dateFilter", "min", "max", "disabled", "separator", "comparisonStart", "comparisonEnd"], exportAs: ["matDateRangeInput"] }, { kind: "directive", type: i1$6.MatStartDate, selector: "input[matStartDate]", outputs: ["dateChange", "dateInput"] }, { kind: "directive", type: i1$6.MatEndDate, selector: "input[matEndDate]", outputs: ["dateChange", "dateInput"] }, { kind: "component", type: i1$6.MatDateRangePicker, selector: "mat-date-range-picker", exportAs: ["matDateRangePicker"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
24689
24174
  }
24690
24175
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangePickerComponent, decorators: [{
24691
24176
  type: Component,
24692
- args: [{ selector: 'gn-ui-date-range-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
24693
- MatNativeDateModule,
24694
- MatDatepickerModule,
24695
- ButtonComponent,
24696
- NgIconComponent,
24697
- ], providers: [
24177
+ args: [{ selector: 'gn-ui-date-range-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [MatDatepickerModule, ButtonComponent, NgIconComponent], providers: [
24698
24178
  provideIcons({ iconoirCalendar }),
24699
24179
  provideNgIconsConfig({
24700
24180
  size: '1.5rem',
24701
24181
  }),
24182
+ provideLocalizedDateAdapter(),
24702
24183
  ], template: "<span class=\"w-full inline-block relative\">\n <mat-date-range-input [rangePicker]=\"picker\" class=\"gn-ui-text-input\">\n <input\n matStartDate\n placeholder=\"Start date\"\n [value]=\"startDate\"\n (dateInput)=\"startDateChange.emit($event.value)\"\n />\n <input\n matEndDate\n placeholder=\"End date\"\n [value]=\"endDate\"\n (dateInput)=\"endDateChange.emit($event.value)\"\n />\n </mat-date-range-input>\n\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n extraClass=\"h-full\"\n data-cy=\"date-picker-button\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-date-range-picker #picker></mat-date-range-picker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"] }]
24703
24184
  }], propDecorators: { startDate: [{
24704
24185
  type: Input
@@ -24716,28 +24197,68 @@ class DragAndDropFileInputComponent {
24716
24197
  constructor() {
24717
24198
  this.placeholder = placeholder;
24718
24199
  this.accept = '*';
24200
+ this.maxFileSizeMb = null;
24201
+ this.icon = null;
24202
+ this.dropzoneBackgroundColor = null;
24203
+ this.textClass = '';
24204
+ this.extraClass = '';
24205
+ this.showFileName = true;
24719
24206
  this.fileChange = new EventEmitter();
24207
+ this.errorChange = new EventEmitter();
24720
24208
  this.selectedFile = null;
24721
24209
  }
24722
24210
  get fileName() {
24723
24211
  return this.selectedFile && this.selectedFile.name;
24724
24212
  }
24213
+ get maxFileSizeBytes() {
24214
+ return typeof this.maxFileSizeMb === 'number'
24215
+ ? megabytesToBytes(this.maxFileSizeMb)
24216
+ : null;
24217
+ }
24725
24218
  selectFile(event) {
24219
+ if (event.rejectedFiles?.length) {
24220
+ const reason = event.rejectedFiles[0].reason;
24221
+ this.errorChange.emit(reason === 'size' ? 'file-too-large' : 'invalid-extension');
24222
+ return;
24223
+ }
24726
24224
  this.selectedFile = event.addedFiles[0];
24727
24225
  this.fileChange.emit(this.selectedFile);
24728
24226
  }
24227
+ openFileSelector() {
24228
+ this.dropzone.showFileSelector();
24229
+ }
24230
+ clear() {
24231
+ this.selectedFile = null;
24232
+ }
24729
24233
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DragAndDropFileInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24730
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DragAndDropFileInputComponent, isStandalone: true, selector: "gn-ui-drag-and-drop-file-input", inputs: { placeholder: "placeholder", accept: "accept" }, outputs: { fileChange: "fileChange" }, ngImport: i0, template: "<div class=\"flex h-full\">\n <ngx-dropzone\n class=\"flex-1\"\n [multiple]=\"false\"\n (change)=\"selectFile($event)\"\n [accept]=\"accept\"\n >\n @if (!fileName) {\n <div class=\"text-gray-900 pl-2 py-2\" translate=\"\">\n {{ placeholder }}\n </div>\n }\n\n @if (fileName) {\n <div class=\"text-gray-900 pl-2 py-2\">{{ fileName }}</div>\n }\n </ngx-dropzone>\n</div>\n", styles: ["ngx-dropzone{height:auto;border:none}\n"], dependencies: [{ kind: "ngmodule", type: NgxDropzoneModule }, { kind: "component", type: i1$7.NgxDropzoneComponent, selector: "ngx-dropzone, [ngx-dropzone]", inputs: ["accept", "disabled", "multiple", "maxFileSize", "expandable", "disableClick", "processDirectoryDrop", "id", "aria-label", "aria-labelledby", "aria-describedby"], outputs: ["change"] }] }); }
24234
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DragAndDropFileInputComponent, isStandalone: true, selector: "gn-ui-drag-and-drop-file-input", inputs: { placeholder: "placeholder", accept: "accept", maxFileSizeMb: "maxFileSizeMb", icon: "icon", dropzoneBackgroundColor: "dropzoneBackgroundColor", textClass: "textClass", extraClass: "extraClass", showFileName: "showFileName" }, outputs: { fileChange: "fileChange", errorChange: "errorChange" }, viewQueries: [{ propertyName: "dropzone", first: true, predicate: NgxDropzoneComponent, descendants: true }], ngImport: i0, template: "<div\n class=\"flex h-full items-center\"\n [class]=\"extraClass\"\n (click)=\"openFileSelector()\"\n>\n <ngx-dropzone\n class=\"flex-1 overflow-hidden\"\n [multiple]=\"false\"\n [disableClick]=\"true\"\n [accept]=\"accept\"\n [maxFileSize]=\"maxFileSizeBytes\"\n [style.background-color]=\"dropzoneBackgroundColor\"\n (change)=\"selectFile($event)\"\n >\n @if (!fileName || !showFileName) {\n <div [class]=\"textClass\">\n {{ placeholder }}\n </div>\n }\n\n @if (fileName && showFileName) {\n <div [class]=\"textClass\">\n {{ fileName }}\n </div>\n }\n </ngx-dropzone>\n\n @if (icon) {\n <ng-icon [name]=\"icon\" class=\"shrink-0 text-gray-950\"></ng-icon>\n }\n</div>\n", styles: ["ngx-dropzone{height:auto;border:none;overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: NgxDropzoneModule }, { kind: "component", type: i1$8.NgxDropzoneComponent, selector: "ngx-dropzone, [ngx-dropzone]", inputs: ["accept", "disabled", "multiple", "maxFileSize", "expandable", "disableClick", "processDirectoryDrop", "id", "aria-label", "aria-labelledby", "aria-describedby"], outputs: ["change"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }] }); }
24731
24235
  }
24732
24236
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DragAndDropFileInputComponent, decorators: [{
24733
24237
  type: Component,
24734
- args: [{ selector: 'gn-ui-drag-and-drop-file-input', standalone: true, imports: [NgxDropzoneModule], template: "<div class=\"flex h-full\">\n <ngx-dropzone\n class=\"flex-1\"\n [multiple]=\"false\"\n (change)=\"selectFile($event)\"\n [accept]=\"accept\"\n >\n @if (!fileName) {\n <div class=\"text-gray-900 pl-2 py-2\" translate=\"\">\n {{ placeholder }}\n </div>\n }\n\n @if (fileName) {\n <div class=\"text-gray-900 pl-2 py-2\">{{ fileName }}</div>\n }\n </ngx-dropzone>\n</div>\n", styles: ["ngx-dropzone{height:auto;border:none}\n"] }]
24238
+ args: [{ selector: 'gn-ui-drag-and-drop-file-input', standalone: true, imports: [NgxDropzoneModule, NgIcon], template: "<div\n class=\"flex h-full items-center\"\n [class]=\"extraClass\"\n (click)=\"openFileSelector()\"\n>\n <ngx-dropzone\n class=\"flex-1 overflow-hidden\"\n [multiple]=\"false\"\n [disableClick]=\"true\"\n [accept]=\"accept\"\n [maxFileSize]=\"maxFileSizeBytes\"\n [style.background-color]=\"dropzoneBackgroundColor\"\n (change)=\"selectFile($event)\"\n >\n @if (!fileName || !showFileName) {\n <div [class]=\"textClass\">\n {{ placeholder }}\n </div>\n }\n\n @if (fileName && showFileName) {\n <div [class]=\"textClass\">\n {{ fileName }}\n </div>\n }\n </ngx-dropzone>\n\n @if (icon) {\n <ng-icon [name]=\"icon\" class=\"shrink-0 text-gray-950\"></ng-icon>\n }\n</div>\n", styles: ["ngx-dropzone{height:auto;border:none;overflow:hidden}\n"] }]
24735
24239
  }], propDecorators: { placeholder: [{
24736
24240
  type: Input
24737
24241
  }], accept: [{
24738
24242
  type: Input
24243
+ }], maxFileSizeMb: [{
24244
+ type: Input
24245
+ }], icon: [{
24246
+ type: Input
24247
+ }], dropzoneBackgroundColor: [{
24248
+ type: Input
24249
+ }], textClass: [{
24250
+ type: Input
24251
+ }], extraClass: [{
24252
+ type: Input
24253
+ }], showFileName: [{
24254
+ type: Input
24739
24255
  }], fileChange: [{
24740
24256
  type: Output
24257
+ }], errorChange: [{
24258
+ type: Output
24259
+ }], dropzone: [{
24260
+ type: ViewChild,
24261
+ args: [NgxDropzoneComponent]
24741
24262
  }] } });
24742
24263
 
24743
24264
  class DropdownMultiselectComponent {
@@ -24786,7 +24307,7 @@ class DropdownMultiselectComponent {
24786
24307
  }
24787
24308
  setFocus() {
24788
24309
  setTimeout(() => {
24789
- this.searchFieldInput.nativeElement.focus();
24310
+ this.searchFieldInput?.nativeElement.focus();
24790
24311
  }, 0);
24791
24312
  }
24792
24313
  openOverlay() {
@@ -24892,7 +24413,10 @@ class DropdownMultiselectComponent {
24892
24413
  matExpandMore,
24893
24414
  matExpandLess,
24894
24415
  }),
24895
- ], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "overlayContainer", first: true, predicate: ["overlayContainer"], descendants: true, read: ElementRef }, { propertyName: "searchFieldInput", first: true, predicate: ["searchFieldInput"], descendants: true }, { propertyName: "checkboxes", predicate: ["checkBox"], descendants: true, read: ElementRef }], ngImport: i0, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelectedChoices) {\n <div\n class=\"gn-ui-multiselect-counter shrink-0 rounded-full font-bold text-[12px] w-5 h-5 flex items-center justify-center mr-1 selected-count\"\n >\n {{ selected.length }}\n </div>\n }\n </div>\n <button class=\"h-6 w-6\" data-cy=\"clearSelection\">\n @if (hasSelectedChoices && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"clearSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container\"\n [style.max-height]=\"overlayMaxHeight\"\n [style.min-width]=\"overlayMinWidth\"\n role=\"listbox\"\n tabindex=\"-1\"\n [attr.id]=\"id\"\n [attr.aria-multiselectable]=\"true\"\n [attr.aria-label]=\"title\"\n (keydown)=\"handleOverlayKeydown($event)\"\n #overlayContainer\n >\n <div\n class=\"border border-gray-300 rounded mb-2 mx-2 min-h-[44px] flex flex-row gap-[2px] flex-wrap p-2 focus-within:rounded focus-within:border-2 focus-within:border-primary\"\n >\n @for (selected of selectedChoices; track selected) {\n <button\n type=\"button\"\n [title]=\"selected.label\"\n class=\"max-w-full bg-main text-white rounded pr-[7px] flex gap-1 items-center opacity-70 hover:opacity-100 focus:opacity-100 transition-opacity mb-1\"\n (click)=\"select(selected, false)\"\n >\n <div class=\"text-sm truncate leading-[26px] px-2\">\n {{ selected.label }}\n </div>\n <div\n class=\"flex items-center justify-center rounded-full bg-white text-main h-[13px] w-[13px] pt-px -mt-px shrink-0\"\n >\n <ng-icon\n name=\"matClose\"\n class=\"!h-[12px] !w-[11px] text-[12px]\"\n ></ng-icon>\n </div>\n </button>\n }\n\n @if (allowSearch) {\n <div class=\"w-[50%] relative grow shrink\">\n <input\n #searchFieldInput\n class=\"w-full px-2 truncate text-[14px] h-full overlaySearchInput focus:outline-none\"\n [(ngModel)]=\"searchInputValue\"\n [placeholder]=\"'multiselect.filter.placeholder' | translate\"\n />\n @if (!!searchInputValue) {\n <button\n class=\"absolute top-1/2 -translate-y-1/2 right-0 px-[7px] leading-tight clear-search-input mr-2\"\n (click)=\"clearSearchInputValue($event)\"\n >\n <ng-icon class=\"!h-[10px] !w-[12px] text-[12px]\" name=\"matClose\">\n </ng-icon>\n </button>\n }\n </div>\n }\n </div>\n\n @for (choice of filteredChoicesByText; track choice) {\n <label\n [title]=\"choice.label\"\n class=\"flex px-5 py-1 w-full text-gray-900 cursor-pointer hover:text-primary-darkest hover:bg-gray-50 focus-within:text-primary-darkest focus-within:bg-gray-50 transition-colors\"\n >\n <input\n class=\"w-[18px] h-[18px] align-text-top shrink-0\"\n type=\"checkbox\"\n #checkBox\n [checked]=\"isSelected(choice)\"\n (change)=\"select(choice, checkBox.checked)\"\n />\n <span class=\"ml-[8px] text-[14px] truncate\">\n {{ choice.label }}\n </span>\n </label>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$8.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$8.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
24416
+ provideNgIconsConfig({
24417
+ size: '1.5rem',
24418
+ }),
24419
+ ], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "overlayContainer", first: true, predicate: ["overlayContainer"], descendants: true, read: ElementRef }, { propertyName: "searchFieldInput", first: true, predicate: ["searchFieldInput"], descendants: true }, { propertyName: "checkboxes", predicate: ["checkBox"], descendants: true, read: ElementRef }], ngImport: i0, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelectedChoices) {\n <div\n class=\"gn-ui-multiselect-counter shrink-0 rounded-full font-bold text-[12px] w-5 h-5 flex items-center justify-center mr-1 selected-count\"\n >\n {{ selected.length }}\n </div>\n }\n </div>\n <button\n class=\"h-6 w-6 flex items-center justify-center\"\n data-test=\"dropdown-clear\"\n >\n @if (hasSelectedChoices && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"clearSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container\"\n [style.max-height]=\"overlayMaxHeight\"\n [style.min-width]=\"overlayMinWidth\"\n role=\"listbox\"\n tabindex=\"-1\"\n [attr.id]=\"id\"\n [attr.aria-multiselectable]=\"true\"\n [attr.aria-label]=\"title\"\n (keydown)=\"handleOverlayKeydown($event)\"\n #overlayContainer\n >\n <div\n class=\"border border-gray-300 rounded mb-2 mx-2 min-h-[44px] flex flex-row gap-1 flex-wrap p-2 focus-within:rounded focus-within:border-2 focus-within:border-primary\"\n >\n @for (selected of selectedChoices; track selected) {\n <button\n type=\"button\"\n [title]=\"selected.label\"\n class=\"max-w-full bg-main text-white rounded pr-[7px] flex gap-1 items-center opacity-70 hover:opacity-100 focus:opacity-100 transition-opacity\"\n (click)=\"select(selected, false)\"\n >\n <div class=\"text-sm truncate leading-[26px] px-2\">\n {{ selected.label }}\n </div>\n <div\n class=\"flex items-center justify-center rounded-full bg-white text-main h-[13px] w-[13px] pt-px -mt-px shrink-0\"\n >\n <ng-icon\n name=\"matClose\"\n class=\"!h-[12px] !w-[11px] text-[12px]\"\n ></ng-icon>\n </div>\n </button>\n }\n\n @if (allowSearch) {\n <div class=\"w-[50%] relative grow shrink\">\n <input\n #searchFieldInput\n class=\"w-full px-2 truncate text-[14px] h-full overlaySearchInput focus:outline-none\"\n [(ngModel)]=\"searchInputValue\"\n [placeholder]=\"'multiselect.filter.placeholder' | translate\"\n />\n @if (!!searchInputValue) {\n <button\n class=\"absolute top-1/2 -translate-y-1/2 right-0 px-[7px] leading-tight clear-search-input mr-2\"\n (click)=\"clearSearchInputValue($event)\"\n >\n <ng-icon class=\"!h-[10px] !w-[12px] text-[12px]\" name=\"matClose\">\n </ng-icon>\n </button>\n }\n </div>\n }\n </div>\n\n @for (choice of filteredChoicesByText; track choice) {\n <label\n [title]=\"choice.label\"\n class=\"flex px-5 py-1 w-full text-gray-900 cursor-pointer hover:text-primary-darkest hover:bg-gray-50 focus-within:text-primary-darkest focus-within:bg-gray-50 transition-colors\"\n >\n <input\n class=\"w-[18px] h-[18px] align-text-top shrink-0\"\n type=\"checkbox\"\n #checkBox\n [checked]=\"isSelected(choice)\"\n (change)=\"select(choice, checkBox.checked)\"\n />\n <span class=\"ml-[8px] text-[14px] truncate\">\n {{ choice.label }}\n </span>\n </label>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
24896
24420
  }
24897
24421
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DropdownMultiselectComponent, decorators: [{
24898
24422
  type: Component,
@@ -24902,7 +24426,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
24902
24426
  matExpandMore,
24903
24427
  matExpandLess,
24904
24428
  }),
24905
- ], standalone: true, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelectedChoices) {\n <div\n class=\"gn-ui-multiselect-counter shrink-0 rounded-full font-bold text-[12px] w-5 h-5 flex items-center justify-center mr-1 selected-count\"\n >\n {{ selected.length }}\n </div>\n }\n </div>\n <button class=\"h-6 w-6\" data-cy=\"clearSelection\">\n @if (hasSelectedChoices && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"clearSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container\"\n [style.max-height]=\"overlayMaxHeight\"\n [style.min-width]=\"overlayMinWidth\"\n role=\"listbox\"\n tabindex=\"-1\"\n [attr.id]=\"id\"\n [attr.aria-multiselectable]=\"true\"\n [attr.aria-label]=\"title\"\n (keydown)=\"handleOverlayKeydown($event)\"\n #overlayContainer\n >\n <div\n class=\"border border-gray-300 rounded mb-2 mx-2 min-h-[44px] flex flex-row gap-[2px] flex-wrap p-2 focus-within:rounded focus-within:border-2 focus-within:border-primary\"\n >\n @for (selected of selectedChoices; track selected) {\n <button\n type=\"button\"\n [title]=\"selected.label\"\n class=\"max-w-full bg-main text-white rounded pr-[7px] flex gap-1 items-center opacity-70 hover:opacity-100 focus:opacity-100 transition-opacity mb-1\"\n (click)=\"select(selected, false)\"\n >\n <div class=\"text-sm truncate leading-[26px] px-2\">\n {{ selected.label }}\n </div>\n <div\n class=\"flex items-center justify-center rounded-full bg-white text-main h-[13px] w-[13px] pt-px -mt-px shrink-0\"\n >\n <ng-icon\n name=\"matClose\"\n class=\"!h-[12px] !w-[11px] text-[12px]\"\n ></ng-icon>\n </div>\n </button>\n }\n\n @if (allowSearch) {\n <div class=\"w-[50%] relative grow shrink\">\n <input\n #searchFieldInput\n class=\"w-full px-2 truncate text-[14px] h-full overlaySearchInput focus:outline-none\"\n [(ngModel)]=\"searchInputValue\"\n [placeholder]=\"'multiselect.filter.placeholder' | translate\"\n />\n @if (!!searchInputValue) {\n <button\n class=\"absolute top-1/2 -translate-y-1/2 right-0 px-[7px] leading-tight clear-search-input mr-2\"\n (click)=\"clearSearchInputValue($event)\"\n >\n <ng-icon class=\"!h-[10px] !w-[12px] text-[12px]\" name=\"matClose\">\n </ng-icon>\n </button>\n }\n </div>\n }\n </div>\n\n @for (choice of filteredChoicesByText; track choice) {\n <label\n [title]=\"choice.label\"\n class=\"flex px-5 py-1 w-full text-gray-900 cursor-pointer hover:text-primary-darkest hover:bg-gray-50 focus-within:text-primary-darkest focus-within:bg-gray-50 transition-colors\"\n >\n <input\n class=\"w-[18px] h-[18px] align-text-top shrink-0\"\n type=\"checkbox\"\n #checkBox\n [checked]=\"isSelected(choice)\"\n (change)=\"select(choice, checkBox.checked)\"\n />\n <span class=\"ml-[8px] text-[14px] truncate\">\n {{ choice.label }}\n </span>\n </label>\n }\n </div>\n</ng-template>\n" }]
24429
+ provideNgIconsConfig({
24430
+ size: '1.5rem',
24431
+ }),
24432
+ ], standalone: true, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelectedChoices) {\n <div\n class=\"gn-ui-multiselect-counter shrink-0 rounded-full font-bold text-[12px] w-5 h-5 flex items-center justify-center mr-1 selected-count\"\n >\n {{ selected.length }}\n </div>\n }\n </div>\n <button\n class=\"h-6 w-6 flex items-center justify-center\"\n data-test=\"dropdown-clear\"\n >\n @if (hasSelectedChoices && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"clearSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container\"\n [style.max-height]=\"overlayMaxHeight\"\n [style.min-width]=\"overlayMinWidth\"\n role=\"listbox\"\n tabindex=\"-1\"\n [attr.id]=\"id\"\n [attr.aria-multiselectable]=\"true\"\n [attr.aria-label]=\"title\"\n (keydown)=\"handleOverlayKeydown($event)\"\n #overlayContainer\n >\n <div\n class=\"border border-gray-300 rounded mb-2 mx-2 min-h-[44px] flex flex-row gap-1 flex-wrap p-2 focus-within:rounded focus-within:border-2 focus-within:border-primary\"\n >\n @for (selected of selectedChoices; track selected) {\n <button\n type=\"button\"\n [title]=\"selected.label\"\n class=\"max-w-full bg-main text-white rounded pr-[7px] flex gap-1 items-center opacity-70 hover:opacity-100 focus:opacity-100 transition-opacity\"\n (click)=\"select(selected, false)\"\n >\n <div class=\"text-sm truncate leading-[26px] px-2\">\n {{ selected.label }}\n </div>\n <div\n class=\"flex items-center justify-center rounded-full bg-white text-main h-[13px] w-[13px] pt-px -mt-px shrink-0\"\n >\n <ng-icon\n name=\"matClose\"\n class=\"!h-[12px] !w-[11px] text-[12px]\"\n ></ng-icon>\n </div>\n </button>\n }\n\n @if (allowSearch) {\n <div class=\"w-[50%] relative grow shrink\">\n <input\n #searchFieldInput\n class=\"w-full px-2 truncate text-[14px] h-full overlaySearchInput focus:outline-none\"\n [(ngModel)]=\"searchInputValue\"\n [placeholder]=\"'multiselect.filter.placeholder' | translate\"\n />\n @if (!!searchInputValue) {\n <button\n class=\"absolute top-1/2 -translate-y-1/2 right-0 px-[7px] leading-tight clear-search-input mr-2\"\n (click)=\"clearSearchInputValue($event)\"\n >\n <ng-icon class=\"!h-[10px] !w-[12px] text-[12px]\" name=\"matClose\">\n </ng-icon>\n </button>\n }\n </div>\n }\n </div>\n\n @for (choice of filteredChoicesByText; track choice) {\n <label\n [title]=\"choice.label\"\n class=\"flex px-5 py-1 w-full text-gray-900 cursor-pointer hover:text-primary-darkest hover:bg-gray-50 focus-within:text-primary-darkest focus-within:bg-gray-50 transition-colors\"\n >\n <input\n class=\"w-[18px] h-[18px] align-text-top shrink-0\"\n type=\"checkbox\"\n #checkBox\n [checked]=\"isSelected(choice)\"\n (change)=\"select(choice, checkBox.checked)\"\n />\n <span class=\"ml-[8px] text-[14px] truncate\">\n {{ choice.label }}\n </span>\n </label>\n }\n </div>\n</ng-template>\n" }]
24906
24433
  }], propDecorators: { title: [{
24907
24434
  type: Input
24908
24435
  }], choices: [{
@@ -25074,7 +24601,7 @@ class DropdownSelectorComponent {
25074
24601
  provideNgIconsConfig({
25075
24602
  size: '1.5em',
25076
24603
  }),
25077
- ], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "choiceInputs", predicate: ["choiceInputs"], descendants: true, read: ElementRef }], ngImport: i0, template: "<div class=\"flex flex-col sm:flex-row sm:items-center relative w-full\">\n @if (showTitle) {\n <span\n class=\"tracking-wide text-sm mb-2 sm:mb-0 sm:mr-2 whitespace-nowrap\"\n [attr.for]=\"id\"\n >\n {{ title }}\n </span>\n }\n <gn-ui-button\n type=\"outline\"\n class=\"grow min-w-0\"\n [style.--gn-ui-button-padding]=\"'8px 8px 8px 16px'\"\n extraClass=\"bg-background flex flex-row w-full {{ extraBtnClass }}\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n (keydown)=\"handleTriggerKeydown($event)\"\n [disabled]=\"disabled\"\n >\n <div class=\"grow truncate py-1 mr-2 text-left\">\n {{ getChoiceLabel() | translate }}\n </div>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n </gn-ui-button>\n</div>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container\"\n [style.max-height]=\"overlayMaxHeight\"\n [style.min-width]=\"overlayWidth\"\n role=\"listbox\"\n tabindex=\"-1\"\n [attr.id]=\"id\"\n [attr.aria-multiselectable]=\"true\"\n [attr.aria-label]=\"title\"\n (keydown)=\"handleOverlayKeydown($event)\"\n >\n @for (choice of choices; track choice) {\n <button\n #choiceInputs\n type=\"button\"\n [title]=\"choice.label | translate\"\n class=\"flex px-5 py-1 w-full text-start cursor-pointer transition-colors\"\n [ngClass]=\"\n isSelected(choice)\n ? 'text-white bg-primary hover:text-white hover:bg-primary-darker focus:text-white focus:bg-primary-darker'\n : 'text-gray-900 hover:text-primary-darkest hover:bg-gray-50 focus:text-primary-darkest focus:bg-gray-50'\n \"\n (click)=\"onSelectValue(choice)\"\n (keydown)=\"selectIfEnter($event, choice)\"\n [attr.data-cy-value]=\"choice.value.toString()\"\n [attr.data-cy-active]=\"isSelected(choice) ? 'true' : undefined\"\n >\n <span class=\"text-[14px]\">\n {{ choice.label | translate }}\n </span>\n </button>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$8.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$8.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
24604
+ ], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "choiceInputs", predicate: ["choiceInputs"], descendants: true, read: ElementRef }], ngImport: i0, template: "<div class=\"flex flex-col sm:flex-row sm:items-center relative w-full\">\n @if (showTitle) {\n <span\n class=\"tracking-wide text-sm mb-2 sm:mb-0 sm:mr-2 whitespace-nowrap\"\n [attr.for]=\"id\"\n >\n {{ title }}\n </span>\n }\n <gn-ui-button\n type=\"outline\"\n class=\"grow min-w-0\"\n [style.--gn-ui-button-padding]=\"'8px 8px 8px 16px'\"\n extraClass=\"bg-background flex flex-row w-full {{ extraBtnClass }}\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n (keydown)=\"handleTriggerKeydown($event)\"\n [disabled]=\"disabled\"\n >\n <div class=\"grow truncate py-1 mr-2 text-left\">\n {{ getChoiceLabel() | translate }}\n </div>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n </gn-ui-button>\n</div>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container\"\n [style.max-height]=\"overlayMaxHeight\"\n [style.min-width]=\"overlayWidth\"\n role=\"listbox\"\n tabindex=\"-1\"\n [attr.id]=\"id\"\n [attr.aria-multiselectable]=\"true\"\n [attr.aria-label]=\"title\"\n (keydown)=\"handleOverlayKeydown($event)\"\n >\n @for (choice of choices; track choice) {\n <button\n #choiceInputs\n type=\"button\"\n [title]=\"choice.label | translate\"\n class=\"flex px-5 py-1 w-full text-start cursor-pointer transition-colors\"\n [ngClass]=\"\n isSelected(choice)\n ? 'text-white bg-primary hover:text-white hover:bg-primary-darker focus:text-white focus:bg-primary-darker'\n : 'text-gray-900 hover:text-primary-darkest hover:bg-gray-50 focus:text-primary-darkest focus:bg-gray-50'\n \"\n (click)=\"onSelectValue(choice)\"\n (keydown)=\"selectIfEnter($event, choice)\"\n [attr.data-cy-value]=\"choice.value.toString()\"\n [attr.data-cy-active]=\"isSelected(choice) ? 'true' : undefined\"\n >\n <span class=\"text-[14px]\">\n {{ choice.label | translate }}\n </span>\n </button>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
25078
24605
  }
25079
24606
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DropdownSelectorComponent, decorators: [{
25080
24607
  type: Component,
@@ -25814,6 +25341,180 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
25814
25341
  type: Output
25815
25342
  }] } });
25816
25343
 
25344
+ marker('search.filters.spatialExtent.import');
25345
+ marker('search.filters.spatialExtent.helpText');
25346
+ marker('search.filters.spatialExtent.error.title');
25347
+ marker('search.filters.spatialExtent.error.outOfBounds');
25348
+ const LABEL_FROM_FILE = marker('search.filters.spatialExtent.bboxFromFile');
25349
+ const LABEL_FROM_FILE_DELETE = marker('search.filters.spatialExtent.bboxFromFileDelete');
25350
+ const LABEL_INITIAL = marker('search.filters.spatialExtent.bboxInitial');
25351
+ const LABEL_INITIAL_DELETE = marker('search.filters.spatialExtent.bboxInitialDelete');
25352
+ class SpatialExtentDropdownComponent {
25353
+ constructor() {
25354
+ this.cd = inject(ChangeDetectorRef);
25355
+ this.scrollStrategies = inject(ScrollStrategyOptions);
25356
+ this.maxFileSizeMb = null;
25357
+ this.bboxChange = new EventEmitter();
25358
+ this.errorChange = new EventEmitter();
25359
+ this.bbox = null;
25360
+ this.fileName = '';
25361
+ this.overlayPositions = [
25362
+ {
25363
+ originX: 'start',
25364
+ originY: 'bottom',
25365
+ overlayX: 'start',
25366
+ overlayY: 'top',
25367
+ offsetY: 8,
25368
+ },
25369
+ {
25370
+ originX: 'start',
25371
+ originY: 'top',
25372
+ overlayX: 'start',
25373
+ overlayY: 'bottom',
25374
+ offsetY: -8,
25375
+ },
25376
+ ];
25377
+ this.scrollStrategy = this.scrollStrategies.reposition();
25378
+ this.overlayOpen = false;
25379
+ this.overlayMinWidth = 'none';
25380
+ this.errorKey = null;
25381
+ }
25382
+ set initialBbox(value) {
25383
+ if (!this.bbox && value) {
25384
+ this.bbox = value;
25385
+ }
25386
+ }
25387
+ get hasSelection() {
25388
+ return !!this.bbox;
25389
+ }
25390
+ get selectionLabelKey() {
25391
+ return this.fileName ? LABEL_FROM_FILE : LABEL_INITIAL;
25392
+ }
25393
+ get selectionDeleteLabelKey() {
25394
+ return this.fileName ? LABEL_FROM_FILE_DELETE : LABEL_INITIAL_DELETE;
25395
+ }
25396
+ get selectionLabelParams() {
25397
+ return { fileName: this.fileName };
25398
+ }
25399
+ openOverlay() {
25400
+ this.overlayMinWidth =
25401
+ this.overlayOrigin.elementRef.nativeElement.getBoundingClientRect()
25402
+ .width + 'px';
25403
+ this.overlayOpen = true;
25404
+ }
25405
+ closeOverlay() {
25406
+ this.overlayOpen = false;
25407
+ }
25408
+ toggleOverlay() {
25409
+ if (this.overlayOpen) {
25410
+ this.closeOverlay();
25411
+ }
25412
+ else {
25413
+ this.openOverlay();
25414
+ }
25415
+ }
25416
+ async handleFileSelected(file) {
25417
+ this.errorKey = null;
25418
+ let content;
25419
+ try {
25420
+ content = await readFileAsText(file);
25421
+ const parsed = JSON.parse(content);
25422
+ const geometry = getGeometryFromGeoJSON(parsed);
25423
+ if (!geometry) {
25424
+ this.setError(marker('search.filters.spatialExtent.error.noGeometry'));
25425
+ return;
25426
+ }
25427
+ const bbox = getGeometryBoundingBox(geometry);
25428
+ if (!isBoundingBoxWithinWorldExtent(bbox)) {
25429
+ this.setError(marker('search.filters.spatialExtent.error.outOfBounds'));
25430
+ return;
25431
+ }
25432
+ this.bbox = bbox;
25433
+ this.fileName = file.name;
25434
+ this.bboxChange.emit(bbox);
25435
+ this.cd.markForCheck();
25436
+ }
25437
+ catch {
25438
+ this.setError(marker('search.filters.spatialExtent.error.invalidFormat'));
25439
+ return;
25440
+ }
25441
+ }
25442
+ handleFileError(error) {
25443
+ if (error === 'file-too-large') {
25444
+ this.setError(marker('search.filters.spatialExtent.error.fileTooLarge'), {
25445
+ maxSize: this.maxFileSizeMb,
25446
+ });
25447
+ }
25448
+ else {
25449
+ this.setError(marker('search.filters.spatialExtent.error.invalidFormat'));
25450
+ }
25451
+ }
25452
+ setError(errorKey, params) {
25453
+ this.errorKey = errorKey;
25454
+ this.errorChange.emit({ key: errorKey, params });
25455
+ this.cd.markForCheck();
25456
+ }
25457
+ removeSelection(event) {
25458
+ this.bbox = null;
25459
+ this.fileName = '';
25460
+ this.errorKey = null;
25461
+ this.fileInput?.clear();
25462
+ this.bboxChange.emit(null);
25463
+ propagateToDocumentOnly(event);
25464
+ }
25465
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
25466
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: SpatialExtentDropdownComponent, isStandalone: true, selector: "gn-ui-spatial-extent-dropdown", inputs: { title: "title", maxFileSizeMb: "maxFileSizeMb", initialBbox: "initialBbox" }, outputs: { bboxChange: "bboxChange", errorChange: "errorChange" }, providers: [
25467
+ provideIcons({
25468
+ iconoirCheckCircle,
25469
+ iconoirSquareDashed,
25470
+ iconoirTrash,
25471
+ iconoirUpload,
25472
+ matClose,
25473
+ matExpandLess,
25474
+ matExpandMore,
25475
+ }),
25476
+ ], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "fileInput", first: true, predicate: DragAndDropFileInputComponent, descendants: true }], ngImport: i0, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelection) {\n <div\n class=\"bg-primary-lighter text-white shrink-0 rounded-full font-bold text-[12px] w-[22px] h-[22px] flex items-center justify-center\"\n >\n 1\n </div>\n }\n </div>\n <button class=\"h-6 w-6 flex items-center\" data-cy=\"clearSelection\">\n @if (hasSelection && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"removeSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-400 rounded shadow-lg pt-[12px] px-[16px] overlay-container w-[280px] flex flex-col gap-2\"\n [style.min-width]=\"overlayMinWidth\"\n >\n <div class=\"text-xs text-black\">\n {{ 'search.filters.spatialExtent.helpText' | translate }}\n </div>\n\n <div\n [class]=\"\n (hasSelection\n ? 'border-b-[1px] border-gray-400 pb-[20px]'\n : 'pb-[12px]') + ' text-black'\n \"\n >\n <gn-ui-drag-and-drop-file-input\n accept=\".json,.geojson\"\n [placeholder]=\"'search.filters.spatialExtent.import' | translate\"\n [maxFileSizeMb]=\"maxFileSizeMb\"\n [showFileName]=\"false\"\n textClass=\"text-black truncate\"\n dropzoneBackgroundColor=\"transparent\"\n icon=\"iconoirUpload\"\n extraClass=\"gn-ui-gray-outline-input w-full !h-[32px] px-[8px] py-[4px] justify-between\"\n (fileChange)=\"handleFileSelected($event)\"\n (errorChange)=\"handleFileError($event)\"\n data-cy=\"importGeojson\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n\n @if (hasSelection) {\n <gn-ui-button\n [type]=\"'primary-light'\"\n [title]=\"selectionDeleteLabelKey | translate: selectionLabelParams\"\n (buttonClick)=\"removeSelection($event)\"\n extraClass=\"group gap-x-2 w-full h-[28px] px-[8px] py-[4px] mb-[12px] bg-primary-lighter text-white\"\n data-test=\"spatial-extent-selected-item\"\n >\n <ng-icon\n name=\"iconoirSquareDashed\"\n class=\"shrink-0\"\n size=\"15px\"\n ></ng-icon>\n <span class=\"group-hover:hidden truncate text-sm m-auto\">\n {{ selectionLabelKey | translate: selectionLabelParams }}\n </span>\n <span class=\"hidden group-hover:block truncate text-sm m-auto\">\n {{ selectionDeleteLabelKey | translate: selectionLabelParams }}\n </span>\n <ng-icon\n name=\"iconoirCheckCircle\"\n class=\"group-hover:hidden shrink-0\"\n size=\"14px\"\n ></ng-icon>\n <ng-icon\n name=\"iconoirTrash\"\n class=\"!hidden group-hover:!block shrink-0\"\n size=\"15px\"\n ></ng-icon>\n </gn-ui-button>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: DragAndDropFileInputComponent, selector: "gn-ui-drag-and-drop-file-input", inputs: ["placeholder", "accept", "maxFileSizeMb", "icon", "dropzoneBackgroundColor", "textClass", "extraClass", "showFileName"], outputs: ["fileChange", "errorChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
25477
+ }
25478
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, decorators: [{
25479
+ type: Component,
25480
+ args: [{ selector: 'gn-ui-spatial-extent-dropdown', standalone: true, imports: [
25481
+ ButtonComponent,
25482
+ NgIcon,
25483
+ OverlayModule,
25484
+ TranslatePipe,
25485
+ DragAndDropFileInputComponent,
25486
+ ], providers: [
25487
+ provideIcons({
25488
+ iconoirCheckCircle,
25489
+ iconoirSquareDashed,
25490
+ iconoirTrash,
25491
+ iconoirUpload,
25492
+ matClose,
25493
+ matExpandLess,
25494
+ matExpandMore,
25495
+ }),
25496
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelection) {\n <div\n class=\"bg-primary-lighter text-white shrink-0 rounded-full font-bold text-[12px] w-[22px] h-[22px] flex items-center justify-center\"\n >\n 1\n </div>\n }\n </div>\n <button class=\"h-6 w-6 flex items-center\" data-cy=\"clearSelection\">\n @if (hasSelection && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"removeSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-400 rounded shadow-lg pt-[12px] px-[16px] overlay-container w-[280px] flex flex-col gap-2\"\n [style.min-width]=\"overlayMinWidth\"\n >\n <div class=\"text-xs text-black\">\n {{ 'search.filters.spatialExtent.helpText' | translate }}\n </div>\n\n <div\n [class]=\"\n (hasSelection\n ? 'border-b-[1px] border-gray-400 pb-[20px]'\n : 'pb-[12px]') + ' text-black'\n \"\n >\n <gn-ui-drag-and-drop-file-input\n accept=\".json,.geojson\"\n [placeholder]=\"'search.filters.spatialExtent.import' | translate\"\n [maxFileSizeMb]=\"maxFileSizeMb\"\n [showFileName]=\"false\"\n textClass=\"text-black truncate\"\n dropzoneBackgroundColor=\"transparent\"\n icon=\"iconoirUpload\"\n extraClass=\"gn-ui-gray-outline-input w-full !h-[32px] px-[8px] py-[4px] justify-between\"\n (fileChange)=\"handleFileSelected($event)\"\n (errorChange)=\"handleFileError($event)\"\n data-cy=\"importGeojson\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n\n @if (hasSelection) {\n <gn-ui-button\n [type]=\"'primary-light'\"\n [title]=\"selectionDeleteLabelKey | translate: selectionLabelParams\"\n (buttonClick)=\"removeSelection($event)\"\n extraClass=\"group gap-x-2 w-full h-[28px] px-[8px] py-[4px] mb-[12px] bg-primary-lighter text-white\"\n data-test=\"spatial-extent-selected-item\"\n >\n <ng-icon\n name=\"iconoirSquareDashed\"\n class=\"shrink-0\"\n size=\"15px\"\n ></ng-icon>\n <span class=\"group-hover:hidden truncate text-sm m-auto\">\n {{ selectionLabelKey | translate: selectionLabelParams }}\n </span>\n <span class=\"hidden group-hover:block truncate text-sm m-auto\">\n {{ selectionDeleteLabelKey | translate: selectionLabelParams }}\n </span>\n <ng-icon\n name=\"iconoirCheckCircle\"\n class=\"group-hover:hidden shrink-0\"\n size=\"14px\"\n ></ng-icon>\n <ng-icon\n name=\"iconoirTrash\"\n class=\"!hidden group-hover:!block shrink-0\"\n size=\"15px\"\n ></ng-icon>\n </gn-ui-button>\n }\n </div>\n</ng-template>\n" }]
25497
+ }], propDecorators: { title: [{
25498
+ type: Input
25499
+ }], maxFileSizeMb: [{
25500
+ type: Input
25501
+ }], initialBbox: [{
25502
+ type: Input
25503
+ }], bboxChange: [{
25504
+ type: Output
25505
+ }], errorChange: [{
25506
+ type: Output
25507
+ }], overlayOrigin: [{
25508
+ type: ViewChild,
25509
+ args: ['overlayOrigin']
25510
+ }], overlay: [{
25511
+ type: ViewChild,
25512
+ args: [CdkConnectedOverlay]
25513
+ }], fileInput: [{
25514
+ type: ViewChild,
25515
+ args: [DragAndDropFileInputComponent]
25516
+ }] } });
25517
+
25817
25518
  class CellPopinComponent {
25818
25519
  constructor() {
25819
25520
  this.scrollDispatcher = inject(ScrollDispatcher);
@@ -25890,7 +25591,7 @@ class CellPopinComponent {
25890
25591
  this.isOpen = false;
25891
25592
  }
25892
25593
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CellPopinComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
25893
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: CellPopinComponent, isStandalone: true, selector: "gn-ui-cell-popin", inputs: { extraClass: "extraClass", cdkScrollContainer: "cdkScrollContainer", scrollContainer: "scrollContainer", activePopin: "activePopin" }, providers: [provideIcons({ iconoirReduce })], viewQueries: [{ propertyName: "anchorRef", first: true, predicate: ["anchorRef"], descendants: true }], ngImport: i0, template: "<div\n class=\"h-full flex items-center justify-center\"\n cdkOverlayOrigin\n #anchorRef\n #trigger=\"cdkOverlayOrigin\"\n [class]=\"extraClass\"\n data-cy=\"cell-popin\"\n>\n <ng-content select=\"[cellContent]\"></ng-content>\n @if (activePopin) {\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayPush]=\"false\"\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayWidth]=\"'auto'\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n [cdkConnectedOverlayGrowAfterOpen]=\"true\"\n (detach)=\"closeOverlay()\"\n >\n @if (activePopin && isVisible) {\n <div\n class=\"bg-white shadow-lg border border-gray-300 relative\"\n data-cy=\"cell-popin-content\"\n >\n <ng-content select=\"[popinContent]\"></ng-content>\n <gn-ui-button\n data-cy=\"cell-popin-close\"\n [style.--gn-ui-button-background]=\"'bg-transparent'\"\n class=\"absolute top-2 right-2\"\n type=\"light\"\n (buttonClick)=\"closeOverlay()\"\n extraClass=\"w-10 h-8 px-1\"\n >\n <ng-icon name=\"iconoirReduce\" size=\"24\"></ng-icon>\n </gn-ui-button>\n </div>\n }\n </ng-template>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$8.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$8.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }] }); }
25594
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: CellPopinComponent, isStandalone: true, selector: "gn-ui-cell-popin", inputs: { extraClass: "extraClass", cdkScrollContainer: "cdkScrollContainer", scrollContainer: "scrollContainer", activePopin: "activePopin" }, providers: [provideIcons({ iconoirReduce })], viewQueries: [{ propertyName: "anchorRef", first: true, predicate: ["anchorRef"], descendants: true }], ngImport: i0, template: "<div\n class=\"h-full flex items-center justify-center\"\n cdkOverlayOrigin\n #anchorRef\n #trigger=\"cdkOverlayOrigin\"\n [class]=\"extraClass\"\n data-cy=\"cell-popin\"\n>\n <ng-content select=\"[cellContent]\"></ng-content>\n @if (activePopin) {\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayPush]=\"false\"\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayWidth]=\"'auto'\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n [cdkConnectedOverlayGrowAfterOpen]=\"true\"\n (detach)=\"closeOverlay()\"\n >\n @if (activePopin && isVisible) {\n <div\n class=\"bg-white shadow-lg border border-gray-300 relative\"\n data-cy=\"cell-popin-content\"\n >\n <ng-content select=\"[popinContent]\"></ng-content>\n <gn-ui-button\n data-cy=\"cell-popin-close\"\n [style.--gn-ui-button-background]=\"'bg-transparent'\"\n class=\"absolute top-2 right-2\"\n type=\"light\"\n (buttonClick)=\"closeOverlay()\"\n extraClass=\"w-10 h-8 px-1\"\n >\n <ng-icon name=\"iconoirReduce\" size=\"24\"></ng-icon>\n </gn-ui-button>\n </div>\n }\n </ng-template>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }] }); }
25894
25595
  }
25895
25596
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CellPopinComponent, decorators: [{
25896
25597
  type: Component,
@@ -26318,11 +26019,11 @@ class BlockListComponent {
26318
26019
  this.goToPage(this.currentPage + 1);
26319
26020
  }
26320
26021
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: BlockListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
26321
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: BlockListComponent, isStandalone: true, selector: "gn-ui-block-list", inputs: { containerClass: "containerClass", paginationContainerClass: "paginationContainerClass" }, outputs: { listChanges: "listChanges" }, host: { listeners: { "window:resize": "onResize()" } }, queries: [{ propertyName: "blocks", predicate: ["block"], read: ElementRef }], viewQueries: [{ propertyName: "blockContainer", first: true, predicate: ["blockContainer"], descendants: true }], ngImport: i0, template: "<div\n class=\"grid gap-4 w-full sm:grid-cols-2\"\n #blockContainer\n [ngClass]=\"[containerClass, 'size-' + subComponentSize]\"\n>\n <ng-content></ng-content>\n</div>\n<gn-ui-pagination-dots\n [containerClass]=\"'absolute ' + paginationContainerClass\"\n [listComponent]=\"this\"\n></gn-ui-pagination-dots>\n", styles: [":host .block-list-container ::ng-deep>*{flex-shrink:0}:host{position:relative}@media screen and (min-width: 769px){.size-L{grid-template-columns:repeat(1,minmax(0,1fr));grid-auto-rows:minmax(0,min-content)}.size-M{grid-template-columns:repeat(2,minmax(0,1fr));grid-auto-rows:minmax(0,min-content)}.size-S{grid-template-columns:repeat(3,minmax(0,1fr));grid-auto-rows:minmax(0,min-content)}.size-XS{grid-template-columns:repeat(2,minmax(0,1fr));grid-auto-rows:minmax(0,min-content)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: PaginationDotsComponent, selector: "gn-ui-pagination-dots", inputs: ["listComponent", "containerClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
26022
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: BlockListComponent, isStandalone: true, selector: "gn-ui-block-list", inputs: { containerClass: "containerClass", paginationContainerClass: "paginationContainerClass" }, outputs: { listChanges: "listChanges" }, host: { listeners: { "window:resize": "onResize()" } }, queries: [{ propertyName: "blocks", predicate: ["block"], read: ElementRef }], viewQueries: [{ propertyName: "blockContainer", first: true, predicate: ["blockContainer"], descendants: true }], ngImport: i0, template: "<div\n class=\"grid gap-4 w-full sm:grid-cols-2\"\n #blockContainer\n [ngClass]=\"[containerClass, 'size-' + subComponentSize]\"\n>\n <ng-content></ng-content>\n</div>\n<gn-ui-pagination-dots\n [containerClass]=\"'absolute ' + paginationContainerClass\"\n [listComponent]=\"this\"\n></gn-ui-pagination-dots>\n", styles: [":host .block-list-container ::ng-deep>*{flex-shrink:0}:host{display:block;position:relative}@media screen and (min-width: 769px){.size-L{grid-template-columns:repeat(1,minmax(0,1fr));grid-auto-rows:minmax(0,min-content)}.size-M{grid-template-columns:repeat(2,minmax(0,1fr));grid-auto-rows:minmax(0,min-content)}.size-S{grid-template-columns:repeat(3,minmax(0,1fr));grid-auto-rows:minmax(0,min-content)}.size-XS{grid-template-columns:repeat(2,minmax(0,1fr));grid-auto-rows:minmax(0,min-content)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: PaginationDotsComponent, selector: "gn-ui-pagination-dots", inputs: ["listComponent", "containerClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
26322
26023
  }
26323
26024
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: BlockListComponent, decorators: [{
26324
26025
  type: Component,
26325
- args: [{ selector: 'gn-ui-block-list', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, PaginationDotsComponent], template: "<div\n class=\"grid gap-4 w-full sm:grid-cols-2\"\n #blockContainer\n [ngClass]=\"[containerClass, 'size-' + subComponentSize]\"\n>\n <ng-content></ng-content>\n</div>\n<gn-ui-pagination-dots\n [containerClass]=\"'absolute ' + paginationContainerClass\"\n [listComponent]=\"this\"\n></gn-ui-pagination-dots>\n", styles: [":host .block-list-container ::ng-deep>*{flex-shrink:0}:host{position:relative}@media screen and (min-width: 769px){.size-L{grid-template-columns:repeat(1,minmax(0,1fr));grid-auto-rows:minmax(0,min-content)}.size-M{grid-template-columns:repeat(2,minmax(0,1fr));grid-auto-rows:minmax(0,min-content)}.size-S{grid-template-columns:repeat(3,minmax(0,1fr));grid-auto-rows:minmax(0,min-content)}.size-XS{grid-template-columns:repeat(2,minmax(0,1fr));grid-auto-rows:minmax(0,min-content)}}\n"] }]
26026
+ args: [{ selector: 'gn-ui-block-list', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, PaginationDotsComponent], template: "<div\n class=\"grid gap-4 w-full sm:grid-cols-2\"\n #blockContainer\n [ngClass]=\"[containerClass, 'size-' + subComponentSize]\"\n>\n <ng-content></ng-content>\n</div>\n<gn-ui-pagination-dots\n [containerClass]=\"'absolute ' + paginationContainerClass\"\n [listComponent]=\"this\"\n></gn-ui-pagination-dots>\n", styles: [":host .block-list-container ::ng-deep>*{flex-shrink:0}:host{display:block;position:relative}@media screen and (min-width: 769px){.size-L{grid-template-columns:repeat(1,minmax(0,1fr));grid-auto-rows:minmax(0,min-content)}.size-M{grid-template-columns:repeat(2,minmax(0,1fr));grid-auto-rows:minmax(0,min-content)}.size-S{grid-template-columns:repeat(3,minmax(0,1fr));grid-auto-rows:minmax(0,min-content)}.size-XS{grid-template-columns:repeat(2,minmax(0,1fr));grid-auto-rows:minmax(0,min-content)}}\n"] }]
26326
26027
  }], propDecorators: { containerClass: [{
26327
26028
  type: Input
26328
26029
  }], paginationContainerClass: [{
@@ -27061,9 +26762,7 @@ const initSearch = () => {
27061
26762
  },
27062
26763
  params: {
27063
26764
  filters: {},
27064
- pageSize: getOptionalSearchConfig()?.LIMIT
27065
- ? getOptionalSearchConfig().LIMIT
27066
- : DEFAULT_PAGE_SIZE,
26765
+ pageSize: DEFAULT_PAGE_SIZE,
27067
26766
  currentPage: 0,
27068
26767
  favoritesOnly: false,
27069
26768
  useSpatialFilter: true,
@@ -27512,6 +27211,70 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
27512
27211
  }]
27513
27212
  }] });
27514
27213
 
27214
+ const DEFAULT_GEOCODING_PROVIDER = [
27215
+ 'geonames',
27216
+ { maxRows: 5 },
27217
+ ];
27218
+ const GEOCODING_PROVIDER = new InjectionToken('geocoding-provider', {
27219
+ providedIn: 'root',
27220
+ factory: () => DEFAULT_GEOCODING_PROVIDER,
27221
+ });
27222
+ class GeocodingService {
27223
+ constructor() {
27224
+ this.provider = inject(GEOCODING_PROVIDER);
27225
+ }
27226
+ query(text) {
27227
+ let queryObservable;
27228
+ switch (this.provider[0]) {
27229
+ case 'geoadmin':
27230
+ queryObservable = from(queryGeoadmin(text, this.provider[1]));
27231
+ break;
27232
+ case 'geonames':
27233
+ queryObservable = from(queryGeonames(text, this.provider[1]));
27234
+ break;
27235
+ case 'geoplateforme':
27236
+ queryObservable = from(queryGeoplateforme(text, this.provider[1]));
27237
+ break;
27238
+ default:
27239
+ return throwError(() => new Error(`Unsupported geocoding provider: ${this.provider[0]}`));
27240
+ }
27241
+ return queryObservable.pipe(catchError((error) => throwError(error)));
27242
+ }
27243
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
27244
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingService, providedIn: 'root' }); }
27245
+ }
27246
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingService, decorators: [{
27247
+ type: Injectable,
27248
+ args: [{
27249
+ providedIn: 'root',
27250
+ }]
27251
+ }] });
27252
+
27253
+ class LocationSearchComponent {
27254
+ constructor() {
27255
+ this.geocodingService = inject(GeocodingService);
27256
+ this.placeholder = '';
27257
+ this.resultSelected = new EventEmitter();
27258
+ this.displayWithFn = (item) => item.label;
27259
+ this.searchAction = (text) => this.geocodingService.query(text);
27260
+ }
27261
+ handleItemSelected(item) {
27262
+ this.resultSelected.emit(item);
27263
+ }
27264
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LocationSearchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
27265
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: LocationSearchComponent, isStandalone: true, selector: "gn-ui-location-search", inputs: { placeholder: "placeholder", displayWithTemplate: "displayWithTemplate" }, outputs: { resultSelected: "resultSelected" }, ngImport: i0, template: "<gn-ui-autocomplete\n [action]=\"searchAction\"\n [displayWithFn]=\"displayWithFn\"\n [placeholder]=\"placeholder\"\n [displayWithTemplate]=\"displayWithTemplate\"\n (itemSelected)=\"handleItemSelected($event)\"\n>\n</gn-ui-autocomplete>\n", dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithTemplate", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
27266
+ }
27267
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LocationSearchComponent, decorators: [{
27268
+ type: Component,
27269
+ args: [{ selector: 'gn-ui-location-search', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [AutocompleteComponent], template: "<gn-ui-autocomplete\n [action]=\"searchAction\"\n [displayWithFn]=\"displayWithFn\"\n [placeholder]=\"placeholder\"\n [displayWithTemplate]=\"displayWithTemplate\"\n (itemSelected)=\"handleItemSelected($event)\"\n>\n</gn-ui-autocomplete>\n" }]
27270
+ }], propDecorators: { placeholder: [{
27271
+ type: Input
27272
+ }], displayWithTemplate: [{
27273
+ type: Input
27274
+ }], resultSelected: [{
27275
+ type: Output
27276
+ }] } });
27277
+
27515
27278
  // expects the replacement key ${uuid}
27516
27279
  const RECORD_DATASET_URL_TOKEN = new InjectionToken('record-dataset-url-token');
27517
27280
  const RECORD_SERVICE_URL_TOKEN = new InjectionToken('record-service-url-token');
@@ -27980,6 +27743,45 @@ class DateRangeSearchField extends SimpleSearchField {
27980
27743
  return 'dateRange';
27981
27744
  }
27982
27745
  }
27746
+ // treats a resource's creation/revision dates as an interval (its min and max), so a record matches
27747
+ // when that interval intersects the filter range, not just when a single date falls inside it
27748
+ class ResourceCreationRevisionDateSearchField extends DateRangeSearchField {
27749
+ constructor(injector, order = 'desc') {
27750
+ super('resourceCreationRevisionDate', injector, order);
27751
+ const dateSourceFields = [
27752
+ 'creationDateForResource',
27753
+ 'revisionDateForResource',
27754
+ ];
27755
+ const collectSortedDates = dateSourceFields
27756
+ .map((field) => `if (doc.containsKey('${field}')) {
27757
+ for (def date : doc['${field}']) { dates.add(date.millis); }
27758
+ }`)
27759
+ .join('\n');
27760
+ this.esService.registerRuntimeField('resourceCreationRevisionDateMin', `def dates = [];
27761
+ ${collectSortedDates}
27762
+ if (dates.length > 0) { dates.sort(null); emit(dates[0]); }`, 'date');
27763
+ this.esService.registerRuntimeField('resourceCreationRevisionDateMax', `def dates = [];
27764
+ ${collectSortedDates}
27765
+ if (dates.length > 0) { dates.sort(null); emit(dates[dates.length - 1]); }`, 'date');
27766
+ }
27767
+ }
27768
+ class BoundingBoxSearchField extends SimpleSearchField {
27769
+ getAvailableValues() {
27770
+ return of([]);
27771
+ }
27772
+ getFiltersForValues(values) {
27773
+ return of({
27774
+ [this.esFieldName]: values.map(Number),
27775
+ });
27776
+ }
27777
+ getValuesForFilter(filters) {
27778
+ const filter = filters[this.esFieldName];
27779
+ return of(isBoundingBox(filter) ? filter : []);
27780
+ }
27781
+ getType() {
27782
+ return 'spatialExtent';
27783
+ }
27784
+ }
27983
27785
  marker('search.filters.availableServices.view');
27984
27786
  marker('search.filters.availableServices.download');
27985
27787
  class AvailableServicesField extends SimpleSearchField {
@@ -28124,6 +27926,9 @@ marker('search.filters.producerOrg');
28124
27926
  marker('search.filters.publisherOrg');
28125
27927
  marker('search.filters.user');
28126
27928
  marker('search.filters.changeDate');
27929
+ marker('search.filters.resourceCreationRevisionDate');
27930
+ marker('search.filters.temporalExtent');
27931
+ marker('search.filters.spatialExtent');
28127
27932
  class FieldsService {
28128
27933
  constructor() {
28129
27934
  this.injector = inject(Injector);
@@ -28146,7 +27951,10 @@ class FieldsService {
28146
27951
  publisherOrg: new MultilingualSearchField('distributorOrgForResourceObject', this.injector, 'asc', 'key'),
28147
27952
  user: new UserSearchField(this.injector),
28148
27953
  changeDate: new DateRangeSearchField('changeDate', this.injector, 'desc'),
27954
+ resourceCreationRevisionDate: new ResourceCreationRevisionDateSearchField(this.injector, 'desc'),
27955
+ temporalExtent: new DateRangeSearchField('resourceTemporalExtentDateRange', this.injector, 'desc'),
28149
27956
  availableServices: new AvailableServicesField(this.injector),
27957
+ spatialExtent: new BoundingBoxSearchField('spatialExtent', this.injector),
28150
27958
  };
28151
27959
  }
28152
27960
  get supportedFields() {
@@ -29409,7 +29217,7 @@ class ContactPillComponent {
29409
29217
  this.overlayOpen = false;
29410
29218
  }
29411
29219
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ContactPillComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
29412
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: ContactPillComponent, isStandalone: true, selector: "gn-ui-contact-pill", inputs: { contact: "contact" }, ngImport: i0, template: "<gn-ui-button\n [type]=\"overlayOpen ? 'gray-light' : 'primary-light'\"\n extraClass=\"group w-full min-h-12 gap-3 justify-between py-2 pl-5 pr-4 rounded\"\n data-test=\"contact-pill\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <span\n class=\"font-title font-medium text-base leading-tight truncate\"\n [class]=\"!overlayOpen ? 'text-primary-black group-hover:text-white' : ''\"\n [title]=\"displayName\"\n >{{ displayName }}</span\n >\n <div\n class=\"gn-ui-card-icon items-center justify-center w-10 h-8\"\n [class]=\"\n !overlayOpen ? 'group-hover:border-white group-hover:text-white' : ''\n \"\n >\n @if (overlayOpen) {\n <ng-icon class=\"!w-6 !h-6 !text-[24px]\" name=\"matClose\"></ng-icon>\n } @else {\n <ng-icon class=\"!w-6 !h-6 !text-[24px]\" name=\"matInfoOutline\"></ng-icon>\n }\n </div>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayOffsetX]=\"overlayOffsetX\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div [style.width.px]=\"overlayWidth\">\n <gn-ui-contact-details [contact]=\"contact\"></gn-ui-contact-details>\n </div>\n</ng-template>\n", dependencies: [{ kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$8.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$8.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: ContactDetailsComponent, selector: "gn-ui-contact-details", inputs: ["contact"] }], viewProviders: [provideIcons({ matClose, matInfoOutline })], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
29220
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: ContactPillComponent, isStandalone: true, selector: "gn-ui-contact-pill", inputs: { contact: "contact" }, ngImport: i0, template: "<gn-ui-button\n [type]=\"overlayOpen ? 'gray-light' : 'primary-light'\"\n extraClass=\"group w-full min-h-12 gap-3 justify-between py-2 pl-5 pr-4 rounded\"\n data-test=\"contact-pill\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <span\n class=\"font-title font-medium text-base leading-tight truncate\"\n [class]=\"!overlayOpen ? 'text-primary-black group-hover:text-white' : ''\"\n [title]=\"displayName\"\n >{{ displayName }}</span\n >\n <div\n class=\"gn-ui-card-icon items-center justify-center w-10 h-8\"\n [class]=\"\n !overlayOpen ? 'group-hover:border-white group-hover:text-white' : ''\n \"\n >\n @if (overlayOpen) {\n <ng-icon class=\"!w-6 !h-6 !text-[24px]\" name=\"matClose\"></ng-icon>\n } @else {\n <ng-icon class=\"!w-6 !h-6 !text-[24px]\" name=\"matInfoOutline\"></ng-icon>\n }\n </div>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayOffsetX]=\"overlayOffsetX\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div [style.width.px]=\"overlayWidth\">\n <gn-ui-contact-details [contact]=\"contact\"></gn-ui-contact-details>\n </div>\n</ng-template>\n", dependencies: [{ kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: ContactDetailsComponent, selector: "gn-ui-contact-details", inputs: ["contact"] }], viewProviders: [provideIcons({ matClose, matInfoOutline })], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
29413
29221
  }
29414
29222
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ContactPillComponent, decorators: [{
29415
29223
  type: Component,
@@ -30135,7 +29943,7 @@ class ServiceCapabilitiesComponent {
30135
29943
  if (this.apiLinks.length > 0 &&
30136
29944
  this.apiLinks[0].accessServiceProtocol !== 'ogcFeatures') {
30137
29945
  this.loading = true;
30138
- this.availableLayers = await getLayers(this.apiLinks[0].url.href, this.apiLinks[0].accessServiceProtocol);
29946
+ this.availableLayers = await getLayers(this.apiLinks[0].url.href, this.apiLinks[0].accessServiceProtocol, true);
30139
29947
  this.loading = false;
30140
29948
  this.cdr.detectChanges();
30141
29949
  this.filteredLayers = this.availableLayers;
@@ -31331,19 +31139,120 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
31331
31139
  type: Output
31332
31140
  }] } });
31333
31141
 
31142
+ class NotificationsService {
31143
+ constructor() {
31144
+ this.notifications$ = new BehaviorSubject([]);
31145
+ }
31146
+ showNotification(content, timeoutMs, error) {
31147
+ error && console.error(error);
31148
+ const id = Math.floor(Math.random() * 1000000);
31149
+ this.notifications$.next([...this.notifications$.value, { ...content, id }]);
31150
+ if (typeof timeoutMs !== 'undefined') {
31151
+ setTimeout(() => {
31152
+ this.removeNotificationById(id);
31153
+ }, timeoutMs);
31154
+ }
31155
+ return id;
31156
+ }
31157
+ removeNotificationById(id) {
31158
+ this.notifications$.next(this.notifications$.value.filter((n) => n.id !== id));
31159
+ }
31160
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
31161
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, providedIn: 'root' }); }
31162
+ }
31163
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, decorators: [{
31164
+ type: Injectable,
31165
+ args: [{
31166
+ providedIn: 'root',
31167
+ }]
31168
+ }] });
31169
+
31170
+ class NotificationsContainerComponent {
31171
+ constructor() {
31172
+ this.notificationsService = inject(NotificationsService);
31173
+ }
31174
+ trackById(index, notification) {
31175
+ return notification.id;
31176
+ }
31177
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
31178
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: NotificationsContainerComponent, isStandalone: true, selector: "gn-ui-notifications-container", ngImport: i0, template: "<div class=\"flex flex-col gap-6 p-6 items-start pointer-events-none\">\n @for (\n notification of notificationsService.notifications$ | async;\n track trackById($index, notification)\n ) {\n <gn-ui-notification\n class=\"max-w-full pointer-events-auto\"\n [text]=\"notification.text\"\n [type]=\"notification.type\"\n [title]=\"notification.title\"\n [closeMessage]=\"notification.closeMessage\"\n (notificationClose)=\"\n notificationsService.removeNotificationById(notification.id)\n \"\n [@enterExit]\n ></gn-ui-notification>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: NotificationComponent, selector: "gn-ui-notification", inputs: ["type", "title", "text", "closeMessage"], outputs: ["notificationClose"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], animations: [
31179
+ trigger('enterExit', [
31180
+ transition(':enter', [
31181
+ animate('150ms', keyframes([
31182
+ style({ transform: 'scale(1)', opacity: 0 }),
31183
+ style({ transform: 'scale(1.03)', opacity: 0.5 }),
31184
+ style({ transform: 'scale(1)', opacity: 1 }),
31185
+ ])),
31186
+ ]),
31187
+ transition(':leave', [
31188
+ animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
31189
+ ]),
31190
+ ]),
31191
+ ], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
31192
+ }
31193
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, decorators: [{
31194
+ type: Component,
31195
+ args: [{ selector: 'gn-ui-notifications-container', standalone: true, imports: [CommonModule, NotificationComponent], changeDetection: ChangeDetectionStrategy.OnPush, animations: [
31196
+ trigger('enterExit', [
31197
+ transition(':enter', [
31198
+ animate('150ms', keyframes([
31199
+ style({ transform: 'scale(1)', opacity: 0 }),
31200
+ style({ transform: 'scale(1.03)', opacity: 0.5 }),
31201
+ style({ transform: 'scale(1)', opacity: 1 }),
31202
+ ])),
31203
+ ]),
31204
+ transition(':leave', [
31205
+ animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
31206
+ ]),
31207
+ ]),
31208
+ ], template: "<div class=\"flex flex-col gap-6 p-6 items-start pointer-events-none\">\n @for (\n notification of notificationsService.notifications$ | async;\n track trackById($index, notification)\n ) {\n <gn-ui-notification\n class=\"max-w-full pointer-events-auto\"\n [text]=\"notification.text\"\n [type]=\"notification.type\"\n [title]=\"notification.title\"\n [closeMessage]=\"notification.closeMessage\"\n (notificationClose)=\"\n notificationsService.removeNotificationById(notification.id)\n \"\n [@enterExit]\n ></gn-ui-notification>\n }\n</div>\n" }]
31209
+ }] });
31210
+
31211
+ // in MB, null means no limit
31212
+ const SPATIAL_EXTENT_MAX_FILE_SIZE = new InjectionToken('spatialExtentMaxFileSize', { factory: () => null });
31334
31213
  class FilterDropdownComponent {
31335
31214
  constructor() {
31336
31215
  this.searchFacade = inject(SearchFacade);
31337
31216
  this.searchService = inject(SearchService);
31338
31217
  this.fieldsService = inject(FieldsService);
31218
+ this.notificationsService = inject(NotificationsService);
31219
+ this.translateService = inject(TranslateService);
31220
+ this.spatialExtentMaxFileSize = inject(SPATIAL_EXTENT_MAX_FILE_SIZE);
31339
31221
  this.selected$ = this.searchFacade.searchFilters$.pipe(switchMap((filters) => this.fieldsService.readFieldValuesFromFilters(filters)), map$1((fieldValues) => fieldValues[this.fieldName]), filter$1((selected) => !!selected), startWith$1([]), catchError(() => of([])));
31340
- this.selectedDateRange$ = this.selected$.pipe(map$1((selectedDateRange) => selectedDateRange));
31222
+ this.selectedDateRange$ = this.selected$.pipe(map$1((selected) => (Array.isArray(selected) ? {} : selected)));
31223
+ this.selectedBoundingBox$ = this.selected$.pipe(map$1((selected) => Array.isArray(selected) && selected.length > 0
31224
+ ? selected
31225
+ : null));
31226
+ this.spatialExtentErrorNotificationId = null;
31341
31227
  }
31342
31228
  onSelectedValues(values) {
31343
31229
  this.fieldsService
31344
31230
  .buildFiltersFromFieldValues({ [this.fieldName]: values })
31345
31231
  .subscribe((filters) => this.searchService.updateFilters(filters));
31346
31232
  }
31233
+ onBboxChange(bbox) {
31234
+ this.fieldsService
31235
+ .buildFiltersFromFieldValues({
31236
+ [this.fieldName]: bbox,
31237
+ })
31238
+ .subscribe((filters) => this.searchService.updateFilters(filters));
31239
+ this.clearSpatialExtentErrorNotification();
31240
+ }
31241
+ onSpatialExtentError(error) {
31242
+ this.clearSpatialExtentErrorNotification();
31243
+ this.spatialExtentErrorNotificationId =
31244
+ this.notificationsService.showNotification({
31245
+ type: 'error',
31246
+ title: this.translateService.instant('search.filters.spatialExtent.error.title'),
31247
+ text: this.translateService.instant(error.key, error.params),
31248
+ });
31249
+ }
31250
+ clearSpatialExtentErrorNotification() {
31251
+ if (this.spatialExtentErrorNotificationId === null)
31252
+ return;
31253
+ this.notificationsService.removeNotificationById(this.spatialExtentErrorNotificationId);
31254
+ this.spatialExtentErrorNotificationId = null;
31255
+ }
31347
31256
  ngOnInit() {
31348
31257
  this.fieldType = this.fieldsService.getFieldType(this.fieldName);
31349
31258
  this.choices$ = this.fieldsService.getAvailableValues(this.fieldName).pipe(startWith$1([]), map$1((values) => values.map((v) => ({
@@ -31351,27 +31260,15 @@ class FilterDropdownComponent {
31351
31260
  value: v.value.toString(), // converting to string for the dropdown
31352
31261
  }))), catchError(() => of([])));
31353
31262
  }
31354
- onStartDateChange(start) {
31355
- if (!start)
31356
- return;
31357
- this.dateRange = { ...this.dateRange, start };
31358
- }
31359
- onEndDateChange(end) {
31360
- if (!end)
31361
- return;
31362
- this.dateRange = { ...this.dateRange, end };
31363
- if (this.dateRange.start && this.dateRange.end) {
31364
- this.fieldsService
31365
- .buildFiltersFromFieldValues({
31366
- [this.fieldName]: this.dateRange,
31367
- })
31368
- .subscribe((filters) => {
31369
- return this.searchService.updateFilters(filters);
31370
- });
31371
- }
31263
+ onDateRangeChange(dateRange) {
31264
+ this.fieldsService
31265
+ .buildFiltersFromFieldValues({
31266
+ [this.fieldName]: dateRange,
31267
+ })
31268
+ .subscribe((filters) => this.searchService.updateFilters(filters));
31372
31269
  }
31373
31270
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
31374
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FilterDropdownComponent, isStandalone: true, selector: "gn-ui-filter-dropdown", inputs: { fieldName: "fieldName", title: "title" }, ngImport: i0, template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [startDate]=\"(selectedDateRange$ | async)?.start\"\n [endDate]=\"(selectedDateRange$ | async)?.end\"\n (startDateChange)=\"onStartDateChange($event)\"\n (endDateChange)=\"onEndDateChange($event)\"\n ></gn-ui-date-range-dropdown>\n} @else {\n <gn-ui-dropdown-multiselect\n class=\"w-full\"\n [title]=\"title\"\n [maxRows]=\"6\"\n [choices]=\"(choices$ | async) || []\"\n [selected]=\"selected$ | async\"\n [allowSearch]=\"true\"\n (selectValues)=\"onSelectedValues($event)\"\n [attr.data-cy-field]=\"fieldName\"\n >\n </gn-ui-dropdown-multiselect>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DateRangeDropdownComponent, selector: "gn-ui-date-range-dropdown", inputs: ["title", "startDate", "endDate"], outputs: ["startDateChange", "endDateChange"] }, { kind: "component", type: DropdownMultiselectComponent, selector: "gn-ui-dropdown-multiselect", inputs: ["title", "choices", "selected", "allowSearch", "maxRows", "searchInputValue"], outputs: ["selectValues"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
31271
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FilterDropdownComponent, isStandalone: true, selector: "gn-ui-filter-dropdown", inputs: { fieldName: "fieldName", title: "title" }, ngImport: i0, template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [dateRange]=\"(selectedDateRange$ | async) ?? {}\"\n (dateRangeChange)=\"onDateRangeChange($event)\"\n [attr.data-cy-field]=\"fieldName\"\n ></gn-ui-date-range-dropdown>\n} @else if (fieldType === 'spatialExtent') {\n <gn-ui-spatial-extent-dropdown\n [title]=\"title\"\n [initialBbox]=\"(selectedBoundingBox$ | async) ?? null\"\n [maxFileSizeMb]=\"spatialExtentMaxFileSize\"\n (bboxChange)=\"onBboxChange($event)\"\n (errorChange)=\"onSpatialExtentError($event)\"\n [attr.data-cy-field]=\"fieldName\"\n ></gn-ui-spatial-extent-dropdown>\n} @else {\n <gn-ui-dropdown-multiselect\n class=\"w-full\"\n [title]=\"title\"\n [maxRows]=\"6\"\n [choices]=\"(choices$ | async) || []\"\n [selected]=\"selected$ | async\"\n [allowSearch]=\"true\"\n (selectValues)=\"onSelectedValues($event)\"\n [attr.data-cy-field]=\"fieldName\"\n >\n </gn-ui-dropdown-multiselect>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DateRangeDropdownComponent, selector: "gn-ui-date-range-dropdown", inputs: ["title", "dateRange"], outputs: ["dateRangeChange"] }, { kind: "component", type: DropdownMultiselectComponent, selector: "gn-ui-dropdown-multiselect", inputs: ["title", "choices", "selected", "allowSearch", "maxRows", "searchInputValue"], outputs: ["selectValues"] }, { kind: "component", type: SpatialExtentDropdownComponent, selector: "gn-ui-spatial-extent-dropdown", inputs: ["title", "maxFileSizeMb", "initialBbox"], outputs: ["bboxChange", "errorChange"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
31375
31272
  }
31376
31273
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, decorators: [{
31377
31274
  type: Component,
@@ -31379,7 +31276,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
31379
31276
  CommonModule,
31380
31277
  DateRangeDropdownComponent,
31381
31278
  DropdownMultiselectComponent,
31382
- ], template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [startDate]=\"(selectedDateRange$ | async)?.start\"\n [endDate]=\"(selectedDateRange$ | async)?.end\"\n (startDateChange)=\"onStartDateChange($event)\"\n (endDateChange)=\"onEndDateChange($event)\"\n ></gn-ui-date-range-dropdown>\n} @else {\n <gn-ui-dropdown-multiselect\n class=\"w-full\"\n [title]=\"title\"\n [maxRows]=\"6\"\n [choices]=\"(choices$ | async) || []\"\n [selected]=\"selected$ | async\"\n [allowSearch]=\"true\"\n (selectValues)=\"onSelectedValues($event)\"\n [attr.data-cy-field]=\"fieldName\"\n >\n </gn-ui-dropdown-multiselect>\n}\n" }]
31279
+ SpatialExtentDropdownComponent,
31280
+ ], template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [dateRange]=\"(selectedDateRange$ | async) ?? {}\"\n (dateRangeChange)=\"onDateRangeChange($event)\"\n [attr.data-cy-field]=\"fieldName\"\n ></gn-ui-date-range-dropdown>\n} @else if (fieldType === 'spatialExtent') {\n <gn-ui-spatial-extent-dropdown\n [title]=\"title\"\n [initialBbox]=\"(selectedBoundingBox$ | async) ?? null\"\n [maxFileSizeMb]=\"spatialExtentMaxFileSize\"\n (bboxChange)=\"onBboxChange($event)\"\n (errorChange)=\"onSpatialExtentError($event)\"\n [attr.data-cy-field]=\"fieldName\"\n ></gn-ui-spatial-extent-dropdown>\n} @else {\n <gn-ui-dropdown-multiselect\n class=\"w-full\"\n [title]=\"title\"\n [maxRows]=\"6\"\n [choices]=\"(choices$ | async) || []\"\n [selected]=\"selected$ | async\"\n [allowSearch]=\"true\"\n (selectValues)=\"onSelectedValues($event)\"\n [attr.data-cy-field]=\"fieldName\"\n >\n </gn-ui-dropdown-multiselect>\n}\n" }]
31383
31281
  }], propDecorators: { fieldName: [{
31384
31282
  type: Input
31385
31283
  }], title: [{
@@ -31440,7 +31338,7 @@ class FuzzySearchComponent {
31440
31338
  this.isSearchActive.emit(event);
31441
31339
  }
31442
31340
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FuzzySearchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
31443
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: FuzzySearchComponent, isStandalone: true, selector: "gn-ui-fuzzy-search", inputs: { autoFocus: "autoFocus", forceTrackPosition: "forceTrackPosition", enterButton: "enterButton", placeholder: "placeholder" }, outputs: { itemSelected: "itemSelected", inputSubmitted: "inputSubmitted", isSearchActive: "isSearchActive" }, viewQueries: [{ propertyName: "autocomplete", first: true, predicate: AutocompleteComponent, descendants: true }], ngImport: i0, template: "<gn-ui-autocomplete\n [placeholder]=\"placeholder ?? ('search.field.any.placeholder' | translate)\"\n [displayWithFn]=\"displayWithFn\"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"handleItemSelection($event)\"\n (inputSubmitted)=\"handleInputSubmission($event)\"\n (inputCleared)=\"handleInputCleared()\"\n (isSearchActive)=\"handleSearchActive($event)\"\n [value]=\"searchInputValue$ | async\"\n [preventCompleteOnSelection]=\"true\"\n [autoFocus]=\"autoFocus\"\n [allowSubmit]=\"true\"\n [forceTrackPosition]=\"forceTrackPosition\"\n [enterButton]=\"enterButton\"\n></gn-ui-autocomplete>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
31341
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: FuzzySearchComponent, isStandalone: true, selector: "gn-ui-fuzzy-search", inputs: { autoFocus: "autoFocus", forceTrackPosition: "forceTrackPosition", enterButton: "enterButton", placeholder: "placeholder" }, outputs: { itemSelected: "itemSelected", inputSubmitted: "inputSubmitted", isSearchActive: "isSearchActive" }, viewQueries: [{ propertyName: "autocomplete", first: true, predicate: AutocompleteComponent, descendants: true }], ngImport: i0, template: "<gn-ui-autocomplete\n [placeholder]=\"placeholder ?? ('search.field.any.placeholder' | translate)\"\n [displayWithFn]=\"displayWithFn\"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"handleItemSelection($event)\"\n (inputSubmitted)=\"handleInputSubmission($event)\"\n (inputCleared)=\"handleInputCleared()\"\n (isSearchActive)=\"handleSearchActive($event)\"\n [value]=\"searchInputValue$ | async\"\n [preventCompleteOnSelection]=\"true\"\n [autoFocus]=\"autoFocus\"\n [allowSubmit]=\"true\"\n [forceTrackPosition]=\"forceTrackPosition\"\n [enterButton]=\"enterButton\"\n></gn-ui-autocomplete>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithTemplate", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
31444
31342
  }
31445
31343
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FuzzySearchComponent, decorators: [{
31446
31344
  type: Component,
@@ -31915,74 +31813,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
31915
31813
  args: ['gnUiSearchStateContainer']
31916
31814
  }] } });
31917
31815
 
31918
- class NotificationsService {
31919
- constructor() {
31920
- this.notifications$ = new BehaviorSubject([]);
31921
- }
31922
- showNotification(content, timeoutMs, error) {
31923
- error && console.error(error);
31924
- const id = Math.floor(Math.random() * 1000000);
31925
- this.notifications$.next([...this.notifications$.value, { ...content, id }]);
31926
- if (typeof timeoutMs === 'undefined')
31927
- return;
31928
- setTimeout(() => {
31929
- this.removeNotificationById(id);
31930
- }, timeoutMs);
31931
- }
31932
- removeNotificationById(id) {
31933
- this.notifications$.next(this.notifications$.value.filter((n) => n.id !== id));
31934
- }
31935
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
31936
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, providedIn: 'root' }); }
31937
- }
31938
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, decorators: [{
31939
- type: Injectable,
31940
- args: [{
31941
- providedIn: 'root',
31942
- }]
31943
- }] });
31944
-
31945
- class NotificationsContainerComponent {
31946
- constructor() {
31947
- this.notificationsService = inject(NotificationsService);
31948
- }
31949
- trackById(index, notification) {
31950
- return notification.id;
31951
- }
31952
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
31953
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: NotificationsContainerComponent, isStandalone: true, selector: "gn-ui-notifications-container", ngImport: i0, template: "<div class=\"flex flex-col gap-6 p-6 items-start pointer-events-none\">\n @for (\n notification of notificationsService.notifications$ | async;\n track trackById($index, notification)\n ) {\n <gn-ui-notification\n class=\"max-w-full pointer-events-auto\"\n [text]=\"notification.text\"\n [type]=\"notification.type\"\n [title]=\"notification.title\"\n [closeMessage]=\"notification.closeMessage\"\n (notificationClose)=\"\n notificationsService.removeNotificationById(notification.id)\n \"\n [@enterExit]\n ></gn-ui-notification>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: NotificationComponent, selector: "gn-ui-notification", inputs: ["type", "title", "text", "closeMessage"], outputs: ["notificationClose"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], animations: [
31954
- trigger('enterExit', [
31955
- transition(':enter', [
31956
- animate('150ms', keyframes([
31957
- style({ transform: 'scale(1)', opacity: 0 }),
31958
- style({ transform: 'scale(1.03)', opacity: 0.5 }),
31959
- style({ transform: 'scale(1)', opacity: 1 }),
31960
- ])),
31961
- ]),
31962
- transition(':leave', [
31963
- animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
31964
- ]),
31965
- ]),
31966
- ], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
31967
- }
31968
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, decorators: [{
31969
- type: Component,
31970
- args: [{ selector: 'gn-ui-notifications-container', standalone: true, imports: [CommonModule, NotificationComponent], changeDetection: ChangeDetectionStrategy.OnPush, animations: [
31971
- trigger('enterExit', [
31972
- transition(':enter', [
31973
- animate('150ms', keyframes([
31974
- style({ transform: 'scale(1)', opacity: 0 }),
31975
- style({ transform: 'scale(1.03)', opacity: 0.5 }),
31976
- style({ transform: 'scale(1)', opacity: 1 }),
31977
- ])),
31978
- ]),
31979
- transition(':leave', [
31980
- animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
31981
- ]),
31982
- ]),
31983
- ], template: "<div class=\"flex flex-col gap-6 p-6 items-start pointer-events-none\">\n @for (\n notification of notificationsService.notifications$ | async;\n track trackById($index, notification)\n ) {\n <gn-ui-notification\n class=\"max-w-full pointer-events-auto\"\n [text]=\"notification.text\"\n [type]=\"notification.type\"\n [title]=\"notification.title\"\n [closeMessage]=\"notification.closeMessage\"\n (notificationClose)=\"\n notificationsService.removeNotificationById(notification.id)\n \"\n [@enterExit]\n ></gn-ui-notification>\n }\n</div>\n" }]
31984
- }] });
31985
-
31986
31816
  class ResultsTableContainerComponent {
31987
31817
  constructor() {
31988
31818
  this.searchFacade = inject(SearchFacade);
@@ -32067,12 +31897,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
32067
31897
 
32068
31898
  marker('search.filters.summaryLabel.user');
32069
31899
  marker('search.filters.summaryLabel.changeDate');
31900
+ marker('search.filters.summaryLabel.resourceCreationRevisionDate');
31901
+ marker('search.filters.summaryLabel.temporalExtent');
31902
+ const OPEN_BOUND = '…';
32070
31903
  class SearchFiltersSummaryItemComponent {
32071
31904
  constructor() {
32072
31905
  this.searchFacade = inject(SearchFacade);
32073
31906
  this.searchService = inject(SearchService);
32074
31907
  this.fieldsService = inject(FieldsService);
32075
- this.datePipe = inject(DatePipe);
31908
+ this.dateService = inject(DateService);
32076
31909
  this.translate = inject(TranslateService);
32077
31910
  this.fieldValues$ = this.searchFacade.searchFilters$.pipe(switchMap((filters) => this.fieldsService.readFieldValuesFromFilters(filters)), map$2((fieldValues) => Array.isArray(fieldValues[this.fieldName])
32078
31911
  ? fieldValues[this.fieldName]
@@ -32099,9 +31932,10 @@ class SearchFiltersSummaryItemComponent {
32099
31932
  getReadableValues(fieldValues) {
32100
31933
  return fieldValues.map((value) => {
32101
31934
  if (this.fieldType === 'dateRange') {
31935
+ const { start, end } = value;
32102
31936
  return {
32103
31937
  value,
32104
- label: `${this.datePipe.transform(value.start, 'dd.MM.yyyy')} - ${this.datePipe.transform(value.end, 'dd.MM.yyyy')}`,
31938
+ label: `${this.formatBound(start)} - ${this.formatBound(end)}`,
32105
31939
  };
32106
31940
  }
32107
31941
  else if (this.fieldName === 'user') {
@@ -32112,6 +31946,15 @@ class SearchFiltersSummaryItemComponent {
32112
31946
  }
32113
31947
  });
32114
31948
  }
31949
+ formatBound(date) {
31950
+ return date
31951
+ ? this.dateService.formatDate(date, {
31952
+ day: '2-digit',
31953
+ month: '2-digit',
31954
+ year: 'numeric',
31955
+ })
31956
+ : OPEN_BOUND;
31957
+ }
32115
31958
  async removeFilterValue(fieldValue) {
32116
31959
  const currentFieldValues = await firstValueFrom(this.fieldValues$);
32117
31960
  const updatedFieldValues = currentFieldValues
@@ -32124,11 +31967,11 @@ class SearchFiltersSummaryItemComponent {
32124
31967
  .subscribe((filters) => this.searchService.updateFilters(filters));
32125
31968
  }
32126
31969
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SearchFiltersSummaryItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
32127
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: SearchFiltersSummaryItemComponent, isStandalone: true, selector: "gn-ui-search-filters-summary-item", inputs: { fieldName: "fieldName" }, providers: [DatePipe], ngImport: i0, template: "@if ((fieldValues$ | async)?.length > 0) {\n <div class=\"flex flex-row items-center gap-2\">\n <span class=\"text-gray-800\" data-cy=\"filterSummaryLabel\">{{\n translatedLabel\n }}</span>\n @for (fieldValue of fieldValues$ | async; track fieldValue) {\n <gn-ui-badge\n class=\"gn-ui-badge\"\n [style.--gn-ui-badge-rounded]=\"'8px'\"\n [style.--gn-ui-badge-padding]=\"'3px 4px'\"\n [style.--gn-ui-badge-text-color]=\"'black'\"\n [style.--gn-ui-badge-background-color]=\"'#F2F2F2'\"\n [style.--gn-ui-badge-font-weight]=\"'700'\"\n [removable]=\"true\"\n (badgeRemoveClicked)=\"removeFilterValue(fieldValue.value)\"\n >{{ fieldValue.label }}</gn-ui-badge\n >\n }\n </div>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: BadgeComponent, selector: "gn-ui-badge", inputs: ["clickable", "removable"], outputs: ["badgeRemoveClicked", "badgeClicked"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }] }); }
31970
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: SearchFiltersSummaryItemComponent, isStandalone: true, selector: "gn-ui-search-filters-summary-item", inputs: { fieldName: "fieldName" }, ngImport: i0, template: "@if ((fieldValues$ | async)?.length > 0) {\n <div class=\"flex flex-row items-center gap-2\">\n <span class=\"text-gray-800\" data-cy=\"filterSummaryLabel\">{{\n translatedLabel\n }}</span>\n @for (fieldValue of fieldValues$ | async; track fieldValue) {\n <gn-ui-badge\n class=\"gn-ui-badge\"\n [style.--gn-ui-badge-rounded]=\"'8px'\"\n [style.--gn-ui-badge-padding]=\"'3px 4px'\"\n [style.--gn-ui-badge-text-color]=\"'black'\"\n [style.--gn-ui-badge-background-color]=\"'#F2F2F2'\"\n [style.--gn-ui-badge-font-weight]=\"'700'\"\n [removable]=\"true\"\n (badgeRemoveClicked)=\"removeFilterValue(fieldValue.value)\"\n >{{ fieldValue.label }}</gn-ui-badge\n >\n }\n </div>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: BadgeComponent, selector: "gn-ui-badge", inputs: ["clickable", "removable"], outputs: ["badgeRemoveClicked", "badgeClicked"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }] }); }
32128
31971
  }
32129
31972
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SearchFiltersSummaryItemComponent, decorators: [{
32130
31973
  type: Component,
32131
- args: [{ selector: 'gn-ui-search-filters-summary-item', standalone: true, imports: [CommonModule, BadgeComponent], providers: [DatePipe], template: "@if ((fieldValues$ | async)?.length > 0) {\n <div class=\"flex flex-row items-center gap-2\">\n <span class=\"text-gray-800\" data-cy=\"filterSummaryLabel\">{{\n translatedLabel\n }}</span>\n @for (fieldValue of fieldValues$ | async; track fieldValue) {\n <gn-ui-badge\n class=\"gn-ui-badge\"\n [style.--gn-ui-badge-rounded]=\"'8px'\"\n [style.--gn-ui-badge-padding]=\"'3px 4px'\"\n [style.--gn-ui-badge-text-color]=\"'black'\"\n [style.--gn-ui-badge-background-color]=\"'#F2F2F2'\"\n [style.--gn-ui-badge-font-weight]=\"'700'\"\n [removable]=\"true\"\n (badgeRemoveClicked)=\"removeFilterValue(fieldValue.value)\"\n >{{ fieldValue.label }}</gn-ui-badge\n >\n }\n </div>\n}\n" }]
31974
+ args: [{ selector: 'gn-ui-search-filters-summary-item', standalone: true, imports: [CommonModule, BadgeComponent], template: "@if ((fieldValues$ | async)?.length > 0) {\n <div class=\"flex flex-row items-center gap-2\">\n <span class=\"text-gray-800\" data-cy=\"filterSummaryLabel\">{{\n translatedLabel\n }}</span>\n @for (fieldValue of fieldValues$ | async; track fieldValue) {\n <gn-ui-badge\n class=\"gn-ui-badge\"\n [style.--gn-ui-badge-rounded]=\"'8px'\"\n [style.--gn-ui-badge-padding]=\"'3px 4px'\"\n [style.--gn-ui-badge-text-color]=\"'black'\"\n [style.--gn-ui-badge-background-color]=\"'#F2F2F2'\"\n [style.--gn-ui-badge-font-weight]=\"'700'\"\n [removable]=\"true\"\n (badgeRemoveClicked)=\"removeFilterValue(fieldValue.value)\"\n >{{ fieldValue.label }}</gn-ui-badge\n >\n }\n </div>\n}\n" }]
32132
31975
  }], propDecorators: { fieldName: [{
32133
31976
  type: Input
32134
31977
  }] } });
@@ -32372,11 +32215,11 @@ class AddLayerFromFileComponent {
32372
32215
  }, 5000);
32373
32216
  }
32374
32217
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AddLayerFromFileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
32375
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: AddLayerFromFileComponent, isStandalone: true, selector: "gn-ui-add-layer-from-file", ngImport: i0, template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n", styles: [""], dependencies: [{ kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: DragAndDropFileInputComponent, selector: "gn-ui-drag-and-drop-file-input", inputs: ["placeholder", "accept"], outputs: ["fileChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
32218
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: AddLayerFromFileComponent, isStandalone: true, selector: "gn-ui-add-layer-from-file", ngImport: i0, template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n textClass=\"text-gray-900 pl-2 py-2\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n", styles: [""], dependencies: [{ kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: DragAndDropFileInputComponent, selector: "gn-ui-drag-and-drop-file-input", inputs: ["placeholder", "accept", "maxFileSizeMb", "icon", "dropzoneBackgroundColor", "textClass", "extraClass", "showFileName"], outputs: ["fileChange", "errorChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
32376
32219
  }
32377
32220
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AddLayerFromFileComponent, decorators: [{
32378
32221
  type: Component,
32379
- args: [{ selector: 'gn-ui-add-layer-from-file', standalone: true, imports: [TranslateDirective, TranslatePipe, DragAndDropFileInputComponent], template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n" }]
32222
+ args: [{ selector: 'gn-ui-add-layer-from-file', standalone: true, imports: [TranslateDirective, TranslatePipe, DragAndDropFileInputComponent], template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n textClass=\"text-gray-900 pl-2 py-2\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n" }]
32380
32223
  }] });
32381
32224
 
32382
32225
  class LayersPanelComponent {
@@ -32437,95 +32280,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
32437
32280
  ], template: "<div\n class=\"bg-white rounded shadow shadow-lg relative h-full w-[400px] overflow-hidden\"\n>\n <div class=\"p-3 border-b border-gray-300 flex items-center\">\n <ng-icon class=\"mr-2\" name=\"matLayersOutline\"></ng-icon>\n <span translate>map.layers.list</span>\n </div>\n <div class=\"flex flex-col px-4 divide-y divide-y-gray-50\">\n @for (layer of layers$ | async; track layer; let index = $index) {\n <div class=\"flex flex-row py-3\">\n <ng-icon class=\"-ml-2 mr-2 shrink-0\" name=\"matChevronRight\"></ng-icon>\n <span class=\"mr-2 grow\">{{ layer.label }}</span>\n <a\n href\n class=\"underline text-sm text-red-700 hover:text-red-900\"\n (click)=\"deleteLayer(index); $event.preventDefault()\"\n >\n delete\n </a>\n </div>\n }\n </div>\n <gn-ui-expandable-panel-button [titleTemplate]=\"addLayerTitle\">\n <mat-tab-group animationDuration=\"200ms\" class=\"h-full\">\n <mat-tab [label]=\"'map.add.layer.catalog' | translate\">\n <div class=\"p-3\">\n <gn-ui-add-layer-from-catalog></gn-ui-add-layer-from-catalog>\n </div>\n </mat-tab>\n <mat-tab [label]=\"'map.add.layer.wms' | translate\" bodyClass=\"h-full\">\n <div class=\"p-3\">\n <gn-ui-add-layer-from-wms></gn-ui-add-layer-from-wms>\n </div>\n </mat-tab>\n <mat-tab [label]=\"'map.add.layer.wfs' | translate\" bodyClass=\"h-full\">\n <div class=\"p-3\">\n <gn-ui-add-layer-from-wfs></gn-ui-add-layer-from-wfs>\n </div>\n </mat-tab>\n <mat-tab [label]=\"'map.add.layer.ogc.api' | translate\" bodyClass=\"h-full\">\n <div class=\"p-3\">\n <gn-ui-add-layer-from-ogc-api\n [ogcUrl]=\"ogcUrl\"\n (layerAdded)=\"addLayer($event)\"\n ></gn-ui-add-layer-from-ogc-api>\n </div>\n </mat-tab>\n <mat-tab [label]=\"'map.add.layer.file' | translate\" bodyClass=\"h-full\">\n <div class=\"p-3\">\n <gn-ui-add-layer-from-file></gn-ui-add-layer-from-file>\n </div>\n </mat-tab>\n </mat-tab-group>\n </gn-ui-expandable-panel-button>\n</div>\n\n<ng-template #addLayerTitle>\n <ng-icon class=\"mr-4\" name=\"matAddCircleOutlineOutline\"> </ng-icon>\n <span translate>map.add.layer</span>\n</ng-template>\n" }]
32438
32281
  }] });
32439
32282
 
32440
- class GeocodingComponent {
32441
- constructor() {
32442
- this.mapFacade = inject(MapFacade);
32443
- this.geocodingService = inject(GeocodingService);
32444
- this.searchText = '';
32445
- this.results = [];
32446
- this.searchTextChanged = new Subject();
32447
- this.destroy$ = new Subject();
32448
- this.errorMessage = null;
32449
- this.searchTextChanged
32450
- .pipe(debounceTime(300), switchMap$1((searchText) => {
32451
- return from(this.geocodingService.query(searchText)).pipe(catchError$1((error) => {
32452
- this.errorMessage =
32453
- 'An error occurred while searching. Please try again.';
32454
- console.error(error);
32455
- return [];
32456
- }));
32457
- }), takeUntil(this.destroy$))
32458
- .subscribe((results) => {
32459
- this.results = results;
32460
- });
32461
- }
32462
- ngOnDestroy() {
32463
- this.destroy$.next();
32464
- this.destroy$.complete();
32465
- }
32466
- onSearchChange(searchText) {
32467
- if (!searchText) {
32468
- this.clearSearch();
32469
- return;
32470
- }
32471
- else {
32472
- this.searchTextChanged.next(searchText);
32473
- }
32474
- }
32475
- clearSearch() {
32476
- this.searchText = '';
32477
- this.results = [];
32478
- this.errorMessage = null;
32479
- }
32480
- async zoomToLocation(result) {
32481
- const geometry = result.geom;
32482
- if (geometry.type === 'Point') {
32483
- await this.zoomToPoint(geometry.coordinates);
32484
- }
32485
- else if (geometry.type === 'Polygon') {
32486
- await this.zoomToPolygon(geometry.coordinates);
32487
- }
32488
- else {
32489
- console.error(`Unsupported geometry type: ${geometry.type}`);
32490
- }
32491
- }
32492
- async zoomToPoint(pointCoords) {
32493
- const context = await firstValueFrom(this.mapFacade.context$);
32494
- const view = {
32495
- center: pointCoords,
32496
- zoom: 10,
32497
- };
32498
- this.mapFacade.applyContext({
32499
- ...context,
32500
- view,
32501
- });
32502
- }
32503
- async zoomToPolygon(polygonCoords) {
32504
- const context = await firstValueFrom(this.mapFacade.context$);
32505
- const view = {
32506
- geometry: {
32507
- type: 'Polygon',
32508
- coordinates: polygonCoords,
32509
- },
32510
- };
32511
- this.mapFacade.applyContext({
32512
- ...context,
32513
- view,
32514
- });
32515
- }
32516
- onEnterPress() {
32517
- if (this.results && this.results.length > 0) {
32518
- this.zoomToLocation(this.results[0]);
32519
- }
32520
- }
32521
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
32522
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: GeocodingComponent, isStandalone: true, selector: "gn-ui-geocoding", ngImport: i0, template: "<gn-ui-search-input\n [(value)]=\"searchText\"\n (valueChange)=\"onSearchChange($event)\"\n (keyup.enter)=\"onEnterPress()\"\n [placeholder]=\"'map.geocoding.placeholder' | translate\"\n>\n</gn-ui-search-input>\n@if (results && results.length) {\n <ul\n class=\"bg-gray-50 border border-gray-200 w-full mt-2 shadow-sm rounded-lg\"\n >\n @for (result of results; track result) {\n <li\n (click)=\"zoomToLocation(result)\"\n class=\"flex items-center pl-8 pr-4 py-2 border-b border-gray-200 relative cursor-pointer hover:bg-blue-100 hover:text-gray-800 transition duration-300 ease-in-out\"\n >\n <svg\n class=\"stroke-current text-blue-500 absolute w-5 h-5 left-3 top-3\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z\"\n />\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M15 11a3 3 0 11-6 0 3 3 0 016 0z\"\n />\n </svg>\n <span class=\"font-sans font-semibold ml-4\">{{ result.label }}</span>\n </li>\n }\n </ul>\n}\n", styles: [""], dependencies: [{ kind: "component", type: SearchInputComponent, selector: "gn-ui-search-input", inputs: ["value", "placeholder"], outputs: ["valueChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
32523
- }
32524
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingComponent, decorators: [{
32525
- type: Component,
32526
- args: [{ selector: 'gn-ui-geocoding', standalone: true, imports: [SearchInputComponent, TranslatePipe], template: "<gn-ui-search-input\n [(value)]=\"searchText\"\n (valueChange)=\"onSearchChange($event)\"\n (keyup.enter)=\"onEnterPress()\"\n [placeholder]=\"'map.geocoding.placeholder' | translate\"\n>\n</gn-ui-search-input>\n@if (results && results.length) {\n <ul\n class=\"bg-gray-50 border border-gray-200 w-full mt-2 shadow-sm rounded-lg\"\n >\n @for (result of results; track result) {\n <li\n (click)=\"zoomToLocation(result)\"\n class=\"flex items-center pl-8 pr-4 py-2 border-b border-gray-200 relative cursor-pointer hover:bg-blue-100 hover:text-gray-800 transition duration-300 ease-in-out\"\n >\n <svg\n class=\"stroke-current text-blue-500 absolute w-5 h-5 left-3 top-3\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z\"\n />\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M15 11a3 3 0 11-6 0 3 3 0 016 0z\"\n />\n </svg>\n <span class=\"font-sans font-semibold ml-4\">{{ result.label }}</span>\n </li>\n }\n </ul>\n}\n" }]
32527
- }], ctorParameters: () => [] });
32528
-
32529
32283
  /*
32530
32284
  Metadata actions
32531
32285
  */
@@ -37570,7 +37324,7 @@ class GenericKeywordsComponent {
37570
37324
  this.deletedKeyword.emit(keyword);
37571
37325
  }
37572
37326
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GenericKeywordsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
37573
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: GenericKeywordsComponent, isStandalone: true, selector: "gn-ui-generic-keywords", inputs: { keywords: "keywords", keywordTypes: "keywordTypes", placeholder: "placeholder", allowSubmit: "allowSubmit" }, outputs: { changedKeywords: "changedKeywords", addedKeyword: "addedKeyword", deletedKeyword: "deletedKeyword" }, ngImport: i0, template: "<div class=\"flex flex-col gap-4\">\n <gn-ui-autocomplete\n [placeholder]=\"placeholder\"\n [displayWithFn]=\"displayWithFn\"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"handleItemSelection($event)\"\n [preventCompleteOnSelection]=\"false\"\n [minCharacterCount]=\"1\"\n [allowSubmit]=\"false\"\n [clearOnSelection]=\"true\"\n ></gn-ui-autocomplete>\n <div class=\"flex gap-2 flex-wrap\">\n @for (keyword of keywords; track keyword) {\n <gn-ui-keyword-badge\n [keyword]=\"keyword\"\n [editable]=\"true\"\n (keywordRemove)=\"removeKeyword($event)\"\n ></gn-ui-keyword-badge>\n }\n </div>\n</div>\n", dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "component", type: KeywordBadgeComponent, selector: "gn-ui-keyword-badge", inputs: ["keyword", "editable"], outputs: ["keywordClick", "keywordRemove"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
37327
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: GenericKeywordsComponent, isStandalone: true, selector: "gn-ui-generic-keywords", inputs: { keywords: "keywords", keywordTypes: "keywordTypes", placeholder: "placeholder", allowSubmit: "allowSubmit" }, outputs: { changedKeywords: "changedKeywords", addedKeyword: "addedKeyword", deletedKeyword: "deletedKeyword" }, ngImport: i0, template: "<div class=\"flex flex-col gap-4\">\n <gn-ui-autocomplete\n [placeholder]=\"placeholder\"\n [displayWithFn]=\"displayWithFn\"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"handleItemSelection($event)\"\n [preventCompleteOnSelection]=\"false\"\n [minCharacterCount]=\"1\"\n [allowSubmit]=\"false\"\n [clearOnSelection]=\"true\"\n ></gn-ui-autocomplete>\n <div class=\"flex gap-2 flex-wrap\">\n @for (keyword of keywords; track keyword) {\n <gn-ui-keyword-badge\n [keyword]=\"keyword\"\n [editable]=\"true\"\n (keywordRemove)=\"removeKeyword($event)\"\n ></gn-ui-keyword-badge>\n }\n </div>\n</div>\n", dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithTemplate", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "component", type: KeywordBadgeComponent, selector: "gn-ui-keyword-badge", inputs: ["keyword", "editable"], outputs: ["keywordClick", "keywordRemove"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
37574
37328
  }
37575
37329
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GenericKeywordsComponent, decorators: [{
37576
37330
  type: Component,
@@ -38162,7 +37916,7 @@ class FormFieldContactsForResourceComponent {
38162
37916
  provideNgIconsConfig({
38163
37917
  size: '1.5rem',
38164
37918
  }),
38165
- ], usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col gap-3\">\n <div class=\"flex flex-row flex-wrap gap-2\" data-test=\"rolesToPick\">\n @for (role of rolesToPick; track role) {\n <gn-ui-button type=\"gray\" (buttonClick)=\"addRoleToDisplay(role)\">\n <ng-icon name=\"iconoirPlus\" class=\"text-primary\"></ng-icon>\n &nbsp;\n <span translate>{{ roleToLabel(role) }}</span>\n </gn-ui-button>\n }\n </div>\n @if (value.length === 0) {\n <div\n class=\"p-4 border border-primary bg-primary-lightest rounded-lg\"\n translate\n >\n editor.record.form.field.contactsForResource.noContact\n </div>\n }\n @if (roleSectionsToDisplay && roleSectionsToDisplay.length > 0) {\n <div class=\"mt-8\" data-test=\"displayedRoles\">\n @for (\n role of roleSectionsToDisplay;\n track role;\n let index = $index;\n let isLast = $last\n ) {\n <div class=\"flex flex-col gap-4\">\n <div class=\"flex flex-row justify-between\">\n <span class=\"font-bold text-base\" translate>{{\n roleToLabel(role)\n }}</span>\n </div>\n @if (role !== 'unspecified' && role !== 'other') {\n <gn-ui-autocomplete\n [placeholder]=\"\n 'editor.record.form.field.contactsForResource.placeholder'\n | translate\n \"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"addContact($event, role)\"\n [displayWithFn]=\"displayWithFn\"\n [minCharacterCount]=\"1\"\n [clearOnSelection]=\"true\"\n [allowSubmit]=\"false\"\n >\n </gn-ui-autocomplete>\n }\n @if (contactsForRessourceByRole.get(role); as contacts) {\n <gn-ui-sortable-list\n [items]=\"contacts\"\n (itemsOrderChange)=\"handleContactsChanged($event, role)\"\n [elementTemplate]=\"contactTemplate\"\n ></gn-ui-sortable-list>\n <ng-template #contactTemplate let-contact>\n <gn-ui-contact-card [contact]=\"contact\"></gn-ui-contact-card>\n </ng-template>\n }\n @if (!isLast) {\n <hr class=\"border-t-[#D6D3D1] mt-4 mb-6\" />\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: [":host{--gn-ui-button-padding: 8px 8px;--gn-ui-button-rounded: 8px}\n"], dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "component", type: ContactCardComponent, selector: "gn-ui-contact-card", inputs: ["contact"] }, { kind: "component", type: SortableListComponent, selector: "gn-ui-sortable-list", inputs: ["elementTemplate", "items"], outputs: ["itemsOrderChange"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
37919
+ ], usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col gap-3\">\n <div class=\"flex flex-row flex-wrap gap-2\" data-test=\"rolesToPick\">\n @for (role of rolesToPick; track role) {\n <gn-ui-button type=\"gray\" (buttonClick)=\"addRoleToDisplay(role)\">\n <ng-icon name=\"iconoirPlus\" class=\"text-primary\"></ng-icon>\n &nbsp;\n <span translate>{{ roleToLabel(role) }}</span>\n </gn-ui-button>\n }\n </div>\n @if (value.length === 0) {\n <div\n class=\"p-4 border border-primary bg-primary-lightest rounded-lg\"\n translate\n >\n editor.record.form.field.contactsForResource.noContact\n </div>\n }\n @if (roleSectionsToDisplay && roleSectionsToDisplay.length > 0) {\n <div class=\"mt-8\" data-test=\"displayedRoles\">\n @for (\n role of roleSectionsToDisplay;\n track role;\n let index = $index;\n let isLast = $last\n ) {\n <div class=\"flex flex-col gap-4\">\n <div class=\"flex flex-row justify-between\">\n <span class=\"font-bold text-base\" translate>{{\n roleToLabel(role)\n }}</span>\n </div>\n @if (role !== 'unspecified' && role !== 'other') {\n <gn-ui-autocomplete\n [placeholder]=\"\n 'editor.record.form.field.contactsForResource.placeholder'\n | translate\n \"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"addContact($event, role)\"\n [displayWithFn]=\"displayWithFn\"\n [minCharacterCount]=\"1\"\n [clearOnSelection]=\"true\"\n [allowSubmit]=\"false\"\n >\n </gn-ui-autocomplete>\n }\n @if (contactsForRessourceByRole.get(role); as contacts) {\n <gn-ui-sortable-list\n [items]=\"contacts\"\n (itemsOrderChange)=\"handleContactsChanged($event, role)\"\n [elementTemplate]=\"contactTemplate\"\n ></gn-ui-sortable-list>\n <ng-template #contactTemplate let-contact>\n <gn-ui-contact-card [contact]=\"contact\"></gn-ui-contact-card>\n </ng-template>\n }\n @if (!isLast) {\n <hr class=\"border-t-[#D6D3D1] mt-4 mb-6\" />\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: [":host{--gn-ui-button-padding: 8px 8px;--gn-ui-button-rounded: 8px}\n"], dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithTemplate", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "component", type: ContactCardComponent, selector: "gn-ui-contact-card", inputs: ["contact"] }, { kind: "component", type: SortableListComponent, selector: "gn-ui-sortable-list", inputs: ["elementTemplate", "items"], outputs: ["itemsOrderChange"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
38166
37920
  }
38167
37921
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FormFieldContactsForResourceComponent, decorators: [{
38168
37922
  type: Component,
@@ -38262,7 +38016,7 @@ class FormFieldContactsComponent {
38262
38016
  this.subscription.unsubscribe();
38263
38017
  }
38264
38018
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FormFieldContactsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
38265
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FormFieldContactsComponent, isStandalone: true, selector: "gn-ui-form-field-contacts", inputs: { value: "value", modelSpecifier: "modelSpecifier" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col gap-3\">\n <gn-ui-autocomplete\n [placeholder]=\"'editor.record.form.field.contacts.placeholder' | translate\"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"addContact($event)\"\n [displayWithFn]=\"displayWithFn\"\n [minCharacterCount]=\"1\"\n [clearOnSelection]=\"true\"\n >\n </gn-ui-autocomplete>\n\n @if (contacts.length > 0) {\n <gn-ui-sortable-list\n [items]=\"contacts\"\n (itemsOrderChange)=\"handleContactsChanged($event)\"\n [elementTemplate]=\"contactTemplate\"\n ></gn-ui-sortable-list>\n <ng-template #contactTemplate let-contact let-index=\"index\">\n @if (modelSpecifier === 'contact:editableDetails') {\n <gn-ui-contact-details-form\n [contact]=\"contact\"\n (contactChange)=\"handleContactChanged($event, index)\"\n ></gn-ui-contact-details-form>\n } @else {\n <gn-ui-contact-card [contact]=\"contact\"></gn-ui-contact-card>\n }\n </ng-template>\n } @else {\n <div\n class=\"p-4 text-sm border border-primary bg-primary-lightest rounded-lg\"\n translate\n >\n editor.record.form.field.contacts.noContact\n </div>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: ContactCardComponent, selector: "gn-ui-contact-card", inputs: ["contact"] }, { kind: "component", type: ContactDetailsFormComponent, selector: "gn-ui-contact-details-form", inputs: ["contact"], outputs: ["contactChange"] }, { kind: "component", type: SortableListComponent, selector: "gn-ui-sortable-list", inputs: ["elementTemplate", "items"], outputs: ["itemsOrderChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
38019
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FormFieldContactsComponent, isStandalone: true, selector: "gn-ui-form-field-contacts", inputs: { value: "value", modelSpecifier: "modelSpecifier" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col gap-3\">\n <gn-ui-autocomplete\n [placeholder]=\"'editor.record.form.field.contacts.placeholder' | translate\"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"addContact($event)\"\n [displayWithFn]=\"displayWithFn\"\n [minCharacterCount]=\"1\"\n [clearOnSelection]=\"true\"\n >\n </gn-ui-autocomplete>\n\n @if (contacts.length > 0) {\n <gn-ui-sortable-list\n [items]=\"contacts\"\n (itemsOrderChange)=\"handleContactsChanged($event)\"\n [elementTemplate]=\"contactTemplate\"\n ></gn-ui-sortable-list>\n <ng-template #contactTemplate let-contact let-index=\"index\">\n @if (modelSpecifier === 'contact:editableDetails') {\n <gn-ui-contact-details-form\n [contact]=\"contact\"\n (contactChange)=\"handleContactChanged($event, index)\"\n ></gn-ui-contact-details-form>\n } @else {\n <gn-ui-contact-card [contact]=\"contact\"></gn-ui-contact-card>\n }\n </ng-template>\n } @else {\n <div\n class=\"p-4 text-sm border border-primary bg-primary-lightest rounded-lg\"\n translate\n >\n editor.record.form.field.contacts.noContact\n </div>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithTemplate", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: ContactCardComponent, selector: "gn-ui-contact-card", inputs: ["contact"] }, { kind: "component", type: ContactDetailsFormComponent, selector: "gn-ui-contact-details-form", inputs: ["contact"], outputs: ["contactChange"] }, { kind: "component", type: SortableListComponent, selector: "gn-ui-sortable-list", inputs: ["elementTemplate", "items"], outputs: ["itemsOrderChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
38266
38020
  }
38267
38021
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FormFieldContactsComponent, decorators: [{
38268
38022
  type: Component,
@@ -38615,7 +38369,7 @@ class OnlineServiceResourceInputComponent {
38615
38369
  provideNgIconsConfig({
38616
38370
  size: '1.5em',
38617
38371
  }),
38618
- ], ngImport: i0, template: "<div class=\"flex flex-row justify-between\">\n <h3 class=\"text-[16px] font-bold text-main mb-[12px]\" translate>\n editor.record.form.field.onlineResource.edit.protocol\n </h3>\n @if (protocolHint) {\n <ng-icon\n name=\"matHelpOutline\"\n size=\"16\"\n class=\"m-2\"\n [matTooltip]=\"protocolHint\"\n matTooltipPosition=\"above\"\n ></ng-icon>\n }\n</div>\n<div class=\"flex flex-row items-center gap-4 h-[48px]\">\n <mat-radio-group\n aria-labelledby=\"example-radio-group-label\"\n class=\"flex flex-row gap-[8px]\"\n [(ngModel)]=\"_service.accessServiceProtocol\"\n [disabled]=\"disabled\"\n (change)=\"resetAllFormFields()\"\n >\n @for (protocolOption of availableProtocolOptions; track protocolOption) {\n <mat-radio-button [value]=\"protocolOption.value\">\n {{ protocolOption.label | translate }}\n </mat-radio-button>\n }\n </mat-radio-group>\n</div>\n\n<div class=\"flex flex-col gap-4\">\n <gn-ui-url-input\n class=\"w-full\"\n (uploadClick)=\"handleUploadClick($event)\"\n (valueChange)=\"handleUrlValueChange($event)\"\n [disabled]=\"disabled\"\n [value]=\"_service.url?.toString()\"\n [showValidateButton]=\"activeLayerSuggestion\"\n [resetUrlOnChange]=\"resetUrlOnChange\"\n >\n @if (activeLayerSuggestion) {\n <ng-content>\n <ng-icon name=\"iconoirRefresh\"></ng-icon>\n </ng-content>\n }\n </gn-ui-url-input>\n\n @if (loading) {\n <div class=\"flex justify-center w-full py-4\">\n <gn-ui-spinning-loader></gn-ui-spinning-loader>\n </div>\n }\n @if (errorMessage) {\n <p class=\"text-sm text-red-500 pl-4\" translate>\n editor.record.form.field.onlineResource.edit.identifier.error\n </p>\n }\n\n @if (\n !activeLayerSuggestion ||\n (_service.url && errorMessage) ||\n (modifyMode && (layers$ | async).length === 0)\n ) {\n <gn-ui-text-input\n class=\"grow border-b border-gray-300 pb-4\"\n [(value)]=\"_service.identifierInService\"\n [placeholder]=\"getIdentifierPlaceholder() | translate\"\n data-cy=\"identifier-in-service\"\n [disabled]=\"disabled\"\n ></gn-ui-text-input>\n }\n @if (activeLayerSuggestion && (layers$ | async)?.length > 0) {\n <gn-ui-autocomplete\n class=\"border-b border-gray-300 pb-4\"\n [placeholder]=\"\n 'editor.record.form.field.onlineResource.edit.identifier.select.label'\n | translate\n \"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"handleSelectValue($event)\"\n [displayWithFn]=\"displayWithFn\"\n [minCharacterCount]=\"0\"\n [clearOnSelection]=\"false\"\n >\n </gn-ui-autocomplete>\n }\n @if (\n !modifyMode &&\n ((activeLayerSuggestion && (layers$ | async)) ||\n !activeLayerSuggestion ||\n (_service.url && errorMessage))\n ) {\n <gn-ui-button\n (buttonClick)=\"submit()\"\n [disabled]=\"disabled || !_service.identifierInService\"\n type=\"primary\"\n >\n <span class=\"text-white font-bold\" translate>\n editor.record.form.field.onlineResource.edit.identifier.submit</span\n >\n </gn-ui-button>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i1$5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$1.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$1.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: SpinningLoaderComponent, selector: "gn-ui-spinning-loader" }, { kind: "component", type: TextInputComponent, selector: "gn-ui-text-input", inputs: ["value", "extraClass", "placeholder", "required", "disabled", "type"], outputs: ["valueChange"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: UrlInputComponent, selector: "gn-ui-url-input", inputs: ["value", "extraClass", "placeholder", "disabled", "showValidateButton", "resetUrlOnChange"], outputs: ["valueChange", "uploadClick"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
38372
+ ], ngImport: i0, template: "<div class=\"flex flex-row justify-between\">\n <h3 class=\"text-[16px] font-bold text-main mb-[12px]\" translate>\n editor.record.form.field.onlineResource.edit.protocol\n </h3>\n @if (protocolHint) {\n <ng-icon\n name=\"matHelpOutline\"\n size=\"16\"\n class=\"m-2\"\n [matTooltip]=\"protocolHint\"\n matTooltipPosition=\"above\"\n ></ng-icon>\n }\n</div>\n<div class=\"flex flex-row items-center gap-4 h-[48px]\">\n <mat-radio-group\n aria-labelledby=\"example-radio-group-label\"\n class=\"flex flex-row gap-[8px]\"\n [(ngModel)]=\"_service.accessServiceProtocol\"\n [disabled]=\"disabled\"\n (change)=\"resetAllFormFields()\"\n >\n @for (protocolOption of availableProtocolOptions; track protocolOption) {\n <mat-radio-button [value]=\"protocolOption.value\">\n {{ protocolOption.label | translate }}\n </mat-radio-button>\n }\n </mat-radio-group>\n</div>\n\n<div class=\"flex flex-col gap-4\">\n <gn-ui-url-input\n class=\"w-full\"\n (uploadClick)=\"handleUploadClick($event)\"\n (valueChange)=\"handleUrlValueChange($event)\"\n [disabled]=\"disabled\"\n [value]=\"_service.url?.toString()\"\n [showValidateButton]=\"activeLayerSuggestion\"\n [resetUrlOnChange]=\"resetUrlOnChange\"\n >\n @if (activeLayerSuggestion) {\n <ng-content>\n <ng-icon name=\"iconoirRefresh\"></ng-icon>\n </ng-content>\n }\n </gn-ui-url-input>\n\n @if (loading) {\n <div class=\"flex justify-center w-full py-4\">\n <gn-ui-spinning-loader></gn-ui-spinning-loader>\n </div>\n }\n @if (errorMessage) {\n <p class=\"text-sm text-red-500 pl-4\" translate>\n editor.record.form.field.onlineResource.edit.identifier.error\n </p>\n }\n\n @if (\n !activeLayerSuggestion ||\n (_service.url && errorMessage) ||\n (modifyMode && (layers$ | async).length === 0)\n ) {\n <gn-ui-text-input\n class=\"grow border-b border-gray-300 pb-4\"\n [(value)]=\"_service.identifierInService\"\n [placeholder]=\"getIdentifierPlaceholder() | translate\"\n data-cy=\"identifier-in-service\"\n [disabled]=\"disabled\"\n ></gn-ui-text-input>\n }\n @if (activeLayerSuggestion && (layers$ | async)?.length > 0) {\n <gn-ui-autocomplete\n class=\"border-b border-gray-300 pb-4\"\n [placeholder]=\"\n 'editor.record.form.field.onlineResource.edit.identifier.select.label'\n | translate\n \"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"handleSelectValue($event)\"\n [displayWithFn]=\"displayWithFn\"\n [minCharacterCount]=\"0\"\n [clearOnSelection]=\"false\"\n >\n </gn-ui-autocomplete>\n }\n @if (\n !modifyMode &&\n ((activeLayerSuggestion && (layers$ | async)) ||\n !activeLayerSuggestion ||\n (_service.url && errorMessage))\n ) {\n <gn-ui-button\n (buttonClick)=\"submit()\"\n [disabled]=\"disabled || !_service.identifierInService\"\n type=\"primary\"\n >\n <span class=\"text-white font-bold\" translate>\n editor.record.form.field.onlineResource.edit.identifier.submit</span\n >\n </gn-ui-button>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithTemplate", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i1$5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$1.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$1.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: SpinningLoaderComponent, selector: "gn-ui-spinning-loader" }, { kind: "component", type: TextInputComponent, selector: "gn-ui-text-input", inputs: ["value", "extraClass", "placeholder", "required", "disabled", "type"], outputs: ["valueChange"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: UrlInputComponent, selector: "gn-ui-url-input", inputs: ["value", "extraClass", "placeholder", "disabled", "showValidateButton", "resetUrlOnChange"], outputs: ["valueChange", "uploadClick"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
38619
38373
  }
38620
38374
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: OnlineServiceResourceInputComponent, decorators: [{
38621
38375
  type: Component,
@@ -40148,7 +39902,7 @@ function flattenQueryParams(params) {
40148
39902
  const start = flattened[key].start;
40149
39903
  const end = flattened[key].end;
40150
39904
  flattened[key] = [
40151
- `${start ? formatDate(start) : ''}..${formatDate(end) || ''}`,
39905
+ `${start ? formatDate(start) : ''}..${end ? formatDate(end) : ''}`,
40152
39906
  ];
40153
39907
  }
40154
39908
  }
@@ -40490,6 +40244,609 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
40490
40244
  args: ['gnUiSearchRouterContainer']
40491
40245
  }] } });
40492
40246
 
40247
+ const flatten = (base, obj, keepLevel = false) => Object.keys(obj).reduce((prev, curr) => {
40248
+ const path = base ? `${base}.${curr}` : curr;
40249
+ const val = obj[curr];
40250
+ if (keepLevel) {
40251
+ return { ...prev, [path]: flatten('', val) };
40252
+ }
40253
+ else if (typeof val === 'object' && !Array.isArray(val)) {
40254
+ return {
40255
+ ...prev,
40256
+ ...flatten(path, val),
40257
+ };
40258
+ }
40259
+ else {
40260
+ return { ...prev, [path]: val };
40261
+ }
40262
+ }, {});
40263
+ const checkKeys = (input, mandatory, optional) => {
40264
+ const keys = Object.keys(input);
40265
+ const missing = mandatory.filter((key) => keys.indexOf(key) === -1);
40266
+ const unrecognized = keys.filter((key) => mandatory.indexOf(key) === -1 && optional.indexOf(key) === -1);
40267
+ return {
40268
+ missing,
40269
+ unrecognized,
40270
+ };
40271
+ };
40272
+ /**
40273
+ * Only keeps recognized keys, flatten nested values (to allow having "." in keys),
40274
+ * pushes warnings and errors to the given arrays; returns null in case of error
40275
+ */
40276
+ function parseConfigSection(fullConfigObj, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors) {
40277
+ if (typeof fullConfigObj[sectionName] !== 'object') {
40278
+ if (mandatoryKeys.length === 0)
40279
+ return null;
40280
+ outErrors.push(`The [${sectionName}] mandatory section is missing.`);
40281
+ return null;
40282
+ }
40283
+ const sectionConf = fullConfigObj[sectionName];
40284
+ const keysCheck = checkKeys(sectionConf, mandatoryKeys, optionalKeys);
40285
+ if (keysCheck.missing.length) {
40286
+ // note: this is not thrown to allow merging several Errors down the line
40287
+ outErrors.push(`In the [${sectionName}] section: ${keysCheck.missing.join(', ')}`);
40288
+ return null;
40289
+ }
40290
+ else if (keysCheck.unrecognized.length) {
40291
+ outWarnings.push(`In the [${sectionName}] section: ${keysCheck.unrecognized.join(', ')}`);
40292
+ keysCheck.unrecognized.forEach((key) => delete sectionConf[key]);
40293
+ }
40294
+ return sectionConf;
40295
+ }
40296
+ /**
40297
+ * Same as parseConfigSection but keeps all keys, recognized or not
40298
+ * Also the first level is the language code (e.g. 'en')
40299
+ * This is optional
40300
+ */
40301
+ function parseTranslationsConfigSection(fullConfigObj, sectionName) {
40302
+ if (typeof fullConfigObj[sectionName] !== 'object') {
40303
+ return {};
40304
+ }
40305
+ return flatten('', fullConfigObj[sectionName], true);
40306
+ }
40307
+ /**
40308
+ * Same as parseConfigSection but handles an array of configs; this is
40309
+ * considered optional; returns null in case of error in at least one of
40310
+ * the sections
40311
+ */
40312
+ function parseMultiConfigSection(fullConfigObj, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors) {
40313
+ const sections = fullConfigObj[sectionName];
40314
+ if (!sections) {
40315
+ return [];
40316
+ }
40317
+ if (!Array.isArray(sections)) {
40318
+ outErrors.push(`Expected [[${sectionName}]] to be an array of sections.`);
40319
+ return null;
40320
+ }
40321
+ const result = sections.map((section) => parseConfigSection({ [sectionName]: section }, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors));
40322
+ if (result.some((r) => r === null)) {
40323
+ return null;
40324
+ }
40325
+ return result;
40326
+ }
40327
+ function checkMetadataLanguage(parsedConfigSection, outWarnings) {
40328
+ const lang2 = toLang2(parsedConfigSection.metadata_language.toLowerCase());
40329
+ if (lang2?.length !== 2) {
40330
+ outWarnings.push(`In the [global] section: metadata_language = "${parsedConfigSection.metadata_language}" is not a recognized ISO 639 language code`);
40331
+ }
40332
+ return parsedConfigSection;
40333
+ }
40334
+ function checkNewRecordDefaultLanguage(parsedConfigSection, outWarnings) {
40335
+ const lang2 = toLang2(parsedConfigSection.new_record_default_language.toLowerCase());
40336
+ if (!(lang2 in LANG_2_TO_3_MAPPER)) {
40337
+ outWarnings.push(`In the [editing] section: new_record_default_language = "${parsedConfigSection.new_record_default_language}" is not a recognized ISO 639 language code`);
40338
+ return {
40339
+ ...parsedConfigSection,
40340
+ new_record_default_language: undefined,
40341
+ };
40342
+ }
40343
+ return {
40344
+ ...parsedConfigSection,
40345
+ new_record_default_language: lang2,
40346
+ };
40347
+ }
40348
+ function checkNewRecordStandard(parsedConfigSection, outWarnings) {
40349
+ const standard = parsedConfigSection.new_record_standard;
40350
+ const normalizedStandard = typeof standard === 'string' ? standard.trim().toLowerCase() : null;
40351
+ if (normalizedStandard === 'iso19139' ||
40352
+ normalizedStandard === 'iso19115-3') {
40353
+ return {
40354
+ ...parsedConfigSection,
40355
+ new_record_standard: normalizedStandard,
40356
+ };
40357
+ }
40358
+ outWarnings.push(`In the [editing] section: new_record_standard = "${standard}" is not a supported metadata standard`);
40359
+ return {
40360
+ ...parsedConfigSection,
40361
+ new_record_standard: undefined,
40362
+ };
40363
+ }
40364
+
40365
+ /**
40366
+ * This loader extends the default one based on JSON files but also loads custom translations
40367
+ * defined in the app configuration
40368
+ * Implements a fallback on default lang if translated labels are empty
40369
+ */
40370
+ class FileWithOverridesTranslateLoader extends FileTranslateLoader {
40371
+ getTranslation(lang) {
40372
+ const baseLang = lang.substring(0, 2); // removing the right part of e.g. en_EN
40373
+ return super.getTranslation(baseLang).pipe(map$1((translations) => ({
40374
+ ...translations,
40375
+ ...getCustomTranslations(baseLang),
40376
+ })));
40377
+ }
40378
+ }
40379
+
40380
+ const MISSING_CONFIG_ERROR = `Application configuration was not initialized correctly.
40381
+ This error might show up in case of an invalid/malformed configuration file.
40382
+
40383
+ Note: make sure that you have called \`loadAppConfig\` from '../../../../../libs/util/app-config/src' before starting the Angular application.`;
40384
+ let globalConfig = null;
40385
+ function getGlobalConfig() {
40386
+ if (globalConfig === null)
40387
+ throw new Error(MISSING_CONFIG_ERROR);
40388
+ return globalConfig;
40389
+ }
40390
+ let themeConfig = null;
40391
+ function getThemeConfig() {
40392
+ if (themeConfig === null)
40393
+ throw new Error(MISSING_CONFIG_ERROR);
40394
+ return themeConfig;
40395
+ }
40396
+ let mapConfig = null;
40397
+ function getOptionalMapConfig() {
40398
+ return mapConfig;
40399
+ }
40400
+ let searchConfig = null;
40401
+ function getOptionalSearchConfig() {
40402
+ return searchConfig;
40403
+ }
40404
+ let editorConfig = null;
40405
+ function getOptionalEditorConfig() {
40406
+ return editorConfig;
40407
+ }
40408
+ let metadataQualityConfig = null;
40409
+ function getMetadataQualityConfig() {
40410
+ return (metadataQualityConfig ||
40411
+ {
40412
+ ENABLED: false,
40413
+ });
40414
+ }
40415
+ let customTranslations = null;
40416
+ function getCustomTranslations(langCode) {
40417
+ if (customTranslations === null)
40418
+ throw new Error(MISSING_CONFIG_ERROR);
40419
+ return langCode in customTranslations ? customTranslations[langCode] : {};
40420
+ }
40421
+ let appConfigLoaded = false;
40422
+ function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
40423
+ console.log(`[geonetwork-ui] Loading application configuration from ${configUrl}`);
40424
+ return fetch(configUrl)
40425
+ .then((resp) => {
40426
+ if (!resp.ok)
40427
+ throw new Error('Configuration file could not be loaded');
40428
+ return resp.text();
40429
+ })
40430
+ .then((conf) => {
40431
+ let parsed;
40432
+ try {
40433
+ parsed = TOML.parse(conf, { joiner: '\n', bigint: false });
40434
+ }
40435
+ catch (e) {
40436
+ throw new Error(`An error occurred when parsing the configuration file: ${e.message}`);
40437
+ }
40438
+ const errors = [];
40439
+ const warnings = [];
40440
+ let parsedGlobalSection = parseConfigSection(parsed, 'global', ['geonetwork4_api_url'], [
40441
+ 'datahub_url',
40442
+ 'edit_url_template',
40443
+ 'proxy_path',
40444
+ 'metadata_language',
40445
+ 'login_url',
40446
+ 'logout_url',
40447
+ 'settings_url',
40448
+ 'web_component_embedder_url',
40449
+ 'languages',
40450
+ 'contact_email',
40451
+ 'reuse_form_url',
40452
+ ], warnings, errors);
40453
+ if (parsedGlobalSection?.metadata_language) {
40454
+ parsedGlobalSection = checkMetadataLanguage(parsedGlobalSection, warnings);
40455
+ }
40456
+ globalConfig =
40457
+ parsedGlobalSection === null
40458
+ ? null
40459
+ : {
40460
+ GN4_API_URL: parsedGlobalSection.geonetwork4_api_url,
40461
+ DATAHUB_URL: parsedGlobalSection.datahub_url,
40462
+ EDIT_URL_TEMPLATE: parsedGlobalSection.edit_url_template,
40463
+ PROXY_PATH: parsedGlobalSection.proxy_path,
40464
+ METADATA_LANGUAGE: parsedGlobalSection.metadata_language
40465
+ ? parsedGlobalSection.metadata_language.toLowerCase()
40466
+ : undefined,
40467
+ LOGIN_URL: parsedGlobalSection.login_url,
40468
+ LOGOUT_URL: parsedGlobalSection.logout_url,
40469
+ SETTINGS_URL: parsedGlobalSection.settings_url,
40470
+ WEB_COMPONENT_EMBEDDER_URL: parsedGlobalSection.web_component_embedder_url,
40471
+ LANGUAGES: parsedGlobalSection.languages,
40472
+ CONTACT_EMAIL: parsedGlobalSection.contact_email,
40473
+ REUSE_FORM_URL: parsedGlobalSection.reuse_form_url,
40474
+ };
40475
+ const parsedLayersSections = parseMultiConfigSection(parsed, 'map_layer', ['type'], ['name', 'url', 'data', 'styleUrl', 'accessToken'], warnings, errors);
40476
+ const parsedMapSection = parseConfigSection(parsed, 'map', [], [
40477
+ 'max_zoom',
40478
+ 'do_not_tile_wms',
40479
+ 'max_extent',
40480
+ 'max_feature_count',
40481
+ 'baselayer',
40482
+ 'do_not_use_default_basemap',
40483
+ 'external_viewer_url_template',
40484
+ 'external_viewer_open_new_tab',
40485
+ ], warnings, errors);
40486
+ mapConfig =
40487
+ parsedMapSection === null || parsedLayersSections === null
40488
+ ? null
40489
+ : {
40490
+ MAX_ZOOM: parsedMapSection.max_zoom,
40491
+ DO_NOT_TILE_WMS: parsedMapSection.do_not_tile_wms,
40492
+ MAX_EXTENT: parsedMapSection.max_extent,
40493
+ MAX_FEATURE_COUNT: parsedMapSection.max_feature_count,
40494
+ EXTERNAL_VIEWER_URL_TEMPLATE: parsedMapSection.external_viewer_url_template,
40495
+ EXTERNAL_VIEWER_OPEN_NEW_TAB: parsedMapSection.external_viewer_open_new_tab,
40496
+ DO_NOT_USE_DEFAULT_BASEMAP: !!parsedMapSection.do_not_use_default_basemap,
40497
+ MAP_LAYERS: parsedLayersSections.map((map_layer) => ({
40498
+ TYPE: map_layer.type,
40499
+ URL: map_layer.url,
40500
+ NAME: map_layer.name,
40501
+ DATA: map_layer.data,
40502
+ STYLE_URL: map_layer.styleUrl,
40503
+ ACCESS_TOKEN: map_layer.accessToken,
40504
+ })),
40505
+ };
40506
+ const parsedThemeSection = parseConfigSection(parsed, 'theme', ['primary_color', 'secondary_color', 'main_color', 'background_color'], [
40507
+ 'header_foreground_color',
40508
+ 'main_font',
40509
+ 'title_font',
40510
+ 'fonts_stylesheet_url',
40511
+ 'thumbnail_placeholder',
40512
+ 'header_background',
40513
+ 'favicon',
40514
+ ], warnings, errors);
40515
+ themeConfig =
40516
+ parsedThemeSection === null
40517
+ ? null
40518
+ : {
40519
+ PRIMARY_COLOR: parsedThemeSection.primary_color,
40520
+ SECONDARY_COLOR: parsedThemeSection.secondary_color,
40521
+ MAIN_COLOR: parsedThemeSection.main_color,
40522
+ BACKGROUND_COLOR: parsedThemeSection.background_color,
40523
+ HEADER_FOREGROUND_COLOR: parsedThemeSection.header_foreground_color,
40524
+ THUMBNAIL_PLACEHOLDER: parsedThemeSection.thumbnail_placeholder,
40525
+ HEADER_BACKGROUND: parsedThemeSection.header_background,
40526
+ TITLE_FONT: parsedThemeSection.title_font,
40527
+ MAIN_FONT: parsedThemeSection.main_font,
40528
+ FONTS_STYLESHEET_URL: parsedThemeSection.fonts_stylesheet_url,
40529
+ FAVICON: parsedThemeSection.favicon,
40530
+ };
40531
+ const parsedSearchSection = parseConfigSection(parsed, 'search', [], [
40532
+ 'record_kind_quick_filter',
40533
+ 'filter_geometry_data',
40534
+ 'filter_geometry_url',
40535
+ 'do_not_use_default_search_preset',
40536
+ 'search_preset',
40537
+ 'advanced_filters',
40538
+ 'limit',
40539
+ 'spatial_extent_max_file_size',
40540
+ 'geocoding_provider',
40541
+ 'geocoding_provider_options',
40542
+ ], warnings, errors);
40543
+ const parsedSearchParams = parseMultiConfigSection(parsed, 'search_preset', ['name'], ['sort', 'filters'], warnings, errors);
40544
+ const parsedCustomFilters = parseMultiConfigSection(parsed, 'custom_filter', ['name', 'base_filter'], ['exclude_values', 'include_values', 'label_key'], warnings, errors);
40545
+ searchConfig =
40546
+ parsedSearchSection === null
40547
+ ? null
40548
+ : {
40549
+ RECORD_KIND_QUICK_FILTER: parsedSearchSection.record_kind_quick_filter,
40550
+ FILTER_GEOMETRY_DATA: parsedSearchSection.filter_geometry_data,
40551
+ FILTER_GEOMETRY_URL: parsedSearchSection.filter_geometry_url,
40552
+ DO_NOT_USE_DEFAULT_SEARCH_PRESET: !!parsedSearchSection.do_not_use_default_search_preset,
40553
+ SEARCH_PRESET: parsedSearchParams.map((param) => ({
40554
+ sort: param.sort,
40555
+ name: param.name,
40556
+ filters: param.filters,
40557
+ })),
40558
+ ADVANCED_FILTERS: parsedSearchSection.advanced_filters,
40559
+ CUSTOM_FILTERS: parsedCustomFilters.map((customFilter) => ({
40560
+ name: customFilter.name,
40561
+ baseFilter: customFilter.base_filter,
40562
+ excludeValues: customFilter.exclude_values,
40563
+ includeValues: customFilter.include_values,
40564
+ labelKey: customFilter.label_key,
40565
+ })),
40566
+ LIMIT: parsedSearchSection.limit,
40567
+ SPATIAL_EXTENT_MAX_FILE_SIZE: parsedSearchSection.spatial_extent_max_file_size,
40568
+ GEOCODING_PROVIDER: parsedSearchSection.geocoding_provider,
40569
+ GEOCODING_PROVIDER_OPTIONS: parsedSearchSection.geocoding_provider_options,
40570
+ };
40571
+ const parsedMetadataQualitySection = parseConfigSection(parsed, 'metadata-quality', [], ['enabled'], warnings, errors);
40572
+ metadataQualityConfig =
40573
+ parsedMetadataQualitySection === null
40574
+ ? null
40575
+ : {
40576
+ ENABLED: parsedMetadataQualitySection.enabled,
40577
+ SORTABLE: parsedMetadataQualitySection.sortable,
40578
+ };
40579
+ let parsedEditingSection = parseConfigSection(parsed, 'editing', [], ['new_record_default_language', 'new_record_standard'], warnings, errors);
40580
+ if (parsedEditingSection !== null &&
40581
+ parsedEditingSection.new_record_default_language !== undefined) {
40582
+ parsedEditingSection = checkNewRecordDefaultLanguage(parsedEditingSection, warnings);
40583
+ }
40584
+ if (parsedEditingSection !== null &&
40585
+ parsedEditingSection.new_record_standard !== undefined) {
40586
+ parsedEditingSection = checkNewRecordStandard(parsedEditingSection, warnings);
40587
+ }
40588
+ editorConfig =
40589
+ parsedEditingSection === null
40590
+ ? null
40591
+ : {
40592
+ NEW_RECORD_DEFAULT_LANGUAGE: parsedEditingSection.new_record_default_language,
40593
+ NEW_RECORD_STANDARD: parsedEditingSection.new_record_standard,
40594
+ };
40595
+ customTranslations = parseTranslationsConfigSection(parsed, 'translations');
40596
+ if (errors.length) {
40597
+ throw new Error(`One or more mandatory settings were missing from the configuration file.
40598
+ ${errors.join('\n')}`);
40599
+ }
40600
+ else if (warnings.length) {
40601
+ console.warn(`One or more unexpected settings were encountered in the configuration file.
40602
+ ${warnings.join('\n')}`);
40603
+ }
40604
+ appConfigLoaded = true;
40605
+ });
40606
+ }
40607
+ function isConfigLoaded() {
40608
+ return appConfigLoaded;
40609
+ }
40610
+ function _reset() {
40611
+ globalConfig = null;
40612
+ themeConfig = null;
40613
+ editorConfig = null;
40614
+ customTranslations = null;
40615
+ }
40616
+ const TRANSLATE_WITH_OVERRIDES_CONFIG = {
40617
+ compiler: {
40618
+ provide: TranslateCompiler,
40619
+ useClass: TranslateMessageFormatCompiler,
40620
+ },
40621
+ loader: [
40622
+ {
40623
+ provide: TRANSLATE_HTTP_LOADER_CONFIG,
40624
+ useValue: {
40625
+ prefix: './assets/i18n/',
40626
+ suffix: '.json',
40627
+ },
40628
+ },
40629
+ {
40630
+ provide: TranslateLoader,
40631
+ useClass: FileWithOverridesTranslateLoader,
40632
+ deps: [HttpClient, TRANSLATE_HTTP_LOADER_CONFIG],
40633
+ },
40634
+ ],
40635
+ };
40636
+
40637
+ const okAppConfigFixture = () => `
40638
+ [global]
40639
+ geonetwork4_api_url = "/geonetwork/srv/api"
40640
+ proxy_path = "/proxy/?url="
40641
+ metadata_language = "fre"
40642
+ login_url = "/cas/login?service="
40643
+ logout_url = "/geonetwork/signout"
40644
+ settings_url = "/geonetwork/settings"
40645
+ web_component_embedder_url = "/datahub/wc-embedder.html"
40646
+
40647
+ [map]
40648
+ max_zoom = 10
40649
+ max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855]
40650
+ do_not_use_default_basemap = false
40651
+ external_viewer_url_template = 'https://example.com/myviewer?'
40652
+ external_viewer_open_new_tab = true
40653
+ [[map_layer]]
40654
+ type = "wms"
40655
+ url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
40656
+ name = "masque_hdf_ign_carto_latin1"
40657
+ [[map_layer]]
40658
+ type = "wfs"
40659
+ url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
40660
+ name = "masque_hdf_ign_carto_latin1"
40661
+
40662
+ [theme]
40663
+ primary_color = "#093564"
40664
+ secondary_color = "#c2e9dc"
40665
+ main_color = "#212029" # All-purpose text color
40666
+ background_color = "#fdfbff"
40667
+ thumbnail_placeholder = 'assets/img/placeholder.svg'
40668
+ header_background = 'teal'
40669
+ header_foreground_color = "#872e2e"
40670
+ main_font = 'sans-serif'
40671
+ title_font = 'serif'
40672
+ fonts_stylesheet_url = "https://fonts.googleapis.com/css2?family=Open+Sans"
40673
+
40674
+ [search]
40675
+ record_kind_quick_filter = false
40676
+ filter_geometry_url = 'https://my.domain.org/geom.json'
40677
+ do_not_use_default_search_preset = false
40678
+ geocoding_provider = "geoplateforme"
40679
+ geocoding_provider_options.category = "administratif"
40680
+ geocoding_provider_options.limit = 5
40681
+ advanced_filters = ['publicationYear', 'documentStandard', 'inspireKeyword', 'topic', 'license', 'myOrg:firstCustomFilter', 'myOrg:secondCustomFilter']
40682
+
40683
+ [[custom_filter]]
40684
+ name = 'myOrg:firstCustomFilter'
40685
+ base_filter = 'keyword'
40686
+ exclude_values = ['my keyword 1', 'my keyword 2']
40687
+ label_key = 'myOrg.firstCustomFilter'
40688
+
40689
+ [[custom_filter]]
40690
+ name = 'myOrg:secondCustomFilter'
40691
+ base_filter = 'keyword'
40692
+ include_values = ['my keyword 3', 'my keyword 4']
40693
+ label_key = 'myOrg.secondCustomFilter'
40694
+
40695
+ [[search_preset]]
40696
+ sort = "-createDate"
40697
+ name = 'filterByOrgs'
40698
+ filters.q = 'Carto'
40699
+ filters.organisation = ['Org1', 'Org2']
40700
+ filters.format = ['ESRI Shapefile']
40701
+ filters.topic = ['boundaries']
40702
+ filters.publicationYear = ['2023', '2022']
40703
+ filters.spatial = ['yes']
40704
+
40705
+ [translations.en]
40706
+ "my.first.key" = 'First label.'
40707
+ "my.second.key" = """
40708
+ Second label,
40709
+ on two lines."""
40710
+
40711
+ [translations.de]
40712
+ my.first.key = 'Erste Etikett.'
40713
+
40714
+ [translations.fr]
40715
+ my.sample.text = "Un bon exemple de texte."
40716
+ "my.quoted.text" = 'du texte entre guillements.'
40717
+ `;
40718
+ const appConfigWithTranslationFixture = () => okAppConfigFixture();
40719
+ const minimalAppConfigFixture = () => `
40720
+ [global]
40721
+ geonetwork4_api_url = "/geonetwork/srv/api"
40722
+ proxy_path = "/proxy/?url="
40723
+
40724
+ [theme]
40725
+ primary_color = "#093564"
40726
+ secondary_color = "#c2e9dc"
40727
+ main_color = "#212029" # All-purpose text color
40728
+ background_color = "#fdfbff"
40729
+ `;
40730
+ const mapConfigFixture = () => ({
40731
+ MAX_ZOOM: 10,
40732
+ MAX_EXTENT: [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855],
40733
+ DO_NOT_USE_DEFAULT_BASEMAP: false,
40734
+ DO_NOT_TILE_WMS: false,
40735
+ EXTERNAL_VIEWER_URL_TEMPLATE: 'https://example.com/myviewer/#/?actions=[{"type":"CATALOG:ADD_LAYERS_FROM_CATALOGS","layers":["${layer_name}"],"sources":[{"url":"${service_url}","type":"${service_type}"}]}]',
40736
+ EXTERNAL_VIEWER_OPEN_NEW_TAB: true,
40737
+ MAP_LAYERS: [
40738
+ {
40739
+ TYPE: 'xyz',
40740
+ URL: 'https://some-basemap-server',
40741
+ },
40742
+ {
40743
+ TYPE: 'wms',
40744
+ URL: 'https://some-wms-server',
40745
+ NAME: 'some_layername',
40746
+ },
40747
+ {
40748
+ TYPE: 'wfs',
40749
+ URL: 'https://some-wfs-server',
40750
+ NAME: 'some_layername',
40751
+ },
40752
+ ],
40753
+ });
40754
+ const malformedConfigFixture = () => `
40755
+ {
40756
+ "I thought": "we were still doing json"
40757
+ }
40758
+ `;
40759
+ const missingMandatoryConfigFixture = () => `
40760
+ [theme]
40761
+ primary_color = "#093564"
40762
+ secondary_color = "#c2e9dc"
40763
+ background_color = "#fdfbff"
40764
+ main_font = 'sans-serif'
40765
+ `;
40766
+ const wrongLanguageCodeConfigFixture = () => `
40767
+ [global]
40768
+ geonetwork4_api_url = "/geonetwork/srv/api"
40769
+ proxy_path = "/proxy/?url="
40770
+ metadata_language = "abc"
40771
+
40772
+ [map]
40773
+
40774
+ [theme]
40775
+ primary_color = "#093564"
40776
+ secondary_color = "#c2e9dc"
40777
+ main_color = "#212029" # All-purpose text color
40778
+ background_color = "#fdfbff"
40779
+ `;
40780
+ const unrecognizedKeysConfigFixture = () => `
40781
+ [global]
40782
+ geonetwork4_api_url = "/geonetwork/srv/api"
40783
+ proxy_path = "/proxy/?url="
40784
+ metadata_language = "fre"
40785
+ another_path = '/whatever'
40786
+
40787
+ [map]
40788
+ max_zoom = 10
40789
+ max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855]
40790
+ do_not_use_default_basemap = false
40791
+ external_viewer_url_template = 'https://example.com/myviewer?'
40792
+ external_viewer_open_new_tab = true
40793
+ another_zoom = 15
40794
+ [[map_layer]]
40795
+ type = "wms"
40796
+ url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
40797
+ name = "masque_hdf_ign_carto_latin1"
40798
+ [[map_layer]]
40799
+ type = "wfs"
40800
+ url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
40801
+ name = "masque_hdf_ign_carto_latin1"
40802
+ another_layer = "wrong layer definition"
40803
+
40804
+ [theme]
40805
+ primary_color = "#093564"
40806
+ secondary_color = "#c2e9dc"
40807
+ main_color = "#212029" # All-purpose text color
40808
+ background_color = "#fdfbff"
40809
+ thumbnail_placeholder = 'assets/img/placeholder.svg'
40810
+ header_background = 'teal'
40811
+ header_foreground_color = "#872e2e"
40812
+ main_font = 'sans-serif'
40813
+ title_font = 'serif'
40814
+ another_color = 'red'
40815
+ `;
40816
+
40817
+ function getMapContextLayerFromConfig(config) {
40818
+ switch (config.TYPE) {
40819
+ case 'wms':
40820
+ return {
40821
+ type: 'wms',
40822
+ url: config.URL,
40823
+ name: config.NAME,
40824
+ };
40825
+ case 'wfs':
40826
+ return {
40827
+ type: 'wfs',
40828
+ url: config.URL,
40829
+ featureType: config.NAME,
40830
+ };
40831
+ case 'xyz':
40832
+ return {
40833
+ type: config.TYPE,
40834
+ url: config.URL,
40835
+ };
40836
+ case 'geojson':
40837
+ return {
40838
+ type: config.TYPE,
40839
+ ...(config.DATA ? { data: config.DATA } : { url: config.URL }),
40840
+ };
40841
+ case 'maplibre-style':
40842
+ return {
40843
+ type: config.TYPE,
40844
+ styleUrl: config.STYLE_URL,
40845
+ accessToken: config.ACCESS_TOKEN,
40846
+ };
40847
+ }
40848
+ }
40849
+
40493
40850
  const CHART_TYPE_VALUES = [
40494
40851
  'bar',
40495
40852
  'bar-horizontal',
@@ -40505,5 +40862,5 @@ const CHART_TYPE_VALUES = [
40505
40862
  * Generated bundle index. Do not edit.
40506
40863
  */
40507
40864
 
40508
- export { ADD_RESULTS, ADD_SEARCH, AVAILABLE_LICENSES, AbstractAction, AbstractSearchField, ActionMenuComponent, AddLayerFromCatalogComponent, AddLayerRecordPreviewComponent, AddResults, AddSearch, AnchorLinkDirective, ApiCardComponent, ApplicationBannerComponent, AuthService, AutocompleteComponent, AvailableServicesField, AvatarComponent, AvatarServiceInterface, BASEMAP_LAYERS, BadgeComponent, BaseConverter, BaseFileReader, BaseReader, BlockListComponent, ButtonComponent, CHART_TYPE_VALUES, CLEAR_ERROR, CLEAR_RESULTS, CarouselComponent, CatalogTitleComponent, CellPopinComponent, ChartComponent, ChartViewComponent, CheckToggleComponent, CheckboxComponent, ClearError, ClearResults, ColorScaleComponent, ConfirmationDialogComponent, ContactDetailsComponent, ContactDetailsFormComponent, ContactPillComponent, ContentGhostComponent, CopyTextButtonComponent, DEFAULT_BASEMAP_LAYER, DEFAULT_CONFIGURATION, DEFAULT_GN4_LOGIN_URL, DEFAULT_GN4_LOGOUT_URL, DEFAULT_GN4_SETTINGS_URL, DEFAULT_LANG, DEFAULT_PAGE_SIZE, DEFAULT_RECORD_CONVERTER, DEFAULT_RESULTS_LAYOUT_CONFIG, DEFAULT_SEARCH_KEY, DEFAULT_SPATIAL_EXTENT_STYLE, DISABLE_AUTH, DISABLE_DRAFT, DO_NOT_USE_DEFAULT_BASEMAP, DataService, DataTableComponent, DataViewComponent, DataViewPermalinkComponent, DataViewShareComponent, DataViewWebComponentComponent, DatePickerComponent, DateRangeDropdownComponent, DateRangeInputsComponent, DateRangePickerComponent, DateRangeSearchField, DateService, DcatApConverter, DefaultRouterModule, DownloadItemComponent, DownloadsListComponent, DragAndDropFileInputComponent, DropdownMultiselectComponent, DropdownSelectorComponent, EDITOR_FEATURE_KEY, ES_QUERY_FIELDS_PRIORITY, ES_RESOURCES_VALUES, ES_SOURCE_SUMMARY, EXTERNAL_VIEWER_OPEN_NEW_TAB, EXTERNAL_VIEWER_URL_TEMPLATE, EditableLabelDirective, EditorFacade, EditorService, ElasticsearchService, ErrorComponent, ErrorType, ExpandablePanelButtonComponent, ExpandablePanelComponent, ExternalLinkCardComponent, ExternalViewerButtonComponent, FIELDS_BRIEF, FIELDS_SUMMARY, FILTER_GEOMETRY, FILTER_SUMMARY_IGNORE_LIST, FORMATS, FacetBlockComponent, FacetItemComponent, FacetListComponent, FacetsContainerComponent, FavoriteStarComponent, FavoritesService, FeatureCatalogListComponent, FeatureDetailComponent, FeatureEditorModule, FeatureMapModule, FeatureRecordModule, FeatureSearchModule, FetchError, FieldFocusDirective, FieldsService, FigureComponent, FigureContainerComponent, FileInputComponent, FileTranslateLoader, FilesDropDirective, FilterDropdownComponent, FormFieldArrayComponent, FormFieldComponent, FormFieldDateComponent, FormFieldFileComponent, FormFieldKeywordsComponent, FormFieldLicenseComponent, FormFieldObjectComponent, FormFieldRichComponent, FormFieldSimpleComponent, FormFieldSpatialExtentComponent, FormFieldTemporalExtentsComponent, FormFieldTopicsComponent, FormFieldWrapperComponent, FullTextSearchField, FuzzySearchComponent, GEONETWORK_UI_TAG_NAME, GEONETWORK_UI_VERSION, GeoDataBadgeComponent, GeoTableViewComponent, GeocodingComponent, GeojsonReader, Gn4Converter, Gn4PlatformMapper, Gn4PlatformService, Gn4Repository, Gn4SettingsService, GnUiHumanizeDateDirective, GpfApiDlComponent, GravatarService, I18nInterceptor, ISO_TOPICS, ImageFallbackDirective, ImageInputComponent, ImageOverlayPreviewComponent, ImportRecordComponent, InlineFilterComponent, InteractiveTableColumnComponent, InteractiveTableComponent, InternalLinkCardComponent, IsSpatialSearchField, Iso191153Converter, Iso19139Converter, KeywordBadgeComponent, KindBadgeComponent, LANGUAGES_LIST, LANGUAGE_NAMES, LANGUAGE_STORAGE_KEY, LANG_2_TO_3_MAPPER, LOGIN_URL, LOGOUT_URL, LONLAT_CRS_CODES, LanguageSwitcherComponent, LayersPanelComponent, LicenseSearchField, LinkClassifierService, LinkUsage, LoadingMaskComponent, LogService, MAP_FEATURE_KEY, MAP_VIEW_CONSTRAINTS, MAX_UPLOAD_SIZE_MB, METADATA_LANGUAGE, MapContainerComponent, MapFacade, MapLegendComponent, MapStateContainerComponent, MapStyleService, MapUtilsService, MapViewComponent, MarkdownEditorComponent, MarkdownParserComponent, MaxLinesComponent, mdview_actions as MdViewActions, MdViewFacade, MetadataCatalogComponent, MetadataContactComponent, MetadataDoiComponent, MetadataInfoComponent, MetadataLinkType, MetadataMapperContext, MetadataQualityComponent, MetadataQualityItemComponent, MetadataQualityPanelComponent, ModalDialogComponent, MultilingualPanelComponent, MultilingualSearchField, MyOrgService, NAMESPACES, NOT_APPLICABLE_CONSTRAINT, NOT_KNOWN_CONSTRAINT, NotificationComponent, NotificationsContainerComponent, NotificationsService, OPEN_DATA_LICENSE, ORGANIZATIONS_STRATEGY, ORGANIZATION_PAGE_URL_TOKEN, ORGANIZATION_URL_TOKEN, OnlineResourceCardComponent, OnlineServiceResourceInputComponent, OrganisationPreviewComponent, OrganisationsComponent, OrganisationsFilterComponent, OrganisationsResultComponent, OrganizationSearchField, OrganizationsFromGroupsService, OrganizationsFromMetadataService, OrganizationsServiceInterface, OwnerSearchField, PAGINATE, PARSE_DELIMITER, PATCH_RESULTS_AGGREGATIONS, PROXY_PATH, Paginate, PaginationButtonsComponent, PaginationComponent, PaginationDotsComponent, PatchResultsAggregations, PlatformServiceInterface, PopoverComponent, PopupAlertComponent, PossibleResourceTypes, PossibleResourceTypesDefinition, PreviousNextButtonsComponent, ProgressBarComponent, ProxyService, QUERY_FIELDS, RECORD_DATASET_URL_TOKEN, RECORD_REUSE_URL_TOKEN, RECORD_SERVICE_URL_TOKEN, REQUEST_MORE_ON_AGGREGATION, REQUEST_MORE_RESULTS, REQUEST_NEW_RESULTS, RESULTS_LAYOUT_CONFIG, REUSE_LIGHT_CONFIGURATION, ROUTER_CONFIG, ROUTER_ROUTE_DATASET, ROUTER_ROUTE_ORGANIZATION, ROUTER_ROUTE_REUSE, ROUTER_ROUTE_SEARCH, ROUTER_ROUTE_SERVICE, ROUTER_STATE_KEY, ROUTE_PARAMS, RecordApiFormComponent, RecordFormComponent, RecordKindField, RecordMetaComponent, RecordMetricComponent, RecordPreviewCardComponent, RecordPreviewComponent, RecordPreviewFeedComponent, RecordPreviewListComponent, RecordPreviewRowComponent, RecordPreviewTextComponent, RecordPreviewTitleComponent, RecordStatusValues, RecordsMetricsComponent, RecordsRepositoryInterface, RecordsService, RequestMoreOnAggregation, RequestMoreResults, RequestNewResults, ResourceTypeLegacyField, ResultsHitsContainerComponent, ResultsHitsNumberComponent, ResultsHitsSearchKindComponent, ResultsLayoutComponent, ResultsLayoutConfigItem, ResultsListComponent, ResultsListContainerComponent, ResultsListItemComponent, ResultsTableComponent, ResultsTableContainerComponent, ReusePresentationForms, RoleLabels, RoleValues, RouterEffects, RouterFacade, RouterService, SEARCH_FEATURE_KEY, SETTINGS_URL, SET_CONFIG_AGGREGATIONS, SET_CONFIG_FILTERS, SET_CONFIG_REQUEST_FIELDS, SET_ERROR, SET_FAVORITES_ONLY, SET_FILTERS, SET_INCLUDE_ON_AGGREGATION, SET_PAGE_SIZE, SET_RESULTS_AGGREGATIONS, SET_RESULTS_HITS, SET_RESULTS_LAYOUT, SET_SEARCH, SET_SORT_BY, SET_SPATIAL_FILTER_ENABLED, SPATIAL_SCOPES, SearchEffects, SearchFacade, SearchFeatureCatalogComponent, SearchFiltersSummaryComponent, SearchFiltersSummaryItemComponent, SearchInputComponent, SearchRouterContainerDirective, SearchService, SearchStateContainerDirective, SelectionService, ServiceCapabilitiesComponent, SetConfigAggregations, SetConfigFilters, SetConfigRequestFields, SetError, SetFavoritesOnly, SetFilters, SetIncludeOnAggregation, SetPageSize, SetResultsAggregations, SetResultsHits, SetResultsLayout, SetSearch, SetSortBy, SetSpatialFilterEnabled, SimpleSearchField, SiteTitleComponent, SortByComponent, SortByEnum, SortableListComponent, SourceLabelComponent, SourcesService, SpatialExtentComponent, SpinningLoaderComponent, StacItemsResultGridComponent, StacViewComponent, StarToggleComponent, StickyHeaderComponent, SupportedTypes, SwitchToggleComponent, THUMBNAIL_PLACEHOLDER, TRANSLATE_DEBUG_CONFIG, TRANSLATE_DEFAULT_CONFIG, TRANSLATE_WITH_OVERRIDES_CONFIG, TableViewComponent, TextAreaComponent, TextInputComponent, ThemeService, ThumbnailComponent, TranslatedSearchField, TruncatedTextComponent, UPDATE_CONFIG_AGGREGATIONS, UPDATE_FILTERS, UPDATE_REQUEST_AGGREGATION_TERM, UpdateConfigAggregations, UpdateFilters, UrlInputComponent, UserFeedbackItemComponent, UserPreviewComponent, UserSearchField, VECTOR_STYLE_DEFAULT, ViewportIntersectorComponent, WEB_COMPONENT_EMBEDDER_URL, XmlParseError, _reset, allChildrenElement, appConfigWithTranslationFixture, appendChildTree, appendChildren, assertValidXml, associationTypeValues, bboxToPolygon, blockModelFixture, bytesToMegabytes, canEditRecord, checkFileFormat, clearSelectedFeatures, createChild, createDocument, createElement, createFuzzyFilter, createNestedChild, createNestedElement, createSpatialExtentLayer, currentPage, defaultMapStyleFixture, defaultMapStyleHlFixture, downgradeImage, downsizeImage, draftSaveSuccess, dragPanCondition, dropEmptyTranslations, editorReducer, emptyBlockModelFixture, findChildElement, findChildOrCreate, findChildrenElement, findConverterForDocument, findNestedChildOrCreate, findNestedElement, findNestedElements, findParent, firstChildElement, formatDate, formatUserInfo, getAddressLines, getAllKeysValidator, getArrayItem, getAsArray, getAsUrl, getBadgeColor, getCustomTranslations, getError, getFavoritesOnly, getFileFormat, getFileFormatFromServiceOutput, getFirstValue, getFormatPriority, getGeometryBoundingBox, getGeometryFromGeoJSON, getGlobalConfig, getIndividualDisplayName, getIsMobile, getJsonDataItemsProxy, getKeywordHierarchyPath, getLayers, getLinkId, getLinkLabel, getLinkPriority, getMapContext, getMapContextLayerFromConfig, getMapState, getMetadataQualityConfig, getMimeTypeForFormat, getNamespace, getOptionalEditorConfig, getOptionalMapConfig, getOptionalSearchConfig, getPageSize, getQualityValidators, getResourceType, getReusePresentationForm, getReuseType, getRootElement, getSearchConfigAggregations, getSearchFilters, getSearchResults, getSearchResultsAggregations, getSearchResultsHits, getSearchResultsLayout, getSearchResultsLoading, getSearchSortBy, getSearchState, getSearchStateSearch, getSelectedFeatures, getSpatialFilterEnabled, getTemporalRangeUnion, getThemeConfig, handleScrollOnNavigation, hasRecordChangedSinceDraft, hasRecordChangedSinceDraftSuccess, initSearch, initialEditorState, initialMapState, initialState, isConfigLoaded, isDateRange, isFormatInQueryParam, isPublished, itemModelFixture, kindToCodeListValue, loadAppConfig, malformedConfigFixture, mapConfigFixture, mapContact, mapKeywords, mapLogo, mapOrganization, mapReducer, markRecordAsChanged, matchesNoApplicableConstraint, matchesNoKnownConstraint, megabytesToBytes, mimeTypeToFormat, minimalAppConfigFixture, missingMandatoryConfigFixture, mouseWheelZoomCondition, noDuplicateFileName, okAppConfigFixture, openDataset, openRecord, organizationsServiceFactory, parse, parseXmlString, placeholder, prioritizePageScroll, propagateToDocumentOnly, provideGn4, provideI18n, provideRepositoryUrl, readAttribute, readDataset, readDatasetHeaders, readText, reducer$2 as reducer, reducerSearch, removeAllChildren, removeChildren, removeChildrenByName, removeSearchParams, removeWhitespace, renameElements, saveRecord, saveRecordFailure, saveRecordSuccess, selectCanEditRecord, selectCurrentPage, selectEditorConfig, selectEditorState, selectFallback, selectFallbackFields, selectField, selectHasRecordChanged, selectIsPublished, selectRecord, selectRecordChangedSinceSave, selectRecordSaveError, selectRecordSaving, selectRecordSections, selectRecordSource, selectTranslatedField, selectTranslatedValue, setContext, setCurrentPage, setEditorConfiguration, setFieldVisibility, setFocusedField, setSelectedFeatures, setTextContent, sortByFromString, sortByToString, sortByToStrings, spatialExtentToGeometry, spatialExtentsToFeatureCollection, stripHtml, stripNamespace, toDate, toIndividual, toLang2, toLang3, totalPages, undoRecordDraft, unrecognizedKeysConfigFixture, updateFrequencyCodeValues, updateLanguages, updateRecordField, updateRecordLanguages, wmsLayerFlatten, writeAttribute, wrongLanguageCodeConfigFixture, xmlToString };
40865
+ export { ADD_RESULTS, ADD_SEARCH, AVAILABLE_LICENSES, AbstractAction, AbstractSearchField, ActionMenuComponent, AddLayerFromCatalogComponent, AddLayerRecordPreviewComponent, AddResults, AddSearch, AnchorLinkDirective, ApiCardComponent, ApplicationBannerComponent, AuthService, AutocompleteComponent, AutofocusDirective, AvailableServicesField, AvatarComponent, AvatarServiceInterface, BASEMAP_LAYERS, BadgeComponent, BaseConverter, BaseFileReader, BaseReader, BlockListComponent, BoundingBoxSearchField, ButtonComponent, CHART_TYPE_VALUES, CLEAR_ERROR, CLEAR_RESULTS, CarouselComponent, CatalogTitleComponent, CellPopinComponent, ChartComponent, ChartViewComponent, CheckToggleComponent, CheckboxComponent, ClearError, ClearResults, ColorScaleComponent, ConfirmationDialogComponent, ContactDetailsComponent, ContactDetailsFormComponent, ContactPillComponent, ContentGhostComponent, CopyTextButtonComponent, DEFAULT_BASEMAP_LAYER, DEFAULT_CONFIGURATION, DEFAULT_GEOCODING_PROVIDER, DEFAULT_GN4_LOGIN_URL, DEFAULT_GN4_LOGOUT_URL, DEFAULT_GN4_SETTINGS_URL, DEFAULT_LANG, DEFAULT_PAGE_SIZE, DEFAULT_RECORD_CONVERTER, DEFAULT_RESULTS_LAYOUT_CONFIG, DEFAULT_SEARCH_KEY, DEFAULT_SPATIAL_EXTENT_STYLE, DISABLE_AUTH, DISABLE_DRAFT, DO_NOT_USE_DEFAULT_BASEMAP, DataService, DataTableComponent, DataViewComponent, DataViewPermalinkComponent, DataViewShareComponent, DataViewWebComponentComponent, DatePickerComponent, DateRangeDropdownComponent, DateRangeInputsComponent, DateRangePickerComponent, DateRangeSearchField, DateService, DcatApConverter, DefaultRouterModule, DownloadItemComponent, DownloadsListComponent, DragAndDropFileInputComponent, DropdownMultiselectComponent, DropdownSelectorComponent, EDITOR_FEATURE_KEY, ES_QUERY_FIELDS_PRIORITY, ES_RESOURCES_VALUES, ES_SOURCE_SUMMARY, EXTERNAL_VIEWER_OPEN_NEW_TAB, EXTERNAL_VIEWER_URL_TEMPLATE, EditableLabelDirective, EditorFacade, EditorService, ElasticsearchService, ErrorComponent, ErrorType, ExpandablePanelButtonComponent, ExpandablePanelComponent, ExternalLinkCardComponent, ExternalViewerButtonComponent, FIELDS_BRIEF, FIELDS_SUMMARY, FILTER_GEOMETRY, FILTER_SUMMARY_IGNORE_LIST, FORMATS, FacetBlockComponent, FacetItemComponent, FacetListComponent, FacetsContainerComponent, FavoriteStarComponent, FavoritesService, FeatureCatalogListComponent, FeatureDetailComponent, FeatureEditorModule, FeatureMapModule, FeatureRecordModule, FeatureSearchModule, FetchError, FieldFocusDirective, FieldsService, FigureComponent, FigureContainerComponent, FileInputComponent, FileTranslateLoader, FilesDropDirective, FilterDropdownComponent, FormFieldArrayComponent, FormFieldComponent, FormFieldDateComponent, FormFieldFileComponent, FormFieldKeywordsComponent, FormFieldLicenseComponent, FormFieldObjectComponent, FormFieldRichComponent, FormFieldSimpleComponent, FormFieldSpatialExtentComponent, FormFieldTemporalExtentsComponent, FormFieldTopicsComponent, FormFieldWrapperComponent, FullTextSearchField, FuzzySearchComponent, GEOCODING_PROVIDER, GEONETWORK_UI_TAG_NAME, GEONETWORK_UI_VERSION, GeoDataBadgeComponent, GeoTableViewComponent, GeocodingService, GeojsonReader, Gn4Converter, Gn4PlatformMapper, Gn4PlatformService, Gn4Repository, Gn4SettingsService, GnUiHumanizeDateDirective, GpfApiDlComponent, GravatarService, I18nInterceptor, ISO_TOPICS, ImageFallbackDirective, ImageInputComponent, ImageOverlayPreviewComponent, ImportRecordComponent, InlineFilterComponent, InteractiveTableColumnComponent, InteractiveTableComponent, InternalLinkCardComponent, IsSpatialSearchField, Iso191153Converter, Iso19139Converter, KeywordBadgeComponent, KindBadgeComponent, LANGUAGES_LIST, LANGUAGE_NAMES, LANGUAGE_STORAGE_KEY, LANG_2_TO_3_MAPPER, LOGIN_URL, LOGOUT_URL, LONLAT_CRS_CODES, LanguageSwitcherComponent, LayersPanelComponent, LicenseSearchField, LinkClassifierService, LinkUsage, LoadingMaskComponent, LocationSearchComponent, LogService, MAP_FEATURE_KEY, MAP_VIEW_CONSTRAINTS, MAX_UPLOAD_SIZE_MB, METADATA_LANGUAGE, MapContainerComponent, MapFacade, MapLegendComponent, MapStateContainerComponent, MapUtilsService, MapViewComponent, MarkdownEditorComponent, MarkdownParserComponent, MaxLinesComponent, mdview_actions as MdViewActions, MdViewFacade, MetadataCatalogComponent, MetadataContactComponent, MetadataDoiComponent, MetadataInfoComponent, MetadataLinkType, MetadataMapperContext, MetadataQualityComponent, MetadataQualityItemComponent, MetadataQualityPanelComponent, ModalDialogComponent, MultilingualPanelComponent, MultilingualSearchField, MyOrgService, NAMESPACES, NOT_APPLICABLE_CONSTRAINT, NOT_KNOWN_CONSTRAINT, NotificationComponent, NotificationsContainerComponent, NotificationsService, OPEN_DATA_LICENSE, ORGANIZATIONS_STRATEGY, ORGANIZATION_PAGE_URL_TOKEN, ORGANIZATION_URL_TOKEN, OnlineResourceCardComponent, OnlineServiceResourceInputComponent, OrganisationPreviewComponent, OrganisationsComponent, OrganisationsFilterComponent, OrganisationsResultComponent, OrganizationSearchField, OrganizationsFromGroupsService, OrganizationsFromMetadataService, OrganizationsServiceInterface, OwnerSearchField, PAGINATE, PARSE_DELIMITER, PATCH_RESULTS_AGGREGATIONS, PROXY_PATH, Paginate, PaginationButtonsComponent, PaginationComponent, PaginationDotsComponent, PatchResultsAggregations, PlatformServiceInterface, PopoverComponent, PopupAlertComponent, PossibleResourceTypes, PossibleResourceTypesDefinition, PreviousNextButtonsComponent, ProgressBarComponent, ProxyService, QUERY_FIELDS, RECORD_DATASET_URL_TOKEN, RECORD_REUSE_URL_TOKEN, RECORD_SERVICE_URL_TOKEN, REQUEST_MORE_ON_AGGREGATION, REQUEST_MORE_RESULTS, REQUEST_NEW_RESULTS, RESULTS_LAYOUT_CONFIG, REUSE_LIGHT_CONFIGURATION, ROUTER_CONFIG, ROUTER_ROUTE_DATASET, ROUTER_ROUTE_ORGANIZATION, ROUTER_ROUTE_REUSE, ROUTER_ROUTE_SEARCH, ROUTER_ROUTE_SERVICE, ROUTER_STATE_KEY, ROUTE_PARAMS, RecordApiFormComponent, RecordFormComponent, RecordKindField, RecordMetaComponent, RecordMetricComponent, RecordPreviewCardComponent, RecordPreviewComponent, RecordPreviewFeedComponent, RecordPreviewListComponent, RecordPreviewRowComponent, RecordPreviewTextComponent, RecordPreviewTitleComponent, RecordStatusValues, RecordsMetricsComponent, RecordsRepositoryInterface, RecordsService, RequestMoreOnAggregation, RequestMoreResults, RequestNewResults, ResourceCreationRevisionDateSearchField, ResourceTypeLegacyField, ResultsHitsContainerComponent, ResultsHitsNumberComponent, ResultsHitsSearchKindComponent, ResultsLayoutComponent, ResultsLayoutConfigItem, ResultsListComponent, ResultsListContainerComponent, ResultsListItemComponent, ResultsTableComponent, ResultsTableContainerComponent, ReusePresentationForms, RoleLabels, RoleValues, RouterEffects, RouterFacade, RouterService, SEARCH_FEATURE_KEY, SETTINGS_URL, SET_CONFIG_AGGREGATIONS, SET_CONFIG_FILTERS, SET_CONFIG_REQUEST_FIELDS, SET_ERROR, SET_FAVORITES_ONLY, SET_FILTERS, SET_INCLUDE_ON_AGGREGATION, SET_PAGE_SIZE, SET_RESULTS_AGGREGATIONS, SET_RESULTS_HITS, SET_RESULTS_LAYOUT, SET_SEARCH, SET_SORT_BY, SET_SPATIAL_FILTER_ENABLED, SPATIAL_EXTENT_MAX_FILE_SIZE, SPATIAL_SCOPES, SearchEffects, SearchFacade, SearchFeatureCatalogComponent, SearchFiltersSummaryComponent, SearchFiltersSummaryItemComponent, SearchInputComponent, SearchRouterContainerDirective, SearchService, SearchStateContainerDirective, SelectionService, ServiceCapabilitiesComponent, SetConfigAggregations, SetConfigFilters, SetConfigRequestFields, SetError, SetFavoritesOnly, SetFilters, SetIncludeOnAggregation, SetPageSize, SetResultsAggregations, SetResultsHits, SetResultsLayout, SetSearch, SetSortBy, SetSpatialFilterEnabled, SimpleSearchField, SiteTitleComponent, SortByComponent, SortByEnum, SortableListComponent, SourceLabelComponent, SourcesService, SpatialExtentComponent, SpatialExtentDropdownComponent, SpinningLoaderComponent, StacItemsResultGridComponent, StacViewComponent, StarToggleComponent, StickyHeaderComponent, SupportedTypes, SwitchToggleComponent, THUMBNAIL_PLACEHOLDER, TRANSLATE_DEBUG_CONFIG, TRANSLATE_DEFAULT_CONFIG, TRANSLATE_WITH_OVERRIDES_CONFIG, TableViewComponent, TextAreaComponent, TextInputComponent, ThemeService, ThumbnailComponent, TranslatedSearchField, TruncatedTextComponent, UPDATE_CONFIG_AGGREGATIONS, UPDATE_FILTERS, UPDATE_REQUEST_AGGREGATION_TERM, UpdateConfigAggregations, UpdateFilters, UrlInputComponent, UserFeedbackItemComponent, UserPreviewComponent, UserSearchField, VECTOR_STYLE_DEFAULT, ViewportIntersectorComponent, WEB_COMPONENT_EMBEDDER_URL, XmlParseError, _reset, allChildrenElement, appConfigWithTranslationFixture, appendChildTree, appendChildren, assertValidXml, associationTypeValues, bboxToPolygon, blockModelFixture, bytesToMegabytes, canEditRecord, checkFileFormat, clearSelectedFeatures, createChild, createDocument, createElement, createFuzzyFilter, createNestedChild, createNestedElement, createSpatialExtentLayer, currentPage, downgradeImage, downsizeImage, draftSaveSuccess, dragPanCondition, dropEmptyTranslations, editorReducer, emptyBlockModelFixture, findChildElement, findChildOrCreate, findChildrenElement, findConverterForDocument, findNestedChildOrCreate, findNestedElement, findNestedElements, findParent, firstChildElement, formatDate, formatUserInfo, getAddressLines, getAllKeysValidator, getArrayItem, getAsArray, getAsUrl, getBadgeColor, getCustomTranslations, getError, getFavoritesOnly, getFileFormat, getFileFormatFromServiceOutput, getFirstValue, getFormatPriority, getGeometryBoundingBox, getGeometryFromGeoJSON, getGlobalConfig, getIndividualDisplayName, getIsMobile, getJsonDataItemsProxy, getKeywordHierarchyPath, getLayers, getLinkId, getLinkLabel, getLinkPriority, getMapContext, getMapContextLayerFromConfig, getMapState, getMetadataQualityConfig, getMimeTypeForFormat, getNamespace, getOptionalEditorConfig, getOptionalMapConfig, getOptionalSearchConfig, getPageSize, getQualityValidators, getResourceType, getReusePresentationForm, getReuseType, getRootElement, getSearchConfigAggregations, getSearchFilters, getSearchResults, getSearchResultsAggregations, getSearchResultsHits, getSearchResultsLayout, getSearchResultsLoading, getSearchSortBy, getSearchState, getSearchStateSearch, getSelectedFeatures, getSpatialFilterEnabled, getTemporalRangeUnion, getThemeConfig, handleScrollOnNavigation, hasRecordChangedSinceDraft, hasRecordChangedSinceDraftSuccess, initSearch, initialEditorState, initialMapState, initialState, isBoundingBox, isBoundingBoxWithinWorldExtent, isConfigLoaded, isDateRange, isFileExtensionValid, isFormatInQueryParam, isPublished, itemModelFixture, kindToCodeListValue, loadAppConfig, malformedConfigFixture, mapConfigFixture, mapContact, mapKeywords, mapLogo, mapOrganization, mapReducer, markRecordAsChanged, matchesNoApplicableConstraint, matchesNoKnownConstraint, megabytesToBytes, mimeTypeToFormat, minimalAppConfigFixture, missingMandatoryConfigFixture, mouseWheelZoomCondition, noDuplicateFileName, okAppConfigFixture, openDataset, openRecord, organizationsServiceFactory, parse, parseXmlString, placeholder, prioritizePageScroll, propagateToDocumentOnly, provideGn4, provideI18n, provideLocalizedDateAdapter, provideRepositoryUrl, readAttribute, readDataset, readDatasetHeaders, readFileAsText, readText, reducer$2 as reducer, reducerSearch, removeAllChildren, removeChildren, removeChildrenByName, removeSearchParams, removeWhitespace, renameElements, saveRecord, saveRecordFailure, saveRecordSuccess, selectCanEditRecord, selectCurrentPage, selectEditorConfig, selectEditorState, selectFallback, selectFallbackFields, selectField, selectHasRecordChanged, selectIsPublished, selectRecord, selectRecordChangedSinceSave, selectRecordSaveError, selectRecordSaving, selectRecordSections, selectRecordSource, selectTranslatedField, selectTranslatedValue, setContext, setCurrentPage, setEditorConfiguration, setFieldVisibility, setFocusedField, setSelectedFeatures, setTextContent, sortByFromString, sortByToString, sortByToStrings, spatialExtentToGeometry, spatialExtentsToFeatureCollection, stripHtml, stripNamespace, toDate, toIndividual, toLang2, toLang3, totalPages, undoRecordDraft, unrecognizedKeysConfigFixture, updateFrequencyCodeValues, updateLanguages, updateRecordField, updateRecordLanguages, wmsLayerFlatten, writeAttribute, wrongLanguageCodeConfigFixture, xmlToString };
40509
40866
  //# sourceMappingURL=geonetwork-ui.mjs.map