geonetwork-ui 2.10.0-dev.fc6515c0b → 2.11.0-dev.2478042b2

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 (185) hide show
  1. package/fesm2022/geonetwork-ui.mjs +2326 -6755
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +309 -219
  4. package/index.d.ts.map +1 -1
  5. package/package.json +32 -42
  6. package/src/libs/api/metadata-converter/src/lib/dcat-ap/dcat-ap.converter.ts +20 -0
  7. package/src/libs/api/metadata-converter/src/lib/fixtures/eu.dcat-ap.records.ts +4 -0
  8. package/src/libs/api/metadata-converter/src/lib/fixtures/generic.records.ts +23 -0
  9. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+ongules.ts +8 -0
  10. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+roilaye.ts +2 -0
  11. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.ts +2 -0
  12. package/src/libs/api/metadata-converter/src/lib/fixtures/geocat-ch.records.ts +2 -0
  13. package/src/libs/api/metadata-converter/src/lib/fixtures/georhena.records.ts +2 -0
  14. package/src/libs/api/metadata-converter/src/lib/fixtures/metadata-for-i18n.records.ts +4 -0
  15. package/src/libs/api/metadata-converter/src/lib/fixtures/metawal.records.ts +23 -0
  16. package/src/libs/api/metadata-converter/src/lib/fixtures/opendataswiss.records.ts +2 -0
  17. package/src/libs/api/metadata-converter/src/lib/fixtures/sextant.records.ts +4 -0
  18. package/src/libs/api/metadata-converter/src/lib/fixtures/vlaanderen.dcat-ap.records.ts +2 -0
  19. package/src/libs/api/metadata-converter/src/lib/fixtures/wallonie.records.reuse.ts +9 -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 +2 -0
  22. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +49 -18
  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/elasticsearch.model.ts +13 -1
  25. package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +1 -0
  26. package/src/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.ts +11 -0
  27. package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +21 -0
  28. package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +52 -0
  29. package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +24 -1
  30. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +77 -0
  31. package/src/libs/api/metadata-converter/src/lib/iso19139/utils/association-type.mapper.ts +12 -0
  32. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +79 -0
  33. package/src/libs/api/repository/src/lib/gn4/auth/auth.service.ts +8 -4
  34. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +25 -5
  35. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +106 -56
  36. package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-groups.service.ts +1 -1
  37. package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.ts +1 -1
  38. package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +37 -2
  39. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +61 -5
  40. package/src/libs/common/domain/src/lib/model/search/sort-by.model.ts +1 -5
  41. package/src/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.ts +2 -1
  42. package/src/libs/common/domain/src/lib/model/user/group.model.ts +8 -0
  43. package/src/libs/common/domain/src/lib/model/user/index.ts +1 -0
  44. package/src/libs/common/domain/src/lib/platform.service.interface.ts +2 -0
  45. package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +4 -3
  46. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +62 -0
  47. package/src/libs/feature/catalog/src/lib/sources/sources.service.ts +5 -2
  48. package/src/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts +3 -2
  49. package/src/libs/feature/dataviz/src/lib/geo-table-view/geo-table-view.component.ts +2 -1
  50. package/src/libs/feature/dataviz/src/lib/service/data.service.ts +22 -22
  51. package/src/libs/feature/editor/src/index.ts +1 -0
  52. package/src/libs/feature/editor/src/lib/+state/editor.actions.ts +4 -1
  53. package/src/libs/feature/editor/src/lib/+state/editor.effects.ts +26 -19
  54. package/src/libs/feature/editor/src/lib/+state/editor.facade.ts +5 -1
  55. package/src/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.ts +0 -2
  56. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.css +0 -0
  57. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.html +68 -0
  58. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.ts +41 -0
  59. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.html +5 -14
  60. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.ts +3 -28
  61. package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.html +5 -5
  62. package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.ts +2 -1
  63. package/src/libs/feature/editor/src/lib/components/record-form/form-field/field-focus.directive.ts +4 -1
  64. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-associated-records/form-field-associated-records.component.css +0 -0
  65. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-associated-records/form-field-associated-records.component.html +33 -0
  66. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-associated-records/form-field-associated-records.component.ts +94 -0
  67. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-constraints-shortcuts/form-field-constraints-shortcuts.component.ts +7 -7
  68. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.html +9 -2
  69. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts +12 -0
  70. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.ts +1 -1
  71. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts +12 -5
  72. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.html +4 -4
  73. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.ts +7 -0
  74. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-temporal-extents/form-field-temporal-extents.component.html +16 -12
  75. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-topics/form-field-topics.component.ts +2 -2
  76. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.html +6 -2
  77. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.ts +3 -1
  78. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.css +0 -34
  79. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +34 -16
  80. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +17 -0
  81. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.css +35 -0
  82. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.html +18 -5
  83. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +76 -19
  84. package/src/libs/feature/editor/src/lib/expressions.ts +380 -8
  85. package/src/libs/feature/editor/src/lib/fields.config.ts +359 -337
  86. package/src/libs/feature/editor/src/lib/models/editor-config.model.ts +10 -3
  87. package/src/libs/feature/editor/src/lib/services/editor.service.ts +9 -13
  88. package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +33 -0
  89. package/src/libs/feature/notify-reuse/src/index.ts +1 -0
  90. package/src/libs/feature/notify-reuse/src/lib/edit-delete-reuse-buttons/edit-delete-reuse-buttons.component.css +0 -0
  91. package/src/libs/feature/notify-reuse/src/lib/edit-delete-reuse-buttons/edit-delete-reuse-buttons.component.html +23 -0
  92. package/src/libs/feature/notify-reuse/src/lib/edit-delete-reuse-buttons/edit-delete-reuse-buttons.component.ts +114 -0
  93. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.html +111 -1
  94. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +219 -8
  95. package/src/libs/feature/notify-reuse/src/lib/utils/url.ts +16 -0
  96. package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +9 -1
  97. package/src/libs/feature/record/src/lib/state/mdview.actions.ts +4 -8
  98. package/src/libs/feature/record/src/lib/state/mdview.effects.ts +7 -16
  99. package/src/libs/feature/record/src/lib/state/mdview.facade.ts +1 -3
  100. package/src/libs/feature/record/src/lib/state/mdview.reducer.ts +4 -9
  101. package/src/libs/feature/record/src/lib/state/mdview.selectors.ts +2 -7
  102. package/src/libs/feature/router/src/lib/default/router.service.ts +1 -1
  103. package/src/libs/feature/search/src/lib/results-table/results-table-container.component.ts +10 -5
  104. package/src/libs/feature/search/src/lib/sort-by/sort-by.component.ts +1 -1
  105. package/src/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.ts +2 -2
  106. package/src/libs/ui/dataviz/src/index.ts +0 -1
  107. package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.html +6 -7
  108. package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.ts +31 -19
  109. package/src/libs/ui/dataviz/src/lib/data-table/data-table.fixtures.ts +2 -2
  110. package/src/libs/ui/elements/src/index.ts +1 -0
  111. package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.html +48 -56
  112. package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.html +7 -3
  113. package/src/libs/ui/elements/src/lib/external-link-card/external-link-card.component.html +2 -1
  114. package/src/libs/ui/elements/src/lib/external-link-card/external-link-card.component.ts +2 -2
  115. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.css +13 -0
  116. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.html +13 -3
  117. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.ts +43 -6
  118. package/src/libs/ui/elements/src/lib/keyword-badge/keyword-badge.component.html +14 -0
  119. package/src/libs/ui/elements/src/lib/keyword-badge/keyword-badge.component.ts +85 -0
  120. package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.ts +2 -2
  121. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.css +5 -0
  122. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +5 -8
  123. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +2 -2
  124. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.html +4 -5
  125. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.ts +4 -0
  126. package/src/libs/ui/inputs/src/lib/badge/badge.component.html +33 -26
  127. package/src/libs/ui/inputs/src/lib/badge/badge.component.ts +9 -0
  128. package/src/libs/ui/inputs/src/lib/button/button.component.ts +4 -0
  129. package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +2 -2
  130. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
  131. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.ts +3 -3
  132. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.css +0 -3
  133. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.html +11 -7
  134. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.ts +56 -30
  135. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.html +5 -5
  136. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.ts +9 -1
  137. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.html +1 -1
  138. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.ts +3 -8
  139. package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.html +7 -7
  140. package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.ts +14 -1
  141. package/src/libs/ui/map/src/lib/components/spatial-extent/spatial-extent.component.ts +4 -54
  142. package/src/libs/ui/map/src/lib/map-utils.ts +48 -0
  143. package/src/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.ts +1 -1
  144. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html +12 -8
  145. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts +3 -1
  146. package/src/libs/ui/search/src/lib/results-table/results-table.component.html +1 -0
  147. package/src/libs/ui/widgets/src/lib/popover/popover.component.ts +7 -1
  148. package/src/libs/ui/widgets/src/lib/spinning-loader/spinning-loader.component.html +1 -1
  149. package/src/libs/util/app-config/src/lib/app-config.ts +35 -10
  150. package/src/libs/util/app-config/src/lib/model.ts +3 -0
  151. package/src/libs/util/app-config/src/lib/parse-utils.ts +27 -0
  152. package/src/libs/util/data-fetcher/src/lib/data-fetcher.ts +19 -22
  153. package/src/libs/util/data-fetcher/src/lib/engine/duckdb.ts +185 -0
  154. package/src/libs/util/data-fetcher/src/lib/engine/results.ts +63 -0
  155. package/src/libs/util/data-fetcher/src/lib/{sql-utils.ts → engine/sql-utils.ts} +28 -13
  156. package/src/libs/util/data-fetcher/src/lib/model.ts +2 -1
  157. package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +53 -39
  158. package/src/libs/util/data-fetcher/src/lib/readers/base.ts +11 -0
  159. package/src/libs/util/data-fetcher/src/lib/readers/csv.ts +9 -47
  160. package/src/libs/util/data-fetcher/src/lib/readers/excel.ts +27 -27
  161. package/src/libs/util/data-fetcher/src/lib/readers/geojson.ts +5 -24
  162. package/src/libs/util/data-fetcher/src/lib/readers/gml.ts +5 -49
  163. package/src/libs/util/data-fetcher/src/lib/readers/json.ts +5 -23
  164. package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +183 -124
  165. package/src/libs/util/data-fetcher/src/lib/utils.ts +0 -143
  166. package/src/libs/util/i18n/src/index.ts +0 -1
  167. package/src/libs/util/i18n/src/lib/i18n.constants.ts +17 -11
  168. package/src/libs/util/i18n/src/lib/i18n.interceptor.ts +2 -2
  169. package/src/libs/util/i18n/src/lib/i18n.providers.ts +14 -17
  170. package/src/libs/util/i18n/src/lib/test.translate.loader.ts +48 -0
  171. package/src/libs/util/shared/src/lib/links/link-utils.ts +23 -1
  172. package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
  173. package/src/libs/util/shared/src/lib/utils/geojson.ts +58 -1
  174. package/style.css +0 -1
  175. package/tailwind.base.css +15 -0
  176. package/translations/de.json +124 -51
  177. package/translations/en.json +129 -56
  178. package/translations/es.json +97 -24
  179. package/translations/fr.json +126 -53
  180. package/translations/it.json +125 -52
  181. package/translations/nl.json +96 -23
  182. package/translations/pt.json +97 -24
  183. package/translations/sk.json +98 -25
  184. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.css +0 -3
  185. package/src/libs/util/data-fetcher/src/lib/readers/base-cache.ts +0 -14
