geonetwork-ui 2.10.0-dev.e97ea6950 → 2.10.0-dev.f1c00d5fe

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 (174) hide show
  1. package/fesm2022/geonetwork-ui.mjs +2558 -6985
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +299 -189
  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 +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 +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 +11 -0
  26. package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +21 -0
  27. package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +52 -0
  28. package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +24 -1
  29. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +73 -0
  30. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +79 -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 +79 -41
  34. package/src/libs/api/repository/src/lib/gn4/gn4.provider.ts +6 -1
  35. package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-groups.service.ts +1 -1
  36. package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.ts +1 -1
  37. package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +37 -2
  38. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +23 -5
  39. package/src/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.ts +2 -1
  40. package/src/libs/common/domain/src/lib/model/user/group.model.ts +8 -0
  41. package/src/libs/common/domain/src/lib/model/user/index.ts +1 -0
  42. package/src/libs/common/domain/src/lib/platform.service.interface.ts +2 -0
  43. package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +2 -1
  44. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +62 -0
  45. package/src/libs/feature/catalog/src/lib/sources/sources.service.ts +5 -2
  46. package/src/libs/feature/editor/src/index.ts +1 -0
  47. package/src/libs/feature/editor/src/lib/+state/editor.actions.ts +10 -1
  48. package/src/libs/feature/editor/src/lib/+state/editor.effects.ts +26 -20
  49. package/src/libs/feature/editor/src/lib/+state/editor.facade.ts +15 -2
  50. package/src/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.ts +0 -2
  51. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.css +0 -0
  52. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.html +68 -0
  53. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.ts +41 -0
  54. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.html +5 -14
  55. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.ts +3 -28
  56. package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.html +18 -3
  57. package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.ts +33 -40
  58. package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.html +5 -5
  59. package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.ts +2 -1
  60. package/src/libs/feature/editor/src/lib/components/record-form/form-field/field-focus.directive.ts +41 -0
  61. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-constraints-shortcuts/form-field-constraints-shortcuts.component.ts +33 -34
  62. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.html +13 -13
  63. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts +18 -4
  64. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.html +8 -7
  65. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.ts +6 -6
  66. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.ts +1 -1
  67. 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
  68. 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
  69. 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
  70. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts +12 -5
  71. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.html +4 -4
  72. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.ts +7 -0
  73. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-topics/form-field-topics.component.ts +2 -2
  74. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.html +6 -2
  75. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.ts +3 -1
  76. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.css +3 -0
  77. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +28 -16
  78. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +16 -0
  79. package/src/libs/feature/editor/src/lib/components/record-form/form-field/index.ts +1 -0
  80. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.css +35 -0
  81. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.html +18 -5
  82. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +118 -5
  83. package/src/libs/feature/editor/src/lib/expressions.ts +380 -8
  84. package/src/libs/feature/editor/src/lib/fields.config.ts +354 -337
  85. package/src/libs/feature/editor/src/lib/models/editor-config.model.ts +10 -3
  86. package/src/libs/feature/editor/src/lib/services/editor.service.ts +9 -13
  87. package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +33 -0
  88. package/src/libs/feature/notify-reuse/src/index.ts +2 -0
  89. package/src/libs/feature/notify-reuse/src/lib/edit-delete-reuse-buttons/edit-delete-reuse-buttons.component.css +0 -0
  90. package/src/libs/feature/notify-reuse/src/lib/edit-delete-reuse-buttons/edit-delete-reuse-buttons.component.html +23 -0
  91. package/src/libs/feature/notify-reuse/src/lib/edit-delete-reuse-buttons/edit-delete-reuse-buttons.component.ts +114 -0
  92. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.css +0 -0
  93. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.html +111 -0
  94. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +234 -0
  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/search/src/lib/results-table/results-table-container.component.ts +10 -5
  98. package/src/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.ts +2 -2
  99. package/src/libs/ui/dataviz/src/index.ts +0 -1
  100. package/src/libs/ui/elements/src/index.ts +3 -0
  101. package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.html +88 -0
  102. package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.ts +45 -0
  103. package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.html +41 -0
  104. package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.ts +70 -0
  105. package/src/libs/ui/elements/src/lib/external-link-card/external-link-card.component.html +2 -1
  106. package/src/libs/ui/elements/src/lib/external-link-card/external-link-card.component.ts +2 -2
  107. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.css +13 -0
  108. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.html +13 -3
  109. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.ts +43 -6
  110. package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.html +1 -1
  111. package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.ts +0 -1
  112. package/src/libs/ui/elements/src/lib/keyword-badge/keyword-badge.component.html +14 -0
  113. package/src/libs/ui/elements/src/lib/keyword-badge/keyword-badge.component.ts +85 -0
  114. package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.ts +2 -2
  115. package/src/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.ts +2 -5
  116. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.css +3 -2
  117. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +26 -68
  118. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +30 -10
  119. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.html +4 -5
  120. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.ts +4 -0
  121. package/src/libs/ui/inputs/src/lib/badge/badge.component.html +33 -26
  122. package/src/libs/ui/inputs/src/lib/badge/badge.component.ts +9 -0
  123. package/src/libs/ui/inputs/src/lib/button/button.component.ts +8 -0
  124. package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +2 -2
  125. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
  126. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.ts +3 -3
  127. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.css +0 -3
  128. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.html +11 -7
  129. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.ts +56 -30
  130. package/src/libs/ui/inputs/src/lib/url-input/url-input.component.html +2 -2
  131. package/src/libs/ui/inputs/src/lib/url-input/url-input.component.ts +2 -1
  132. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.html +5 -5
  133. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.ts +9 -1
  134. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.html +1 -1
  135. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.ts +3 -8
  136. package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.html +7 -7
  137. package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.ts +14 -1
  138. package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +2 -1
  139. package/src/libs/ui/map/src/lib/components/spatial-extent/spatial-extent.component.ts +15 -64
  140. package/src/libs/ui/map/src/lib/map-utils.ts +48 -0
  141. package/src/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.ts +1 -1
  142. package/src/libs/ui/search/src/lib/record-preview-row/record-preview-row.component.html +0 -1
  143. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html +12 -8
  144. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts +3 -1
  145. package/src/libs/ui/search/src/lib/results-table/results-table.component.html +1 -0
  146. package/src/libs/ui/widgets/src/lib/popover/popover.component.ts +7 -1
  147. package/src/libs/ui/widgets/src/lib/spinning-loader/spinning-loader.component.html +1 -1
  148. package/src/libs/util/app-config/src/lib/app-config.ts +70 -9
  149. package/src/libs/util/app-config/src/lib/model.ts +7 -0
  150. package/src/libs/util/app-config/src/lib/parse-utils.ts +50 -1
  151. package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +1 -2
  152. package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +6 -3
  153. package/src/libs/util/i18n/src/index.ts +0 -1
  154. package/src/libs/util/i18n/src/lib/i18n.constants.ts +17 -11
  155. package/src/libs/util/i18n/src/lib/i18n.interceptor.ts +2 -2
  156. package/src/libs/util/i18n/src/lib/i18n.providers.ts +14 -17
  157. package/src/libs/util/i18n/src/lib/test.translate.loader.ts +48 -0
  158. package/src/libs/util/shared/src/lib/links/link-utils.ts +23 -1
  159. package/src/libs/util/shared/src/lib/record/quality-score.util.ts +33 -18
  160. package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
  161. package/src/libs/util/shared/src/lib/utils/geojson.ts +58 -1
  162. package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
  163. package/src/libs/util/shared/src/lib/utils/user-display.ts +32 -0
  164. package/style.css +0 -1
  165. package/tailwind.base.css +26 -2
  166. package/translations/de.json +112 -50
  167. package/translations/en.json +117 -55
  168. package/translations/es.json +86 -24
  169. package/translations/fr.json +114 -52
  170. package/translations/it.json +113 -51
  171. package/translations/nl.json +85 -23
  172. package/translations/pt.json +86 -24
  173. package/translations/sk.json +86 -24
  174. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.css +0 -3
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  Component,
3
3
  EventEmitter,
