geonetwork-ui 2.11.0-dev.6c8b66d60 → 2.11.0-dev.7f3cfba04

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 (56) hide show
  1. package/fesm2022/geonetwork-ui.mjs +1353 -1212
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +109 -59
  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 +317 -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 +2 -0
  11. package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +98 -0
  12. package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +7 -0
  13. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +2 -1
  14. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +29 -3
  15. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +5 -34
  16. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +6 -13
  17. package/src/libs/common/domain/src/lib/model/search/aggregation.model.ts +2 -0
  18. package/src/libs/common/domain/src/lib/model/search/filter.model.ts +4 -1
  19. package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +1 -1
  20. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +42 -62
  21. package/src/libs/feature/dataviz/src/lib/service/data.service.ts +32 -19
  22. package/src/libs/feature/map/src/index.ts +0 -1
  23. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +8 -3
  24. package/src/libs/feature/record/src/lib/state/mdview.effects.ts +3 -1
  25. package/src/libs/feature/record/src/lib/state/mdview.facade.ts +1 -1
  26. package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +7 -1
  27. package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +26 -4
  28. package/src/libs/feature/search/src/lib/geocoding/geocoding.service.ts +52 -8
  29. package/src/libs/feature/search/src/lib/location-search/location-search.component.html +11 -1
  30. package/src/libs/feature/search/src/lib/location-search/location-search.component.ts +77 -3
  31. package/src/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts +1 -0
  32. package/src/libs/feature/search/src/lib/state/reducer.ts +1 -4
  33. package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +56 -4
  34. package/src/libs/feature/search/src/lib/utils/service/fields.ts +33 -9
  35. package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.html +2 -2
  36. package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.ts +2 -2
  37. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.css +0 -4
  38. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.html +2 -2
  39. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts +3 -3
  40. package/src/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.ts +2 -1
  41. package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html +7 -1
  42. package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts +26 -3
  43. package/src/libs/util/app-config/src/lib/app-config.ts +34 -0
  44. package/src/libs/util/app-config/src/lib/fixtures.ts +37 -1
  45. package/src/libs/util/app-config/src/lib/model.ts +24 -1
  46. package/translations/de.json +8 -2
  47. package/translations/en.json +8 -2
  48. package/translations/es.json +8 -2
  49. package/translations/fr.json +12 -6
  50. package/translations/it.json +8 -2
  51. package/translations/nl.json +8 -2
  52. package/translations/pt.json +8 -2
  53. package/translations/sk.json +8 -2
  54. package/src/libs/feature/map/src/lib/style/index.ts +0 -2
  55. package/src/libs/feature/map/src/lib/style/map-style.fixtures.ts +0 -47
  56. 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
  }
@@ -2633,6 +2636,31 @@ function readDefaultLanguage$1(rootEl) {
2633
2636
  function readOtherLanguages(rootEl) {
2634
2637
  return pipe(findChildrenElement('mdb:otherLocale', false), mapArray(readLocaleElement()), map((languages) => languages.filter((lang) => lang !== null)))(rootEl);
2635
2638
  }
2639
+ function extractCharacterStringOrText() {
2640
+ return fallback(extractCharacterString(), readText());
2641
+ }
2642
+ function extractListedValue() {
2643
+ return pipe(combine(pipe(findChildElement('gfc:code', false), extractCharacterString()), pipe(findChildElement('gfc:label', false), extractCharacterString())), map(([code, label]) => ({
2644
+ ...(code && { code }),
2645
+ ...(label && { label }),
2646
+ })));
2647
+ }
2648
+ function extractFeatureAttribute() {
2649
+ return pipe(combine(pipe(findChildElement('gfc:memberName', false), extractCharacterStringOrText()), 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.code || value.label)))), map(([name, description, code, type, values]) => ({
2650
+ name: name ?? '',
2651
+ ...(description && { description }),
2652
+ ...(code && { code }),
2653
+ ...(type && { type }),
2654
+ ...(values.length > 0 && { values }),
2655
+ })));
2656
+ }
2657
+ function readFeatureTypeDescriptions(rootEl) {
2658
+ 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:definition', false), extractCharacterString()), pipe(findNestedElements('gfc:carrierOfCharacteristics', 'gfc:FC_FeatureAttribute'), mapArray(extractFeatureAttribute()))), map(([name, description, attributes]) => ({
2659
+ name: name ?? '',
2660
+ ...(description && { description }),
2661
+ attributes,
2662
+ })))))(rootEl);
2663
+ }
2636
2664
 
