geonetwork-ui 2.11.0-dev.0504d85cc → 2.11.0-dev.0559cfd9d

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 (58) hide show
  1. package/fesm2022/geonetwork-ui.mjs +1347 -1152
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +117 -64
  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 +29 -3
  16. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +5 -34
  17. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +14 -18
  18. package/src/libs/common/domain/src/lib/model/search/aggregation.model.ts +2 -0
  19. package/src/libs/common/domain/src/lib/model/search/filter.model.ts +4 -1
  20. package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +1 -1
  21. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +42 -62
  22. package/src/libs/feature/dataviz/src/lib/service/data.service.ts +32 -19
  23. package/src/libs/feature/map/src/index.ts +0 -1
  24. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +8 -3
  25. package/src/libs/feature/record/src/lib/state/mdview.effects.ts +3 -1
  26. package/src/libs/feature/record/src/lib/state/mdview.facade.ts +1 -1
  27. package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +7 -1
  28. package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +26 -4
  29. package/src/libs/feature/search/src/lib/geocoding/geocoding.service.ts +52 -8
  30. package/src/libs/feature/search/src/lib/location-search/location-search.component.html +11 -1
  31. package/src/libs/feature/search/src/lib/location-search/location-search.component.ts +77 -3
  32. package/src/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts +1 -0
  33. package/src/libs/feature/search/src/lib/state/reducer.ts +1 -4
  34. package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +56 -4
  35. package/src/libs/feature/search/src/lib/utils/service/fields.ts +33 -9
  36. package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.html +3 -3
  37. package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.ts +2 -2
  38. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.css +0 -4
  39. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.html +2 -2
  40. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts +3 -3
  41. package/src/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.ts +2 -1
  42. package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html +7 -1
  43. package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts +26 -3
  44. package/src/libs/ui/layout/src/lib/block-list/block-list.component.css +1 -0
  45. package/src/libs/util/app-config/src/lib/app-config.ts +34 -0
  46. package/src/libs/util/app-config/src/lib/fixtures.ts +37 -1
  47. package/src/libs/util/app-config/src/lib/model.ts +24 -1
  48. package/translations/de.json +8 -2
  49. package/translations/en.json +8 -2
  50. package/translations/es.json +8 -2
  51. package/translations/fr.json +12 -6
  52. package/translations/it.json +8 -2
  53. package/translations/nl.json +8 -2
  54. package/translations/pt.json +8 -2
  55. package/translations/sk.json +8 -2
  56. package/src/libs/feature/map/src/lib/style/index.ts +0 -2
  57. package/src/libs/feature/map/src/lib/style/map-style.fixtures.ts +0 -47
  58. package/src/libs/feature/map/src/lib/style/map-style.service.ts +0 -108
@@ -36,9 +36,6 @@ import { createLegendFromLayer } from '@geospatial-sdk/legend';
36
36
  import { mouseOnly, noModifierKeys, primaryAction, platformModifierKeyOnly } from 'ol/events/condition.js';
37
37
  import { defaults, DragPan, MouseWheelZoom } from 'ol/interaction.js';
38
38
  import { extend } from 'ol/extent.js';
39
- import * as TOML from '@ltd/j-toml';
40
- import { Style, Stroke, Fill, Circle } from 'ol/style.js';
41
- import CircleStyle from 'ol/style/Circle.js';
42
39
  import EmblaCarousel from 'embla-carousel';
43
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';
44
41
  import { MatButtonModule } from '@angular/material/button';
@@ -73,7 +70,8 @@ import { MatTabsModule } from '@angular/material/tabs';
73
70
  import * as i2$2 from '@ngrx/effects';
74
71
  import { Actions, createEffect, ofType, EffectsModule } from '@ngrx/effects';
75
72
  import { valid as valid$1 } from 'geojson-validation';
76
- import { queryDataGouvFr, queryGeonames, queryGeoadmin } from '@geospatial-sdk/geocoding';
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) {
@@ -20452,7 +20564,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
20452
20564
  }] } });
20453
20565
 
20454
20566
  var name = "geonetwork-ui";
20455
- var version = "2.11.0-dev.0504d85cc";
20567
+ var version = "2.11.0-dev.0559cfd9d";
20456
20568
  var engines = {
20457
20569
  node: ">=24"
20458
20570
  };
