geonetwork-ui 2.10.0-dev.91673b3ba → 2.10.0-dev.981f59a75

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 (161) hide show
  1. package/fesm2022/geonetwork-ui.mjs +1536 -6196
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +257 -182
  4. package/index.d.ts.map +1 -1
  5. package/package.json +25 -35
  6. package/src/libs/api/metadata-converter/src/lib/dcat-ap/dcat-ap.converter.ts +9 -0
  7. package/src/libs/api/metadata-converter/src/lib/fixtures/eu.dcat-ap.records.ts +2 -0
  8. package/src/libs/api/metadata-converter/src/lib/fixtures/generic.records.ts +1 -0
  9. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+ongules.ts +7 -0
  10. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+roilaye.ts +1 -0
  11. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.ts +1 -0
  12. package/src/libs/api/metadata-converter/src/lib/fixtures/geocat-ch.records.ts +1 -0
  13. package/src/libs/api/metadata-converter/src/lib/fixtures/georhena.records.ts +1 -0
  14. package/src/libs/api/metadata-converter/src/lib/fixtures/metadata-for-i18n.records.ts +2 -0
  15. package/src/libs/api/metadata-converter/src/lib/fixtures/metawal.records.ts +1 -0
  16. package/src/libs/api/metadata-converter/src/lib/fixtures/opendataswiss.records.ts +1 -0
  17. package/src/libs/api/metadata-converter/src/lib/fixtures/sextant.records.ts +2 -0
  18. package/src/libs/api/metadata-converter/src/lib/fixtures/vlaanderen.dcat-ap.records.ts +1 -0
  19. package/src/libs/api/metadata-converter/src/lib/fixtures/wallonie.records.reuse.ts +8 -0
  20. package/src/libs/api/metadata-converter/src/lib/gn4/atomic-operations.ts +48 -7
  21. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.converter.ts +1 -0
  22. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +6 -5
  23. package/src/libs/api/metadata-converter/src/lib/gn4/metadata-url.service.ts +2 -2
  24. package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +1 -0
  25. package/src/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.ts +7 -0
  26. package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +8 -0
  27. package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +12 -0
  28. package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +14 -0
  29. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +33 -0
  30. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +36 -0
  31. package/src/libs/api/repository/src/lib/gn4/auth/auth.service.ts +8 -4
  32. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +2 -2
  33. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +59 -37
  34. package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-groups.service.ts +1 -1
  35. package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.ts +1 -1
  36. package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +37 -2
  37. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +13 -5
  38. package/src/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.ts +2 -1
  39. package/src/libs/common/domain/src/lib/model/user/group.model.ts +8 -0
  40. package/src/libs/common/domain/src/lib/model/user/index.ts +1 -0
  41. package/src/libs/common/domain/src/lib/platform.service.interface.ts +2 -0
  42. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +56 -0
  43. package/src/libs/feature/catalog/src/lib/sources/sources.service.ts +5 -2
  44. package/src/libs/feature/editor/src/lib/+state/editor.actions.ts +10 -1
  45. package/src/libs/feature/editor/src/lib/+state/editor.effects.ts +26 -19
  46. package/src/libs/feature/editor/src/lib/+state/editor.facade.ts +15 -2
  47. package/src/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.ts +0 -2
  48. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.css +0 -0
  49. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.html +68 -0
  50. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.ts +39 -0
  51. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.html +5 -14
  52. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.ts +3 -28
  53. package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.html +18 -3
  54. package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.ts +33 -40
  55. package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.html +5 -5
  56. package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.ts +2 -1
  57. package/src/libs/feature/editor/src/lib/components/record-form/form-field/field-focus.directive.ts +41 -0
  58. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-constraints-shortcuts/form-field-constraints-shortcuts.component.ts +33 -34
  59. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.html +9 -2
  60. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts +12 -0
  61. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.ts +1 -1
  62. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-single-link-resource/form-field-online-single-link-resource.component.css +0 -0
  63. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-single-link-resource/form-field-online-single-link-resource.component.html +5 -0
  64. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-single-link-resource/form-field-online-single-link-resource.component.ts +89 -0
  65. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts +12 -5
  66. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.html +4 -4
  67. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.ts +7 -0
  68. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-topics/form-field-topics.component.ts +2 -2
  69. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.html +6 -2
  70. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.ts +3 -1
  71. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.css +3 -0
  72. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +21 -16
  73. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +14 -0
  74. package/src/libs/feature/editor/src/lib/components/record-form/form-field/index.ts +1 -0
  75. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.css +35 -0
  76. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.html +18 -5
  77. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +118 -5
  78. package/src/libs/feature/editor/src/lib/expressions.ts +380 -8
  79. package/src/libs/feature/editor/src/lib/fields.config.ts +310 -336
  80. package/src/libs/feature/editor/src/lib/models/editor-config.model.ts +6 -3
  81. package/src/libs/feature/editor/src/lib/services/editor.service.ts +9 -13
  82. package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +33 -0
  83. package/src/libs/feature/notify-reuse/src/index.ts +1 -0
  84. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.css +0 -0
  85. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.html +111 -0
  86. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +229 -0
  87. package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +9 -1
  88. package/src/libs/feature/search/src/lib/results-table/results-table-container.component.ts +10 -5
  89. package/src/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.ts +2 -2
  90. package/src/libs/ui/dataviz/src/index.ts +0 -1
  91. package/src/libs/ui/elements/src/index.ts +2 -0
  92. package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.html +88 -0
  93. package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.ts +45 -0
  94. package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.html +29 -4
  95. package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.ts +51 -7
  96. package/src/libs/ui/elements/src/lib/external-link-card/external-link-card.component.html +2 -1
  97. package/src/libs/ui/elements/src/lib/external-link-card/external-link-card.component.ts +2 -2
  98. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.css +13 -0
  99. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.html +13 -3
  100. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.ts +43 -6
  101. package/src/libs/ui/elements/src/lib/keyword-badge/keyword-badge.component.html +14 -0
  102. package/src/libs/ui/elements/src/lib/keyword-badge/keyword-badge.component.ts +85 -0
  103. package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.ts +2 -2
  104. package/src/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.ts +2 -5
  105. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.css +5 -0
  106. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +7 -10
  107. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +2 -2
  108. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.html +4 -5
  109. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.ts +4 -0
  110. package/src/libs/ui/inputs/src/lib/badge/badge.component.html +33 -26
  111. package/src/libs/ui/inputs/src/lib/badge/badge.component.ts +9 -0
  112. package/src/libs/ui/inputs/src/lib/button/button.component.ts +4 -0
  113. package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +2 -2
  114. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
  115. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.ts +3 -3
  116. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.css +0 -3
  117. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.html +11 -7
  118. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.ts +56 -30
  119. package/src/libs/ui/inputs/src/lib/url-input/url-input.component.html +2 -2
  120. package/src/libs/ui/inputs/src/lib/url-input/url-input.component.ts +2 -1
  121. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.html +5 -5
  122. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.ts +9 -1
  123. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.html +1 -1
  124. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.ts +3 -8
  125. package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.html +7 -7
  126. package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.ts +14 -1
  127. package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +2 -1
  128. package/src/libs/ui/map/src/lib/components/spatial-extent/spatial-extent.component.ts +15 -64
  129. package/src/libs/ui/map/src/lib/map-utils.ts +48 -0
  130. package/src/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.ts +1 -1
  131. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html +12 -8
  132. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts +3 -1
  133. package/src/libs/ui/search/src/lib/results-table/results-table.component.html +1 -0
  134. package/src/libs/ui/widgets/src/lib/popover/popover.component.ts +7 -1
  135. package/src/libs/ui/widgets/src/lib/spinning-loader/spinning-loader.component.html +1 -1
  136. package/src/libs/util/app-config/src/lib/app-config.ts +70 -9
  137. package/src/libs/util/app-config/src/lib/model.ts +7 -0
  138. package/src/libs/util/app-config/src/lib/parse-utils.ts +50 -1
  139. package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +1 -2
  140. package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +6 -3
  141. package/src/libs/util/i18n/src/index.ts +0 -1
  142. package/src/libs/util/i18n/src/lib/i18n.constants.ts +17 -11
  143. package/src/libs/util/i18n/src/lib/i18n.interceptor.ts +2 -2
  144. package/src/libs/util/i18n/src/lib/i18n.providers.ts +14 -17
  145. package/src/libs/util/i18n/src/lib/test.translate.loader.ts +48 -0
  146. package/src/libs/util/shared/src/lib/links/link-utils.ts +23 -1
  147. package/src/libs/util/shared/src/lib/record/quality-score.util.ts +33 -18
  148. package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
  149. package/src/libs/util/shared/src/lib/utils/geojson.ts +58 -1
  150. package/src/libs/util/shared/src/lib/utils/user-display.ts +9 -0
  151. package/style.css +0 -1
  152. package/tailwind.base.css +20 -2
  153. package/translations/de.json +93 -48
  154. package/translations/en.json +98 -53
  155. package/translations/es.json +67 -22
  156. package/translations/fr.json +95 -50
  157. package/translations/it.json +94 -49
  158. package/translations/nl.json +66 -21
  159. package/translations/pt.json +67 -22
  160. package/translations/sk.json +67 -22
  161. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.css +0 -3
