geonetwork-ui 2.11.0-dev.fb3fea50c → 2.11.0-dev.fb48cfeee

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 (84) hide show
  1. package/fesm2022/geonetwork-ui.mjs +1675 -1322
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +205 -111
  4. package/index.d.ts.map +1 -1
  5. package/package.json +6 -5
  6. package/src/libs/api/metadata-converter/src/lib/dcat-ap/dcat-ap.converter.ts +2 -0
  7. package/src/libs/api/metadata-converter/src/lib/fixtures/mel.records.dataset+featurecatalog.ts +395 -0
  8. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +35 -8
  9. package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +19 -0
  10. package/src/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.ts +17 -6
  11. package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +110 -0
  12. package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +131 -0
  13. package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +9 -0
  14. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +2 -1
  15. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +67 -10
  16. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +83 -35
  17. package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +11 -0
  18. package/src/libs/common/domain/src/lib/model/record/flags.model.ts +1 -0
  19. package/src/libs/common/domain/src/lib/model/record/index.ts +1 -0
  20. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +14 -18
  21. package/src/libs/common/domain/src/lib/model/search/aggregation.model.ts +2 -0
  22. package/src/libs/common/domain/src/lib/model/search/filter.model.ts +12 -2
  23. package/src/libs/common/domain/src/lib/platform.service.interface.ts +1 -0
  24. package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +20 -1
  25. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +42 -62
  26. package/src/libs/feature/dataviz/src/lib/service/data.service.ts +32 -19
  27. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-constraints/form-field-constraints.component.ts +3 -5
  28. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts +2 -2
  29. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-record-flags/form-field-record-flag-component.html +18 -0
  30. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-record-flags/form-field-record-flag-component.ts +86 -0
  31. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +100 -88
  32. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +11 -4
  33. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +18 -4
  34. package/src/libs/feature/editor/src/lib/expressions.ts +67 -0
  35. package/src/libs/feature/editor/src/lib/fields.config.ts +13 -0
  36. package/src/libs/feature/editor/src/lib/models/editor-config.model.ts +10 -4
  37. package/src/libs/feature/map/src/index.ts +0 -2
  38. package/src/libs/feature/map/src/lib/feature-map.module.ts +1 -8
  39. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +8 -3
  40. package/src/libs/feature/record/src/lib/state/mdview.effects.ts +3 -1
  41. package/src/libs/feature/record/src/lib/state/mdview.facade.ts +1 -1
  42. package/src/libs/feature/search/src/index.ts +2 -0
  43. package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +7 -1
  44. package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +26 -4
  45. package/src/libs/feature/search/src/lib/geocoding/geocoding.service.ts +105 -0
  46. package/src/libs/feature/search/src/lib/location-search/location-search.component.html +18 -0
  47. package/src/libs/feature/search/src/lib/location-search/location-search.component.ts +114 -0
  48. package/src/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts +1 -0
  49. package/src/libs/feature/search/src/lib/state/reducer.ts +1 -4
  50. package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +56 -4
  51. package/src/libs/feature/search/src/lib/utils/service/fields.ts +58 -16
  52. package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.html +3 -3
  53. package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.ts +2 -2
  54. package/src/libs/ui/elements/src/lib/service-capabilities/service-capabilities.component.ts +2 -1
  55. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.css +0 -4
  56. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.html +12 -3
  57. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts +11 -3
  58. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +7 -4
  59. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.ts +12 -9
  60. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.model.ts +2 -2
  61. package/src/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.ts +2 -1
  62. package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html +8 -2
  63. package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts +34 -5
  64. package/src/libs/ui/layout/src/lib/block-list/block-list.component.css +1 -0
  65. package/src/libs/util/app-config/src/lib/app-config.ts +34 -0
  66. package/src/libs/util/app-config/src/lib/fixtures.ts +37 -1
  67. package/src/libs/util/app-config/src/lib/model.ts +24 -1
  68. package/src/libs/util/shared/src/lib/links/link-utils.ts +19 -10
  69. package/src/libs/util/shared/src/lib/utils/geojson.ts +16 -0
  70. package/translations/de.json +12 -2
  71. package/translations/en.json +14 -4
  72. package/translations/es.json +12 -2
  73. package/translations/fr.json +19 -9
  74. package/translations/it.json +12 -2
  75. package/translations/nl.json +12 -2
  76. package/translations/pt.json +12 -2
  77. package/translations/sk.json +12 -2
  78. package/src/libs/feature/map/src/lib/geocoding/geocoding.component.html +0 -41
  79. package/src/libs/feature/map/src/lib/geocoding/geocoding.component.ts +0 -112
  80. package/src/libs/feature/map/src/lib/geocoding.service.ts +0 -57
  81. package/src/libs/feature/map/src/lib/style/index.ts +0 -2
  82. package/src/libs/feature/map/src/lib/style/map-style.fixtures.ts +0 -47
  83. package/src/libs/feature/map/src/lib/style/map-style.service.ts +0 -108
  84. /package/src/libs/feature/{map/src/lib/geocoding/geocoding.component.css → editor/src/lib/components/record-form/form-field/form-field-record-flags/form-field-record-flag-component.css} +0 -0
@@ -6,7 +6,7 @@ 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
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';
@@ -14,7 +14,7 @@ import * as i4 from '@ngx-translate/core';
14
14
  import { TranslateLoader, TranslateCompiler, TranslateDefaultParser, TranslateParser, TranslateService, provideTranslateService, TranslateDirective, TranslatePipe, TranslateModule } from '@ngx-translate/core';
15
15
  import { TranslateMessageFormatCompiler } from 'ngx-translate-messageformat-compiler';
16
16
  import { TranslateHttpLoader, TRANSLATE_HTTP_LOADER_CONFIG } from '@ngx-translate/http-loader';
17
- import { map as map$1, shareReplay as shareReplay$1, catchError, tap as tap$1, filter as filter$1, startWith as startWith$1, withLatestFrom, switchMap as switchMap$1, take, mergeMap, delay, debounceTime, distinctUntilChanged, finalize, throttleTime, first as first$1, pairwise as pairwise$1, share, defaultIfEmpty, toArray } from 'rxjs/operators';
17
+ import { map as map$1, shareReplay as shareReplay$1, catchError, tap as tap$1, withLatestFrom, filter as filter$1, startWith as startWith$1, switchMap as switchMap$1, take, mergeMap, delay, debounceTime, distinctUntilChanged, finalize, throttleTime, first as first$1, pairwise as pairwise$1, share, defaultIfEmpty, toArray } from 'rxjs/operators';
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';
@@ -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,11 +36,8 @@ 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, iconoirTrash, iconoirSquareDashed, iconoirImport, iconoirCheckCircle, iconoirReduce, iconoirNavArrowUp, iconoirNavArrowDown, iconoirExpand, iconoirSettings, iconoirDownload, iconoirPlus, iconoirBin, iconoirMediaImageXmark, iconoirMediaImage, iconoirAppWindow, iconoirCode, iconoirDatabase, iconoirAppleWallet, iconoirBank, iconoirList, iconoirTranslate, iconoirLock, iconoirUser, iconoirArrowLeft, iconoirLightBulbOn, iconoirBadgeCheck, iconoirSystemShut, iconoirCircle, 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
42
  import * as i1$7 from '@angular/cdk/overlay';