@@ -20494,10 +20606,10 @@ var dependencies = {
20494
20606
  "@biesbjerg/ngx-translate-extract-marker": "~1.0.0",
20495
20607
  "@camptocamp/ogc-client": "1.3.1-dev.200c02a",
20496
20608
  "@duckdb/duckdb-wasm": "~1.32.0",
20497
- "@geospatial-sdk/core": "0.0.5-dev.78",
20498
- "@geospatial-sdk/geocoding": "0.0.5-dev.78",
20499
- "@geospatial-sdk/legend": "0.0.5-dev.78",
20500
- "@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",
20501
20613
  "@ltd/j-toml": "~1.38.0",
20502
20614
  "@messageformat/core": "~3.4.0",
20503
20615
  "@ng-icons/core": "~32.5.0",
@@ -20514,6 +20626,7 @@ var dependencies = {
20514
20626
  "embla-carousel": "~8.6.0",
20515
20627
  "flag-icons": "~7.5.0",
20516
20628
  "geojson-validation": "~1.0.2",
20629
+ jsonpointer: "^5.0.1",
20517
20630
  marked: "~17.0.4",
20518
20631
  "ngx-dropzone": "~3.1.0",
20519
20632
  "ngx-translate-messageformat-compiler": "~7.2.0",
@@ -20690,6 +20803,7 @@ class ElasticsearchService {
20690
20803
  // runtime fields are computed using a Painless script
20691
20804
  // see: https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime-mapping-fields.html
20692
20805
  this.runtimeFields = {};
20806
+ this.fieldAliases = {};
20693
20807
  }
20694
20808
  // we're using getters in case the defined languages change over time
20695
20809
  get metadataLang() {
@@ -20761,6 +20875,9 @@ class ElasticsearchService {
20761
20875
  registerRuntimeField(fieldName, expression, type = 'keyword') {
20762
20876
  this.runtimeFields[fieldName] = { script: expression, type };
20763
20877
  }
20878
+ registerFieldAlias(fieldIdentifier, esFieldName) {
20879
+ this.fieldAliases[fieldIdentifier] = esFieldName;
20880
+ }
20764
20881
  getMetadataByIdsPayload(uuids) {
20765
20882
  return {
20766
20883
  query: {
@@ -20882,12 +20999,25 @@ class ElasticsearchService {
20882
20999
  }
20883
21000
  return Object.values(filters).find(isBoundingBox);
20884
21001
  }
20885
- // if a field registered min/max runtime fields, its dates form an interval
20886
- // the record matches if that interval intersects the filter range
21002
+ // builds a query matching records whose dates intersect the filter range
20887
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
+ }
20888
21017
  const minField = `${searchField}Min`;
20889
21018
  const maxField = `${searchField}Max`;
20890
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
20891
21021
  const filter = [
20892
21022
  dateRange.start && {
20893
21023
  range: {
@@ -20942,7 +21072,7 @@ class ElasticsearchService {
20942
21072
  .filter((fieldname) => !Array.isArray(filters[fieldname]))
20943
21073
  .filter((fieldname) => filters[fieldname] &&
20944
21074
  JSON.stringify(filters[fieldname]) !== '{}')
20945
- .map((fieldname) => `${fieldname}:(${makeQuery(filters[fieldname])})`)
21075
+ .map((fieldname) => `${this.fieldAliases[fieldname] ?? fieldname}:(${makeQuery(filters[fieldname])})`)
20946
21076
  .join(' AND ');
20947
21077
  if (filters['gn-ui-crossFieldFilter']) {
20948
21078
  queryString = `${queryString} AND (${filters['gn-ui-crossFieldFilter']})`;
@@ -21240,6 +21370,12 @@ class ElasticsearchService {
21240
21370
  order: {
21241
21371
  [`_${aggregation.sort[1]}`]: aggregation.sort[0],
21242
21372
  },
21373
+ ...(aggregation.includeValues && {
21374
+ include: aggregation.includeValues,
21375
+ }),
21376
+ ...(aggregation.excludeValues && {
21377
+ exclude: aggregation.excludeValues,
21378
+ }),
21243
21379
  },
21244
21380
  };
21245
21381
  case 'histogram':
@@ -21374,42 +21510,16 @@ class Gn4Repository {
21374
21510
  ? this.gn4Mapper.readRecords(records)
21375
21511
  : of(null)));
21376
21512
  }
21377
- mapEmbeddedFeatureCatalog(featureTypes) {
21378
- return {
21379
- featureTypes: featureTypes.map((featureType) => ({
21380
- name: featureType.typeName || '',
21381
- definition: featureType.definition || '',
21382
- attributes: Array.isArray(featureType.attributeTable)
21383
- ? featureType.attributeTable.map((attr) => {
21384
- const values = attr.values
21385
- ?.filter((v) => v.code || v.label)
21386
- .map((v) => ({
21387
- code: v.code,
21388
- label: v.label,
21389
- }));
21390
- return {
21391
- name: attr.name,
21392
- code: attr.code,
21393
- definition: attr.definition,
21394
- type: attr.type,
21395
- ...(values?.length > 0 ? { values } : {}),
21396
- };
21397
- })
21398
- : [],
21399
- })),
21400
- };
21401
- }
21402
- getFeatureCatalog(record, visited = new Set() // prevent looping
21513
+ queryLinkedFeatureCatalog(record, visited = new Set() // prevent looping
21403
21514
  ) {
21404
- if (record.extras?.['featureTypes'] &&
21405
- Array.isArray(record.extras['featureTypes']) &&
21406
- record.extras['featureTypes'].length > 0) {
21407
- return of(this.mapEmbeddedFeatureCatalog(record.extras['featureTypes']));
21515
+ if ('featureTypeDescriptions' in record &&
21516
+ record.featureTypeDescriptions?.length > 0) {
21517
+ return of({ featureTypes: record.featureTypeDescriptions });
21408
21518
  }
21409
21519
  const featureCatalogIdentifier = record.extras['featureCatalogIdentifier'];
21410
21520
  if (featureCatalogIdentifier && !visited.has(featureCatalogIdentifier)) {
21411
21521
  visited.add(featureCatalogIdentifier);
21412
- return this.getRecord(featureCatalogIdentifier).pipe(switchMap((record) => record ? this.getFeatureCatalog(record, visited) : of(null)));
21522
+ return this.getRecord(featureCatalogIdentifier).pipe(switchMap((record) => record ? this.queryLinkedFeatureCatalog(record, visited) : of(null)));
21413
21523
  }
21414
21524
  return of(null);
21415
21525
  }
@@ -23082,1082 +23192,374 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
23082
23192
  }]
23083
23193
  }] });
23084
23194
 
23085
- const flatten = (base, obj, keepLevel = false) => Object.keys(obj).reduce((prev, curr) => {
23086
- const path = base ? `${base}.${curr}` : curr;
23087
- const val = obj[curr];
23088
- if (keepLevel) {
23089
- return { ...prev, [path]: flatten('', val) };
23195
+ class AnchorLinkDirective {
23196
+ constructor() {
23197
+ this.changeDetector = inject(ChangeDetectorRef);
23198
+ this.disabled = false;
23199
+ this.observer = new MutationObserver(() => {
23200
+ this.refreshDisabledState();
23201
+ });
23202
+ this.inView = false;
23203
+ this.initialized = false;
23090
23204
  }
23091
- else if (typeof val === 'object' && !Array.isArray(val)) {
23092
- return {
23093
- ...prev,
23094
- ...flatten(path, val),
23095
- };
23205
+ get elementClass() {
23206
+ if (this.disabled) {
23207
+ return this.disabledClass;
23208
+ }
23209
+ if (this.inView) {
23210
+ return `${this.inViewClass} ${this.enabledClass}`;
23211
+ }
23212
+ else {
23213
+ return `${this.outOfViewClass} ${this.enabledClass}`;
23214
+ }
23096
23215
  }
23097
- else {
23098
- return { ...prev, [path]: val };
23216
+ ngOnInit() {
23217
+ this.observer.observe(document.body, {
23218
+ childList: true,
23219
+ subtree: true,
23220
+ });
23221
+ this.refreshDisabledState();
23099
23222
  }
23100
- }, {});
23101
- const checkKeys = (input, mandatory, optional) => {
23102
- const keys = Object.keys(input);
23103
- const missing = mandatory.filter((key) => keys.indexOf(key) === -1);
23104
- const unrecognized = keys.filter((key) => mandatory.indexOf(key) === -1 && optional.indexOf(key) === -1);
23105
- return {
23106
- missing,
23107
- unrecognized,
23108
- };
23109
- };
23110
- /**
23111
- * Only keeps recognized keys, flatten nested values (to allow having "." in keys),
23112
- * pushes warnings and errors to the given arrays; returns null in case of error
23113
- */
23114
- function parseConfigSection(fullConfigObj, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors) {
23115
- if (typeof fullConfigObj[sectionName] !== 'object') {
23116
- if (mandatoryKeys.length === 0)
23117
- return null;
23118
- outErrors.push(`The [${sectionName}] mandatory section is missing.`);
23119
- return null;
23223
+ ngAfterViewChecked() {
23224
+ if (!this.initialized && !this.disabled) {
23225
+ const target = document.getElementById(this.targetId);
23226
+ if (target) {
23227
+ this.initializeIntersectionObserver(target);
23228
+ this.initialized = true;
23229
+ }
23230
+ }
23120
23231
  }
23121
- const sectionConf = fullConfigObj[sectionName];
23122
- const keysCheck = checkKeys(sectionConf, mandatoryKeys, optionalKeys);
23123
- if (keysCheck.missing.length) {
23124
- // note: this is not thrown to allow merging several Errors down the line
23125
- outErrors.push(`In the [${sectionName}] section: ${keysCheck.missing.join(', ')}`);
23126
- return null;
23232
+ initializeIntersectionObserver(target) {
23233
+ this.intersectionObserver = new IntersectionObserver((entries) => {
23234
+ entries.forEach((entry) => {
23235
+ this.inView = entry.isIntersecting;
23236
+ this.changeDetector.detectChanges();
23237
+ });
23238
+ }, {
23239
+ root: null,
23240
+ rootMargin: '-30% 0% -60% 0%',
23241
+ });
23242
+ this.intersectionObserver.observe(target);
23127
23243
  }
23128
- else if (keysCheck.unrecognized.length) {
23129
- outWarnings.push(`In the [${sectionName}] section: ${keysCheck.unrecognized.join(', ')}`);
23130
- keysCheck.unrecognized.forEach((key) => delete sectionConf[key]);
23244
+ ngOnDestroy() {
23245
+ this.observer.disconnect();
23246
+ if (this.intersectionObserver) {
23247
+ this.intersectionObserver.disconnect();
23248
+ }
23131
23249
  }
23132
- return sectionConf;
23250
+ refreshDisabledState() {
23251
+ const targetNotPresent = !document.getElementById(this.targetId);
23252
+ if (targetNotPresent !== this.disabled) {
23253
+ this.disabled = targetNotPresent;
23254
+ this.changeDetector.detectChanges();
23255
+ }
23256
+ }
23257
+ scrollToTarget() {
23258
+ const target = document.getElementById(this.targetId);
23259
+ if (!target)
23260
+ return;
23261
+ target.scrollIntoView({
23262
+ behavior: 'smooth',
23263
+ block: 'start',
23264
+ });
23265
+ }
23266
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AnchorLinkDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
23267
+ 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 }); }
23133
23268
  }
23134
- /**
23135
- * Same as parseConfigSection but keeps all keys, recognized or not
23136
- * Also the first level is the language code (e.g. 'en')
23137
- * This is optional
23138
- */
23139
- function parseTranslationsConfigSection(fullConfigObj, sectionName) {
23140
- if (typeof fullConfigObj[sectionName] !== 'object') {
23141
- return {};
23269
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AnchorLinkDirective, decorators: [{
23270
+ type: Directive,
23271
+ args: [{
23272
+ selector: '[gnUiAnchorLink]',
23273
+ standalone: true,
23274
+ }]
23275
+ }], propDecorators: { targetId: [{
23276
+ type: Input,
23277
+ args: ['gnUiAnchorLink']
23278
+ }], disabledClass: [{
23279
+ type: Input,
23280
+ args: ['gnUiAnchorLinkDisabledClass']
23281
+ }], enabledClass: [{
23282
+ type: Input,
23283
+ args: ['gnUiAnchorLinkEnabledClass']
23284
+ }], inViewClass: [{
23285
+ type: Input,
23286
+ args: ['gnUiAnchorLinkInViewClass']
23287
+ }], outOfViewClass: [{
23288
+ type: Input,
23289
+ args: ['gnUiAnchorLinkOutOfViewClass']
23290
+ }], elementClass: [{
23291
+ type: HostBinding,
23292
+ args: ['class']
23293
+ }], scrollToTarget: [{
23294
+ type: HostListener,
23295
+ args: ['click']
23296
+ }] } });
23297
+
23298
+ class PaginationDotsComponent {
23299
+ constructor() {
23300
+ this.containerClass = '';
23142
23301
  }
23143
- return flatten('', fullConfigObj[sectionName], true);
23302
+ // 1-based
23303
+ get steps() {
23304
+ return Array.from({ length: this.listComponent.pagesCount }, (_, i) => i + 1);
23305
+ }
23306
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PaginationDotsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23307
+ 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: [
23308
+ provideIcons({
23309
+ iconoirNavArrowRight,
23310
+ iconoirNavArrowLeft,
23311
+ }),
23312
+ ] }); }
23144
23313
  }
23145
- /**
23146
- * Same as parseConfigSection but handles an array of configs; this is
23147
- * considered optional; returns null in case of error in at least one of
23148
- * the sections
23149
- */
23150
- function parseMultiConfigSection(fullConfigObj, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors) {
23151
- const sections = fullConfigObj[sectionName];
23152
- if (!sections) {
23153
- return [];
23314
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PaginationDotsComponent, decorators: [{
23315
+ type: Component,
23316
+ args: [{ selector: 'gn-ui-pagination-dots', standalone: true, imports: [CommonModule], viewProviders: [
23317
+ provideIcons({
23318
+ iconoirNavArrowRight,
23319
+ iconoirNavArrowLeft,
23320
+ }),
23321
+ ], 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"] }]
23322
+ }], propDecorators: { listComponent: [{
23323
+ type: Input
23324
+ }], containerClass: [{
23325
+ type: Input
23326
+ }] } });
23327
+
23328
+ class CarouselComponent {
23329
+ constructor() {
23330
+ this.changeDetector = inject(ChangeDetectorRef);
23331
+ this.containerClass = '';
23332
+ this.stepsContainerClass = 'w-full bottom-0 top-auto';
23333
+ this.currentStepChange = new EventEmitter();
23334
+ this.steps = [];
23335
+ this.currentStep = 0;
23336
+ this.refreshSteps = () => {
23337
+ this.steps = this.emblaApi.scrollSnapList();
23338
+ this.currentStep = this.emblaApi.selectedScrollSnap();
23339
+ this.currentStepChange.emit(this.currentStep);
23340
+ this.changeDetector.detectChanges();
23341
+ };
23154
23342
  }
23155
- if (!Array.isArray(sections)) {
23156
- outErrors.push(`Expected [[${sectionName}]] to be an array of sections.`);
23157
- return null;
23343
+ // Paginable API
23344
+ get isFirstPage() {
23345
+ return this.currentStep === 0;
23158
23346
  }
23159
- const result = sections.map((section) => parseConfigSection({ [sectionName]: section }, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors));
23160
- if (result.some((r) => r === null)) {
23161
- return null;
23347
+ get isLastPage() {
23348
+ return this.currentStep === this.steps.length - 1;
23162
23349
  }
23163
- return result;
23164
- }
23165
- function checkMetadataLanguage(parsedConfigSection, outWarnings) {
23166
- const lang2 = toLang2(parsedConfigSection.metadata_language.toLowerCase());
23167
- if (lang2?.length !== 2) {
23168
- outWarnings.push(`In the [global] section: metadata_language = "${parsedConfigSection.metadata_language}" is not a recognized ISO 639 language code`);
23350
+ get currentPage() {
23351
+ return this.currentStep + 1; // this is 1-based
23169
23352
  }
23170
- return parsedConfigSection;
23171
- }
23172
- function checkNewRecordDefaultLanguage(parsedConfigSection, outWarnings) {
23173
- const lang2 = toLang2(parsedConfigSection.new_record_default_language.toLowerCase());
23174
- if (!(lang2 in LANG_2_TO_3_MAPPER)) {
23175
- outWarnings.push(`In the [editing] section: new_record_default_language = "${parsedConfigSection.new_record_default_language}" is not a recognized ISO 639 language code`);
23176
- return {
23177
- ...parsedConfigSection,
23178
- new_record_default_language: undefined,
23179
- };
23353
+ get pagesCount() {
23354
+ return this.steps.length;
23180
23355
  }
23181
- return {
23182
- ...parsedConfigSection,
23183
- new_record_default_language: lang2,
23184
- };
23185
- }
23186
- function checkNewRecordStandard(parsedConfigSection, outWarnings) {
23187
- const standard = parsedConfigSection.new_record_standard;
23188
- const normalizedStandard = typeof standard === 'string' ? standard.trim().toLowerCase() : null;
23189
- if (normalizedStandard === 'iso19139' ||
23190
- normalizedStandard === 'iso19115-3') {
23191
- return {
23192
- ...parsedConfigSection,
23193
- new_record_standard: normalizedStandard,
23194
- };
23356
+ goToPage(stepIndex) {
23357
+ this.emblaApi.scrollTo(stepIndex - 1); // this is 0-based
23195
23358
  }
23196
- outWarnings.push(`In the [editing] section: new_record_standard = "${standard}" is not a supported metadata standard`);
23197
- return {
23198
- ...parsedConfigSection,
23199
- new_record_standard: undefined,
23200
- };
23359
+ goToPrevPage() {
23360
+ if (this.isFirstPage)
23361
+ return;
23362
+ this.emblaApi.scrollPrev();
23363
+ }
23364
+ goToNextPage() {
23365
+ if (this.isLastPage)
23366
+ return;
23367
+ this.emblaApi.scrollNext();
23368
+ }
23369
+ ngAfterViewInit() {
23370
+ this.emblaApi = EmblaCarousel(this.carouselOverflowContainer.nativeElement, {
23371
+ duration: 15,
23372
+ });
23373
+ this.emblaApi
23374
+ .on('init', this.refreshSteps)
23375
+ .on('reInit', this.refreshSteps)
23376
+ .on('select', this.refreshSteps);
23377
+ }
23378
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CarouselComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23379
+ 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 }); }
23201
23380
  }
23381
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CarouselComponent, decorators: [{
23382
+ type: Component,
23383
+ 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"] }]
23384
+ }], propDecorators: { carouselOverflowContainer: [{
23385
+ type: ViewChild,
23386
+ args: ['carouselOverflowContainer']
23387
+ }], containerClass: [{
23388
+ type: Input
23389
+ }], stepsContainerClass: [{
23390
+ type: Input
23391
+ }], currentStepChange: [{
23392
+ type: Output
23393
+ }] } });
23202
23394
 
23203
- /**
23204
- * This loader extends the default one based on JSON files but also loads custom translations
23205
- * defined in the app configuration
23206
- * Implements a fallback on default lang if translated labels are empty
23207
- */
23208
- class FileWithOverridesTranslateLoader extends FileTranslateLoader {
23209
- getTranslation(lang) {
23210
- const baseLang = lang.substring(0, 2); // removing the right part of e.g. en_EN
23211
- return super.getTranslation(baseLang).pipe(map$1((translations) => ({
23212
- ...translations,
23213
- ...getCustomTranslations(baseLang),
23214
- })));
23395
+ class ProgressBarComponent {
23396
+ constructor() {
23397
+ this.value = 0;
23398
+ this.type = 'default';
23399
+ }
23400
+ get progress() {
23401
+ return this.value > 0 ? (this.value < 100 ? this.value : 100) : 0;
23402
+ }
23403
+ get color() {
23404
+ switch (this.type) {
23405
+ case 'default':
23406
+ return {
23407
+ outerBar: 'bg-gray-200',
23408
+ innerBar: 'bg-gray-100',
23409
+ text: 'text-gray-900',
23410
+ };
23411
+ case 'primary':
23412
+ return {
23413
+ outerBar: 'bg-primary',
23414
+ innerBar: 'bg-primary-lighter',
23415
+ text: 'text-white',
23416
+ };
23417
+ case 'secondary':
23418
+ return {
23419
+ outerBar: 'bg-secondary',
23420
+ innerBar: 'bg-secondary-lighter',
23421
+ text: 'text-white',
23422
+ };
23423
+ case 'light':
23424
+ return {
23425
+ outerBar: 'bg-primary-white',
23426
+ innerBar: 'bg-primary',
23427
+ text: 'text-main',
23428
+ };
23429
+ }
23215
23430
  }
23431
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProgressBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23432
+ 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"] }); }
23216
23433
  }
23434
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProgressBarComponent, decorators: [{
23435
+ type: Component,
23436
+ 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"] }]
23437
+ }], propDecorators: { value: [{
23438
+ type: Input
23439
+ }], type: [{
23440
+ type: Input
23441
+ }] } });
23217
23442
 
23218
- const MISSING_CONFIG_ERROR = `Application configuration was not initialized correctly.
23219
- This error might show up in case of an invalid/malformed configuration file.
23220
-
23221
- Note: make sure that you have called \`loadAppConfig\` from '../../../../../libs/util/app-config/src' before starting the Angular application.`;
23222
- let globalConfig = null;
23223
- function getGlobalConfig() {
23224
- if (globalConfig === null)
23225
- throw new Error(MISSING_CONFIG_ERROR);
23226
- return globalConfig;
23227
- }
23228
- let themeConfig = null;
23229
- function getThemeConfig() {
23230
- if (themeConfig === null)
23231
- throw new Error(MISSING_CONFIG_ERROR);
23232
- return themeConfig;
23233
- }
23234
- let mapConfig = null;
23235
- function getOptionalMapConfig() {
23236
- return mapConfig;
23237
- }
23238
- let searchConfig = null;
23239
- function getOptionalSearchConfig() {
23240
- return searchConfig;
23241
- }
23242
- let editorConfig = null;
23243
- function getOptionalEditorConfig() {
23244
- return editorConfig;
23245
- }
23246
- let metadataQualityConfig = null;
23247
- function getMetadataQualityConfig() {
23248
- return (metadataQualityConfig ||
23249
- {
23250
- ENABLED: false,
23443
+ class PopoverComponent {
23444
+ constructor() {
23445
+ this.viewContainerRef = inject(ViewContainerRef);
23446
+ this.renderer = inject(Renderer2);
23447
+ }
23448
+ getContent() {
23449
+ if (this.content instanceof TemplateRef) {
23450
+ if (this.view) {
23451
+ this.view.destroy();
23452
+ }
23453
+ this.view = this.viewContainerRef.createEmbeddedView(this.content);
23454
+ this.view.detectChanges();
23455
+ const wrapper = this.renderer.createElement('div'); // Create a wrapper div
23456
+ this.view.rootNodes.forEach((node) => {
23457
+ this.renderer.appendChild(wrapper, node); // Append each root node to the wrapper
23458
+ });
23459
+ return wrapper;
23460
+ }
23461
+ return this.content;
23462
+ }
23463
+ ngAfterViewInit() {
23464
+ this.tippyInstance = tippy(this.popoverContent.nativeElement, {
23465
+ content: this.getContent(),
23466
+ allowHTML: true,
23467
+ theme: this.theme,
23251
23468
  });
23252
- }
23253
- let customTranslations = null;
23254
- function getCustomTranslations(langCode) {
23255
- if (customTranslations === null)
23256
- throw new Error(MISSING_CONFIG_ERROR);
23257
- return langCode in customTranslations ? customTranslations[langCode] : {};
23258
- }
23259
- let appConfigLoaded = false;
23260
- function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
23261
- console.log(`[geonetwork-ui] Loading application configuration from ${configUrl}`);
23262
- return fetch(configUrl)
23263
- .then((resp) => {
23264
- if (!resp.ok)
23265
- throw new Error('Configuration file could not be loaded');
23266
- return resp.text();
23267
- })
23268
- .then((conf) => {
23269
- let parsed;
23270
- try {
23271
- parsed = TOML.parse(conf, { joiner: '\n', bigint: false });
23469
+ }
23470
+ ngOnChanges(changes) {
23471
+ if (changes['theme']) {
23472
+ this.theme = changes['theme'].currentValue;
23473
+ if (this.tippyInstance) {
23474
+ this.tippyInstance.setProps({ theme: this.theme });
23475
+ }
23272
23476
  }
23273
- catch (e) {
23274
- throw new Error(`An error occurred when parsing the configuration file: ${e.message}`);
23477
+ if (changes['content']) {
23478
+ this.content = changes['content'].currentValue;
23479
+ if (this.tippyInstance) {
23480
+ this.tippyInstance.setContent(this.getContent());
23481
+ }
23275
23482
  }
23276
- const errors = [];
23277
- const warnings = [];
23278
- let parsedGlobalSection = parseConfigSection(parsed, 'global', ['geonetwork4_api_url'], [
23279
- 'datahub_url',
23280
- 'edit_url_template',
23281
- 'proxy_path',
23282
- 'metadata_language',
23283
- 'login_url',
23284
- 'logout_url',
23285
- 'settings_url',
23286
- 'web_component_embedder_url',
23287
- 'languages',
23288
- 'contact_email',
23289
- 'reuse_form_url',
23290
- ], warnings, errors);
23291
- if (parsedGlobalSection?.metadata_language) {
23292
- parsedGlobalSection = checkMetadataLanguage(parsedGlobalSection, warnings);
23483
+ }
23484
+ ngOnDestroy() {
23485
+ if (this.tippyInstance) {
23486
+ this.tippyInstance.destroy();
23293
23487
  }
23294
- globalConfig =
23295
- parsedGlobalSection === null
23296
- ? null
23297
- : {
23298
- GN4_API_URL: parsedGlobalSection.geonetwork4_api_url,
23299
- DATAHUB_URL: parsedGlobalSection.datahub_url,
23300
- EDIT_URL_TEMPLATE: parsedGlobalSection.edit_url_template,
23301
- PROXY_PATH: parsedGlobalSection.proxy_path,
23302
- METADATA_LANGUAGE: parsedGlobalSection.metadata_language
23303
- ? parsedGlobalSection.metadata_language.toLowerCase()
23304
- : undefined,
23305
- LOGIN_URL: parsedGlobalSection.login_url,
23306
- LOGOUT_URL: parsedGlobalSection.logout_url,
23307
- SETTINGS_URL: parsedGlobalSection.settings_url,
23308
- WEB_COMPONENT_EMBEDDER_URL: parsedGlobalSection.web_component_embedder_url,
23309
- LANGUAGES: parsedGlobalSection.languages,
23310
- CONTACT_EMAIL: parsedGlobalSection.contact_email,
23311
- REUSE_FORM_URL: parsedGlobalSection.reuse_form_url,
23312
- };
23313
- const parsedLayersSections = parseMultiConfigSection(parsed, 'map_layer', ['type'], ['name', 'url', 'data', 'styleUrl', 'accessToken'], warnings, errors);
23314
- const parsedMapSection = parseConfigSection(parsed, 'map', [], [
23315
- 'max_zoom',
23316
- 'do_not_tile_wms',
23317
- 'max_extent',
23318
- 'max_feature_count',
23319
- 'baselayer',
23320
- 'do_not_use_default_basemap',
23321
- 'external_viewer_url_template',
23322
- 'external_viewer_open_new_tab',
23323
- ], warnings, errors);
23324
- mapConfig =
23325
- parsedMapSection === null || parsedLayersSections === null
23326
- ? null
23327
- : {
23328
- MAX_ZOOM: parsedMapSection.max_zoom,
23329
- DO_NOT_TILE_WMS: parsedMapSection.do_not_tile_wms,
23330
- MAX_EXTENT: parsedMapSection.max_extent,
23331
- MAX_FEATURE_COUNT: parsedMapSection.max_feature_count,
23332
- EXTERNAL_VIEWER_URL_TEMPLATE: parsedMapSection.external_viewer_url_template,
23333
- EXTERNAL_VIEWER_OPEN_NEW_TAB: parsedMapSection.external_viewer_open_new_tab,
23334
- DO_NOT_USE_DEFAULT_BASEMAP: !!parsedMapSection.do_not_use_default_basemap,
23335
- MAP_LAYERS: parsedLayersSections.map((map_layer) => ({
23336
- TYPE: map_layer.type,
23337
- URL: map_layer.url,
23338
- NAME: map_layer.name,
23339
- DATA: map_layer.data,
23340
- STYLE_URL: map_layer.styleUrl,
23341
- ACCESS_TOKEN: map_layer.accessToken,
23342
- })),
23343
- };
23344
- const parsedThemeSection = parseConfigSection(parsed, 'theme', ['primary_color', 'secondary_color', 'main_color', 'background_color'], [
23345
- 'header_foreground_color',
23346
- 'main_font',
23347
- 'title_font',
23348
- 'fonts_stylesheet_url',
23349
- 'thumbnail_placeholder',
23350
- 'header_background',
23351
- 'favicon',
23352
- ], warnings, errors);
23353
- themeConfig =
23354
- parsedThemeSection === null
23355
- ? null
23356
- : {
23357
- PRIMARY_COLOR: parsedThemeSection.primary_color,
23358
- SECONDARY_COLOR: parsedThemeSection.secondary_color,
23359
- MAIN_COLOR: parsedThemeSection.main_color,
23360
- BACKGROUND_COLOR: parsedThemeSection.background_color,
23361
- HEADER_FOREGROUND_COLOR: parsedThemeSection.header_foreground_color,
23362
- THUMBNAIL_PLACEHOLDER: parsedThemeSection.thumbnail_placeholder,
23363
- HEADER_BACKGROUND: parsedThemeSection.header_background,
23364
- TITLE_FONT: parsedThemeSection.title_font,
23365
- MAIN_FONT: parsedThemeSection.main_font,
23366
- FONTS_STYLESHEET_URL: parsedThemeSection.fonts_stylesheet_url,
23367
- FAVICON: parsedThemeSection.favicon,
23368
- };
23369
- const parsedSearchSection = parseConfigSection(parsed, 'search', [], [
23370
- 'record_kind_quick_filter',
23371
- 'filter_geometry_data',
23372
- 'filter_geometry_url',
23373
- 'do_not_use_default_search_preset',
23374
- 'search_preset',
23375
- 'advanced_filters',
23376
- 'limit',
23377
- 'spatial_extent_max_file_size',
23378
- ], warnings, errors);
23379
- const parsedSearchParams = parseMultiConfigSection(parsed, 'search_preset', ['name'], ['sort', 'filters'], warnings, errors);
23380
- searchConfig =
23381
- parsedSearchSection === null
23382
- ? null
23383
- : {
23384
- RECORD_KIND_QUICK_FILTER: parsedSearchSection.record_kind_quick_filter,
23385
- FILTER_GEOMETRY_DATA: parsedSearchSection.filter_geometry_data,
23386
- FILTER_GEOMETRY_URL: parsedSearchSection.filter_geometry_url,
23387
- DO_NOT_USE_DEFAULT_SEARCH_PRESET: !!parsedSearchSection.do_not_use_default_search_preset,
23388
- SEARCH_PRESET: parsedSearchParams.map((param) => ({
23389
- sort: param.sort,
23390
- name: param.name,
23391
- filters: param.filters,
23392
- })),
23393
- ADVANCED_FILTERS: parsedSearchSection.advanced_filters,
23394
- LIMIT: parsedSearchSection.limit,
23395
- SPATIAL_EXTENT_MAX_FILE_SIZE: parsedSearchSection.spatial_extent_max_file_size,
23396
- };
23397
- const parsedMetadataQualitySection = parseConfigSection(parsed, 'metadata-quality', [], ['enabled'], warnings, errors);
23398
- metadataQualityConfig =
23399
- parsedMetadataQualitySection === null
23400
- ? null
23401
- : {
23402
- ENABLED: parsedMetadataQualitySection.enabled,
23403
- SORTABLE: parsedMetadataQualitySection.sortable,
23404
- };
23405
- let parsedEditingSection = parseConfigSection(parsed, 'editing', [], ['new_record_default_language', 'new_record_standard'], warnings, errors);
23406
- if (parsedEditingSection !== null &&
23407
- parsedEditingSection.new_record_default_language !== undefined) {
23408
- parsedEditingSection = checkNewRecordDefaultLanguage(parsedEditingSection, warnings);
23409
- }
23410
- if (parsedEditingSection !== null &&
23411
- parsedEditingSection.new_record_standard !== undefined) {
23412
- parsedEditingSection = checkNewRecordStandard(parsedEditingSection, warnings);
23413
- }
23414
- editorConfig =
23415
- parsedEditingSection === null
23416
- ? null
23417
- : {
23418
- NEW_RECORD_DEFAULT_LANGUAGE: parsedEditingSection.new_record_default_language,
23419
- NEW_RECORD_STANDARD: parsedEditingSection.new_record_standard,
23420
- };
23421
- customTranslations = parseTranslationsConfigSection(parsed, 'translations');
23422
- if (errors.length) {
23423
- throw new Error(`One or more mandatory settings were missing from the configuration file.
23424
- ${errors.join('\n')}`);
23425
- }
23426
- else if (warnings.length) {
23427
- console.warn(`One or more unexpected settings were encountered in the configuration file.
23428
- ${warnings.join('\n')}`);
23488
+ if (this.view) {
23489
+ this.view.destroy();
23429
23490
  }
23430
- appConfigLoaded = true;
23431
- });
23432
- }
23433
- function isConfigLoaded() {
23434
- return appConfigLoaded;
23435
- }
23436
- function _reset() {
23437
- globalConfig = null;
23438
- themeConfig = null;
23439
- editorConfig = null;
23440
- customTranslations = null;
23441
- }
23442
- const TRANSLATE_WITH_OVERRIDES_CONFIG = {
23443
- compiler: {
23444
- provide: TranslateCompiler,
23445
- useClass: TranslateMessageFormatCompiler,
23446
- },
23447
- loader: [
23448
- {
23449
- provide: TRANSLATE_HTTP_LOADER_CONFIG,
23450
- useValue: {
23451
- prefix: './assets/i18n/',
23452
- suffix: '.json',
23453
- },
23454
- },
23455
- {
23456
- provide: TranslateLoader,
23457
- useClass: FileWithOverridesTranslateLoader,
23458
- deps: [HttpClient, TRANSLATE_HTTP_LOADER_CONFIG],
23459
- },
23460
- ],
23461
- };
23462
-
23463
- const okAppConfigFixture = () => `
23464
- [global]
23465
- geonetwork4_api_url = "/geonetwork/srv/api"
23466
- proxy_path = "/proxy/?url="
23467
- metadata_language = "fre"
23468
- login_url = "/cas/login?service="
23469
- logout_url = "/geonetwork/signout"
23470
- settings_url = "/geonetwork/settings"
23471
- web_component_embedder_url = "/datahub/wc-embedder.html"
23472
-
23473
- [map]
23474
- max_zoom = 10
23475
- max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855]
23476
- do_not_use_default_basemap = false
23477
- external_viewer_url_template = 'https://example.com/myviewer?'
23478
- external_viewer_open_new_tab = true
23479
- [[map_layer]]
23480
- type = "wms"
23481
- url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
23482
- name = "masque_hdf_ign_carto_latin1"
23483
- [[map_layer]]
23484
- type = "wfs"
23485
- url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
23486
- name = "masque_hdf_ign_carto_latin1"
23487
-
23488
- [theme]
23489
- primary_color = "#093564"
23490
- secondary_color = "#c2e9dc"
23491
- main_color = "#212029" # All-purpose text color
23492
- background_color = "#fdfbff"
23493
- thumbnail_placeholder = 'assets/img/placeholder.svg'
23494
- header_background = 'teal'
23495
- header_foreground_color = "#872e2e"
23496
- main_font = 'sans-serif'
23497
- title_font = 'serif'
23498
- fonts_stylesheet_url = "https://fonts.googleapis.com/css2?family=Open+Sans"
23499
-
23500
- [search]
23501
- record_kind_quick_filter = false
23502
- filter_geometry_url = 'https://my.domain.org/geom.json'
23503
- do_not_use_default_search_preset = false
23504
- advanced_filters = ['publicationYear', 'documentStandard', 'inspireKeyword', 'topic', 'license']
23505
-
23506
- [[search_preset]]
23507
- sort = "-createDate"
23508
- name = 'filterByOrgs'
23509
- filters.q = 'Carto'
23510
- filters.organisation = ['Org1', 'Org2']
23511
- filters.format = ['ESRI Shapefile']
23512
- filters.topic = ['boundaries']
23513
- filters.publicationYear = ['2023', '2022']
23514
- filters.spatial = ['yes']
23515
-
23516
- [translations.en]
23517
- "my.first.key" = 'First label.'
23518
- "my.second.key" = """
23519
- Second label,
23520
- on two lines."""
23521
-
23522
- [translations.de]
23523
- my.first.key = 'Erste Etikett.'
23524
-
23525
- [translations.fr]
23526
- my.sample.text = "Un bon exemple de texte."
23527
- "my.quoted.text" = 'du texte entre guillements.'
23528
- `;
23529
- const appConfigWithTranslationFixture = () => okAppConfigFixture();
23530
- const minimalAppConfigFixture = () => `
23531
- [global]
23532
- geonetwork4_api_url = "/geonetwork/srv/api"
23533
- proxy_path = "/proxy/?url="
23534
-
23535
- [theme]
23536
- primary_color = "#093564"
23537
- secondary_color = "#c2e9dc"
23538
- main_color = "#212029" # All-purpose text color
23539
- background_color = "#fdfbff"
23540
- `;
23541
- const mapConfigFixture = () => ({
23542
- MAX_ZOOM: 10,
23543
- MAX_EXTENT: [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855],
23544
- DO_NOT_USE_DEFAULT_BASEMAP: false,
23545
- DO_NOT_TILE_WMS: false,
23546
- 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}"}]}]',
23547
- EXTERNAL_VIEWER_OPEN_NEW_TAB: true,
23548
- MAP_LAYERS: [
23549
- {
23550
- TYPE: 'xyz',
23551
- URL: 'https://some-basemap-server',
23552
- },
23553
- {
23554
- TYPE: 'wms',
23555
- URL: 'https://some-wms-server',
23556
- NAME: 'some_layername',
23557
- },
23558
- {
23559
- TYPE: 'wfs',
23560
- URL: 'https://some-wfs-server',
23561
- NAME: 'some_layername',
23562
- },
23563
- ],
23564
- });
23565
- const malformedConfigFixture = () => `
23566
- {
23567
- "I thought": "we were still doing json"
23491
+ }
23492
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23493
+ 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: [""] }); }
23568
23494
  }
23569
- `;
23570
- const missingMandatoryConfigFixture = () => `
23571
- [theme]
23572
- primary_color = "#093564"
23573
- secondary_color = "#c2e9dc"
23574
- background_color = "#fdfbff"
23575
- main_font = 'sans-serif'
23576
- `;
23577
- const wrongLanguageCodeConfigFixture = () => `
23578
- [global]
23579
- geonetwork4_api_url = "/geonetwork/srv/api"
23580
- proxy_path = "/proxy/?url="
23581
- metadata_language = "abc"
23582
-
23583
- [map]
23584
-
23585
- [theme]
23586
- primary_color = "#093564"
23587
- secondary_color = "#c2e9dc"
23588
- main_color = "#212029" # All-purpose text color
23589
- background_color = "#fdfbff"
23590
- `;
23591
- const unrecognizedKeysConfigFixture = () => `
23592
- [global]
23593
- geonetwork4_api_url = "/geonetwork/srv/api"
23594
- proxy_path = "/proxy/?url="
23595
- metadata_language = "fre"
23596
- another_path = '/whatever'
23597
-
23598
- [map]
23599
- max_zoom = 10
23600
- max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855]
23601
- do_not_use_default_basemap = false
23602
- external_viewer_url_template = 'https://example.com/myviewer?'
23603
- external_viewer_open_new_tab = true
23604
- another_zoom = 15
23605
- [[map_layer]]
23606
- type = "wms"
23607
- url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
23608
- name = "masque_hdf_ign_carto_latin1"
23609
- [[map_layer]]
23610
- type = "wfs"
23611
- url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
23612
- name = "masque_hdf_ign_carto_latin1"
23613
- another_layer = "wrong layer definition"
23614
-
23615
- [theme]
23616
- primary_color = "#093564"
23617
- secondary_color = "#c2e9dc"
23618
- main_color = "#212029" # All-purpose text color
23619
- background_color = "#fdfbff"
23620
- thumbnail_placeholder = 'assets/img/placeholder.svg'
23621
- header_background = 'teal'
23622
- header_foreground_color = "#872e2e"
23623
- main_font = 'sans-serif'
23624
- title_font = 'serif'
23625
- another_color = 'red'
23626
- `;
23495
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopoverComponent, decorators: [{
23496
+ type: Component,
23497
+ args: [{ selector: 'gn-ui-popover', standalone: true, imports: [], template: "<span #popoverContent>\n <ng-content></ng-content>\n</span>\n" }]
23498
+ }], propDecorators: { popoverContent: [{
23499
+ type: ViewChild,
23500
+ args: ['popoverContent', { static: false }]
23501
+ }], content: [{
23502
+ type: Input
23503
+ }], theme: [{
23504
+ type: Input
23505
+ }] } });
23627
23506
 
23628
- function getMapContextLayerFromConfig(config) {
23629
- switch (config.TYPE) {
23630
- case 'wms':
23631
- return {
23632
- type: 'wms',
23633
- url: config.URL,
23634
- name: config.NAME,
23635
- };
23636
- case 'wfs':
23637
- return {
23638
- type: 'wfs',
23639
- url: config.URL,
23640
- featureType: config.NAME,
23641
- };
23642
- case 'xyz':
23643
- return {
23644
- type: config.TYPE,
23645
- url: config.URL,
23646
- };
23647
- case 'geojson':
23648
- return {
23649
- type: config.TYPE,
23650
- ...(config.DATA ? { data: config.DATA } : { url: config.URL }),
23651
- };
23652
- case 'maplibre-style':
23653
- return {
23654
- type: config.TYPE,
23655
- styleUrl: config.STYLE_URL,
23656
- accessToken: config.ACCESS_TOKEN,
23657
- };
23658
- }
23507
+ class LoadingMaskComponent {
23508
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LoadingMaskComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23509
+ 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 }); }
23659
23510
  }
23511
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LoadingMaskComponent, decorators: [{
23512
+ type: Component,
23513
+ 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"] }]
23514
+ }], propDecorators: { message: [{
23515
+ type: Input
23516
+ }] } });
23660
23517
 
23661
- class MapStyleService {
23662
- constructor() {
23663
- this.styles = {
23664
- default: this.createStyleFunction(this.createGeometryStyles({
23665
- color: isConfigLoaded() ? getThemeConfig().PRIMARY_COLOR : 'blue',
23666
- })),
23667
- defaultHL: this.createStyleFunction(this.createGeometryStyles({
23668
- color: isConfigLoaded() ? getThemeConfig().SECONDARY_COLOR : 'red',
23669
- isFocused: true,
23670
- })),
23671
- };
23672
- }
23673
- createGeometryStyles(options) {
23674
- const { color, isFocused } = options;
23675
- const zIndex = isFocused ? 10 : undefined;
23676
- return {
23677
- polygon: new Style({
23678
- fill: new Fill({
23679
- color: this.computeTransparentFillColor(color),
23680
- }),
23681
- stroke: new Stroke({
23682
- color: 'white',
23683
- width: 2,
23684
- }),
23685
- zIndex,
23686
- }),
23687
- point: new Style({
23688
- image: new CircleStyle({
23689
- fill: new Fill({
23690
- color,
23691
- }),
23692
- stroke: new Stroke({
23693
- color: 'white',
23694
- width: isFocused ? 3 : 2,
23695
- }),
23696
- radius: isFocused ? 8 : 7,
23697
- }),
23698
- zIndex,
23699
- }),
23700
- line: [
23701
- new Style({
23702
- stroke: new Stroke({
23703
- color: 'white',
23704
- width: isFocused ? 8 : 6,
23705
- }),
23706
- zIndex,
23707
- }),
23708
- new Style({
23709
- stroke: new Stroke({
23710
- color,
23711
- width: isFocused ? 3 : 2,
23712
- }),
23713
- zIndex,
23714
- }),
23715
- ],
23716
- };
23717
- }
23718
- createStyleFunction(styleByGeometryType) {
23719
- return (feature) => {
23720
- const geometryType = feature?.getGeometry()?.getType();
23721
- switch (geometryType) {
23722
- case 'LinearRing':
23723
- case 'LineString':
23724
- case 'MultiLineString':
23725
- return styleByGeometryType.line;
23726
- case 'Point':
23727
- case 'MultiPoint':
23728
- return styleByGeometryType.point;
23729
- case 'Circle':
23730
- case 'Polygon':
23731
- case 'MultiPolygon':
23732
- return styleByGeometryType.polygon;
23733
- default:
23734
- return styleByGeometryType.point;
23735
- }
23736
- };
23737
- }
23738
- computeTransparentFillColor(color, alpha = 0.25) {
23739
- return chroma(color).alpha(alpha).css();
23740
- }
23741
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: MapStyleService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
23742
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: MapStyleService, providedIn: 'root' }); }
23518
+ class ColorScaleComponent {
23519
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ColorScaleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23520
+ 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: [""] }); }
23743
23521
  }
23744
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: MapStyleService, decorators: [{
23745
- type: Injectable,
23746
- args: [{
23747
- providedIn: 'root',
23748
- }]
23522
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ColorScaleComponent, decorators: [{
23523
+ type: Component,
23524
+ 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" }]
23749
23525
  }] });
23750
23526
 
23751
- const colorPrimary = 'blue';
23752
- const colorSecondary = 'red';
23753
- const colorStroke = 'white';
23754
- const defaultMapStyleFixture = () => new Style({
23755
- image: new Circle({
23756
- fill: new Fill({
23757
- color: colorPrimary,
23758
- }),
23759
- stroke: new Stroke({
23760
- color: colorStroke,
23761
- width: 2,
23762
- }),
23763
- radius: 5,
23764
- }),
23765
- fill: new Fill({
23766
- color: 'rgba(0,0,255,0.25)',
23767
- }),
23768
- stroke: new Stroke({
23769
- color: colorStroke,
23770
- width: 2,
23771
- }),
23772
- });
23773
- const defaultMapStyleHlFixture = () => new Style({
23774
- image: new Circle({
23775
- fill: new Fill({
23776
- color: colorSecondary,
23777
- }),
23778
- stroke: new Stroke({
23779
- color: colorStroke,
23780
- width: 3,
23781
- }),
23782
- radius: 6,
23783
- }),
23784
- fill: new Fill({
23785
- color: 'rgba(255,0,0,0.25)',
23786
- }),
23787
- stroke: new Stroke({
23788
- color: colorSecondary,
23789
- width: 3,
23790
- }),
23791
- });
23792
-
23793
- class AnchorLinkDirective {
23527
+ class PopupAlertComponent {
23794
23528
  constructor() {
23795
23529
  this.changeDetector = inject(ChangeDetectorRef);
23796
- this.disabled = false;
23797
- this.observer = new MutationObserver(() => {
23798
- this.refreshDisabledState();
23799
- });
23800
- this.inView = false;
23801
- this.initialized = false;
23530
+ this.type = 'info';
23531
+ this.position = 'top';
23532
+ this.expanded = false;
23533
+ this.timeout = null;
23802
23534
  }
23803
- get elementClass() {
23804
- if (this.disabled) {
23805
- return this.disabledClass;
23806
- }
23807
- if (this.inView) {
23808
- return `${this.inViewClass} ${this.enabledClass}`;
23809
- }
23810
- else {
23811
- return `${this.outOfViewClass} ${this.enabledClass}`;
23812
- }
23535
+ get showDuration() {
23536
+ const chars = this.content.nativeElement.innerHTML.length;
23537
+ return Math.max(3000, chars * 20);
23813
23538
  }
23814
23539
  ngOnInit() {
23815
- this.observer.observe(document.body, {
23816
- childList: true,
23817
- subtree: true,
23818
- });
23819
- this.refreshDisabledState();
23820
- }
23821
- ngAfterViewChecked() {
23822
- if (!this.initialized && !this.disabled) {
23823
- const target = document.getElementById(this.targetId);
23824
- if (target) {
23825
- this.initializeIntersectionObserver(target);
23826
- this.initialized = true;
23827
- }
23828
- }
23829
- }
23830
- initializeIntersectionObserver(target) {
23831
- this.intersectionObserver = new IntersectionObserver((entries) => {
23832
- entries.forEach((entry) => {
23833
- this.inView = entry.isIntersecting;
23834
- this.changeDetector.detectChanges();
23835
- });
23836
- }, {
23837
- root: null,
23838
- rootMargin: '-30% 0% -60% 0%',
23839
- });
23840
- this.intersectionObserver.observe(target);
23540
+ this.expandAndClose();
23841
23541
  }
23842
- ngOnDestroy() {
23843
- this.observer.disconnect();
23844
- if (this.intersectionObserver) {
23845
- this.intersectionObserver.disconnect();
23846
- }
23542
+ expand() {
23543
+ this.expanded = true;
23544
+ this.changeDetector.detectChanges();
23545
+ clearTimeout(this.timeout);
23847
23546
  }
23848
- refreshDisabledState() {
23849
- const targetNotPresent = !document.getElementById(this.targetId);
23850
- if (targetNotPresent !== this.disabled) {
23851
- this.disabled = targetNotPresent;
23547
+ expandAndClose() {
23548
+ this.expanded = true;
23549
+ this.changeDetector.detectChanges();
23550
+ clearTimeout(this.timeout);
23551
+ this.timeout = setTimeout(() => {
23552
+ this.expanded = false;
23852
23553
  this.changeDetector.detectChanges();
23853
- }
23854
- }
23855
- scrollToTarget() {
23856
- const target = document.getElementById(this.targetId);
23857
- if (!target)
23858
- return;
23859
- target.scrollIntoView({
23860
- behavior: 'smooth',
23861
- block: 'start',
23862
- });
23554
+ }, this.showDuration);
23863
23555
  }
23864
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AnchorLinkDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
23865
- 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 }); }
23866
- }
23867
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AnchorLinkDirective, decorators: [{
23868
- type: Directive,
23869
- args: [{
23870
- selector: '[gnUiAnchorLink]',
23871
- standalone: true,
23872
- }]
23873
- }], propDecorators: { targetId: [{
23874
- type: Input,
23875
- args: ['gnUiAnchorLink']
23876
- }], disabledClass: [{
23877
- type: Input,
23878
- args: ['gnUiAnchorLinkDisabledClass']
23879
- }], enabledClass: [{
23880
- type: Input,
23881
- args: ['gnUiAnchorLinkEnabledClass']
23882
- }], inViewClass: [{
23883
- type: Input,
23884
- args: ['gnUiAnchorLinkInViewClass']
23885
- }], outOfViewClass: [{
23886
- type: Input,
23887
- args: ['gnUiAnchorLinkOutOfViewClass']
23888
- }], elementClass: [{
23889
- type: HostBinding,
23890
- args: ['class']
23891
- }], scrollToTarget: [{
23892
- type: HostListener,
23893
- args: ['click']
23894
- }] } });
23895
-
23896
- class PaginationDotsComponent {
23897
- constructor() {
23898
- this.containerClass = '';
23899
- }
23900
- // 1-based
23901
- get steps() {
23902
- return Array.from({ length: this.listComponent.pagesCount }, (_, i) => i + 1);
23903
- }
23904
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PaginationDotsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23905
- 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: [
23906
- provideIcons({
23907
- iconoirNavArrowRight,
23908
- iconoirNavArrowLeft,
23909
- }),
23910
- ] }); }
23911
- }
23912
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PaginationDotsComponent, decorators: [{
23913
- type: Component,
23914
- args: [{ selector: 'gn-ui-pagination-dots', standalone: true, imports: [CommonModule], viewProviders: [
23915
- provideIcons({
23916
- iconoirNavArrowRight,
23917
- iconoirNavArrowLeft,
23918
- }),
23919
- ], 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"] }]
23920
- }], propDecorators: { listComponent: [{
23921
- type: Input
23922
- }], containerClass: [{
23923
- type: Input
23924
- }] } });
23925
-
23926
- class CarouselComponent {
23927
- constructor() {
23928
- this.changeDetector = inject(ChangeDetectorRef);
23929
- this.containerClass = '';
23930
- this.stepsContainerClass = 'w-full bottom-0 top-auto';
23931
- this.currentStepChange = new EventEmitter();
23932
- this.steps = [];
23933
- this.currentStep = 0;
23934
- this.refreshSteps = () => {
23935
- this.steps = this.emblaApi.scrollSnapList();
23936
- this.currentStep = this.emblaApi.selectedScrollSnap();
23937
- this.currentStepChange.emit(this.currentStep);
23938
- this.changeDetector.detectChanges();
23939
- };
23940
- }
23941
- // Paginable API
23942
- get isFirstPage() {
23943
- return this.currentStep === 0;
23944
- }
23945
- get isLastPage() {
23946
- return this.currentStep === this.steps.length - 1;
23947
- }
23948
- get currentPage() {
23949
- return this.currentStep + 1; // this is 1-based
23950
- }
23951
- get pagesCount() {
23952
- return this.steps.length;
23953
- }
23954
- goToPage(stepIndex) {
23955
- this.emblaApi.scrollTo(stepIndex - 1); // this is 0-based
23956
- }
23957
- goToPrevPage() {
23958
- if (this.isFirstPage)
23959
- return;
23960
- this.emblaApi.scrollPrev();
23961
- }
23962
- goToNextPage() {
23963
- if (this.isLastPage)
23964
- return;
23965
- this.emblaApi.scrollNext();
23966
- }
23967
- ngAfterViewInit() {
23968
- this.emblaApi = EmblaCarousel(this.carouselOverflowContainer.nativeElement, {
23969
- duration: 15,
23970
- });
23971
- this.emblaApi
23972
- .on('init', this.refreshSteps)
23973
- .on('reInit', this.refreshSteps)
23974
- .on('select', this.refreshSteps);
23975
- }
23976
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CarouselComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23977
- 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 }); }
23978
- }
23979
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CarouselComponent, decorators: [{
23980
- type: Component,
23981
- 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"] }]
23982
- }], propDecorators: { carouselOverflowContainer: [{
23983
- type: ViewChild,
23984
- args: ['carouselOverflowContainer']
23985
- }], containerClass: [{
23986
- type: Input
23987
- }], stepsContainerClass: [{
23988
- type: Input
23989
- }], currentStepChange: [{
23990
- type: Output
23991
- }] } });
23992
-
23993
- class ProgressBarComponent {
23994
- constructor() {
23995
- this.value = 0;
23996
- this.type = 'default';
23997
- }
23998
- get progress() {
23999
- return this.value > 0 ? (this.value < 100 ? this.value : 100) : 0;
24000
- }
24001
- get color() {
24002
- switch (this.type) {
24003
- case 'default':
24004
- return {
24005
- outerBar: 'bg-gray-200',
24006
- innerBar: 'bg-gray-100',
24007
- text: 'text-gray-900',
24008
- };
24009
- case 'primary':
24010
- return {
24011
- outerBar: 'bg-primary',
24012
- innerBar: 'bg-primary-lighter',
24013
- text: 'text-white',
24014
- };
24015
- case 'secondary':
24016
- return {
24017
- outerBar: 'bg-secondary',
24018
- innerBar: 'bg-secondary-lighter',
24019
- text: 'text-white',
24020
- };
24021
- case 'light':
24022
- return {
24023
- outerBar: 'bg-primary-white',
24024
- innerBar: 'bg-primary',
24025
- text: 'text-main',
24026
- };
24027
- }
24028
- }
24029
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProgressBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24030
- 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"] }); }
24031
- }
24032
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProgressBarComponent, decorators: [{
24033
- type: Component,
24034
- 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"] }]
24035
- }], propDecorators: { value: [{
24036
- type: Input
24037
- }], type: [{
24038
- type: Input
24039
- }] } });
24040
-
24041
- class PopoverComponent {
24042
- constructor() {
24043
- this.viewContainerRef = inject(ViewContainerRef);
24044
- this.renderer = inject(Renderer2);
24045
- }
24046
- getContent() {
24047
- if (this.content instanceof TemplateRef) {
24048
- if (this.view) {
24049
- this.view.destroy();
24050
- }
24051
- this.view = this.viewContainerRef.createEmbeddedView(this.content);
24052
- this.view.detectChanges();
24053
- const wrapper = this.renderer.createElement('div'); // Create a wrapper div
24054
- this.view.rootNodes.forEach((node) => {
24055
- this.renderer.appendChild(wrapper, node); // Append each root node to the wrapper
24056
- });
24057
- return wrapper;
24058
- }
24059
- return this.content;
24060
- }
24061
- ngAfterViewInit() {
24062
- this.tippyInstance = tippy(this.popoverContent.nativeElement, {
24063
- content: this.getContent(),
24064
- allowHTML: true,
24065
- theme: this.theme,
24066
- });
24067
- }
24068
- ngOnChanges(changes) {
24069
- if (changes['theme']) {
24070
- this.theme = changes['theme'].currentValue;
24071
- if (this.tippyInstance) {
24072
- this.tippyInstance.setProps({ theme: this.theme });
24073
- }
24074
- }
24075
- if (changes['content']) {
24076
- this.content = changes['content'].currentValue;
24077
- if (this.tippyInstance) {
24078
- this.tippyInstance.setContent(this.getContent());
24079
- }
24080
- }
24081
- }
24082
- ngOnDestroy() {
24083
- if (this.tippyInstance) {
24084
- this.tippyInstance.destroy();
24085
- }
24086
- if (this.view) {
24087
- this.view.destroy();
24088
- }
24089
- }
24090
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24091
- 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: [""] }); }
24092
- }
24093
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopoverComponent, decorators: [{
24094
- type: Component,
24095
- args: [{ selector: 'gn-ui-popover', standalone: true, imports: [], template: "<span #popoverContent>\n <ng-content></ng-content>\n</span>\n" }]
24096
- }], propDecorators: { popoverContent: [{
24097
- type: ViewChild,
24098
- args: ['popoverContent', { static: false }]
24099
- }], content: [{
24100
- type: Input
24101
- }], theme: [{
24102
- type: Input
24103
- }] } });
24104
-
24105
- class LoadingMaskComponent {
24106
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LoadingMaskComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24107
- 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 }); }
24108
- }
24109
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LoadingMaskComponent, decorators: [{
24110
- type: Component,
24111
- 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"] }]
24112
- }], propDecorators: { message: [{
24113
- type: Input
24114
- }] } });
24115
-
24116
- class ColorScaleComponent {
24117
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ColorScaleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24118
- 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: [""] }); }
24119
- }
24120
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ColorScaleComponent, decorators: [{
24121
- type: Component,
24122
- 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" }]
24123
- }] });
24124
-
24125
- class PopupAlertComponent {
24126
- constructor() {
24127
- this.changeDetector = inject(ChangeDetectorRef);
24128
- this.type = 'info';
24129
- this.position = 'top';
24130
- this.expanded = false;
24131
- this.timeout = null;
24132
- }
24133
- get showDuration() {
24134
- const chars = this.content.nativeElement.innerHTML.length;
24135
- return Math.max(3000, chars * 20);
24136
- }
24137
- ngOnInit() {
24138
- this.expandAndClose();
24139
- }
24140
- expand() {
24141
- this.expanded = true;
24142
- this.changeDetector.detectChanges();
24143
- clearTimeout(this.timeout);
24144
- }
24145
- expandAndClose() {
24146
- this.expanded = true;
24147
- this.changeDetector.detectChanges();
24148
- clearTimeout(this.timeout);
24149
- this.timeout = setTimeout(() => {
24150
- this.expanded = false;
24151
- this.changeDetector.detectChanges();
24152
- }, this.showDuration);
24153
- }
24154
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopupAlertComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24155
- 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: [
24156
- provideIcons({ matErrorOutlineOutline }),
24157
- provideNgIconsConfig({
24158
- size: '1.5em',
24159
- }),
24160
- ], 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 }); }
23556
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopupAlertComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23557
+ 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: [
23558
+ provideIcons({ matErrorOutlineOutline }),
23559
+ provideNgIconsConfig({
23560
+ size: '1.5em',
23561
+ }),
23562
+ ], 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 }); }
24161
23563
  }