2637
2665
  function writeUniqueIdentifier$1(record, rootEl) {
2638
2666
  pipe(findNestedChildOrCreate('mdb:metadataIdentifier', 'mcc:MD_Identifier', 'mcc:code'), writeCharacterString(record.uniqueIdentifier))(rootEl);
@@ -2803,6 +2831,7 @@ class Iso191153Converter extends Iso19139Converter {
2803
2831
  this.readers['lineage'] = readLineage;
2804
2832
  this.readers['sourceRecords'] = readSourceRecords;
2805
2833
  this.readers['associatedRecords'] = readAssociatedRecords;
2834
+ this.readers['featureTypeDescriptions'] = readFeatureTypeDescriptions;
2806
2835
  this.readers['onlineResources'] = readOnlineResources$1;
2807
2836
  this.readers['defaultLanguage'] = readDefaultLanguage$1;
2808
2837
  this.readers['otherLanguages'] = readOtherLanguages;
@@ -3459,6 +3488,7 @@ class DcatApConverter extends BaseConverter {
3459
3488
  lineage: () => '',
3460
3489
  sourceRecords: () => [],
3461
3490
  associatedRecords: () => [],
3491
+ featureTypeDescriptions: () => undefined,
3462
3492
  temporalExtents: () => [],
3463
3493
  spatialRepresentation: () => undefined,
3464
3494
  extras: () => undefined,
@@ -3494,6 +3524,7 @@ class DcatApConverter extends BaseConverter {
3494
3524
  lineage: () => undefined,
3495
3525
  sourceRecords: () => [],
3496
3526
  associatedRecords: () => [],
3527
+ featureTypeDescriptions: () => undefined,
3497
3528
  onlineResources: () => undefined,
3498
3529
  temporalExtents: () => undefined,
3499
3530
  spatialExtents: () => undefined,
@@ -18904,9 +18935,15 @@ class Gn4FieldMapper {
18904
18935
  isOpenData: (output, source) => this.addExtra({
18905
18936
  isOpenData: selectField(source, 'isOpenData') !== 'false',
18906
18937
  }, output),
18907
- featureTypes: (output, source) => this.addExtra({
18908
- featureTypes: selectField(source, 'featureTypes'),
18909
- }, output),
18938
+ featureTypes: (output, source) => {
18939
+ const featureTypes = getAsArray(selectField(source, 'featureTypes'));
18940
+ if (!featureTypes.length)
18941
+ return output;
18942
+ return {
18943
+ ...output,
18944
+ featureTypeDescriptions: this.featureTypesField(featureTypes),
18945
+ };
18946
+ },
18910
18947
  related: (output, source) => {
18911
18948
  const related = selectField(source, 'related');
18912
18949
  const fcatSource = selectField(getFirstValue(selectField(related, 'fcats')) ?? {}, '_source');
@@ -19042,6 +19079,27 @@ class Gn4FieldMapper {
19042
19079
  },
19043
19080
  };
19044
19081
  this.genericField = (output) => output;
19082
+ this.featureTypesField = (featureTypes) => featureTypes.map((featureType) => ({
19083
+ name: featureType.typeName ?? '',
19084
+ ...(featureType.definition && {
19085
+ description: featureType.definition,
19086
+ }),
19087
+ attributes: getAsArray(featureType.attributeTable ?? []).map((attr) => {
19088
+ const values = (attr.values ?? [])
19089
+ .filter((value) => value.code || value.label)
19090
+ .map((value) => ({
19091
+ ...(value.code && { code: value.code }),
19092
+ ...(value.label && { label: value.label }),
19093
+ }));
19094
+ return {
19095
+ name: attr.name ?? '',
19096
+ ...(attr.definition && { description: attr.definition }),
19097
+ ...(attr.code && { code: attr.code }),
19098
+ ...(attr.type && { type: attr.type }),
19099
+ ...(values.length > 0 && { values }),
19100
+ };
19101
+ }),
19102
+ }));
19045
19103
  this.constraintField = (type, output, constraintArray) => {
19046
19104
  let outputField;
19047
19105
  switch (type) {
@@ -20452,7 +20510,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
20452
20510
  }] } });
20453
20511
 
20454
20512
  var name = "geonetwork-ui";
20455
- var version = "2.11.0-dev.6c8b66d60";
20513
+ var version = "2.11.0-dev.7f3cfba04";
20456
20514
  var engines = {
20457
20515
  node: ">=24"
20458
20516
  };
@@ -20494,10 +20552,10 @@ var dependencies = {
20494
20552
  "@biesbjerg/ngx-translate-extract-marker": "~1.0.0",
20495
20553
  "@camptocamp/ogc-client": "1.3.1-dev.200c02a",
20496
20554
  "@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",
20555
+ "@geospatial-sdk/core": "0.0.5-dev.86.sha-124eae3",
20556
+ "@geospatial-sdk/geocoding": "0.0.5-dev.86.sha-124eae3",
20557
+ "@geospatial-sdk/legend": "0.0.5-dev.86.sha-124eae3",
20558
+ "@geospatial-sdk/openlayers": "0.0.5-dev.86.sha-124eae3",
20501
20559
  "@ltd/j-toml": "~1.38.0",
20502
20560
  "@messageformat/core": "~3.4.0",
20503
20561
  "@ng-icons/core": "~32.5.0",
@@ -20514,6 +20572,7 @@ var dependencies = {
20514
20572
  "embla-carousel": "~8.6.0",
20515
20573
  "flag-icons": "~7.5.0",
20516
20574
  "geojson-validation": "~1.0.2",
20575
+ jsonpointer: "^5.0.1",
20517
20576
  marked: "~17.0.4",
20518
20577
  "ngx-dropzone": "~3.1.0",
20519
20578
  "ngx-translate-messageformat-compiler": "~7.2.0",
@@ -20690,6 +20749,7 @@ class ElasticsearchService {
20690
20749
  // runtime fields are computed using a Painless script
20691
20750
  // see: https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime-mapping-fields.html
20692
20751
  this.runtimeFields = {};
20752
+ this.fieldAliases = {};
20693
20753
  }
20694
20754
  // we're using getters in case the defined languages change over time
20695
20755
  get metadataLang() {
@@ -20761,6 +20821,9 @@ class ElasticsearchService {
20761
20821
  registerRuntimeField(fieldName, expression, type = 'keyword') {
20762
20822
  this.runtimeFields[fieldName] = { script: expression, type };
20763
20823
  }
20824
+ registerFieldAlias(fieldIdentifier, esFieldName) {
20825
+ this.fieldAliases[fieldIdentifier] = esFieldName;
20826
+ }
20764
20827
  getMetadataByIdsPayload(uuids) {
20765
20828
  return {
20766
20829
  query: {
@@ -20882,12 +20945,25 @@ class ElasticsearchService {
20882
20945
  }
20883
20946
  return Object.values(filters).find(isBoundingBox);
20884
20947
  }
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
20948
+ // builds a query matching records whose dates intersect the filter range
20887
20949
  buildDateRangeQuery(searchField, dateRange) {
20950
+ // fields ending with 'DateRange' maps as ES range type
20951
+ if (searchField.endsWith('DateRange')) {
20952
+ return {
20953
+ range: {
20954
+ [searchField]: {
20955
+ ...(dateRange.start && { gte: formatDate(dateRange.start) }),
20956
+ ...(dateRange.end && { lte: formatDate(dateRange.end) }),
20957
+ format: 'yyyy-MM-dd',
20958
+ relation: 'intersects',
20959
+ },
20960
+ },
20961
+ };
20962
+ }
20888
20963
  const minField = `${searchField}Min`;
20889
20964
  const maxField = `${searchField}Max`;
20890
20965
  if (minField in this.runtimeFields && maxField in this.runtimeFields) {
20966
+ // min/max runtime fields form an interval per record; match if it intersects the filter range
20891
20967
  const filter = [
20892
20968
  dateRange.start && {
20893
20969
  range: {
@@ -20942,7 +21018,7 @@ class ElasticsearchService {
20942
21018
  .filter((fieldname) => !Array.isArray(filters[fieldname]))
20943
21019
  .filter((fieldname) => filters[fieldname] &&
20944
21020
  JSON.stringify(filters[fieldname]) !== '{}')
20945
- .map((fieldname) => `${fieldname}:(${makeQuery(filters[fieldname])})`)
21021
+ .map((fieldname) => `${this.fieldAliases[fieldname] ?? fieldname}:(${makeQuery(filters[fieldname])})`)
20946
21022
  .join(' AND ');
20947
21023
  if (filters['gn-ui-crossFieldFilter']) {
20948
21024
  queryString = `${queryString} AND (${filters['gn-ui-crossFieldFilter']})`;
@@ -21240,6 +21316,12 @@ class ElasticsearchService {
21240
21316
  order: {
21241
21317
  [`_${aggregation.sort[1]}`]: aggregation.sort[0],
21242
21318
  },
21319
+ ...(aggregation.includeValues && {
21320
+ include: aggregation.includeValues,
21321
+ }),
21322
+ ...(aggregation.excludeValues && {
21323
+ exclude: aggregation.excludeValues,
21324
+ }),
21243
21325
  },
21244
21326
  };
21245
21327
  case 'histogram':
@@ -21374,42 +21456,16 @@ class Gn4Repository {
21374
21456
  ? this.gn4Mapper.readRecords(records)
21375
21457
  : of(null)));
21376
21458
  }
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
21459
+ queryLinkedFeatureCatalog(record, visited = new Set() // prevent looping
21403
21460
  ) {
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']));
21461
+ if ('featureTypeDescriptions' in record &&
21462
+ record.featureTypeDescriptions?.length > 0) {
21463
+ return of({ featureTypes: record.featureTypeDescriptions });
21408
21464
  }
21409
21465
  const featureCatalogIdentifier = record.extras['featureCatalogIdentifier'];
21410
21466
  if (featureCatalogIdentifier && !visited.has(featureCatalogIdentifier)) {
21411
21467
  visited.add(featureCatalogIdentifier);
21412
- return this.getRecord(featureCatalogIdentifier).pipe(switchMap((record) => record ? this.getFeatureCatalog(record, visited) : of(null)));
21468
+ return this.getRecord(featureCatalogIdentifier).pipe(switchMap((record) => record ? this.queryLinkedFeatureCatalog(record, visited) : of(null)));
21413
21469
  }
21414
21470
  return of(null);
21415
21471
  }
@@ -23082,1149 +23138,441 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
23082
23138
  }]
23083
23139
  }] });
23084
23140
 
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) };
23141
+ class AnchorLinkDirective {
23142
+ constructor() {
23143
+ this.changeDetector = inject(ChangeDetectorRef);
23144
+ this.disabled = false;
23145
+ this.observer = new MutationObserver(() => {
23146
+ this.refreshDisabledState();
23147
+ });
23148
+ this.inView = false;
23149
+ this.initialized = false;
23090
23150
  }
23091
- else if (typeof val === 'object' && !Array.isArray(val)) {
23092
- return {
23093
- ...prev,
23094
- ...flatten(path, val),
23095
- };
23151
+ get elementClass() {
23152
+ if (this.disabled) {
23153
+ return this.disabledClass;
23154
+ }
23155
+ if (this.inView) {
23156
+ return `${this.inViewClass} ${this.enabledClass}`;
23157
+ }
23158
+ else {
23159
+ return `${this.outOfViewClass} ${this.enabledClass}`;
23160
+ }
23096
23161
  }
23097
- else {
23098
- return { ...prev, [path]: val };
23162
+ ngOnInit() {
23163
+ this.observer.observe(document.body, {
23164
+ childList: true,
23165
+ subtree: true,
23166
+ });
23167
+ this.refreshDisabledState();
23099
23168
  }
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;
23169
+ ngAfterViewChecked() {
23170
+ if (!this.initialized && !this.disabled) {
23171
+ const target = document.getElementById(this.targetId);
23172
+ if (target) {
23173
+ this.initializeIntersectionObserver(target);
23174
+ this.initialized = true;
23175
+ }
23176
+ }
23120
23177
  }
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;
23178
+ initializeIntersectionObserver(target) {
23179
+ this.intersectionObserver = new IntersectionObserver((entries) => {
23180
+ entries.forEach((entry) => {
23181
+ this.inView = entry.isIntersecting;
23182
+ this.changeDetector.detectChanges();
23183
+ });
23184
+ }, {
23185
+ root: null,
23186
+ rootMargin: '-30% 0% -60% 0%',
23187
+ });
23188
+ this.intersectionObserver.observe(target);
23127
23189
  }
23128
- else if (keysCheck.unrecognized.length) {
23129
- outWarnings.push(`In the [${sectionName}] section: ${keysCheck.unrecognized.join(', ')}`);
23130
- keysCheck.unrecognized.forEach((key) => delete sectionConf[key]);
23190
+ ngOnDestroy() {
23191
+ this.observer.disconnect();
23192
+ if (this.intersectionObserver) {
23193
+ this.intersectionObserver.disconnect();
23194
+ }
23131
23195
  }
23132
- return sectionConf;
23196
+ refreshDisabledState() {
23197
+ const targetNotPresent = !document.getElementById(this.targetId);
23198
+ if (targetNotPresent !== this.disabled) {
23199
+ this.disabled = targetNotPresent;
23200
+ this.changeDetector.detectChanges();
23201
+ }
23202
+ }
23203
+ scrollToTarget() {
23204
+ const target = document.getElementById(this.targetId);
23205
+ if (!target)
23206
+ return;
23207
+ target.scrollIntoView({
23208
+ behavior: 'smooth',
23209
+ block: 'start',
23210
+ });
23211
+ }
23212
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AnchorLinkDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
23213
+ 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
23214
  }
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 {};
23215
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AnchorLinkDirective, decorators: [{
23216
+ type: Directive,
23217
+ args: [{
23218
+ selector: '[gnUiAnchorLink]',
23219
+ standalone: true,
23220
+ }]
23221
+ }], propDecorators: { targetId: [{
23222
+ type: Input,
23223
+ args: ['gnUiAnchorLink']
23224
+ }], disabledClass: [{
23225
+ type: Input,
23226
+ args: ['gnUiAnchorLinkDisabledClass']
23227
+ }], enabledClass: [{
23228
+ type: Input,
23229
+ args: ['gnUiAnchorLinkEnabledClass']
23230
+ }], inViewClass: [{
23231
+ type: Input,
23232
+ args: ['gnUiAnchorLinkInViewClass']
23233
+ }], outOfViewClass: [{
23234
+ type: Input,
23235
+ args: ['gnUiAnchorLinkOutOfViewClass']
23236
+ }], elementClass: [{
23237
+ type: HostBinding,
23238
+ args: ['class']
23239
+ }], scrollToTarget: [{
23240
+ type: HostListener,
23241
+ args: ['click']
23242
+ }] } });
23243
+
23244
+ class PaginationDotsComponent {
23245
+ constructor() {
23246
+ this.containerClass = '';
23142
23247
  }
23143
- return flatten('', fullConfigObj[sectionName], true);
23248
+ // 1-based
23249
+ get steps() {
23250
+ return Array.from({ length: this.listComponent.pagesCount }, (_, i) => i + 1);
23251
+ }
23252
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PaginationDotsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23253
+ 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: [
23254
+ provideIcons({
23255
+ iconoirNavArrowRight,
23256
+ iconoirNavArrowLeft,
23257
+ }),
23258
+ ] }); }
23144
23259
  }
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 [];
23260
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PaginationDotsComponent, decorators: [{
23261
+ type: Component,
23262
+ args: [{ selector: 'gn-ui-pagination-dots', standalone: true, imports: [CommonModule], viewProviders: [
23263
+ provideIcons({
23264
+ iconoirNavArrowRight,
23265
+ iconoirNavArrowLeft,
23266
+ }),
23267
+ ], 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"] }]
23268
+ }], propDecorators: { listComponent: [{
23269
+ type: Input
23270
+ }], containerClass: [{
23271
+ type: Input
23272
+ }] } });
23273
+
23274
+ class CarouselComponent {
23275
+ constructor() {
23276
+ this.changeDetector = inject(ChangeDetectorRef);
23277
+ this.containerClass = '';
23278
+ this.stepsContainerClass = 'w-full bottom-0 top-auto';
23279
+ this.currentStepChange = new EventEmitter();
23280
+ this.steps = [];
23281
+ this.currentStep = 0;
23282
+ this.refreshSteps = () => {
23283
+ this.steps = this.emblaApi.scrollSnapList();
23284
+ this.currentStep = this.emblaApi.selectedScrollSnap();
23285
+ this.currentStepChange.emit(this.currentStep);
23286
+ this.changeDetector.detectChanges();
23287
+ };
23154
23288
  }
23155
- if (!Array.isArray(sections)) {
23156
- outErrors.push(`Expected [[${sectionName}]] to be an array of sections.`);
23157
- return null;
23289
+ // Paginable API
23290
+ get isFirstPage() {
23291
+ return this.currentStep === 0;
23158
23292
  }
23159
- const result = sections.map((section) => parseConfigSection({ [sectionName]: section }, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors));
23160
- if (result.some((r) => r === null)) {
23161
- return null;
23293
+ get isLastPage() {
23294
+ return this.currentStep === this.steps.length - 1;
23162
23295
  }
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`);
23296
+ get currentPage() {
23297
+ return this.currentStep + 1; // this is 1-based
23169
23298
  }
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
- };
23299
+ get pagesCount() {
23300
+ return this.steps.length;
23180
23301
  }
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
- };
23302
+ goToPage(stepIndex) {
23303
+ this.emblaApi.scrollTo(stepIndex - 1); // this is 0-based
23195
23304
  }
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
- };
23305
+ goToPrevPage() {
23306
+ if (this.isFirstPage)
23307
+ return;
23308
+ this.emblaApi.scrollPrev();
23309
+ }
23310
+ goToNextPage() {
23311
+ if (this.isLastPage)
23312
+ return;
23313
+ this.emblaApi.scrollNext();
23314
+ }
23315
+ ngAfterViewInit() {
23316
+ this.emblaApi = EmblaCarousel(this.carouselOverflowContainer.nativeElement, {
23317
+ duration: 15,
23318
+ });
23319
+ this.emblaApi
23320
+ .on('init', this.refreshSteps)
23321
+ .on('reInit', this.refreshSteps)
23322
+ .on('select', this.refreshSteps);
23323
+ }
23324
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CarouselComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23325
+ 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
23326
  }
23327
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CarouselComponent, decorators: [{
23328
+ type: Component,
23329
+ 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"] }]
23330
+ }], propDecorators: { carouselOverflowContainer: [{
23331
+ type: ViewChild,
23332
+ args: ['carouselOverflowContainer']
23333
+ }], containerClass: [{
23334
+ type: Input
23335
+ }], stepsContainerClass: [{
23336
+ type: Input
23337
+ }], currentStepChange: [{
23338
+ type: Output
23339
+ }] } });
23202
23340
 
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
- })));
23341
+ class ProgressBarComponent {
23342
+ constructor() {
23343
+ this.value = 0;
23344
+ this.type = 'default';
23345
+ }
23346
+ get progress() {
23347
+ return this.value > 0 ? (this.value < 100 ? this.value : 100) : 0;
23348
+ }
23349
+ get color() {
23350
+ switch (this.type) {
23351
+ case 'default':
23352
+ return {
23353
+ outerBar: 'bg-gray-200',
23354
+ innerBar: 'bg-gray-100',
23355
+ text: 'text-gray-900',
23356
+ };
23357
+ case 'primary':
23358
+ return {
23359
+ outerBar: 'bg-primary',
23360
+ innerBar: 'bg-primary-lighter',
23361
+ text: 'text-white',
23362
+ };
23363
+ case 'secondary':
23364
+ return {
23365
+ outerBar: 'bg-secondary',
23366
+ innerBar: 'bg-secondary-lighter',
23367
+ text: 'text-white',
23368
+ };
23369
+ case 'light':
23370
+ return {
23371
+ outerBar: 'bg-primary-white',
23372
+ innerBar: 'bg-primary',
23373
+ text: 'text-main',
23374
+ };
23375
+ }
23215
23376
  }
23377
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProgressBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23378
+ 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
23379
  }
23380
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProgressBarComponent, decorators: [{
23381
+ type: Component,
23382
+ 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"] }]
23383
+ }], propDecorators: { value: [{
23384
+ type: Input
23385
+ }], type: [{
23386
+ type: Input
23387
+ }] } });
23217
23388
 
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,
23389
+ class PopoverComponent {
23390
+ constructor() {
23391
+ this.viewContainerRef = inject(ViewContainerRef);
23392
+ this.renderer = inject(Renderer2);
23393
+ }
23394
+ getContent() {
23395
+ if (this.content instanceof TemplateRef) {
23396
+ if (this.view) {
23397
+ this.view.destroy();
23398
+ }
23399
+ this.view = this.viewContainerRef.createEmbeddedView(this.content);
23400
+ this.view.detectChanges();
23401
+ const wrapper = this.renderer.createElement('div'); // Create a wrapper div
23402
+ this.view.rootNodes.forEach((node) => {
23403
+ this.renderer.appendChild(wrapper, node); // Append each root node to the wrapper
23404
+ });
23405
+ return wrapper;
23406
+ }
23407
+ return this.content;
23408
+ }
23409
+ ngAfterViewInit() {
23410
+ this.tippyInstance = tippy(this.popoverContent.nativeElement, {
23411
+ content: this.getContent(),
23412
+ allowHTML: true,
23413
+ theme: this.theme,
23251
23414
  });
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 });
23415
+ }
23416
+ ngOnChanges(changes) {
23417
+ if (changes['theme']) {
23418
+ this.theme = changes['theme'].currentValue;
23419
+ if (this.tippyInstance) {
23420
+ this.tippyInstance.setProps({ theme: this.theme });
23421
+ }
23272
23422
  }
23273
- catch (e) {
23274
- throw new Error(`An error occurred when parsing the configuration file: ${e.message}`);
23423
+ if (changes['content']) {
23424
+ this.content = changes['content'].currentValue;
23425
+ if (this.tippyInstance) {
23426
+ this.tippyInstance.setContent(this.getContent());
23427
+ }
23275
23428
  }
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);
23429
+ }
23430
+ ngOnDestroy() {
23431
+ if (this.tippyInstance) {
23432
+ this.tippyInstance.destroy();
23293
23433
  }
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')}`);
23434
+ if (this.view) {
23435
+ this.view.destroy();
23429
23436
  }
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;
23437
+ }
23438
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23439
+ 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: [""] }); }
23441
23440
  }
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="
23441
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopoverComponent, decorators: [{
23442
+ type: Component,
23443
+ args: [{ selector: 'gn-ui-popover', standalone: true, imports: [], template: "<span #popoverContent>\n <ng-content></ng-content>\n</span>\n" }]
23444
+ }], propDecorators: { popoverContent: [{
23445
+ type: ViewChild,
23446
+ args: ['popoverContent', { static: false }]
23447
+ }], content: [{
23448
+ type: Input
23449
+ }], theme: [{
23450
+ type: Input
23451
+ }] } });
23534
23452
 
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"
23453
+ class LoadingMaskComponent {
23454
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LoadingMaskComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23455
+ 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 }); }
23568
23456
  }
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
- `;
23457
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LoadingMaskComponent, decorators: [{
23458
+ type: Component,
23459
+ 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"] }]
23460
+ }], propDecorators: { message: [{
23461
+ type: Input
23462
+ }] } });
23627
23463
 
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
- }
23464
+ class ColorScaleComponent {
23465
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ColorScaleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23466
+ 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: [""] }); }
23659
23467
  }
23468
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ColorScaleComponent, decorators: [{
23469
+ type: Component,
23470
+ 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" }]
23471
+ }] });
23660
23472
 
23661
- class MapStyleService {
23473
+ class PopupAlertComponent {
23662
23474
  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
- };
23475
+ this.changeDetector = inject(ChangeDetectorRef);
23476
+ this.type = 'info';
23477
+ this.position = 'top';
23478
+ this.expanded = false;
23479
+ this.timeout = null;
23672
23480
  }
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
- };
23481
+ get showDuration() {
23482
+ const chars = this.content.nativeElement.innerHTML.length;
23483
+ return Math.max(3000, chars * 20);
23717
23484
  }
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
- };
23485
+ ngOnInit() {
23486
+ this.expandAndClose();
23487
+ }
23488
+ expand() {
23489
+ this.expanded = true;
23490
+ this.changeDetector.detectChanges();
23491
+ clearTimeout(this.timeout);
23737
23492
  }
23738
- computeTransparentFillColor(color, alpha = 0.25) {
23739
- return chroma(color).alpha(alpha).css();
23493
+ expandAndClose() {
23494
+ this.expanded = true;
23495
+ this.changeDetector.detectChanges();
23496
+ clearTimeout(this.timeout);
23497
+ this.timeout = setTimeout(() => {
23498
+ this.expanded = false;
23499
+ this.changeDetector.detectChanges();
23500
+ }, this.showDuration);
23740
23501
  }
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' }); }
23502
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopupAlertComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23503
+ 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: [
23504
+ provideIcons({ matErrorOutlineOutline }),
23505
+ provideNgIconsConfig({
23506
+ size: '1.5em',
23507
+ }),
23508
+ ], 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 }); }
23743
23509
  }
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
- }]
23749
- }] });
23510
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopupAlertComponent, decorators: [{
23511
+ type: Component,
23512
+ args: [{ selector: 'gn-ui-popup-alert', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, NgIconComponent], providers: [
23513
+ provideIcons({ matErrorOutlineOutline }),
23514
+ provideNgIconsConfig({
23515
+ size: '1.5em',
23516
+ }),
23517
+ ], 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"] }]
23518
+ }], propDecorators: { icon: [{
23519
+ type: Input
23520
+ }], type: [{
23521
+ type: Input
23522
+ }], position: [{
23523
+ type: Input
23524
+ }], content: [{
23525
+ type: ViewChild,
23526
+ args: ['content']
23527
+ }] } });
23750
23528
 
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
- });
23529
+ class SpinningLoaderComponent {
23530
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpinningLoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23531
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: SpinningLoaderComponent, isStandalone: true, selector: "gn-ui-spinning-loader", ngImport: i0, template: "<span>\n <svg\n class=\"animate-spin\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n</span>\n", styles: [""] }); }
23532
+ }
23533
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpinningLoaderComponent, decorators: [{
23534
+ type: Component,
23535
+ args: [{ selector: 'gn-ui-spinning-loader', standalone: true, template: "<span>\n <svg\n class=\"animate-spin\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n</span>\n" }]
23536
+ }] });
23792
23537
 
23793
- class AnchorLinkDirective {
23538
+ class ButtonComponent {
23794
23539
  constructor() {
23795
- this.changeDetector = inject(ChangeDetectorRef);
23540
+ this.btnClass = 'gn-ui-btn-default';
23796
23541
  this.disabled = false;
23797
- this.observer = new MutationObserver(() => {
23798
- this.refreshDisabledState();
23799
- });
23800
- this.inView = false;
23801
- this.initialized = false;
23542
+ this.extraClass = '';
23543
+ this.buttonClick = new EventEmitter();
23802
23544
  }
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
- }
23813
- }
23814
- 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);
23841
- }
23842
- ngOnDestroy() {
23843
- this.observer.disconnect();
23844
- if (this.intersectionObserver) {
23845
- this.intersectionObserver.disconnect();
23846
- }
23847
- }
23848
- refreshDisabledState() {
23849
- const targetNotPresent = !document.getElementById(this.targetId);
23850
- if (targetNotPresent !== this.disabled) {
23851
- this.disabled = targetNotPresent;
23852
- 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
- });
23863
- }
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 }); }
24161
- }
24162
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: PopupAlertComponent, decorators: [{
24163
- type: Component,
24164
- args: [{ selector: 'gn-ui-popup-alert', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, NgIconComponent], providers: [
24165
- provideIcons({ matErrorOutlineOutline }),
24166
- provideNgIconsConfig({
24167
- size: '1.5em',
24168
- }),
24169
- ], 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"] }]
24170
- }], propDecorators: { icon: [{
24171
- type: Input
24172
- }], type: [{
24173
- type: Input
24174
- }], position: [{
24175
- type: Input
24176
- }], content: [{
24177
- type: ViewChild,
24178
- args: ['content']
24179
- }] } });
24180
-
24181
- class SpinningLoaderComponent {
24182
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpinningLoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
24183
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: SpinningLoaderComponent, isStandalone: true, selector: "gn-ui-spinning-loader", ngImport: i0, template: "<span>\n <svg\n class=\"animate-spin\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n</span>\n", styles: [""] }); }
24184
- }
24185
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpinningLoaderComponent, decorators: [{
24186
- type: Component,
24187
- args: [{ selector: 'gn-ui-spinning-loader', standalone: true, template: "<span>\n <svg\n class=\"animate-spin\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n</span>\n" }]
24188
- }] });
24189
-
24190
- class ButtonComponent {
24191
- constructor() {
24192
- this.btnClass = 'gn-ui-btn-default';
24193
- this.disabled = false;
24194
- this.extraClass = '';
24195
- this.buttonClick = new EventEmitter();
24196
- }
24197
- set type(value) {
24198
- // btn-classes are written in full to be picked up by tailwind
24199
- switch (value) {
24200
- case 'primary':
24201
- this.btnClass = 'gn-ui-btn-primary';
24202
- break;
24203
- case 'secondary':
24204
- this.btnClass = 'gn-ui-btn-secondary';
24205
- break;
24206
- case 'outline':
24207
- this.btnClass = 'gn-ui-btn-outline';
24208
- break;
24209
- case 'light':
24210
- this.btnClass = 'gn-ui-btn-light';
24211
- break;
24212
- case 'gray':
24213
- this.btnClass = 'gn-ui-btn-gray';
24214
- break;
24215
- case 'gray-light':
24216
- this.btnClass = 'gn-ui-btn-gray-light';
24217
- break;
24218
- case 'black':
24219
- this.btnClass = 'gn-ui-btn-black';
24220
- break;
24221
- case 'primary-light':
24222
- this.btnClass = 'gn-ui-btn-primary-light';
24223
- break;
24224
- case 'default':
24225
- default:
24226
- this.btnClass = 'gn-ui-btn-default';
24227
- break;
23545
+ set type(value) {
23546
+ // btn-classes are written in full to be picked up by tailwind
23547
+ switch (value) {
23548
+ case 'primary':
23549
+ this.btnClass = 'gn-ui-btn-primary';
23550
+ break;
23551
+ case 'secondary':
23552
+ this.btnClass = 'gn-ui-btn-secondary';
23553
+ break;
23554
+ case 'outline':
23555
+ this.btnClass = 'gn-ui-btn-outline';
23556
+ break;
23557
+ case 'light':
23558
+ this.btnClass = 'gn-ui-btn-light';
23559
+ break;
23560
+ case 'gray':
23561
+ this.btnClass = 'gn-ui-btn-gray';
23562
+ break;
23563
+ case 'gray-light':
23564
+ this.btnClass = 'gn-ui-btn-gray-light';
23565
+ break;
23566
+ case 'black':
23567
+ this.btnClass = 'gn-ui-btn-black';
23568
+ break;
23569
+ case 'primary-light':
23570
+ this.btnClass = 'gn-ui-btn-primary-light';
23571
+ break;
23572
+ case 'default':
23573
+ default:
23574
+ this.btnClass = 'gn-ui-btn-default';
23575
+ break;
24228
23576
  }
24229
23577
  }
24230
23578
  get classList() {
@@ -24306,15 +23654,15 @@ class AutocompleteComponent {
24306
23654
  getExtraClass() {
24307
23655
  if (this.allowSubmit) {
24308
23656
  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]';
23657
+ 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
23658
  }
24311
23659
  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))]';
23660
+ 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
23661
  }
24314
23662
  }
24315
23663
  else {
24316
23664
  if (!this.enterButton) {
24317
- return 'border rounded-lg absolute w-8 h-8 right-2 inset-y-2';
23665
+ return 'border rounded-lg absolute w-8 h-8 right-2 top-1/2 -translate-y-1/2';
24318
23666
  }
24319
23667
  }
24320
23668
  return 'border rounded-lg absolute w-8 h-8';
@@ -24459,7 +23807,7 @@ class AutocompleteComponent {
24459
23807
  provideNgIconsConfig({
24460
23808
  size: '1.75rem',
24461
23809
  }),
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 }); }
23810
+ ], 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
23811
  }
24464
23812
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AutocompleteComponent, decorators: [{
24465
23813
  type: Component,
@@ -24480,7 +23828,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
24480
23828
  provideNgIconsConfig({
24481
23829
  size: '1.75rem',
24482
23830
  }),
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"] }]
23831
+ ], 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
23832
  }], propDecorators: { placeholder: [{
24485
23833
  type: Input
24486
23834
  }], enterButton: [{
@@ -25956,7 +25304,8 @@ class SearchFeatureCatalogComponent {
25956
25304
  const filtered = (this.featureCatalog?.featureTypes || []).filter((featureType) => {
25957
25305
  const fuzzyFilter = createFuzzyFilter(searchTerm);
25958
25306
  return (fuzzyFilter(featureType.name) ||
25959
- (featureType.definition && fuzzyFilter(featureType.definition)));
25307
+ (featureType.description &&
25308
+ fuzzyFilter(featureType.description)));
25960
25309
  });
25961
25310
  this.filteredFeatureCatalog = {
25962
25311
  ...this.featureCatalog,
@@ -26044,6 +25393,8 @@ marker('search.filters.spatialExtent.error.title');
26044
25393
  marker('search.filters.spatialExtent.error.outOfBounds');
26045
25394
  const LABEL_FROM_FILE = marker('search.filters.spatialExtent.bboxFromFile');
26046
25395
  const LABEL_FROM_FILE_DELETE = marker('search.filters.spatialExtent.bboxFromFileDelete');
25396
+ const LABEL_FROM_LOCATION = marker('search.filters.spatialExtent.bboxFromLocation');
25397
+ const LABEL_FROM_LOCATION_DELETE = marker('search.filters.spatialExtent.bboxFromLocationDelete');
26047
25398
  const LABEL_INITIAL = marker('search.filters.spatialExtent.bboxInitial');
26048
25399
  const LABEL_INITIAL_DELETE = marker('search.filters.spatialExtent.bboxInitialDelete');
26049
25400
  class SpatialExtentDropdownComponent {
@@ -26055,6 +25406,7 @@ class SpatialExtentDropdownComponent {
26055
25406
  this.errorChange = new EventEmitter();
26056
25407
  this.bbox = null;
26057
25408
  this.fileName = '';
25409
+ this.locationName = '';
26058
25410
  this.overlayPositions = [
26059
25411
  {
26060
25412
  originX: 'start',
@@ -26085,13 +25437,21 @@ class SpatialExtentDropdownComponent {
26085
25437
  return !!this.bbox;
26086
25438
  }
26087
25439
  get selectionLabelKey() {
26088
- return this.fileName ? LABEL_FROM_FILE : LABEL_INITIAL;
25440
+ if (this.fileName)
25441
+ return LABEL_FROM_FILE;
25442
+ if (this.locationName)
25443
+ return LABEL_FROM_LOCATION;
25444
+ return LABEL_INITIAL;
26089
25445
  }
26090
25446
  get selectionDeleteLabelKey() {
26091
- return this.fileName ? LABEL_FROM_FILE_DELETE : LABEL_INITIAL_DELETE;
25447
+ if (this.fileName)
25448
+ return LABEL_FROM_FILE_DELETE;
25449
+ if (this.locationName)
25450
+ return LABEL_FROM_LOCATION_DELETE;
25451
+ return LABEL_INITIAL_DELETE;
26092
25452
  }
26093
25453
  get selectionLabelParams() {
26094
- return { fileName: this.fileName };
25454
+ return { fileName: this.fileName, locationName: this.locationName };
26095
25455
  }
26096
25456
  openOverlay() {
26097
25457
  this.overlayMinWidth =
@@ -26128,6 +25488,7 @@ class SpatialExtentDropdownComponent {
26128
25488
  }
26129
25489
  this.bbox = bbox;
26130
25490
  this.fileName = file.name;
25491
+ this.locationName = '';
26131
25492
  this.bboxChange.emit(bbox);
26132
25493
  this.cd.markForCheck();
26133
25494
  }
@@ -26136,6 +25497,15 @@ class SpatialExtentDropdownComponent {
26136
25497
  return;
26137
25498
  }
26138
25499
  }
25500
+ setLocationBbox(bbox, name) {
25501
+ this.errorKey = null;
25502
+ this.bbox = bbox;
25503
+ this.locationName = name;
25504
+ this.fileName = '';
25505
+ this.fileInput?.clear();
25506
+ this.bboxChange.emit(bbox);
25507
+ this.cd.markForCheck();
25508
+ }
26139
25509
  handleFileError(error) {
26140
25510
  if (error === 'file-too-large') {
26141
25511
  this.setError(marker('search.filters.spatialExtent.error.fileTooLarge'), {
@@ -26154,6 +25524,7 @@ class SpatialExtentDropdownComponent {
26154
25524
  removeSelection(event) {
26155
25525
  this.bbox = null;
26156
25526
  this.fileName = '';
25527
+ this.locationName = '';
26157
25528
  this.errorKey = null;
26158
25529
  this.fileInput?.clear();
26159
25530
  this.bboxChange.emit(null);
@@ -26170,7 +25541,7 @@ class SpatialExtentDropdownComponent {
26170
25541
  matExpandLess,
26171
25542
  matExpandMore,
26172
25543
  }),
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 }); }
25544
+ ], 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
25545
  }
26175
25546
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, decorators: [{
26176
25547
  type: Component,
@@ -26190,7 +25561,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
26190
25561
  matExpandLess,
26191
25562
  matExpandMore,
26192
25563
  }),
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" }]
25564
+ ], 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
25565
  }], propDecorators: { title: [{
26195
25566
  type: Input
26196
25567
  }], maxFileSizeMb: [{
@@ -27459,9 +26830,7 @@ const initSearch = () => {
27459
26830
  },
27460
26831
  params: {
27461
26832
  filters: {},
27462
- pageSize: getOptionalSearchConfig()?.LIMIT
27463
- ? getOptionalSearchConfig().LIMIT
27464
- : DEFAULT_PAGE_SIZE,
26833
+ pageSize: DEFAULT_PAGE_SIZE,
27465
26834
  currentPage: 0,
27466
26835
  favoritesOnly: false,
27467
26836
  useSpatialFilter: true,
@@ -27910,10 +27279,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
27910
27279
  }]
27911
27280
  }] });
27912
27281
 
27282
+ const DEFAULT_GEOCODING_PROVIDER = [
27283
+ 'geonames',
27284
+ { maxRows: 5 },
27285
+ ];
27913
27286
  const GEOCODING_PROVIDER = new InjectionToken('geocoding-provider', {
27914
27287
  providedIn: 'root',
27915
- factory: () => ['geonames', { maxRows: 5 }],
27288
+ factory: () => DEFAULT_GEOCODING_PROVIDER,
27916
27289
  });
27290
+ // these options are arrays in the providers, but they can be configured
27291
+ // with a single value for simplicity
27292
+ const GEOCODING_PROVIDER_ARRAY_OPTIONS = {
27293
+ geoadmin: ['origins', 'features'],
27294
+ geoplateforme: [
27295
+ 'index',
27296
+ 'category',
27297
+ 'type',
27298
+ 'postCode',
27299
+ 'cityCode',
27300
+ 'depCode',
27301
+ ],
27302
+ };
27303
+ function wrapArrayOptions(options, keys) {
27304
+ const wrapped = { ...options };
27305
+ for (const key of keys) {
27306
+ if (typeof wrapped[key] === 'string') {
27307
+ wrapped[key] = [wrapped[key]];
27308
+ }
27309
+ }
27310
+ return wrapped;
27311
+ }
27917
27312
  class GeocodingService {
27918
27313
  constructor() {
27919
27314
  this.provider = inject(GEOCODING_PROVIDER);
@@ -27922,13 +27317,16 @@ class GeocodingService {
27922
27317
  let queryObservable;
27923
27318
  switch (this.provider[0]) {
27924
27319
  case 'geoadmin':
27925
- queryObservable = from(queryGeoadmin(text, this.provider[1]));
27320
+ queryObservable = from(queryGeoadmin(text, wrapArrayOptions(this.provider[1], GEOCODING_PROVIDER_ARRAY_OPTIONS.geoadmin)));
27926
27321
  break;
27927
27322
  case 'geonames':
27928
27323
  queryObservable = from(queryGeonames(text, this.provider[1]));
27929
27324
  break;
27930
- case 'data-gouv-fr':
27931
- queryObservable = from(queryDataGouvFr(text, this.provider[1]));
27325
+ case 'geoplateforme':
27326
+ queryObservable = from(queryGeoplateforme(text, {
27327
+ ...wrapArrayOptions(this.provider[1], GEOCODING_PROVIDER_ARRAY_OPTIONS.geoplateforme),
27328
+ returnTrueGeometry: true,
27329
+ }));
27932
27330
  break;
27933
27331
  default:
27934
27332
  return throwError(() => new Error(`Unsupported geocoding provider: ${this.provider[0]}`));
@@ -27945,29 +27343,80 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
27945
27343
  }]
27946
27344
  }] });
27947
27345
 
27346
+ const GEOCODING_PROVIDER_LABELS = new InjectionToken('geocodingProviderLabels', {
27347
+ factory: () => ({}),
27348
+ });
27948
27349
  class LocationSearchComponent {
27949
27350
  constructor() {
27950
27351
  this.geocodingService = inject(GeocodingService);
27352
+ this.labels = inject(GEOCODING_PROVIDER_LABELS);
27951
27353
  this.placeholder = '';
27952
27354
  this.resultSelected = new EventEmitter();
27355
+ this.bboxSelected = new EventEmitter();
27953
27356
  this.displayWithFn = (item) => item.label;
27954
27357
  this.searchAction = (text) => this.geocodingService.query(text);
27955
27358
  }
27359
+ getSecondaryLabel(result) {
27360
+ return this.resolveLabel(result, this.labels.secondary);
27361
+ }
27362
+ getMainLabel(result) {
27363
+ return this.resolveLabel(result, this.labels.main) ?? result.label;
27364
+ }
27365
+ getTertiaryLabel(result) {
27366
+ return this.resolveLabel(result, this.labels.tertiary);
27367
+ }
27368
+ getDisplayLabel(result) {
27369
+ const tertiary = this.getTertiaryLabel(result);
27370
+ return this.getMainLabel(result) + (tertiary ? ', ' + tertiary : '');
27371
+ }
27372
+ resolveLabel(result, path) {
27373
+ if (!path)
27374
+ return undefined;
27375
+ let value;
27376
+ try {
27377
+ value = pointer.get(result, path);
27378
+ }
27379
+ catch {
27380
+ return undefined;
27381
+ }
27382
+ const values = Array.isArray(value) ? value : [value];
27383
+ const label = values
27384
+ .filter((v) => typeof v === 'string' || typeof v === 'number')
27385
+ .join(', ');
27386
+ return label || undefined;
27387
+ }
27388
+ // geoplateforme keeps a simplified geometry in geom and puts the real one,
27389
+ // as a JSON string, in properties.truegeometry when returnTrueGeometry is set
27390
+ getGeometry(result) {
27391
+ const trueGeometry = result.properties?.['truegeometry'];
27392
+ if (typeof trueGeometry === 'string') {
27393
+ return JSON.parse(trueGeometry);
27394
+ }
27395
+ return trueGeometry ?? result.geom;
27396
+ }
27956
27397
  handleItemSelected(item) {
27957
- this.resultSelected.emit(item);
27398
+ const result = item;
27399
+ this.resultSelected.emit(result);
27400
+ const geometry = this.getGeometry(result);
27401
+ if (geometry) {
27402
+ this.bboxSelected.emit({
27403
+ bbox: getGeometryBoundingBox(geometry),
27404
+ label: this.getDisplayLabel(result),
27405
+ });
27406
+ }
27958
27407
  }
27959
27408
  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 }); }
27409
+ 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
27410
  }
27962
27411
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LocationSearchComponent, decorators: [{
27963
27412
  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" }]
27413
+ 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
27414
  }], propDecorators: { placeholder: [{
27966
27415
  type: Input
27967
- }], displayWithTemplate: [{
27968
- type: Input
27969
27416
  }], resultSelected: [{
27970
27417
  type: Output
27418
+ }], bboxSelected: [{
27419
+ type: Output
27971
27420
  }] } });
27972
27421
 
27973
27422
  // expects the replacement key ${uuid}
@@ -28128,6 +27577,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
28128
27577
  class AbstractSearchField {
28129
27578
  }
28130
27579
  class SimpleSearchField {
27580
+ get fieldIdentifier() {
27581
+ return this.customIdentifier ?? this.esFieldName;
27582
+ }
28131
27583
  constructor(esFieldName, injector, order = 'asc', orderType = 'key') {
28132
27584
  this.esFieldName = esFieldName;
28133
27585
  this.injector = injector;
@@ -28136,14 +27588,26 @@ class SimpleSearchField {
28136
27588
  this.repository = this.injector.get(RecordsRepositoryInterface);
28137
27589
  // FIXME: this is required to register runtime fields; abstract this as well
28138
27590
  this.esService = this.injector.get(ElasticsearchService);
27591
+ this.includeValues = [];
27592
+ this.excludeValues = [];
27593
+ }
27594
+ setFieldIdentifier(fieldIdentifier) {
27595
+ this.customIdentifier = fieldIdentifier;
27596
+ this.esService.registerFieldAlias(fieldIdentifier, this.esFieldName);
28139
27597
  }
28140
27598
  getAggregations() {
28141
27599
  return {
28142
- [this.esFieldName]: {
27600
+ [this.fieldIdentifier]: {
28143
27601
  type: 'terms',
28144
27602
  field: this.esFieldName,
28145
27603
  limit: 1000,
28146
27604
  sort: [this.order, this.orderType],
27605
+ ...(this.includeValues?.length && {
27606
+ includeValues: this.includeValues,
27607
+ }),
27608
+ ...(this.excludeValues?.length && {
27609
+ excludeValues: this.excludeValues,
27610
+ }),
28147
27611
  },
28148
27612
  };
28149
27613
  }
@@ -28151,7 +27615,7 @@ class SimpleSearchField {
28151
27615
  return bucket.term.toString();
28152
27616
  }
28153
27617
  getAvailableValues() {
28154
- return this.repository.aggregate(this.getAggregations()).pipe(map$1((response) => response[this.esFieldName].buckets || []), switchMap((buckets) => {
27618
+ return this.repository.aggregate(this.getAggregations()).pipe(map$1((response) => response[this.fieldIdentifier].buckets || []), switchMap((buckets) => {
28155
27619
  const bucketPromises = buckets.map(async (bucket) => ({
28156
27620
  label: `${await this.getBucketLabel(bucket)} (${bucket.count})`,
28157
27621
  value: bucket.term.toString(),
@@ -28164,7 +27628,7 @@ class SimpleSearchField {
28164
27628
  // FieldValue[]
28165
27629
  if (this.getType() === 'values') {
28166
27630
  return of({
28167
- [this.esFieldName]: values.reduce((acc, val) => {
27631
+ [this.fieldIdentifier]: values.reduce((acc, val) => {
28168
27632
  const value = val.toString();
28169
27633
  if (value !== '') {
28170
27634
  return { ...acc, [value]: true };
@@ -28175,11 +27639,11 @@ class SimpleSearchField {
28175
27639
  }
28176
27640
  // DateRange
28177
27641
  return of({
28178
- [this.esFieldName]: values[0] !== '' ? values[0] : {},
27642
+ [this.fieldIdentifier]: values[0] !== '' ? values[0] : {},
28179
27643
  });
28180
27644
  }
28181
27645
  getValuesForFilter(filters) {
28182
- const filter = filters[this.esFieldName];
27646
+ const filter = filters[this.fieldIdentifier];
28183
27647
  if (!filter)
28184
27648
  return of([]);
28185
27649
  // filter by expression
@@ -28197,6 +27661,9 @@ class SimpleSearchField {
28197
27661
  getType() {
28198
27662
  return 'values';
28199
27663
  }
27664
+ clone() {
27665
+ return Object.create(this);
27666
+ }
28200
27667
  }
28201
27668
  class TranslatedSearchField extends SimpleSearchField {
28202
27669
  constructor(esFieldName, injector, order = 'asc', orderType = 'key') {
@@ -28466,11 +27933,11 @@ class BoundingBoxSearchField extends SimpleSearchField {
28466
27933
  }
28467
27934
  getFiltersForValues(values) {
28468
27935
  return of({
28469
- [this.esFieldName]: values.map(Number),
27936
+ [this.fieldIdentifier]: values.map(Number),
28470
27937
  });
28471
27938
  }
28472
27939
  getValuesForFilter(filters) {
28473
- const filter = filters[this.esFieldName];
27940
+ const filter = filters[this.fieldIdentifier];
28474
27941
  return of(isBoundingBox(filter) ? filter : []);
28475
27942
  }
28476
27943
  getType() {
@@ -28569,7 +28036,7 @@ class RecordKindField extends SimpleSearchField {
28569
28036
  }
28570
28037
  getFiltersForValues(values) {
28571
28038
  const filters = {
28572
- [this.esFieldName]: values.reduce((acc, value) => {
28039
+ [this.fieldIdentifier]: values.reduce((acc, value) => {
28573
28040
  if (value === '')
28574
28041
  return { ...acc, [value]: true };
28575
28042
  const keysToAdd = this.TYPE_MAPPING[value] || [value];
@@ -28592,7 +28059,7 @@ class RecordKindField extends SimpleSearchField {
28592
28059
  return of(filters);
28593
28060
  }
28594
28061
  getValuesForFilter(filters) {
28595
- const filter = filters[this.esFieldName];
28062
+ const filter = filters[this.fieldIdentifier];
28596
28063
  if (!filter)
28597
28064
  return of([]);
28598
28065
  const activeValues = Object.keys(filter).filter((v) => filter[v]);
@@ -28622,11 +28089,15 @@ marker('search.filters.publisherOrg');
28622
28089
  marker('search.filters.user');
28623
28090
  marker('search.filters.changeDate');
28624
28091
  marker('search.filters.resourceCreationRevisionDate');
28092
+ marker('search.filters.temporalExtent');
28625
28093
  marker('search.filters.spatialExtent');
28094
+ marker('search.filters.documentStandard');
28095
+ const CUSTOM_FIELDS = new InjectionToken('custom-fields');
28626
28096
  class FieldsService {
28627
28097
  constructor() {
28628
28098
  this.injector = inject(Injector);
28629
- this.fields = {
28099
+ this.customFields = inject(CUSTOM_FIELDS, { optional: true }) ?? [];
28100
+ this.baseFields = {
28630
28101
  organization: new OrganizationSearchField(this.injector),
28631
28102
  format: new SimpleSearchField('format', this.injector, 'asc'),
28632
28103
  resourceType: new ResourceTypeLegacyField(this.injector), // Deprecated, use `recordKind` instead
@@ -28646,9 +28117,31 @@ class FieldsService {
28646
28117
  user: new UserSearchField(this.injector),
28647
28118
  changeDate: new DateRangeSearchField('changeDate', this.injector, 'desc'),
28648
28119
  resourceCreationRevisionDate: new ResourceCreationRevisionDateSearchField(this.injector, 'desc'),
28120
+ temporalExtent: new DateRangeSearchField('resourceTemporalExtentDateRange', this.injector, 'desc'),
28649
28121
  availableServices: new AvailableServicesField(this.injector),
28650
28122
  spatialExtent: new BoundingBoxSearchField('spatialExtent', this.injector),
28651
28123
  };
28124
+ this.fields = { ...this.baseFields };
28125
+ for (const customField of this.customFields) {
28126
+ const baseField = this.baseFields[customField.baseFilter];
28127
+ if (!(baseField instanceof SimpleSearchField)) {
28128
+ console.warn(`The custom field '${customField.name}' relies on a base field '${customField.baseFilter}' that is not supported. This field will be ignored.`);
28129
+ continue;
28130
+ }
28131
+ if (!customField.name.includes(':')) {
28132
+ 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.`);
28133
+ continue;
28134
+ }
28135
+ const newField = baseField.clone();
28136
+ newField.setFieldIdentifier(customField.name);
28137
+ if (customField.includeValues) {
28138
+ newField.includeValues = customField.includeValues;
28139
+ }
28140
+ if (customField.excludeValues) {
28141
+ newField.excludeValues = customField.excludeValues;
28142
+ }
28143
+ this.fields[customField.name] = newField;
28144
+ }
28652
28145
  }