47
43
  import { ScrollStrategyOptions, OverlayModule, CdkConnectedOverlay, ScrollDispatcher, Overlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
@@ -56,7 +52,7 @@ import { matClose, matContentCopy, matExpandMore, matExpandLess, matSearch, matS
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
- import { MatTooltipModule } from '@angular/material/tooltip';
55
+ import { MatTooltipModule, MatTooltip } from '@angular/material/tooltip';
60
56
  import { MAT_DATE_LOCALE, MAT_DATE_FORMATS, DateAdapter } from '@angular/material/core';
61
57
  import { MAT_DATE_FNS_FORMATS, DateFnsAdapter } from '@angular/material-date-fns-adapter';
62
58
  import { enUS } from 'date-fns/locale/en-US';
@@ -74,6 +70,8 @@ import { MatTabsModule } from '@angular/material/tabs';
74
70
  import * as i2$2 from '@ngrx/effects';
75
71
  import { Actions, createEffect, ofType, EffectsModule } from '@ngrx/effects';
76
72
  import { valid as valid$1 } from 'geojson-validation';
73
+ import { queryGeoplateforme, queryGeonames, queryGeoadmin } from '@geospatial-sdk/geocoding';
74
+ import pointer from 'jsonpointer';
77
75
  import { TemplatePortal } from '@angular/cdk/portal';
78
76
  import { parse as parse$3 } from 'marked';
79
77
  import * as i2$3 from '@angular/cdk/scrolling';
@@ -101,6 +99,7 @@ import { TextFieldModule } from '@angular/cdk/text-field';
101
99
  import * as i2$5 from '@ngrx/router-store';
102
100
  import { getRouterSelectors, routerReducer, StoreRouterConnectingModule, FullRouterStateSerializer } from '@ngrx/router-store';
103
101
  import { navigation } from '@ngrx/router-store/data-persistence';
102
+ import * as TOML from '@ltd/j-toml';
104
103
 
105
104
  class MetadataMapperContext {
106
105
  }
@@ -1139,7 +1138,7 @@ function kindToCodeListValue(record) {
1139
1138
  }
1140
1139
 
1141
1140
  function extractCharacterString() {
1142
- return pipe(fallback(findChildElement('gco:CharacterString', false), findChildElement('gmx:Anchor', false), findChildElement('gmx:MimeFileType', false)), readText());
1141
+ return pipe(fallback(findChildElement('gco:CharacterString', false), findChildElement('gmx:Anchor', false), findChildElement('gmx:MimeFileType', false), findChildElement('gco:LocalName', false)), readText());
1143
1142
  }
1144
1143
  /**
1145
1144
  * Translated values will also be collected in the translations object
@@ -2202,6 +2201,7 @@ class Iso19139Converter extends BaseConverter {
2202
2201
  lineage: readLineage$1,
2203
2202
  sourceRecords: readSourceRecords$1,
2204
2203
  associatedRecords: readAssociatedRecords$1,
2204
+ featureTypeDescriptions: () => undefined,
2205
2205
  onlineResources: readOnlineResources$2,
2206
2206
  temporalExtents: readTemporalExtents,
2207
2207
  spatialExtents: readSpatialExtents$1,
@@ -2241,6 +2241,7 @@ class Iso19139Converter extends BaseConverter {
2241
2241
  lineage: writeLineage$1,
2242
2242
  sourceRecords: writeSourceRecords$1,
2243
2243
  associatedRecords: writeAssociatedRecords$1,
2244
+ featureTypeDescriptions: () => undefined,
2244
2245
  onlineResources: writeOnlineResources$1,
2245
2246
  temporalExtents: writeTemporalExtents,
2246
2247
  spatialExtents: writeSpatialExtents,
@@ -2367,6 +2368,7 @@ class Iso19139Converter extends BaseConverter {
2367
2368
  const sourceRecords = this.readers['sourceRecords'](rootEl, tr);
2368
2369
  const associatedRecords = this.readers['associatedRecords'](rootEl, tr);
2369
2370
  const updateFrequency = this.readers['updateFrequency'](rootEl, tr);
2371
+ const featureTypeDescriptions = this.readers['featureTypeDescriptions'](rootEl, tr);
2370
2372
  return this.afterRecordRead({
2371
2373
  ...this.readBaseRecord(rootEl, tr),
2372
2374
  kind,
@@ -2377,6 +2379,7 @@ class Iso19139Converter extends BaseConverter {
2377
2379
  ...(spatialRepresentation && { spatialRepresentation }),
2378
2380
  temporalExtents,
2379
2381
  updateFrequency,
2382
+ ...(featureTypeDescriptions?.length && { featureTypeDescriptions }),
2380
2383
  translations: tr,
2381
2384
  });
2382
2385
  }
@@ -2466,6 +2469,8 @@ class Iso19139Converter extends BaseConverter {
2466
2469
  this.writers['resourceIdentifiers'](record, rootEl);
2467
2470
  if (record.kind === 'dataset') {
2468
2471
  fieldChanged('status') && this.writers['status'](record, rootEl);
2472
+ fieldChanged('featureTypeDescriptions') &&
2473
+ this.writers['featureTypeDescriptions'](record, rootEl);
2469
2474
  }
2470
2475
  if (record.kind === 'dataset' || record.kind === 'reuse') {
2471
2476
  fieldChanged('updateFrequency') &&
@@ -2633,6 +2638,34 @@ function readDefaultLanguage$1(rootEl) {
2633
2638
  function readOtherLanguages(rootEl) {
2634
2639
  return pipe(findChildrenElement('mdb:otherLocale', false), mapArray(readLocaleElement()), map((languages) => languages.filter((lang) => lang !== null)))(rootEl);
2635
2640
  }
2641
+ function extractCharacterStringOrText() {
2642
+ return fallback(extractCharacterString(), readText());
2643
+ }
2644
+ function extractListedValue() {
2645
+ return pipe(combine(pipe(findChildElement('gfc:code', false), extractCharacterString()), pipe(findChildElement('gfc:label', false), extractCharacterString()), pipe(findChildElement('gfc:definition', false), extractCharacterString())), map(([code, label, description]) => ({
2646
+ value: code ?? '',
2647
+ ...(label && { label }),
2648
+ ...(description && { description }),
2649
+ })));
2650
+ }
2651
+ function extractFeatureAttribute() {
2652
+ return pipe(combine(pipe(findChildElement('gfc:memberName', false), extractCharacterStringOrText()), pipe(findChildElement('gfc:designation', false), extractCharacterString()), pipe(findChildElement('gfc:definition', false), extractCharacterString()), pipe(findChildElement('gfc:code', false), extractCharacterString()), pipe(findNestedElement('gfc:valueType', 'gco:TypeName', 'gco:aName'), extractCharacterString()), pipe(findNestedElements('gfc:listedValue', 'gfc:FC_ListedValue'), mapArray(extractListedValue()), filterArray((value) => !!(value.value || value.label)))), map(([name, label, description, code, type, values]) => ({
2653
+ name: name ?? '',
2654
+ ...(label && { label }),
2655
+ ...(description && { description }),
2656
+ ...(code && { code }),
2657
+ ...(type && { type }),
2658
+ ...(values.length > 0 && { values }),
2659
+ })));
2660
+ }
2661
+ function readFeatureTypeDescriptions(rootEl) {
2662
+ return pipe(findNestedElements('mdb:contentInfo', 'mrc:MD_FeatureCatalogue', 'mrc:featureCatalogue', 'gfc:FC_FeatureCatalogue', 'gfc:featureType', 'gfc:FC_FeatureType'), mapArray(pipe(combine(pipe(findChildElement('gfc:typeName', false), extractCharacterStringOrText()), pipe(findChildElement('gfc:designation', false), extractCharacterString()), pipe(findChildElement('gfc:definition', false), extractCharacterString()), pipe(findNestedElements('gfc:carrierOfCharacteristics', 'gfc:FC_FeatureAttribute'), mapArray(extractFeatureAttribute()))), map(([name, label, description, attributes]) => ({
2663
+ name: name ?? '',
2664
+ ...(label && { label }),
2665
+ ...(description && { description }),
2666
+ attributes,
2667
+ })))))(rootEl);
2668
+ }
2636
2669
 
2637
2670
  function writeUniqueIdentifier$1(record, rootEl) {
2638
2671
  pipe(findNestedChildOrCreate('mdb:metadataIdentifier', 'mcc:MD_Identifier', 'mcc:code'), writeCharacterString(record.uniqueIdentifier))(rootEl);
@@ -2787,6 +2820,52 @@ function writeAssociatedRecords(record, rootEl) {
2787
2820
  .filter((assoc) => assoc.uniqueIdentifier && assoc.associationType)
2788
2821
  .map((assoc) => pipe(createNestedElement('mri:associatedResource', 'mri:MD_AssociatedResource'), appendChildren(pipe(createNestedElement('mri:associationType', 'mri:DS_AssociationTypeCode'), writeAttribute('codeList', 'http://standards.iso.org/iso/19115/resources/Codelists/cat/codelists.xml#DS_AssociationTypeCode'), writeAttribute('codeListValue', assoc.associationType)), pipe(createElement('mri:metadataReference'), writeAttribute('uuidref', assoc.uniqueIdentifier)))))))(rootEl);
2789
2822
  }
2823
+ function createNilElement(name, nilReason) {
2824
+ return pipe(createElement(name), writeAttribute('gco:nilReason', nilReason));
2825
+ }
2826
+ function createFeatureCatalogueShell() {
2827
+ return pipe(createNestedElement('mdb:contentInfo', 'mrc:MD_FeatureCatalogue', 'mrc:featureCatalogue', 'gfc:FC_FeatureCatalogue'),
2828
+ // create mandatory elements which are not part of the model yet
2829
+ appendChildren(createNilElement('cat:name', 'missing'), createNilElement('cat:scope', 'missing'), createNilElement('cat:versionNumber', 'missing'), createNilElement('cat:versionDate', 'missing'), createNilElement('gfc:producer', 'missing')));
2830
+ }
2831
+ function createListedValue(value) {
2832
+ return pipe(createNestedElement('gfc:listedValue', 'gfc:FC_ListedValue'), appendChildren(value.label
2833
+ ? pipe(createElement('gfc:label'), writeCharacterString(value.label))
2834
+ : createNilElement('gfc:label', 'missing'), value.value
2835
+ ? pipe(createElement('gfc:code'), writeCharacterString(value.value))
2836
+ : null, value.description
2837
+ ? pipe(createElement('gfc:definition'), writeCharacterString(value.description))
2838
+ : null));
2839
+ }
2840
+ function createFeatureAttribute(attribute) {
2841
+ return pipe(createNestedElement('gfc:carrierOfCharacteristics', 'gfc:FC_FeatureAttribute'), appendChildren(pipe(createElement('gfc:memberName'), setTextContent(attribute.name)), attribute.description
2842
+ ? pipe(createElement('gfc:definition'), writeCharacterString(attribute.description))
2843
+ : null, createNilElement('gfc:cardinality', 'missing'), attribute.label
2844
+ ? pipe(createElement('gfc:designation'), writeCharacterString(attribute.label))
2845
+ : null, attribute.code
2846
+ ? pipe(createElement('gfc:code'), writeCharacterString(attribute.code))
2847
+ : null, attribute.type
2848
+ ? pipe(createNestedElement('gfc:valueType', 'gco:TypeName', 'gco:aName'), writeCharacterString(attribute.type))
2849
+ : null, ...(attribute.values ?? [])
2850
+ .filter((value) => value.value || value.label)
2851
+ .map(createListedValue)));
2852
+ }
2853
+ function createFeatureType(featureType) {
2854
+ return pipe(createNestedElement('gfc:featureType', 'gfc:FC_FeatureType'), appendChildren(pipe(createElement('gfc:typeName'), setTextContent(featureType.name)), featureType.description
2855
+ ? pipe(createElement('gfc:definition'), writeCharacterString(featureType.description))
2856
+ : null, pipe(createNestedElement('gfc:isAbstract', 'gco:Boolean'), setTextContent('false')), featureType.label
2857
+ ? pipe(createElement('gfc:designation'), writeCharacterString(featureType.label))
2858
+ : null, ...featureType.attributes.map(createFeatureAttribute), createNilElement('gfc:featureCatalogue', 'missing')));
2859
+ }
2860
+ function writeFeatureTypeDescriptions(record, rootEl) {
2861
+ const featureTypes = record.featureTypeDescriptions ?? [];
2862
+ const catalogues = findNestedElements('mdb:contentInfo', 'mrc:MD_FeatureCatalogue', 'mrc:featureCatalogue', 'gfc:FC_FeatureCatalogue')(rootEl);
2863
+ catalogues.forEach((catalogue) => removeChildrenByName('gfc:featureType')(catalogue));
2864
+ if (!featureTypes.length)
2865
+ return;
2866
+ const catalogue = catalogues[0] ?? appendChildTree(createFeatureCatalogueShell())(rootEl);
2867
+ appendChildren(...featureTypes.map(createFeatureType))(catalogue);
2868
+ }
2790
2869
 
2791
2870
  class Iso191153Converter extends Iso19139Converter {
2792
2871
  constructor() {
@@ -2803,6 +2882,7 @@ class Iso191153Converter extends Iso19139Converter {
2803
2882
  this.readers['lineage'] = readLineage;
2804
2883
  this.readers['sourceRecords'] = readSourceRecords;
2805
2884
  this.readers['associatedRecords'] = readAssociatedRecords;
2885
+ this.readers['featureTypeDescriptions'] = readFeatureTypeDescriptions;
2806
2886
  this.readers['onlineResources'] = readOnlineResources$1;
2807
2887
  this.readers['defaultLanguage'] = readDefaultLanguage$1;
2808
2888
  this.readers['otherLanguages'] = readOtherLanguages;
@@ -2823,6 +2903,7 @@ class Iso191153Converter extends Iso19139Converter {
2823
2903
  this.writers['lineage'] = writeLineage;
2824
2904
  this.writers['sourceRecords'] = writeSourceRecords;
2825
2905
  this.writers['associatedRecords'] = writeAssociatedRecords;
2906
+ this.writers['featureTypeDescriptions'] = writeFeatureTypeDescriptions;
2826
2907
  this.writers['onlineResources'] = writeOnlineResources;
2827
2908
  this.writers['status'] = writeStatus;
2828
2909
  this.writers['spatialRepresentation'] = writeSpatialRepresentation;
@@ -2912,10 +2993,12 @@ class Iso191153Converter extends Iso19139Converter {
2912
2993
  });
2913
2994
  }
2914
2995
  async writeRecord(record, reference) {
2915
- let result = await super.writeRecord(record, reference);
2916
- // fix gco namespace definition (changes between iso19139 and iso19115-3
2917
- result = result.replace('"http://www.isotc211.org/2005/gco"', '"http://standards.iso.org/iso/19115/-3/gco/1.0"');
2918
- return result;
2996
+ const result = await super.writeRecord(record, reference);
2997
+ // fix the gco and gfc namespace definitions (they change between iso19139 and iso19115-3);
2998
+ // only on the root element, where they are declared, so that nested declarations stay untouched
2999
+ return result.replace(/<mdb:MD_Metadata[^>]*>/, (rootTag) => rootTag
3000
+ .replace('"http://www.isotc211.org/2005/gco"', '"http://standards.iso.org/iso/19115/-3/gco/1.0"')
3001
+ .replace('"http://www.isotc211.org/2005/gfc"', '"http://standards.iso.org/iso/19110/gfc/1.1"'));
2919
3002
  }
2920
3003
  }
2921
3004
 
@@ -3459,6 +3542,7 @@ class DcatApConverter extends BaseConverter {
3459
3542
  lineage: () => '',
3460
3543
  sourceRecords: () => [],
3461
3544
  associatedRecords: () => [],
3545
+ featureTypeDescriptions: () => undefined,
3462
3546
  temporalExtents: () => [],
3463
3547
  spatialRepresentation: () => undefined,
3464
3548
  extras: () => undefined,
@@ -3494,6 +3578,7 @@ class DcatApConverter extends BaseConverter {
3494
3578
  lineage: () => undefined,
3495
3579
  sourceRecords: () => [],
3496
3580
  associatedRecords: () => [],
3581
+ featureTypeDescriptions: () => undefined,
3497
3582
  onlineResources: () => undefined,
3498
3583
  temporalExtents: () => undefined,
3499
3584
  spatialExtents: () => undefined,
@@ -18904,9 +18989,15 @@ class Gn4FieldMapper {
18904
18989
  isOpenData: (output, source) => this.addExtra({
18905
18990
  isOpenData: selectField(source, 'isOpenData') !== 'false',
18906
18991
  }, output),
18907
- featureTypes: (output, source) => this.addExtra({
18908
- featureTypes: selectField(source, 'featureTypes'),
18909
- }, output),
18992
+ featureTypes: (output, source) => {
18993
+ const featureTypes = getAsArray(selectField(source, 'featureTypes'));
18994
+ if (!featureTypes.length)
18995
+ return output;
18996
+ return {
18997
+ ...output,
18998
+ featureTypeDescriptions: this.featureTypesField(featureTypes),
18999
+ };
19000
+ },
18910
19001
  related: (output, source) => {
18911
19002
  const related = selectField(source, 'related');
18912
19003
  const fcatSource = selectField(getFirstValue(selectField(related, 'fcats')) ?? {}, '_source');
@@ -19042,6 +19133,27 @@ class Gn4FieldMapper {
19042
19133
  },
19043
19134
  };
19044
19135
  this.genericField = (output) => output;
19136
+ this.featureTypesField = (featureTypes) => featureTypes.map((featureType) => ({
19137
+ name: featureType.typeName ?? '',
19138
+ ...(featureType.definition && {
19139
+ description: featureType.definition,
19140
+ }),
19141
+ attributes: getAsArray(featureType.attributeTable ?? []).map((attr) => {
19142
+ const values = (attr.values ?? [])
19143
+ .filter((value) => value.code || value.label)
19144
+ .map((value) => ({
19145
+ value: value.code || '',
19146
+ ...(value.label && { label: value.label }),
19147
+ }));
19148
+ return {
19149
+ name: attr.name ?? '',
19150
+ ...(attr.definition && { description: attr.definition }),
19151
+ ...(attr.code && { code: attr.code }),
19152
+ ...(attr.type && { type: attr.type }),
19153
+ ...(values.length > 0 && { values }),
19154
+ };
19155
+ }),
19156
+ }));
19045
19157
  this.constraintField = (type, output, constraintArray) => {
19046
19158
  let outputField;
19047
19159
  switch (type) {
@@ -19596,6 +19708,19 @@ function isBoundingBox(value) {
19596
19708
  value.length === 4 &&
19597
19709
  value.every((item) => typeof item === 'number'));
19598
19710
  }
19711
+ /**
19712
+ * Checks that a bounding box's coordinates stay within the valid
19713
+ * longitude (-180/180) and latitude (-90/90) ranges.
19714
+ */
19715
+ function isBoundingBoxWithinWorldExtent(bbox) {
19716
+ const [minX, minY, maxX, maxY] = bbox;
19717
+ return (minX >= -180 &&
19718
+ maxX <= 180 &&
19719
+ minY >= -90 &&
19720
+ maxY <= 90 &&
19721
+ minX <= maxX &&
19722
+ minY <= maxY);
19723
+ }
19599
19724
  function getGeometryBoundingBox(geometry) {
19600
19725
  // use the bounding box if specified in the GeoJSON object
19601
19726
  if (geometry.bbox) {
@@ -20264,7 +20389,7 @@ function getLinkLabel(link) {
20264
20389
  const label = link.description || ('name' in link ? link.name : '');
20265
20390
  return format ? `${label} (${format})` : label;
20266
20391
  }
20267
- async function getLayers(url, serviceProtocol) {
20392
+ async function getLayers(url, serviceProtocol, deep = false) {
20268
20393
  switch (serviceProtocol) {
20269
20394
  case 'ogcFeatures': {
20270
20395
  const layers = await new OgcApiEndpoint(url).allCollections;
@@ -20272,7 +20397,10 @@ async function getLayers(url, serviceProtocol) {
20272
20397
  }
20273
20398
  case 'wfs': {
20274
20399
  const endpointWfs = await new WfsEndpoint(url).isReady();
20275
- const featureTypes = await endpointWfs.getFeatureTypes();
20400
+ const featureTypes = endpointWfs.getFeatureTypes();
20401
+ if (!deep) {
20402
+ return featureTypes;
20403
+ }
20276
20404
  const layers = (await Promise.allSettled(featureTypes.map((collection) => {
20277
20405
  return endpointWfs.getFeatureTypeFull(collection.name);
20278
20406
  })))
@@ -20282,12 +20410,14 @@ async function getLayers(url, serviceProtocol) {
20282
20410
  }
20283
20411
  case 'wms': {
20284
20412
  const endpointWms = await new WmsEndpoint(url).isReady();
20285
- const layers = (await endpointWms
20413
+ const layersSummary = endpointWms
20286
20414
  .getLayers()
20287
20415
  .flatMap(wmsLayerFlatten)
20288
- .filter((l) => l.name)).map((collection) => {
20289
- return endpointWms.getLayerByName(collection.name);
20290
- });
20416
+ .filter((l) => l.name);
20417
+ if (!deep) {
20418
+ return layersSummary;
20419
+ }
20420
+ const layers = layersSummary.map((collection) => endpointWms.getLayerByName(collection.name));
20291
20421
  return layers;
20292
20422
  }
20293
20423
  case 'wmts': {
@@ -20434,7 +20564,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
20434
20564
  }] } });
20435
20565
 
20436
20566
  var name = "geonetwork-ui";
20437
- var version = "2.11.0-dev.fb3fea50c";
20567
+ var version = "2.11.0-dev.fb48cfeee";
20438
20568
  var engines = {
20439
20569
  node: ">=24"
20440
20570
  };
@@ -20476,10 +20606,10 @@ var dependencies = {
20476
20606
  "@biesbjerg/ngx-translate-extract-marker": "~1.0.0",
20477
20607
  "@camptocamp/ogc-client": "1.3.1-dev.200c02a",
20478
20608
  "@duckdb/duckdb-wasm": "~1.32.0",
20479
- "@geospatial-sdk/core": "0.0.5-dev.78",
20480
- "@geospatial-sdk/geocoding": "0.0.5-dev.78",
20481
- "@geospatial-sdk/legend": "0.0.5-dev.78",
20482
- "@geospatial-sdk/openlayers": "0.0.5-dev.78",
20609
+ "@geospatial-sdk/core": "0.0.5-dev.86.sha-124eae3",
20610
+ "@geospatial-sdk/geocoding": "0.0.5-dev.86.sha-124eae3",
20611
+ "@geospatial-sdk/legend": "0.0.5-dev.86.sha-124eae3",
20612
+ "@geospatial-sdk/openlayers": "0.0.5-dev.86.sha-124eae3",
20483
20613
  "@ltd/j-toml": "~1.38.0",
20484
20614
  "@messageformat/core": "~3.4.0",
20485
20615
  "@ng-icons/core": "~32.5.0",
@@ -20496,6 +20626,7 @@ var dependencies = {
20496
20626
  "embla-carousel": "~8.6.0",
20497
20627
  "flag-icons": "~7.5.0",
20498
20628
  "geojson-validation": "~1.0.2",
20629
+ jsonpointer: "^5.0.1",
20499
20630
  marked: "~17.0.4",
20500
20631
  "ngx-dropzone": "~3.1.0",
20501
20632
  "ngx-translate-messageformat-compiler": "~7.2.0",
@@ -20672,6 +20803,7 @@ class ElasticsearchService {
20672
20803
  // runtime fields are computed using a Painless script
20673
20804
  // see: https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime-mapping-fields.html
20674
20805
  this.runtimeFields = {};
20806
+ this.fieldAliases = {};
20675
20807
  }
20676
20808
  // we're using getters in case the defined languages change over time
20677
20809
  get metadataLang() {
@@ -20743,6 +20875,9 @@ class ElasticsearchService {
20743
20875
  registerRuntimeField(fieldName, expression, type = 'keyword') {
20744
20876
  this.runtimeFields[fieldName] = { script: expression, type };
20745
20877
  }
20878
+ registerFieldAlias(fieldIdentifier, esFieldName) {
20879
+ this.fieldAliases[fieldIdentifier] = esFieldName;
20880
+ }
20746
20881
  getMetadataByIdsPayload(uuids) {
20747
20882
  return {
20748
20883
  query: {
@@ -20864,6 +20999,55 @@ class ElasticsearchService {
20864
20999
  }
20865
21000
  return Object.values(filters).find(isBoundingBox);
20866
21001
  }
21002
+ // builds a query matching records whose dates intersect the filter range
21003
+ buildDateRangeQuery(searchField, dateRange) {
21004
+ // fields ending with 'DateRange' maps as ES range type
21005
+ if (searchField.endsWith('DateRange')) {
21006
+ return {
21007
+ range: {
21008
+ [searchField]: {
21009
+ ...(dateRange.start && { gte: formatDate(dateRange.start) }),
21010
+ ...(dateRange.end && { lte: formatDate(dateRange.end) }),
21011
+ format: 'yyyy-MM-dd',
21012
+ relation: 'intersects',
21013
+ },
21014
+ },
21015
+ };
21016
+ }
21017
+ const minField = `${searchField}Min`;
21018
+ const maxField = `${searchField}Max`;
21019
+ if (minField in this.runtimeFields && maxField in this.runtimeFields) {
21020
+ // min/max runtime fields form an interval per record; match if it intersects the filter range
21021
+ const filter = [
21022
+ dateRange.start && {
21023
+ range: {
21024
+ [maxField]: {
21025
+ gte: formatDate(dateRange.start),
21026
+ format: 'yyyy-MM-dd',
21027
+ },
21028
+ },
21029
+ },
21030
+ dateRange.end && {
21031
+ range: {
21032
+ [minField]: {
21033
+ lte: formatDate(dateRange.end),
21034
+ format: 'yyyy-MM-dd',
21035
+ },
21036
+ },
21037
+ },
21038
+ ].filter(Boolean);
21039
+ return { bool: { filter } };
21040
+ }
21041
+ return {
21042
+ range: {
21043
+ [searchField]: {
21044
+ ...(dateRange.start && { gte: formatDate(dateRange.start) }),
21045
+ ...(dateRange.end && { lte: formatDate(dateRange.end) }),
21046
+ format: 'yyyy-MM-dd',
21047
+ },
21048
+ },
21049
+ };
21050
+ }
20867
21051
  filtersToQuery(filters, spatialFilterExtent = this.findSpatialFilterExtent(filters)) {
20868
21052
  const addQuote = (key) => (/^\/.+\/$/.test(key) ? key : `"${key}"`);
20869
21053
  const makeQuery = (filter) => {
@@ -20888,7 +21072,7 @@ class ElasticsearchService {
20888
21072
  .filter((fieldname) => !Array.isArray(filters[fieldname]))
20889
21073
  .filter((fieldname) => filters[fieldname] &&
20890
21074
  JSON.stringify(filters[fieldname]) !== '{}')
20891
- .map((fieldname) => `${fieldname}:(${makeQuery(filters[fieldname])})`)
21075
+ .map((fieldname) => `${this.fieldAliases[fieldname] ?? fieldname}:(${makeQuery(filters[fieldname])})`)
20892
21076
  .join(' AND ');
20893
21077
  if (filters['gn-ui-crossFieldFilter']) {
20894
21078
  queryString = `${queryString} AND (${filters['gn-ui-crossFieldFilter']})`;
@@ -20900,15 +21084,7 @@ class ElasticsearchService {
20900
21084
  query: queryString,
20901
21085
  },
20902
21086
  },
20903
- ...queryRanges.map(([searchField, dateRange]) => ({
20904
- range: {
20905
- [searchField]: {
20906
- ...(dateRange.start && { gte: formatDate(dateRange.start) }),
20907
- ...(dateRange.end && { lte: formatDate(dateRange.end) }),
20908
- format: 'yyyy-MM-dd',
20909
- },
20910
- },
20911
- })),
21087
+ ...queryRanges.map(([searchField, dateRange]) => this.buildDateRangeQuery(searchField, dateRange)),
20912
21088
  spatialFilterExtent && {
20913
21089
  geo_shape: {
20914
21090
  geom: {
@@ -21194,6 +21370,12 @@ class ElasticsearchService {
21194
21370
  order: {
21195
21371
  [`_${aggregation.sort[1]}`]: aggregation.sort[0],
21196
21372
  },
21373
+ ...(aggregation.includeValues && {
21374
+ include: aggregation.includeValues,
21375
+ }),
21376
+ ...(aggregation.excludeValues && {
21377
+ exclude: aggregation.excludeValues,
21378
+ }),
21197
21379
  },
21198
21380
  };
21199
21381
  case 'histogram':
@@ -21295,10 +21477,21 @@ class Gn4Repository {
21295
21477
  this.platformService = inject(PlatformServiceInterface);
21296
21478
  this.gn4LanguagesApi = inject(LanguagesApiService);
21297
21479
  this.settingsService = inject(Gn4SettingsService);
21480
+ this.gn4TagsApi = inject(TagsApiService);
21298
21481
  this.disableDraft = inject(DISABLE_DRAFT, { optional: true }) ?? false;
21299
21482
  this.defaultConverter = inject(DEFAULT_RECORD_CONVERTER);
21300
21483
  this._draftsChanged = new Subject();
21301
21484
  this.draftsChanged$ = this._draftsChanged.asObservable();
21485
+ /*
21486
+ * FLAGS
21487
+ * Note: in GN4 API, categories are named "tags"
21488
+ */
21489
+ this.categoriesIdentifierForFlags = this.gn4TagsApi.getTags().pipe(map$1((categories) => categories.reduce((acc, category) => {
21490
+ if (category.name.startsWith('geonetwork-ui-flag:')) {
21491
+ acc[category.name.replace(/^geonetwork-ui-flag:/, '')] = category.id;
21492
+ }
21493
+ return acc;
21494
+ }, {})), shareReplay$1(1));
21302
21495
  }
21303
21496
  search({ filters, fields, offset, limit, sort, filterIds, filterGeometry, }) {
21304
21497
  return this.gn4SearchApi
@@ -21328,42 +21521,16 @@ class Gn4Repository {
21328
21521
  ? this.gn4Mapper.readRecords(records)
21329
21522
  : of(null)));
21330
21523
  }
21331
- mapEmbeddedFeatureCatalog(featureTypes) {
21332
- return {
21333
- featureTypes: featureTypes.map((featureType) => ({
21334
- name: featureType.typeName || '',
21335
- definition: featureType.definition || '',
21336
- attributes: Array.isArray(featureType.attributeTable)
21337
- ? featureType.attributeTable.map((attr) => {
21338
- const values = attr.values
21339
- ?.filter((v) => v.code || v.label)
21340
- .map((v) => ({
21341
- code: v.code,
21342
- label: v.label,
21343
- }));
21344
- return {
21345
- name: attr.name,
21346
- code: attr.code,
21347
- definition: attr.definition,
21348
- type: attr.type,
21349
- ...(values?.length > 0 ? { values } : {}),
21350
- };
21351
- })
21352
- : [],
21353
- })),
21354
- };
21355
- }
21356
- getFeatureCatalog(record, visited = new Set() // prevent looping
21524
+ queryLinkedFeatureCatalog(record, visited = new Set() // prevent looping
21357
21525
  ) {
21358
- if (record.extras?.['featureTypes'] &&
21359
- Array.isArray(record.extras['featureTypes']) &&
21360
- record.extras['featureTypes'].length > 0) {
21361
- return of(this.mapEmbeddedFeatureCatalog(record.extras['featureTypes']));
21526
+ if ('featureTypeDescriptions' in record &&
21527
+ record.featureTypeDescriptions?.length > 0) {
21528
+ return of({ featureTypes: record.featureTypeDescriptions });
21362
21529
  }
21363
21530
  const featureCatalogIdentifier = record.extras['featureCatalogIdentifier'];
21364
21531
  if (featureCatalogIdentifier && !visited.has(featureCatalogIdentifier)) {
21365
21532
  visited.add(featureCatalogIdentifier);
21366
- return this.getRecord(featureCatalogIdentifier).pipe(switchMap((record) => record ? this.getFeatureCatalog(record, visited) : of(null)));
21533
+ return this.getRecord(featureCatalogIdentifier).pipe(switchMap((record) => record ? this.queryLinkedFeatureCatalog(record, visited) : of(null)));
21367
21534
  }
21368
21535
  return of(null);
21369
21536
  }
@@ -21626,6 +21793,32 @@ class Gn4Repository {
21626
21793
  removeRecordFromLocalStorage(recordId) {
21627
21794
  window.localStorage.removeItem(this.getLocalStorageKeyForRecord(recordId));
21628
21795
  }
21796
+ getCategoryIdForFlag(flagName) {
21797
+ return this.categoriesIdentifierForFlags.pipe(map$1((categories) => {
21798
+ const categoryId = categories[flagName];
21799
+ if (categoryId === undefined) {
21800
+ console.warn(`The record flag ${flagName} is not created on GeoNetwork backend; please create a category with the name geonetwork-ui-flag:${flagName} for this to work.`);
21801
+ return null;
21802
+ }
21803
+ return categoryId;
21804
+ }));
21805
+ }
21806
+ setRecordFlag(uniqueIdentifier, flagName, value) {
21807
+ return this.getCategoryIdForFlag(flagName).pipe(switchMap((categoryId) => {
21808
+ if (categoryId === null)
21809
+ return of();
21810
+ return value
21811
+ ? this.gn4RecordsApi.tagRecord(uniqueIdentifier, [categoryId], false)
21812
+ : this.gn4RecordsApi.deleteTags(uniqueIdentifier, [categoryId]);
21813
+ }));
21814
+ }
21815
+ getRecordFlag(uniqueIdentifier, flagName) {
21816
+ return this.gn4RecordsApi.getRecordTags(uniqueIdentifier).pipe(withLatestFrom(this.getCategoryIdForFlag(flagName)), map$1(([tags, categoryId]) => {
21817
+ if (categoryId === null)
21818
+ return false;
21819
+ return Array.from(tags).some((tag) => tag.id === categoryId);
21820
+ }));
21821
+ }
21629
21822
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: Gn4Repository, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
21630
21823
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: Gn4Repository }); }
21631
21824
  }
@@ -22241,6 +22434,9 @@ class Gn4PlatformService {
22241
22434
  this.me$ = this.disableAuth
22242
22435
  ? of(null)
22243
22436
  : of(true).pipe(switchMap(() => this.meApi.getMe()), switchMap((apiUser) => this.mapper.userFromMeApi(apiUser)), shareReplay$1({ bufferSize: 1, refCount: true }));
22437
+ this.isAdmin$ = this.disableAuth
22438
+ ? of(false)
22439
+ : of(true).pipe(switchMap(() => this.meApi.getMe()), map$1((apiUser) => !!apiUser?.admin), shareReplay$1({ bufferSize: 1, refCount: true }));
22244
22440
  /**
22245
22441
  * A map of already loaded thesauri (groups of keywords); the key is a URI
22246
22442
  * @private
@@ -22276,6 +22472,9 @@ class Gn4PlatformService {
22276
22472
  isAnonymous() {
22277
22473
  return this.isUserAnonymous$;
22278
22474
  }
22475
+ isAdministrator() {
22476
+ return this.isAdmin$;
22477
+ }
22279
22478
  getOrganizations() {
22280
22479
  return undefined;
22281
22480
  }
@@ -22582,48 +22781,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
22582
22781
  type: Injectable
22583
22782
  }] });
22584
22783
 
22585
- const GEOCODING_PROVIDER = new InjectionToken('geocoding-provider');
22586
- class GeocodingService {
22587
- constructor() {
22588
- this.provider = inject(GEOCODING_PROVIDER);
22589
- }
22590
- query(text) {
22591
- let queryObservable;
22592
- switch (this.provider[0]) {
22593
- case 'geoadmin':
22594
- queryObservable = from(queryGeoadmin(text, this.provider[1]));
22595
- break;
22596
- case 'geonames':
22597
- queryObservable = from(queryGeonames(text, this.provider[1]));
22598
- break;
22599
- case 'data-gouv-fr':
22600
- queryObservable = from(queryDataGouvFr(text, this.provider[1]));
22601
- break;
22602
- default:
22603
- return throwError(() => new Error(`Unsupported geocoding provider: ${this.provider[0]}`));
22604
- }
22605
- return queryObservable.pipe(catchError((error) => throwError(error)));
22606
- }
22607
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
22608
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingService, providedIn: 'root' }); }
22609
- }
22610
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingService, decorators: [{
22611
- type: Injectable,
22612
- args: [{
22613
- providedIn: 'root',
22614
- }]
22615
- }] });
22616
-
22617
22784
  class FeatureMapModule {
22618
22785
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FeatureMapModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
22619
22786
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.19", ngImport: i0, type: FeatureMapModule, imports: [i1.StoreFeatureModule] }); }
22620
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FeatureMapModule, providers: [
22621
- MapFacade,
22622
- {
22623
- provide: GEOCODING_PROVIDER,
22624
- useValue: ['geonames', { maxRows: 5 }],
22625
- },
22626
- ], imports: [StoreModule.forFeature(MAP_FEATURE_KEY, mapReducer)] }); }
22787
+ 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)] }); }
22627
22788
  }
22628
22789
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FeatureMapModule, decorators: [{
22629
22790
  type: NgModule,
@@ -22631,13 +22792,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
22631
22792
  imports: [
22632
22793
  StoreModule.forFeature(MAP_FEATURE_KEY, mapReducer),
22633
22794
  ],
22634
- providers: [
22635
- MapFacade,
22636
- {
22637
- provide: GEOCODING_PROVIDER,
22638
- useValue: ['geonames', { maxRows: 5 }],
22639
- },
22640
- ],
22795
+ providers: [MapFacade],
22641
22796
  }]
22642
22797
  }] });
22643
22798
 
@@ -23080,1082 +23235,374 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
23080
23235
  }]
23081
23236
  }] });
23082
23237
 
23083
- const flatten = (base, obj, keepLevel = false) => Object.keys(obj).reduce((prev, curr) => {
23084
- const path = base ? `${base}.${curr}` : curr;
23085
- const val = obj[curr];
23086
- if (keepLevel) {
23087
- return { ...prev, [path]: flatten('', val) };
23238
+ class AnchorLinkDirective {
23239
+ constructor() {
23240
+ this.changeDetector = inject(ChangeDetectorRef);
23241
+ this.disabled = false;
23242
+ this.observer = new MutationObserver(() => {
23243
+ this.refreshDisabledState();
23244
+ });
23245
+ this.inView = false;
23246
+ this.initialized = false;
23088
23247
  }
23089
- else if (typeof val === 'object' && !Array.isArray(val)) {
23090
- return {
23091
- ...prev,
23092
- ...flatten(path, val),
23093
- };
23248
+ get elementClass() {
23249
+ if (this.disabled) {
23250
+ return this.disabledClass;
23251
+ }
23252
+ if (this.inView) {
23253
+ return `${this.inViewClass} ${this.enabledClass}`;
23254
+ }
23255
+ else {
23256
+ return `${this.outOfViewClass} ${this.enabledClass}`;
23257
+ }
23094
23258
  }
23095
- else {
23096
- return { ...prev, [path]: val };
23259
+ ngOnInit() {
23260
+ this.observer.observe(document.body, {
23261
+ childList: true,
23262
+ subtree: true,
23263
+ });
23264
+ this.refreshDisabledState();
23097
23265
  }
23098
- }, {});
23099
- const checkKeys = (input, mandatory, optional) => {
23100
- const keys = Object.keys(input);
23101
- const missing = mandatory.filter((key) => keys.indexOf(key) === -1);
23102
- const unrecognized = keys.filter((key) => mandatory.indexOf(key) === -1 && optional.indexOf(key) === -1);
23103
- return {
23104
- missing,
23105
- unrecognized,
23106
- };
23107
- };
23108
- /**
23109
- * Only keeps recognized keys, flatten nested values (to allow having "." in keys),
23110
- * pushes warnings and errors to the given arrays; returns null in case of error
23111
- */
23112
- function parseConfigSection(fullConfigObj, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors) {
23113
- if (typeof fullConfigObj[sectionName] !== 'object') {
23114
- if (mandatoryKeys.length === 0)
23115
- return null;
23116
- outErrors.push(`The [${sectionName}] mandatory section is missing.`);
23117
- return null;
23266
+ ngAfterViewChecked() {
23267
+ if (!this.initialized && !this.disabled) {
23268
+ const target = document.getElementById(this.targetId);
23269
+ if (target) {
23270
+ this.initializeIntersectionObserver(target);
23271
+ this.initialized = true;
23272
+ }
23273
+ }
23118
23274
  }
23119
- const sectionConf = fullConfigObj[sectionName];
23120
- const keysCheck = checkKeys(sectionConf, mandatoryKeys, optionalKeys);
23121
- if (keysCheck.missing.length) {
23122
- // note: this is not thrown to allow merging several Errors down the line
23123
- outErrors.push(`In the [${sectionName}] section: ${keysCheck.missing.join(', ')}`);
23124
- return null;
23275
+ initializeIntersectionObserver(target) {
23276
+ this.intersectionObserver = new IntersectionObserver((entries) => {
23277
+ entries.forEach((entry) => {
23278
+ this.inView = entry.isIntersecting;
23279
+ this.changeDetector.detectChanges();
23280
+ });
23281
+ }, {
23282
+ root: null,
23283
+ rootMargin: '-30% 0% -60% 0%',
23284
+ });
23285
+ this.intersectionObserver.observe(target);
23125
23286
  }
23126
- else if (keysCheck.unrecognized.length) {
23127
- outWarnings.push(`In the [${sectionName}] section: ${keysCheck.unrecognized.join(', ')}`);
23128
- keysCheck.unrecognized.forEach((key) => delete sectionConf[key]);
23287
+ ngOnDestroy() {
23288
+ this.observer.disconnect();
23289
+ if (this.intersectionObserver) {
23290
+ this.intersectionObserver.disconnect();
23291
+ }
23129
23292
  }
23130
- return sectionConf;
23293
+ refreshDisabledState() {
23294
+ const targetNotPresent = !document.getElementById(this.targetId);
23295
+ if (targetNotPresent !== this.disabled) {
23296
+ this.disabled = targetNotPresent;
23297
+ this.changeDetector.detectChanges();
23298
+ }
23299
+ }
23300
+ scrollToTarget() {
23301
+ const target = document.getElementById(this.targetId);
23302
+ if (!target)
23303
+ return;
23304
+ target.scrollIntoView({
23305
+ behavior: 'smooth',
23306
+ block: 'start',
23307
+ });
23308
+ }
23309
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AnchorLinkDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
23310
+ 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 }); }
23131
23311
  }
23132
- /**
23133
- * Same as parseConfigSection but keeps all keys, recognized or not
23134
- * Also the first level is the language code (e.g. 'en')
23135
- * This is optional
23136
- */
23137
- function parseTranslationsConfigSection(fullConfigObj, sectionName) {
23138
- if (typeof fullConfigObj[sectionName] !== 'object') {
23139
- return {};
23312
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AnchorLinkDirective, decorators: [{
23313
+ type: Directive,
23314
+ args: [{
23315
+ selector: '[gnUiAnchorLink]',
23316
+ standalone: true,
23317
+ }]
23318
+ }], propDecorators: { targetId: [{
23319
+ type: Input,
23320
+ args: ['gnUiAnchorLink']
23321
+ }], disabledClass: [{
23322
+ type: Input,
23323
+ args: ['gnUiAnchorLinkDisabledClass']
23324
+ }], enabledClass: [{
23325
+ type: Input,
23326
+ args: ['gnUiAnchorLinkEnabledClass']
23327
+ }], inViewClass: [{
23328
+ type: Input,
23329
+ args: ['gnUiAnchorLinkInViewClass']
23330
+ }], outOfViewClass: [{
23331
+ type: Input,
23332
+ args: ['gnUiAnchorLinkOutOfViewClass']
23333
+ }], elementClass: [{
23334
+ type: HostBinding,
23335
+ args: ['class']
23336
+ }], scrollToTarget: [{
23337
+ type: HostListener,
23338
+ args: ['click']
23339
+ }] } });
23340
+
23341
+ class PaginationDotsComponent {
23342
+ constructor() {
23343
+ this.containerClass = '';
23140
23344
  }
23141
- return flatten('', fullConfigObj[sectionName], true);
23345
+ // 1-based
23346
+ get steps() {
23347
+ return Array.from({ length: this.listComponent.pagesCount }, (_, i) => i + 1);
23348
+ }
23349
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PaginationDotsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23350
+ 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: [
23351
+ provideIcons({
23352
+ iconoirNavArrowRight,
23353
+ iconoirNavArrowLeft,
23354
+ }),
23355
+ ] }); }
23142
23356
  }
23143
- /**
23144
- * Same as parseConfigSection but handles an array of configs; this is
23145
- * considered optional; returns null in case of error in at least one of
23146
- * the sections
23147
- */
23148
- function parseMultiConfigSection(fullConfigObj, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors) {
23149
- const sections = fullConfigObj[sectionName];
23150
- if (!sections) {
23151
- return [];
23357
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PaginationDotsComponent, decorators: [{
23358
+ type: Component,
23359
+ args: [{ selector: 'gn-ui-pagination-dots', standalone: true, imports: [CommonModule], viewProviders: [
23360
+ provideIcons({
23361
+ iconoirNavArrowRight,
23362
+ iconoirNavArrowLeft,
23363
+ }),
23364
+ ], 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"] }]
23365
+ }], propDecorators: { listComponent: [{
23366
+ type: Input
23367
+ }], containerClass: [{
23368
+ type: Input
23369
+ }] } });
23370
+
23371
+ class CarouselComponent {
23372
+ constructor() {
23373
+ this.changeDetector = inject(ChangeDetectorRef);
23374
+ this.containerClass = '';
23375
+ this.stepsContainerClass = 'w-full bottom-0 top-auto';
23376
+ this.currentStepChange = new EventEmitter();
23377
+ this.steps = [];
23378
+ this.currentStep = 0;
23379
+ this.refreshSteps = () => {
23380
+ this.steps = this.emblaApi.scrollSnapList();
23381
+ this.currentStep = this.emblaApi.selectedScrollSnap();
23382
+ this.currentStepChange.emit(this.currentStep);
23383
+ this.changeDetector.detectChanges();
23384
+ };
23152
23385
  }
23153
- if (!Array.isArray(sections)) {
23154
- outErrors.push(`Expected [[${sectionName}]] to be an array of sections.`);
23155
- return null;
23386
+ // Paginable API
23387
+ get isFirstPage() {
23388
+ return this.currentStep === 0;
23156
23389
  }
23157
- const result = sections.map((section) => parseConfigSection({ [sectionName]: section }, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors));
23158
- if (result.some((r) => r === null)) {
23159
- return null;
23390
+ get isLastPage() {
23391
+ return this.currentStep === this.steps.length - 1;
23160
23392
  }
23161
- return result;
23162
- }
23163
- function checkMetadataLanguage(parsedConfigSection, outWarnings) {
23164
- const lang2 = toLang2(parsedConfigSection.metadata_language.toLowerCase());
23165
- if (lang2?.length !== 2) {
23166
- outWarnings.push(`In the [global] section: metadata_language = "${parsedConfigSection.metadata_language}" is not a recognized ISO 639 language code`);
23393
+ get currentPage() {
23394
+ return this.currentStep + 1; // this is 1-based
23167
23395
  }
23168
- return parsedConfigSection;
23169
- }
23170
- function checkNewRecordDefaultLanguage(parsedConfigSection, outWarnings) {
23171
- const lang2 = toLang2(parsedConfigSection.new_record_default_language.toLowerCase());
23172
- if (!(lang2 in LANG_2_TO_3_MAPPER)) {
23173
- outWarnings.push(`In the [editing] section: new_record_default_language = "${parsedConfigSection.new_record_default_language}" is not a recognized ISO 639 language code`);
23174
- return {
23175
- ...parsedConfigSection,
23176
- new_record_default_language: undefined,
23177
- };
23396
+ get pagesCount() {
23397
+ return this.steps.length;
23178
23398
  }
23179
- return {
23180
- ...parsedConfigSection,
23181
- new_record_default_language: lang2,
23182
- };
23183
- }
23184
- function checkNewRecordStandard(parsedConfigSection, outWarnings) {
23185
- const standard = parsedConfigSection.new_record_standard;
23186
- const normalizedStandard = typeof standard === 'string' ? standard.trim().toLowerCase() : null;
23187
- if (normalizedStandard === 'iso19139' ||
23188
- normalizedStandard === 'iso19115-3') {
23189
- return {
23190
- ...parsedConfigSection,
23191
- new_record_standard: normalizedStandard,
23192
- };
23399
+ goToPage(stepIndex) {
23400
+ this.emblaApi.scrollTo(stepIndex - 1); // this is 0-based
23193
23401
  }
23194
- outWarnings.push(`In the [editing] section: new_record_standard = "${standard}" is not a supported metadata standard`);
23195
- return {
23196
- ...parsedConfigSection,
23197
- new_record_standard: undefined,
23198
- };
23402
+ goToPrevPage() {
23403
+ if (this.isFirstPage)
23404
+ return;
23405
+ this.emblaApi.scrollPrev();
23406
+ }
23407
+ goToNextPage() {
23408
+ if (this.isLastPage)
23409
+ return;
23410
+ this.emblaApi.scrollNext();
23411
+ }
23412
+ ngAfterViewInit() {
23413
+ this.emblaApi = EmblaCarousel(this.carouselOverflowContainer.nativeElement, {
23414
+ duration: 15,
23415
+ });
23416
+ this.emblaApi
23417
+ .on('init', this.refreshSteps)
23418
+ .on('reInit', this.refreshSteps)
23419
+ .on('select', this.refreshSteps);
23420
+ }
23421
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CarouselComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23422
+ 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 }); }
23199
23423
  }
23424
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CarouselComponent, decorators: [{
23425
+ type: Component,
23426
+ 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"] }]
23427
+ }], propDecorators: { carouselOverflowContainer: [{
23428
+ type: ViewChild,
23429
+ args: ['carouselOverflowContainer']
23430
+ }], containerClass: [{
23431
+ type: Input
23432
+ }], stepsContainerClass: [{
23433
+ type: Input
23434
+ }], currentStepChange: [{
23435
+ type: Output
23436
+ }] } });
23200
23437
 
23201
- /**
23202
- * This loader extends the default one based on JSON files but also loads custom translations
23203
- * defined in the app configuration
23204
- * Implements a fallback on default lang if translated labels are empty
23205
- */
23206
- class FileWithOverridesTranslateLoader extends FileTranslateLoader {
23207
- getTranslation(lang) {
23208
- const baseLang = lang.substring(0, 2); // removing the right part of e.g. en_EN
23209
- return super.getTranslation(baseLang).pipe(map$1((translations) => ({
23210
- ...translations,
23211
- ...getCustomTranslations(baseLang),
23212
- })));
23438
+ class ProgressBarComponent {
23439
+ constructor() {
23440
+ this.value = 0;
23441
+ this.type = 'default';
23442
+ }
23443
+ get progress() {
23444
+ return this.value > 0 ? (this.value < 100 ? this.value : 100) : 0;
23445
+ }
23446
+ get color() {
23447
+ switch (this.type) {
23448
+ case 'default':
23449
+ return {
23450
+ outerBar: 'bg-gray-200',
23451
+ innerBar: 'bg-gray-100',
23452
+ text: 'text-gray-900',
23453
+ };
23454
+ case 'primary':
23455
+ return {
23456
+ outerBar: 'bg-primary',
23457
+ innerBar: 'bg-primary-lighter',
23458
+ text: 'text-white',
23459
+ };
23460
+ case 'secondary':
23461
+ return {
23462
+ outerBar: 'bg-secondary',
23463
+ innerBar: 'bg-secondary-lighter',
23464
+ text: 'text-white',
23465
+ };
23466
+ case 'light':
23467
+ return {
23468
+ outerBar: 'bg-primary-white',
23469
+ innerBar: 'bg-primary',
23470
+ text: 'text-main',
23471
+ };
23472
+ }
23213
23473
  }
23474
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProgressBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23475
+ 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"] }); }
23214
23476
  }
23477
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProgressBarComponent, decorators: [{
23478
+ type: Component,
23479
+ 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"] }]
23480
+ }], propDecorators: { value: [{
23481
+ type: Input
23482
+ }], type: [{
23483
+ type: Input
23484
+ }] } });
23215
23485
 
23216
- const MISSING_CONFIG_ERROR = `Application configuration was not initialized correctly.
23217
- This error might show up in case of an invalid/malformed configuration file.
23218
-
23219
- Note: make sure that you have called \`loadAppConfig\` from '../../../../../libs/util/app-config/src' before starting the Angular application.`;
23220
- let globalConfig = null;
23221
- function getGlobalConfig() {
23222
- if (globalConfig === null)
23223
- throw new Error(MISSING_CONFIG_ERROR);
23224
- return globalConfig;
23225
- }
23226
- let themeConfig = null;
23227
- function getThemeConfig() {
23228
- if (themeConfig === null)
23229
- throw new Error(MISSING_CONFIG_ERROR);
23230
- return themeConfig;
23231
- }
23232
- let mapConfig = null;
23233
- function getOptionalMapConfig() {
23234
- return mapConfig;
23235
- }
23236
- let searchConfig = null;
23237
- function getOptionalSearchConfig() {
23238
- return searchConfig;
23239
- }
23240
- let editorConfig = null;
23241
- function getOptionalEditorConfig() {
23242
- return editorConfig;
23243
- }
23244
- let metadataQualityConfig = null;
23245
- function getMetadataQualityConfig() {
23246
- return (metadataQualityConfig ||
23247
- {
23248
- ENABLED: false,
23249
- });
23250
- }
23251
- let customTranslations = null;
23252
- function getCustomTranslations(langCode) {
23253
- if (customTranslations === null)
23254
- throw new Error(MISSING_CONFIG_ERROR);
23255
- return langCode in customTranslations ? customTranslations[langCode] : {};
23256
- }
23257
- let appConfigLoaded = false;
23258
- function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
23259
- console.log(`[geonetwork-ui] Loading application configuration from ${configUrl}`);
23260
- return fetch(configUrl)
23261
- .then((resp) => {
23262
- if (!resp.ok)
23263
- throw new Error('Configuration file could not be loaded');
23264
- return resp.text();
23265
- })
23266
- .then((conf) => {
23267
- let parsed;
23268
- try {
23269
- parsed = TOML.parse(conf, { joiner: '\n', bigint: false });
23270
- }
23271
- catch (e) {
23272
- throw new Error(`An error occurred when parsing the configuration file: ${e.message}`);
23273
- }
23274
- const errors = [];
23275
- const warnings = [];
23276
- let parsedGlobalSection = parseConfigSection(parsed, 'global', ['geonetwork4_api_url'], [
23277
- 'datahub_url',
23278
- 'edit_url_template',
23279
- 'proxy_path',
23280
- 'metadata_language',
23281
- 'login_url',
23282
- 'logout_url',
23283
- 'settings_url',
23284
- 'web_component_embedder_url',
23285
- 'languages',
23286
- 'contact_email',
23287
- 'reuse_form_url',
23288
- ], warnings, errors);
23289
- if (parsedGlobalSection?.metadata_language) {
23290
- parsedGlobalSection = checkMetadataLanguage(parsedGlobalSection, warnings);
23486
+ class PopoverComponent {
23487
+ constructor() {
23488
+ this.viewContainerRef = inject(ViewContainerRef);
23489
+ this.renderer = inject(Renderer2);
23490
+ }
23491
+ getContent() {
23492
+ if (this.content instanceof TemplateRef) {
23493
+ if (this.view) {
23494
+ this.view.destroy();
23495
+ }
23496
+ this.view = this.viewContainerRef.createEmbeddedView(this.content);
23497
+ this.view.detectChanges();
23498
+ const wrapper = this.renderer.createElement('div'); // Create a wrapper div
23499
+ this.view.rootNodes.forEach((node) => {
23500
+ this.renderer.appendChild(wrapper, node); // Append each root node to the wrapper
23501
+ });
23502
+ return wrapper;
23291
23503
  }
23292
- globalConfig =
23293
- parsedGlobalSection === null
23294
- ? null
23295
- : {
23296
- GN4_API_URL: parsedGlobalSection.geonetwork4_api_url,
23297
- DATAHUB_URL: parsedGlobalSection.datahub_url,
23298
- EDIT_URL_TEMPLATE: parsedGlobalSection.edit_url_template,
23299
- PROXY_PATH: parsedGlobalSection.proxy_path,
23300
- METADATA_LANGUAGE: parsedGlobalSection.metadata_language
23301
- ? parsedGlobalSection.metadata_language.toLowerCase()
23302
- : undefined,
23303
- LOGIN_URL: parsedGlobalSection.login_url,
23304
- LOGOUT_URL: parsedGlobalSection.logout_url,
23305
- SETTINGS_URL: parsedGlobalSection.settings_url,
23306
- WEB_COMPONENT_EMBEDDER_URL: parsedGlobalSection.web_component_embedder_url,
23307
- LANGUAGES: parsedGlobalSection.languages,
23308
- CONTACT_EMAIL: parsedGlobalSection.contact_email,
23309
- REUSE_FORM_URL: parsedGlobalSection.reuse_form_url,
23310
- };
23311
- const parsedLayersSections = parseMultiConfigSection(parsed, 'map_layer', ['type'], ['name', 'url', 'data', 'styleUrl', 'accessToken'], warnings, errors);
23312
- const parsedMapSection = parseConfigSection(parsed, 'map', [], [
23313
- 'max_zoom',
23314
- 'do_not_tile_wms',
23315
- 'max_extent',
23316
- 'max_feature_count',
23317
- 'baselayer',
23318
- 'do_not_use_default_basemap',
23319
- 'external_viewer_url_template',
23320
- 'external_viewer_open_new_tab',
23321
- ], warnings, errors);
23322
- mapConfig =
23323
- parsedMapSection === null || parsedLayersSections === null
23324
- ? null
23325
- : {
23326
- MAX_ZOOM: parsedMapSection.max_zoom,
23327
- DO_NOT_TILE_WMS: parsedMapSection.do_not_tile_wms,
23328
- MAX_EXTENT: parsedMapSection.max_extent,
23329
- MAX_FEATURE_COUNT: parsedMapSection.max_feature_count,
23330
- EXTERNAL_VIEWER_URL_TEMPLATE: parsedMapSection.external_viewer_url_template,
23331
- EXTERNAL_VIEWER_OPEN_NEW_TAB: parsedMapSection.external_viewer_open_new_tab,
23332
- DO_NOT_USE_DEFAULT_BASEMAP: !!parsedMapSection.do_not_use_default_basemap,
23333
- MAP_LAYERS: parsedLayersSections.map((map_layer) => ({
23334
- TYPE: map_layer.type,
23335
- URL: map_layer.url,
23336
- NAME: map_layer.name,
23337
- DATA: map_layer.data,
23338
- STYLE_URL: map_layer.styleUrl,
23339
- ACCESS_TOKEN: map_layer.accessToken,
23340
- })),
23341
- };
23342
- const parsedThemeSection = parseConfigSection(parsed, 'theme', ['primary_color', 'secondary_color', 'main_color', 'background_color'], [
23343
- 'header_foreground_color',
23344
- 'main_font',
23345
- 'title_font',
23346
- 'fonts_stylesheet_url',
23347
- 'thumbnail_placeholder',
23348
- 'header_background',
23349
- 'favicon',
23350
- ], warnings, errors);
23351
- themeConfig =
23352
- parsedThemeSection === null
23353
- ? null
23354
- : {
23355
- PRIMARY_COLOR: parsedThemeSection.primary_color,
23356
- SECONDARY_COLOR: parsedThemeSection.secondary_color,
23357
- MAIN_COLOR: parsedThemeSection.main_color,
23358
- BACKGROUND_COLOR: parsedThemeSection.background_color,
23359
- HEADER_FOREGROUND_COLOR: parsedThemeSection.header_foreground_color,
23360
- THUMBNAIL_PLACEHOLDER: parsedThemeSection.thumbnail_placeholder,
23361
- HEADER_BACKGROUND: parsedThemeSection.header_background,
23362
- TITLE_FONT: parsedThemeSection.title_font,
23363
- MAIN_FONT: parsedThemeSection.main_font,
23364
- FONTS_STYLESHEET_URL: parsedThemeSection.fonts_stylesheet_url,
23365
- FAVICON: parsedThemeSection.favicon,
23366
- };
23367
- const parsedSearchSection = parseConfigSection(parsed, 'search', [], [
23368
- 'record_kind_quick_filter',
23369
- 'filter_geometry_data',
23370
- 'filter_geometry_url',
23371
- 'do_not_use_default_search_preset',
23372
- 'search_preset',
23373
- 'advanced_filters',
23374
- 'limit',
23375
- 'spatial_extent_max_file_size',
23376
- ], warnings, errors);
23377
- const parsedSearchParams = parseMultiConfigSection(parsed, 'search_preset', ['name'], ['sort', 'filters'], warnings, errors);
23378
- searchConfig =
23379
- parsedSearchSection === null
23380
- ? null
23381
- : {
23382
- RECORD_KIND_QUICK_FILTER: parsedSearchSection.record_kind_quick_filter,
23383
- FILTER_GEOMETRY_DATA: parsedSearchSection.filter_geometry_data,
23384
- FILTER_GEOMETRY_URL: parsedSearchSection.filter_geometry_url,
23385
- DO_NOT_USE_DEFAULT_SEARCH_PRESET: !!parsedSearchSection.do_not_use_default_search_preset,
23386
- SEARCH_PRESET: parsedSearchParams.map((param) => ({
23387
- sort: param.sort,
23388
- name: param.name,
23389
- filters: param.filters,
23390
- })),
23391
- ADVANCED_FILTERS: parsedSearchSection.advanced_filters,
23392
- LIMIT: parsedSearchSection.limit,
23393
- SPATIAL_EXTENT_MAX_FILE_SIZE: parsedSearchSection.spatial_extent_max_file_size,
23394
- };
23395
- const parsedMetadataQualitySection = parseConfigSection(parsed, 'metadata-quality', [], ['enabled'], warnings, errors);
23396
- metadataQualityConfig =
23397
- parsedMetadataQualitySection === null
23398
- ? null
23399
- : {
23400
- ENABLED: parsedMetadataQualitySection.enabled,
23401
- SORTABLE: parsedMetadataQualitySection.sortable,
23402
- };
23403
- let parsedEditingSection = parseConfigSection(parsed, 'editing', [], ['new_record_default_language', 'new_record_standard'], warnings, errors);
23404
- if (parsedEditingSection !== null &&
23405
- parsedEditingSection.new_record_default_language !== undefined) {
23406
- parsedEditingSection = checkNewRecordDefaultLanguage(parsedEditingSection, warnings);
23504
+ return this.content;
23505
+ }
23506
+ ngAfterViewInit() {
23507
+ this.tippyInstance = tippy(this.popoverContent.nativeElement, {
23508
+ content: this.getContent(),
23509
+ allowHTML: true,
23510
+ theme: this.theme,
23511
+ });
23512
+ }
23513
+ ngOnChanges(changes) {
23514
+ if (changes['theme']) {
23515
+ this.theme = changes['theme'].currentValue;
23516
+ if (this.tippyInstance) {
23517
+ this.tippyInstance.setProps({ theme: this.theme });
23518
+ }
23407
23519
  }
23408
- if (parsedEditingSection !== null &&
23409
- parsedEditingSection.new_record_standard !== undefined) {
23410
- parsedEditingSection = checkNewRecordStandard(parsedEditingSection, warnings);
23520
+ if (changes['content']) {
23521
+ this.content = changes['content'].currentValue;
23522
+ if (this.tippyInstance) {
23523
+ this.tippyInstance.setContent(this.getContent());
23524
+ }
23411
23525
  }
23412
- editorConfig =
23413
- parsedEditingSection === null
23414
- ? null
23415
- : {
23416
- NEW_RECORD_DEFAULT_LANGUAGE: parsedEditingSection.new_record_default_language,
23417
- NEW_RECORD_STANDARD: parsedEditingSection.new_record_standard,
23418
- };
23419
- customTranslations = parseTranslationsConfigSection(parsed, 'translations');
23420
- if (errors.length) {
23421
- throw new Error(`One or more mandatory settings were missing from the configuration file.
23422
- ${errors.join('\n')}`);
23526
+ }
23527
+ ngOnDestroy() {
23528
+ if (this.tippyInstance) {
23529
+ this.tippyInstance.destroy();
23423
23530
  }
23424
- else if (warnings.length) {
23425
- console.warn(`One or more unexpected settings were encountered in the configuration file.
23426
- ${warnings.join('\n')}`);
23531
+ if (this.view) {
23532
+ this.view.destroy();
23427
23533
  }
23428
- appConfigLoaded = true;
23429
- });
23430
- }
23431
- function isConfigLoaded() {
23432
- return appConfigLoaded;
23433
- }
23434
- function _reset() {
23435
- globalConfig = null;
23436
- themeConfig = null;
23437
- editorConfig = null;
23438
- customTranslations = null;
23534
+ }
23535
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23536
+ 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: [""] }); }
23439
23537
  }
23440
- const TRANSLATE_WITH_OVERRIDES_CONFIG = {
23441
- compiler: {
23442
- provide: TranslateCompiler,
23443
- useClass: TranslateMessageFormatCompiler,
23444
- },
23445
- loader: [
23446
- {
23447
- provide: TRANSLATE_HTTP_LOADER_CONFIG,
23448
- useValue: {
23449
- prefix: './assets/i18n/',
23450
- suffix: '.json',
23451
- },
23452
- },
23453
- {
23454
- provide: TranslateLoader,
23455
- useClass: FileWithOverridesTranslateLoader,
23456
- deps: [HttpClient, TRANSLATE_HTTP_LOADER_CONFIG],
23457
- },
23458
- ],
23459
- };
23460
-
23461
- const okAppConfigFixture = () => `
23462
- [global]
23463
- geonetwork4_api_url = "/geonetwork/srv/api"
23464
- proxy_path = "/proxy/?url="
23465
- metadata_language = "fre"
23466
- login_url = "/cas/login?service="
23467
- logout_url = "/geonetwork/signout"
23468
- settings_url = "/geonetwork/settings"
23469
- web_component_embedder_url = "/datahub/wc-embedder.html"
23470
-
23471
- [map]
23472
- max_zoom = 10
23473
- max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855]
23474
- do_not_use_default_basemap = false
23475
- external_viewer_url_template = 'https://example.com/myviewer?'
23476
- external_viewer_open_new_tab = true
23477
- [[map_layer]]
23478
- type = "wms"
23479
- url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
23480
- name = "masque_hdf_ign_carto_latin1"
23481
- [[map_layer]]
23482
- type = "wfs"
23483
- url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
23484
- name = "masque_hdf_ign_carto_latin1"
23485
-
23486
- [theme]
23487
- primary_color = "#093564"
23488
- secondary_color = "#c2e9dc"
23489
- main_color = "#212029" # All-purpose text color
23490
- background_color = "#fdfbff"
23491
- thumbnail_placeholder = 'assets/img/placeholder.svg'
23492
- header_background = 'teal'
23493
- header_foreground_color = "#872e2e"
23494
- main_font = 'sans-serif'
23495
- title_font = 'serif'
23496
- fonts_stylesheet_url = "https://fonts.googleapis.com/css2?family=Open+Sans"
23497
-
23498
- [search]
23499
- record_kind_quick_filter = false
23500
- filter_geometry_url = 'https://my.domain.org/geom.json'
23501
- do_not_use_default_search_preset = false
23502
- advanced_filters = ['publicationYear', 'documentStandard', 'inspireKeyword', 'topic', 'license']
23503
-
23504
- [[search_preset]]
23505
- sort = "-createDate"
23506
- name = 'filterByOrgs'
23507
- filters.q = 'Carto'
23508
- filters.organisation = ['Org1', 'Org2']
23509
- filters.format = ['ESRI Shapefile']
23510
- filters.topic = ['boundaries']
23511
- filters.publicationYear = ['2023', '2022']
23512
- filters.spatial = ['yes']
23513
-
23514
- [translations.en]
23515
- "my.first.key" = 'First label.'
23516
- "my.second.key" = """
23517
- Second label,
23518
- on two lines."""
23519
-
23520
- [translations.de]
23521
- my.first.key = 'Erste Etikett.'
23522
-
23523
- [translations.fr]
23524
- my.sample.text = "Un bon exemple de texte."
23525
- "my.quoted.text" = 'du texte entre guillements.'
23526
- `;
23527
- const appConfigWithTranslationFixture = () => okAppConfigFixture();
23528
- const minimalAppConfigFixture = () => `
23529
- [global]
23530
- geonetwork4_api_url = "/geonetwork/srv/api"
23531
- proxy_path = "/proxy/?url="
23538
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopoverComponent, decorators: [{
23539
+ type: Component,
23540
+ args: [{ selector: 'gn-ui-popover', standalone: true, imports: [], template: "<span #popoverContent>\n <ng-content></ng-content>\n</span>\n" }]
23541
+ }], propDecorators: { popoverContent: [{
23542
+ type: ViewChild,
23543
+ args: ['popoverContent', { static: false }]
23544
+ }], content: [{
23545
+ type: Input
23546
+ }], theme: [{
23547
+ type: Input
23548
+ }] } });
23532
23549
 
23533
- [theme]
23534
- primary_color = "#093564"
23535
- secondary_color = "#c2e9dc"
23536
- main_color = "#212029" # All-purpose text color
23537
- background_color = "#fdfbff"
23538
- `;
23539
- const mapConfigFixture = () => ({
23540
- MAX_ZOOM: 10,
23541
- MAX_EXTENT: [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855],
23542
- DO_NOT_USE_DEFAULT_BASEMAP: false,
23543
- DO_NOT_TILE_WMS: false,
23544
- 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}"}]}]',
23545
- EXTERNAL_VIEWER_OPEN_NEW_TAB: true,
23546
- MAP_LAYERS: [
23547
- {
23548
- TYPE: 'xyz',
23549
- URL: 'https://some-basemap-server',
23550
- },
23551
- {
23552
- TYPE: 'wms',
23553
- URL: 'https://some-wms-server',
23554
- NAME: 'some_layername',
23555
- },
23556
- {
23557
- TYPE: 'wfs',
23558
- URL: 'https://some-wfs-server',
23559
- NAME: 'some_layername',
23560
- },
23561
- ],
23562
- });
23563
- const malformedConfigFixture = () => `
23564
- {
23565
- "I thought": "we were still doing json"
23550
+ class LoadingMaskComponent {
23551
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LoadingMaskComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23552
+ 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 }); }
23566
23553
  }
23567
- `;
23568
- const missingMandatoryConfigFixture = () => `
23569
- [theme]
23570
- primary_color = "#093564"
23571
- secondary_color = "#c2e9dc"
23572
- background_color = "#fdfbff"
23573
- main_font = 'sans-serif'
23574
- `;
23575
- const wrongLanguageCodeConfigFixture = () => `
23576
- [global]
23577
- geonetwork4_api_url = "/geonetwork/srv/api"
23578
- proxy_path = "/proxy/?url="
23579
- metadata_language = "abc"
23580
-
23581
- [map]
23582
-
23583
- [theme]
23584
- primary_color = "#093564"
23585
- secondary_color = "#c2e9dc"
23586
- main_color = "#212029" # All-purpose text color
23587
- background_color = "#fdfbff"
23588
- `;
23589
- const unrecognizedKeysConfigFixture = () => `
23590
- [global]
23591
- geonetwork4_api_url = "/geonetwork/srv/api"
23592
- proxy_path = "/proxy/?url="
23593
- metadata_language = "fre"
23594
- another_path = '/whatever'
23595
-
23596
- [map]
23597
- max_zoom = 10
23598
- max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855]
23599
- do_not_use_default_basemap = false
23600
- external_viewer_url_template = 'https://example.com/myviewer?'
23601
- external_viewer_open_new_tab = true
23602
- another_zoom = 15
23603
- [[map_layer]]
23604
- type = "wms"
23605
- url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
23606
- name = "masque_hdf_ign_carto_latin1"
23607
- [[map_layer]]
23608
- type = "wfs"
23609
- url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
23610
- name = "masque_hdf_ign_carto_latin1"
23611
- another_layer = "wrong layer definition"
23612
-
23613
- [theme]
23614
- primary_color = "#093564"
23615
- secondary_color = "#c2e9dc"
23616
- main_color = "#212029" # All-purpose text color
23617
- background_color = "#fdfbff"
23618
- thumbnail_placeholder = 'assets/img/placeholder.svg'
23619
- header_background = 'teal'
23620
- header_foreground_color = "#872e2e"
23621
- main_font = 'sans-serif'
23622
- title_font = 'serif'
23623
- another_color = 'red'
23624
- `;
23554
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LoadingMaskComponent, decorators: [{
23555
+ type: Component,
23556
+ 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"] }]
23557
+ }], propDecorators: { message: [{
23558
+ type: Input
23559
+ }] } });
23625
23560
 
23626
- function getMapContextLayerFromConfig(config) {
23627
- switch (config.TYPE) {
23628
- case 'wms':
23629
- return {
23630
- type: 'wms',
23631
- url: config.URL,
23632
- name: config.NAME,
23633
- };
23634
- case 'wfs':
23635
- return {
23636
- type: 'wfs',
23637
- url: config.URL,
23638
- featureType: config.NAME,
23639
- };
23640
- case 'xyz':
23641
- return {
23642
- type: config.TYPE,
23643
- url: config.URL,
23644
- };
23645
- case 'geojson':
23646
- return {
23647
- type: config.TYPE,
23648
- ...(config.DATA ? { data: config.DATA } : { url: config.URL }),
23649
- };
23650
- case 'maplibre-style':
23651
- return {
23652
- type: config.TYPE,
23653
- styleUrl: config.STYLE_URL,
23654
- accessToken: config.ACCESS_TOKEN,
23655
- };
23656
- }
23561
+ class ColorScaleComponent {
23562
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ColorScaleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23563
+ 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: [""] }); }
23657
23564
  }
23565
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ColorScaleComponent, decorators: [{
23566
+ type: Component,
23567
+ 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" }]
23568
+ }] });
23658
23569
 
23659
- class MapStyleService {
23570
+ class PopupAlertComponent {
23660
23571
  constructor() {
23661
- this.styles = {
23662
- default: this.createStyleFunction(this.createGeometryStyles({
23663
- color: isConfigLoaded() ? getThemeConfig().PRIMARY_COLOR : 'blue',
23664
- })),
23665
- defaultHL: this.createStyleFunction(this.createGeometryStyles({
23666
- color: isConfigLoaded() ? getThemeConfig().SECONDARY_COLOR : 'red',
23667
- isFocused: true,
23668
- })),
23669
- };
23572
+ this.changeDetector = inject(ChangeDetectorRef);
23573
+ this.type = 'info';
23574
+ this.position = 'top';
23575
+ this.expanded = false;
23576
+ this.timeout = null;
23670
23577
  }
23671
- createGeometryStyles(options) {
23672
- const { color, isFocused } = options;
23673
- const zIndex = isFocused ? 10 : undefined;
23674
- return {
23675
- polygon: new Style({
23676
- fill: new Fill({
23677
- color: this.computeTransparentFillColor(color),
23678
- }),
23679
- stroke: new Stroke({
23680
- color: 'white',
23681
- width: 2,
23682
- }),
23683
- zIndex,
23684
- }),
23685
- point: new Style({
23686
- image: new CircleStyle({
23687
- fill: new Fill({
23688
- color,
23689
- }),
23690
- stroke: new Stroke({
23691
- color: 'white',
23692
- width: isFocused ? 3 : 2,
23693
- }),
23694
- radius: isFocused ? 8 : 7,
23695
- }),
23696
- zIndex,
23697
- }),
23698
- line: [
23699
- new Style({
23700
- stroke: new Stroke({
23701
- color: 'white',
23702
- width: isFocused ? 8 : 6,
23703
- }),
23704
- zIndex,
23705
- }),
23706
- new Style({
23707
- stroke: new Stroke({
23708
- color,
23709
- width: isFocused ? 3 : 2,
23710
- }),
23711
- zIndex,
23712
- }),
23713
- ],
23714
- };
23578
+ get showDuration() {
23579
+ const chars = this.content.nativeElement.innerHTML.length;
23580
+ return Math.max(3000, chars * 20);
23715
23581
  }
23716
- createStyleFunction(styleByGeometryType) {
23717
- return (feature) => {
23718
- const geometryType = feature?.getGeometry()?.getType();
23719
- switch (geometryType) {
23720
- case 'LinearRing':
23721
- case 'LineString':
23722
- case 'MultiLineString':
23723
- return styleByGeometryType.line;
23724
- case 'Point':
23725
- case 'MultiPoint':
23726
- return styleByGeometryType.point;
23727
- case 'Circle':
23728
- case 'Polygon':
23729
- case 'MultiPolygon':
23730
- return styleByGeometryType.polygon;
23731
- default:
23732
- return styleByGeometryType.point;
23733
- }
23734
- };
23582
+ ngOnInit() {
23583
+ this.expandAndClose();
23584
+ }
23585
+ expand() {
23586
+ this.expanded = true;
23587
+ this.changeDetector.detectChanges();
23588
+ clearTimeout(this.timeout);
23735
23589
  }
23736
- computeTransparentFillColor(color, alpha = 0.25) {
23737
- return chroma(color).alpha(alpha).css();
23590
+ expandAndClose() {
23591
+ this.expanded = true;
23592
+ this.changeDetector.detectChanges();
23593
+ clearTimeout(this.timeout);
23594
+ this.timeout = setTimeout(() => {
23595
+ this.expanded = false;
23596
+ this.changeDetector.detectChanges();
23597
+ }, this.showDuration);
23738
23598
  }
23739
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: MapStyleService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
23740
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: MapStyleService, providedIn: 'root' }); }
23741
- }
23742
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: MapStyleService, decorators: [{
23743
- type: Injectable,
23744
- args: [{
23745
- providedIn: 'root',
23746
- }]
23747
- }] });
23748
-
23749
- const colorPrimary = 'blue';
23750
- const colorSecondary = 'red';
23751
- const colorStroke = 'white';
23752
- const defaultMapStyleFixture = () => new Style({
23753
- image: new Circle({
23754
- fill: new Fill({
23755
- color: colorPrimary,
23756
- }),
23757
- stroke: new Stroke({
23758
- color: colorStroke,
23759
- width: 2,
23760
- }),
23761
- radius: 5,
23762
- }),
23763
- fill: new Fill({
23764
- color: 'rgba(0,0,255,0.25)',
23765
- }),
23766
- stroke: new Stroke({
23767
- color: colorStroke,
23768
- width: 2,
23769
- }),
23770
- });
23771
- const defaultMapStyleHlFixture = () => new Style({
23772
- image: new Circle({
23773
- fill: new Fill({
23774
- color: colorSecondary,
23775
- }),
23776
- stroke: new Stroke({
23777
- color: colorStroke,
23778
- width: 3,
23779
- }),
23780
- radius: 6,
23781
- }),
23782
- fill: new Fill({
23783
- color: 'rgba(255,0,0,0.25)',
23784
- }),
23785
- stroke: new Stroke({
23786
- color: colorSecondary,
23787
- width: 3,
23788
- }),
23789
- });
23790
-
23791
- class AnchorLinkDirective {
23792
- constructor() {
23793
- this.changeDetector = inject(ChangeDetectorRef);
23794
- this.disabled = false;
23795
- this.observer = new MutationObserver(() => {
23796
- this.refreshDisabledState();
23797
- });
23798
- this.inView = false;
23799
- this.initialized = false;
23800
- }
23801
- get elementClass() {
23802
- if (this.disabled) {
23803
- return this.disabledClass;
23804
- }
23805
- if (this.inView) {
23806
- return `${this.inViewClass} ${this.enabledClass}`;
23807
- }
23808
- else {
23809
- return `${this.outOfViewClass} ${this.enabledClass}`;
23810
- }
23811
- }
23812
- ngOnInit() {
23813
- this.observer.observe(document.body, {
23814
- childList: true,
23815
- subtree: true,
23816
- });
23817
- this.refreshDisabledState();
23818
- }
23819
- ngAfterViewChecked() {
23820
- if (!this.initialized && !this.disabled) {
23821
- const target = document.getElementById(this.targetId);
23822
- if (target) {
23823
- this.initializeIntersectionObserver(target);
23824
- this.initialized = true;
23825
- }
23826
- }
23827
- }
23828
- initializeIntersectionObserver(target) {
23829
- this.intersectionObserver = new IntersectionObserver((entries) => {
23830
- entries.forEach((entry) => {
23831
- this.inView = entry.isIntersecting;
23832
- this.changeDetector.detectChanges();
23833
- });
23834
- }, {
23835
- root: null,
23836
- rootMargin: '-30% 0% -60% 0%',
23837
- });
23838
- this.intersectionObserver.observe(target);
23839
- }
23840
- ngOnDestroy() {
23841
- this.observer.disconnect();
23842
- if (this.intersectionObserver) {
23843
- this.intersectionObserver.disconnect();
23844
- }
23845
- }
23846
- refreshDisabledState() {
23847
- const targetNotPresent = !document.getElementById(this.targetId);
23848
- if (targetNotPresent !== this.disabled) {
23849
- this.disabled = targetNotPresent;
23850
- this.changeDetector.detectChanges();
23851
- }
23852
- }
23853
- scrollToTarget() {
23854
- const target = document.getElementById(this.targetId);
23855
- if (!target)
23856
- return;
23857
- target.scrollIntoView({
23858
- behavior: 'smooth',
23859
- block: 'start',
23860
- });
23861
- }
23862
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AnchorLinkDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
23863
- 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 }); }
23864
- }
23865
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AnchorLinkDirective, decorators: [{
23866
- type: Directive,
23867
- args: [{
23868
- selector: '[gnUiAnchorLink]',
23869
- standalone: true,
23870
- }]
23871
- }], propDecorators: { targetId: [{
23872
- type: Input,
23873
- args: ['gnUiAnchorLink']
23874
- }], disabledClass: [{
23875
- type: Input,
23876
- args: ['gnUiAnchorLinkDisabledClass']
23877
- }], enabledClass: [{
23878
- type: Input,
23879
- args: ['gnUiAnchorLinkEnabledClass']
23880
- }], inViewClass: [{
23881
- type: Input,
23882
- args: ['gnUiAnchorLinkInViewClass']
23883
- }], outOfViewClass: [{
23884
- type: Input,
23885
- args: ['gnUiAnchorLinkOutOfViewClass']
23886
- }], elementClass: [{
23887
- type: HostBinding,
23888
- args: ['class']
23889
- }], scrollToTarget: [{
23890
- type: HostListener,
23891
- args: ['click']
23892
- }] } });
23893
-
23894
- class PaginationDotsComponent {
23895
- constructor() {
23896
- this.containerClass = '';
23897
- }
23898
- // 1-based
23899
- get steps() {
23900
- return Array.from({ length: this.listComponent.pagesCount }, (_, i) => i + 1);
23901
- }
23902
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PaginationDotsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23903
- 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: [
23904
- provideIcons({
23905
- iconoirNavArrowRight,
23906
- iconoirNavArrowLeft,
23907
- }),
23908
- ] }); }
23909
- }
23910
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PaginationDotsComponent, decorators: [{
23911
- type: Component,
23912
- args: [{ selector: 'gn-ui-pagination-dots', standalone: true, imports: [CommonModule], viewProviders: [
23913
- provideIcons({
23914
- iconoirNavArrowRight,
23915
- iconoirNavArrowLeft,
23916
- }),
23917
- ], 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"] }]
23918
- }], propDecorators: { listComponent: [{
23919
- type: Input
23920
- }], containerClass: [{
23921
- type: Input
23922
- }] } });
23923
-
23924
- class CarouselComponent {
23925
- constructor() {
23926
- this.changeDetector = inject(ChangeDetectorRef);
23927
- this.containerClass = '';
23928
- this.stepsContainerClass = 'w-full bottom-0 top-auto';
23929
- this.currentStepChange = new EventEmitter();
23930
- this.steps = [];
23931
- this.currentStep = 0;
23932
- this.refreshSteps = () => {
23933
- this.steps = this.emblaApi.scrollSnapList();
23934
- this.currentStep = this.emblaApi.selectedScrollSnap();
23935
- this.currentStepChange.emit(this.currentStep);
23936
- this.changeDetector.detectChanges();
23937
- };
23938
- }
23939
- // Paginable API
23940
- get isFirstPage() {
23941
- return this.currentStep === 0;
23942
- }
23943
- get isLastPage() {
23944
- return this.currentStep === this.steps.length - 1;
23945
- }
23946
- get currentPage() {
23947
- return this.currentStep + 1; // this is 1-based
23948
- }
23949
- get pagesCount() {
23950
- return this.steps.length;
23951
- }
23952
- goToPage(stepIndex) {
23953
- this.emblaApi.scrollTo(stepIndex - 1); // this is 0-based
23954
- }
23955
- goToPrevPage() {
23956
- if (this.isFirstPage)
23957
- return;
23958
- this.emblaApi.scrollPrev();
23959
- }
23960
- goToNextPage() {
23961
- if (this.isLastPage)
23962
- return;
23963
- this.emblaApi.scrollNext();
23964
- }
23965
- ngAfterViewInit() {
23966
- this.emblaApi = EmblaCarousel(this.carouselOverflowContainer.nativeElement, {
23967
- duration: 15,
23968
- });
23969
- this.emblaApi
23970
- .on('init', this.refreshSteps)
23971
- .on('reInit', this.refreshSteps)
23972
- .on('select', this.refreshSteps);
23973
- }
23974
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CarouselComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23975
- 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 }); }
23976
- }
23977
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CarouselComponent, decorators: [{
23978
- type: Component,
23979
- 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"] }]
23980
- }], propDecorators: { carouselOverflowContainer: [{
23981
- type: ViewChild,
23982
- args: ['carouselOverflowContainer']
23983
- }], containerClass: [{
23984
- type: Input
23985
- }], stepsContainerClass: [{
23986
- type: Input
23987
- }], currentStepChange: [{
23988
- type: Output
23989
- }] } });
23990
-
23991
- class ProgressBarComponent {
23992
- constructor() {
23993
- this.value = 0;
23994
- this.type = 'default';
23995
- }
23996
- get progress() {
23997
- return this.value > 0 ? (this.value < 100 ? this.value : 100) : 0;
23998
- }
23999
- get color() {
24000
- switch (this.type) {
24001
- case 'default':
24002
- return {
24003
- outerBar: 'bg-gray-200',
24004
- innerBar: 'bg-gray-100',
24005
- text: 'text-gray-900',
24006
- };
24007
- case 'primary':
24008
- return {
24009
- outerBar: 'bg-primary',
24010
- innerBar: 'bg-primary-lighter',
24011
- text: 'text-white',
24012
- };
24013
- case 'secondary':
24014
- return {
24015
- outerBar: 'bg-secondary',
24016
- innerBar: 'bg-secondary-lighter',
24017
- text: 'text-white',
24018
- };
24019
- case 'light':
24020
- return {
24021
- outerBar: 'bg-primary-white',
24022
- innerBar: 'bg-primary',
24023
- text: 'text-main',
24024
- };
24025
- }
24026
- }
24027
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProgressBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24028
- 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"] }); }
24029
- }
24030
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProgressBarComponent, decorators: [{
24031
- type: Component,
24032
- 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"] }]
24033
- }], propDecorators: { value: [{
24034
- type: Input
24035
- }], type: [{
24036
- type: Input
24037
- }] } });
24038
-
24039
- class PopoverComponent {
24040
- constructor() {
24041
- this.viewContainerRef = inject(ViewContainerRef);
24042
- this.renderer = inject(Renderer2);
24043
- }
24044
- getContent() {
24045
- if (this.content instanceof TemplateRef) {
24046
- if (this.view) {
24047
- this.view.destroy();
24048
- }
24049
- this.view = this.viewContainerRef.createEmbeddedView(this.content);
24050
- this.view.detectChanges();
24051
- const wrapper = this.renderer.createElement('div'); // Create a wrapper div
24052
- this.view.rootNodes.forEach((node) => {
24053
- this.renderer.appendChild(wrapper, node); // Append each root node to the wrapper
24054
- });
24055
- return wrapper;
24056
- }
24057
- return this.content;
24058
- }
24059
- ngAfterViewInit() {
24060
- this.tippyInstance = tippy(this.popoverContent.nativeElement, {
24061
- content: this.getContent(),
24062
- allowHTML: true,
24063
- theme: this.theme,
24064
- });
24065
- }
24066
- ngOnChanges(changes) {
24067
- if (changes['theme']) {
24068
- this.theme = changes['theme'].currentValue;
24069
- if (this.tippyInstance) {
24070
- this.tippyInstance.setProps({ theme: this.theme });
24071
- }
24072
- }
24073
- if (changes['content']) {
24074
- this.content = changes['content'].currentValue;
24075
- if (this.tippyInstance) {
24076
- this.tippyInstance.setContent(this.getContent());
24077
- }
24078
- }
24079
- }
24080
- ngOnDestroy() {
24081
- if (this.tippyInstance) {
24082
- this.tippyInstance.destroy();
24083
- }
24084
- if (this.view) {
24085
- this.view.destroy();
24086
- }
24087
- }
24088
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24089
- 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: [""] }); }
24090
- }
24091
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopoverComponent, decorators: [{
24092
- type: Component,
24093
- args: [{ selector: 'gn-ui-popover', standalone: true, imports: [], template: "<span #popoverContent>\n <ng-content></ng-content>\n</span>\n" }]
24094
- }], propDecorators: { popoverContent: [{
24095
- type: ViewChild,
24096
- args: ['popoverContent', { static: false }]
24097
- }], content: [{
24098
- type: Input
24099
- }], theme: [{
24100
- type: Input
24101
- }] } });
24102
-
24103
- class LoadingMaskComponent {
24104
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LoadingMaskComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24105
- 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 }); }
24106
- }
24107
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LoadingMaskComponent, decorators: [{
24108
- type: Component,
24109
- 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"] }]
24110
- }], propDecorators: { message: [{
24111
- type: Input
24112
- }] } });
24113
-
24114
- class ColorScaleComponent {
24115
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ColorScaleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24116
- 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: [""] }); }
24117
- }
24118
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ColorScaleComponent, decorators: [{
24119
- type: Component,
24120
- 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" }]
24121
- }] });
24122
-
24123
- class PopupAlertComponent {
24124
- constructor() {
24125
- this.changeDetector = inject(ChangeDetectorRef);
24126
- this.type = 'info';
24127
- this.position = 'top';
24128
- this.expanded = false;
24129
- this.timeout = null;
24130
- }
24131
- get showDuration() {
24132
- const chars = this.content.nativeElement.innerHTML.length;
24133
- return Math.max(3000, chars * 20);
24134
- }
24135
- ngOnInit() {
24136
- this.expandAndClose();
24137
- }
24138
- expand() {
24139
- this.expanded = true;
24140
- this.changeDetector.detectChanges();
24141
- clearTimeout(this.timeout);
24142
- }
24143
- expandAndClose() {
24144
- this.expanded = true;
24145
- this.changeDetector.detectChanges();
24146
- clearTimeout(this.timeout);
24147
- this.timeout = setTimeout(() => {
24148
- this.expanded = false;
24149
- this.changeDetector.detectChanges();
24150
- }, this.showDuration);
24151
- }
24152
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopupAlertComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24153
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: PopupAlertComponent, isStandalone: true, selector: "gn-ui-popup-alert", inputs: { icon: "icon", type: "type", position: "position" }, providers: [
24154
- provideIcons({ matErrorOutlineOutline }),
24155
- provideNgIconsConfig({
24156
- size: '1.5em',
24157
- }),
24158
- ], viewQueries: [{ propertyName: "content", first: true, predicate: ["content"], descendants: true }], ngImport: i0, template: "<div class=\"h-full relative container\">\n <div\n class=\"pointer-events-auto absolute text-white flex flex-row p-2 rounded message\"\n role=\"alert\"\n [ngClass]=\"{\n 'position-bottom': position === 'bottom',\n 'position-top': position === 'top',\n expanded: this.expanded,\n 'bg-red-500': type === 'danger',\n 'bg-yellow-500': type === 'warning',\n 'bg-blue-500': type === 'info',\n }\"\n (mouseenter)=\"expand()\"\n (mouseleave)=\"expandAndClose()\"\n >\n <ng-icon class=\"mr-2 shrink-0 select-none\" [name]=\"icon\"></ng-icon>\n <div class=\"grow\" #content [ngClass]=\"{ invisible: !expanded }\">\n <ng-content></ng-content>\n </div>\n </div>\n</div>\n", styles: [":host{pointer-events:none}.container{filter:drop-shadow(0px 4px 3px rgba(0,0,0,.2))}.message{transition:clip-path .3s cubic-bezier(.25,.46,.45,.94)}.message.expanded{clip-path:circle(100%)}.position-top{clip-path:circle(19px at 20px 20px);align-items:start;top:0;left:0}.position-bottom{clip-path:circle(19px at 20px calc(100% - 20px));align-items:end;bottom:0;left:0}.container ::ng-deep a{text-decoration:underline;font-weight:700}.container ::ng-deep a:hover{opacity:.85}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
23599
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopupAlertComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23600
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: PopupAlertComponent, isStandalone: true, selector: "gn-ui-popup-alert", inputs: { icon: "icon", type: "type", position: "position" }, providers: [
23601
+ provideIcons({ matErrorOutlineOutline }),
23602
+ provideNgIconsConfig({
23603
+ size: '1.5em',
23604
+ }),
23605
+ ], viewQueries: [{ propertyName: "content", first: true, predicate: ["content"], descendants: true }], ngImport: i0, template: "<div class=\"h-full relative container\">\n <div\n class=\"pointer-events-auto absolute text-white flex flex-row p-2 rounded message\"\n role=\"alert\"\n [ngClass]=\"{\n 'position-bottom': position === 'bottom',\n 'position-top': position === 'top',\n expanded: this.expanded,\n 'bg-red-500': type === 'danger',\n 'bg-yellow-500': type === 'warning',\n 'bg-blue-500': type === 'info',\n }\"\n (mouseenter)=\"expand()\"\n (mouseleave)=\"expandAndClose()\"\n >\n <ng-icon class=\"mr-2 shrink-0 select-none\" [name]=\"icon\"></ng-icon>\n <div class=\"grow\" #content [ngClass]=\"{ invisible: !expanded }\">\n <ng-content></ng-content>\n </div>\n </div>\n</div>\n", styles: [":host{pointer-events:none}.container{filter:drop-shadow(0px 4px 3px rgba(0,0,0,.2))}.message{transition:clip-path .3s cubic-bezier(.25,.46,.45,.94)}.message.expanded{clip-path:circle(100%)}.position-top{clip-path:circle(19px at 20px 20px);align-items:start;top:0;left:0}.position-bottom{clip-path:circle(19px at 20px calc(100% - 20px));align-items:end;bottom:0;left:0}.container ::ng-deep a{text-decoration:underline;font-weight:700}.container ::ng-deep a:hover{opacity:.85}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
24159
23606
  }
24160
23607
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopupAlertComponent, decorators: [{
24161
23608
  type: Component,
@@ -24304,15 +23751,15 @@ class AutocompleteComponent {
24304
23751
  getExtraClass() {
24305
23752
  if (this.allowSubmit) {
24306
23753
  if (this.enterButton) {
24307
- return 'border rounded-lg absolute w-8 h-8 right-[calc(var(--icon-width)+var(--icon-padding))] inset-y-[--icon-padding]';
23754
+ return 'border rounded-lg absolute w-8 h-8 right-[calc(var(--icon-width)+var(--icon-padding))] top-1/2 -translate-y-1/2';
24308
23755
  }
24309
23756
  else {
24310
- return 'border rounded-lg absolute w-8 h-8 right-[calc(var(--icon-width)+0.25*var(--icon-width))] inset-y-[calc(0.25*var(--icon-width))]';
23757
+ return 'border rounded-lg absolute w-8 h-8 right-[calc(var(--icon-width)+0.25*var(--icon-width))] top-1/2 -translate-y-1/2';
24311
23758
  }
24312
23759
  }
24313
23760
  else {
24314
23761
  if (!this.enterButton) {
24315
- return 'border rounded-lg absolute w-8 h-8 right-2 inset-y-2';
23762
+ return 'border rounded-lg absolute w-8 h-8 right-2 top-1/2 -translate-y-1/2';
24316
23763
  }
24317
23764
  }
24318
23765
  return 'border rounded-lg absolute w-8 h-8';
@@ -24448,7 +23895,7 @@ class AutocompleteComponent {
24448
23895
  this.enterBtnPosition = event.target['value'].length * 8 + 80;
24449
23896
  }
24450
23897
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AutocompleteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24451
- 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: [
23898
+ 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: [
24452
23899
  provideIcons({
24453
23900
  iconoirSearch,
24454
23901
  matClose,
@@ -24457,7 +23904,7 @@ class AutocompleteComponent {
24457
23904
  provideNgIconsConfig({
24458
23905
  size: '1.75rem',
24459
23906
  }),
24460
- ], 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 }); }
23907
+ ], 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 flex items-center justify-center\"\n >\n <ng-icon name=\"iconoirSearch\" class=\"text-primary\"></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}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 }); }
24461
23908
  }
24462
23909
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AutocompleteComponent, decorators: [{
24463
23910
  type: Component,
@@ -24478,7 +23925,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
24478
23925
  provideNgIconsConfig({
24479
23926
  size: '1.75rem',
24480
23927
  }),
24481
- ], 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"] }]
23928
+ ], 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 flex items-center justify-center\"\n >\n <ng-icon name=\"iconoirSearch\" class=\"text-primary\"></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}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"] }]
24482
23929
  }], propDecorators: { placeholder: [{
24483
23930
  type: Input
24484
23931
  }], enterButton: [{
@@ -24516,6 +23963,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
24516
23963
  }], inputRef: [{
24517
23964
  type: ViewChild,
24518
23965
  args: ['searchInput']
23966
+ }], displayWithTemplate: [{
23967
+ type: Input
24519
23968
  }], displayWithFn: [{
24520
23969
  type: Input
24521
23970
  }] } });