@@ -1,10 +1,19 @@
1
- import { Component, EventEmitter, Input, Output } from '@angular/core'
2
- import * as basicLightbox from 'basiclightbox'
1
+ import {
2
+ Component,
3
+ inject,
4
+ input,
5
+ output,
6
+ TemplateRef,
7
+ viewChild,
8
+ ViewContainerRef,
9
+ } from '@angular/core'
3
10
  import { ContentGhostComponent } from '../content-ghost/content-ghost.component'
4
11
  import { ThumbnailComponent } from '../thumbnail/thumbnail.component'
5
12
  import { ButtonComponent } from '../../../../../../libs/ui/inputs/src'
6
13
  import { NgIcon, provideIcons, provideNgIconsConfig } from '@ng-icons/core'
7
14
  import { matZoomOutMap } from '@ng-icons/material-icons/baseline'
15
+ import { Overlay, OverlayRef } from '@angular/cdk/overlay'
16
+ import { TemplatePortal } from '@angular/cdk/portal'
8
17
 
9
18
  @Component({
10
19
  selector: 'gn-ui-image-overlay-preview',
@@ -20,9 +29,37 @@ import { matZoomOutMap } from '@ng-icons/material-icons/baseline'
20
29
  ],
21
30
  })
22
31
  export class ImageOverlayPreviewComponent {
23
- @Input() imageUrl: string
24
- @Output() isPlaceholderShown = new EventEmitter<boolean>()
25
- openLightbox(src: string) {
26
- basicLightbox.create(`<img src="${src}"/>`).show()
32
+ imageUrl = input<string>()
33
+ isPlaceholderShown = output<boolean>({})
34
+
35
+ private imageTemplate = viewChild<TemplateRef<HTMLElement>>('imageTemplate')
36
+ private overlay = inject(Overlay)
37
+ private viewContainerRef = inject(ViewContainerRef)
38
+ private overlayRef: OverlayRef
39
+
40
+ openLightbox() {
41
+ const positionStrategy = this.overlay
42
+ .position()
43
+ .global()
44
+ .centerHorizontally()
45
+ .centerVertically()
46
+ const scrollStrategy = this.overlay.scrollStrategies.block()
47
+
48
+ this.overlayRef = this.overlay.create({
49
+ hasBackdrop: true,
50
+ backdropClass: 'bg-black/80',
51
+ positionStrategy,
52
+ scrollStrategy,
53
+ })
54
+ this.overlayRef.backdropClick().subscribe(() => this.overlayRef.dispose())
55
+ const portal = new TemplatePortal(
56
+ this.imageTemplate(),
57
+ this.viewContainerRef
58
+ )
59
+ this.overlayRef.attach(portal)
60
+ }
61
+
62
+ closeLightbox() {
63
+ this.overlayRef?.dispose()
27
64
  }
28
65
  }
@@ -0,0 +1,14 @@
1
+ <gn-ui-badge
2
+ [clickable]="!editable"
3
+ [removable]="editable"
4
+ (badgeClicked)="keywordClick.emit(keyword)"
5
+ (badgeRemoveClicked)="keywordRemove.emit(keyword)"
6
+ ><ng-container>{{ keyword.label }}</ng-container>
7
+ @if (editable && isPlaceWithoutExtent) {
8
+ <ng-icon
9
+ [title]="'editor.record.form.keywords.place.withoutExtent' | translate"
10
+ class="!text-[1.4em] ml-1"
11
+ name="matWarningAmberOutline"
12
+ ></ng-icon>
13
+ }
14
+ </gn-ui-badge>
@@ -0,0 +1,85 @@
1
+ import {
2
+ AfterViewInit,
3
+ ChangeDetectionStrategy,
4
+ Component,
5
+ ElementRef,
6
+ EventEmitter,
7
+ inject,
8
+ Input,
9
+ Output,
10
+ ViewChild,
11
+ } from '@angular/core'
12
+ import { Keyword } from '../../../../../../libs/common/domain/src/lib/model/record'
13
+ import { BadgeComponent } from '../../../../../../libs/ui/inputs/src'
14
+ import { TranslatePipe, TranslateService } from '@ngx-translate/core'
15
+ import { marker } from '@biesbjerg/ngx-translate-extract-marker'
16
+ import {
17
+ NgIconComponent,
18
+ provideIcons,
19
+ provideNgIconsConfig,
20
+ } from '@ng-icons/core'
21
+ import { matWarningAmberOutline } from '@ng-icons/material-icons/outline'
22
+ import tippy from 'tippy.js'
23
+
24
+ marker('domain.record.keywordType.theme')
25
+ marker('domain.record.keywordType.place')
26
+ marker('domain.record.keywordType.temporal')
27
+ marker('domain.record.keywordType.other')
28
+
29
+ @Component({
30
+ selector: 'gn-ui-keyword-badge',
31
+ templateUrl: './keyword-badge.component.html',
32
+ changeDetection: ChangeDetectionStrategy.OnPush,
33
+ standalone: true,
34
+ imports: [BadgeComponent, NgIconComponent, TranslatePipe],
35
+ providers: [
36
+ provideIcons({
37
+ matWarningAmberOutline,
38
+ }),
39
+ provideNgIconsConfig({
40
+ size: '1.5em',
41
+ }),
42
+ ],
43
+ })
44
+ export class KeywordBadgeComponent implements AfterViewInit {
45
+ private translateService = inject(TranslateService)
46
+
47
+ @Input() keyword: Keyword
48
+ @Input() editable = false // if false, keyword is only clickable
49
+ @Output() keywordClick = new EventEmitter<Keyword>()
50
+ @Output() keywordRemove = new EventEmitter<Keyword>()
51
+ @ViewChild(BadgeComponent, { read: ElementRef }) badgeComponent: ElementRef
52
+
53
+ get segments(): string[] {
54
+ if (this.keyword.hierarchyPath?.length) {
55
+ return this.keyword.hierarchyPath
56
+ }
57
+ if (this.keyword.thesaurus?.name) {
58
+ return [this.keyword.thesaurus.name, this.keyword.label]
59
+ }
60
+ return [
61
+ this.translateService.instant(
62
+ `domain.record.keywordType.${this.keyword.type}`
63
+ ),
64
+ this.keyword.label,
65
+ ]
66
+ }
67
+
68
+ get isPlaceWithoutExtent(): boolean {
69
+ return this.keyword.type === 'place' && !this.keyword.bbox
70
+ }
71
+
72
+ ngAfterViewInit() {
73
+ const content = this.segments
74
+ .map(
75
+ (segment, i, arr) =>
76
+ `<span ${i === arr.length - 1 ? 'class="font-bold"' : ''}>${segment}</span>`
77
+ )
78
+ .join(' &gt; ')
79
+ tippy(this.badgeComponent.nativeElement, {
80
+ appendTo: () => document.body,
81
+ content,
82
+ allowHTML: true,
83
+ })
84
+ }
85
+ }
@@ -1,5 +1,5 @@
1
1
  import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
2
- import { marked } from 'marked'
2
+ import { parse } from 'marked'
3
3
 
4
4
  @Component({
5
5
  selector: 'gn-ui-markdown-parser',
@@ -14,6 +14,6 @@ export class MarkdownParserComponent {
14
14
  @Input() whitoutStyles?: boolean
15
15
 
16
16
  get parsedMarkdown() {
17
- return marked.parse(this.textContent ?? '')
17
+ return parse(this.textContent ?? '')
18
18
  }
19
19
  }
@@ -16,3 +16,8 @@
16
16
  --gn-ui-badge-background-color: var(--color-primary-white);
17
17
  --gn-ui-badge-text-color: var(--color-primary-darkest);
18
18
  }
19
+
20
+ .metadata-info-keywords {
21
+ --gn-ui-badge-padding: 0.25rem 0.5rem;
22
+ text-transform: lowercase;
23
+ }
@@ -28,13 +28,10 @@
28
28
  <gn-ui-max-lines [maxLines]="7">
29
29
  <div class="metadata-info-keywords sm:pb-4 flex flex-wrap gap-2">
30
30
  @for (keyword of metadata.keywords; track keyword) {
31
- <gn-ui-button
32
- type="primary-light"
33
- class="inline-block opacity-70"
34
- extraClass="lowercase text-sm py-1 px-2"
35
- (buttonClick)="onKeywordClick(keyword)"
36
- >{{ keyword.label }}</gn-ui-button
37
- >
31
+ <gn-ui-keyword-badge
32
+ [keyword]="keyword"
33
+ (keywordClick)="onKeywordClick($event)"
34
+ ></gn-ui-keyword-badge>
38
35
  }
39
36
  </div>
40
37
  </gn-ui-max-lines>
@@ -113,7 +110,7 @@
113
110
  >
114
111
  <div class="flex flex-col gap-1 pt-3 pb-4">
115
112
  @for (group of contactGroups; track group.role) {
116
- <div class="flex flex-col gap-1 rounded bg-gray-50 py-4 px-2">
113
+ <div class="flex flex-col gap-1 rounded py-4 px-2">
117
114
  <p class="text-xs font-normal text-black">
118
115
  {{ group.roleLabel | translate }}
119
116
  </p>
@@ -22,10 +22,10 @@ import {
22
22
  import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
23
23
  import {
24
24
  BadgeComponent,
25
- ButtonComponent,
26
25
  CopyTextButtonComponent,
27
26
  } from '../../../../../../libs/ui/inputs/src'
28
27
  import { ContentGhostComponent } from '../content-ghost/content-ghost.component'
28
+ import { KeywordBadgeComponent } from '../keyword-badge/keyword-badge.component'
29
29
  import { NgIcon, provideIcons } from '@ng-icons/core'
30
30
  import { matOpenInNew } from '@ng-icons/material-icons/baseline'
31
31
  import { matMailOutline } from '@ng-icons/material-icons/outline'
@@ -46,7 +46,6 @@ import { ContactPillComponent } from '../contact-pill/contact-pill.component'
46
46
  TranslatePipe,
47
47
  MarkdownParserComponent,
48
48
  ExpandablePanelComponent,
49
- ButtonComponent,
50
49
  BadgeComponent,
51
50
  ContentGhostComponent,
52
51
  MaxLinesComponent,
@@ -56,6 +55,7 @@ import { ContactPillComponent } from '../contact-pill/contact-pill.component'
56
55
  GnUiHumanizeDateDirective,
57
56
  SpatialExtentComponent,
58
57
  ContactPillComponent,
58
+ KeywordBadgeComponent,
59
59
  ],
60
60
  viewProviders: [
61
61
  provideIcons({
@@ -52,12 +52,11 @@
52
52
  </gn-ui-text-input>
53
53
  @if (!supportOffset) {
54
54
  <div class="flex items-center gap-2 text-orange-500 z-10 ml-3">
55
- <span
56
- class="material-symbols-outlined"
55
+ <ng-icon
56
+ name="matWarningOutline"
57
+ size="24"
57
58
  matTooltip="Not supported on this service"
58
- >
59
- warning
60
- </span>
59
+ ></ng-icon>
61
60
  </div>
62
61
  }
63
62
  </div>
@@ -15,6 +15,8 @@ import {
15
15
  import { CommonModule } from '@angular/common'
16
16
  import { TranslateDirective } from '@ngx-translate/core'
17
17
  import { MatTooltipModule } from '@angular/material/tooltip'
18
+ import { NgIconComponent, provideIcons } from '@ng-icons/core'
19
+ import { matWarningOutline } from '@ng-icons/material-icons/outline'
18
20
 
19
21
  const DEFAULT_PARAMS = {
20
22
  OFFSET: '',
@@ -35,7 +37,9 @@ const DEFAULT_PARAMS = {
35
37
  CopyTextButtonComponent,
36
38
  TranslateDirective,
37
39
  MatTooltipModule,
40
+ NgIconComponent,
38
41
  ],
42
+ providers: [provideIcons({ matWarningOutline })],
39
43
  })
40
44
  export class RecordApiFormComponent {
41
45
  @Input() set apiLink(value: DatasetServiceDistribution) {
@@ -1,27 +1,34 @@
1
- <div
2
- class="gn-ui-badge flex items-center gap-1.5"
3
- [ngClass]="
4
- clickable
5
- ? 'hover:bg-primary cursor-pointer transition-colors duration-100'
6
- : ''
7
- "
8
- >
1
+ <ng-template #badgeContent>
9
2
  <span><ng-content></ng-content></span>
10
- @if (removable) {
11
- <gn-ui-button
12
- type="light"
13
- (buttonClick)="removeBadge()"
14
- extraClass="text-xs border-0 px-0 py-0"
15
- style="
16
- --gn-ui-button-padding: 0;
17
- --gn-ui-button-font-size: 0.8em;
18
- --gn-ui-button-width: 1.4em;
19
- --gn-ui-button-height: 1.4em;
20
- --gn-ui-button-rounded: 1.4em;
21
- --gn-ui-button-background: var(--gn-ui-badge-background-color, white);
22
- "
23
- >
24
- <ng-icon name="matClose"></ng-icon>
25
- </gn-ui-button>
26
- }
27
- </div>
3
+ </ng-template>
4
+
5
+ @if (clickable) {
6
+ <button
7
+ type="button"
8
+ class="gn-ui-badge-clickable"
9
+ (click)="handleClick($event)"
10
+ >
11
+ <ng-container [ngTemplateOutlet]="badgeContent"></ng-container>
12
+ </button>
13
+ } @else {
14
+ <span class="gn-ui-badge gap-1.5">
15
+ <ng-container [ngTemplateOutlet]="badgeContent"></ng-container>
16
+ @if (removable) {
17
+ <gn-ui-button
18
+ type="light"
19
+ (buttonClick)="removeBadge()"
20
+ extraClass="text-xs border-0 px-0 py-0"
21
+ style="
22
+ --gn-ui-button-padding: 0;
23
+ --gn-ui-button-font-size: 0.8em;
24
+ --gn-ui-button-width: 1.4em;
25
+ --gn-ui-button-height: 1.4em;
26
+ --gn-ui-button-rounded: 1.4em;
27
+ --gn-ui-button-background: var(--gn-ui-badge-background-color, white);
28
+ "
29
+ >
30
+ <ng-icon name="matClose"></ng-icon>
31
+ </gn-ui-button>
32
+ }
33
+ </span>
34
+ }
@@ -13,6 +13,7 @@ import {
13
13
  provideNgIconsConfig,
14
14
  } from '@ng-icons/core'
15
15
  import { matClose } from '@ng-icons/material-icons/baseline'
16
+ import { propagateToDocumentOnly } from '../../../../../../libs/util/shared/src'
16
17
 
17
18
  @Component({
18
19
  selector: 'gn-ui-badge',
@@ -34,8 +35,16 @@ export class BadgeComponent {
34
35
  @Input() clickable? = false
35
36
  @Input() removable? = false
36
37
  @Output() badgeRemoveClicked = new EventEmitter<void>()
38
+ @Output() badgeClicked = new EventEmitter<void>()
37
39
 
38
40
  removeBadge() {
39
41
  this.badgeRemoveClicked.emit()
40
42
  }
43
+
44
+ handleClick(event: MouseEvent) {
45
+ this.badgeClicked.emit()
46
+ event.preventDefault()
47
+ ;(event.currentTarget as HTMLElement).blur()
48
+ propagateToDocumentOnly(event)
49
+ }
41
50
  }
@@ -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
  }
@@ -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 ''