24162
23564
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopupAlertComponent, decorators: [{
24163
23565
  type: Component,
@@ -24306,15 +23708,15 @@ class AutocompleteComponent {
24306
23708
  getExtraClass() {
24307
23709
  if (this.allowSubmit) {
24308
23710
  if (this.enterButton) {
24309
- return 'border rounded-lg absolute w-8 h-8 right-[calc(var(--icon-width)+var(--icon-padding))] inset-y-[--icon-padding]';
23711
+ return 'border rounded-lg absolute w-8 h-8 right-[calc(var(--icon-width)+var(--icon-padding))] top-1/2 -translate-y-1/2';
24310
23712
  }
24311
23713
  else {
24312
- 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))]';
23714
+ 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';
24313
23715
  }
24314
23716
  }
24315
23717
  else {
24316
23718
  if (!this.enterButton) {
24317
- return 'border rounded-lg absolute w-8 h-8 right-2 inset-y-2';
23719
+ return 'border rounded-lg absolute w-8 h-8 right-2 top-1/2 -translate-y-1/2';
24318
23720
  }
24319
23721
  }
24320
23722
  return 'border rounded-lg absolute w-8 h-8';
@@ -24459,7 +23861,7 @@ class AutocompleteComponent {
24459
23861
  provideNgIconsConfig({
24460
23862
  size: '1.75rem',
24461
23863
  }),
24462
- ], viewQueries: [{ propertyName: "triggerRef", first: true, predicate: MatAutocompleteTrigger, descendants: true }, { propertyName: "autocomplete", first: true, predicate: MatAutocomplete, descendants: true }, { propertyName: "inputRef", first: true, predicate: ["searchInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<span class=\"w-full inline-block relative\">\n @if (!allowSubmit) {\n <div\n class=\"absolute inset-y-[--icon-padding] left-[--icon-padding] w-[--icon-width] pointer-events-none\"\n >\n <ng-icon name=\"iconoirSearch\" class=\"text-primary search\"></ng-icon>\n </div>\n }\n <div class=\"flex flex-row\">\n <input\n #searchInput\n type=\"text\"\n class=\"gn-ui-text-input\"\n (input)=\"handleInput($event)\"\n [placeholder]=\"placeholder\"\n [formControl]=\"control\"\n [matAutocomplete]=\"auto\"\n (keyup.enter)=\"handleSearch()\"\n [ngClass]=\"{\n 'text-primary': searchActive && enterButton,\n 'text-gray-900': !searchActive,\n 'px-[--icon-width]': !allowSubmit,\n }\"\n />\n @if (searchInput.value && displayEnterBtn) {\n <gn-ui-button\n type=\"gray\"\n extraClass=\"w-32 h-8 !opacity-100\"\n [ngStyle]=\"{ left: enterBtnPosition + 'px' }\"\n class=\"absolute\"\n disabled=\"true\"\n >\n <ng-icon\n name=\"iconoirLongArrowDownLeft\"\n class=\"!w-4 text-gray-900 font-bold\"\n ></ng-icon>\n <span translate class=\"text-bold text-gray-900 font-bold\">\n Enter to search\n </span>\n </gn-ui-button>\n }\n </div>\n @if (searchInput.value) {\n <gn-ui-button\n type=\"primary\"\n [extraClass]=\"getExtraClass()\"\n data-test=\"clear-btn\"\n aria-label=\"Clear\"\n (buttonClick)=\"clear()\"\n >\n <ng-icon class=\"text-white\" name=\"matClose\"></ng-icon>\n </gn-ui-button>\n }\n @if (allowSubmit) {\n <gn-ui-button\n type=\"light\"\n extraClass=\"border-0 border-l-[1px] border-gray-300 hover:border-gray-500 text-primary hover:text-primary-darkest focus:text-primary-darkest absolute inset-y-[--icon-padding] right-[--icon-padding]\"\n aria-label=\"Trigger search\"\n data-test=\"autocomplete-submit-btn\"\n (buttonClick)=\"handleSearch()\"\n >\n <ng-icon name=\"iconoirSearch\"></ng-icon>\n </gn-ui-button>\n }\n\n @if (error) {\n <gn-ui-popup-alert\n class=\"absolute mt-2 w-full top-[100%] left-0\"\n icon=\"matErrorOutlineOutline\"\n position=\"top\"\n type=\"warning\"\n >\n <span translate>search.autocomplete.error</span>\n {{ error }}\n </gn-ui-popup-alert>\n }\n</span>\n\n<mat-autocomplete\n #auto=\"matAutocomplete\"\n (optionSelected)=\"handleSelection($event)\"\n [displayWith]=\"displayWithFnInternal\"\n>\n @for (suggestion of suggestions$ | async; track suggestion) {\n <mat-option [value]=\"suggestion\" class=\"p-2 suggestion text-start\">\n @if (displayWithTemplate) {\n <ng-container\n *ngTemplateOutlet=\"\n displayWithTemplate;\n context: { $implicit: suggestion }\n \"\n ></ng-container>\n } @else {\n {{ displayWithFnInternal(suggestion) }}\n }\n </mat-option>\n }\n</mat-autocomplete>\n", styles: ["span{--icon-padding: var(--gn-ui-text-input-border-size, 2px);--icon-width: calc(var(--gn-ui-text-input-padding, .6em) * 2 + 1.25em)}span>*:not(gn-ui-popup-alert){font-size:var(--gn-ui-text-input-font-size, 1em)}input:placeholder-shown{text-overflow:ellipsis}gn-ui-button{--gn-ui-button-rounded: 0;--gn-ui-button-width: var(--icon-width);--gn-ui-button-padding: 0;font-size:1em}ng-icon.search{margin-top:.6rem;margin-left:.6rem}gn-ui-button:last-of-type{--gn-ui-button-rounded: 0 var(--gn-ui-text-input-rounded, .25em) var(--gn-ui-text-input-rounded, .25em) 0}.mat-mdc-option.suggestion.mat-mdc-option-active{background-color:var(--color-primary-lightest)}\n"], dependencies: [{ kind: "component", type: PopupAlertComponent, selector: "gn-ui-popup-alert", inputs: ["icon", "type", "position"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i1$2.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i1$2.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i1$2.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
23864
+ ], 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 }); }
24463
23865
  }
24464
23866
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AutocompleteComponent, decorators: [{
24465
23867
  type: Component,
@@ -24480,7 +23882,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
24480
23882
  provideNgIconsConfig({
24481
23883
  size: '1.75rem',
24482
23884
  }),
24483
- ], template: "<span class=\"w-full inline-block relative\">\n @if (!allowSubmit) {\n <div\n class=\"absolute inset-y-[--icon-padding] left-[--icon-padding] w-[--icon-width] pointer-events-none\"\n >\n <ng-icon name=\"iconoirSearch\" class=\"text-primary search\"></ng-icon>\n </div>\n }\n <div class=\"flex flex-row\">\n <input\n #searchInput\n type=\"text\"\n class=\"gn-ui-text-input\"\n (input)=\"handleInput($event)\"\n [placeholder]=\"placeholder\"\n [formControl]=\"control\"\n [matAutocomplete]=\"auto\"\n (keyup.enter)=\"handleSearch()\"\n [ngClass]=\"{\n 'text-primary': searchActive && enterButton,\n 'text-gray-900': !searchActive,\n 'px-[--icon-width]': !allowSubmit,\n }\"\n />\n @if (searchInput.value && displayEnterBtn) {\n <gn-ui-button\n type=\"gray\"\n extraClass=\"w-32 h-8 !opacity-100\"\n [ngStyle]=\"{ left: enterBtnPosition + 'px' }\"\n class=\"absolute\"\n disabled=\"true\"\n >\n <ng-icon\n name=\"iconoirLongArrowDownLeft\"\n class=\"!w-4 text-gray-900 font-bold\"\n ></ng-icon>\n <span translate class=\"text-bold text-gray-900 font-bold\">\n Enter to search\n </span>\n </gn-ui-button>\n }\n </div>\n @if (searchInput.value) {\n <gn-ui-button\n type=\"primary\"\n [extraClass]=\"getExtraClass()\"\n data-test=\"clear-btn\"\n aria-label=\"Clear\"\n (buttonClick)=\"clear()\"\n >\n <ng-icon class=\"text-white\" name=\"matClose\"></ng-icon>\n </gn-ui-button>\n }\n @if (allowSubmit) {\n <gn-ui-button\n type=\"light\"\n extraClass=\"border-0 border-l-[1px] border-gray-300 hover:border-gray-500 text-primary hover:text-primary-darkest focus:text-primary-darkest absolute inset-y-[--icon-padding] right-[--icon-padding]\"\n aria-label=\"Trigger search\"\n data-test=\"autocomplete-submit-btn\"\n (buttonClick)=\"handleSearch()\"\n >\n <ng-icon name=\"iconoirSearch\"></ng-icon>\n </gn-ui-button>\n }\n\n @if (error) {\n <gn-ui-popup-alert\n class=\"absolute mt-2 w-full top-[100%] left-0\"\n icon=\"matErrorOutlineOutline\"\n position=\"top\"\n type=\"warning\"\n >\n <span translate>search.autocomplete.error</span>\n {{ error }}\n </gn-ui-popup-alert>\n }\n</span>\n\n<mat-autocomplete\n #auto=\"matAutocomplete\"\n (optionSelected)=\"handleSelection($event)\"\n [displayWith]=\"displayWithFnInternal\"\n>\n @for (suggestion of suggestions$ | async; track suggestion) {\n <mat-option [value]=\"suggestion\" class=\"p-2 suggestion text-start\">\n @if (displayWithTemplate) {\n <ng-container\n *ngTemplateOutlet=\"\n displayWithTemplate;\n context: { $implicit: suggestion }\n \"\n ></ng-container>\n } @else {\n {{ displayWithFnInternal(suggestion) }}\n }\n </mat-option>\n }\n</mat-autocomplete>\n", styles: ["span{--icon-padding: var(--gn-ui-text-input-border-size, 2px);--icon-width: calc(var(--gn-ui-text-input-padding, .6em) * 2 + 1.25em)}span>*:not(gn-ui-popup-alert){font-size:var(--gn-ui-text-input-font-size, 1em)}input:placeholder-shown{text-overflow:ellipsis}gn-ui-button{--gn-ui-button-rounded: 0;--gn-ui-button-width: var(--icon-width);--gn-ui-button-padding: 0;font-size:1em}ng-icon.search{margin-top:.6rem;margin-left:.6rem}gn-ui-button:last-of-type{--gn-ui-button-rounded: 0 var(--gn-ui-text-input-rounded, .25em) var(--gn-ui-text-input-rounded, .25em) 0}.mat-mdc-option.suggestion.mat-mdc-option-active{background-color:var(--color-primary-lightest)}\n"] }]
23885
+ ], 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"] }]
24484
23886
  }], propDecorators: { placeholder: [{
24485
23887
  type: Input
24486
23888
  }], enterButton: [{
@@ -25956,7 +25358,8 @@ class SearchFeatureCatalogComponent {
25956
25358
  const filtered = (this.featureCatalog?.featureTypes || []).filter((featureType) => {
25957
25359
  const fuzzyFilter = createFuzzyFilter(searchTerm);
25958
25360
  return (fuzzyFilter(featureType.name) ||
25959
- (featureType.definition && fuzzyFilter(featureType.definition)));
25361
+ (featureType.description &&
25362
+ fuzzyFilter(featureType.description)));
25960
25363
  });
25961
25364
  this.filteredFeatureCatalog = {
25962
25365
  ...this.featureCatalog,
@@ -26044,6 +25447,8 @@ marker('search.filters.spatialExtent.error.title');
26044
25447
  marker('search.filters.spatialExtent.error.outOfBounds');
26045
25448
  const LABEL_FROM_FILE = marker('search.filters.spatialExtent.bboxFromFile');
26046
25449
  const LABEL_FROM_FILE_DELETE = marker('search.filters.spatialExtent.bboxFromFileDelete');
25450
+ const LABEL_FROM_LOCATION = marker('search.filters.spatialExtent.bboxFromLocation');
25451
+ const LABEL_FROM_LOCATION_DELETE = marker('search.filters.spatialExtent.bboxFromLocationDelete');
26047
25452
  const LABEL_INITIAL = marker('search.filters.spatialExtent.bboxInitial');
26048
25453
  const LABEL_INITIAL_DELETE = marker('search.filters.spatialExtent.bboxInitialDelete');
26049
25454
  class SpatialExtentDropdownComponent {
@@ -26055,6 +25460,7 @@ class SpatialExtentDropdownComponent {
26055
25460
  this.errorChange = new EventEmitter();
26056
25461
  this.bbox = null;
26057
25462
  this.fileName = '';
25463
+ this.locationName = '';
26058
25464
  this.overlayPositions = [
26059
25465
  {
26060
25466
  originX: 'start',
@@ -26085,13 +25491,21 @@ class SpatialExtentDropdownComponent {
26085
25491
  return !!this.bbox;
26086
25492
  }
26087
25493
  get selectionLabelKey() {
26088
- return this.fileName ? LABEL_FROM_FILE : LABEL_INITIAL;
25494
+ if (this.fileName)
25495
+ return LABEL_FROM_FILE;
25496
+ if (this.locationName)
25497
+ return LABEL_FROM_LOCATION;
25498
+ return LABEL_INITIAL;
26089
25499
  }
26090
25500
  get selectionDeleteLabelKey() {
26091
- return this.fileName ? LABEL_FROM_FILE_DELETE : LABEL_INITIAL_DELETE;
25501
+ if (this.fileName)
25502
+ return LABEL_FROM_FILE_DELETE;
25503
+ if (this.locationName)
25504
+ return LABEL_FROM_LOCATION_DELETE;
25505
+ return LABEL_INITIAL_DELETE;
26092
25506
  }
26093
25507
  get selectionLabelParams() {
26094
- return { fileName: this.fileName };
25508
+ return { fileName: this.fileName, locationName: this.locationName };
26095
25509
  }
26096
25510
  openOverlay() {
26097
25511
  this.overlayMinWidth =
@@ -26128,6 +25542,7 @@ class SpatialExtentDropdownComponent {
26128
25542
  }
26129
25543
  this.bbox = bbox;
26130
25544
  this.fileName = file.name;
25545
+ this.locationName = '';
26131
25546
  this.bboxChange.emit(bbox);
26132
25547
  this.cd.markForCheck();
26133
25548
  }
@@ -26136,6 +25551,15 @@ class SpatialExtentDropdownComponent {
26136
25551
  return;
26137
25552
  }
26138
25553
  }
25554
+ setLocationBbox(bbox, name) {
25555
+ this.errorKey = null;
25556
+ this.bbox = bbox;
25557
+ this.locationName = name;
25558
+ this.fileName = '';
25559
+ this.fileInput?.clear();
25560
+ this.bboxChange.emit(bbox);
25561
+ this.cd.markForCheck();
25562
+ }
26139
25563
  handleFileError(error) {
26140
25564
  if (error === 'file-too-large') {
26141
25565
  this.setError(marker('search.filters.spatialExtent.error.fileTooLarge'), {
@@ -26154,6 +25578,7 @@ class SpatialExtentDropdownComponent {
26154
25578
  removeSelection(event) {
26155
25579
  this.bbox = null;
26156
25580
  this.fileName = '';
25581
+ this.locationName = '';
26157
25582
  this.errorKey = null;
26158
25583
  this.fileInput?.clear();
26159
25584
  this.bboxChange.emit(null);
@@ -26170,7 +25595,7 @@ class SpatialExtentDropdownComponent {
26170
25595
  matExpandLess,
26171
25596
  matExpandMore,
26172
25597
  }),
26173
- ], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "fileInput", first: true, predicate: DragAndDropFileInputComponent, descendants: true }], ngImport: i0, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelection) {\n <div\n class=\"bg-primary-lighter text-white shrink-0 rounded-full font-bold text-[12px] w-[22px] h-[22px] flex items-center justify-center\"\n >\n 1\n </div>\n }\n </div>\n <button class=\"h-6 w-6 flex items-center\" data-cy=\"clearSelection\">\n @if (hasSelection && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"removeSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-400 rounded shadow-lg pt-[12px] px-[16px] overlay-container w-[280px] flex flex-col gap-2\"\n [style.min-width]=\"overlayMinWidth\"\n >\n <div class=\"text-xs text-black\">\n {{ 'search.filters.spatialExtent.helpText' | translate }}\n </div>\n\n <div\n [class]=\"\n (hasSelection\n ? 'border-b-[1px] border-gray-400 pb-[20px]'\n : 'pb-[12px]') + ' text-black'\n \"\n >\n <gn-ui-drag-and-drop-file-input\n accept=\".json,.geojson\"\n [placeholder]=\"'search.filters.spatialExtent.import' | translate\"\n [maxFileSizeMb]=\"maxFileSizeMb\"\n [showFileName]=\"false\"\n textClass=\"text-black truncate\"\n dropzoneBackgroundColor=\"transparent\"\n icon=\"iconoirUpload\"\n extraClass=\"gn-ui-gray-outline-input w-full !h-[32px] px-[8px] py-[4px] justify-between\"\n (fileChange)=\"handleFileSelected($event)\"\n (errorChange)=\"handleFileError($event)\"\n data-cy=\"importGeojson\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n\n @if (hasSelection) {\n <gn-ui-button\n [type]=\"'primary-light'\"\n [title]=\"selectionDeleteLabelKey | translate: selectionLabelParams\"\n (buttonClick)=\"removeSelection($event)\"\n extraClass=\"group gap-x-2 w-full h-[28px] px-[8px] py-[4px] mb-[12px] bg-primary-lighter text-white\"\n data-test=\"spatial-extent-selected-item\"\n >\n <ng-icon\n name=\"iconoirSquareDashed\"\n class=\"shrink-0\"\n size=\"15px\"\n ></ng-icon>\n <span class=\"group-hover:hidden truncate text-sm m-auto\">\n {{ selectionLabelKey | translate: selectionLabelParams }}\n </span>\n <span class=\"hidden group-hover:block truncate text-sm m-auto\">\n {{ selectionDeleteLabelKey | translate: selectionLabelParams }}\n </span>\n <ng-icon\n name=\"iconoirCheckCircle\"\n class=\"group-hover:hidden shrink-0\"\n size=\"14px\"\n ></ng-icon>\n <ng-icon\n name=\"iconoirTrash\"\n class=\"!hidden group-hover:!block shrink-0\"\n size=\"15px\"\n ></ng-icon>\n </gn-ui-button>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: DragAndDropFileInputComponent, selector: "gn-ui-drag-and-drop-file-input", inputs: ["placeholder", "accept", "maxFileSizeMb", "icon", "dropzoneBackgroundColor", "textClass", "extraClass", "showFileName"], outputs: ["fileChange", "errorChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
25598
+ ], 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 }); }
26174
25599
  }
26175
25600
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, decorators: [{
26176
25601
  type: Component,
@@ -26190,7 +25615,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
26190
25615
  matExpandLess,
26191
25616
  matExpandMore,
26192
25617
  }),
26193
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelection) {\n <div\n class=\"bg-primary-lighter text-white shrink-0 rounded-full font-bold text-[12px] w-[22px] h-[22px] flex items-center justify-center\"\n >\n 1\n </div>\n }\n </div>\n <button class=\"h-6 w-6 flex items-center\" data-cy=\"clearSelection\">\n @if (hasSelection && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"removeSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-400 rounded shadow-lg pt-[12px] px-[16px] overlay-container w-[280px] flex flex-col gap-2\"\n [style.min-width]=\"overlayMinWidth\"\n >\n <div class=\"text-xs text-black\">\n {{ 'search.filters.spatialExtent.helpText' | translate }}\n </div>\n\n <div\n [class]=\"\n (hasSelection\n ? 'border-b-[1px] border-gray-400 pb-[20px]'\n : 'pb-[12px]') + ' text-black'\n \"\n >\n <gn-ui-drag-and-drop-file-input\n accept=\".json,.geojson\"\n [placeholder]=\"'search.filters.spatialExtent.import' | translate\"\n [maxFileSizeMb]=\"maxFileSizeMb\"\n [showFileName]=\"false\"\n textClass=\"text-black truncate\"\n dropzoneBackgroundColor=\"transparent\"\n icon=\"iconoirUpload\"\n extraClass=\"gn-ui-gray-outline-input w-full !h-[32px] px-[8px] py-[4px] justify-between\"\n (fileChange)=\"handleFileSelected($event)\"\n (errorChange)=\"handleFileError($event)\"\n data-cy=\"importGeojson\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n\n @if (hasSelection) {\n <gn-ui-button\n [type]=\"'primary-light'\"\n [title]=\"selectionDeleteLabelKey | translate: selectionLabelParams\"\n (buttonClick)=\"removeSelection($event)\"\n extraClass=\"group gap-x-2 w-full h-[28px] px-[8px] py-[4px] mb-[12px] bg-primary-lighter text-white\"\n data-test=\"spatial-extent-selected-item\"\n >\n <ng-icon\n name=\"iconoirSquareDashed\"\n class=\"shrink-0\"\n size=\"15px\"\n ></ng-icon>\n <span class=\"group-hover:hidden truncate text-sm m-auto\">\n {{ selectionLabelKey | translate: selectionLabelParams }}\n </span>\n <span class=\"hidden group-hover:block truncate text-sm m-auto\">\n {{ selectionDeleteLabelKey | translate: selectionLabelParams }}\n </span>\n <ng-icon\n name=\"iconoirCheckCircle\"\n class=\"group-hover:hidden shrink-0\"\n size=\"14px\"\n ></ng-icon>\n <ng-icon\n name=\"iconoirTrash\"\n class=\"!hidden group-hover:!block shrink-0\"\n size=\"15px\"\n ></ng-icon>\n </gn-ui-button>\n }\n </div>\n</ng-template>\n" }]
25618
+ ], 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" }]
26194
25619
  }], propDecorators: { title: [{
26195
25620
  type: Input
26196
25621
  }], maxFileSizeMb: [{
@@ -26716,11 +26141,11 @@ class BlockListComponent {
26716
26141
  this.goToPage(this.currentPage + 1);
26717
26142
  }
26718
26143
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: BlockListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
26719
- 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 }); }
26144
+ 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 }); }
26720
26145
  }