4
+ inject,
4
5
  Input,
5
6
  OnDestroy,
6
7
  Output,
7
- inject,
8
8
  } from '@angular/core'
9
9
  import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
10
10
  import { SearchFacade } from '../state/search.facade'
@@ -67,6 +67,7 @@ export class ResultsTableContainerComponent implements OnDestroy {
67
67
 
68
68
  async handleDeleteRecord(item: unknown) {
69
69
  const uniqueIdentifier = (item as CatalogRecord).uniqueIdentifier
70
+ const recordKind = (item as CatalogRecord).kind
70
71
  this.subscription.add(
71
72
  this.recordsRepository.deleteRecord(uniqueIdentifier).subscribe({
72
73
  next: () => {
@@ -76,10 +77,12 @@ export class ResultsTableContainerComponent implements OnDestroy {
76
77
  {
77
78
  type: 'success',
78
79
  title: this.translateService.instant(
79
- 'editor.record.deleteSuccess.title'
80
+ 'editor.record.deleteSuccess.title',
81
+ { recordKind }
80
82
  ),
81
83
  text: `${this.translateService.instant(
82
- 'editor.record.deleteSuccess.body'
84
+ 'editor.record.deleteSuccess.body',
85
+ { recordKind }
83
86
  )}`,
84
87
  },
85
88
  2500
@@ -90,10 +93,12 @@ export class ResultsTableContainerComponent implements OnDestroy {
90
93
  {
91
94
  type: 'error',
92
95
  title: this.translateService.instant(
93
- 'editor.record.deleteError.title'
96
+ 'editor.record.deleteError.title',
97
+ { recordKind }
94
98
  ),
95
99
  text: `${this.translateService.instant(
96
- 'editor.record.deleteError.body'
100
+ 'editor.record.deleteError.body',
101
+ { recordKind }
97
102
  )} ${error}`,
98
103
  closeMessage: this.translateService.instant(
99
104
  'editor.record.deleteError.closeMessage'
@@ -1,4 +1,4 @@
1
- import { Component, InjectionToken, inject } from '@angular/core'
1
+ import { Component, inject, InjectionToken } from '@angular/core'
2
2
  import { LANGUAGE_STORAGE_KEY } from '../../../../../../libs/util/i18n/src'
3
3
  import { TranslateService } from '@ngx-translate/core'
4
4
  import { DropdownSelectorComponent } from '../../../../../../libs/ui/inputs/src'
@@ -26,7 +26,7 @@ export class LanguageSwitcherComponent {
26
26
  )
27
27
 
28
28
  get currentLang() {
29
- return this.translate.currentLang
29
+ return this.translate.getCurrentLang()
30
30
  }
31
31
 
32
32
  changeLanguage(value: unknown) {
@@ -1,4 +1,3 @@
1
1
  export * from './lib/chart/chart.component'
2
2
  export * from './lib/data-table/data-table.component'
3
- export * from './lib/data-table/data-table.fixtures'
4
3
  export * from './lib/figure/figure.component'
@@ -11,6 +11,7 @@ export * from './lib/image-overlay-preview/image-overlay-preview.component'
11
11
  export * from './lib/kind-badge/kind-badge.component'
12
12
  export * from './lib/external-link-card/external-link-card.component'
13
13
  export * from './lib/markdown-editor/markdown-editor.component'
14
+ export * from './lib/keyword-badge/keyword-badge.component'
14
15
  export * from './lib/markdown-parser/markdown-parser.component'
15
16
  export * from './lib/metadata-catalog/metadata-catalog.component'
16
17
  export * from './lib/metadata-contact/metadata-contact.component'
@@ -18,6 +19,8 @@ export * from './lib/metadata-doi/metadata-doi.component'
18
19
  export * from './lib/metadata-info/metadata-info.component'
19
20
  export * from './lib/metadata-quality-item/metadata-quality-item.component'
20
21
  export * from './lib/metadata-quality/metadata-quality.component'
22
+ export * from './lib/contact-pill/contact-pill.component'
23
+ export * from './lib/contact-details/contact-details.component'
21
24
  export * from './lib/notification/notification.component'
22
25
  export * from './lib/record-api-form/record-api-form.component'
23
26
  export * from './lib/thumbnail/thumbnail.component'
@@ -0,0 +1,88 @@
1
+ <div
2
+ class="bg-gray-50 text-black rounded border border-gray-200 shadow-md p-4 flex flex-col gap-2 w-full"
3
+ data-test="contact-details"
4
+ >
5
+ @if (displayName) {
6
+ <div class="flex items-center gap-2.5">
7
+ @if (organization?.logoUrl?.href) {
8
+ <div
9
+ class="flex items-center justify-center rounded-lg bg-white w-14 h-14 shrink-0 overflow-hidden"
10
+ >
11
+ <gn-ui-thumbnail
12
+ class="relative h-full w-full"
13
+ [thumbnailUrl]="organization.logoUrl.href"
14
+ fit="contain"
15
+ ></gn-ui-thumbnail>
16
+ </div>
17
+ }
18
+ <span
19
+ class="font-title text-xl leading-tight"
20
+ data-test="contact-details-name"
21
+ >{{ displayName }}</span
22
+ >
23
+ </div>
24
+ }
25
+
26
+ @if (contact.email || contact.phone) {
27
+ <div
28
+ class="grid gap-1 items-start"
29
+ [class.grid-cols-2]="contact.email && contact.phone"
30
+ >
31
+ @if (contact.email) {
32
+ <div class="bg-gray-100 rounded-lg flex items-center gap-2 px-3 py-2">
33
+ <ng-icon
34
+ class="!w-5 !h-5 !text-[20px] shrink-0 opacity-30"
35
+ name="matMailOutline"
36
+ ></ng-icon>
37
+ <a
38
+ [href]="'mailto:' + contact.email"
39
+ class="text-sm break-all hover:underline"
40
+ data-test="contact-details-email"
41
+ >{{ contact.email }}</a
42
+ >
43
+ </div>
44
+ }
45
+ @if (contact.phone) {
46
+ <div class="bg-gray-100 rounded-lg flex items-center gap-2 px-3 py-2">
47
+ <ng-icon
48
+ class="!w-5 !h-5 !text-[20px] shrink-0 opacity-30"
49
+ name="matCallOutline"
50
+ ></ng-icon>
51
+ <span class="text-sm" data-test="contact-details-phone">{{
52
+ contact.phone
53
+ }}</span>
54
+ </div>
55
+ }
56
+ </div>
57
+ }
58
+
59
+ @if (addressLines.length) {
60
+ <div class="bg-gray-100 rounded-lg flex items-start gap-2 px-3 py-2">
61
+ <ng-icon
62
+ class="!w-5 !h-5 !text-[20px] shrink-0 mt-0.5 opacity-30"
63
+ name="matLocationOnOutline"
64
+ ></ng-icon>
65
+ <div class="flex flex-col" data-test="contact-details-address">
66
+ @for (line of addressLines; track line) {
67
+ <p class="text-sm m-0">{{ line }}</p>
68
+ }
69
+ </div>
70
+ </div>
71
+ }
72
+
73
+ @if (organization?.website) {
74
+ <div class="bg-gray-100 rounded-lg flex items-center gap-2 px-3 py-2">
75
+ <ng-icon
76
+ class="!w-5 !h-5 !text-[20px] shrink-0 opacity-30"
77
+ name="matOpenInNew"
78
+ ></ng-icon>
79
+ <a
80
+ [href]="organization.website.href"
81
+ target="_blank"
82
+ class="text-sm break-all hover:underline"
83
+ data-test="contact-details-website"
84
+ >{{ organization.website.href }}</a
85
+ >
86
+ </div>
87
+ }
88
+ </div>
@@ -0,0 +1,45 @@
1
+ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
2
+ import { Individual } from '../../../../../../libs/common/domain/src/lib/model/record'
3
+ import {
4
+ getAddressLines,
5
+ getIndividualDisplayName,
6
+ } from '../../../../../../libs/util/shared/src'
7
+ import { NgIcon, provideIcons } from '@ng-icons/core'
8
+ import { matMailOutline, matOpenInNew } from '@ng-icons/material-icons/baseline'
9
+ import {
10
+ matCallOutline,
11
+ matLocationOnOutline,
12
+ } from '@ng-icons/material-icons/outline'
13
+ import { ThumbnailComponent } from '../thumbnail/thumbnail.component'
14
+ import { TranslateModule } from '@ngx-translate/core'
15
+
16
+ @Component({
17
+ selector: 'gn-ui-contact-details',
18
+ templateUrl: './contact-details.component.html',
19
+ changeDetection: ChangeDetectionStrategy.OnPush,
20
+ standalone: true,
21
+ imports: [NgIcon, ThumbnailComponent, TranslateModule],
22
+ viewProviders: [
23
+ provideIcons({
24
+ matCallOutline,
25
+ matLocationOnOutline,
26
+ matMailOutline,
27
+ matOpenInNew,
28
+ }),
29
+ ],
30
+ })
31
+ export class ContactDetailsComponent {
32
+ @Input() contact: Individual
33
+
34
+ get organization() {
35
+ return this.contact?.organization
36
+ }
37
+
38
+ get displayName(): string {
39
+ return getIndividualDisplayName(this.contact)
40
+ }
41
+
42
+ get addressLines(): string[] {
43
+ return getAddressLines(this.contact?.address)
44
+ }
45
+ }
@@ -0,0 +1,41 @@
1
+ <gn-ui-button
2
+ [type]="overlayOpen ? 'gray-light' : 'primary-light'"
3
+ extraClass="group w-full min-h-12 gap-3 justify-between py-2 pl-5 pr-4 rounded"
4
+ data-test="contact-pill"
5
+ (buttonClick)="toggleOverlay()"
6
+ cdkOverlayOrigin
7
+ #overlayOrigin="cdkOverlayOrigin"
8
+ >
9
+ <span
10
+ class="font-title font-medium text-base leading-tight truncate"
11
+ [class]="!overlayOpen ? 'text-primary-black group-hover:text-white' : ''"
12
+ [title]="displayName"
13
+ >{{ displayName }}</span
14
+ >
15
+ <div
16
+ class="gn-ui-card-icon items-center justify-center w-10 h-8"
17
+ [class]="
18
+ !overlayOpen ? 'group-hover:border-white group-hover:text-white' : ''
19
+ "
20
+ >
21
+ @if (overlayOpen) {
22
+ <ng-icon class="!w-6 !h-6 !text-[24px]" name="matClose"></ng-icon>
23
+ } @else {
24
+ <ng-icon class="!w-6 !h-6 !text-[24px]" name="matInfoOutline"></ng-icon>
25
+ }
26
+ </div>
27
+ </gn-ui-button>
28
+
29
+ <ng-template
30
+ cdkConnectedOverlay
31
+ [cdkConnectedOverlayOrigin]="overlayOrigin"
32
+ [cdkConnectedOverlayOpen]="overlayOpen"
33
+ [cdkConnectedOverlayPositions]="overlayPositions"
34
+ [cdkConnectedOverlayOffsetX]="overlayOffsetX"
35
+ (overlayOutsideClick)="closeOverlay()"
36
+ (detach)="closeOverlay()"
37
+ >
38
+ <div [style.width.px]="overlayWidth">
39
+ <gn-ui-contact-details [contact]="contact"></gn-ui-contact-details>
40
+ </div>
41
+ </ng-template>
@@ -0,0 +1,70 @@
1
+ import { ConnectedPosition, OverlayModule } from '@angular/cdk/overlay'
2
+ import {
3
+ ChangeDetectionStrategy,
4
+ Component,
5
+ ElementRef,
6
+ inject,
7
+ Input,
8
+ } from '@angular/core'
9
+ import { Individual } from '../../../../../../libs/common/domain/src/lib/model/record'
10
+ import { NgIcon, provideIcons } from '@ng-icons/core'
11
+ import { matClose } from '@ng-icons/material-icons/baseline'
12
+ import { matInfoOutline } from '@ng-icons/material-icons/outline'
13
+ import { ButtonComponent } from '../../../../../../libs/ui/inputs/src'
14
+ import { getIndividualDisplayName } from '../../../../../../libs/util/shared/src'
15
+ import { ContactDetailsComponent } from '../contact-details/contact-details.component'
16
+
17
+ @Component({
18
+ selector: 'gn-ui-contact-pill',
19
+ templateUrl: './contact-pill.component.html',
20
+ changeDetection: ChangeDetectionStrategy.OnPush,
21
+ standalone: true,
22
+ imports: [NgIcon, ButtonComponent, OverlayModule, ContactDetailsComponent],
23
+ viewProviders: [provideIcons({ matClose, matInfoOutline })],
24
+ })
25
+ export class ContactPillComponent {
26
+ @Input() contact: Individual
27
+
28
+ private host = inject(ElementRef<HTMLElement>)
29
+
30
+ overlayOpen = false
31
+ overlayWidth = 0
32
+ overlayOffsetX = 0
33
+ overlayPositions: ConnectedPosition[] = [
34
+ {
35
+ originX: 'start',
36
+ originY: 'bottom',
37
+ overlayX: 'start',
38
+ overlayY: 'top',
39
+ offsetY: 4,
40
+ },
41
+ {
42
+ originX: 'start',
43
+ originY: 'top',
44
+ overlayX: 'start',
45
+ overlayY: 'bottom',
46
+ offsetY: -4,
47
+ },
48
+ ]
49
+
50
+ get displayName(): string {
51
+ return getIndividualDisplayName(this.contact)
52
+ }
53
+
54
+ toggleOverlay() {
55
+ if (!this.overlayOpen) {
56
+ // Calculate the width and horizontal offset of the overlay to align it with the parent element
57
+ const parent =
58
+ this.host.nativeElement.parentElement.getBoundingClientRect()
59
+ const pill = this.host.nativeElement.getBoundingClientRect()
60
+ this.overlayWidth = parent.width
61
+ this.overlayOffsetX = parent.left - pill.left
62
+ }
63
+
64
+ this.overlayOpen = !this.overlayOpen
65
+ }
66
+
67
+ closeOverlay() {
68
+ this.overlayOpen = false
69
+ }
70
+ }
@@ -54,8 +54,9 @@
54
54
  class="inline-flex items-center justify-center px-2 py-1 text-13 font-medium leading-none text-white rounded transition-opacity opacity-70 group-hover:opacity-100"
55
55
  [style.background-color]="getLinkColor(link)"
56
56
  data-cy="download-format"
57
+ translate
57
58
  >
58
- {{ getLinkFormat(link) || ('downloads.format.unknown' | translate) }}
59
+ downloads.format.{{ getLinkFormat(link) || 'unknown' }}
59
60
  </span>
60
61
  </div>
61
62
  </div>
@@ -9,7 +9,7 @@ import {
9
9
  import { matOpenInNew } from '@ng-icons/material-icons/baseline'
10
10
  import { iconoirDatabase } from '@ng-icons/iconoir'
11
11
  import { getBadgeColor, getFileFormat } from '../../../../../../libs/util/shared/src'
12
- import { TranslatePipe } from '@ngx-translate/core'
12
+ import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
13
13
 
14
14
  type CardSize = 'L' | 'M' | 'S' | 'XS'
15
15
  @Component({
@@ -18,7 +18,7 @@ type CardSize = 'L' | 'M' | 'S' | 'XS'
18
18
  styleUrls: ['./external-link-card.component.css'],
19
19
  changeDetection: ChangeDetectionStrategy.OnPush,
20
20
  standalone: true,
21
- imports: [CommonModule, NgIconComponent, TranslatePipe],
21
+ imports: [CommonModule, NgIconComponent, TranslateDirective, TranslatePipe],
22
22
  providers: [
23
23
  provideIcons({
24
24
  matOpenInNew,
@@ -0,0 +1,13 @@
1
+ /** the animation values are made to be identical to the cdk overlay values */
2
+ .lightbox-image {
3
+ animation: show 400ms cubic-bezier(0.25, 0.8, 0.25, 1) forwards;
4
+ opacity: 0;
5
+ transform: scale(0.9);
6
+ }
7
+
8
+ @keyframes show {
9
+ 100% {
10
+ opacity: 1;
11
+ transform: none;
12
+ }
13
+ }
@@ -1,5 +1,5 @@
1
1
  <gn-ui-content-ghost
2
- [showContent]="imageUrl !== undefined"
2
+ [showContent]="imageUrl() !== undefined"
3
3
  ghostClass="h-full w-full"
4
4
  >
5
5
  @if (imageUrl) {
@@ -9,7 +9,7 @@
9
9
  >
10
10
  <gn-ui-thumbnail
11
11
  class="relative h-full w-full"
12
- [thumbnailUrl]="imageUrl"
12
+ [thumbnailUrl]="imageUrl()"
13
13
  fit="cover"
14
14
  (placeholderShown)="isPlaceholderShown.emit($event)"
15
15
  ></gn-ui-thumbnail>
@@ -18,7 +18,7 @@
18
18
  class="absolute bottom-0 right-0 z-10 mr-2 mb-2"
19
19
  [type]="'outline'"
20
20
  [extraClass]="'!py-2 !px-0'"
21
- (buttonClick)="openLightbox(imageUrl)"
21
+ (buttonClick)="openLightbox()"
22
22
  >
23
23
  <ng-icon name="matZoomOutMap"></ng-icon>
24
24
  </gn-ui-button>
@@ -26,3 +26,13 @@
26
26
  </div>
27
27
  }
28
28
  </gn-ui-content-ghost>
29
+
30
+ <ng-template #imageTemplate>
31
+ <img
32
+ data-test="lightbox-image"
33
+ class="lightbox-image bg-white max-h-[90vh] max-w-[90vw]"
34
+ [src]="imageUrl()"
35
+ alt="Preview image"
36
+ (click)="closeLightbox()"
37
+ />
38
+ </ng-template>
@@ -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
  }
@@ -1,6 +1,6 @@
1
1
  <a
2
2
  [attr.href]="linkHref"
3
- [target]="linkTarget"
3
+ target="_self"
4
4
  class="record-card"
5
5
  [ngClass]="cardClass"
6
6
  >
@@ -58,7 +58,6 @@ export class InternalLinkCardComponent implements OnInit {
58
58
  protected elementRef = inject(ElementRef)
59
59
 
60
60
  @Input() record: CatalogRecord
61
- @Input() linkTarget = '_blank'
62
61
  @Input() linkHref: string = null
63
62
  @Input() metadataQualityDisplay: boolean
64
63
  @Input() favoriteTemplate: TemplateRef<{ $implicit: CatalogRecord }>
@@ -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
  }
@@ -21,7 +21,7 @@ import {
21
21
  matCallOutline,
22
22
  matLocationOnOutline,
23
23
  } from '@ng-icons/material-icons/outline'
24
-
24
+ import { getAddressLines } from '../../../../../../libs/util/shared/src'
25
25
  import { TranslateDirective } from '@ngx-translate/core'
26
26
 
27
27
  @Component({
@@ -59,10 +59,7 @@ export class MetadataContactComponent {
59
59
  }
60
60
 
61
61
  get address() {
62
- const addressParts = this.contacts[0].address
63
- .split(',')
64
- .map((part) => part.trim())
65
- return addressParts
62
+ return getAddressLines(this.contacts[0]?.address)
66
63
  }
67
64
 
68
65
  onOrganizationClick() {
@@ -17,6 +17,7 @@
17
17
  --gn-ui-badge-text-color: var(--color-primary-darkest);
18
18
  }
19
19
 
20
- :host .metadata-info-keywords ::ng-deep gn-ui-badge:hover {
21
- --gn-ui-badge-text-color: white;
20
+ .metadata-info-keywords {
21
+ --gn-ui-badge-padding: 0.25rem 0.5rem;
22
+ text-transform: lowercase;
22
23
  }