@@ -25000,7 +24449,7 @@ class DropdownMultiselectComponent {
25000
24449
  }
25001
24450
  setFocus() {
25002
24451
  setTimeout(() => {
25003
- this.searchFieldInput.nativeElement.focus();
24452
+ this.searchFieldInput?.nativeElement.focus();
25004
24453
  }, 0);
25005
24454
  }
25006
24455
  openOverlay() {
@@ -25106,7 +24555,10 @@ class DropdownMultiselectComponent {
25106
24555
  matExpandMore,
25107
24556
  matExpandLess,
25108
24557
  }),
25109
- ], 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-test=\"dropdown-clear\">\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$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 }); }
24558
+ provideNgIconsConfig({
24559
+ size: '1.5rem',
24560
+ }),
24561
+ ], 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 }); }
25110
24562
  }
25111
24563
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DropdownMultiselectComponent, decorators: [{
25112
24564
  type: Component,
@@ -25116,7 +24568,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
25116
24568
  matExpandMore,
25117
24569
  matExpandLess,
25118
24570
  }),
25119
- ], 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-test=\"dropdown-clear\">\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" }]
24571
+ provideNgIconsConfig({
24572
+ size: '1.5rem',
24573
+ }),
24574
+ ], 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" }]
25120
24575
  }], propDecorators: { title: [{
25121
24576
  type: Input
25122
24577
  }], choices: [{
@@ -25946,7 +25401,8 @@ class SearchFeatureCatalogComponent {
25946
25401
  const filtered = (this.featureCatalog?.featureTypes || []).filter((featureType) => {
25947
25402
  const fuzzyFilter = createFuzzyFilter(searchTerm);
25948
25403
  return (fuzzyFilter(featureType.name) ||
25949
- (featureType.definition && fuzzyFilter(featureType.definition)));
25404
+ (featureType.description &&
25405
+ fuzzyFilter(featureType.description)));
25950
25406
  });
25951
25407
  this.filteredFeatureCatalog = {
25952
25408
  ...this.featureCatalog,
@@ -26031,8 +25487,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
26031
25487
  marker('search.filters.spatialExtent.import');
26032
25488
  marker('search.filters.spatialExtent.helpText');
26033
25489
  marker('search.filters.spatialExtent.error.title');
25490
+ marker('search.filters.spatialExtent.error.outOfBounds');
26034
25491
  const LABEL_FROM_FILE = marker('search.filters.spatialExtent.bboxFromFile');
26035
25492
  const LABEL_FROM_FILE_DELETE = marker('search.filters.spatialExtent.bboxFromFileDelete');
25493
+ const LABEL_FROM_LOCATION = marker('search.filters.spatialExtent.bboxFromLocation');
25494
+ const LABEL_FROM_LOCATION_DELETE = marker('search.filters.spatialExtent.bboxFromLocationDelete');
26036
25495
  const LABEL_INITIAL = marker('search.filters.spatialExtent.bboxInitial');
26037
25496
  const LABEL_INITIAL_DELETE = marker('search.filters.spatialExtent.bboxInitialDelete');
26038
25497
  class SpatialExtentDropdownComponent {
@@ -26044,6 +25503,7 @@ class SpatialExtentDropdownComponent {
26044
25503
  this.errorChange = new EventEmitter();
26045
25504
  this.bbox = null;
26046
25505
  this.fileName = '';
25506
+ this.locationName = '';
26047
25507
  this.overlayPositions = [
26048
25508
  {
26049
25509
  originX: 'start',
@@ -26074,13 +25534,21 @@ class SpatialExtentDropdownComponent {
26074
25534
  return !!this.bbox;
26075
25535
  }
26076
25536
  get selectionLabelKey() {
26077
- return this.fileName ? LABEL_FROM_FILE : LABEL_INITIAL;
25537
+ if (this.fileName)
25538
+ return LABEL_FROM_FILE;
25539
+ if (this.locationName)
25540
+ return LABEL_FROM_LOCATION;
25541
+ return LABEL_INITIAL;
26078
25542
  }
26079
25543
  get selectionDeleteLabelKey() {
26080
- return this.fileName ? LABEL_FROM_FILE_DELETE : LABEL_INITIAL_DELETE;
25544
+ if (this.fileName)
25545
+ return LABEL_FROM_FILE_DELETE;
25546
+ if (this.locationName)
25547
+ return LABEL_FROM_LOCATION_DELETE;
25548
+ return LABEL_INITIAL_DELETE;
26081
25549
  }
26082
25550
  get selectionLabelParams() {
26083
- return { fileName: this.fileName };
25551
+ return { fileName: this.fileName, locationName: this.locationName };
26084
25552
  }
26085
25553
  openOverlay() {
26086
25554
  this.overlayMinWidth =
@@ -26111,8 +25579,13 @@ class SpatialExtentDropdownComponent {
26111
25579
  return;
26112
25580
  }
26113
25581
  const bbox = getGeometryBoundingBox(geometry);
25582
+ if (!isBoundingBoxWithinWorldExtent(bbox)) {
25583
+ this.setError(marker('search.filters.spatialExtent.error.outOfBounds'));
25584
+ return;
25585
+ }
26114
25586
  this.bbox = bbox;
26115
25587
  this.fileName = file.name;
25588
+ this.locationName = '';
26116
25589
  this.bboxChange.emit(bbox);
26117
25590
  this.cd.markForCheck();
26118
25591
  }
@@ -26121,6 +25594,15 @@ class SpatialExtentDropdownComponent {
26121
25594
  return;
26122
25595
  }
26123
25596
  }
25597
+ setLocationBbox(bbox, name) {
25598
+ this.errorKey = null;
25599
+ this.bbox = bbox;
25600
+ this.locationName = name;
25601
+ this.fileName = '';
25602
+ this.fileInput?.clear();
25603
+ this.bboxChange.emit(bbox);
25604
+ this.cd.markForCheck();
25605
+ }
26124
25606
  handleFileError(error) {
26125
25607
  if (error === 'file-too-large') {
26126
25608
  this.setError(marker('search.filters.spatialExtent.error.fileTooLarge'), {
@@ -26139,6 +25621,7 @@ class SpatialExtentDropdownComponent {
26139
25621
  removeSelection(event) {
26140
25622
  this.bbox = null;
26141
25623
  this.fileName = '';
25624
+ this.locationName = '';
26142
25625
  this.errorKey = null;
26143
25626
  this.fileInput?.clear();
26144
25627
  this.bboxChange.emit(null);
@@ -26148,14 +25631,14 @@ class SpatialExtentDropdownComponent {
26148
25631
  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: [
26149
25632
  provideIcons({
26150
25633
  iconoirCheckCircle,
26151
- iconoirImport,
26152
25634
  iconoirSquareDashed,
26153
25635
  iconoirTrash,
25636
+ iconoirUpload,
26154
25637
  matClose,
26155
25638
  matExpandLess,
26156
25639
  matExpandMore,
26157
25640
  }),
26158
- ], 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-[8px]') + ' 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=\"iconoirImport\"\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 }); }
25641
+ ], 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 flex flex-col gap-[8px]'\n \"\n style=\"\n --gn-ui-text-input-rounded: 8px;\n --gn-ui-text-input-border-size: 1px;\n --gn-ui-text-input-padding: 6px;\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 <ng-content></ng-content>\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 }); }
26159
25642
  }
26160
25643
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, decorators: [{
26161
25644
  type: Component,
@@ -26168,14 +25651,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
26168
25651
  ], providers: [
26169
25652
  provideIcons({
26170
25653
  iconoirCheckCircle,
26171
- iconoirImport,
26172
25654
  iconoirSquareDashed,
26173
25655
  iconoirTrash,
25656
+ iconoirUpload,
26174
25657
  matClose,
26175
25658
  matExpandLess,
26176
25659
  matExpandMore,
26177
25660
  }),
26178
- ], 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-[8px]') + ' 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=\"iconoirImport\"\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" }]
25661
+ ], 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 flex flex-col gap-[8px]'\n \"\n style=\"\n --gn-ui-text-input-rounded: 8px;\n --gn-ui-text-input-border-size: 1px;\n --gn-ui-text-input-padding: 6px;\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 <ng-content></ng-content>\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" }]
26179
25662
  }], propDecorators: { title: [{
26180
25663
  type: Input
26181
25664
  }], maxFileSizeMb: [{
@@ -26701,11 +26184,11 @@ class BlockListComponent {
26701
26184
  this.goToPage(this.currentPage + 1);
26702
26185
  }
26703
26186
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: BlockListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
26704
- 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 }); }
26187
+ 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 }); }
26705
26188
  }
26706
26189
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: BlockListComponent, decorators: [{
26707
26190
  type: Component,
26708
- 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"] }]
26191
+ 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"] }]
26709
26192
  }], propDecorators: { containerClass: [{
26710
26193
  type: Input
26711
26194
  }], paginationContainerClass: [{
@@ -27444,9 +26927,7 @@ const initSearch = () => {
27444
26927
  },
27445
26928
  params: {
27446
26929
  filters: {},
27447
- pageSize: getOptionalSearchConfig()?.LIMIT
27448
- ? getOptionalSearchConfig().LIMIT
27449
- : DEFAULT_PAGE_SIZE,
26930
+ pageSize: DEFAULT_PAGE_SIZE,
27450
26931
  currentPage: 0,
27451
26932
  favoritesOnly: false,
27452
26933
  useSpatialFilter: true,
@@ -27895,6 +27376,146 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
27895
27376
  }]
27896
27377
  }] });
27897
27378
 
27379
+ const DEFAULT_GEOCODING_PROVIDER = [
27380
+ 'geonames',
27381
+ { maxRows: 5 },
27382
+ ];
27383
+ const GEOCODING_PROVIDER = new InjectionToken('geocoding-provider', {
27384
+ providedIn: 'root',
27385
+ factory: () => DEFAULT_GEOCODING_PROVIDER,
27386
+ });
27387
+ // these options are arrays in the providers, but they can be configured
27388
+ // with a single value for simplicity
27389
+ const GEOCODING_PROVIDER_ARRAY_OPTIONS = {
27390
+ geoadmin: ['origins', 'features'],
27391
+ geoplateforme: [
27392
+ 'index',
27393
+ 'category',
27394
+ 'type',
27395
+ 'postCode',
27396
+ 'cityCode',
27397
+ 'depCode',
27398
+ ],
27399
+ };
27400
+ function wrapArrayOptions(options, keys) {
27401
+ const wrapped = { ...options };
27402
+ for (const key of keys) {
27403
+ if (typeof wrapped[key] === 'string') {
27404
+ wrapped[key] = [wrapped[key]];
27405
+ }
27406
+ }
27407
+ return wrapped;
27408
+ }
27409
+ class GeocodingService {
27410
+ constructor() {
27411
+ this.provider = inject(GEOCODING_PROVIDER);
27412
+ }
27413
+ query(text) {
27414
+ let queryObservable;
27415
+ switch (this.provider[0]) {
27416
+ case 'geoadmin':
27417
+ queryObservable = from(queryGeoadmin(text, wrapArrayOptions(this.provider[1], GEOCODING_PROVIDER_ARRAY_OPTIONS.geoadmin)));
27418
+ break;
27419
+ case 'geonames':
27420
+ queryObservable = from(queryGeonames(text, this.provider[1]));
27421
+ break;
27422
+ case 'geoplateforme':
27423
+ queryObservable = from(queryGeoplateforme(text, {
27424
+ ...wrapArrayOptions(this.provider[1], GEOCODING_PROVIDER_ARRAY_OPTIONS.geoplateforme),
27425
+ returnTrueGeometry: true,
27426
+ }));
27427
+ break;
27428
+ default:
27429
+ return throwError(() => new Error(`Unsupported geocoding provider: ${this.provider[0]}`));
27430
+ }
27431
+ return queryObservable.pipe(catchError((error) => throwError(error)));
27432
+ }
27433
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
27434
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingService, providedIn: 'root' }); }
27435
+ }
27436
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingService, decorators: [{
27437
+ type: Injectable,
27438
+ args: [{
27439
+ providedIn: 'root',
27440
+ }]
27441
+ }] });
27442
+
27443
+ const GEOCODING_PROVIDER_LABELS = new InjectionToken('geocodingProviderLabels', {
27444
+ factory: () => ({}),
27445
+ });
27446
+ class LocationSearchComponent {
27447
+ constructor() {
27448
+ this.geocodingService = inject(GeocodingService);
27449
+ this.labels = inject(GEOCODING_PROVIDER_LABELS);
27450
+ this.placeholder = '';
27451
+ this.resultSelected = new EventEmitter();
27452
+ this.bboxSelected = new EventEmitter();
27453
+ this.displayWithFn = (item) => item.label;
27454
+ this.searchAction = (text) => this.geocodingService.query(text);
27455
+ }
27456
+ getSecondaryLabel(result) {
27457
+ return this.resolveLabel(result, this.labels.secondary);
27458
+ }
27459
+ getMainLabel(result) {
27460
+ return this.resolveLabel(result, this.labels.main) ?? result.label;
27461
+ }
27462
+ getTertiaryLabel(result) {
27463
+ return this.resolveLabel(result, this.labels.tertiary);
27464
+ }
27465
+ getDisplayLabel(result) {
27466
+ const tertiary = this.getTertiaryLabel(result);
27467
+ return this.getMainLabel(result) + (tertiary ? ', ' + tertiary : '');
27468
+ }
27469
+ resolveLabel(result, path) {
27470
+ if (!path)
27471
+ return undefined;
27472
+ let value;
27473
+ try {
27474
+ value = pointer.get(result, path);
27475
+ }
27476
+ catch {
27477
+ return undefined;
27478
+ }
27479
+ const values = Array.isArray(value) ? value : [value];
27480
+ const label = values
27481
+ .filter((v) => typeof v === 'string' || typeof v === 'number')
27482
+ .join(', ');
27483
+ return label || undefined;
27484
+ }
27485
+ // geoplateforme keeps a simplified geometry in geom and puts the real one,
27486
+ // as a JSON string, in properties.truegeometry when returnTrueGeometry is set
27487
+ getGeometry(result) {
27488
+ const trueGeometry = result.properties?.['truegeometry'];
27489
+ if (typeof trueGeometry === 'string') {
27490
+ return JSON.parse(trueGeometry);
27491
+ }
27492
+ return trueGeometry ?? result.geom;
27493
+ }
27494
+ handleItemSelected(item) {
27495
+ const result = item;
27496
+ this.resultSelected.emit(result);
27497
+ const geometry = this.getGeometry(result);
27498
+ if (geometry) {
27499
+ this.bboxSelected.emit({
27500
+ bbox: getGeometryBoundingBox(geometry),
27501
+ label: this.getDisplayLabel(result),
27502
+ });
27503
+ }
27504
+ }
27505
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LocationSearchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
27506
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: LocationSearchComponent, isStandalone: true, selector: "gn-ui-location-search", inputs: { placeholder: "placeholder" }, outputs: { resultSelected: "resultSelected", bboxSelected: "bboxSelected" }, ngImport: i0, template: "<gn-ui-autocomplete\n [action]=\"searchAction\"\n [displayWithFn]=\"displayWithFn\"\n [placeholder]=\"placeholder\"\n [displayWithTemplate]=\"defaultItemTpl\"\n [clearOnSelection]=\"true\"\n (itemSelected)=\"handleItemSelected($event)\"\n>\n</gn-ui-autocomplete>\n\n<ng-template #defaultItemTpl let-result>\n <div class=\"py-2\">\n @if (getSecondaryLabel(result)) {\n <div class=\"gn-ui-card-detail\">{{ getSecondaryLabel(result) }}</div>\n }\n <div class=\"gn-ui-card-title\">{{ getDisplayLabel(result) }}</div>\n </div>\n</ng-template>\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 }); }
27507
+ }
27508
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LocationSearchComponent, decorators: [{
27509
+ type: Component,
27510
+ 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]=\"defaultItemTpl\"\n [clearOnSelection]=\"true\"\n (itemSelected)=\"handleItemSelected($event)\"\n>\n</gn-ui-autocomplete>\n\n<ng-template #defaultItemTpl let-result>\n <div class=\"py-2\">\n @if (getSecondaryLabel(result)) {\n <div class=\"gn-ui-card-detail\">{{ getSecondaryLabel(result) }}</div>\n }\n <div class=\"gn-ui-card-title\">{{ getDisplayLabel(result) }}</div>\n </div>\n</ng-template>\n" }]
27511
+ }], propDecorators: { placeholder: [{
27512
+ type: Input
27513
+ }], resultSelected: [{
27514
+ type: Output
27515
+ }], bboxSelected: [{
27516
+ type: Output
27517
+ }] } });
27518
+
27898
27519
  // expects the replacement key ${uuid}