26721
26146
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: BlockListComponent, decorators: [{
26722
26147
  type: Component,
26723
- 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"] }]
26148
+ 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"] }]
26724
26149
  }], propDecorators: { containerClass: [{
26725
26150
  type: Input
26726
26151
  }], paginationContainerClass: [{
@@ -27459,9 +26884,7 @@ const initSearch = () => {
27459
26884
  },
27460
26885
  params: {
27461
26886
  filters: {},
27462
- pageSize: getOptionalSearchConfig()?.LIMIT
27463
- ? getOptionalSearchConfig().LIMIT
27464
- : DEFAULT_PAGE_SIZE,
26887
+ pageSize: DEFAULT_PAGE_SIZE,
27465
26888
  currentPage: 0,
27466
26889
  favoritesOnly: false,
27467
26890
  useSpatialFilter: true,
@@ -27910,10 +27333,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
27910
27333
  }]
27911
27334
  }] });
27912
27335
 
27336
+ const DEFAULT_GEOCODING_PROVIDER = [
27337
+ 'geonames',
27338
+ { maxRows: 5 },
27339
+ ];
27913
27340
  const GEOCODING_PROVIDER = new InjectionToken('geocoding-provider', {
27914
27341
  providedIn: 'root',
27915
- factory: () => ['geonames', { maxRows: 5 }],
27342
+ factory: () => DEFAULT_GEOCODING_PROVIDER,
27916
27343
  });