@@ -25,6 +25,7 @@ export class ButtonComponent {
25
25
  | 'outline'
26
26
  | 'light'
27
27
  | 'gray'
28
+ | 'gray-light'
28
29
  | 'black'
29
30
  | 'primary-light'
30
31
  ) {
@@ -45,6 +46,9 @@ export class ButtonComponent {
45
46
  case 'gray':
46
47
  this.btnClass = 'gn-ui-btn-gray'
47
48
  break
49
+ case 'gray-light':
50
+ this.btnClass = 'gn-ui-btn-gray-light'
51
+ break
48
52
  case 'black':
49
53
  this.btnClass = 'gn-ui-btn-black'
50
54
  break
@@ -2,9 +2,9 @@ import {
2
2
  ChangeDetectionStrategy,
3
3
  Component,
4
4
  EventEmitter,
5
+ inject,
5
6
  Input,
6
7
  Output,
7
- inject,
8
8
  } from '@angular/core'
9
9
  import {
10
10
  DateAdapter,
@@ -52,6 +52,6 @@ export class DatePickerComponent {
52
52
  @Output() dateChange = new EventEmitter<Date>()
53
53
 
54
54
  constructor() {
55
- this.dateAdapter.setLocale(this.translate.currentLang)
55
+ this.dateAdapter.setLocale(this.translate.getCurrentLang())
56
56
  }
57
57
  }
@@ -51,7 +51,7 @@
51
51
  <div
52
52
  class="bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container"
53
53
  [style.max-height]="overlayMaxHeight"
54
- [style.width]="overlayWidth"
54
+ [style.min-width]="overlayMinWidth"
55
55
  role="listbox"
56
56
  tabindex="-1"
57
57
  [attr.id]="id"
@@ -3,12 +3,12 @@ import {
3
3
  Component,
4
4
  ElementRef,
5
5
  EventEmitter,
6
+ inject,
6
7
  Input,
7
8
  Output,
8
9
  QueryList,
9
10
  ViewChild,
10
11
  ViewChildren,
11
- inject,
12
12
  } from '@angular/core'
13
13
  import {
14
14
  CdkConnectedOverlay,
@@ -85,7 +85,7 @@ export class DropdownMultiselectComponent {
85
85
  ]
86
86
  scrollStrategy = this.scrollStrategies.reposition()
87
87
  overlayOpen = false
88
- overlayWidth = 'auto'
88
+ overlayMinWidth = 'none'
89
89
  overlayMaxHeight = 'none'
90
90
  id = `dropdown-multiselect-${Math.floor(Math.random() * 10000)}`
91
91
 
@@ -120,7 +120,7 @@ export class DropdownMultiselectComponent {
120
120
  }
121
121
 
122
122
  openOverlay() {
123
- this.overlayWidth =
123
+ this.overlayMinWidth =
124
124
  this.overlayOrigin.elementRef.nativeElement.getBoundingClientRect()
125
125
  .width + 'px'
126
126
  this.overlayMaxHeight = this.maxRows
@@ -1,3 +0,0 @@
1
- .invalid {
2
- border-color: var(--color-secondary);
3
- }
@@ -1,13 +1,17 @@
1
1
  <input
2
2
  #input
3
3
  class="gn-ui-text-input"
4
- [ngClass]="extraClass"
5
- type="text"
6
- [value]="value"
4
+ [class]="extraClass()"
5
+ [ngClass]="{
6
+ 'border-red-400 focus:border-red-800 hover:border-red-800 text-red-800':
7
+ !isValid(),
8
+ }"
9
+ [type]="type()"
10
+ [(ngModel)]="inputModel"
7
11
  (change)="handleChange($event)"
8
12
  (input)="handleChange($event)"
9
- [placeholder]="placeholder"
10
- [attr.aria-label]="placeholder"
11
- [attr.required]="required || null"
12
- [disabled]="disabled"
13
+ [placeholder]="placeholder()"
14
+ [attr.aria-label]="placeholder()"
15
+ [attr.required]="required() || null"
16
+ [disabled]="disabled()"
13
17
  />
@@ -1,45 +1,71 @@
1
1
  import {
2
- AfterViewInit,
2
+ ChangeDetectionStrategy,
3
3
  Component,
4
- Input,
5
- Output,
6
- ViewChild,
4
+ computed,
5
+ ElementRef,
6
+ input,
7
+ output,
8
+ signal,
9
+ viewChild,
7
10
  } from '@angular/core'
8
- import { distinctUntilChanged } from 'rxjs/operators'
9
- import { Subject } from 'rxjs'
10
- import { CommonModule } from '@angular/common'
11
+ import { NgClass } from '@angular/common'
12
+ import { FormsModule } from '@angular/forms'
11
13
 
12
14
  @Component({
13
15
  selector: 'gn-ui-text-input',
14
16
  templateUrl: './text-input.component.html',
15
17
  styleUrls: ['./text-input.component.css'],
16
18
  standalone: true,
17
- imports: [CommonModule],
19
+ changeDetection: ChangeDetectionStrategy.OnPush,
20
+ imports: [NgClass, FormsModule],
18
21
  })
19
- export class TextInputComponent implements AfterViewInit {
20
- @Input() value = ''
21
- @Input() extraClass = ''
22
- @Input() placeholder: string
23
- @Input() required = false
24
- @Input() disabled: boolean
25
- rawChange = new Subject<string>()
26
- @Output() valueChange = this.rawChange.pipe(distinctUntilChanged())
27
- @ViewChild('input') input
22
+ export class TextInputComponent {
23
+ value = input<string, string>('', {
24
+ transform: (v: string) => {
25
+ this.inputModel.set(v)
26
+ // make sure the native element value is up to date
27
+ // TODO: use form() signal when migrated to angular 22 for cleaner implementation
28
+ if (this.inputRef()) {
29
+ this.inputRef().nativeElement.value = v
30
+ }
31
+ return v
32
+ },
33
+ })
34
+ extraClass = input('')
35
+ placeholder = input<string>()
36
+ required = input(false)
37
+ disabled = input<boolean>()
38
+ type = input('text', {
39
+ transform: (type: string) => {
40
+ // make sure the native element type is up to date
41
+ // TODO: use form() signal when migrated to angular 22 for cleaner implementation
42
+ if (this.inputRef()) {
43
+ this.inputRef().nativeElement.type = type
44
+ }
45
+ return type
46
+ },
47
+ })
28
48
 
29
- ngAfterViewInit() {
30
- this.checkRequired(this.input.nativeElement.value)
31
- }
49
+ valueChange = output<string>()
32
50
 
33
- checkRequired(value) {
34
- this.input.nativeElement.classList.toggle(
35
- 'invalid',
36
- this.required && value === ''
37
- )
38
- }
51
+ inputModel = signal('')
52
+ inputRef = viewChild<ElementRef<HTMLInputElement>>('input')
53
+
54
+ isValid = computed<boolean>(() => {
55
+ const isValid = this.inputRef().nativeElement.checkValidity()
56
+ const isEmpty = this.inputModel().trim() === ''
57
+ const isRequired = this.required()
58
+ return isValid && !(isEmpty && isRequired)
59
+ })
60
+
61
+ private lastEmittedValue: string | undefined = undefined
39
62
 
40
- handleChange($event) {
41
- const value = $event.target.value
42
- this.checkRequired(value)
43
- this.rawChange.next(value)
63
+ handleChange($event: Event) {
64
+ const input = $event.target as HTMLInputElement
65
+ const value = input.value
66
+ if (value === this.lastEmittedValue) return
67
+ this.inputModel.set(value)
68
+ this.lastEmittedValue = value
69
+ this.valueChange.emit(value)
44
70
  }
45
71
  }
@@ -6,7 +6,7 @@
6
6
  type="url"
7
7
  [value]="inputValue"
8
8
  (input)="handleInput($event)"
9
- (keydown.enter)="handleUpload(input)"
9
+ (keydown.enter)="handleUpload(input, $event)"
10
10
  [placeholder]="placeholder"
11
11
  [attr.aria-label]="placeholder"
12
12
  [disabled]="disabled"
@@ -25,7 +25,7 @@
25
25
  extraClass="absolute inset-y-[var(--side-padding)] right-[var(--side-padding)]"
26
26
  type="primary"
27
27
  [disabled]="disabled || input.value === '' || !isValidUrl(input.value)"
28
- (buttonClick)="handleUpload(input)"
28
+ (buttonClick)="handleUpload(input, $event)"
29
29
  >
30
30
  <ng-content>
31
31
  <ng-icon name="iconoirArrowUp"></ng-icon>
@@ -81,7 +81,8 @@ export class UrlInputComponent implements OnChanges {
81
81
  this.valueChange.next(value)
82
82
  }
83
83
 
84
- handleUpload(element: HTMLInputElement) {
84
+ handleUpload(element: HTMLInputElement, event: Event) {
85
+ event.stopPropagation()
85
86
  const value = element.value
86
87
  if (!value || !this.isValidUrl(value)) return
87
88
  this.uploadClick.next(value)
@@ -3,14 +3,14 @@
3
3
  <span class="flex-none font-bold" translate="">{{ label }}</span>
4
4
  <div class="flex-1 flex justify-end items-center">
5
5
  <ng-content select="[form-field-interaction]"></ng-content>
6
- <span
6
+ <ng-icon
7
7
  *ngIf="hint"
8
- class="material-symbols-outlined m-2 gn-ui-icon-small"
8
+ name="matHelpOutline"
9
+ size="16"
10
+ class="m-2"
9
11
  [matTooltip]="hint"
10
12
  matTooltipPosition="above"
11
- >
12
- help
13
- </span>
13
+ ></ng-icon>
14
14
  </div>
15
15
  </div>
16
16
  <div class="flex-1 mt-2">
@@ -2,6 +2,8 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
2
2
  import { MatTooltipModule } from '@angular/material/tooltip'
3
3
  import { CommonModule } from '@angular/common'
4
4
  import { TranslateDirective } from '@ngx-translate/core'
5
+ import { NgIconComponent, provideIcons } from '@ng-icons/core'
6
+ import { matHelpOutline } from '@ng-icons/material-icons/outline'
5
7
 
6
8
  @Component({
7
9
  selector: 'gn-ui-form-field-wrapper',
@@ -9,7 +11,13 @@ import { TranslateDirective } from '@ngx-translate/core'
9
11
  styleUrls: ['./form-field-wrapper.component.css'],
10
12
  changeDetection: ChangeDetectionStrategy.OnPush,
11
13
  standalone: true,
12
- imports: [MatTooltipModule, CommonModule, TranslateDirective],
14
+ imports: [
15
+ MatTooltipModule,
16
+ CommonModule,
17
+ TranslateDirective,
18
+ NgIconComponent,
19
+ ],
20
+ providers: [provideIcons({ matHelpOutline })],
13
21
  })
14
22
  export class FormFieldWrapperComponent {
15
23
  @Input() label?: string
@@ -37,7 +37,7 @@
37
37
  (click)="handleRowClick(item)"
38
38
  data-cy="table-row"
39
39
  [attr.data-cy-title]="item.title"
40
- [title]="getItemTitle(item) | translate"
40
+ [title]="getItemTitle(item) | translate: { recordKind: item['kind'] }"
41
41
  >
42
42
  <div
43
43
  class="relative h-0"
@@ -16,9 +16,6 @@ import { marker } from '@biesbjerg/ngx-translate-extract-marker'
16
16
  import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
17
17
  import { Observable, of } from 'rxjs'
18
18
 
19
- marker('editor.record.lock.resourceType')
20
- marker('editor.record.lock.harvested')
21
- marker('editor.record.lock.owner')
22
19
  @Component({
23
20
  selector: 'gn-ui-interactive-table',
24
21
  templateUrl: './interactive-table.component.html',
@@ -53,12 +50,10 @@ export class InteractiveTableComponent {
53
50
 
54
51
  getItemTitle(item: CatalogRecord) {
55
52
  if (!this.isDraftPage) {
56
- if (item.kind !== 'dataset') {
57
- return 'editor.record.lock.resourceType'
58
- } else if (item.extras?.isHarvested) {
59
- return 'editor.record.lock.harvested'
53
+ if (item.extras?.isHarvested) {
54
+ return marker('editor.record.lock.harvested')
60
55
  } else if (!item.extras?.edit) {
61
- return 'editor.record.lock.owner'
56
+ return marker('editor.record.lock.owner')
62
57
  }
63
58
  }
64
59
  return ''
@@ -5,12 +5,12 @@
5
5
  >
6
6
  @for (item of items; track trackByFn(index); let index = $index) {
7
7
  <div class="sortable-box bg-white flex items-center" cdkDrag>
8
- <span
8
+ <ng-icon
9
9
  cdkDragHandle
10
- class="material-symbols-outlined mx-[10px] cursor-grab gn-ui-icon-medium flex-none"
11
- >
12
- drag_handle
13
- </span>
10
+ name="matDragHandleOutline"
11
+ size="20"
12
+ class="mx-[10px] cursor-grab flex-none"
13
+ ></ng-icon>
14
14
  <div class="flex-1">
15
15
  <ng-container
16
16
  *ngTemplateOutlet="
@@ -29,8 +29,8 @@
29
29
  class="ml-[8px] mr-[10px]"
30
30
  (buttonClick)="removeItem(index)"
31
31
  data-test="remove-item"
32
- ><span class="material-symbols-outlined gn-ui-icon-medium">close</span>
33
- </gn-ui-button>
32
+ ><ng-icon name="matCloseOutline" size="20"></ng-icon
33
+ ></gn-ui-button>
34
34
  </div>
35
35
  }
36
36
  </div>
@@ -15,6 +15,11 @@ import {
15
15
  TemplateRef,
16
16
  } from '@angular/core'
17
17
  import { ButtonComponent } from '../../../../../../libs/ui/inputs/src'
18
+ import { NgIconComponent, provideIcons } from '@ng-icons/core'
19
+ import {
20
+ matCloseOutline,
21
+ matDragHandleOutline,
22
+ } from '@ng-icons/material-icons/outline'
18
23
 
19
24
  @Component({
20
25
  selector: 'gn-ui-sortable-list',
@@ -22,7 +27,15 @@ import { ButtonComponent } from '../../../../../../libs/ui/inputs/src'
22
27
  styleUrls: ['sortable-list.component.css'],
23
28
  changeDetection: ChangeDetectionStrategy.OnPush,
24
29
  standalone: true,
25
- imports: [CommonModule, CdkDropList, CdkDrag, CdkDragHandle, ButtonComponent],
30
+ imports: [
31
+ CommonModule,
32
+ CdkDropList,
33
+ CdkDrag,
34
+ CdkDragHandle,
35
+ ButtonComponent,
36
+ NgIconComponent,
37
+ ],
38
+ providers: [provideIcons({ matDragHandleOutline, matCloseOutline })],
26
39
  })
27
40
  export class SortableListComponent {
28
41
  @Input() elementTemplate: TemplateRef<unknown>
@@ -204,11 +204,12 @@ export class MapContainerComponent implements AfterViewInit, OnChanges {
204
204
 
205
205
  async ngOnChanges(changes: SimpleChanges) {
206
206
  if ('context' in changes && !changes['context'].isFirstChange()) {
207
+ const olMap = await this.openlayersMap
207
208
  const diff = computeMapContextDiff(
208
209
  this.processContext(changes['context'].currentValue),
209
210
  this.processContext(changes['context'].previousValue)
210
211
  )
211
- await applyContextDiffToMap(this.olMap, diff)
212
+ await applyContextDiffToMap(olMap, diff)
212
213
 
213
214
  if (this._resolvedExtentChange && diff.viewChanges) {
214
215
  this._resolvedExtentChange.emit(this.calculateCurrentMapExtent())
@@ -1,18 +1,11 @@
1
- import { Component, Input } from '@angular/core'
1
+ import { ChangeDetectorRef, Component, inject, Input } from '@angular/core'
2
2
  import { CommonModule } from '@angular/common'
3
- import { Geometry } from 'geojson'
4
- import { GeoJSONFeatureCollection } from 'ol/format/GeoJSON.js'
5
- import GeoJSON from 'ol/format/GeoJSON.js'
6
- import { Polygon } from 'ol/geom.js'
7
- import {
8
- createViewFromLayer,
9
- MapContext,
10
- MapContextLayer,
11
- } from '@geospatial-sdk/core'
3
+ import { createViewFromLayer, MapContext } from '@geospatial-sdk/core'
12
4
  import { MapContainerComponent } from '../map-container/map-container.component'
13
- import { BehaviorSubject, Observable } from 'rxjs'
14
- import { switchMap } from 'rxjs/operators'
5
+ import { BehaviorSubject, from, Observable, of } from 'rxjs'
6
+ import { map, switchMap, tap } from 'rxjs/operators'
15
7
  import { DatasetSpatialExtent } from '../../../../../../../libs/common/domain/src/lib/model/record'
8
+ import { createSpatialExtentLayer } from '../../map-utils'
16
9
 
17
10
  @Component({
18
11
  selector: 'gn-ui-spatial-extent',
@@ -22,66 +15,24 @@ import { DatasetSpatialExtent } from '../../../../../../../libs/common/domain/sr
22
15
  styleUrl: './spatial-extent.component.css',
23
16
  })
24
17
  export class SpatialExtentComponent {
18
+ private _cdr = inject(ChangeDetectorRef)
19
+
25
20
  @Input() set spatialExtents(value: DatasetSpatialExtent[]) {
26
21
  this.spatialExtents$.next(value)
27
22
  }
28
23
  spatialExtents$ = new BehaviorSubject<DatasetSpatialExtent[]>([])
29
24
  mapContext$: Observable<MapContext> = this.spatialExtents$.pipe(
30
- switchMap(async (extents) => {
31
- if (extents.length === 0) {
32
- return null // null extent means default view
33
- }
34
- const featureCollection: GeoJSONFeatureCollection = {
35
- type: 'FeatureCollection',
36
- features: [],
37
- }
38
- extents.forEach((extent) => {
39
- if (extent.geometry) {
40
- featureCollection.features.push({
41
- type: 'Feature',
42
- properties: {},
43
- geometry: extent.geometry,
44
- })
45
- } else if (extent.bbox?.length >= 0) {
46
- featureCollection.features.push({
47
- type: 'Feature',
48
- properties: {},
49
- geometry: this.bboxCoordsToGeometry(extent.bbox),
50
- })
51
- }
52
- })
53
-
54
- const layer: MapContextLayer = {
55
- type: 'geojson',
56
- data: featureCollection,
57
- label: 'Spatial extents',
58
- style: {
59
- 'stroke-color': 'black',
60
- 'stroke-width': 2,
61
- 'fill-color': 'rgba(153, 153, 153, 0.3)',
62
- },
63
- }
64
- const view = await createViewFromLayer(layer)
65
- return {
66
- view,
67
- layers: [layer],
25
+ switchMap((extents) => {
26
+ const layer = createSpatialExtentLayer(extents)
27
+ if (!layer) {
28
+ return of(null)
68
29
  }
30
+ return from(createViewFromLayer(layer)).pipe(
31
+ map((view) => ({ view, layers: [layer] }) as MapContext),
32
+ tap(() => this._cdr.markForCheck())
33
+ )
69
34
  })
70
35
  )
71
36
 
72
37
  error = ''
73
-
74
- bboxCoordsToGeometry(bbox: [number, number, number, number]): Geometry {
75
- const geometry = new Polygon([
76
- [
77
- [bbox[0], bbox[1]],
78
- [bbox[0], bbox[3]],
79
- [bbox[2], bbox[3]],
80
- [bbox[2], bbox[1]],
81
- [bbox[0], bbox[1]],
82
- ],
83
- ])
84
-
85
- return new GeoJSON().writeGeometryObject(geometry)
86
- }
87
38
  }
@@ -12,6 +12,54 @@ import {
12
12
  MouseWheelZoom,
13
13
  } from 'ol/interaction.js'
14
14
  import MapBrowserEvent from 'ol/MapBrowserEvent.js'
15
+ import type { DatasetSpatialExtent } from '../../../../../libs/common/domain/src/lib/model/record'
16
+ import type {
17
+ MapContextLayer,
18
+ MapContextLayerGeojson,
19
+ } from '@geospatial-sdk/core'
20
+ import { spatialExtentsToFeatureCollection } from '../../../../../libs/util/shared/src'
21
+
22
+ export type SpatialExtentLayerStyle = NonNullable<
23
+ MapContextLayerGeojson['style']
24
+ >
25
+
26
+ /**
27
+ * Default style for a spatial-extent layer: a solid black outline over a
28
+ * translucent grey fill.
29
+ */
30
+ export const DEFAULT_SPATIAL_EXTENT_STYLE: SpatialExtentLayerStyle = {
31
+ 'stroke-color': 'black',
32
+ 'stroke-width': 2,
33
+ 'fill-color': 'rgba(153, 153, 153, 0.3)',
34
+ }
35
+
36
+ /**
37
+ * Builds a GeoJSON map layer drawing the given spatial extents: each extent is
38
+ * rendered from its own geometry or, when only a bounding box is available,
39
+ * from a polygon derived from that box.
40
+ *
41
+ * @returns the layer, or `null` when none of the extents can be represented.
42
+ */
43
+ export function createSpatialExtentLayer(
44
+ extents: DatasetSpatialExtent[],
45
+ overrides?: {
46
+ label?: string
47
+ clickable?: boolean
48
+ style?: SpatialExtentLayerStyle
49
+ }
50
+ ): MapContextLayer | null {
51
+ const data = spatialExtentsToFeatureCollection(extents)
52
+ if (data.features.length === 0) {
53
+ return null
54
+ }
55
+ return {
56
+ type: 'geojson',
57
+ data,
58
+ label: 'Spatial extents',
59
+ style: DEFAULT_SPATIAL_EXTENT_STYLE,
60
+ ...overrides,
61
+ }
62
+ }
15
63
 
16
64
  export function prioritizePageScroll(interactions: Collection<Interaction>) {
17
65
  interactions.clear()
@@ -52,7 +52,7 @@ export class RecordPreviewFeedComponent extends RecordPreviewComponent {
52
52
  protected elementRef: ElementRef
53
53
  private translate = inject(TranslateService)
54
54
 
55
- timeFormat = new Duration(this.translate.currentLang, {})
55
+ timeFormat = new Duration(this.translate.getCurrentLang(), {})
56
56
 
57
57
  constructor() {
58
58
  const elementRef = inject(ElementRef)
@@ -52,10 +52,10 @@
52
52
  class="w-72 p-6 flex flex-col gap-3 mt-2 border border-gray-100 bg-white shadow-2xl rounded-2xl"
53
53
  >
54
54
  <span class="text-lg font-bold text-center">{{
55
- 'editor.record.delete.confirmation.title' | translate
55
+ 'editor.record.delete.confirmation.title' | translate: { recordKind }
56
56
  }}</span>
57
57
  <span class="text-center">{{
58
- 'editor.record.delete.confirmation.message' | translate
58
+ 'editor.record.delete.confirmation.message' | translate: { recordKind }
59
59
  }}</span>
60
60
  <div class="flex flex-row gap-8 justify-center">
61
61
  <gn-ui-button
@@ -65,14 +65,16 @@
65
65
  data-cy="confirm-button"
66
66
  [style.--gn-ui-button-width]="'100px'"
67
67
  >{{
68
- 'editor.record.delete.confirmation.confirmText' | translate
68
+ 'editor.record.delete.confirmation.confirmText'
69
+ | translate: { recordKind }
69
70
  }}</gn-ui-button
70
71
  >
71
72
  <gn-ui-button
72
73
  [style.--gn-ui-button-width]="'100px'"
73
74
  (buttonClick)="closeActionMenu.emit()"
74
75
  >{{
75
- 'editor.record.delete.confirmation.cancelText' | translate
76
+ 'editor.record.delete.confirmation.cancelText'
77
+ | translate: { recordKind }
76
78
  }}</gn-ui-button
77
79
  >
78
80
  </div>
@@ -84,10 +86,10 @@
84
86
  class="w-80 p-6 flex flex-col gap-3 mt-2 border border-gray-100 bg-white shadow-2xl rounded-2xl"
85
87
  >
86
88
  <span class="text-lg font-bold text-center">{{
87
- 'editor.record.undo.confirmation.title' | translate
89
+ 'editor.record.undo.confirmation.title' | translate: { recordKind }
88
90
  }}</span>
89
91
  <span class="text-center">{{
90
- 'editor.record.undo.confirmation.message' | translate
92
+ 'editor.record.undo.confirmation.message' | translate: { recordKind }
91
93
  }}</span>
92
94
  <div class="flex flex-row gap-8 justify-center">
93
95
  <gn-ui-button
@@ -97,14 +99,16 @@
97
99
  data-cy="confirm-button"
98
100
  [style.--gn-ui-button-width]="'120px'"
99
101
  >{{
100
- 'editor.record.undo.confirmation.confirmText' | translate
102
+ 'editor.record.undo.confirmation.confirmText'
103
+ | translate: { recordKind }
101
104
  }}</gn-ui-button
102
105
  >
103
106
  <gn-ui-button
104
107
  [style.--gn-ui-button-width]="'120px'"
105
108
  (buttonClick)="closeActionMenu.emit()"
106
109
  >{{
107
- 'editor.record.undo.confirmation.cancelText' | translate
110
+ 'editor.record.undo.confirmation.cancelText'
111
+ | translate: { recordKind }
108
112
  }}</gn-ui-button
109
113
  >
110
114
  </div>
@@ -2,15 +2,16 @@ import {
2
2
  ChangeDetectorRef,
3
3
  Component,
4
4
  EventEmitter,
5
+ inject,
5
6
  Input,
6
7
  Output,
7
8
  ViewChild,
8
- inject,
9
9
  } from '@angular/core'
10
10
  import { MatDialog, MatDialogModule } from '@angular/material/dialog'
11
11
  import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu'
12
12
  import { ButtonComponent } from '../../../../../../../libs/ui/inputs/src'
13
13
  import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
14
+ import { RecordKind } from '../../../../../../../libs/common/domain/src/lib/model'
14
15
 
15
16
  type ActionMenuPage = 'mainMenu' | 'deleteMenu' | 'rollbackMenu'
16
17
 
@@ -35,6 +36,7 @@ export class ActionMenuComponent {
35
36
  @Input() isDuplicating: boolean
36
37
  @Input() canDelete = true
37
38
  @Input() page: 'draft' | 'main' | 'record'
39
+ @Input() recordKind: RecordKind
38
40
  @Output() duplicate = new EventEmitter<void>()
39
41
  @Output() delete = new EventEmitter<void>()
40
42
  @Output() closeActionMenu = new EventEmitter<void>()
@@ -172,6 +172,7 @@
172
172
  (delete)="handleDelete(item)"
173
173
  (closeActionMenu)="closeActionMenu()"
174
174
  (rollback)="handleRollback(item)"
175
+ [recordKind]="item.kind"
175
176
  >
176
177
  </gn-ui-action-menu>
177
178
  </ng-template>