28653
28146
  get supportedFields() {
28654
28147
  return Object.keys(this.fields);
@@ -28696,7 +28189,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
28696
28189
  args: [{
28697
28190
  providedIn: 'root',
28698
28191
  }]
28699
- }] });
28192
+ }], ctorParameters: () => [] });
28700
28193
 
28701
28194
  class ApiCardComponent {
28702
28195
  constructor() {
@@ -30722,14 +30215,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
30722
30215
  marker('feature.catalog.attribute.type');
30723
30216
  marker('feature.catalog.attribute.name');
30724
30217
  marker('feature.catalog.attribute.code');
30725
- marker('feature.catalog.attribute.definition');
30218
+ marker('feature.catalog.attribute.description');
30726
30219
  marker('feature.catalog.attribute.values');
30727
30220
  class FeatureCatalogListComponent {
30728
30221
  constructor() {
30729
30222
  this.COLUMNS_DEFAULT = [
30730
30223
  { key: 'type', width: '25%' },
30731
30224
  { key: 'name', width: '40%' },
30732
- { key: 'definition', width: 'minmax(0px, 1fr)' },
30225
+ { key: 'description', width: 'minmax(0px, 1fr)' },
30733
30226
  ];
30734
30227
  this.COLUMN_VALUES = {
30735
30228
  key: 'values',
@@ -30750,7 +30243,7 @@ class FeatureCatalogListComponent {
30750
30243
  return col.key;
30751
30244
  }
30752
30245
  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" }] }); }
30246
+ 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.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" }] }); }
30754
30247
  }
30755
30248
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FeatureCatalogListComponent, decorators: [{
30756
30249
  type: Component,
@@ -30763,7 +30256,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
30763
30256
  TruncatedTextComponent,
30764
30257
  NgIconComponent,
30765
30258
  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" }]
30259
+ ], 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.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" }]
30767
30260
  }], propDecorators: { filteredFeatureCatalog: [{
30768
30261
  type: Input
30769
30262
  }], scrollContainer: [{
@@ -31901,6 +31394,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
31901
31394
  ], 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
31395
  }] });
31903
31396
 
31397
+ marker('search.filters.spatialExtent.searchLocation');
31398
+ // in MB, null means no limit
31399
+ const SPATIAL_EXTENT_MAX_FILE_SIZE = new InjectionToken('spatialExtentMaxFileSize', { factory: () => null });
31904
31400
  class FilterDropdownComponent {
31905
31401
  constructor() {
31906
31402
  this.searchFacade = inject(SearchFacade);
@@ -31908,7 +31404,7 @@ class FilterDropdownComponent {
31908
31404
  this.fieldsService = inject(FieldsService);
31909
31405
  this.notificationsService = inject(NotificationsService);
31910
31406
  this.translateService = inject(TranslateService);
31911
- this.spatialExtentMaxFileSize = getOptionalSearchConfig()?.SPATIAL_EXTENT_MAX_FILE_SIZE;
31407
+ this.spatialExtentMaxFileSize = inject(SPATIAL_EXTENT_MAX_FILE_SIZE);
31912
31408
  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
31409
  this.selectedDateRange$ = this.selected$.pipe(map$1((selected) => (Array.isArray(selected) ? {} : selected)));
31914
31410
  this.selectedBoundingBox$ = this.selected$.pipe(map$1((selected) => Array.isArray(selected) && selected.length > 0
@@ -31929,6 +31425,9 @@ class FilterDropdownComponent {
31929
31425
  .subscribe((filters) => this.searchService.updateFilters(filters));
31930
31426
  this.clearSpatialExtentErrorNotification();
31931
31427
  }
31428
+ onLocationSelected({ bbox, label }) {
31429
+ this.spatialExtentDropdown.setLocationBbox(bbox, label);
31430
+ }
31932
31431
  onSpatialExtentError(error) {
31933
31432
  this.clearSpatialExtentErrorNotification();
31934
31433
  this.spatialExtentErrorNotificationId =
@@ -31959,7 +31458,7 @@ class FilterDropdownComponent {
31959
31458
  .subscribe((filters) => this.searchService.updateFilters(filters));
31960
31459
  }
31961
31460
  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 }); }
31461
+ 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
31462
  }
31964
31463
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, decorators: [{
31965
31464
  type: Component,
@@ -31968,11 +31467,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
31968
31467
  DateRangeDropdownComponent,
31969
31468
  DropdownMultiselectComponent,
31970
31469
  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" }]
31470
+ LocationSearchComponent,
31471
+ TranslatePipe,
31472
+ ], 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
31473
  }], propDecorators: { fieldName: [{
31973
31474
  type: Input
31974
31475
  }], title: [{
31975
31476
  type: Input
31477
+ }], spatialExtentDropdown: [{
31478
+ type: ViewChild,
31479
+ args: [SpatialExtentDropdownComponent]
31976
31480
  }] } });