27344
+ // these options are arrays in the providers, but they can be configured
27345
+ // with a single value for simplicity
27346
+ const GEOCODING_PROVIDER_ARRAY_OPTIONS = {
27347
+ geoadmin: ['origins', 'features'],
27348
+ geoplateforme: [
27349
+ 'index',
27350
+ 'category',
27351
+ 'type',
27352
+ 'postCode',
27353
+ 'cityCode',
27354
+ 'depCode',
27355
+ ],
27356
+ };
27357
+ function wrapArrayOptions(options, keys) {
27358
+ const wrapped = { ...options };
27359
+ for (const key of keys) {
27360
+ if (typeof wrapped[key] === 'string') {
27361
+ wrapped[key] = [wrapped[key]];
27362
+ }
27363
+ }
27364
+ return wrapped;
27365
+ }
27917
27366
  class GeocodingService {
27918
27367
  constructor() {
27919
27368
  this.provider = inject(GEOCODING_PROVIDER);
@@ -27922,13 +27371,16 @@ class GeocodingService {
27922
27371
  let queryObservable;
27923
27372
  switch (this.provider[0]) {
27924
27373
  case 'geoadmin':
27925
- queryObservable = from(queryGeoadmin(text, this.provider[1]));
27374
+ queryObservable = from(queryGeoadmin(text, wrapArrayOptions(this.provider[1], GEOCODING_PROVIDER_ARRAY_OPTIONS.geoadmin)));
27926
27375
  break;
27927
27376
  case 'geonames':
27928
27377
  queryObservable = from(queryGeonames(text, this.provider[1]));
27929
27378
  break;
27930
- case 'data-gouv-fr':
27931
- queryObservable = from(queryDataGouvFr(text, this.provider[1]));
27379
+ case 'geoplateforme':
27380
+ queryObservable = from(queryGeoplateforme(text, {
27381
+ ...wrapArrayOptions(this.provider[1], GEOCODING_PROVIDER_ARRAY_OPTIONS.geoplateforme),
27382
+ returnTrueGeometry: true,
27383
+ }));
27932
27384
  break;
27933
27385
  default:
27934
27386
  return throwError(() => new Error(`Unsupported geocoding provider: ${this.provider[0]}`));
@@ -27945,29 +27397,80 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
27945
27397
  }]
27946
27398
  }] });