27899
27520
  const RECORD_DATASET_URL_TOKEN = new InjectionToken('record-dataset-url-token');
27900
27521
  const RECORD_SERVICE_URL_TOKEN = new InjectionToken('record-service-url-token');
@@ -28053,6 +27674,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
28053
27674
  class AbstractSearchField {
28054
27675
  }
28055
27676
  class SimpleSearchField {
27677
+ get fieldIdentifier() {
27678
+ return this.customIdentifier ?? this.esFieldName;
27679
+ }
28056
27680
  constructor(esFieldName, injector, order = 'asc', orderType = 'key') {
28057
27681
  this.esFieldName = esFieldName;
28058
27682
  this.injector = injector;
@@ -28061,14 +27685,26 @@ class SimpleSearchField {
28061
27685
  this.repository = this.injector.get(RecordsRepositoryInterface);
28062
27686
  // FIXME: this is required to register runtime fields; abstract this as well
28063
27687
  this.esService = this.injector.get(ElasticsearchService);
27688
+ this.includeValues = [];
27689
+ this.excludeValues = [];
27690
+ }
27691
+ setFieldIdentifier(fieldIdentifier) {
27692
+ this.customIdentifier = fieldIdentifier;
27693
+ this.esService.registerFieldAlias(fieldIdentifier, this.esFieldName);
28064
27694
  }
28065
27695
  getAggregations() {
28066
27696
  return {
28067
- [this.esFieldName]: {
27697
+ [this.fieldIdentifier]: {
28068
27698
  type: 'terms',
28069
27699
  field: this.esFieldName,
28070
27700
  limit: 1000,
28071
27701
  sort: [this.order, this.orderType],
27702
+ ...(this.includeValues?.length && {
27703
+ includeValues: this.includeValues,
27704
+ }),
27705
+ ...(this.excludeValues?.length && {
27706
+ excludeValues: this.excludeValues,
27707
+ }),
28072
27708
  },
28073
27709
  };
28074
27710
  }
@@ -28076,7 +27712,7 @@ class SimpleSearchField {
28076
27712
  return bucket.term.toString();
28077
27713
  }
28078
27714
  getAvailableValues() {
28079
- return this.repository.aggregate(this.getAggregations()).pipe(map$1((response) => response[this.esFieldName].buckets || []), switchMap((buckets) => {
27715
+ return this.repository.aggregate(this.getAggregations()).pipe(map$1((response) => response[this.fieldIdentifier].buckets || []), switchMap((buckets) => {
28080
27716
  const bucketPromises = buckets.map(async (bucket) => ({
28081
27717
  label: `${await this.getBucketLabel(bucket)} (${bucket.count})`,
28082
27718
  value: bucket.term.toString(),
@@ -28089,7 +27725,7 @@ class SimpleSearchField {
28089
27725
  // FieldValue[]
28090
27726
  if (this.getType() === 'values') {
28091
27727
  return of({
28092
- [this.esFieldName]: values.reduce((acc, val) => {
27728
+ [this.fieldIdentifier]: values.reduce((acc, val) => {
28093
27729
  const value = val.toString();
28094
27730
  if (value !== '') {
28095
27731
  return { ...acc, [value]: true };
@@ -28100,11 +27736,11 @@ class SimpleSearchField {
28100
27736
  }
28101
27737
  // DateRange
28102
27738
  return of({
28103
- [this.esFieldName]: values[0] !== '' ? values[0] : {},
27739
+ [this.fieldIdentifier]: values[0] !== '' ? values[0] : {},
28104
27740
  });
28105
27741
  }
28106
27742
  getValuesForFilter(filters) {
28107
- const filter = filters[this.esFieldName];
27743
+ const filter = filters[this.fieldIdentifier];
28108
27744
  if (!filter)
28109
27745
  return of([]);
28110
27746
  // filter by expression
@@ -28122,6 +27758,9 @@ class SimpleSearchField {
28122
27758
  getType() {
28123
27759
  return 'values';
28124
27760
  }
27761
+ clone() {
27762
+ return Object.create(this);
27763
+ }
28125
27764
  }
28126
27765
  class TranslatedSearchField extends SimpleSearchField {
28127
27766
  constructor(esFieldName, injector, order = 'asc', orderType = 'key') {
@@ -28363,15 +28002,26 @@ class DateRangeSearchField extends SimpleSearchField {
28363
28002
  return 'dateRange';
28364
28003
  }
28365
28004
  }
28005
+ // treats a resource's creation/revision dates as an interval (its min and max), so a record matches
28006
+ // when that interval intersects the filter range, not just when a single date falls inside it
28366
28007
  class ResourceCreationRevisionDateSearchField extends DateRangeSearchField {
28367
28008
  constructor(injector, order = 'desc') {
28368
28009
  super('resourceCreationRevisionDate', injector, order);
28369
- this.esService.registerRuntimeField('resourceCreationRevisionDate', `if (doc.containsKey('creationDateForResource') && doc['creationDateForResource'].size() > 0) {
28370
- for (def date : doc['creationDateForResource']) { emit(date.millis); }
28371
- }
28372
- if (doc.containsKey('revisionDateForResource') && doc['revisionDateForResource'].size() > 0) {
28373
- for (def date : doc['revisionDateForResource']) { emit(date.millis); }
28374
- }`, 'date');
28010
+ const dateSourceFields = [
28011
+ 'creationDateForResource',
28012
+ 'revisionDateForResource',
28013
+ ];
28014
+ const collectSortedDates = dateSourceFields
28015
+ .map((field) => `if (doc.containsKey('${field}')) {
28016
+ for (def date : doc['${field}']) { dates.add(date.millis); }
28017
+ }`)
28018
+ .join('\n');
28019
+ this.esService.registerRuntimeField('resourceCreationRevisionDateMin', `def dates = [];
28020
+ ${collectSortedDates}
28021
+ if (dates.length > 0) { dates.sort(null); emit(dates[0]); }`, 'date');
28022
+ this.esService.registerRuntimeField('resourceCreationRevisionDateMax', `def dates = [];
28023
+ ${collectSortedDates}
28024
+ if (dates.length > 0) { dates.sort(null); emit(dates[dates.length - 1]); }`, 'date');
28375
28025
  }
28376
28026
  }
28377
28027
  class BoundingBoxSearchField extends SimpleSearchField {
@@ -28380,11 +28030,11 @@ class BoundingBoxSearchField extends SimpleSearchField {
28380
28030
  }
28381
28031
  getFiltersForValues(values) {
28382
28032
  return of({
28383
- [this.esFieldName]: values.map(Number),
28033
+ [this.fieldIdentifier]: values.map(Number),
28384
28034
  });
28385
28035
  }
28386
28036
  getValuesForFilter(filters) {
28387
- const filter = filters[this.esFieldName];
28037
+ const filter = filters[this.fieldIdentifier];
28388
28038
  return of(isBoundingBox(filter) ? filter : []);
28389
28039
  }
28390
28040
  getType() {
@@ -28483,7 +28133,7 @@ class RecordKindField extends SimpleSearchField {
28483
28133
  }
28484
28134
  getFiltersForValues(values) {
28485
28135
  const filters = {
28486
- [this.esFieldName]: values.reduce((acc, value) => {
28136
+ [this.fieldIdentifier]: values.reduce((acc, value) => {
28487
28137
  if (value === '')
28488
28138
  return { ...acc, [value]: true };
28489
28139
  const keysToAdd = this.TYPE_MAPPING[value] || [value];
@@ -28506,7 +28156,7 @@ class RecordKindField extends SimpleSearchField {
28506
28156
  return of(filters);
28507
28157
  }
28508
28158
  getValuesForFilter(filters) {
28509
- const filter = filters[this.esFieldName];
28159
+ const filter = filters[this.fieldIdentifier];
28510
28160
  if (!filter)
28511
28161
  return of([]);
28512
28162
  const activeValues = Object.keys(filter).filter((v) => filter[v]);
@@ -28536,11 +28186,15 @@ marker('search.filters.publisherOrg');
28536
28186
  marker('search.filters.user');
28537
28187
  marker('search.filters.changeDate');
28538
28188
  marker('search.filters.resourceCreationRevisionDate');
28189
+ marker('search.filters.temporalExtent');
28539
28190
  marker('search.filters.spatialExtent');
28191
+ marker('search.filters.documentStandard');
28192
+ const CUSTOM_FIELDS = new InjectionToken('custom-fields');
28540
28193
  class FieldsService {
28541
28194
  constructor() {
28542
28195
  this.injector = inject(Injector);
28543
- this.fields = {
28196
+ this.customFields = inject(CUSTOM_FIELDS, { optional: true }) ?? [];
28197
+ this.baseFields = {
28544
28198
  organization: new OrganizationSearchField(this.injector),
28545
28199
  format: new SimpleSearchField('format', this.injector, 'asc'),
28546
28200
  resourceType: new ResourceTypeLegacyField(this.injector), // Deprecated, use `recordKind` instead
@@ -28560,9 +28214,31 @@ class FieldsService {
28560
28214
  user: new UserSearchField(this.injector),
28561
28215
  changeDate: new DateRangeSearchField('changeDate', this.injector, 'desc'),
28562
28216
  resourceCreationRevisionDate: new ResourceCreationRevisionDateSearchField(this.injector, 'desc'),
28217
+ temporalExtent: new DateRangeSearchField('resourceTemporalExtentDateRange', this.injector, 'desc'),
28563
28218
  availableServices: new AvailableServicesField(this.injector),
28564
28219
  spatialExtent: new BoundingBoxSearchField('spatialExtent', this.injector),
28565
28220
  };
28221
+ this.fields = { ...this.baseFields };
28222
+ for (const customField of this.customFields) {
28223
+ const baseField = this.baseFields[customField.baseFilter];
28224
+ if (!(baseField instanceof SimpleSearchField)) {
28225
+ console.warn(`The custom field '${customField.name}' relies on a base field '${customField.baseFilter}' that is not supported. This field will be ignored.`);
28226
+ continue;
28227
+ }
28228
+ if (!customField.name.includes(':')) {
28229
+ console.warn(`The custom field '${customField.name}' is not scoped with a prefix. Custom fields must have their names prefixed, for instance "myOrg:myKeywords". This field will be ignored.`);
28230
+ continue;
28231
+ }
28232
+ const newField = baseField.clone();
28233
+ newField.setFieldIdentifier(customField.name);
28234
+ if (customField.includeValues) {
28235
+ newField.includeValues = customField.includeValues;
28236
+ }
28237
+ if (customField.excludeValues) {
28238
+ newField.excludeValues = customField.excludeValues;
28239
+ }
28240
+ this.fields[customField.name] = newField;
28241
+ }
28566
28242
  }
28567
28243
  get supportedFields() {
28568
28244
  return Object.keys(this.fields);
@@ -28610,7 +28286,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
28610
28286
  args: [{
28611
28287
  providedIn: 'root',
28612
28288
  }]
28613
- }] });
28289
+ }], ctorParameters: () => [] });
28614
28290
 
28615
28291
  class ApiCardComponent {
28616
28292
  constructor() {
@@ -30550,7 +30226,7 @@ class ServiceCapabilitiesComponent {
30550
30226
  if (this.apiLinks.length > 0 &&
30551
30227
  this.apiLinks[0].accessServiceProtocol !== 'ogcFeatures') {
30552
30228
  this.loading = true;
30553
- this.availableLayers = await getLayers(this.apiLinks[0].url.href, this.apiLinks[0].accessServiceProtocol);
30229
+ this.availableLayers = await getLayers(this.apiLinks[0].url.href, this.apiLinks[0].accessServiceProtocol, true);
30554
30230
  this.loading = false;
30555
30231
  this.cdr.detectChanges();
30556
30232
  this.filteredLayers = this.availableLayers;
@@ -30636,14 +30312,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
30636
30312
  marker('feature.catalog.attribute.type');
30637
30313
  marker('feature.catalog.attribute.name');
30638
30314
  marker('feature.catalog.attribute.code');
30639
- marker('feature.catalog.attribute.definition');
30315
+ marker('feature.catalog.attribute.description');
30640
30316
  marker('feature.catalog.attribute.values');
30641
30317
  class FeatureCatalogListComponent {
30642
30318
  constructor() {
30643
30319
  this.COLUMNS_DEFAULT = [
30644
30320
  { key: 'type', width: '25%' },
30645
30321
  { key: 'name', width: '40%' },
30646
- { key: 'definition', width: 'minmax(0px, 1fr)' },
30322
+ { key: 'description', width: 'minmax(0px, 1fr)' },
30647
30323
  ];
30648
30324
  this.COLUMN_VALUES = {
30649
30325
  key: 'values',
@@ -30664,7 +30340,7 @@ class FeatureCatalogListComponent {
30664
30340
  return col.key;
30665
30341
  }
30666
30342
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FeatureCatalogListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
30667
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FeatureCatalogListComponent, isStandalone: true, selector: "gn-ui-feature-catalog-list", inputs: { filteredFeatureCatalog: "filteredFeatureCatalog" }, providers: [provideIcons({ iconoirList })], viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true }, { propertyName: "scrollable", first: true, predicate: CdkScrollable, descendants: true, static: true }, { propertyName: "panelComponent", first: true, predicate: ["expanel"], descendants: true, read: ExpandablePanelComponent, static: true }], ngImport: i0, template: "<div\n cdkScrollable\n class=\"h-[562px] relative overflow-y-auto overflow-hidden\"\n data-cy=\"feat-catalog-scrollable\"\n>\n <div #scrollContainer class=\"flex flex-col gap-2 py-5 px-5\">\n @for (\n featureType of filteredFeatureCatalog?.featureTypes;\n track featureType\n ) {\n <div class=\"rounded shadow bg-white\">\n <gn-ui-expandable-panel\n #expanel\n [collapsed]=\"filteredFeatureCatalog?.featureTypes?.length !== 1\"\n iconColor=\"black\"\n >\n <ng-template #titleTemplate>\n <div class=\"px-2\">\n <div class=\"text-lg font-bold\">{{ featureType.name }}</div>\n @if (featureType.definition) {\n <div class=\"text-sm\">\n {{ featureType.definition }}\n </div>\n }\n </div>\n </ng-template>\n @if (expanel && !expanel.collapsed) {\n <div\n class=\"grid gap-0\"\n [style.grid-template-columns]=\"\n getGridTemplateColumns(featureType.attributes)\n \"\n data-cy=\"feature-type-content\"\n >\n @for (\n col of getColumnsDefinition(featureType.attributes);\n track trackByColumn(i, col);\n let i = $index\n ) {\n <div\n class=\"py-1 px-2 text-sm font-bold text-left border-t break-all\"\n [class.border-l]=\"i > 0\"\n [class.border-gray-300]=\"i > 0\"\n [ngClass]=\"col.class || ''\"\n data-test=\"column-label\"\n >\n {{ 'feature.catalog.attribute.' + col.key | translate }}\n </div>\n }\n @if (expanel && !expanel.collapsed) {\n @for (row of featureType.attributes; track row) {\n @for (\n col of getColumnsDefinition(featureType.attributes);\n track trackByColumn(i, col);\n let i = $index\n ) {\n <div\n class=\"bg-white text-sm font-normal text-left border-t\"\n [class.border-l]=\"i > 0\"\n [class.border-gray-300]=\"i > 0\"\n data-test=\"cell-content\"\n >\n @if (col.key === 'values') {\n @if (row.values?.length > 0) {\n <gn-ui-cell-popin\n #popinRef\n [scrollContainer]=\"scrollContainer\"\n [cdkScrollContainer]=\"scrollable\"\n >\n <div\n cellContent\n data-cy=\"values-cell-popin-trigger\"\n >\n <gn-ui-button\n type=\"light\"\n extraClass=\"bg-transparent border-none\"\n (buttonClick)=\"popinRef.openOverlay()\"\n >\n <ng-icon name=\"iconoirList\" size=\"24\"></ng-icon>\n </gn-ui-button>\n </div>\n <div\n popinContent\n class=\"max-h-60 overflow-y-auto min-w-64 py-4 px-6\"\n style=\"scrollbar-width: thin\"\n >\n <ul class=\"list-disc list-inside mr-4\">\n @for (v of row.values; track v) {\n <li>\n {{ v.label || v.code }}\n </li>\n }\n </ul>\n </div>\n </gn-ui-cell-popin>\n }\n } @else {\n <gn-ui-truncated-text\n [scrollContainer]=\"scrollContainer\"\n [cdkScrollContainer]=\"scrollable\"\n [text]=\"row[col.key]\"\n ></gn-ui-truncated-text>\n }\n </div>\n }\n }\n }\n </div>\n }\n </gn-ui-expandable-panel>\n </div>\n }\n </div>\n</div>\n", dependencies: [{ kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: CellPopinComponent, selector: "gn-ui-cell-popin", inputs: ["extraClass", "cdkScrollContainer", "scrollContainer", "activePopin"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ExpandablePanelComponent, selector: "gn-ui-expandable-panel", inputs: ["title", "iconColor", "collapsed"] }, { kind: "component", type: TruncatedTextComponent, selector: "gn-ui-truncated-text", inputs: ["text", "extraClass", "scrollContainer", "cdkScrollContainer"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i2$3.CdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
30343
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FeatureCatalogListComponent, isStandalone: true, selector: "gn-ui-feature-catalog-list", inputs: { filteredFeatureCatalog: "filteredFeatureCatalog" }, providers: [provideIcons({ iconoirList })], viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true }, { propertyName: "scrollable", first: true, predicate: CdkScrollable, descendants: true, static: true }, { propertyName: "panelComponent", first: true, predicate: ["expanel"], descendants: true, read: ExpandablePanelComponent, static: true }], ngImport: i0, template: "<div\n cdkScrollable\n class=\"h-[562px] relative overflow-y-auto overflow-hidden\"\n data-cy=\"feat-catalog-scrollable\"\n>\n <div #scrollContainer class=\"flex flex-col gap-2 py-5 px-5\">\n @for (\n featureType of filteredFeatureCatalog?.featureTypes;\n track featureType\n ) {\n <div class=\"rounded shadow bg-white\">\n <gn-ui-expandable-panel\n #expanel\n [collapsed]=\"filteredFeatureCatalog?.featureTypes?.length !== 1\"\n iconColor=\"black\"\n >\n <ng-template #titleTemplate>\n <div class=\"px-2\">\n <div class=\"text-lg font-bold\">{{ featureType.name }}</div>\n @if (featureType.description) {\n <div class=\"text-sm\">\n {{ featureType.description }}\n </div>\n }\n </div>\n </ng-template>\n @if (expanel && !expanel.collapsed) {\n <div\n class=\"grid gap-0\"\n [style.grid-template-columns]=\"\n getGridTemplateColumns(featureType.attributes)\n \"\n data-cy=\"feature-type-content\"\n >\n @for (\n col of getColumnsDefinition(featureType.attributes);\n track trackByColumn(i, col);\n let i = $index\n ) {\n <div\n class=\"py-1 px-2 text-sm font-bold text-left border-t break-all\"\n [class.border-l]=\"i > 0\"\n [class.border-gray-300]=\"i > 0\"\n [ngClass]=\"col.class || ''\"\n data-test=\"column-label\"\n >\n {{ 'feature.catalog.attribute.' + col.key | translate }}\n </div>\n }\n @if (expanel && !expanel.collapsed) {\n @for (row of featureType.attributes; track row) {\n @for (\n col of getColumnsDefinition(featureType.attributes);\n track trackByColumn(i, col);\n let i = $index\n ) {\n <div\n class=\"bg-white text-sm font-normal text-left border-t\"\n [class.border-l]=\"i > 0\"\n [class.border-gray-300]=\"i > 0\"\n data-test=\"cell-content\"\n >\n @if (col.key === 'values') {\n @if (row.values?.length > 0) {\n <gn-ui-cell-popin\n #popinRef\n [scrollContainer]=\"scrollContainer\"\n [cdkScrollContainer]=\"scrollable\"\n >\n <div\n cellContent\n data-cy=\"values-cell-popin-trigger\"\n >\n <gn-ui-button\n type=\"light\"\n extraClass=\"bg-transparent border-none\"\n (buttonClick)=\"popinRef.openOverlay()\"\n >\n <ng-icon name=\"iconoirList\" size=\"24\"></ng-icon>\n </gn-ui-button>\n </div>\n <div\n popinContent\n class=\"max-h-60 overflow-y-auto min-w-64 py-4 px-6\"\n style=\"scrollbar-width: thin\"\n >\n <ul class=\"list-disc list-inside mr-4\">\n @for (v of row.values; track v) {\n <li>\n {{ v.label || v.value }}\n </li>\n }\n </ul>\n </div>\n </gn-ui-cell-popin>\n }\n } @else {\n <gn-ui-truncated-text\n [scrollContainer]=\"scrollContainer\"\n [cdkScrollContainer]=\"scrollable\"\n [text]=\"row[col.key]\"\n ></gn-ui-truncated-text>\n }\n </div>\n }\n }\n }\n </div>\n }\n </gn-ui-expandable-panel>\n </div>\n }\n </div>\n</div>\n", dependencies: [{ kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: CellPopinComponent, selector: "gn-ui-cell-popin", inputs: ["extraClass", "cdkScrollContainer", "scrollContainer", "activePopin"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ExpandablePanelComponent, selector: "gn-ui-expandable-panel", inputs: ["title", "iconColor", "collapsed"] }, { kind: "component", type: TruncatedTextComponent, selector: "gn-ui-truncated-text", inputs: ["text", "extraClass", "scrollContainer", "cdkScrollContainer"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i2$3.CdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
30668
30344
  }
30669
30345
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FeatureCatalogListComponent, decorators: [{
30670
30346
  type: Component,
@@ -30677,7 +30353,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
30677
30353
  TruncatedTextComponent,
30678
30354
  NgIconComponent,
30679
30355
  ScrollingModule,
30680
- ], providers: [provideIcons({ iconoirList })], template: "<div\n cdkScrollable\n class=\"h-[562px] relative overflow-y-auto overflow-hidden\"\n data-cy=\"feat-catalog-scrollable\"\n>\n <div #scrollContainer class=\"flex flex-col gap-2 py-5 px-5\">\n @for (\n featureType of filteredFeatureCatalog?.featureTypes;\n track featureType\n ) {\n <div class=\"rounded shadow bg-white\">\n <gn-ui-expandable-panel\n #expanel\n [collapsed]=\"filteredFeatureCatalog?.featureTypes?.length !== 1\"\n iconColor=\"black\"\n >\n <ng-template #titleTemplate>\n <div class=\"px-2\">\n <div class=\"text-lg font-bold\">{{ featureType.name }}</div>\n @if (featureType.definition) {\n <div class=\"text-sm\">\n {{ featureType.definition }}\n </div>\n }\n </div>\n </ng-template>\n @if (expanel && !expanel.collapsed) {\n <div\n class=\"grid gap-0\"\n [style.grid-template-columns]=\"\n getGridTemplateColumns(featureType.attributes)\n \"\n data-cy=\"feature-type-content\"\n >\n @for (\n col of getColumnsDefinition(featureType.attributes);\n track trackByColumn(i, col);\n let i = $index\n ) {\n <div\n class=\"py-1 px-2 text-sm font-bold text-left border-t break-all\"\n [class.border-l]=\"i > 0\"\n [class.border-gray-300]=\"i > 0\"\n [ngClass]=\"col.class || ''\"\n data-test=\"column-label\"\n >\n {{ 'feature.catalog.attribute.' + col.key | translate }}\n </div>\n }\n @if (expanel && !expanel.collapsed) {\n @for (row of featureType.attributes; track row) {\n @for (\n col of getColumnsDefinition(featureType.attributes);\n track trackByColumn(i, col);\n let i = $index\n ) {\n <div\n class=\"bg-white text-sm font-normal text-left border-t\"\n [class.border-l]=\"i > 0\"\n [class.border-gray-300]=\"i > 0\"\n data-test=\"cell-content\"\n >\n @if (col.key === 'values') {\n @if (row.values?.length > 0) {\n <gn-ui-cell-popin\n #popinRef\n [scrollContainer]=\"scrollContainer\"\n [cdkScrollContainer]=\"scrollable\"\n >\n <div\n cellContent\n data-cy=\"values-cell-popin-trigger\"\n >\n <gn-ui-button\n type=\"light\"\n extraClass=\"bg-transparent border-none\"\n (buttonClick)=\"popinRef.openOverlay()\"\n >\n <ng-icon name=\"iconoirList\" size=\"24\"></ng-icon>\n </gn-ui-button>\n </div>\n <div\n popinContent\n class=\"max-h-60 overflow-y-auto min-w-64 py-4 px-6\"\n style=\"scrollbar-width: thin\"\n >\n <ul class=\"list-disc list-inside mr-4\">\n @for (v of row.values; track v) {\n <li>\n {{ v.label || v.code }}\n </li>\n }\n </ul>\n </div>\n </gn-ui-cell-popin>\n }\n } @else {\n <gn-ui-truncated-text\n [scrollContainer]=\"scrollContainer\"\n [cdkScrollContainer]=\"scrollable\"\n [text]=\"row[col.key]\"\n ></gn-ui-truncated-text>\n }\n </div>\n }\n }\n }\n </div>\n }\n </gn-ui-expandable-panel>\n </div>\n }\n </div>\n</div>\n" }]
30356
+ ], providers: [provideIcons({ iconoirList })], template: "<div\n cdkScrollable\n class=\"h-[562px] relative overflow-y-auto overflow-hidden\"\n data-cy=\"feat-catalog-scrollable\"\n>\n <div #scrollContainer class=\"flex flex-col gap-2 py-5 px-5\">\n @for (\n featureType of filteredFeatureCatalog?.featureTypes;\n track featureType\n ) {\n <div class=\"rounded shadow bg-white\">\n <gn-ui-expandable-panel\n #expanel\n [collapsed]=\"filteredFeatureCatalog?.featureTypes?.length !== 1\"\n iconColor=\"black\"\n >\n <ng-template #titleTemplate>\n <div class=\"px-2\">\n <div class=\"text-lg font-bold\">{{ featureType.name }}</div>\n @if (featureType.description) {\n <div class=\"text-sm\">\n {{ featureType.description }}\n </div>\n }\n </div>\n </ng-template>\n @if (expanel && !expanel.collapsed) {\n <div\n class=\"grid gap-0\"\n [style.grid-template-columns]=\"\n getGridTemplateColumns(featureType.attributes)\n \"\n data-cy=\"feature-type-content\"\n >\n @for (\n col of getColumnsDefinition(featureType.attributes);\n track trackByColumn(i, col);\n let i = $index\n ) {\n <div\n class=\"py-1 px-2 text-sm font-bold text-left border-t break-all\"\n [class.border-l]=\"i > 0\"\n [class.border-gray-300]=\"i > 0\"\n [ngClass]=\"col.class || ''\"\n data-test=\"column-label\"\n >\n {{ 'feature.catalog.attribute.' + col.key | translate }}\n </div>\n }\n @if (expanel && !expanel.collapsed) {\n @for (row of featureType.attributes; track row) {\n @for (\n col of getColumnsDefinition(featureType.attributes);\n track trackByColumn(i, col);\n let i = $index\n ) {\n <div\n class=\"bg-white text-sm font-normal text-left border-t\"\n [class.border-l]=\"i > 0\"\n [class.border-gray-300]=\"i > 0\"\n data-test=\"cell-content\"\n >\n @if (col.key === 'values') {\n @if (row.values?.length > 0) {\n <gn-ui-cell-popin\n #popinRef\n [scrollContainer]=\"scrollContainer\"\n [cdkScrollContainer]=\"scrollable\"\n >\n <div\n cellContent\n data-cy=\"values-cell-popin-trigger\"\n >\n <gn-ui-button\n type=\"light\"\n extraClass=\"bg-transparent border-none\"\n (buttonClick)=\"popinRef.openOverlay()\"\n >\n <ng-icon name=\"iconoirList\" size=\"24\"></ng-icon>\n </gn-ui-button>\n </div>\n <div\n popinContent\n class=\"max-h-60 overflow-y-auto min-w-64 py-4 px-6\"\n style=\"scrollbar-width: thin\"\n >\n <ul class=\"list-disc list-inside mr-4\">\n @for (v of row.values; track v) {\n <li>\n {{ v.label || v.value }}\n </li>\n }\n </ul>\n </div>\n </gn-ui-cell-popin>\n }\n } @else {\n <gn-ui-truncated-text\n [scrollContainer]=\"scrollContainer\"\n [cdkScrollContainer]=\"scrollable\"\n [text]=\"row[col.key]\"\n ></gn-ui-truncated-text>\n }\n </div>\n }\n }\n }\n </div>\n }\n </gn-ui-expandable-panel>\n </div>\n }\n </div>\n</div>\n" }]
30681
30357
  }], propDecorators: { filteredFeatureCatalog: [{
30682
30358
  type: Input
30683
30359
  }], scrollContainer: [{
@@ -31815,6 +31491,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
31815
31491
  ], 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" }]
31816
31492
  }] });
31817
31493
 
31494
+ marker('search.filters.spatialExtent.searchLocation');
31495
+ // in MB, null means no limit
31496
+ const SPATIAL_EXTENT_MAX_FILE_SIZE = new InjectionToken('spatialExtentMaxFileSize', { factory: () => null });
31818
31497
  class FilterDropdownComponent {
31819
31498
  constructor() {
31820
31499
  this.searchFacade = inject(SearchFacade);
@@ -31822,7 +31501,7 @@ class FilterDropdownComponent {
31822
31501
  this.fieldsService = inject(FieldsService);
31823
31502
  this.notificationsService = inject(NotificationsService);
31824
31503
  this.translateService = inject(TranslateService);
31825
- this.spatialExtentMaxFileSize = getOptionalSearchConfig()?.SPATIAL_EXTENT_MAX_FILE_SIZE;
31504
+ this.spatialExtentMaxFileSize = inject(SPATIAL_EXTENT_MAX_FILE_SIZE);
31826
31505
  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([])));
31827
31506
  this.selectedDateRange$ = this.selected$.pipe(map$1((selected) => (Array.isArray(selected) ? {} : selected)));
31828
31507
  this.selectedBoundingBox$ = this.selected$.pipe(map$1((selected) => Array.isArray(selected) && selected.length > 0
@@ -31843,6 +31522,9 @@ class FilterDropdownComponent {
31843
31522
  .subscribe((filters) => this.searchService.updateFilters(filters));
31844
31523
  this.clearSpatialExtentErrorNotification();
31845
31524
  }
31525
+ onLocationSelected({ bbox, label }) {
31526
+ this.spatialExtentDropdown.setLocationBbox(bbox, label);
31527
+ }
31846
31528
  onSpatialExtentError(error) {
31847
31529
  this.clearSpatialExtentErrorNotification();
31848
31530
  this.spatialExtentErrorNotificationId =
@@ -31873,7 +31555,7 @@ class FilterDropdownComponent {
31873
31555
  .subscribe((filters) => this.searchService.updateFilters(filters));
31874
31556
  }
31875
31557
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
31876
- 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 }); }
31558
+ 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" }, viewQueries: [{ propertyName: "spatialExtentDropdown", first: true, predicate: SpatialExtentDropdownComponent, descendants: true }], 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 >\n <gn-ui-location-search\n [placeholder]=\"'search.filters.spatialExtent.searchLocation' | translate\"\n (bboxSelected)=\"onLocationSelected($event)\"\n data-cy=\"locationSearch\"\n ></gn-ui-location-search>\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: "component", type: LocationSearchComponent, selector: "gn-ui-location-search", inputs: ["placeholder"], outputs: ["resultSelected", "bboxSelected"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
31877
31559
  }
31878
31560
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, decorators: [{
31879
31561
  type: Component,
@@ -31882,11 +31564,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
31882
31564
  DateRangeDropdownComponent,
31883
31565
  DropdownMultiselectComponent,
31884
31566
  SpatialExtentDropdownComponent,
31885
- ], 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" }]
31567
+ LocationSearchComponent,
31568
+ TranslatePipe,
31569
+ ], 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 >\n <gn-ui-location-search\n [placeholder]=\"'search.filters.spatialExtent.searchLocation' | translate\"\n (bboxSelected)=\"onLocationSelected($event)\"\n data-cy=\"locationSearch\"\n ></gn-ui-location-search>\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" }]
31886
31570
  }], propDecorators: { fieldName: [{
31887
31571
  type: Input
31888
31572
  }], title: [{
31889
31573
  type: Input
31574
+ }], spatialExtentDropdown: [{
31575
+ type: ViewChild,
31576
+ args: [SpatialExtentDropdownComponent]
31890
31577
  }] } });
31891
31578
 
31892
31579
  class FuzzySearchComponent {
@@ -31943,7 +31630,7 @@ class FuzzySearchComponent {
31943
31630
  this.isSearchActive.emit(event);
31944
31631
  }
31945
31632
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FuzzySearchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
31946
- 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 }); }
31633
+ 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 }); }
31947
31634
  }
31948
31635
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FuzzySearchComponent, decorators: [{
31949
31636
  type: Component,
@@ -32503,6 +32190,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
32503
32190
  marker('search.filters.summaryLabel.user');
32504
32191
  marker('search.filters.summaryLabel.changeDate');
32505
32192
  marker('search.filters.summaryLabel.resourceCreationRevisionDate');
32193
+ marker('search.filters.summaryLabel.temporalExtent');
32506
32194
  const OPEN_BOUND = '…';
32507
32195
  class SearchFiltersSummaryItemComponent {
32508
32196
  constructor() {
@@ -32884,95 +32572,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
32884
32572
  ], 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" }]
32885
32573
  }] });
32886
32574
 
32887
- class GeocodingComponent {
32888
- constructor() {
32889
- this.mapFacade = inject(MapFacade);
32890
- this.geocodingService = inject(GeocodingService);
32891
- this.searchText = '';
32892
- this.results = [];
32893
- this.searchTextChanged = new Subject();
32894
- this.destroy$ = new Subject();
32895
- this.errorMessage = null;
32896
- this.searchTextChanged
32897
- .pipe(debounceTime(300), switchMap$1((searchText) => {
32898
- return from(this.geocodingService.query(searchText)).pipe(catchError$1((error) => {
32899
- this.errorMessage =
32900
- 'An error occurred while searching. Please try again.';
32901
- console.error(error);
32902
- return [];
32903
- }));
32904
- }), takeUntil(this.destroy$))
32905
- .subscribe((results) => {
32906
- this.results = results;
32907
- });
32908
- }
32909
- ngOnDestroy() {
32910
- this.destroy$.next();
32911
- this.destroy$.complete();
32912
- }
32913
- onSearchChange(searchText) {
32914
- if (!searchText) {
32915
- this.clearSearch();
32916
- return;
32917
- }
32918
- else {
32919
- this.searchTextChanged.next(searchText);
32920
- }
32921
- }
32922
- clearSearch() {
32923
- this.searchText = '';
32924
- this.results = [];
32925
- this.errorMessage = null;
32926
- }
32927
- async zoomToLocation(result) {
32928
- const geometry = result.geom;
32929
- if (geometry.type === 'Point') {
32930
- await this.zoomToPoint(geometry.coordinates);
32931
- }
32932
- else if (geometry.type === 'Polygon') {
32933
- await this.zoomToPolygon(geometry.coordinates);
32934
- }
32935
- else {
32936
- console.error(`Unsupported geometry type: ${geometry.type}`);
32937
- }
32938
- }
32939
- async zoomToPoint(pointCoords) {
32940
- const context = await firstValueFrom(this.mapFacade.context$);
32941
- const view = {
32942
- center: pointCoords,
32943
- zoom: 10,
32944
- };
32945
- this.mapFacade.applyContext({
32946
- ...context,
32947
- view,
32948
- });
32949
- }
32950
- async zoomToPolygon(polygonCoords) {
32951
- const context = await firstValueFrom(this.mapFacade.context$);
32952
- const view = {
32953
- geometry: {
32954
- type: 'Polygon',
32955
- coordinates: polygonCoords,
32956
- },
32957
- };
32958
- this.mapFacade.applyContext({
32959
- ...context,
32960
- view,
32961
- });
32962
- }
32963
- onEnterPress() {
32964
- if (this.results && this.results.length > 0) {
32965
- this.zoomToLocation(this.results[0]);
32966
- }
32967
- }
32968
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
32969
- 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" }] }); }
32970
- }
32971
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingComponent, decorators: [{
32972
- type: Component,
32973
- 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" }]
32974
- }], ctorParameters: () => [] });
32975
-
32976
32575
  /*
32977
32576
  Metadata actions
32978
32577
  */
@@ -34155,7 +33754,7 @@ class DataService {
34155
33754
  }));
34156
33755
  }
34157
33756
  async getDownloadLinksFromOgcApiFeatures(ogcApiLink) {
34158
- const collectionInfo = await this.getDownloadUrlsFromOgcApi(ogcApiLink.url.href);
33757
+ const collectionInfo = await this.getDownloadUrlsFromOgcApi(ogcApiLink.url.href, ogcApiLink.name);
34159
33758
  return Object.keys(collectionInfo.bulkDownloadLinks).map((mimeType) => {
34160
33759
  const urlWithoutLimit = new URL(collectionInfo.bulkDownloadLinks[mimeType]);
34161
33760
  urlWithoutLimit.searchParams.delete('limit');
@@ -34168,27 +33767,31 @@ class DataService {
34168
33767
  };
34169
33768
  });
34170
33769
  }
34171
- async getDownloadUrlsFromOgcApi(url) {
33770
+ async getTargetCollectionId(endpoint, collectionId) {
33771
+ const collections = await endpoint.featureCollections;
33772
+ return collections.length === 1 && !collectionId
33773
+ ? collections[0]
33774
+ : collectionId;
33775
+ }
33776
+ async getDownloadUrlsFromOgcApi(url, collectionId) {
34172
33777
  const endpoint = new OgcApiEndpoint(url);
34173
- return await endpoint.featureCollections
34174
- .then((collections) => {
34175
- return endpoint.getCollectionInfo(collections[0]);
34176
- })
34177
- .catch((error) => {
34178
- throw new Error(`ogc.unreachable.unknown`);
34179
- });
33778
+ try {
33779
+ const id = await this.getTargetCollectionId(endpoint, collectionId);
33780
+ return await endpoint.getCollectionInfo(id);
33781
+ }
33782
+ catch {
33783
+ throw new Error('ogc.unreachable.unknown');
33784
+ }
34180
33785
  }
34181
- async getItemsFromOgcApi(url, limit) {
33786
+ async getItemsFromOgcApi(url, collectionId, limit) {
34182
33787
  const endpoint = new OgcApiEndpoint(url);
34183
- return await endpoint.featureCollections
34184
- .then((collections) => {
34185
- return collections.length
34186
- ? endpoint.getCollectionItems(collections[0], limit)
34187
- : null;
34188
- })
34189
- .catch(() => {
34190
- throw new Error(`ogc.unreachable.unknown`);
34191
- });
33788
+ try {
33789
+ const id = await this.getTargetCollectionId(endpoint, collectionId);
33790
+ return await endpoint.getCollectionItems(id, limit);
33791
+ }
33792
+ catch {
33793
+ throw new Error('ogc.unreachable.unknown');
33794
+ }
34192
33795
  }
34193
33796
  async getItemsFromStacApi(url, options) {
34194
33797
  return await StacEndpoint.getItemsFromUrl(url, options)
@@ -34276,7 +33879,7 @@ class DataService {
34276
33879
  }
34277
33880
  else if (link.type === 'service' &&
34278
33881
  link.accessServiceProtocol === 'ogcFeatures') {
34279
- return from(this.getDownloadUrlsFromOgcApi(link.url.href)).pipe(switchMap$1((collectionInfo) => {
33882
+ return from(this.getDownloadUrlsFromOgcApi(link.url.href, link.name)).pipe(switchMap$1((collectionInfo) => {
34280
33883
  const isMimeTypeJson = (mimeType) => {
34281
33884
  return mimeType.toLowerCase().indexOf('json') > -1;
34282
33885
  };
@@ -35220,7 +34823,7 @@ class MdViewFacade {
35220
34823
  if (this.linkClassifier.hasUsage(link, LinkUsage.GEODATA)) {
35221
34824
  if (link.type === 'service' &&
35222
34825
  link.accessServiceProtocol === 'ogcFeatures') {
35223
- return from(this.dataService.getItemsFromOgcApi(link.url.href, 1)).pipe(map$1((collectionRecords) => {
34826
+ return from(this.dataService.getItemsFromOgcApi(link.url.href, link.name, 1)).pipe(map$1((collectionRecords) => {
35224
34827
  return collectionRecords &&
35225
34828
  collectionRecords[0] &&
35226
34829
  collectionRecords[0].geometry
@@ -35303,7 +34906,7 @@ class MdViewEffects {
35303
34906
  }
35304
34907
  return loadFullMetadataSuccess({ full: record });
35305
34908
  }), catchError((error) => of(loadFullMetadataFailure({ otherError: error.message })))));
35306
- this.loadFeatureCatalog$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), filter$1(({ full }) => full !== undefined), switchMap$1(({ full }) => this.recordsRepository.getFeatureCatalog(full)), map$1((featureCatalog) => loadFeatureCatalogSuccess({
34909
+ this.loadFeatureCatalog$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), filter$1(({ full }) => full !== undefined), switchMap$1(({ full }) => this.recordsRepository.queryLinkedFeatureCatalog(full)), map$1((featureCatalog) => loadFeatureCatalogSuccess({
35307
34910
  datasetCatalog: featureCatalog,
35308
34911
  })), catchError((error) => of(loadFeatureCatalogFailure({
35309
34912
  error: error.message,
@@ -36962,6 +36565,15 @@ const DEFAULT_CONFIGURATION = {
36962
36565
  labelKey: marker('editor.record.form.field.title'),
36963
36566
  },
36964
36567
  },
36568
+ {
36569
+ model: 'flags',
36570
+ modelSpecifier: 'flags:IS_REFERENCE_DATASET',
36571
+ formFieldConfig: {
36572
+ labelKey: marker('editor.record.form.field.flags.IS_REFERENCE_DATASET'),
36573
+ hintKey: marker('editor.record.form.field.hint.flags.IS_REFERENCE_DATASET'),
36574
+ },
36575
+ hidden: '${record.kind != "dataset" || !user.isAdmin}',
36576
+ },
36965
36577
  {
36966
36578
  model: 'abstract',
36967
36579
  formFieldConfig: {
@@ -38017,7 +37629,7 @@ class GenericKeywordsComponent {
38017
37629
  this.deletedKeyword.emit(keyword);
38018
37630
  }
38019
37631
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GenericKeywordsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
38020
- 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 }); }
37632
+ 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 }); }
38021
37633
  }
38022
37634
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GenericKeywordsComponent, decorators: [{
38023
37635
  type: Component,
@@ -38609,7 +38221,7 @@ class FormFieldContactsForResourceComponent {
38609
38221
  provideNgIconsConfig({
38610
38222
  size: '1.5rem',
38611
38223
  }),
38612
- ], 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 }); }
38224
+ ], 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 }); }
38613
38225
  }
38614
38226
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FormFieldContactsForResourceComponent, decorators: [{
38615
38227
  type: Component,
@@ -38709,7 +38321,7 @@ class FormFieldContactsComponent {
38709
38321
  this.subscription.unsubscribe();
38710
38322
  }
38711
38323
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FormFieldContactsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
38712
- 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 }); }
38324
+ 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 }); }
38713
38325
  }
38714
38326
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FormFieldContactsComponent, decorators: [{
38715
38327
  type: Component,
@@ -39062,7 +38674,7 @@ class OnlineServiceResourceInputComponent {
39062
38674
  provideNgIconsConfig({
39063
38675
  size: '1.5em',
39064
38676
  }),
39065
- ], 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 }); }
38677
+ ], 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 }); }
39066
38678
  }
39067
38679
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: OnlineServiceResourceInputComponent, decorators: [{
39068
38680
  type: Component,
@@ -39846,6 +39458,56 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
39846
39458
  type: Output
39847
39459
  }] } });
39848
39460
 
39461
+ class FormFieldRecordFlagComponent {
39462
+ constructor() {
39463
+ this.flagName = input(...(ngDevMode ? [undefined, { debugName: "flagName" }] : []));
39464
+ this.labelKey = input(...(ngDevMode ? [undefined, { debugName: "labelKey" }] : []));
39465
+ this.hintKey = input(...(ngDevMode ? [undefined, { debugName: "hintKey" }] : []));
39466
+ this.editorFacade = inject(EditorFacade);
39467
+ this.repository = inject(RecordsRepositoryInterface);
39468
+ this.notificationsService = inject(NotificationsService);
39469
+ this.translateService = inject(TranslateService);
39470
+ this.record = toSignal(this.editorFacade.record$);
39471
+ this.recordKind = toSignal(this.editorFacade.record$.pipe(map$1((record) => record.kind)));
39472
+ this.initialValue$ = this.editorFacade.record$.pipe(filter((record) => !!record), switchMap$1((record) => this.repository.getRecordFlag(record.uniqueIdentifier, this.flagName())));
39473
+ }
39474
+ onToggle(enabled) {
39475
+ if (this.record() === null) {
39476
+ return;
39477
+ }
39478
+ this.repository
39479
+ .setRecordFlag(this.record().uniqueIdentifier, this.flagName(), enabled)
39480
+ .subscribe({
39481
+ next: () => {
39482
+ // do nothing
39483
+ },
39484
+ error: (error) => {
39485
+ this.notificationsService.showNotification({
39486
+ type: 'error',
39487
+ title: this.translateService.instant('editor.record.flag.saveError.title'),
39488
+ text: `${this.translateService.instant('editor.record.flag.saveError.body', {
39489
+ recordKind: this.recordKind(),
39490
+ })} ${error.message}`,
39491
+ }, undefined, error);
39492
+ },
39493
+ });
39494
+ }
39495
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FormFieldRecordFlagComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
39496
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FormFieldRecordFlagComponent, isStandalone: true, selector: "gn-ui-form-field-record-flag", inputs: { flagName: { classPropertyName: "flagName", publicName: "flagName", isSignal: true, isRequired: false, transformFunction: null }, labelKey: { classPropertyName: "labelKey", publicName: "labelKey", isSignal: true, isRequired: false, transformFunction: null }, hintKey: { classPropertyName: "hintKey", publicName: "hintKey", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"flex-1 flex justify-between items-center\">\n <gn-ui-check-toggle\n [label]=\"labelKey() | translate\"\n [value]=\"initialValue$ | async\"\n (toggled)=\"onToggle($event)\"\n [attr.data-cy]=\"'record-flag-toggle-' + flagName()\"\n ></gn-ui-check-toggle>\n\n @if (hintKey()) {\n <ng-icon\n name=\"matHelpOutline\"\n size=\"16\"\n class=\"m-2\"\n [matTooltip]=\"hintKey() | translate\"\n matTooltipPosition=\"above\"\n ></ng-icon>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: CheckToggleComponent, selector: "gn-ui-check-toggle", inputs: ["title", "label", "value", "color"], outputs: ["toggled"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], viewProviders: [
39497
+ provideIcons({
39498
+ matHelpOutline,
39499
+ }),
39500
+ ], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
39501
+ }
39502
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FormFieldRecordFlagComponent, decorators: [{
39503
+ type: Component,
39504
+ args: [{ selector: 'gn-ui-form-field-record-flag', standalone: true, imports: [AsyncPipe, CheckToggleComponent, TranslatePipe, NgIcon, MatTooltip], viewProviders: [
39505
+ provideIcons({
39506
+ matHelpOutline,
39507
+ }),
39508
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex-1 flex justify-between items-center\">\n <gn-ui-check-toggle\n [label]=\"labelKey() | translate\"\n [value]=\"initialValue$ | async\"\n (toggled)=\"onToggle($event)\"\n [attr.data-cy]=\"'record-flag-toggle-' + flagName()\"\n ></gn-ui-check-toggle>\n\n @if (hintKey()) {\n <ng-icon\n name=\"matHelpOutline\"\n size=\"16\"\n class=\"m-2\"\n [matTooltip]=\"hintKey() | translate\"\n matTooltipPosition=\"above\"\n ></ng-icon>\n }\n</div>\n" }]
39509
+ }], propDecorators: { flagName: [{ type: i0.Input, args: [{ isSignal: true, alias: "flagName", required: false }] }], labelKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelKey", required: false }] }], hintKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintKey", required: false }] }] } });
39510
+
39849
39511
  class FormFieldComponent {
39850
39512
  constructor() {
39851
39513
  this.valueChange = new EventEmitter();
@@ -39864,6 +39526,7 @@ class FormFieldComponent {
39864
39526
  this.model === 'legalConstraints' ||
39865
39527
  this.model === 'securityConstraints' ||
39866
39528
  this.model === 'otherConstraints' ||
39529
+ this.model === 'flags' ||
39867
39530
  this.componentName === 'form-field-constraints-shortcuts');
39868
39531
  }
39869
39532
  get valueAsString() {
@@ -39903,6 +39566,9 @@ class FormFieldComponent {
39903
39566
  const identifiers = this.value;
39904
39567
  return identifiers?.[0]?.code || '';
39905
39568
  }
39569
+ get recordFlag() {
39570
+ return this.modelSpecifier?.replace(/^flags:/, '');
39571
+ }
39906
39572
  handleResourceIdentifierChange(code) {
39907
39573
  const identifiers = this.value;
39908
39574
  if (!code) {
@@ -39920,7 +39586,7 @@ class FormFieldComponent {
39920
39586
  }
39921
39587
  }
39922
39588
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FormFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
39923
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: FormFieldComponent, isStandalone: true, selector: "gn-ui-form-field", inputs: { uniqueIdentifier: "uniqueIdentifier", recordKind: "recordKind", model: "model", modelSpecifier: "modelSpecifier", componentName: "componentName", config: "config", value: "value" }, outputs: { valueChange: "valueChange" }, providers: [provideIcons({ matEditOutline, matHelpOutline })], viewQueries: [{ propertyName: "titleInput", first: true, predicate: ["titleInput"], descendants: true }], hostDirectives: [{ directive: FieldFocusDirective }], ngImport: i0, template: "<!-- TEMPORARY - disabling the open data switch -->\n<!-- <ng-container *ngIf=\"model === 'licenses'\">\n <gn-ui-form-field-open-data\n [value]=\"valueAsConstraints\"\n (valueChange)=\"valueChange.emit($event)\"\n (openDataChange)=\"toggleIsOpenData($event)\"\n ></gn-ui-form-field-open-data>\n</ng-container> -->\n<div class=\"flex flex-col h-full\">\n <ng-container *ngIf=\"withoutWrapper; else withGenericWrapper\">\n <ng-container *ngTemplateOutlet=\"fieldContent\"></ng-container>\n </ng-container>\n <ng-template #withGenericWrapper>\n <gn-ui-form-field-wrapper\n [label]=\"config.labelKey! | translate: { recordKind }\"\n [hint]=\"config.hintKey! | translate: { recordKind }\"\n >\n <ng-container *ngTemplateOutlet=\"fieldContent\"></ng-container>\n </gn-ui-form-field-wrapper>\n </ng-template>\n</div>\n\n<ng-template #fieldContent>\n <ng-container [ngSwitch]=\"model\">\n <ng-container *ngSwitchCase=\"'title'\">\n <div class=\"flex flex-row flex-start items-center gap-3 mb-[12px]\">\n <textarea\n #titleInput\n cdkTextareaAutosize\n #autosize=\"cdkTextareaAutosize\"\n cdkAutosizeMinRows=\"1\"\n data-test=\"recordTitleInput\"\n class=\"grow font-title text-3xl font-normal overflow-hidden text-black/80\"\n [value]=\"valueAsString\"\n (change)=\"valueChange.emit($event.target.value)\"\n [placeholder]=\"\n 'editor.record.form.field.title.placeholder'\n | translate: { recordKind }\n \"\n ></textarea>\n <div class=\"flex flex-row justify-between self-start mt-0.5\">\n <ng-icon\n name=\"matEditOutline\"\n size=\"16\"\n class=\"m-2 cursor-pointer\"\n (click)=\"focusTitleInput()\"\n ></ng-icon>\n <ng-icon\n *ngIf=\"config.hintKey\"\n name=\"matHelpOutline\"\n size=\"16\"\n class=\"m-2\"\n [matTooltip]=\"config.hintKey! | translate: { recordKind }\"\n matTooltipPosition=\"above\"\n ></ng-icon>\n </div>\n </div>\n </ng-container>\n <ng-container *ngSwitchCase=\"'abstract'\">\n <gn-ui-form-field-rich\n [label]=\"config.labelKey! | translate: { recordKind }\"\n [hint]=\"config.hintKey! | translate: { recordKind }\"\n [value]=\"valueAsString\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-rich>\n </ng-container>\n <ng-container *ngSwitchCase=\"'overviews'\">\n <gn-ui-form-field-overviews\n [metadataUuid]=\"uniqueIdentifier\"\n [recordKind]=\"recordKind\"\n [value]=\"valueAsOverviews\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-overviews>\n </ng-container>\n <ng-container *ngSwitchCase=\"'resourceIdentifiers'\">\n <gn-ui-form-field-simple\n [type]=\"'text'\"\n [value]=\"valueAsResourceIdentifierCode\"\n (valueChange)=\"handleResourceIdentifierChange($event)\"\n ></gn-ui-form-field-simple>\n </ng-container>\n <ng-container *ngSwitchCase=\"'resourceCreated'\">\n <gn-ui-form-field-date\n [value]=\"valueAsDate\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-date>\n </ng-container>\n <ng-container *ngSwitchCase=\"'resourceUpdated'\">\n <gn-ui-form-field-date\n [value]=\"valueAsDate\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-date>\n </ng-container>\n <ng-container *ngSwitchCase=\"'updateFrequency'\">\n <gn-ui-form-field-update-frequency\n [value]=\"valueAsUpdateFrequency\"\n (valueChange)=\"valueChange.emit($event)\"\n [recordKind]=\"recordKind\"\n ></gn-ui-form-field-update-frequency>\n </ng-container>\n <ng-container *ngSwitchCase=\"'associatedRecords'\">\n <gn-ui-form-field-associated-records\n [value]=\"valueAsAssociatedRecords\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-associated-records>\n </ng-container>\n <ng-container *ngSwitchCase=\"'temporalExtents'\">\n <gn-ui-form-field-temporal-extents\n [value]=\"valueAsTemporalExtents\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-temporal-extents>\n </ng-container>\n <ng-container *ngSwitchCase=\"'spatialExtents'\">\n <gn-ui-form-field-spatial-extent></gn-ui-form-field-spatial-extent>\n </ng-container>\n <ng-container *ngSwitchCase=\"'keywords'\">\n <gn-ui-form-field-keywords\n [value]=\"valueAsKeywords\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-keywords>\n </ng-container>\n <ng-container *ngSwitchCase=\"'topics'\">\n <gn-ui-form-field-topics\n [value]=\"valueAsTopics\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-topics>\n </ng-container>\n <ng-container *ngSwitchCase=\"'licenses'\">\n <gn-ui-form-field-license\n [label]=\"config.labelKey! | translate: { recordKind }\"\n [recordLicences]=\"valueAsConstraints\"\n (recordLicencesChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-license>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'legalConstraints'\">\n <ng-container *ngTemplateOutlet=\"formFieldConstraints\"></ng-container>\n </ng-container>\n <ng-container *ngSwitchCase=\"'securityConstraints'\">\n <ng-container *ngTemplateOutlet=\"formFieldConstraints\"></ng-container>\n </ng-container>\n <ng-container *ngSwitchCase=\"'otherConstraints'\">\n <ng-container *ngTemplateOutlet=\"formFieldConstraints\"></ng-container\n ></ng-container>\n <ng-template #formFieldConstraints>\n <gn-ui-form-field-constraints\n [label]=\"config.labelKey\"\n [value]=\"valueAsConstraints\"\n [constraintType]=\"model\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-constraints\n ></ng-template>\n\n <ng-container *ngSwitchCase=\"'contactsForResource'\">\n <gn-ui-form-field-contacts-for-resource\n [value]=\"valueAsIndividuals\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-contacts-for-resource>\n </ng-container>\n <ng-container *ngSwitchCase=\"'contacts'\">\n <gn-ui-form-field-contacts\n [value]=\"valueAsIndividuals\"\n [modelSpecifier]=\"modelSpecifier\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-contacts>\n </ng-container>\n <ng-container *ngSwitchCase=\"'onlineResources'\">\n <gn-ui-form-field-online-resources\n *ngIf=\"modelSpecifier === 'onlineResourceType:!link'\"\n [metadataUuid]=\"uniqueIdentifier\"\n [value]=\"valueAsOnlineResources\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-online-resources>\n </ng-container>\n <ng-container *ngSwitchCase=\"'onlineResources'\">\n <gn-ui-form-field-online-link-resources\n *ngIf=\"modelSpecifier === 'onlineResourceType:link'\"\n [metadataUuid]=\"uniqueIdentifier\"\n [value]=\"valueAsOnlineResources\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-online-link-resources>\n </ng-container>\n <ng-container *ngSwitchCase=\"'onlineResources'\">\n <gn-ui-form-field-online-single-link-resource\n *ngIf=\"modelSpecifier === 'onlineResourceType:singleLink'\"\n [value]=\"valueAsOnlineResources\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-online-single-link-resource>\n </ng-container>\n </ng-container>\n\n <ng-container *ngIf=\"!model\">\n <ng-container [ngSwitch]=\"componentName\">\n <ng-container *ngSwitchCase=\"'form-field-constraints-shortcuts'\">\n <gn-ui-form-field-constraints-shortcuts></gn-ui-form-field-constraints-shortcuts>\n </ng-container>\n <ng-container *ngSwitchCase=\"'form-field-spatial-toggle'\">\n <gn-ui-form-field-spatial-toggle></gn-ui-form-field-spatial-toggle>\n </ng-container>\n </ng-container>\n </ng-container>\n</ng-template>\n", styles: [":host{scroll-margin-top:90px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i1$5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: FormFieldWrapperComponent, selector: "gn-ui-form-field-wrapper", inputs: ["label", "hint"] }, { kind: "component", type: FormFieldLicenseComponent, selector: "gn-ui-form-field-license", inputs: ["label", "recordLicences"], outputs: ["recordLicencesChange"] }, { kind: "component", type: FormFieldDateComponent, selector: "gn-ui-form-field-date", inputs: ["value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldUpdateFrequencyComponent, selector: "gn-ui-form-field-update-frequency", inputs: ["value", "recordKind"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldAssociatedRecordsComponent, selector: "gn-ui-form-field-associated-records", inputs: ["value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldTemporalExtentsComponent, selector: "gn-ui-form-field-temporal-extents", inputs: ["value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldSimpleComponent, selector: "gn-ui-form-field-simple", inputs: ["type", "readonly", "invalid", "placeholder", "value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldRichComponent, selector: "gn-ui-form-field-rich", inputs: ["label", "hint", "placeholder", "value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldSpatialExtentComponent, selector: "gn-ui-form-field-spatial-extent" }, { kind: "component", type: FormFieldKeywordsComponent, selector: "gn-ui-form-field-keywords", inputs: ["value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldOverviewsComponent, selector: "gn-ui-form-field-overviews", inputs: ["metadataUuid", "recordKind", "value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldContactsForResourceComponent, selector: "gn-ui-form-field-contacts-for-resource", inputs: ["value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldOnlineResourcesComponent, selector: "gn-ui-form-field-online-resources", inputs: ["metadataUuid", "value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldOnlineLinkResourcesComponent, selector: "gn-ui-form-field-online-link-resources", inputs: ["metadataUuid", "value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldOnlineSingleLinkResourceComponent, selector: "gn-ui-form-field-online-single-link-resource", inputs: ["value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldContactsComponent, selector: "gn-ui-form-field-contacts", inputs: ["value", "modelSpecifier"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldConstraintsComponent, selector: "gn-ui-form-field-constraints", inputs: ["label", "value", "constraintType"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldConstraintsShortcutsComponent, selector: "gn-ui-form-field-constraints-shortcuts" }, { kind: "component", type: FormFieldSpatialToggleComponent, selector: "gn-ui-form-field-spatial-toggle" }, { kind: "component", type: FormFieldTopicsComponent, selector: "gn-ui-form-field-topics", inputs: ["value"], outputs: ["valueChange"] }, { kind: "ngmodule", type: TextFieldModule }, { kind: "directive", type: i4$1.CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
39589
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FormFieldComponent, isStandalone: true, selector: "gn-ui-form-field", inputs: { uniqueIdentifier: "uniqueIdentifier", recordKind: "recordKind", model: "model", modelSpecifier: "modelSpecifier", componentName: "componentName", config: "config", value: "value" }, outputs: { valueChange: "valueChange" }, providers: [provideIcons({ matEditOutline, matHelpOutline })], viewQueries: [{ propertyName: "titleInput", first: true, predicate: ["titleInput"], descendants: true }], hostDirectives: [{ directive: FieldFocusDirective }], ngImport: i0, template: "<!-- TEMPORARY - disabling the open data switch -->\n<!-- <ng-container *ngIf=\"model === 'licenses'\">\n <gn-ui-form-field-open-data\n [value]=\"valueAsConstraints\"\n (valueChange)=\"valueChange.emit($event)\"\n (openDataChange)=\"toggleIsOpenData($event)\"\n ></gn-ui-form-field-open-data>\n</ng-container> -->\n<div class=\"flex flex-col h-full\">\n @if (withoutWrapper) {\n <ng-container *ngTemplateOutlet=\"fieldContent\"></ng-container>\n } @else {\n <gn-ui-form-field-wrapper\n [label]=\"config.labelKey! | translate: { recordKind }\"\n [hint]=\"config.hintKey! | translate: { recordKind }\"\n >\n <ng-container *ngTemplateOutlet=\"fieldContent\"></ng-container>\n </gn-ui-form-field-wrapper>\n }\n</div>\n\n<ng-template #fieldContent>\n @switch (model) {\n @case ('title') {\n <div class=\"flex flex-row flex-start items-center gap-3\">\n <textarea\n #titleInput\n cdkTextareaAutosize\n #autosize=\"cdkTextareaAutosize\"\n cdkAutosizeMinRows=\"1\"\n data-test=\"recordTitleInput\"\n class=\"grow font-title text-3xl font-normal overflow-hidden text-black/80\"\n [value]=\"valueAsString\"\n (change)=\"valueChange.emit($event.target.value)\"\n [placeholder]=\"\n 'editor.record.form.field.title.placeholder'\n | translate: { recordKind }\n \"\n ></textarea>\n <div class=\"flex flex-row justify-between self-start mt-0.5\">\n <ng-icon\n name=\"matEditOutline\"\n size=\"16\"\n class=\"m-2 cursor-pointer\"\n (click)=\"focusTitleInput()\"\n ></ng-icon>\n @if (config.hintKey) {\n <ng-icon\n name=\"matHelpOutline\"\n size=\"16\"\n class=\"m-2\"\n [matTooltip]=\"config.hintKey! | translate: { recordKind }\"\n matTooltipPosition=\"above\"\n ></ng-icon>\n }\n </div>\n </div>\n }\n @case ('abstract') {\n <gn-ui-form-field-rich\n [label]=\"config.labelKey! | translate: { recordKind }\"\n [hint]=\"config.hintKey! | translate: { recordKind }\"\n [value]=\"valueAsString\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-rich>\n }\n @case ('overviews') {\n <gn-ui-form-field-overviews\n [metadataUuid]=\"uniqueIdentifier\"\n [recordKind]=\"recordKind\"\n [value]=\"valueAsOverviews\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-overviews>\n }\n @case ('resourceIdentifiers') {\n <gn-ui-form-field-simple\n [type]=\"'text'\"\n [value]=\"valueAsResourceIdentifierCode\"\n (valueChange)=\"handleResourceIdentifierChange($event)\"\n ></gn-ui-form-field-simple>\n }\n @case ('resourceCreated') {\n <gn-ui-form-field-date\n [value]=\"valueAsDate\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-date>\n }\n @case ('resourceUpdated') {\n <gn-ui-form-field-date\n [value]=\"valueAsDate\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-date>\n }\n @case ('updateFrequency') {\n <gn-ui-form-field-update-frequency\n [value]=\"valueAsUpdateFrequency\"\n (valueChange)=\"valueChange.emit($event)\"\n [recordKind]=\"recordKind\"\n ></gn-ui-form-field-update-frequency>\n }\n @case ('associatedRecords') {\n <gn-ui-form-field-associated-records\n [value]=\"valueAsAssociatedRecords\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-associated-records>\n }\n @case ('temporalExtents') {\n <gn-ui-form-field-temporal-extents\n [value]=\"valueAsTemporalExtents\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-temporal-extents>\n }\n @case ('spatialExtents') {\n <gn-ui-form-field-spatial-extent></gn-ui-form-field-spatial-extent>\n }\n @case ('keywords') {\n <gn-ui-form-field-keywords\n [value]=\"valueAsKeywords\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-keywords>\n }\n @case ('topics') {\n <gn-ui-form-field-topics\n [value]=\"valueAsTopics\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-topics>\n }\n @case ('licenses') {\n <gn-ui-form-field-license\n [label]=\"config.labelKey! | translate: { recordKind }\"\n [recordLicences]=\"valueAsConstraints\"\n (recordLicencesChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-license>\n }\n\n <!-- TODO: merge these cases together when migrated to angular 21 -->\n @case ('legalConstraints') {\n <gn-ui-form-field-constraints\n [label]=\"config.labelKey\"\n [value]=\"valueAsConstraints\"\n [constraintType]=\"model\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-constraints>\n }\n @case ('securityConstraints') {\n <gn-ui-form-field-constraints\n [label]=\"config.labelKey\"\n [value]=\"valueAsConstraints\"\n [constraintType]=\"model\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-constraints>\n }\n @case ('otherConstraints') {\n <gn-ui-form-field-constraints\n [label]=\"config.labelKey\"\n [value]=\"valueAsConstraints\"\n [constraintType]=\"model\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-constraints>\n }\n\n @case ('contactsForResource') {\n <gn-ui-form-field-contacts-for-resource\n [value]=\"valueAsIndividuals\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-contacts-for-resource>\n }\n @case ('contacts') {\n <gn-ui-form-field-contacts\n [value]=\"valueAsIndividuals\"\n [modelSpecifier]=\"modelSpecifier\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-contacts>\n }\n @case ('onlineResources') {\n @if (modelSpecifier === 'onlineResourceType:!link') {\n <gn-ui-form-field-online-resources\n [metadataUuid]=\"uniqueIdentifier\"\n [value]=\"valueAsOnlineResources\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-online-resources>\n } @else if (modelSpecifier === 'onlineResourceType:link') {\n <gn-ui-form-field-online-link-resources\n [metadataUuid]=\"uniqueIdentifier\"\n [value]=\"valueAsOnlineResources\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-online-link-resources>\n } @else if (modelSpecifier === 'onlineResourceType:singleLink') {\n <gn-ui-form-field-online-single-link-resource\n [value]=\"valueAsOnlineResources\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-online-single-link-resource>\n }\n }\n @case ('flags') {\n <gn-ui-form-field-record-flag\n [flagName]=\"recordFlag\"\n [labelKey]=\"config.labelKey\"\n [hintKey]=\"config.hintKey\"\n ></gn-ui-form-field-record-flag>\n }\n }\n\n @if (!model) {\n @switch (componentName) {\n @case ('form-field-constraints-shortcuts') {\n <gn-ui-form-field-constraints-shortcuts></gn-ui-form-field-constraints-shortcuts>\n }\n @case ('form-field-spatial-toggle') {\n <gn-ui-form-field-spatial-toggle></gn-ui-form-field-spatial-toggle>\n }\n }\n }\n</ng-template>\n", styles: [":host{scroll-margin-top:90px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i1$5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: FormFieldWrapperComponent, selector: "gn-ui-form-field-wrapper", inputs: ["label", "hint"] }, { kind: "component", type: FormFieldLicenseComponent, selector: "gn-ui-form-field-license", inputs: ["label", "recordLicences"], outputs: ["recordLicencesChange"] }, { kind: "component", type: FormFieldDateComponent, selector: "gn-ui-form-field-date", inputs: ["value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldUpdateFrequencyComponent, selector: "gn-ui-form-field-update-frequency", inputs: ["value", "recordKind"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldAssociatedRecordsComponent, selector: "gn-ui-form-field-associated-records", inputs: ["value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldTemporalExtentsComponent, selector: "gn-ui-form-field-temporal-extents", inputs: ["value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldSimpleComponent, selector: "gn-ui-form-field-simple", inputs: ["type", "readonly", "invalid", "placeholder", "value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldRichComponent, selector: "gn-ui-form-field-rich", inputs: ["label", "hint", "placeholder", "value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldSpatialExtentComponent, selector: "gn-ui-form-field-spatial-extent" }, { kind: "component", type: FormFieldKeywordsComponent, selector: "gn-ui-form-field-keywords", inputs: ["value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldOverviewsComponent, selector: "gn-ui-form-field-overviews", inputs: ["metadataUuid", "recordKind", "value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldContactsForResourceComponent, selector: "gn-ui-form-field-contacts-for-resource", inputs: ["value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldOnlineResourcesComponent, selector: "gn-ui-form-field-online-resources", inputs: ["metadataUuid", "value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldOnlineLinkResourcesComponent, selector: "gn-ui-form-field-online-link-resources", inputs: ["metadataUuid", "value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldOnlineSingleLinkResourceComponent, selector: "gn-ui-form-field-online-single-link-resource", inputs: ["value"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldContactsComponent, selector: "gn-ui-form-field-contacts", inputs: ["value", "modelSpecifier"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldConstraintsComponent, selector: "gn-ui-form-field-constraints", inputs: ["label", "value", "constraintType"], outputs: ["valueChange"] }, { kind: "component", type: FormFieldConstraintsShortcutsComponent, selector: "gn-ui-form-field-constraints-shortcuts" }, { kind: "component", type: FormFieldSpatialToggleComponent, selector: "gn-ui-form-field-spatial-toggle" }, { kind: "component", type: FormFieldTopicsComponent, selector: "gn-ui-form-field-topics", inputs: ["value"], outputs: ["valueChange"] }, { kind: "ngmodule", type: TextFieldModule }, { kind: "directive", type: i4$1.CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: FormFieldRecordFlagComponent, selector: "gn-ui-form-field-record-flag", inputs: ["flagName", "labelKey", "hintKey"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
39924
39590
  }
39925
39591
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FormFieldComponent, decorators: [{
39926
39592
  type: Component,
@@ -39950,7 +39616,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
39950
39616
  FormFieldTopicsComponent,
39951
39617
  TextFieldModule,
39952
39618
  NgIconComponent,
39953
- ], providers: [provideIcons({ matEditOutline, matHelpOutline })], hostDirectives: [FieldFocusDirective], template: "<!-- TEMPORARY - disabling the open data switch -->\n<!-- <ng-container *ngIf=\"model === 'licenses'\">\n <gn-ui-form-field-open-data\n [value]=\"valueAsConstraints\"\n (valueChange)=\"valueChange.emit($event)\"\n (openDataChange)=\"toggleIsOpenData($event)\"\n ></gn-ui-form-field-open-data>\n</ng-container> -->\n<div class=\"flex flex-col h-full\">\n <ng-container *ngIf=\"withoutWrapper; else withGenericWrapper\">\n <ng-container *ngTemplateOutlet=\"fieldContent\"></ng-container>\n </ng-container>\n <ng-template #withGenericWrapper>\n <gn-ui-form-field-wrapper\n [label]=\"config.labelKey! | translate: { recordKind }\"\n [hint]=\"config.hintKey! | translate: { recordKind }\"\n >\n <ng-container *ngTemplateOutlet=\"fieldContent\"></ng-container>\n </gn-ui-form-field-wrapper>\n </ng-template>\n</div>\n\n<ng-template #fieldContent>\n <ng-container [ngSwitch]=\"model\">\n <ng-container *ngSwitchCase=\"'title'\">\n <div class=\"flex flex-row flex-start items-center gap-3 mb-[12px]\">\n <textarea\n #titleInput\n cdkTextareaAutosize\n #autosize=\"cdkTextareaAutosize\"\n cdkAutosizeMinRows=\"1\"\n data-test=\"recordTitleInput\"\n class=\"grow font-title text-3xl font-normal overflow-hidden text-black/80\"\n [value]=\"valueAsString\"\n (change)=\"valueChange.emit($event.target.value)\"\n [placeholder]=\"\n 'editor.record.form.field.title.placeholder'\n | translate: { recordKind }\n \"\n ></textarea>\n <div class=\"flex flex-row justify-between self-start mt-0.5\">\n <ng-icon\n name=\"matEditOutline\"\n size=\"16\"\n class=\"m-2 cursor-pointer\"\n (click)=\"focusTitleInput()\"\n ></ng-icon>\n <ng-icon\n *ngIf=\"config.hintKey\"\n name=\"matHelpOutline\"\n size=\"16\"\n class=\"m-2\"\n [matTooltip]=\"config.hintKey! | translate: { recordKind }\"\n matTooltipPosition=\"above\"\n ></ng-icon>\n </div>\n </div>\n </ng-container>\n <ng-container *ngSwitchCase=\"'abstract'\">\n <gn-ui-form-field-rich\n [label]=\"config.labelKey! | translate: { recordKind }\"\n [hint]=\"config.hintKey! | translate: { recordKind }\"\n [value]=\"valueAsString\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-rich>\n </ng-container>\n <ng-container *ngSwitchCase=\"'overviews'\">\n <gn-ui-form-field-overviews\n [metadataUuid]=\"uniqueIdentifier\"\n [recordKind]=\"recordKind\"\n [value]=\"valueAsOverviews\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-overviews>\n </ng-container>\n <ng-container *ngSwitchCase=\"'resourceIdentifiers'\">\n <gn-ui-form-field-simple\n [type]=\"'text'\"\n [value]=\"valueAsResourceIdentifierCode\"\n (valueChange)=\"handleResourceIdentifierChange($event)\"\n ></gn-ui-form-field-simple>\n </ng-container>\n <ng-container *ngSwitchCase=\"'resourceCreated'\">\n <gn-ui-form-field-date\n [value]=\"valueAsDate\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-date>\n </ng-container>\n <ng-container *ngSwitchCase=\"'resourceUpdated'\">\n <gn-ui-form-field-date\n [value]=\"valueAsDate\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-date>\n </ng-container>\n <ng-container *ngSwitchCase=\"'updateFrequency'\">\n <gn-ui-form-field-update-frequency\n [value]=\"valueAsUpdateFrequency\"\n (valueChange)=\"valueChange.emit($event)\"\n [recordKind]=\"recordKind\"\n ></gn-ui-form-field-update-frequency>\n </ng-container>\n <ng-container *ngSwitchCase=\"'associatedRecords'\">\n <gn-ui-form-field-associated-records\n [value]=\"valueAsAssociatedRecords\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-associated-records>\n </ng-container>\n <ng-container *ngSwitchCase=\"'temporalExtents'\">\n <gn-ui-form-field-temporal-extents\n [value]=\"valueAsTemporalExtents\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-temporal-extents>\n </ng-container>\n <ng-container *ngSwitchCase=\"'spatialExtents'\">\n <gn-ui-form-field-spatial-extent></gn-ui-form-field-spatial-extent>\n </ng-container>\n <ng-container *ngSwitchCase=\"'keywords'\">\n <gn-ui-form-field-keywords\n [value]=\"valueAsKeywords\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-keywords>\n </ng-container>\n <ng-container *ngSwitchCase=\"'topics'\">\n <gn-ui-form-field-topics\n [value]=\"valueAsTopics\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-topics>\n </ng-container>\n <ng-container *ngSwitchCase=\"'licenses'\">\n <gn-ui-form-field-license\n [label]=\"config.labelKey! | translate: { recordKind }\"\n [recordLicences]=\"valueAsConstraints\"\n (recordLicencesChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-license>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'legalConstraints'\">\n <ng-container *ngTemplateOutlet=\"formFieldConstraints\"></ng-container>\n </ng-container>\n <ng-container *ngSwitchCase=\"'securityConstraints'\">\n <ng-container *ngTemplateOutlet=\"formFieldConstraints\"></ng-container>\n </ng-container>\n <ng-container *ngSwitchCase=\"'otherConstraints'\">\n <ng-container *ngTemplateOutlet=\"formFieldConstraints\"></ng-container\n ></ng-container>\n <ng-template #formFieldConstraints>\n <gn-ui-form-field-constraints\n [label]=\"config.labelKey\"\n [value]=\"valueAsConstraints\"\n [constraintType]=\"model\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-constraints\n ></ng-template>\n\n <ng-container *ngSwitchCase=\"'contactsForResource'\">\n <gn-ui-form-field-contacts-for-resource\n [value]=\"valueAsIndividuals\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-contacts-for-resource>\n </ng-container>\n <ng-container *ngSwitchCase=\"'contacts'\">\n <gn-ui-form-field-contacts\n [value]=\"valueAsIndividuals\"\n [modelSpecifier]=\"modelSpecifier\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-contacts>\n </ng-container>\n <ng-container *ngSwitchCase=\"'onlineResources'\">\n <gn-ui-form-field-online-resources\n *ngIf=\"modelSpecifier === 'onlineResourceType:!link'\"\n [metadataUuid]=\"uniqueIdentifier\"\n [value]=\"valueAsOnlineResources\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-online-resources>\n </ng-container>\n <ng-container *ngSwitchCase=\"'onlineResources'\">\n <gn-ui-form-field-online-link-resources\n *ngIf=\"modelSpecifier === 'onlineResourceType:link'\"\n [metadataUuid]=\"uniqueIdentifier\"\n [value]=\"valueAsOnlineResources\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-online-link-resources>\n </ng-container>\n <ng-container *ngSwitchCase=\"'onlineResources'\">\n <gn-ui-form-field-online-single-link-resource\n *ngIf=\"modelSpecifier === 'onlineResourceType:singleLink'\"\n [value]=\"valueAsOnlineResources\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-online-single-link-resource>\n </ng-container>\n </ng-container>\n\n <ng-container *ngIf=\"!model\">\n <ng-container [ngSwitch]=\"componentName\">\n <ng-container *ngSwitchCase=\"'form-field-constraints-shortcuts'\">\n <gn-ui-form-field-constraints-shortcuts></gn-ui-form-field-constraints-shortcuts>\n </ng-container>\n <ng-container *ngSwitchCase=\"'form-field-spatial-toggle'\">\n <gn-ui-form-field-spatial-toggle></gn-ui-form-field-spatial-toggle>\n </ng-container>\n </ng-container>\n </ng-container>\n</ng-template>\n", styles: [":host{scroll-margin-top:90px}\n"] }]
39619
+ FormFieldRecordFlagComponent,
39620
+ ], providers: [provideIcons({ matEditOutline, matHelpOutline })], hostDirectives: [FieldFocusDirective], template: "<!-- TEMPORARY - disabling the open data switch -->\n<!-- <ng-container *ngIf=\"model === 'licenses'\">\n <gn-ui-form-field-open-data\n [value]=\"valueAsConstraints\"\n (valueChange)=\"valueChange.emit($event)\"\n (openDataChange)=\"toggleIsOpenData($event)\"\n ></gn-ui-form-field-open-data>\n</ng-container> -->\n<div class=\"flex flex-col h-full\">\n @if (withoutWrapper) {\n <ng-container *ngTemplateOutlet=\"fieldContent\"></ng-container>\n } @else {\n <gn-ui-form-field-wrapper\n [label]=\"config.labelKey! | translate: { recordKind }\"\n [hint]=\"config.hintKey! | translate: { recordKind }\"\n >\n <ng-container *ngTemplateOutlet=\"fieldContent\"></ng-container>\n </gn-ui-form-field-wrapper>\n }\n</div>\n\n<ng-template #fieldContent>\n @switch (model) {\n @case ('title') {\n <div class=\"flex flex-row flex-start items-center gap-3\">\n <textarea\n #titleInput\n cdkTextareaAutosize\n #autosize=\"cdkTextareaAutosize\"\n cdkAutosizeMinRows=\"1\"\n data-test=\"recordTitleInput\"\n class=\"grow font-title text-3xl font-normal overflow-hidden text-black/80\"\n [value]=\"valueAsString\"\n (change)=\"valueChange.emit($event.target.value)\"\n [placeholder]=\"\n 'editor.record.form.field.title.placeholder'\n | translate: { recordKind }\n \"\n ></textarea>\n <div class=\"flex flex-row justify-between self-start mt-0.5\">\n <ng-icon\n name=\"matEditOutline\"\n size=\"16\"\n class=\"m-2 cursor-pointer\"\n (click)=\"focusTitleInput()\"\n ></ng-icon>\n @if (config.hintKey) {\n <ng-icon\n name=\"matHelpOutline\"\n size=\"16\"\n class=\"m-2\"\n [matTooltip]=\"config.hintKey! | translate: { recordKind }\"\n matTooltipPosition=\"above\"\n ></ng-icon>\n }\n </div>\n </div>\n }\n @case ('abstract') {\n <gn-ui-form-field-rich\n [label]=\"config.labelKey! | translate: { recordKind }\"\n [hint]=\"config.hintKey! | translate: { recordKind }\"\n [value]=\"valueAsString\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-rich>\n }\n @case ('overviews') {\n <gn-ui-form-field-overviews\n [metadataUuid]=\"uniqueIdentifier\"\n [recordKind]=\"recordKind\"\n [value]=\"valueAsOverviews\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-overviews>\n }\n @case ('resourceIdentifiers') {\n <gn-ui-form-field-simple\n [type]=\"'text'\"\n [value]=\"valueAsResourceIdentifierCode\"\n (valueChange)=\"handleResourceIdentifierChange($event)\"\n ></gn-ui-form-field-simple>\n }\n @case ('resourceCreated') {\n <gn-ui-form-field-date\n [value]=\"valueAsDate\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-date>\n }\n @case ('resourceUpdated') {\n <gn-ui-form-field-date\n [value]=\"valueAsDate\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-date>\n }\n @case ('updateFrequency') {\n <gn-ui-form-field-update-frequency\n [value]=\"valueAsUpdateFrequency\"\n (valueChange)=\"valueChange.emit($event)\"\n [recordKind]=\"recordKind\"\n ></gn-ui-form-field-update-frequency>\n }\n @case ('associatedRecords') {\n <gn-ui-form-field-associated-records\n [value]=\"valueAsAssociatedRecords\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-associated-records>\n }\n @case ('temporalExtents') {\n <gn-ui-form-field-temporal-extents\n [value]=\"valueAsTemporalExtents\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-temporal-extents>\n }\n @case ('spatialExtents') {\n <gn-ui-form-field-spatial-extent></gn-ui-form-field-spatial-extent>\n }\n @case ('keywords') {\n <gn-ui-form-field-keywords\n [value]=\"valueAsKeywords\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-keywords>\n }\n @case ('topics') {\n <gn-ui-form-field-topics\n [value]=\"valueAsTopics\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-topics>\n }\n @case ('licenses') {\n <gn-ui-form-field-license\n [label]=\"config.labelKey! | translate: { recordKind }\"\n [recordLicences]=\"valueAsConstraints\"\n (recordLicencesChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-license>\n }\n\n <!-- TODO: merge these cases together when migrated to angular 21 -->\n @case ('legalConstraints') {\n <gn-ui-form-field-constraints\n [label]=\"config.labelKey\"\n [value]=\"valueAsConstraints\"\n [constraintType]=\"model\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-constraints>\n }\n @case ('securityConstraints') {\n <gn-ui-form-field-constraints\n [label]=\"config.labelKey\"\n [value]=\"valueAsConstraints\"\n [constraintType]=\"model\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-constraints>\n }\n @case ('otherConstraints') {\n <gn-ui-form-field-constraints\n [label]=\"config.labelKey\"\n [value]=\"valueAsConstraints\"\n [constraintType]=\"model\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-constraints>\n }\n\n @case ('contactsForResource') {\n <gn-ui-form-field-contacts-for-resource\n [value]=\"valueAsIndividuals\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-contacts-for-resource>\n }\n @case ('contacts') {\n <gn-ui-form-field-contacts\n [value]=\"valueAsIndividuals\"\n [modelSpecifier]=\"modelSpecifier\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-contacts>\n }\n @case ('onlineResources') {\n @if (modelSpecifier === 'onlineResourceType:!link') {\n <gn-ui-form-field-online-resources\n [metadataUuid]=\"uniqueIdentifier\"\n [value]=\"valueAsOnlineResources\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-online-resources>\n } @else if (modelSpecifier === 'onlineResourceType:link') {\n <gn-ui-form-field-online-link-resources\n [metadataUuid]=\"uniqueIdentifier\"\n [value]=\"valueAsOnlineResources\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-online-link-resources>\n } @else if (modelSpecifier === 'onlineResourceType:singleLink') {\n <gn-ui-form-field-online-single-link-resource\n [value]=\"valueAsOnlineResources\"\n (valueChange)=\"valueChange.emit($event)\"\n ></gn-ui-form-field-online-single-link-resource>\n }\n }\n @case ('flags') {\n <gn-ui-form-field-record-flag\n [flagName]=\"recordFlag\"\n [labelKey]=\"config.labelKey\"\n [hintKey]=\"config.hintKey\"\n ></gn-ui-form-field-record-flag>\n }\n }\n\n @if (!model) {\n @switch (componentName) {\n @case ('form-field-constraints-shortcuts') {\n <gn-ui-form-field-constraints-shortcuts></gn-ui-form-field-constraints-shortcuts>\n }\n @case ('form-field-spatial-toggle') {\n <gn-ui-form-field-spatial-toggle></gn-ui-form-field-spatial-toggle>\n }\n }\n }\n</ng-template>\n", styles: [":host{scroll-margin-top:90px}\n"] }]
39954
39621
  }], propDecorators: { uniqueIdentifier: [{
39955
39622
  type: Input
39956
39623
  }], recordKind: [{
@@ -39981,9 +39648,13 @@ const TOKEN_OPERATOR_LOGICAL = ['==', '!='];
39981
39648
  const TOKEN_STRING_LITERAL = ["'", '"'];
39982
39649
  const TOKEN_GROUP_START = '(';
39983
39650
  const TOKEN_GROUP_END = ')';
39651
+ const TOKEN_OPERATOR_BOOLEAN = ['&&', '||'];
39652
+ const TOKEN_OPERATOR_NOT = '!';
39984
39653
  // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence
39985
39654
  const OPERATORS_BY_PRECEDENCE = [
39986
39655
  [TOKEN_OPERATOR_TERNARY_FIRST, TOKEN_OPERATOR_TERNARY_SECOND],
39656
+ TOKEN_OPERATOR_BOOLEAN,
39657
+ [TOKEN_OPERATOR_NOT],
39987
39658
  TOKEN_OPERATOR_LOGICAL,
39988
39659
  ];
39989
39660
  const ALL_OPERATORS = OPERATORS_BY_PRECEDENCE.flat();
@@ -40093,6 +39764,16 @@ function evaluateInternal(expression, currentContext) {
40093
39764
  pos++;
40094
39765
  continue;
40095
39766
  }
39767
+ // BOOLEAN LITERAL
39768
+ const isBooleanLiteral = readNext(expression, pos, 4) === 'true' ||
39769
+ readNext(expression, pos, 5) === 'false';
39770
+ if (isBooleanLiteral) {
39771
+ const literal = readNext(expression, pos, 4) === 'true' ? 'true' : 'false';
39772
+ pos += literal.length;
39773
+ const booleanLiteral = literal === 'true';
39774
+ currentEvaluator = () => booleanLiteral;
39775
+ continue;
39776
+ }
40096
39777
  // SYMBOL (NAME)
40097
39778
  const isSymbol = /[a-zA-Z]/.test(readNext(expression, pos));
40098
39779
  if (isSymbol) {
@@ -40183,6 +39864,35 @@ function evaluateInternal(expression, currentContext) {
40183
39864
  };
40184
39865
  continue;
40185
39866
  }
39867
+ // BOOLEAN OPERATORS
39868
+ const isBooleanOperator = TOKEN_OPERATOR_BOOLEAN.some((token) => readNext(expression, pos, token.length) === token);
39869
+ if (isBooleanOperator) {
39870
+ const matchingOperator = TOKEN_OPERATOR_BOOLEAN.find((token) => readNext(expression, pos, token.length) === token);
39871
+ const isAnd = matchingOperator === '&&';
39872
+ pos += matchingOperator.length;
39873
+ const leftEvaluator = currentEvaluator;
39874
+ const rightExpression = readUntilNextOperator(expression, pos, matchingOperator);
39875
+ pos += rightExpression.length;
39876
+ const rightEvaluator = evaluateInternal(rightExpression, currentContext).evaluator;
39877
+ currentEvaluator = (context) => {
39878
+ return isAnd
39879
+ ? Boolean(leftEvaluator(context) && rightEvaluator(context))
39880
+ : Boolean(leftEvaluator(context) || rightEvaluator(context));
39881
+ };
39882
+ continue;
39883
+ }
39884
+ // PREFIX OPERATORS
39885
+ const isNotOperator = readNext(expression, pos) === TOKEN_OPERATOR_NOT;
39886
+ if (isNotOperator) {
39887
+ pos++;
39888
+ const operandExpression = readUntilNextOperator(expression, pos, TOKEN_OPERATOR_NOT);
39889
+ pos += operandExpression.length;
39890
+ const operandEvaluator = evaluateInternal(operandExpression, currentContext).evaluator;
39891
+ currentEvaluator = (context) => {
39892
+ return !operandEvaluator(context);
39893
+ };
39894
+ continue;
39895
+ }
40186
39896
  // TERNARY OPERATOR
40187
39897
  const isTernary = readNext(expression, pos) === TOKEN_OPERATOR_TERNARY_FIRST;
40188
39898
  if (isTernary) {
@@ -40253,6 +39963,7 @@ class RecordFormComponent {
40253
39963
  constructor() {
40254
39964
  this.facade = inject(EditorFacade);
40255
39965
  this.notifications = inject(NotificationsService);
39966
+ this.platform = inject(PlatformServiceInterface);
40256
39967
  this.subscription = new Subscription();
40257
39968
  this.recordUniqueIdentifier$ = this.facade.record$.pipe(map$2((record) => record.uniqueIdentifier));
40258
39969
  this.recordKind$ = this.facade.record$.pipe(map$2((record) => record.kind));
@@ -40263,6 +39974,8 @@ class RecordFormComponent {
40263
39974
  this.formFields = viewChildren(FormFieldComponent, ...(ngDevMode ? [{ debugName: "formFields" }] : []));
40264
39975
  this.sectionFocusDirectives = viewChildren('sectionFocus', ...(ngDevMode ? [{ debugName: "sectionFocusDirectives" }] : []));
40265
39976
  this.recordSignal = toSignal(this.facade.record$, { requireSync: true });
39977
+ this.userSignal = toSignal(this.platform.getMe());
39978
+ this.isAdminSignal = toSignal(this.platform.isAdministrator());
40266
39979
  this.expressionCache = new Map();
40267
39980
  }
40268
39981
  focusField(model, sectionIndex) {
@@ -40287,7 +40000,7 @@ class RecordFormComponent {
40287
40000
  this.subscription.unsubscribe();
40288
40001
  }
40289
40002
  handleFieldValueChange(model, newValue) {
40290
- if (!model) {
40003
+ if (!model || model === 'flags') {
40291
40004
  return;
40292
40005
  }
40293
40006
  this.facade.updateRecordField(model, newValue);
@@ -40330,7 +40043,15 @@ ${errors.map((err) => `> ${err}`).join('\n')}`);
40330
40043
  text: 'An error happened while evaluating an expression inside the editor configuration; open the developer console for more information',
40331
40044
  });
40332
40045
  }
40333
- this.expressionCache.set(expression, computed(() => evaluator({ globals: { record: this.recordSignal() } })));
40046
+ this.expressionCache.set(expression, computed(() => evaluator({
40047
+ globals: {
40048
+ record: this.recordSignal(),
40049
+ user: {
40050
+ ...this.userSignal(),
40051
+ isAdmin: this.isAdminSignal(),
40052
+ },
40053
+ },
40054
+ })));
40334
40055
  return this.expressionCache.get(expression);
40335
40056
  }
40336
40057
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: RecordFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
@@ -40937,6 +40658,638 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
40937
40658
  args: ['gnUiSearchRouterContainer']
40938
40659
  }] } });
40939
40660
 
40661
+ const flatten = (base, obj, keepLevel = false) => Object.keys(obj).reduce((prev, curr) => {
40662
+ const path = base ? `${base}.${curr}` : curr;
40663
+ const val = obj[curr];
40664
+ if (keepLevel) {
40665
+ return { ...prev, [path]: flatten('', val) };
40666
+ }
40667
+ else if (typeof val === 'object' && !Array.isArray(val)) {
40668
+ return {
40669
+ ...prev,
40670
+ ...flatten(path, val),
40671
+ };
40672
+ }
40673
+ else {
40674
+ return { ...prev, [path]: val };
40675
+ }
40676
+ }, {});
40677
+ const checkKeys = (input, mandatory, optional) => {
40678
+ const keys = Object.keys(input);
40679
+ const missing = mandatory.filter((key) => keys.indexOf(key) === -1);
40680
+ const unrecognized = keys.filter((key) => mandatory.indexOf(key) === -1 && optional.indexOf(key) === -1);
40681
+ return {
40682
+ missing,
40683
+ unrecognized,
40684
+ };
40685
+ };
40686
+ /**
40687
+ * Only keeps recognized keys, flatten nested values (to allow having "." in keys),
40688
+ * pushes warnings and errors to the given arrays; returns null in case of error
40689
+ */
40690
+ function parseConfigSection(fullConfigObj, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors) {
40691
+ if (typeof fullConfigObj[sectionName] !== 'object') {
40692
+ if (mandatoryKeys.length === 0)
40693
+ return null;
40694
+ outErrors.push(`The [${sectionName}] mandatory section is missing.`);
40695
+ return null;
40696
+ }
40697
+ const sectionConf = fullConfigObj[sectionName];
40698
+ const keysCheck = checkKeys(sectionConf, mandatoryKeys, optionalKeys);
40699
+ if (keysCheck.missing.length) {
40700
+ // note: this is not thrown to allow merging several Errors down the line
40701
+ outErrors.push(`In the [${sectionName}] section: ${keysCheck.missing.join(', ')}`);
40702
+ return null;
40703
+ }
40704
+ else if (keysCheck.unrecognized.length) {
40705
+ outWarnings.push(`In the [${sectionName}] section: ${keysCheck.unrecognized.join(', ')}`);
40706
+ keysCheck.unrecognized.forEach((key) => delete sectionConf[key]);
40707
+ }
40708
+ return sectionConf;
40709
+ }
40710
+ /**
40711
+ * Same as parseConfigSection but keeps all keys, recognized or not
40712
+ * Also the first level is the language code (e.g. 'en')
40713
+ * This is optional
40714
+ */
40715
+ function parseTranslationsConfigSection(fullConfigObj, sectionName) {
40716
+ if (typeof fullConfigObj[sectionName] !== 'object') {
40717
+ return {};
40718
+ }
40719
+ return flatten('', fullConfigObj[sectionName], true);
40720
+ }
40721
+ /**
40722
+ * Same as parseConfigSection but handles an array of configs; this is
40723
+ * considered optional; returns null in case of error in at least one of
40724
+ * the sections
40725
+ */
40726
+ function parseMultiConfigSection(fullConfigObj, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors) {
40727
+ const sections = fullConfigObj[sectionName];
40728
+ if (!sections) {
40729
+ return [];
40730
+ }
40731
+ if (!Array.isArray(sections)) {
40732
+ outErrors.push(`Expected [[${sectionName}]] to be an array of sections.`);
40733
+ return null;
40734
+ }
40735
+ const result = sections.map((section) => parseConfigSection({ [sectionName]: section }, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors));
40736
+ if (result.some((r) => r === null)) {
40737
+ return null;
40738
+ }
40739
+ return result;
40740
+ }
40741
+ function checkMetadataLanguage(parsedConfigSection, outWarnings) {
40742
+ const lang2 = toLang2(parsedConfigSection.metadata_language.toLowerCase());
40743
+ if (lang2?.length !== 2) {
40744
+ outWarnings.push(`In the [global] section: metadata_language = "${parsedConfigSection.metadata_language}" is not a recognized ISO 639 language code`);
40745
+ }
40746
+ return parsedConfigSection;
40747
+ }
40748
+ function checkNewRecordDefaultLanguage(parsedConfigSection, outWarnings) {
40749
+ const lang2 = toLang2(parsedConfigSection.new_record_default_language.toLowerCase());
40750
+ if (!(lang2 in LANG_2_TO_3_MAPPER)) {
40751
+ outWarnings.push(`In the [editing] section: new_record_default_language = "${parsedConfigSection.new_record_default_language}" is not a recognized ISO 639 language code`);
40752
+ return {
40753
+ ...parsedConfigSection,
40754
+ new_record_default_language: undefined,
40755
+ };
40756
+ }
40757
+ return {
40758
+ ...parsedConfigSection,
40759
+ new_record_default_language: lang2,
40760
+ };
40761
+ }
40762
+ function checkNewRecordStandard(parsedConfigSection, outWarnings) {
40763
+ const standard = parsedConfigSection.new_record_standard;
40764
+ const normalizedStandard = typeof standard === 'string' ? standard.trim().toLowerCase() : null;
40765
+ if (normalizedStandard === 'iso19139' ||
40766
+ normalizedStandard === 'iso19115-3') {
40767
+ return {
40768
+ ...parsedConfigSection,
40769
+ new_record_standard: normalizedStandard,
40770
+ };
40771
+ }
40772
+ outWarnings.push(`In the [editing] section: new_record_standard = "${standard}" is not a supported metadata standard`);
40773
+ return {
40774
+ ...parsedConfigSection,
40775
+ new_record_standard: undefined,
40776
+ };
40777
+ }
40778
+
40779
+ /**
40780
+ * This loader extends the default one based on JSON files but also loads custom translations
40781
+ * defined in the app configuration
40782
+ * Implements a fallback on default lang if translated labels are empty
40783
+ */
40784
+ class FileWithOverridesTranslateLoader extends FileTranslateLoader {
40785
+ getTranslation(lang) {
40786
+ const baseLang = lang.substring(0, 2); // removing the right part of e.g. en_EN
40787
+ return super.getTranslation(baseLang).pipe(map$1((translations) => ({
40788
+ ...translations,
40789
+ ...getCustomTranslations(baseLang),
40790
+ })));
40791
+ }
40792
+ }
40793
+
40794
+ const MISSING_CONFIG_ERROR = `Application configuration was not initialized correctly.
40795
+ This error might show up in case of an invalid/malformed configuration file.
40796
+
40797
+ Note: make sure that you have called \`loadAppConfig\` from '../../../../../libs/util/app-config/src' before starting the Angular application.`;
40798
+ let globalConfig = null;
40799
+ function getGlobalConfig() {
40800
+ if (globalConfig === null)
40801
+ throw new Error(MISSING_CONFIG_ERROR);
40802
+ return globalConfig;
40803
+ }
40804
+ let themeConfig = null;
40805
+ function getThemeConfig() {
40806
+ if (themeConfig === null)
40807
+ throw new Error(MISSING_CONFIG_ERROR);
40808
+ return themeConfig;
40809
+ }
40810
+ let mapConfig = null;
40811
+ function getOptionalMapConfig() {
40812
+ return mapConfig;
40813
+ }
40814
+ let searchConfig = null;
40815
+ function getOptionalSearchConfig() {
40816
+ return searchConfig;
40817
+ }
40818
+ let editorConfig = null;
40819
+ function getOptionalEditorConfig() {
40820
+ return editorConfig;
40821
+ }
40822
+ let metadataQualityConfig = null;
40823
+ function getMetadataQualityConfig() {
40824
+ return (metadataQualityConfig ||
40825
+ {
40826
+ ENABLED: false,
40827
+ });
40828
+ }
40829
+ let customTranslations = null;
40830
+ function getCustomTranslations(langCode) {
40831
+ if (customTranslations === null)
40832
+ throw new Error(MISSING_CONFIG_ERROR);
40833
+ return langCode in customTranslations ? customTranslations[langCode] : {};
40834
+ }
40835
+ let appConfigLoaded = false;
40836
+ function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
40837
+ console.log(`[geonetwork-ui] Loading application configuration from ${configUrl}`);
40838
+ return fetch(configUrl)
40839
+ .then((resp) => {
40840
+ if (!resp.ok)
40841
+ throw new Error('Configuration file could not be loaded');
40842
+ return resp.text();
40843
+ })
40844
+ .then((conf) => {
40845
+ let parsed;
40846
+ try {
40847
+ parsed = TOML.parse(conf, { joiner: '\n', bigint: false });
40848
+ }
40849
+ catch (e) {
40850
+ throw new Error(`An error occurred when parsing the configuration file: ${e.message}`);
40851
+ }
40852
+ const errors = [];
40853
+ const warnings = [];
40854
+ let parsedGlobalSection = parseConfigSection(parsed, 'global', ['geonetwork4_api_url'], [
40855
+ 'datahub_url',
40856
+ 'edit_url_template',
40857
+ 'proxy_path',
40858
+ 'metadata_language',
40859
+ 'login_url',
40860
+ 'logout_url',
40861
+ 'settings_url',
40862
+ 'web_component_embedder_url',
40863
+ 'languages',
40864
+ 'contact_email',
40865
+ 'reuse_form_url',
40866
+ ], warnings, errors);
40867
+ if (parsedGlobalSection?.metadata_language) {
40868
+ parsedGlobalSection = checkMetadataLanguage(parsedGlobalSection, warnings);
40869
+ }
40870
+ globalConfig =
40871
+ parsedGlobalSection === null
40872
+ ? null
40873
+ : {
40874
+ GN4_API_URL: parsedGlobalSection.geonetwork4_api_url,
40875
+ DATAHUB_URL: parsedGlobalSection.datahub_url,
40876
+ EDIT_URL_TEMPLATE: parsedGlobalSection.edit_url_template,
40877
+ PROXY_PATH: parsedGlobalSection.proxy_path,
40878
+ METADATA_LANGUAGE: parsedGlobalSection.metadata_language
40879
+ ? parsedGlobalSection.metadata_language.toLowerCase()
40880
+ : undefined,
40881
+ LOGIN_URL: parsedGlobalSection.login_url,
40882
+ LOGOUT_URL: parsedGlobalSection.logout_url,
40883
+ SETTINGS_URL: parsedGlobalSection.settings_url,
40884
+ WEB_COMPONENT_EMBEDDER_URL: parsedGlobalSection.web_component_embedder_url,
40885
+ LANGUAGES: parsedGlobalSection.languages,
40886
+ CONTACT_EMAIL: parsedGlobalSection.contact_email,
40887
+ REUSE_FORM_URL: parsedGlobalSection.reuse_form_url,
40888
+ };
40889
+ const parsedLayersSections = parseMultiConfigSection(parsed, 'map_layer', ['type'], ['name', 'url', 'data', 'styleUrl', 'accessToken'], warnings, errors);
40890
+ const parsedMapSection = parseConfigSection(parsed, 'map', [], [
40891
+ 'max_zoom',
40892
+ 'do_not_tile_wms',
40893
+ 'max_extent',
40894
+ 'max_feature_count',
40895
+ 'baselayer',
40896
+ 'do_not_use_default_basemap',
40897
+ 'external_viewer_url_template',
40898
+ 'external_viewer_open_new_tab',
40899
+ ], warnings, errors);
40900
+ mapConfig =
40901
+ parsedMapSection === null || parsedLayersSections === null
40902
+ ? null
40903
+ : {
40904
+ MAX_ZOOM: parsedMapSection.max_zoom,
40905
+ DO_NOT_TILE_WMS: parsedMapSection.do_not_tile_wms,
40906
+ MAX_EXTENT: parsedMapSection.max_extent,
40907
+ MAX_FEATURE_COUNT: parsedMapSection.max_feature_count,
40908
+ EXTERNAL_VIEWER_URL_TEMPLATE: parsedMapSection.external_viewer_url_template,
40909
+ EXTERNAL_VIEWER_OPEN_NEW_TAB: parsedMapSection.external_viewer_open_new_tab,
40910
+ DO_NOT_USE_DEFAULT_BASEMAP: !!parsedMapSection.do_not_use_default_basemap,
40911
+ MAP_LAYERS: parsedLayersSections.map((map_layer) => ({
40912
+ TYPE: map_layer.type,
40913
+ URL: map_layer.url,
40914
+ NAME: map_layer.name,
40915
+ DATA: map_layer.data,
40916
+ STYLE_URL: map_layer.styleUrl,
40917
+ ACCESS_TOKEN: map_layer.accessToken,
40918
+ })),
40919
+ };
40920
+ const parsedThemeSection = parseConfigSection(parsed, 'theme', ['primary_color', 'secondary_color', 'main_color', 'background_color'], [
40921
+ 'header_foreground_color',
40922
+ 'main_font',
40923
+ 'title_font',
40924
+ 'fonts_stylesheet_url',
40925
+ 'thumbnail_placeholder',
40926
+ 'header_background',
40927
+ 'favicon',
40928
+ ], warnings, errors);
40929
+ themeConfig =
40930
+ parsedThemeSection === null
40931
+ ? null
40932
+ : {
40933
+ PRIMARY_COLOR: parsedThemeSection.primary_color,
40934
+ SECONDARY_COLOR: parsedThemeSection.secondary_color,
40935
+ MAIN_COLOR: parsedThemeSection.main_color,
40936
+ BACKGROUND_COLOR: parsedThemeSection.background_color,
40937
+ HEADER_FOREGROUND_COLOR: parsedThemeSection.header_foreground_color,
40938
+ THUMBNAIL_PLACEHOLDER: parsedThemeSection.thumbnail_placeholder,
40939
+ HEADER_BACKGROUND: parsedThemeSection.header_background,
40940
+ TITLE_FONT: parsedThemeSection.title_font,
40941
+ MAIN_FONT: parsedThemeSection.main_font,
40942
+ FONTS_STYLESHEET_URL: parsedThemeSection.fonts_stylesheet_url,
40943
+ FAVICON: parsedThemeSection.favicon,
40944
+ };
40945
+ const parsedSearchSection = parseConfigSection(parsed, 'search', [], [
40946
+ 'record_kind_quick_filter',
40947
+ 'filter_geometry_data',
40948
+ 'filter_geometry_url',
40949
+ 'do_not_use_default_search_preset',
40950
+ 'search_preset',
40951
+ 'advanced_filters',
40952
+ 'limit',
40953
+ 'spatial_extent_max_file_size',
40954
+ 'geocoding_provider',
40955
+ 'geocoding_provider_options',
40956
+ 'geocoding_provider_labels',
40957
+ ], warnings, errors);
40958
+ const parsedSearchParams = parseMultiConfigSection(parsed, 'search_preset', ['name'], ['sort', 'filters'], warnings, errors);
40959
+ const parsedCustomFilters = parseMultiConfigSection(parsed, 'custom_filter', ['name', 'base_filter'], ['exclude_values', 'include_values', 'label_key'], warnings, errors);
40960
+ const parsedGeocodingProviderLabels = parsedSearchSection?.geocoding_provider_labels;
40961
+ searchConfig =
40962
+ parsedSearchSection === null
40963
+ ? null
40964
+ : {
40965
+ RECORD_KIND_QUICK_FILTER: parsedSearchSection.record_kind_quick_filter,
40966
+ FILTER_GEOMETRY_DATA: parsedSearchSection.filter_geometry_data,
40967
+ FILTER_GEOMETRY_URL: parsedSearchSection.filter_geometry_url,
40968
+ DO_NOT_USE_DEFAULT_SEARCH_PRESET: !!parsedSearchSection.do_not_use_default_search_preset,
40969
+ SEARCH_PRESET: parsedSearchParams.map((param) => ({
40970
+ sort: param.sort,
40971
+ name: param.name,
40972
+ filters: param.filters,
40973
+ })),
40974
+ ADVANCED_FILTERS: parsedSearchSection.advanced_filters,
40975
+ CUSTOM_FILTERS: (parsedCustomFilters ?? []).map((customFilter) => ({
40976
+ name: customFilter.name,
40977
+ baseFilter: customFilter.base_filter,
40978
+ excludeValues: customFilter.exclude_values,
40979
+ includeValues: customFilter.include_values,
40980
+ labelKey: customFilter.label_key,
40981
+ })),
40982
+ LIMIT: parsedSearchSection.limit,
40983
+ SPATIAL_EXTENT_MAX_FILE_SIZE: parsedSearchSection.spatial_extent_max_file_size,
40984
+ GEOCODING_PROVIDER: parsedSearchSection.geocoding_provider,
40985
+ GEOCODING_PROVIDER_OPTIONS: parsedSearchSection.geocoding_provider_options,
40986
+ GEOCODING_PROVIDER_LABELS: parsedGeocodingProviderLabels
40987
+ ? {
40988
+ MAIN: parsedGeocodingProviderLabels.main,
40989
+ SECONDARY: parsedGeocodingProviderLabels.secondary,
40990
+ TERTIARY: parsedGeocodingProviderLabels.tertiary,
40991
+ }
40992
+ : undefined,
40993
+ };
40994
+ const parsedMetadataQualitySection = parseConfigSection(parsed, 'metadata-quality', [], ['enabled'], warnings, errors);
40995
+ metadataQualityConfig =
40996
+ parsedMetadataQualitySection === null
40997
+ ? null
40998
+ : {
40999
+ ENABLED: parsedMetadataQualitySection.enabled,
41000
+ SORTABLE: parsedMetadataQualitySection.sortable,
41001
+ };
41002
+ let parsedEditingSection = parseConfigSection(parsed, 'editing', [], ['new_record_default_language', 'new_record_standard'], warnings, errors);
41003
+ if (parsedEditingSection !== null &&
41004
+ parsedEditingSection.new_record_default_language !== undefined) {
41005
+ parsedEditingSection = checkNewRecordDefaultLanguage(parsedEditingSection, warnings);
41006
+ }
41007
+ if (parsedEditingSection !== null &&
41008
+ parsedEditingSection.new_record_standard !== undefined) {
41009
+ parsedEditingSection = checkNewRecordStandard(parsedEditingSection, warnings);
41010
+ }
41011
+ editorConfig =
41012
+ parsedEditingSection === null
41013
+ ? null
41014
+ : {
41015
+ NEW_RECORD_DEFAULT_LANGUAGE: parsedEditingSection.new_record_default_language,
41016
+ NEW_RECORD_STANDARD: parsedEditingSection.new_record_standard,
41017
+ };
41018
+ customTranslations = parseTranslationsConfigSection(parsed, 'translations');
41019
+ if (errors.length) {
41020
+ throw new Error(`One or more mandatory settings were missing from the configuration file.
41021
+ ${errors.join('\n')}`);
41022
+ }
41023
+ else if (warnings.length) {
41024
+ console.warn(`One or more unexpected settings were encountered in the configuration file.
41025
+ ${warnings.join('\n')}`);
41026
+ }
41027
+ appConfigLoaded = true;
41028
+ });
41029
+ }
41030
+ function isConfigLoaded() {
41031
+ return appConfigLoaded;
41032
+ }
41033
+ function _reset() {
41034
+ globalConfig = null;
41035
+ themeConfig = null;
41036
+ editorConfig = null;
41037
+ customTranslations = null;
41038
+ }
41039
+ const TRANSLATE_WITH_OVERRIDES_CONFIG = {
41040
+ compiler: {
41041
+ provide: TranslateCompiler,
41042
+ useClass: TranslateMessageFormatCompiler,
41043
+ },
41044
+ loader: [
41045
+ {
41046
+ provide: TRANSLATE_HTTP_LOADER_CONFIG,
41047
+ useValue: {
41048
+ prefix: './assets/i18n/',
41049
+ suffix: '.json',
41050
+ },
41051
+ },
41052
+ {
41053
+ provide: TranslateLoader,
41054
+ useClass: FileWithOverridesTranslateLoader,
41055
+ deps: [HttpClient, TRANSLATE_HTTP_LOADER_CONFIG],
41056
+ },
41057
+ ],
41058
+ };
41059
+
41060
+ const okAppConfigFixture = () => `
41061
+ [global]
41062
+ geonetwork4_api_url = "/geonetwork/srv/api"
41063
+ proxy_path = "/proxy/?url="
41064
+ metadata_language = "fre"
41065
+ login_url = "/cas/login?service="
41066
+ logout_url = "/geonetwork/signout"
41067
+ settings_url = "/geonetwork/settings"
41068
+ web_component_embedder_url = "/datahub/wc-embedder.html"
41069
+
41070
+ [map]
41071
+ max_zoom = 10
41072
+ max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855]
41073
+ do_not_use_default_basemap = false
41074
+ external_viewer_url_template = 'https://example.com/myviewer?'
41075
+ external_viewer_open_new_tab = true
41076
+ [[map_layer]]
41077
+ type = "wms"
41078
+ url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
41079
+ name = "masque_hdf_ign_carto_latin1"
41080
+ [[map_layer]]
41081
+ type = "wfs"
41082
+ url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
41083
+ name = "masque_hdf_ign_carto_latin1"
41084
+
41085
+ [theme]
41086
+ primary_color = "#093564"
41087
+ secondary_color = "#c2e9dc"
41088
+ main_color = "#212029" # All-purpose text color
41089
+ background_color = "#fdfbff"
41090
+ thumbnail_placeholder = 'assets/img/placeholder.svg'
41091
+ header_background = 'teal'
41092
+ header_foreground_color = "#872e2e"
41093
+ main_font = 'sans-serif'
41094
+ title_font = 'serif'
41095
+ fonts_stylesheet_url = "https://fonts.googleapis.com/css2?family=Open+Sans"
41096
+
41097
+ [search]
41098
+ record_kind_quick_filter = false
41099
+ filter_geometry_url = 'https://my.domain.org/geom.json'
41100
+ do_not_use_default_search_preset = false
41101
+ geocoding_provider = "geoplateforme"
41102
+ geocoding_provider_options.index = "poi"
41103
+ geocoding_provider_options.category = "administratif"
41104
+ geocoding_provider_options.limit = 5
41105
+ geocoding_provider_labels.main = '/properties/name/0'
41106
+ geocoding_provider_labels.secondary = '/properties/citycode/0'
41107
+ geocoding_provider_labels.tertiary = '/properties/category/1'
41108
+ advanced_filters = ['publicationYear', 'documentStandard', 'inspireKeyword', 'topic', 'license', 'myOrg:firstCustomFilter', 'myOrg:secondCustomFilter']
41109
+
41110
+ [[custom_filter]]
41111
+ name = 'myOrg:firstCustomFilter'
41112
+ base_filter = 'keyword'
41113
+ exclude_values = ['my keyword 1', 'my keyword 2']
41114
+ label_key = 'myOrg.firstCustomFilter'
41115
+
41116
+ [[custom_filter]]
41117
+ name = 'myOrg:secondCustomFilter'
41118
+ base_filter = 'keyword'
41119
+ include_values = ['my keyword 3', 'my keyword 4']
41120
+ label_key = 'myOrg.secondCustomFilter'
41121
+
41122
+ [[search_preset]]
41123
+ sort = "-createDate"
41124
+ name = 'filterByOrgs'
41125
+ filters.q = 'Carto'
41126
+ filters.organisation = ['Org1', 'Org2']
41127
+ filters.format = ['ESRI Shapefile']
41128
+ filters.topic = ['boundaries']
41129
+ filters.publicationYear = ['2023', '2022']
41130
+ filters.spatial = ['yes']
41131
+
41132
+ [translations.en]
41133
+ "my.first.key" = 'First label.'
41134
+ "my.second.key" = """
41135
+ Second label,
41136
+ on two lines."""
41137
+
41138
+ [translations.de]
41139
+ my.first.key = 'Erste Etikett.'
41140
+
41141
+ [translations.fr]
41142
+ my.sample.text = "Un bon exemple de texte."
41143
+ "my.quoted.text" = 'du texte entre guillements.'
41144
+ `;
41145
+ const appConfigWithTranslationFixture = () => okAppConfigFixture();
41146
+ const minimalAppConfigFixture = () => `
41147
+ [global]
41148
+ geonetwork4_api_url = "/geonetwork/srv/api"
41149
+ proxy_path = "/proxy/?url="
41150
+
41151
+ [theme]
41152
+ primary_color = "#093564"
41153
+ secondary_color = "#c2e9dc"
41154
+ main_color = "#212029" # All-purpose text color
41155
+ background_color = "#fdfbff"
41156
+ `;
41157
+ const customFilterMissingMandatoryConfigFixture = () => `
41158
+ [global]
41159
+ geonetwork4_api_url = "/geonetwork/srv/api"
41160
+
41161
+ [theme]
41162
+ primary_color = "#093564"
41163
+ secondary_color = "#c2e9dc"
41164
+ main_color = "#212029" # All-purpose text color
41165
+ background_color = "#fdfbff"
41166
+
41167
+ [search]
41168
+ advanced_filters = ['myOrg:firstCustomFilter']
41169
+
41170
+ [[custom_filter]]
41171
+ name = 'myOrg:firstCustomFilter'
41172
+ `;
41173
+ const mapConfigFixture = () => ({
41174
+ MAX_ZOOM: 10,
41175
+ MAX_EXTENT: [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855],
41176
+ DO_NOT_USE_DEFAULT_BASEMAP: false,
41177
+ DO_NOT_TILE_WMS: false,
41178
+ 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}"}]}]',
41179
+ EXTERNAL_VIEWER_OPEN_NEW_TAB: true,
41180
+ MAP_LAYERS: [
41181
+ {
41182
+ TYPE: 'xyz',
41183
+ URL: 'https://some-basemap-server',
41184
+ },
41185
+ {
41186
+ TYPE: 'wms',
41187
+ URL: 'https://some-wms-server',
41188
+ NAME: 'some_layername',
41189
+ },
41190
+ {
41191
+ TYPE: 'wfs',
41192
+ URL: 'https://some-wfs-server',
41193
+ NAME: 'some_layername',
41194
+ },
41195
+ ],
41196
+ });
41197
+ const malformedConfigFixture = () => `
41198
+ {
41199
+ "I thought": "we were still doing json"
41200
+ }
41201
+ `;
41202
+ const missingMandatoryConfigFixture = () => `
41203
+ [theme]
41204
+ primary_color = "#093564"
41205
+ secondary_color = "#c2e9dc"
41206
+ background_color = "#fdfbff"
41207
+ main_font = 'sans-serif'
41208
+ `;
41209
+ const wrongLanguageCodeConfigFixture = () => `
41210
+ [global]
41211
+ geonetwork4_api_url = "/geonetwork/srv/api"
41212
+ proxy_path = "/proxy/?url="
41213
+ metadata_language = "abc"
41214
+
41215
+ [map]
41216
+
41217
+ [theme]
41218
+ primary_color = "#093564"
41219
+ secondary_color = "#c2e9dc"
41220
+ main_color = "#212029" # All-purpose text color
41221
+ background_color = "#fdfbff"
41222
+ `;
41223
+ const unrecognizedKeysConfigFixture = () => `
41224
+ [global]
41225
+ geonetwork4_api_url = "/geonetwork/srv/api"
41226
+ proxy_path = "/proxy/?url="
41227
+ metadata_language = "fre"
41228
+ another_path = '/whatever'
41229
+
41230
+ [map]
41231
+ max_zoom = 10
41232
+ max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855]
41233
+ do_not_use_default_basemap = false
41234
+ external_viewer_url_template = 'https://example.com/myviewer?'
41235
+ external_viewer_open_new_tab = true
41236
+ another_zoom = 15
41237
+ [[map_layer]]
41238
+ type = "wms"
41239
+ url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
41240
+ name = "masque_hdf_ign_carto_latin1"
41241
+ [[map_layer]]
41242
+ type = "wfs"
41243
+ url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
41244
+ name = "masque_hdf_ign_carto_latin1"
41245
+ another_layer = "wrong layer definition"
41246
+
41247
+ [theme]
41248
+ primary_color = "#093564"
41249
+ secondary_color = "#c2e9dc"
41250
+ main_color = "#212029" # All-purpose text color
41251
+ background_color = "#fdfbff"
41252
+ thumbnail_placeholder = 'assets/img/placeholder.svg'
41253
+ header_background = 'teal'
41254
+ header_foreground_color = "#872e2e"
41255
+ main_font = 'sans-serif'
41256
+ title_font = 'serif'
41257
+ another_color = 'red'
41258
+ `;
41259
+
41260
+ function getMapContextLayerFromConfig(config) {
41261
+ switch (config.TYPE) {
41262
+ case 'wms':
41263
+ return {
41264
+ type: 'wms',
41265
+ url: config.URL,
41266
+ name: config.NAME,
41267
+ };
41268
+ case 'wfs':
41269
+ return {
41270
+ type: 'wfs',
41271
+ url: config.URL,
41272
+ featureType: config.NAME,
41273
+ };
41274
+ case 'xyz':
41275
+ return {
41276
+ type: config.TYPE,
41277
+ url: config.URL,
41278
+ };
41279
+ case 'geojson':
41280
+ return {
41281
+ type: config.TYPE,
41282
+ ...(config.DATA ? { data: config.DATA } : { url: config.URL }),
41283
+ };
41284
+ case 'maplibre-style':
41285
+ return {
41286
+ type: config.TYPE,
41287
+ styleUrl: config.STYLE_URL,
41288
+ accessToken: config.ACCESS_TOKEN,
41289
+ };
41290
+ }
41291
+ }
41292
+
40940
41293
  const CHART_TYPE_VALUES = [
40941
41294
  'bar',
40942
41295
  'bar-horizontal',
@@ -40952,5 +41305,5 @@ const CHART_TYPE_VALUES = [
40952
41305
  * Generated bundle index. Do not edit.
40953
41306
  */
40954
41307
 
40955
- 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_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, 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_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, 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, isBoundingBox, 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 };
41308
+ 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, CUSTOM_FIELDS, 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, GEOCODING_PROVIDER_LABELS, 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, customFilterMissingMandatoryConfigFixture, 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 };
40956
41309
  //# sourceMappingURL=geonetwork-ui.mjs.map