31977
31481
 
31978
31482
  class FuzzySearchComponent {
@@ -32589,6 +32093,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
32589
32093
  marker('search.filters.summaryLabel.user');
32590
32094
  marker('search.filters.summaryLabel.changeDate');
32591
32095
  marker('search.filters.summaryLabel.resourceCreationRevisionDate');
32096
+ marker('search.filters.summaryLabel.temporalExtent');
32592
32097
  const OPEN_BOUND = '…';
32593
32098
  class SearchFiltersSummaryItemComponent {
32594
32099
  constructor() {
@@ -34152,7 +33657,7 @@ class DataService {
34152
33657
  }));
34153
33658
  }
34154
33659
  async getDownloadLinksFromOgcApiFeatures(ogcApiLink) {
34155
- const collectionInfo = await this.getDownloadUrlsFromOgcApi(ogcApiLink.url.href);
33660
+ const collectionInfo = await this.getDownloadUrlsFromOgcApi(ogcApiLink.url.href, ogcApiLink.name);
34156
33661
  return Object.keys(collectionInfo.bulkDownloadLinks).map((mimeType) => {
34157
33662
  const urlWithoutLimit = new URL(collectionInfo.bulkDownloadLinks[mimeType]);
34158
33663
  urlWithoutLimit.searchParams.delete('limit');
@@ -34165,27 +33670,31 @@ class DataService {
34165
33670
  };
34166
33671
  });
34167
33672
  }