27947
27399
 
27400
+ const GEOCODING_PROVIDER_LABELS = new InjectionToken('geocodingProviderLabels', {
27401
+ factory: () => ({}),
27402
+ });
27948
27403
  class LocationSearchComponent {
27949
27404
  constructor() {
27950
27405
  this.geocodingService = inject(GeocodingService);
27406
+ this.labels = inject(GEOCODING_PROVIDER_LABELS);
27951
27407
  this.placeholder = '';
27952
27408
  this.resultSelected = new EventEmitter();
27409
+ this.bboxSelected = new EventEmitter();
27953
27410
  this.displayWithFn = (item) => item.label;
27954
27411
  this.searchAction = (text) => this.geocodingService.query(text);
27955
27412
  }
27413
+ getSecondaryLabel(result) {
27414
+ return this.resolveLabel(result, this.labels.secondary);
27415
+ }
27416
+ getMainLabel(result) {
27417
+ return this.resolveLabel(result, this.labels.main) ?? result.label;
27418
+ }
27419
+ getTertiaryLabel(result) {
27420
+ return this.resolveLabel(result, this.labels.tertiary);
27421
+ }
27422
+ getDisplayLabel(result) {
27423
+ const tertiary = this.getTertiaryLabel(result);
27424
+ return this.getMainLabel(result) + (tertiary ? ', ' + tertiary : '');
27425
+ }
27426
+ resolveLabel(result, path) {
27427
+ if (!path)
27428
+ return undefined;
27429
+ let value;
27430
+ try {
27431
+ value = pointer.get(result, path);
27432
+ }
27433
+ catch {
27434
+ return undefined;
27435
+ }
27436
+ const values = Array.isArray(value) ? value : [value];
27437
+ const label = values
27438
+ .filter((v) => typeof v === 'string' || typeof v === 'number')
27439
+ .join(', ');
27440
+ return label || undefined;
27441
+ }
27442
+ // geoplateforme keeps a simplified geometry in geom and puts the real one,
27443
+ // as a JSON string, in properties.truegeometry when returnTrueGeometry is set
27444
+ getGeometry(result) {
27445
+ const trueGeometry = result.properties?.['truegeometry'];
27446
+ if (typeof trueGeometry === 'string') {
27447
+ return JSON.parse(trueGeometry);
27448
+ }
27449
+ return trueGeometry ?? result.geom;
27450
+ }
27956
27451
  handleItemSelected(item) {
27957
- this.resultSelected.emit(item);
27452
+ const result = item;
27453
+ this.resultSelected.emit(result);
27454
+ const geometry = this.getGeometry(result);
27455
+ if (geometry) {
27456
+ this.bboxSelected.emit({
27457
+ bbox: getGeometryBoundingBox(geometry),
27458
+ label: this.getDisplayLabel(result),
27459
+ });
27460
+ }
27958
27461
  }
27959
27462
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LocationSearchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
27960
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: LocationSearchComponent, isStandalone: true, selector: "gn-ui-location-search", inputs: { placeholder: "placeholder", displayWithTemplate: "displayWithTemplate" }, outputs: { resultSelected: "resultSelected" }, ngImport: i0, template: "<gn-ui-autocomplete\n [action]=\"searchAction\"\n [displayWithFn]=\"displayWithFn\"\n [placeholder]=\"placeholder\"\n [displayWithTemplate]=\"displayWithTemplate\"\n (itemSelected)=\"handleItemSelected($event)\"\n>\n</gn-ui-autocomplete>\n", dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithTemplate", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
27463
+ 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 }); }
27961
27464
  }
27962
27465
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LocationSearchComponent, decorators: [{
27963
27466
  type: Component,
27964
- args: [{ selector: 'gn-ui-location-search', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [AutocompleteComponent], template: "<gn-ui-autocomplete\n [action]=\"searchAction\"\n [displayWithFn]=\"displayWithFn\"\n [placeholder]=\"placeholder\"\n [displayWithTemplate]=\"displayWithTemplate\"\n (itemSelected)=\"handleItemSelected($event)\"\n>\n</gn-ui-autocomplete>\n" }]
27467
+ 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" }]
27965
27468
  }], propDecorators: { placeholder: [{
27966
27469
  type: Input
27967
- }], displayWithTemplate: [{
27968
- type: Input
27969
27470
  }], resultSelected: [{
27970
27471
  type: Output
27472
+ }], bboxSelected: [{
27473
+ type: Output
27971
27474
  }] } });
27972
27475
 
27973
27476
  // expects the replacement key ${uuid}
@@ -28128,6 +27631,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
28128
27631
  class AbstractSearchField {
28129
27632
  }
28130
27633
  class SimpleSearchField {
27634
+ get fieldIdentifier() {
27635
+ return this.customIdentifier ?? this.esFieldName;
27636
+ }
28131
27637
  constructor(esFieldName, injector, order = 'asc', orderType = 'key') {
28132
27638
  this.esFieldName = esFieldName;
28133
27639
  this.injector = injector;
@@ -28136,14 +27642,26 @@ class SimpleSearchField {
28136
27642
  this.repository = this.injector.get(RecordsRepositoryInterface);
28137
27643
  // FIXME: this is required to register runtime fields; abstract this as well
28138
27644
  this.esService = this.injector.get(ElasticsearchService);
27645
+ this.includeValues = [];
27646
+ this.excludeValues = [];
27647
+ }
27648
+ setFieldIdentifier(fieldIdentifier) {
27649
+ this.customIdentifier = fieldIdentifier;
27650
+ this.esService.registerFieldAlias(fieldIdentifier, this.esFieldName);
28139
27651
  }
28140
27652
  getAggregations() {
28141
27653
  return {
28142
- [this.esFieldName]: {
27654
+ [this.fieldIdentifier]: {
28143
27655
  type: 'terms',
28144
27656
  field: this.esFieldName,
28145
27657
  limit: 1000,
28146
27658
  sort: [this.order, this.orderType],
27659
+ ...(this.includeValues?.length && {
27660
+ includeValues: this.includeValues,
27661
+ }),
27662
+ ...(this.excludeValues?.length && {
27663
+ excludeValues: this.excludeValues,
27664
+ }),
28147
27665
  },
28148
27666
  };
28149
27667
  }
@@ -28151,7 +27669,7 @@ class SimpleSearchField {
28151
27669
  return bucket.term.toString();
28152
27670
  }
28153
27671
  getAvailableValues() {
28154
- return this.repository.aggregate(this.getAggregations()).pipe(map$1((response) => response[this.esFieldName].buckets || []), switchMap((buckets) => {
27672
+ return this.repository.aggregate(this.getAggregations()).pipe(map$1((response) => response[this.fieldIdentifier].buckets || []), switchMap((buckets) => {
28155
27673
  const bucketPromises = buckets.map(async (bucket) => ({
28156
27674
  label: `${await this.getBucketLabel(bucket)} (${bucket.count})`,
28157
27675
  value: bucket.term.toString(),
@@ -28164,7 +27682,7 @@ class SimpleSearchField {
28164
27682
  // FieldValue[]
28165
27683
  if (this.getType() === 'values') {
28166
27684
  return of({
28167
- [this.esFieldName]: values.reduce((acc, val) => {
27685
+ [this.fieldIdentifier]: values.reduce((acc, val) => {
28168
27686
  const value = val.toString();
28169
27687
  if (value !== '') {
28170
27688
  return { ...acc, [value]: true };
@@ -28175,11 +27693,11 @@ class SimpleSearchField {
28175
27693
  }
28176
27694
  // DateRange
28177
27695
  return of({
28178
- [this.esFieldName]: values[0] !== '' ? values[0] : {},
27696
+ [this.fieldIdentifier]: values[0] !== '' ? values[0] : {},
28179
27697
  });
28180
27698
  }
28181
27699
  getValuesForFilter(filters) {
28182
- const filter = filters[this.esFieldName];
27700
+ const filter = filters[this.fieldIdentifier];
28183
27701
  if (!filter)
28184
27702
  return of([]);
28185
27703
  // filter by expression
@@ -28197,6 +27715,9 @@ class SimpleSearchField {
28197
27715
  getType() {
28198
27716
  return 'values';
28199
27717
  }
27718
+ clone() {
27719
+ return Object.create(this);
27720
+ }
28200
27721
  }
28201
27722
  class TranslatedSearchField extends SimpleSearchField {
28202
27723
  constructor(esFieldName, injector, order = 'asc', orderType = 'key') {
@@ -28466,11 +27987,11 @@ class BoundingBoxSearchField extends SimpleSearchField {
28466
27987
  }
28467
27988
  getFiltersForValues(values) {
28468
27989
  return of({
28469
- [this.esFieldName]: values.map(Number),
27990
+ [this.fieldIdentifier]: values.map(Number),
28470
27991
  });
28471
27992
  }
28472
27993
  getValuesForFilter(filters) {
28473
- const filter = filters[this.esFieldName];
27994
+ const filter = filters[this.fieldIdentifier];
28474
27995
  return of(isBoundingBox(filter) ? filter : []);
28475
27996
  }
28476
27997
  getType() {
@@ -28569,7 +28090,7 @@ class RecordKindField extends SimpleSearchField {
28569
28090
  }
28570
28091
  getFiltersForValues(values) {
28571
28092
  const filters = {
28572
- [this.esFieldName]: values.reduce((acc, value) => {
28093
+ [this.fieldIdentifier]: values.reduce((acc, value) => {
28573
28094
  if (value === '')
28574
28095
  return { ...acc, [value]: true };
28575
28096
  const keysToAdd = this.TYPE_MAPPING[value] || [value];
@@ -28592,7 +28113,7 @@ class RecordKindField extends SimpleSearchField {
28592
28113
  return of(filters);
28593
28114
  }
28594
28115
  getValuesForFilter(filters) {
28595
- const filter = filters[this.esFieldName];
28116
+ const filter = filters[this.fieldIdentifier];
28596
28117
  if (!filter)
28597
28118
  return of([]);
28598
28119
  const activeValues = Object.keys(filter).filter((v) => filter[v]);
@@ -28622,11 +28143,15 @@ marker('search.filters.publisherOrg');
28622
28143
  marker('search.filters.user');
28623
28144
  marker('search.filters.changeDate');
28624
28145
  marker('search.filters.resourceCreationRevisionDate');
28146
+ marker('search.filters.temporalExtent');
28625
28147
  marker('search.filters.spatialExtent');
28148
+ marker('search.filters.documentStandard');
28149
+ const CUSTOM_FIELDS = new InjectionToken('custom-fields');
28626
28150
  class FieldsService {
28627
28151
  constructor() {
28628
28152
  this.injector = inject(Injector);
28629
- this.fields = {
28153
+ this.customFields = inject(CUSTOM_FIELDS, { optional: true }) ?? [];
28154
+ this.baseFields = {
28630
28155
  organization: new OrganizationSearchField(this.injector),
28631
28156
  format: new SimpleSearchField('format', this.injector, 'asc'),
28632
28157
  resourceType: new ResourceTypeLegacyField(this.injector), // Deprecated, use `recordKind` instead
@@ -28646,9 +28171,31 @@ class FieldsService {
28646
28171
  user: new UserSearchField(this.injector),
28647
28172
  changeDate: new DateRangeSearchField('changeDate', this.injector, 'desc'),
28648
28173
  resourceCreationRevisionDate: new ResourceCreationRevisionDateSearchField(this.injector, 'desc'),
28174
+ temporalExtent: new DateRangeSearchField('resourceTemporalExtentDateRange', this.injector, 'desc'),
28649
28175
  availableServices: new AvailableServicesField(this.injector),
28650
28176
  spatialExtent: new BoundingBoxSearchField('spatialExtent', this.injector),
28651
28177
  };
28178
+ this.fields = { ...this.baseFields };
28179
+ for (const customField of this.customFields) {
28180
+ const baseField = this.baseFields[customField.baseFilter];
28181
+ if (!(baseField instanceof SimpleSearchField)) {
28182
+ console.warn(`The custom field '${customField.name}' relies on a base field '${customField.baseFilter}' that is not supported. This field will be ignored.`);
28183
+ continue;
28184
+ }
28185
+ if (!customField.name.includes(':')) {
28186
+ 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.`);
28187
+ continue;
28188
+ }
28189
+ const newField = baseField.clone();
28190
+ newField.setFieldIdentifier(customField.name);
28191
+ if (customField.includeValues) {
28192
+ newField.includeValues = customField.includeValues;
28193
+ }
28194
+ if (customField.excludeValues) {
28195
+ newField.excludeValues = customField.excludeValues;
28196
+ }
28197
+ this.fields[customField.name] = newField;
28198
+ }
28652
28199
  }
28653
28200
  get supportedFields() {
28654
28201
  return Object.keys(this.fields);
@@ -28696,7 +28243,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
28696
28243
  args: [{
28697
28244
  providedIn: 'root',
28698
28245
  }]
28699
- }] });
28246
+ }], ctorParameters: () => [] });
28700
28247
 
28701
28248
  class ApiCardComponent {
28702
28249
  constructor() {
@@ -30722,14 +30269,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
30722
30269
  marker('feature.catalog.attribute.type');
30723
30270
  marker('feature.catalog.attribute.name');
30724
30271
  marker('feature.catalog.attribute.code');
30725
- marker('feature.catalog.attribute.definition');
30272
+ marker('feature.catalog.attribute.description');
30726
30273
  marker('feature.catalog.attribute.values');
30727
30274
  class FeatureCatalogListComponent {
30728
30275
  constructor() {
30729
30276
  this.COLUMNS_DEFAULT = [
30730
30277
  { key: 'type', width: '25%' },
30731
30278
  { key: 'name', width: '40%' },
30732
- { key: 'definition', width: 'minmax(0px, 1fr)' },
30279
+ { key: 'description', width: 'minmax(0px, 1fr)' },
30733
30280
  ];
30734
30281
  this.COLUMN_VALUES = {
30735
30282
  key: 'values',
@@ -30750,7 +30297,7 @@ class FeatureCatalogListComponent {
30750
30297
  return col.key;
30751
30298
  }
30752
30299
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FeatureCatalogListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
30753
- 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" }] }); }
30300
+ 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" }] }); }
30754
30301
  }
30755
30302
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FeatureCatalogListComponent, decorators: [{
30756
30303
  type: Component,
@@ -30763,7 +30310,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
30763
30310
  TruncatedTextComponent,
30764
30311
  NgIconComponent,
30765
30312
  ScrollingModule,
30766
- ], 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" }]
30313
+ ], 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" }]
30767
30314
  }], propDecorators: { filteredFeatureCatalog: [{
30768
30315
  type: Input
30769
30316
  }], scrollContainer: [{
@@ -31901,6 +31448,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
31901
31448
  ], 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" }]
31902
31449
  }] });
31903
31450
 
31451
+ marker('search.filters.spatialExtent.searchLocation');
31452
+ // in MB, null means no limit
31453
+ const SPATIAL_EXTENT_MAX_FILE_SIZE = new InjectionToken('spatialExtentMaxFileSize', { factory: () => null });
31904
31454
  class FilterDropdownComponent {
31905
31455
  constructor() {
31906
31456
  this.searchFacade = inject(SearchFacade);
@@ -31908,7 +31458,7 @@ class FilterDropdownComponent {
31908
31458
  this.fieldsService = inject(FieldsService);
31909
31459
  this.notificationsService = inject(NotificationsService);
31910
31460
  this.translateService = inject(TranslateService);
31911
- this.spatialExtentMaxFileSize = getOptionalSearchConfig()?.SPATIAL_EXTENT_MAX_FILE_SIZE;
31461
+ this.spatialExtentMaxFileSize = inject(SPATIAL_EXTENT_MAX_FILE_SIZE);
31912
31462
  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([])));
31913
31463
  this.selectedDateRange$ = this.selected$.pipe(map$1((selected) => (Array.isArray(selected) ? {} : selected)));
31914
31464
  this.selectedBoundingBox$ = this.selected$.pipe(map$1((selected) => Array.isArray(selected) && selected.length > 0
@@ -31929,6 +31479,9 @@ class FilterDropdownComponent {
31929
31479
  .subscribe((filters) => this.searchService.updateFilters(filters));
31930
31480
  this.clearSpatialExtentErrorNotification();
31931
31481
  }
31482
+ onLocationSelected({ bbox, label }) {
31483
+ this.spatialExtentDropdown.setLocationBbox(bbox, label);
31484
+ }
31932
31485
  onSpatialExtentError(error) {
31933
31486
  this.clearSpatialExtentErrorNotification();
31934
31487
  this.spatialExtentErrorNotificationId =
@@ -31959,7 +31512,7 @@ class FilterDropdownComponent {
31959
31512
  .subscribe((filters) => this.searchService.updateFilters(filters));
31960
31513
  }
31961
31514
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
31962
- 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 }); }
31515
+ 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 }); }
31963
31516
  }
31964
31517
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, decorators: [{
31965
31518
  type: Component,
@@ -31968,11 +31521,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
31968
31521
  DateRangeDropdownComponent,
31969
31522
  DropdownMultiselectComponent,
31970
31523
  SpatialExtentDropdownComponent,
31971
- ], 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" }]
31524
+ LocationSearchComponent,
31525
+ TranslatePipe,
31526
+ ], 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" }]
31972
31527
  }], propDecorators: { fieldName: [{
31973
31528
  type: Input
31974
31529
  }], title: [{
31975
31530
  type: Input
31531
+ }], spatialExtentDropdown: [{
31532
+ type: ViewChild,
31533
+ args: [SpatialExtentDropdownComponent]
31976
31534
  }] } });
31977
31535
 
31978
31536
  class FuzzySearchComponent {
@@ -32589,6 +32147,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
32589
32147
  marker('search.filters.summaryLabel.user');
32590
32148
  marker('search.filters.summaryLabel.changeDate');
32591
32149
  marker('search.filters.summaryLabel.resourceCreationRevisionDate');
32150
+ marker('search.filters.summaryLabel.temporalExtent');
32592
32151
  const OPEN_BOUND = '…';
32593
32152
  class SearchFiltersSummaryItemComponent {
32594
32153
  constructor() {
@@ -34152,7 +33711,7 @@ class DataService {
34152
33711
  }));
34153
33712
  }
34154
33713
  async getDownloadLinksFromOgcApiFeatures(ogcApiLink) {
34155
- const collectionInfo = await this.getDownloadUrlsFromOgcApi(ogcApiLink.url.href);
33714
+ const collectionInfo = await this.getDownloadUrlsFromOgcApi(ogcApiLink.url.href, ogcApiLink.name);
34156
33715
  return Object.keys(collectionInfo.bulkDownloadLinks).map((mimeType) => {
34157
33716
  const urlWithoutLimit = new URL(collectionInfo.bulkDownloadLinks[mimeType]);
34158
33717
  urlWithoutLimit.searchParams.delete('limit');
@@ -34165,27 +33724,31 @@ class DataService {
34165
33724
  };
34166
33725
  });
34167
33726
  }
34168
- async getDownloadUrlsFromOgcApi(url) {
33727
+ async getTargetCollectionId(endpoint, collectionId) {
33728
+ const collections = await endpoint.featureCollections;
33729
+ return collections.length === 1 && !collectionId
33730
+ ? collections[0]
33731
+ : collectionId;
33732
+ }
33733
+ async getDownloadUrlsFromOgcApi(url, collectionId) {
34169
33734
  const endpoint = new OgcApiEndpoint(url);
34170
- return await endpoint.featureCollections
34171
- .then((collections) => {
34172
- return endpoint.getCollectionInfo(collections[0]);
34173
- })
34174
- .catch((error) => {
34175
- throw new Error(`ogc.unreachable.unknown`);
34176
- });
33735
+ try {
33736
+ const id = await this.getTargetCollectionId(endpoint, collectionId);
33737
+ return await endpoint.getCollectionInfo(id);
33738
+ }
33739
+ catch {
33740
+ throw new Error('ogc.unreachable.unknown');
33741
+ }
34177
33742
  }
34178
- async getItemsFromOgcApi(url, limit) {
33743
+ async getItemsFromOgcApi(url, collectionId, limit) {
34179
33744
  const endpoint = new OgcApiEndpoint(url);
34180
- return await endpoint.featureCollections
34181
- .then((collections) => {
34182
- return collections.length
34183
- ? endpoint.getCollectionItems(collections[0], limit)
34184
- : null;
34185
- })
34186
- .catch(() => {
34187
- throw new Error(`ogc.unreachable.unknown`);
34188
- });
33745
+ try {
33746
+ const id = await this.getTargetCollectionId(endpoint, collectionId);
33747
+ return await endpoint.getCollectionItems(id, limit);
33748
+ }
33749
+ catch {
33750
+ throw new Error('ogc.unreachable.unknown');
33751
+ }
34189
33752
  }
34190
33753
  async getItemsFromStacApi(url, options) {
34191
33754
  return await StacEndpoint.getItemsFromUrl(url, options)
@@ -34273,7 +33836,7 @@ class DataService {
34273
33836
  }
34274
33837
  else if (link.type === 'service' &&
34275
33838
  link.accessServiceProtocol === 'ogcFeatures') {
34276
- return from(this.getDownloadUrlsFromOgcApi(link.url.href)).pipe(switchMap$1((collectionInfo) => {
33839
+ return from(this.getDownloadUrlsFromOgcApi(link.url.href, link.name)).pipe(switchMap$1((collectionInfo) => {
34277
33840
  const isMimeTypeJson = (mimeType) => {
34278
33841
  return mimeType.toLowerCase().indexOf('json') > -1;
34279
33842
  };
@@ -35217,7 +34780,7 @@ class MdViewFacade {
35217
34780
  if (this.linkClassifier.hasUsage(link, LinkUsage.GEODATA)) {
35218
34781
  if (link.type === 'service' &&
35219
34782
  link.accessServiceProtocol === 'ogcFeatures') {
35220
- return from(this.dataService.getItemsFromOgcApi(link.url.href, 1)).pipe(map$1((collectionRecords) => {
34783
+ return from(this.dataService.getItemsFromOgcApi(link.url.href, link.name, 1)).pipe(map$1((collectionRecords) => {
35221
34784
  return collectionRecords &&
35222
34785
  collectionRecords[0] &&
35223
34786
  collectionRecords[0].geometry
@@ -35300,7 +34863,7 @@ class MdViewEffects {
35300
34863
  }
35301
34864
  return loadFullMetadataSuccess({ full: record });
35302
34865
  }), catchError((error) => of(loadFullMetadataFailure({ otherError: error.message })))));
35303
- this.loadFeatureCatalog$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), filter$1(({ full }) => full !== undefined), switchMap$1(({ full }) => this.recordsRepository.getFeatureCatalog(full)), map$1((featureCatalog) => loadFeatureCatalogSuccess({
34866
+ this.loadFeatureCatalog$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), filter$1(({ full }) => full !== undefined), switchMap$1(({ full }) => this.recordsRepository.queryLinkedFeatureCatalog(full)), map$1((featureCatalog) => loadFeatureCatalogSuccess({
35304
34867
  datasetCatalog: featureCatalog,
35305
34868
  })), catchError((error) => of(loadFeatureCatalogFailure({
35306
34869
  error: error.message,
@@ -40934,6 +40497,638 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
40934
40497
  args: ['gnUiSearchRouterContainer']
40935
40498
  }] } });
40936
40499
 
40500
+ const flatten = (base, obj, keepLevel = false) => Object.keys(obj).reduce((prev, curr) => {
40501
+ const path = base ? `${base}.${curr}` : curr;
40502
+ const val = obj[curr];
40503
+ if (keepLevel) {
40504
+ return { ...prev, [path]: flatten('', val) };
40505
+ }
40506
+ else if (typeof val === 'object' && !Array.isArray(val)) {
40507
+ return {
40508
+ ...prev,
40509
+ ...flatten(path, val),
40510
+ };
40511
+ }
40512
+ else {
40513
+ return { ...prev, [path]: val };
40514
+ }
40515
+ }, {});
40516
+ const checkKeys = (input, mandatory, optional) => {
40517
+ const keys = Object.keys(input);
40518
+ const missing = mandatory.filter((key) => keys.indexOf(key) === -1);
40519
+ const unrecognized = keys.filter((key) => mandatory.indexOf(key) === -1 && optional.indexOf(key) === -1);
40520
+ return {
40521
+ missing,
40522
+ unrecognized,
40523
+ };
40524
+ };
40525
+ /**
40526
+ * Only keeps recognized keys, flatten nested values (to allow having "." in keys),
40527
+ * pushes warnings and errors to the given arrays; returns null in case of error
40528
+ */
40529
+ function parseConfigSection(fullConfigObj, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors) {
40530
+ if (typeof fullConfigObj[sectionName] !== 'object') {
40531
+ if (mandatoryKeys.length === 0)
40532
+ return null;
40533
+ outErrors.push(`The [${sectionName}] mandatory section is missing.`);
40534
+ return null;
40535
+ }
40536
+ const sectionConf = fullConfigObj[sectionName];
40537
+ const keysCheck = checkKeys(sectionConf, mandatoryKeys, optionalKeys);
40538
+ if (keysCheck.missing.length) {
40539
+ // note: this is not thrown to allow merging several Errors down the line
40540
+ outErrors.push(`In the [${sectionName}] section: ${keysCheck.missing.join(', ')}`);
40541
+ return null;
40542
+ }
40543
+ else if (keysCheck.unrecognized.length) {
40544
+ outWarnings.push(`In the [${sectionName}] section: ${keysCheck.unrecognized.join(', ')}`);
40545
+ keysCheck.unrecognized.forEach((key) => delete sectionConf[key]);
40546
+ }
40547
+ return sectionConf;
40548
+ }
40549
+ /**
40550
+ * Same as parseConfigSection but keeps all keys, recognized or not
40551
+ * Also the first level is the language code (e.g. 'en')
40552
+ * This is optional
40553
+ */
40554
+ function parseTranslationsConfigSection(fullConfigObj, sectionName) {
40555
+ if (typeof fullConfigObj[sectionName] !== 'object') {
40556
+ return {};
40557
+ }
40558
+ return flatten('', fullConfigObj[sectionName], true);
40559
+ }
40560
+ /**
40561
+ * Same as parseConfigSection but handles an array of configs; this is
40562
+ * considered optional; returns null in case of error in at least one of
40563
+ * the sections
40564
+ */
40565
+ function parseMultiConfigSection(fullConfigObj, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors) {
40566
+ const sections = fullConfigObj[sectionName];
40567
+ if (!sections) {
40568
+ return [];
40569
+ }
40570
+ if (!Array.isArray(sections)) {
40571
+ outErrors.push(`Expected [[${sectionName}]] to be an array of sections.`);
40572
+ return null;
40573
+ }
40574
+ const result = sections.map((section) => parseConfigSection({ [sectionName]: section }, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors));
40575
+ if (result.some((r) => r === null)) {
40576
+ return null;
40577
+ }
40578
+ return result;
40579
+ }
40580
+ function checkMetadataLanguage(parsedConfigSection, outWarnings) {
40581
+ const lang2 = toLang2(parsedConfigSection.metadata_language.toLowerCase());
40582
+ if (lang2?.length !== 2) {
40583
+ outWarnings.push(`In the [global] section: metadata_language = "${parsedConfigSection.metadata_language}" is not a recognized ISO 639 language code`);
40584
+ }
40585
+ return parsedConfigSection;
40586
+ }
40587
+ function checkNewRecordDefaultLanguage(parsedConfigSection, outWarnings) {
40588
+ const lang2 = toLang2(parsedConfigSection.new_record_default_language.toLowerCase());
40589
+ if (!(lang2 in LANG_2_TO_3_MAPPER)) {
40590
+ outWarnings.push(`In the [editing] section: new_record_default_language = "${parsedConfigSection.new_record_default_language}" is not a recognized ISO 639 language code`);
40591
+ return {
40592
+ ...parsedConfigSection,
40593
+ new_record_default_language: undefined,
40594
+ };
40595
+ }
40596
+ return {
40597
+ ...parsedConfigSection,
40598
+ new_record_default_language: lang2,
40599
+ };
40600
+ }
40601
+ function checkNewRecordStandard(parsedConfigSection, outWarnings) {
40602
+ const standard = parsedConfigSection.new_record_standard;
40603
+ const normalizedStandard = typeof standard === 'string' ? standard.trim().toLowerCase() : null;
40604
+ if (normalizedStandard === 'iso19139' ||
40605
+ normalizedStandard === 'iso19115-3') {
40606
+ return {
40607
+ ...parsedConfigSection,
40608
+ new_record_standard: normalizedStandard,
40609
+ };
40610
+ }
40611
+ outWarnings.push(`In the [editing] section: new_record_standard = "${standard}" is not a supported metadata standard`);
40612
+ return {
40613
+ ...parsedConfigSection,
40614
+ new_record_standard: undefined,
40615
+ };
40616
+ }
40617
+
40618
+ /**
40619
+ * This loader extends the default one based on JSON files but also loads custom translations
40620
+ * defined in the app configuration
40621
+ * Implements a fallback on default lang if translated labels are empty
40622
+ */
40623
+ class FileWithOverridesTranslateLoader extends FileTranslateLoader {
40624
+ getTranslation(lang) {
40625
+ const baseLang = lang.substring(0, 2); // removing the right part of e.g. en_EN
40626
+ return super.getTranslation(baseLang).pipe(map$1((translations) => ({
40627
+ ...translations,
40628
+ ...getCustomTranslations(baseLang),
40629
+ })));
40630
+ }
40631
+ }
40632
+
40633
+ const MISSING_CONFIG_ERROR = `Application configuration was not initialized correctly.
40634
+ This error might show up in case of an invalid/malformed configuration file.
40635
+
40636
+ Note: make sure that you have called \`loadAppConfig\` from '../../../../../libs/util/app-config/src' before starting the Angular application.`;
40637
+ let globalConfig = null;
40638
+ function getGlobalConfig() {
40639
+ if (globalConfig === null)
40640
+ throw new Error(MISSING_CONFIG_ERROR);
40641
+ return globalConfig;
40642
+ }
40643
+ let themeConfig = null;
40644
+ function getThemeConfig() {
40645
+ if (themeConfig === null)
40646
+ throw new Error(MISSING_CONFIG_ERROR);
40647
+ return themeConfig;
40648
+ }
40649
+ let mapConfig = null;
40650
+ function getOptionalMapConfig() {
40651
+ return mapConfig;
40652
+ }
40653
+ let searchConfig = null;
40654
+ function getOptionalSearchConfig() {
40655
+ return searchConfig;
40656
+ }
40657
+ let editorConfig = null;
40658
+ function getOptionalEditorConfig() {
40659
+ return editorConfig;
40660
+ }
40661
+ let metadataQualityConfig = null;
40662
+ function getMetadataQualityConfig() {
40663
+ return (metadataQualityConfig ||
40664
+ {
40665
+ ENABLED: false,
40666
+ });
40667
+ }
40668
+ let customTranslations = null;
40669
+ function getCustomTranslations(langCode) {
40670
+ if (customTranslations === null)
40671
+ throw new Error(MISSING_CONFIG_ERROR);
40672
+ return langCode in customTranslations ? customTranslations[langCode] : {};
40673
+ }
40674
+ let appConfigLoaded = false;
40675
+ function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
40676
+ console.log(`[geonetwork-ui] Loading application configuration from ${configUrl}`);
40677
+ return fetch(configUrl)
40678
+ .then((resp) => {
40679
+ if (!resp.ok)
40680
+ throw new Error('Configuration file could not be loaded');
40681
+ return resp.text();
40682
+ })
40683
+ .then((conf) => {
40684
+ let parsed;
40685
+ try {
40686
+ parsed = TOML.parse(conf, { joiner: '\n', bigint: false });
40687
+ }
40688
+ catch (e) {
40689
+ throw new Error(`An error occurred when parsing the configuration file: ${e.message}`);
40690
+ }
40691
+ const errors = [];
40692
+ const warnings = [];
40693
+ let parsedGlobalSection = parseConfigSection(parsed, 'global', ['geonetwork4_api_url'], [
40694
+ 'datahub_url',
40695
+ 'edit_url_template',
40696
+ 'proxy_path',
40697
+ 'metadata_language',
40698
+ 'login_url',
40699
+ 'logout_url',
40700
+ 'settings_url',
40701
+ 'web_component_embedder_url',
40702
+ 'languages',
40703
+ 'contact_email',
40704
+ 'reuse_form_url',
40705
+ ], warnings, errors);
40706
+ if (parsedGlobalSection?.metadata_language) {
40707
+ parsedGlobalSection = checkMetadataLanguage(parsedGlobalSection, warnings);
40708
+ }
40709
+ globalConfig =
40710
+ parsedGlobalSection === null
40711
+ ? null
40712
+ : {
40713
+ GN4_API_URL: parsedGlobalSection.geonetwork4_api_url,
40714
+ DATAHUB_URL: parsedGlobalSection.datahub_url,
40715
+ EDIT_URL_TEMPLATE: parsedGlobalSection.edit_url_template,
40716
+ PROXY_PATH: parsedGlobalSection.proxy_path,
40717
+ METADATA_LANGUAGE: parsedGlobalSection.metadata_language
40718
+ ? parsedGlobalSection.metadata_language.toLowerCase()
40719
+ : undefined,
40720
+ LOGIN_URL: parsedGlobalSection.login_url,
40721
+ LOGOUT_URL: parsedGlobalSection.logout_url,
40722
+ SETTINGS_URL: parsedGlobalSection.settings_url,
40723
+ WEB_COMPONENT_EMBEDDER_URL: parsedGlobalSection.web_component_embedder_url,
40724
+ LANGUAGES: parsedGlobalSection.languages,
40725
+ CONTACT_EMAIL: parsedGlobalSection.contact_email,
40726
+ REUSE_FORM_URL: parsedGlobalSection.reuse_form_url,
40727
+ };
40728
+ const parsedLayersSections = parseMultiConfigSection(parsed, 'map_layer', ['type'], ['name', 'url', 'data', 'styleUrl', 'accessToken'], warnings, errors);
40729
+ const parsedMapSection = parseConfigSection(parsed, 'map', [], [
40730
+ 'max_zoom',
40731
+ 'do_not_tile_wms',
40732
+ 'max_extent',
40733
+ 'max_feature_count',
40734
+ 'baselayer',
40735
+ 'do_not_use_default_basemap',
40736
+ 'external_viewer_url_template',
40737
+ 'external_viewer_open_new_tab',
40738
+ ], warnings, errors);
40739
+ mapConfig =
40740
+ parsedMapSection === null || parsedLayersSections === null
40741
+ ? null
40742
+ : {
40743
+ MAX_ZOOM: parsedMapSection.max_zoom,
40744
+ DO_NOT_TILE_WMS: parsedMapSection.do_not_tile_wms,
40745
+ MAX_EXTENT: parsedMapSection.max_extent,
40746
+ MAX_FEATURE_COUNT: parsedMapSection.max_feature_count,
40747
+ EXTERNAL_VIEWER_URL_TEMPLATE: parsedMapSection.external_viewer_url_template,
40748
+ EXTERNAL_VIEWER_OPEN_NEW_TAB: parsedMapSection.external_viewer_open_new_tab,
40749
+ DO_NOT_USE_DEFAULT_BASEMAP: !!parsedMapSection.do_not_use_default_basemap,
40750
+ MAP_LAYERS: parsedLayersSections.map((map_layer) => ({
40751
+ TYPE: map_layer.type,
40752
+ URL: map_layer.url,
40753
+ NAME: map_layer.name,
40754
+ DATA: map_layer.data,
40755
+ STYLE_URL: map_layer.styleUrl,
40756
+ ACCESS_TOKEN: map_layer.accessToken,
40757
+ })),
40758
+ };
40759
+ const parsedThemeSection = parseConfigSection(parsed, 'theme', ['primary_color', 'secondary_color', 'main_color', 'background_color'], [
40760
+ 'header_foreground_color',
40761
+ 'main_font',
40762
+ 'title_font',
40763
+ 'fonts_stylesheet_url',
40764
+ 'thumbnail_placeholder',
40765
+ 'header_background',
40766
+ 'favicon',
40767
+ ], warnings, errors);
40768
+ themeConfig =
40769
+ parsedThemeSection === null
40770
+ ? null
40771
+ : {
40772
+ PRIMARY_COLOR: parsedThemeSection.primary_color,
40773
+ SECONDARY_COLOR: parsedThemeSection.secondary_color,
40774
+ MAIN_COLOR: parsedThemeSection.main_color,
40775
+ BACKGROUND_COLOR: parsedThemeSection.background_color,
40776
+ HEADER_FOREGROUND_COLOR: parsedThemeSection.header_foreground_color,
40777
+ THUMBNAIL_PLACEHOLDER: parsedThemeSection.thumbnail_placeholder,
40778
+ HEADER_BACKGROUND: parsedThemeSection.header_background,
40779
+ TITLE_FONT: parsedThemeSection.title_font,
40780
+ MAIN_FONT: parsedThemeSection.main_font,
40781
+ FONTS_STYLESHEET_URL: parsedThemeSection.fonts_stylesheet_url,
40782
+ FAVICON: parsedThemeSection.favicon,
40783
+ };
40784
+ const parsedSearchSection = parseConfigSection(parsed, 'search', [], [
40785
+ 'record_kind_quick_filter',
40786
+ 'filter_geometry_data',
40787
+ 'filter_geometry_url',
40788
+ 'do_not_use_default_search_preset',
40789
+ 'search_preset',
40790
+ 'advanced_filters',
40791
+ 'limit',
40792
+ 'spatial_extent_max_file_size',
40793
+ 'geocoding_provider',
40794
+ 'geocoding_provider_options',
40795
+ 'geocoding_provider_labels',
40796
+ ], warnings, errors);
40797
+ const parsedSearchParams = parseMultiConfigSection(parsed, 'search_preset', ['name'], ['sort', 'filters'], warnings, errors);
40798
+ const parsedCustomFilters = parseMultiConfigSection(parsed, 'custom_filter', ['name', 'base_filter'], ['exclude_values', 'include_values', 'label_key'], warnings, errors);
40799
+ const parsedGeocodingProviderLabels = parsedSearchSection?.geocoding_provider_labels;
40800
+ searchConfig =
40801
+ parsedSearchSection === null
40802
+ ? null
40803
+ : {
40804
+ RECORD_KIND_QUICK_FILTER: parsedSearchSection.record_kind_quick_filter,
40805
+ FILTER_GEOMETRY_DATA: parsedSearchSection.filter_geometry_data,
40806
+ FILTER_GEOMETRY_URL: parsedSearchSection.filter_geometry_url,
40807
+ DO_NOT_USE_DEFAULT_SEARCH_PRESET: !!parsedSearchSection.do_not_use_default_search_preset,
40808
+ SEARCH_PRESET: parsedSearchParams.map((param) => ({
40809
+ sort: param.sort,
40810
+ name: param.name,
40811
+ filters: param.filters,
40812
+ })),
40813
+ ADVANCED_FILTERS: parsedSearchSection.advanced_filters,
40814
+ CUSTOM_FILTERS: (parsedCustomFilters ?? []).map((customFilter) => ({
40815
+ name: customFilter.name,
40816
+ baseFilter: customFilter.base_filter,
40817
+ excludeValues: customFilter.exclude_values,
40818
+ includeValues: customFilter.include_values,
40819
+ labelKey: customFilter.label_key,
40820
+ })),
40821
+ LIMIT: parsedSearchSection.limit,
40822
+ SPATIAL_EXTENT_MAX_FILE_SIZE: parsedSearchSection.spatial_extent_max_file_size,
40823
+ GEOCODING_PROVIDER: parsedSearchSection.geocoding_provider,
40824
+ GEOCODING_PROVIDER_OPTIONS: parsedSearchSection.geocoding_provider_options,
40825
+ GEOCODING_PROVIDER_LABELS: parsedGeocodingProviderLabels
40826
+ ? {
40827
+ MAIN: parsedGeocodingProviderLabels.main,
40828
+ SECONDARY: parsedGeocodingProviderLabels.secondary,
40829
+ TERTIARY: parsedGeocodingProviderLabels.tertiary,
40830
+ }
40831
+ : undefined,
40832
+ };
40833
+ const parsedMetadataQualitySection = parseConfigSection(parsed, 'metadata-quality', [], ['enabled'], warnings, errors);
40834
+ metadataQualityConfig =
40835
+ parsedMetadataQualitySection === null
40836
+ ? null
40837
+ : {
40838
+ ENABLED: parsedMetadataQualitySection.enabled,
40839
+ SORTABLE: parsedMetadataQualitySection.sortable,
40840
+ };
40841
+ let parsedEditingSection = parseConfigSection(parsed, 'editing', [], ['new_record_default_language', 'new_record_standard'], warnings, errors);
40842
+ if (parsedEditingSection !== null &&
40843
+ parsedEditingSection.new_record_default_language !== undefined) {
40844
+ parsedEditingSection = checkNewRecordDefaultLanguage(parsedEditingSection, warnings);
40845
+ }
40846
+ if (parsedEditingSection !== null &&
40847
+ parsedEditingSection.new_record_standard !== undefined) {
40848
+ parsedEditingSection = checkNewRecordStandard(parsedEditingSection, warnings);
40849
+ }
40850
+ editorConfig =
40851
+ parsedEditingSection === null
40852
+ ? null
40853
+ : {
40854
+ NEW_RECORD_DEFAULT_LANGUAGE: parsedEditingSection.new_record_default_language,
40855
+ NEW_RECORD_STANDARD: parsedEditingSection.new_record_standard,
40856
+ };
40857
+ customTranslations = parseTranslationsConfigSection(parsed, 'translations');
40858
+ if (errors.length) {
40859
+ throw new Error(`One or more mandatory settings were missing from the configuration file.
40860
+ ${errors.join('\n')}`);
40861
+ }
40862
+ else if (warnings.length) {
40863
+ console.warn(`One or more unexpected settings were encountered in the configuration file.
40864
+ ${warnings.join('\n')}`);
40865
+ }
40866
+ appConfigLoaded = true;
40867
+ });
40868
+ }
40869
+ function isConfigLoaded() {
40870
+ return appConfigLoaded;
40871
+ }
40872
+ function _reset() {
40873
+ globalConfig = null;
40874
+ themeConfig = null;
40875
+ editorConfig = null;
40876
+ customTranslations = null;
40877
+ }
40878
+ const TRANSLATE_WITH_OVERRIDES_CONFIG = {
40879
+ compiler: {
40880
+ provide: TranslateCompiler,
40881
+ useClass: TranslateMessageFormatCompiler,
40882
+ },
40883
+ loader: [
40884
+ {
40885
+ provide: TRANSLATE_HTTP_LOADER_CONFIG,
40886
+ useValue: {
40887
+ prefix: './assets/i18n/',
40888
+ suffix: '.json',
40889
+ },
40890
+ },
40891
+ {
40892
+ provide: TranslateLoader,
40893
+ useClass: FileWithOverridesTranslateLoader,
40894
+ deps: [HttpClient, TRANSLATE_HTTP_LOADER_CONFIG],
40895
+ },
40896
+ ],
40897
+ };
40898
+
40899
+ const okAppConfigFixture = () => `
40900
+ [global]
40901
+ geonetwork4_api_url = "/geonetwork/srv/api"
40902
+ proxy_path = "/proxy/?url="
40903
+ metadata_language = "fre"
40904
+ login_url = "/cas/login?service="
40905
+ logout_url = "/geonetwork/signout"
40906
+ settings_url = "/geonetwork/settings"
40907
+ web_component_embedder_url = "/datahub/wc-embedder.html"
40908
+
40909
+ [map]
40910
+ max_zoom = 10
40911
+ max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855]
40912
+ do_not_use_default_basemap = false
40913
+ external_viewer_url_template = 'https://example.com/myviewer?'
40914
+ external_viewer_open_new_tab = true
40915
+ [[map_layer]]
40916
+ type = "wms"
40917
+ url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
40918
+ name = "masque_hdf_ign_carto_latin1"
40919
+ [[map_layer]]
40920
+ type = "wfs"
40921
+ url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
40922
+ name = "masque_hdf_ign_carto_latin1"
40923
+
40924
+ [theme]
40925
+ primary_color = "#093564"
40926
+ secondary_color = "#c2e9dc"
40927
+ main_color = "#212029" # All-purpose text color
40928
+ background_color = "#fdfbff"
40929
+ thumbnail_placeholder = 'assets/img/placeholder.svg'
40930
+ header_background = 'teal'
40931
+ header_foreground_color = "#872e2e"
40932
+ main_font = 'sans-serif'
40933
+ title_font = 'serif'
40934
+ fonts_stylesheet_url = "https://fonts.googleapis.com/css2?family=Open+Sans"
40935
+
40936
+ [search]
40937
+ record_kind_quick_filter = false
40938
+ filter_geometry_url = 'https://my.domain.org/geom.json'
40939
+ do_not_use_default_search_preset = false
40940
+ geocoding_provider = "geoplateforme"
40941
+ geocoding_provider_options.index = "poi"
40942
+ geocoding_provider_options.category = "administratif"
40943
+ geocoding_provider_options.limit = 5
40944
+ geocoding_provider_labels.main = '/properties/name/0'
40945
+ geocoding_provider_labels.secondary = '/properties/citycode/0'
40946
+ geocoding_provider_labels.tertiary = '/properties/category/1'
40947
+ advanced_filters = ['publicationYear', 'documentStandard', 'inspireKeyword', 'topic', 'license', 'myOrg:firstCustomFilter', 'myOrg:secondCustomFilter']
40948
+
40949
+ [[custom_filter]]
40950
+ name = 'myOrg:firstCustomFilter'
40951
+ base_filter = 'keyword'
40952
+ exclude_values = ['my keyword 1', 'my keyword 2']
40953
+ label_key = 'myOrg.firstCustomFilter'
40954
+
40955
+ [[custom_filter]]
40956
+ name = 'myOrg:secondCustomFilter'
40957
+ base_filter = 'keyword'
40958
+ include_values = ['my keyword 3', 'my keyword 4']
40959
+ label_key = 'myOrg.secondCustomFilter'
40960
+
40961
+ [[search_preset]]
40962
+ sort = "-createDate"
40963
+ name = 'filterByOrgs'
40964
+ filters.q = 'Carto'
40965
+ filters.organisation = ['Org1', 'Org2']
40966
+ filters.format = ['ESRI Shapefile']
40967
+ filters.topic = ['boundaries']
40968
+ filters.publicationYear = ['2023', '2022']
40969
+ filters.spatial = ['yes']
40970
+
40971
+ [translations.en]
40972
+ "my.first.key" = 'First label.'
40973
+ "my.second.key" = """
40974
+ Second label,
40975
+ on two lines."""
40976
+
40977
+ [translations.de]
40978
+ my.first.key = 'Erste Etikett.'
40979
+
40980
+ [translations.fr]
40981
+ my.sample.text = "Un bon exemple de texte."
40982
+ "my.quoted.text" = 'du texte entre guillements.'
40983
+ `;
40984
+ const appConfigWithTranslationFixture = () => okAppConfigFixture();
40985
+ const minimalAppConfigFixture = () => `
40986
+ [global]
40987
+ geonetwork4_api_url = "/geonetwork/srv/api"
40988
+ proxy_path = "/proxy/?url="
40989
+
40990
+ [theme]
40991
+ primary_color = "#093564"
40992
+ secondary_color = "#c2e9dc"
40993
+ main_color = "#212029" # All-purpose text color
40994
+ background_color = "#fdfbff"
40995
+ `;
40996
+ const customFilterMissingMandatoryConfigFixture = () => `
40997
+ [global]
40998
+ geonetwork4_api_url = "/geonetwork/srv/api"
40999
+
41000
+ [theme]
41001
+ primary_color = "#093564"
41002
+ secondary_color = "#c2e9dc"
41003
+ main_color = "#212029" # All-purpose text color
41004
+ background_color = "#fdfbff"
41005
+
41006
+ [search]
41007
+ advanced_filters = ['myOrg:firstCustomFilter']
41008
+
41009
+ [[custom_filter]]
41010
+ name = 'myOrg:firstCustomFilter'
41011
+ `;
41012
+ const mapConfigFixture = () => ({
41013
+ MAX_ZOOM: 10,
41014
+ MAX_EXTENT: [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855],
41015
+ DO_NOT_USE_DEFAULT_BASEMAP: false,
41016
+ DO_NOT_TILE_WMS: false,
41017
+ 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}"}]}]',
41018
+ EXTERNAL_VIEWER_OPEN_NEW_TAB: true,
41019
+ MAP_LAYERS: [
41020
+ {
41021
+ TYPE: 'xyz',
41022
+ URL: 'https://some-basemap-server',
41023
+ },
41024
+ {
41025
+ TYPE: 'wms',
41026
+ URL: 'https://some-wms-server',
41027
+ NAME: 'some_layername',
41028
+ },
41029
+ {
41030
+ TYPE: 'wfs',
41031
+ URL: 'https://some-wfs-server',
41032
+ NAME: 'some_layername',
41033
+ },
41034
+ ],
41035
+ });
41036
+ const malformedConfigFixture = () => `
41037
+ {
41038
+ "I thought": "we were still doing json"
41039
+ }
41040
+ `;
41041
+ const missingMandatoryConfigFixture = () => `
41042
+ [theme]
41043
+ primary_color = "#093564"
41044
+ secondary_color = "#c2e9dc"
41045
+ background_color = "#fdfbff"
41046
+ main_font = 'sans-serif'
41047
+ `;
41048
+ const wrongLanguageCodeConfigFixture = () => `
41049
+ [global]
41050
+ geonetwork4_api_url = "/geonetwork/srv/api"
41051
+ proxy_path = "/proxy/?url="
41052
+ metadata_language = "abc"
41053
+
41054
+ [map]
41055
+
41056
+ [theme]
41057
+ primary_color = "#093564"
41058
+ secondary_color = "#c2e9dc"
41059
+ main_color = "#212029" # All-purpose text color
41060
+ background_color = "#fdfbff"
41061
+ `;
41062
+ const unrecognizedKeysConfigFixture = () => `
41063
+ [global]
41064
+ geonetwork4_api_url = "/geonetwork/srv/api"
41065
+ proxy_path = "/proxy/?url="
41066
+ metadata_language = "fre"
41067
+ another_path = '/whatever'
41068
+
41069
+ [map]
41070
+ max_zoom = 10
41071
+ max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855]
41072
+ do_not_use_default_basemap = false
41073
+ external_viewer_url_template = 'https://example.com/myviewer?'
41074
+ external_viewer_open_new_tab = true
41075
+ another_zoom = 15
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
+ another_layer = "wrong layer definition"
41085
+
41086
+ [theme]
41087
+ primary_color = "#093564"
41088
+ secondary_color = "#c2e9dc"
41089
+ main_color = "#212029" # All-purpose text color
41090
+ background_color = "#fdfbff"
41091
+ thumbnail_placeholder = 'assets/img/placeholder.svg'
41092
+ header_background = 'teal'
41093
+ header_foreground_color = "#872e2e"
41094
+ main_font = 'sans-serif'
41095
+ title_font = 'serif'
41096
+ another_color = 'red'
41097
+ `;
41098
+
41099
+ function getMapContextLayerFromConfig(config) {
41100
+ switch (config.TYPE) {
41101
+ case 'wms':
41102
+ return {
41103
+ type: 'wms',
41104
+ url: config.URL,
41105
+ name: config.NAME,
41106
+ };
41107
+ case 'wfs':
41108
+ return {
41109
+ type: 'wfs',
41110
+ url: config.URL,
41111
+ featureType: config.NAME,
41112
+ };
41113
+ case 'xyz':
41114
+ return {
41115
+ type: config.TYPE,
41116
+ url: config.URL,
41117
+ };
41118
+ case 'geojson':
41119
+ return {
41120
+ type: config.TYPE,
41121
+ ...(config.DATA ? { data: config.DATA } : { url: config.URL }),
41122
+ };
41123
+ case 'maplibre-style':
41124
+ return {
41125
+ type: config.TYPE,
41126
+ styleUrl: config.STYLE_URL,
41127
+ accessToken: config.ACCESS_TOKEN,
41128
+ };
41129
+ }
41130
+ }
41131
+
40937
41132
  const CHART_TYPE_VALUES = [
40938
41133
  'bar',
40939
41134
  'bar-horizontal',
@@ -40949,5 +41144,5 @@ const CHART_TYPE_VALUES = [
40949
41144
  * Generated bundle index. Do not edit.
40950
41145
  */
40951
41146
 
40952
- 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, GEOCODING_PROVIDER, GEONETWORK_UI_TAG_NAME, GEONETWORK_UI_VERSION, GeoDataBadgeComponent, GeoTableViewComponent, GeocodingService, GeojsonReader, Gn4Converter, Gn4PlatformMapper, Gn4PlatformService, Gn4Repository, Gn4SettingsService, GnUiHumanizeDateDirective, GpfApiDlComponent, GravatarService, I18nInterceptor, ISO_TOPICS, ImageFallbackDirective, ImageInputComponent, ImageOverlayPreviewComponent, ImportRecordComponent, InlineFilterComponent, InteractiveTableColumnComponent, InteractiveTableComponent, InternalLinkCardComponent, IsSpatialSearchField, Iso191153Converter, Iso19139Converter, KeywordBadgeComponent, KindBadgeComponent, LANGUAGES_LIST, LANGUAGE_NAMES, LANGUAGE_STORAGE_KEY, LANG_2_TO_3_MAPPER, LOGIN_URL, LOGOUT_URL, LONLAT_CRS_CODES, LanguageSwitcherComponent, LayersPanelComponent, LicenseSearchField, LinkClassifierService, LinkUsage, LoadingMaskComponent, LocationSearchComponent, LogService, MAP_FEATURE_KEY, MAP_VIEW_CONSTRAINTS, MAX_UPLOAD_SIZE_MB, METADATA_LANGUAGE, MapContainerComponent, MapFacade, MapLegendComponent, MapStateContainerComponent, 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, 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 };
41147
+ 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 };
40953
41148
  //# sourceMappingURL=geonetwork-ui.mjs.map