34168
- async getDownloadUrlsFromOgcApi(url) {
33673
+ async getTargetCollectionId(endpoint, collectionId) {
33674
+ const collections = await endpoint.featureCollections;
33675
+ return collections.length === 1 && !collectionId
33676
+ ? collections[0]
33677
+ : collectionId;
33678
+ }
33679
+ async getDownloadUrlsFromOgcApi(url, collectionId) {
34169
33680
  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
- });
33681
+ try {
33682
+ const id = await this.getTargetCollectionId(endpoint, collectionId);
33683
+ return await endpoint.getCollectionInfo(id);
33684
+ }
33685
+ catch {
33686
+ throw new Error('ogc.unreachable.unknown');
33687
+ }
34177
33688
  }
34178
- async getItemsFromOgcApi(url, limit) {
33689
+ async getItemsFromOgcApi(url, collectionId, limit) {
34179
33690
  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
- });
33691
+ try {
33692
+ const id = await this.getTargetCollectionId(endpoint, collectionId);
33693
+ return await endpoint.getCollectionItems(id, limit);
33694
+ }
33695
+ catch {
33696
+ throw new Error('ogc.unreachable.unknown');
33697
+ }
34189
33698
  }
34190
33699
  async getItemsFromStacApi(url, options) {
34191
33700
  return await StacEndpoint.getItemsFromUrl(url, options)
@@ -34273,7 +33782,7 @@ class DataService {
34273
33782
  }
34274
33783
  else if (link.type === 'service' &&
34275
33784
  link.accessServiceProtocol === 'ogcFeatures') {
34276
- return from(this.getDownloadUrlsFromOgcApi(link.url.href)).pipe(switchMap$1((collectionInfo) => {
33785
+ return from(this.getDownloadUrlsFromOgcApi(link.url.href, link.name)).pipe(switchMap$1((collectionInfo) => {
34277
33786
  const isMimeTypeJson = (mimeType) => {
34278
33787
  return mimeType.toLowerCase().indexOf('json') > -1;
34279
33788
  };
@@ -35217,7 +34726,7 @@ class MdViewFacade {
35217
34726
  if (this.linkClassifier.hasUsage(link, LinkUsage.GEODATA)) {
35218
34727
  if (link.type === 'service' &&
35219
34728
  link.accessServiceProtocol === 'ogcFeatures') {
35220
- return from(this.dataService.getItemsFromOgcApi(link.url.href, 1)).pipe(map$1((collectionRecords) => {
34729
+ return from(this.dataService.getItemsFromOgcApi(link.url.href, link.name, 1)).pipe(map$1((collectionRecords) => {
35221
34730
  return collectionRecords &&
35222
34731
  collectionRecords[0] &&
35223
34732
  collectionRecords[0].geometry
@@ -35300,7 +34809,7 @@ class MdViewEffects {
35300
34809
  }
35301
34810
  return loadFullMetadataSuccess({ full: record });
35302
34811
  }), 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({
34812
+ 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
34813
  datasetCatalog: featureCatalog,
35305
34814
  })), catchError((error) => of(loadFeatureCatalogFailure({
35306
34815
  error: error.message,
@@ -40934,6 +40443,638 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
40934
40443
  args: ['gnUiSearchRouterContainer']
40935
40444
  }] } });
40936
40445
 
40446
+ const flatten = (base, obj, keepLevel = false) => Object.keys(obj).reduce((prev, curr) => {
40447
+ const path = base ? `${base}.${curr}` : curr;
40448
+ const val = obj[curr];
40449
+ if (keepLevel) {
40450
+ return { ...prev, [path]: flatten('', val) };
40451
+ }
40452
+ else if (typeof val === 'object' && !Array.isArray(val)) {
40453
+ return {
40454
+ ...prev,
40455
+ ...flatten(path, val),
40456
+ };
40457
+ }
40458
+ else {
40459
+ return { ...prev, [path]: val };
40460
+ }
40461
+ }, {});
40462
+ const checkKeys = (input, mandatory, optional) => {
40463
+ const keys = Object.keys(input);
40464
+ const missing = mandatory.filter((key) => keys.indexOf(key) === -1);
40465
+ const unrecognized = keys.filter((key) => mandatory.indexOf(key) === -1 && optional.indexOf(key) === -1);
40466
+ return {
40467
+ missing,
40468
+ unrecognized,
40469
+ };
40470
+ };
40471
+ /**
40472
+ * Only keeps recognized keys, flatten nested values (to allow having "." in keys),
40473
+ * pushes warnings and errors to the given arrays; returns null in case of error
40474
+ */
40475
+ function parseConfigSection(fullConfigObj, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors) {
40476
+ if (typeof fullConfigObj[sectionName] !== 'object') {
40477
+ if (mandatoryKeys.length === 0)
40478
+ return null;
40479
+ outErrors.push(`The [${sectionName}] mandatory section is missing.`);
40480
+ return null;
40481
+ }
40482
+ const sectionConf = fullConfigObj[sectionName];
40483
+ const keysCheck = checkKeys(sectionConf, mandatoryKeys, optionalKeys);
40484
+ if (keysCheck.missing.length) {
40485
+ // note: this is not thrown to allow merging several Errors down the line
40486
+ outErrors.push(`In the [${sectionName}] section: ${keysCheck.missing.join(', ')}`);
40487
+ return null;
40488
+ }
40489
+ else if (keysCheck.unrecognized.length) {
40490
+ outWarnings.push(`In the [${sectionName}] section: ${keysCheck.unrecognized.join(', ')}`);
40491
+ keysCheck.unrecognized.forEach((key) => delete sectionConf[key]);
40492
+ }
40493
+ return sectionConf;
40494
+ }
40495
+ /**
40496
+ * Same as parseConfigSection but keeps all keys, recognized or not
40497
+ * Also the first level is the language code (e.g. 'en')
40498
+ * This is optional
40499
+ */
40500
+ function parseTranslationsConfigSection(fullConfigObj, sectionName) {
40501
+ if (typeof fullConfigObj[sectionName] !== 'object') {
40502
+ return {};
40503
+ }
40504
+ return flatten('', fullConfigObj[sectionName], true);
40505
+ }
40506
+ /**
40507
+ * Same as parseConfigSection but handles an array of configs; this is
40508
+ * considered optional; returns null in case of error in at least one of
40509
+ * the sections
40510
+ */
40511
+ function parseMultiConfigSection(fullConfigObj, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors) {
40512
+ const sections = fullConfigObj[sectionName];
40513
+ if (!sections) {
40514
+ return [];
40515
+ }
40516
+ if (!Array.isArray(sections)) {
40517
+ outErrors.push(`Expected [[${sectionName}]] to be an array of sections.`);
40518
+ return null;
40519
+ }
40520
+ const result = sections.map((section) => parseConfigSection({ [sectionName]: section }, sectionName, mandatoryKeys, optionalKeys, outWarnings, outErrors));
40521
+ if (result.some((r) => r === null)) {
40522
+ return null;
40523
+ }
40524
+ return result;
40525
+ }
40526
+ function checkMetadataLanguage(parsedConfigSection, outWarnings) {
40527
+ const lang2 = toLang2(parsedConfigSection.metadata_language.toLowerCase());
40528
+ if (lang2?.length !== 2) {
40529
+ outWarnings.push(`In the [global] section: metadata_language = "${parsedConfigSection.metadata_language}" is not a recognized ISO 639 language code`);
40530
+ }
40531
+ return parsedConfigSection;
40532
+ }
40533
+ function checkNewRecordDefaultLanguage(parsedConfigSection, outWarnings) {
40534
+ const lang2 = toLang2(parsedConfigSection.new_record_default_language.toLowerCase());
40535
+ if (!(lang2 in LANG_2_TO_3_MAPPER)) {
40536
+ outWarnings.push(`In the [editing] section: new_record_default_language = "${parsedConfigSection.new_record_default_language}" is not a recognized ISO 639 language code`);
40537
+ return {
40538
+ ...parsedConfigSection,
40539
+ new_record_default_language: undefined,
40540
+ };
40541
+ }
40542
+ return {
40543
+ ...parsedConfigSection,
40544
+ new_record_default_language: lang2,
40545
+ };
40546
+ }
40547
+ function checkNewRecordStandard(parsedConfigSection, outWarnings) {
40548
+ const standard = parsedConfigSection.new_record_standard;
40549
+ const normalizedStandard = typeof standard === 'string' ? standard.trim().toLowerCase() : null;
40550
+ if (normalizedStandard === 'iso19139' ||
40551
+ normalizedStandard === 'iso19115-3') {
40552
+ return {
40553
+ ...parsedConfigSection,
40554
+ new_record_standard: normalizedStandard,
40555
+ };
40556
+ }
40557
+ outWarnings.push(`In the [editing] section: new_record_standard = "${standard}" is not a supported metadata standard`);
40558
+ return {
40559
+ ...parsedConfigSection,
40560
+ new_record_standard: undefined,
40561
+ };
40562
+ }
40563
+
40564
+ /**
40565
+ * This loader extends the default one based on JSON files but also loads custom translations
40566
+ * defined in the app configuration
40567
+ * Implements a fallback on default lang if translated labels are empty
40568
+ */
40569
+ class FileWithOverridesTranslateLoader extends FileTranslateLoader {
40570
+ getTranslation(lang) {
40571
+ const baseLang = lang.substring(0, 2); // removing the right part of e.g. en_EN
40572
+ return super.getTranslation(baseLang).pipe(map$1((translations) => ({
40573
+ ...translations,
40574
+ ...getCustomTranslations(baseLang),
40575
+ })));
40576
+ }
40577
+ }
40578
+
40579
+ const MISSING_CONFIG_ERROR = `Application configuration was not initialized correctly.
40580
+ This error might show up in case of an invalid/malformed configuration file.
40581
+
40582
+ Note: make sure that you have called \`loadAppConfig\` from '../../../../../libs/util/app-config/src' before starting the Angular application.`;
40583
+ let globalConfig = null;
40584
+ function getGlobalConfig() {
40585
+ if (globalConfig === null)
40586
+ throw new Error(MISSING_CONFIG_ERROR);
40587
+ return globalConfig;
40588
+ }
40589
+ let themeConfig = null;
40590
+ function getThemeConfig() {
40591
+ if (themeConfig === null)
40592
+ throw new Error(MISSING_CONFIG_ERROR);
40593
+ return themeConfig;
40594
+ }
40595
+ let mapConfig = null;
40596
+ function getOptionalMapConfig() {
40597
+ return mapConfig;
40598
+ }
40599
+ let searchConfig = null;
40600
+ function getOptionalSearchConfig() {
40601
+ return searchConfig;
40602
+ }
40603
+ let editorConfig = null;
40604
+ function getOptionalEditorConfig() {
40605
+ return editorConfig;
40606
+ }
40607
+ let metadataQualityConfig = null;
40608
+ function getMetadataQualityConfig() {
40609
+ return (metadataQualityConfig ||
40610
+ {
40611
+ ENABLED: false,
40612
+ });
40613
+ }
40614
+ let customTranslations = null;
40615
+ function getCustomTranslations(langCode) {
40616
+ if (customTranslations === null)
40617
+ throw new Error(MISSING_CONFIG_ERROR);
40618
+ return langCode in customTranslations ? customTranslations[langCode] : {};
40619
+ }
40620
+ let appConfigLoaded = false;
40621
+ function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
40622
+ console.log(`[geonetwork-ui] Loading application configuration from ${configUrl}`);
40623
+ return fetch(configUrl)
40624
+ .then((resp) => {
40625
+ if (!resp.ok)
40626
+ throw new Error('Configuration file could not be loaded');
40627
+ return resp.text();
40628
+ })
40629
+ .then((conf) => {
40630
+ let parsed;
40631
+ try {
40632
+ parsed = TOML.parse(conf, { joiner: '\n', bigint: false });
40633
+ }
40634
+ catch (e) {
40635
+ throw new Error(`An error occurred when parsing the configuration file: ${e.message}`);
40636
+ }
40637
+ const errors = [];
40638
+ const warnings = [];
40639
+ let parsedGlobalSection = parseConfigSection(parsed, 'global', ['geonetwork4_api_url'], [
40640
+ 'datahub_url',
40641
+ 'edit_url_template',
40642
+ 'proxy_path',
40643
+ 'metadata_language',
40644
+ 'login_url',
40645
+ 'logout_url',
40646
+ 'settings_url',
40647
+ 'web_component_embedder_url',
40648
+ 'languages',
40649
+ 'contact_email',
40650
+ 'reuse_form_url',
40651
+ ], warnings, errors);
40652
+ if (parsedGlobalSection?.metadata_language) {
40653
+ parsedGlobalSection = checkMetadataLanguage(parsedGlobalSection, warnings);
40654
+ }
40655
+ globalConfig =
40656
+ parsedGlobalSection === null
40657
+ ? null
40658
+ : {
40659
+ GN4_API_URL: parsedGlobalSection.geonetwork4_api_url,
40660
+ DATAHUB_URL: parsedGlobalSection.datahub_url,
40661
+ EDIT_URL_TEMPLATE: parsedGlobalSection.edit_url_template,
40662
+ PROXY_PATH: parsedGlobalSection.proxy_path,
40663
+ METADATA_LANGUAGE: parsedGlobalSection.metadata_language
40664
+ ? parsedGlobalSection.metadata_language.toLowerCase()
40665
+ : undefined,
40666
+ LOGIN_URL: parsedGlobalSection.login_url,
40667
+ LOGOUT_URL: parsedGlobalSection.logout_url,
40668
+ SETTINGS_URL: parsedGlobalSection.settings_url,
40669
+ WEB_COMPONENT_EMBEDDER_URL: parsedGlobalSection.web_component_embedder_url,
40670
+ LANGUAGES: parsedGlobalSection.languages,
40671
+ CONTACT_EMAIL: parsedGlobalSection.contact_email,
40672
+ REUSE_FORM_URL: parsedGlobalSection.reuse_form_url,
40673
+ };
40674
+ const parsedLayersSections = parseMultiConfigSection(parsed, 'map_layer', ['type'], ['name', 'url', 'data', 'styleUrl', 'accessToken'], warnings, errors);
40675
+ const parsedMapSection = parseConfigSection(parsed, 'map', [], [
40676
+ 'max_zoom',
40677
+ 'do_not_tile_wms',
40678
+ 'max_extent',
40679
+ 'max_feature_count',
40680
+ 'baselayer',
40681
+ 'do_not_use_default_basemap',
40682
+ 'external_viewer_url_template',
40683
+ 'external_viewer_open_new_tab',
40684
+ ], warnings, errors);
40685
+ mapConfig =
40686
+ parsedMapSection === null || parsedLayersSections === null
40687
+ ? null
40688
+ : {
40689
+ MAX_ZOOM: parsedMapSection.max_zoom,
40690
+ DO_NOT_TILE_WMS: parsedMapSection.do_not_tile_wms,
40691
+ MAX_EXTENT: parsedMapSection.max_extent,
40692
+ MAX_FEATURE_COUNT: parsedMapSection.max_feature_count,
40693
+ EXTERNAL_VIEWER_URL_TEMPLATE: parsedMapSection.external_viewer_url_template,
40694
+ EXTERNAL_VIEWER_OPEN_NEW_TAB: parsedMapSection.external_viewer_open_new_tab,
40695
+ DO_NOT_USE_DEFAULT_BASEMAP: !!parsedMapSection.do_not_use_default_basemap,
40696
+ MAP_LAYERS: parsedLayersSections.map((map_layer) => ({
40697
+ TYPE: map_layer.type,
40698
+ URL: map_layer.url,
40699
+ NAME: map_layer.name,
40700
+ DATA: map_layer.data,
40701
+ STYLE_URL: map_layer.styleUrl,
40702
+ ACCESS_TOKEN: map_layer.accessToken,
40703
+ })),
40704
+ };
40705
+ const parsedThemeSection = parseConfigSection(parsed, 'theme', ['primary_color', 'secondary_color', 'main_color', 'background_color'], [
40706
+ 'header_foreground_color',
40707
+ 'main_font',
40708
+ 'title_font',
40709
+ 'fonts_stylesheet_url',
40710
+ 'thumbnail_placeholder',
40711
+ 'header_background',
40712
+ 'favicon',
40713
+ ], warnings, errors);
40714
+ themeConfig =
40715
+ parsedThemeSection === null
40716
+ ? null
40717
+ : {
40718
+ PRIMARY_COLOR: parsedThemeSection.primary_color,
40719
+ SECONDARY_COLOR: parsedThemeSection.secondary_color,
40720
+ MAIN_COLOR: parsedThemeSection.main_color,
40721
+ BACKGROUND_COLOR: parsedThemeSection.background_color,
40722
+ HEADER_FOREGROUND_COLOR: parsedThemeSection.header_foreground_color,
40723
+ THUMBNAIL_PLACEHOLDER: parsedThemeSection.thumbnail_placeholder,
40724
+ HEADER_BACKGROUND: parsedThemeSection.header_background,
40725
+ TITLE_FONT: parsedThemeSection.title_font,
40726
+ MAIN_FONT: parsedThemeSection.main_font,
40727
+ FONTS_STYLESHEET_URL: parsedThemeSection.fonts_stylesheet_url,
40728
+ FAVICON: parsedThemeSection.favicon,
40729
+ };
40730
+ const parsedSearchSection = parseConfigSection(parsed, 'search', [], [
40731
+ 'record_kind_quick_filter',
40732
+ 'filter_geometry_data',
40733
+ 'filter_geometry_url',
40734
+ 'do_not_use_default_search_preset',
40735
+ 'search_preset',
40736
+ 'advanced_filters',
40737
+ 'limit',
40738
+ 'spatial_extent_max_file_size',
40739
+ 'geocoding_provider',
40740
+ 'geocoding_provider_options',
40741
+ 'geocoding_provider_labels',
40742
+ ], warnings, errors);
40743
+ const parsedSearchParams = parseMultiConfigSection(parsed, 'search_preset', ['name'], ['sort', 'filters'], warnings, errors);
40744
+ const parsedCustomFilters = parseMultiConfigSection(parsed, 'custom_filter', ['name', 'base_filter'], ['exclude_values', 'include_values', 'label_key'], warnings, errors);
40745
+ const parsedGeocodingProviderLabels = parsedSearchSection?.geocoding_provider_labels;
40746
+ searchConfig =
40747
+ parsedSearchSection === null
40748
+ ? null
40749
+ : {
40750
+ RECORD_KIND_QUICK_FILTER: parsedSearchSection.record_kind_quick_filter,
40751
+ FILTER_GEOMETRY_DATA: parsedSearchSection.filter_geometry_data,
40752
+ FILTER_GEOMETRY_URL: parsedSearchSection.filter_geometry_url,
40753
+ DO_NOT_USE_DEFAULT_SEARCH_PRESET: !!parsedSearchSection.do_not_use_default_search_preset,
40754
+ SEARCH_PRESET: parsedSearchParams.map((param) => ({
40755
+ sort: param.sort,
40756
+ name: param.name,
40757
+ filters: param.filters,
40758
+ })),
40759
+ ADVANCED_FILTERS: parsedSearchSection.advanced_filters,
40760
+ CUSTOM_FILTERS: (parsedCustomFilters ?? []).map((customFilter) => ({
40761
+ name: customFilter.name,
40762
+ baseFilter: customFilter.base_filter,
40763
+ excludeValues: customFilter.exclude_values,
40764
+ includeValues: customFilter.include_values,
40765
+ labelKey: customFilter.label_key,
40766
+ })),
40767
+ LIMIT: parsedSearchSection.limit,
40768
+ SPATIAL_EXTENT_MAX_FILE_SIZE: parsedSearchSection.spatial_extent_max_file_size,
40769
+ GEOCODING_PROVIDER: parsedSearchSection.geocoding_provider,
40770
+ GEOCODING_PROVIDER_OPTIONS: parsedSearchSection.geocoding_provider_options,
40771
+ GEOCODING_PROVIDER_LABELS: parsedGeocodingProviderLabels
40772
+ ? {
40773
+ MAIN: parsedGeocodingProviderLabels.main,
40774
+ SECONDARY: parsedGeocodingProviderLabels.secondary,
40775
+ TERTIARY: parsedGeocodingProviderLabels.tertiary,
40776
+ }
40777
+ : undefined,
40778
+ };
40779
+ const parsedMetadataQualitySection = parseConfigSection(parsed, 'metadata-quality', [], ['enabled'], warnings, errors);
40780
+ metadataQualityConfig =
40781
+ parsedMetadataQualitySection === null
40782
+ ? null
40783
+ : {
40784
+ ENABLED: parsedMetadataQualitySection.enabled,
40785
+ SORTABLE: parsedMetadataQualitySection.sortable,
40786
+ };
40787
+ let parsedEditingSection = parseConfigSection(parsed, 'editing', [], ['new_record_default_language', 'new_record_standard'], warnings, errors);
40788
+ if (parsedEditingSection !== null &&
40789
+ parsedEditingSection.new_record_default_language !== undefined) {
40790
+ parsedEditingSection = checkNewRecordDefaultLanguage(parsedEditingSection, warnings);
40791
+ }
40792
+ if (parsedEditingSection !== null &&
40793
+ parsedEditingSection.new_record_standard !== undefined) {
40794
+ parsedEditingSection = checkNewRecordStandard(parsedEditingSection, warnings);
40795
+ }
40796
+ editorConfig =
40797
+ parsedEditingSection === null
40798
+ ? null
40799
+ : {
40800
+ NEW_RECORD_DEFAULT_LANGUAGE: parsedEditingSection.new_record_default_language,
40801
+ NEW_RECORD_STANDARD: parsedEditingSection.new_record_standard,
40802
+ };
40803
+ customTranslations = parseTranslationsConfigSection(parsed, 'translations');
40804
+ if (errors.length) {
40805
+ throw new Error(`One or more mandatory settings were missing from the configuration file.
40806
+ ${errors.join('\n')}`);
40807
+ }
40808
+ else if (warnings.length) {
40809
+ console.warn(`One or more unexpected settings were encountered in the configuration file.
40810
+ ${warnings.join('\n')}`);
40811
+ }
40812
+ appConfigLoaded = true;
40813
+ });
40814
+ }
40815
+ function isConfigLoaded() {
40816
+ return appConfigLoaded;
40817
+ }
40818
+ function _reset() {
40819
+ globalConfig = null;
40820
+ themeConfig = null;
40821
+ editorConfig = null;
40822
+ customTranslations = null;
40823
+ }
40824
+ const TRANSLATE_WITH_OVERRIDES_CONFIG = {
40825
+ compiler: {
40826
+ provide: TranslateCompiler,
40827
+ useClass: TranslateMessageFormatCompiler,
40828
+ },
40829
+ loader: [
40830
+ {
40831
+ provide: TRANSLATE_HTTP_LOADER_CONFIG,
40832
+ useValue: {
40833
+ prefix: './assets/i18n/',
40834
+ suffix: '.json',
40835
+ },
40836
+ },
40837
+ {
40838
+ provide: TranslateLoader,
40839
+ useClass: FileWithOverridesTranslateLoader,
40840
+ deps: [HttpClient, TRANSLATE_HTTP_LOADER_CONFIG],
40841
+ },
40842
+ ],
40843
+ };
40844
+
40845
+ const okAppConfigFixture = () => `
40846
+ [global]
40847
+ geonetwork4_api_url = "/geonetwork/srv/api"
40848
+ proxy_path = "/proxy/?url="
40849
+ metadata_language = "fre"
40850
+ login_url = "/cas/login?service="
40851
+ logout_url = "/geonetwork/signout"
40852
+ settings_url = "/geonetwork/settings"
40853
+ web_component_embedder_url = "/datahub/wc-embedder.html"
40854
+
40855
+ [map]
40856
+ max_zoom = 10
40857
+ max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855]
40858
+ do_not_use_default_basemap = false
40859
+ external_viewer_url_template = 'https://example.com/myviewer?'
40860
+ external_viewer_open_new_tab = true
40861
+ [[map_layer]]
40862
+ type = "wms"
40863
+ url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
40864
+ name = "masque_hdf_ign_carto_latin1"
40865
+ [[map_layer]]
40866
+ type = "wfs"
40867
+ url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
40868
+ name = "masque_hdf_ign_carto_latin1"
40869
+
40870
+ [theme]
40871
+ primary_color = "#093564"
40872
+ secondary_color = "#c2e9dc"
40873
+ main_color = "#212029" # All-purpose text color
40874
+ background_color = "#fdfbff"
40875
+ thumbnail_placeholder = 'assets/img/placeholder.svg'
40876
+ header_background = 'teal'
40877
+ header_foreground_color = "#872e2e"
40878
+ main_font = 'sans-serif'
40879
+ title_font = 'serif'
40880
+ fonts_stylesheet_url = "https://fonts.googleapis.com/css2?family=Open+Sans"
40881
+
40882
+ [search]
40883
+ record_kind_quick_filter = false
40884
+ filter_geometry_url = 'https://my.domain.org/geom.json'
40885
+ do_not_use_default_search_preset = false
40886
+ geocoding_provider = "geoplateforme"
40887
+ geocoding_provider_options.index = "poi"
40888
+ geocoding_provider_options.category = "administratif"
40889
+ geocoding_provider_options.limit = 5
40890
+ geocoding_provider_labels.main = '/properties/name/0'
40891
+ geocoding_provider_labels.secondary = '/properties/citycode/0'
40892
+ geocoding_provider_labels.tertiary = '/properties/category/1'
40893
+ advanced_filters = ['publicationYear', 'documentStandard', 'inspireKeyword', 'topic', 'license', 'myOrg:firstCustomFilter', 'myOrg:secondCustomFilter']
40894
+
40895
+ [[custom_filter]]
40896
+ name = 'myOrg:firstCustomFilter'
40897
+ base_filter = 'keyword'
40898
+ exclude_values = ['my keyword 1', 'my keyword 2']
40899
+ label_key = 'myOrg.firstCustomFilter'
40900
+
40901
+ [[custom_filter]]
40902
+ name = 'myOrg:secondCustomFilter'
40903
+ base_filter = 'keyword'
40904
+ include_values = ['my keyword 3', 'my keyword 4']
40905
+ label_key = 'myOrg.secondCustomFilter'
40906
+
40907
+ [[search_preset]]
40908
+ sort = "-createDate"
40909
+ name = 'filterByOrgs'
40910
+ filters.q = 'Carto'
40911
+ filters.organisation = ['Org1', 'Org2']
40912
+ filters.format = ['ESRI Shapefile']
40913
+ filters.topic = ['boundaries']
40914
+ filters.publicationYear = ['2023', '2022']
40915
+ filters.spatial = ['yes']
40916
+
40917
+ [translations.en]
40918
+ "my.first.key" = 'First label.'
40919
+ "my.second.key" = """
40920
+ Second label,
40921
+ on two lines."""
40922
+
40923
+ [translations.de]
40924
+ my.first.key = 'Erste Etikett.'
40925
+
40926
+ [translations.fr]
40927
+ my.sample.text = "Un bon exemple de texte."
40928
+ "my.quoted.text" = 'du texte entre guillements.'
40929
+ `;
40930
+ const appConfigWithTranslationFixture = () => okAppConfigFixture();
40931
+ const minimalAppConfigFixture = () => `
40932
+ [global]
40933
+ geonetwork4_api_url = "/geonetwork/srv/api"
40934
+ proxy_path = "/proxy/?url="
40935
+
40936
+ [theme]
40937
+ primary_color = "#093564"
40938
+ secondary_color = "#c2e9dc"
40939
+ main_color = "#212029" # All-purpose text color
40940
+ background_color = "#fdfbff"
40941
+ `;
40942
+ const customFilterMissingMandatoryConfigFixture = () => `
40943
+ [global]
40944
+ geonetwork4_api_url = "/geonetwork/srv/api"
40945
+
40946
+ [theme]
40947
+ primary_color = "#093564"
40948
+ secondary_color = "#c2e9dc"
40949
+ main_color = "#212029" # All-purpose text color
40950
+ background_color = "#fdfbff"
40951
+
40952
+ [search]
40953
+ advanced_filters = ['myOrg:firstCustomFilter']
40954
+
40955
+ [[custom_filter]]
40956
+ name = 'myOrg:firstCustomFilter'
40957
+ `;
40958
+ const mapConfigFixture = () => ({
40959
+ MAX_ZOOM: 10,
40960
+ MAX_EXTENT: [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855],
40961
+ DO_NOT_USE_DEFAULT_BASEMAP: false,
40962
+ DO_NOT_TILE_WMS: false,
40963
+ 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}"}]}]',
40964
+ EXTERNAL_VIEWER_OPEN_NEW_TAB: true,
40965
+ MAP_LAYERS: [
40966
+ {
40967
+ TYPE: 'xyz',
40968
+ URL: 'https://some-basemap-server',
40969
+ },
40970
+ {
40971
+ TYPE: 'wms',
40972
+ URL: 'https://some-wms-server',
40973
+ NAME: 'some_layername',
40974
+ },
40975
+ {
40976
+ TYPE: 'wfs',
40977
+ URL: 'https://some-wfs-server',
40978
+ NAME: 'some_layername',
40979
+ },
40980
+ ],
40981
+ });
40982
+ const malformedConfigFixture = () => `
40983
+ {
40984
+ "I thought": "we were still doing json"
40985
+ }
40986
+ `;
40987
+ const missingMandatoryConfigFixture = () => `
40988
+ [theme]
40989
+ primary_color = "#093564"
40990
+ secondary_color = "#c2e9dc"
40991
+ background_color = "#fdfbff"
40992
+ main_font = 'sans-serif'
40993
+ `;
40994
+ const wrongLanguageCodeConfigFixture = () => `
40995
+ [global]
40996
+ geonetwork4_api_url = "/geonetwork/srv/api"
40997
+ proxy_path = "/proxy/?url="
40998
+ metadata_language = "abc"
40999
+
41000
+ [map]
41001
+
41002
+ [theme]
41003
+ primary_color = "#093564"
41004
+ secondary_color = "#c2e9dc"
41005
+ main_color = "#212029" # All-purpose text color
41006
+ background_color = "#fdfbff"
41007
+ `;
41008
+ const unrecognizedKeysConfigFixture = () => `
41009
+ [global]
41010
+ geonetwork4_api_url = "/geonetwork/srv/api"
41011
+ proxy_path = "/proxy/?url="
41012
+ metadata_language = "fre"
41013
+ another_path = '/whatever'
41014
+
41015
+ [map]
41016
+ max_zoom = 10
41017
+ max_extent = [-418263.418776, 5251529.591305, 961272.067714, 6706890.609855]
41018
+ do_not_use_default_basemap = false
41019
+ external_viewer_url_template = 'https://example.com/myviewer?'
41020
+ external_viewer_open_new_tab = true
41021
+ another_zoom = 15
41022
+ [[map_layer]]
41023
+ type = "wms"
41024
+ url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
41025
+ name = "masque_hdf_ign_carto_latin1"
41026
+ [[map_layer]]
41027
+ type = "wfs"
41028
+ url = "https://www.geo2france.fr/geoserver/cr_hdf/ows"
41029
+ name = "masque_hdf_ign_carto_latin1"
41030
+ another_layer = "wrong layer definition"
41031
+
41032
+ [theme]
41033
+ primary_color = "#093564"
41034
+ secondary_color = "#c2e9dc"
41035
+ main_color = "#212029" # All-purpose text color
41036
+ background_color = "#fdfbff"
41037
+ thumbnail_placeholder = 'assets/img/placeholder.svg'
41038
+ header_background = 'teal'
41039
+ header_foreground_color = "#872e2e"
41040
+ main_font = 'sans-serif'
41041
+ title_font = 'serif'
41042
+ another_color = 'red'
41043
+ `;
41044
+
41045
+ function getMapContextLayerFromConfig(config) {
41046
+ switch (config.TYPE) {
41047
+ case 'wms':
41048
+ return {
41049
+ type: 'wms',
41050
+ url: config.URL,
41051
+ name: config.NAME,
41052
+ };
41053
+ case 'wfs':
41054
+ return {
41055
+ type: 'wfs',
41056
+ url: config.URL,
41057
+ featureType: config.NAME,
41058
+ };
41059
+ case 'xyz':
41060
+ return {
41061
+ type: config.TYPE,
41062
+ url: config.URL,
41063
+ };
41064
+ case 'geojson':
41065
+ return {
41066
+ type: config.TYPE,
41067
+ ...(config.DATA ? { data: config.DATA } : { url: config.URL }),
41068
+ };
41069
+ case 'maplibre-style':
41070
+ return {
41071
+ type: config.TYPE,
41072
+ styleUrl: config.STYLE_URL,
41073
+ accessToken: config.ACCESS_TOKEN,
41074
+ };
41075
+ }
41076
+ }
41077
+
40937
41078
  const CHART_TYPE_VALUES = [
40938
41079
  'bar',
40939
41080
  'bar-horizontal',
@@ -40949,5 +41090,5 @@ const CHART_TYPE_VALUES = [
40949
41090
  * Generated bundle index. Do not edit.
40950
41091
  */
40951
41092
 
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 };
41093
+ 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
41094
  //# sourceMappingURL=geonetwork-ui.mjs.map