geonetwork-ui 2.4.0-dev.00c93bef → 2.4.0-dev.3c297076

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 (152) hide show
  1. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.mjs +5 -5
  2. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/read-parts.mjs +30 -2
  3. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/utils/geometry.mjs +31 -0
  4. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/write-parts.mjs +23 -1
  5. package/esm2022/libs/api/metadata-converter/src/lib/xml-utils.mjs +6 -1
  6. package/esm2022/libs/api/repository/src/lib/gn4/gn4-repository.mjs +31 -3
  7. package/esm2022/libs/common/domain/src/lib/model/record/metadata.model.mjs +1 -1
  8. package/esm2022/libs/common/domain/src/lib/repository/records-repository.interface.mjs +1 -1
  9. package/esm2022/libs/data-access/gn4/src/openapi/api/records.api.service.mjs +2 -6
  10. package/esm2022/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.mjs +131 -0
  11. package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.mjs +21 -0
  12. package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-temporal-extents/form-field-temporal-extents.component.mjs +7 -6
  13. package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.mjs +19 -9
  14. package/esm2022/libs/feature/editor/src/lib/fields.config.mjs +12 -2
  15. package/esm2022/libs/feature/map/src/lib/utils/map-utils.service.mjs +10 -5
  16. package/esm2022/libs/feature/search/src/lib/results-table/results-table-container.component.mjs +41 -7
  17. package/esm2022/libs/feature/search/src/lib/state/search.facade.mjs +6 -2
  18. package/esm2022/libs/ui/elements/src/index.mjs +2 -1
  19. package/esm2022/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.mjs +27 -0
  20. package/esm2022/libs/ui/elements/src/lib/markdown-editor/markdown-editor.component.mjs +4 -3
  21. package/esm2022/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.mjs +2 -2
  22. package/esm2022/libs/ui/elements/src/lib/metadata-info/metadata-info.component.mjs +3 -3
  23. package/esm2022/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.mjs +5 -11
  24. package/esm2022/libs/ui/elements/src/lib/metadata-quality-item/metadata-quality-item.component.mjs +3 -3
  25. package/esm2022/libs/ui/elements/src/lib/sortable-list/sortable-list.component.mjs +6 -3
  26. package/esm2022/libs/ui/elements/src/lib/ui-elements.module.mjs +5 -2
  27. package/esm2022/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.mjs +3 -3
  28. package/esm2022/libs/ui/layout/src/lib/interactive-table/interactive-table.component.mjs +3 -3
  29. package/esm2022/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.mjs +67 -0
  30. package/esm2022/libs/ui/search/src/lib/results-table/results-table.component.mjs +28 -8
  31. package/esm2022/libs/ui/widgets/src/index.mjs +2 -1
  32. package/esm2022/libs/ui/widgets/src/lib/popover/popover.component.mjs +68 -0
  33. package/esm2022/translations/de.json +18 -17
  34. package/esm2022/translations/en.json +11 -10
  35. package/esm2022/translations/es.json +10 -9
  36. package/esm2022/translations/fr.json +20 -19
  37. package/esm2022/translations/it.json +11 -10
  38. package/esm2022/translations/nl.json +10 -9
  39. package/esm2022/translations/pt.json +10 -9
  40. package/fesm2022/geonetwork-ui.mjs +734 -264
  41. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  42. package/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.d.ts.map +1 -1
  43. package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts +8 -1
  44. package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts.map +1 -1
  45. package/libs/api/metadata-converter/src/lib/iso19139/utils/geometry.d.ts +5 -0
  46. package/libs/api/metadata-converter/src/lib/iso19139/utils/geometry.d.ts.map +1 -0
  47. package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts +3 -1
  48. package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts.map +1 -1
  49. package/libs/api/metadata-converter/src/lib/xml-utils.d.ts +1 -0
  50. package/libs/api/metadata-converter/src/lib/xml-utils.d.ts.map +1 -1
  51. package/libs/api/repository/src/lib/gn4/gn4-repository.d.ts +7 -1
  52. package/libs/api/repository/src/lib/gn4/gn4-repository.d.ts.map +1 -1
  53. package/libs/common/domain/src/lib/model/record/metadata.model.d.ts +2 -1
  54. package/libs/common/domain/src/lib/model/record/metadata.model.d.ts.map +1 -1
  55. package/libs/common/domain/src/lib/repository/records-repository.interface.d.ts +17 -0
  56. package/libs/common/domain/src/lib/repository/records-repository.interface.d.ts.map +1 -1
  57. package/libs/data-access/gn4/src/openapi/api/records.api.service.d.ts.map +1 -1
  58. package/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.d.ts +27 -0
  59. package/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.d.ts.map +1 -0
  60. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.d.ts +11 -0
  61. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.d.ts.map +1 -0
  62. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-temporal-extents/form-field-temporal-extents.component.d.ts +3 -1
  63. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-temporal-extents/form-field-temporal-extents.component.d.ts.map +1 -1
  64. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.d.ts +5 -1
  65. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.d.ts.map +1 -1
  66. package/libs/feature/editor/src/lib/fields.config.d.ts +1 -0
  67. package/libs/feature/editor/src/lib/fields.config.d.ts.map +1 -1
  68. package/libs/feature/map/src/lib/utils/map-utils.service.d.ts.map +1 -1
  69. package/libs/feature/search/src/lib/results-table/results-table-container.component.d.ts +14 -4
  70. package/libs/feature/search/src/lib/results-table/results-table-container.component.d.ts.map +1 -1
  71. package/libs/feature/search/src/lib/state/search.facade.d.ts +1 -0
  72. package/libs/feature/search/src/lib/state/search.facade.d.ts.map +1 -1
  73. package/libs/ui/elements/src/index.d.ts +1 -0
  74. package/libs/ui/elements/src/index.d.ts.map +1 -1
  75. package/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.d.ts +18 -0
  76. package/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.d.ts.map +1 -0
  77. package/libs/ui/elements/src/lib/downloads-list/downloads-list.component.d.ts +1 -1
  78. package/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.d.ts +0 -3
  79. package/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.d.ts.map +1 -1
  80. package/libs/ui/elements/src/lib/record-api-form/record-api-form.component.d.ts +1 -1
  81. package/libs/ui/elements/src/lib/sortable-list/sortable-list.component.d.ts +3 -2
  82. package/libs/ui/elements/src/lib/sortable-list/sortable-list.component.d.ts.map +1 -1
  83. package/libs/ui/elements/src/lib/ui-elements.module.d.ts +7 -6
  84. package/libs/ui/elements/src/lib/ui-elements.module.d.ts.map +1 -1
  85. package/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.d.ts +20 -0
  86. package/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.d.ts.map +1 -0
  87. package/libs/ui/search/src/lib/results-table/results-table.component.d.ts +9 -3
  88. package/libs/ui/search/src/lib/results-table/results-table.component.d.ts.map +1 -1
  89. package/libs/ui/widgets/src/index.d.ts +1 -0
  90. package/libs/ui/widgets/src/index.d.ts.map +1 -1
  91. package/libs/ui/widgets/src/lib/popover/popover.component.d.ts +19 -0
  92. package/libs/ui/widgets/src/lib/popover/popover.component.d.ts.map +1 -0
  93. package/package.json +1 -1
  94. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.ts +5 -1
  95. package/src/libs/api/metadata-converter/src/lib/fixtures/geocat-ch.records.ts +37 -12
  96. package/src/libs/api/metadata-converter/src/lib/fixtures/metawal.records.ts +5 -1
  97. package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +4 -2
  98. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +72 -2
  99. package/src/libs/api/metadata-converter/src/lib/iso19139/utils/geometry.ts +39 -0
  100. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +70 -1
  101. package/src/libs/api/metadata-converter/src/lib/xml-utils.ts +8 -0
  102. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +42 -2
  103. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +2 -1
  104. package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +22 -0
  105. package/src/libs/data-access/gn4/src/openapi/api/records.api.service.ts +1 -5
  106. package/src/libs/data-access/gn4/src/spec.yaml +0 -8
  107. package/src/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.html +2 -1
  108. package/src/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.ts +110 -19
  109. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.css +0 -0
  110. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.html +5 -0
  111. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts +22 -0
  112. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-temporal-extents/form-field-temporal-extents.component.ts +8 -7
  113. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +6 -0
  114. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +13 -1
  115. package/src/libs/feature/editor/src/lib/fields.config.ts +12 -1
  116. package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +8 -4
  117. package/src/libs/feature/search/src/lib/results-table/results-table-container.component.html +3 -1
  118. package/src/libs/feature/search/src/lib/results-table/results-table-container.component.ts +45 -3
  119. package/src/libs/feature/search/src/lib/state/search.facade.ts +6 -0
  120. package/src/libs/ui/elements/src/index.ts +1 -0
  121. package/src/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.css +0 -0
  122. package/src/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.html +12 -0
  123. package/src/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.ts +37 -0
  124. package/src/libs/ui/elements/src/lib/markdown-editor/markdown-editor.component.html +4 -1
  125. package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.css +2 -1
  126. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +12 -8
  127. package/src/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.html +14 -20
  128. package/src/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.ts +0 -10
  129. package/src/libs/ui/elements/src/lib/metadata-quality-item/metadata-quality-item.component.html +1 -1
  130. package/src/libs/ui/elements/src/lib/sortable-list/sortable-list.component.html +3 -1
  131. package/src/libs/ui/elements/src/lib/sortable-list/sortable-list.component.ts +6 -2
  132. package/src/libs/ui/elements/src/lib/ui-elements.module.ts +2 -1
  133. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.html +1 -1
  134. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.html +1 -0
  135. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.css +0 -0
  136. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html +26 -0
  137. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts +74 -0
  138. package/src/libs/ui/search/src/lib/results-table/results-table.component.html +15 -1
  139. package/src/libs/ui/search/src/lib/results-table/results-table.component.ts +26 -12
  140. package/src/libs/ui/widgets/src/index.ts +1 -0
  141. package/src/libs/ui/widgets/src/lib/popover/popover.component.css +0 -0
  142. package/src/libs/ui/widgets/src/lib/popover/popover.component.html +3 -0
  143. package/src/libs/ui/widgets/src/lib/popover/popover.component.ts +85 -0
  144. package/tailwind.base.css +2 -1
  145. package/translations/de.json +18 -17
  146. package/translations/en.json +11 -10
  147. package/translations/es.json +10 -9
  148. package/translations/fr.json +20 -19
  149. package/translations/it.json +11 -10
  150. package/translations/nl.json +10 -9
  151. package/translations/pt.json +10 -9
  152. package/translations/sk.json +11 -10
@@ -32,7 +32,7 @@ import { ProxyService } from '../../../../../../libs/util/shared/src'
32
32
  import { WmsEndpoint, WmtsEndpoint } from '@camptocamp/ogc-client'
33
33
  import { LONLAT_CRS_CODES } from '../constant/projections'
34
34
  import { fromEPSGCode, register } from 'ol/proj/proj4'
35
- import proj4 from 'proj4/dist/proj4'
35
+ import proj4 from 'proj4'
36
36
  import { defaults as defaultControls } from 'ol/control/defaults'
37
37
 
38
38
  const FEATURE_PROJECTION = 'EPSG:3857'
@@ -220,11 +220,15 @@ export class MapUtilsService {
220
220
  if (!('spatialExtents' in record) || record.spatialExtents.length === 0) {
221
221
  return null
222
222
  }
223
- // transform an array of geojson geometries into a bbox
223
+ // extend all the spatial extents into an including bbox
224
224
  const totalExtent = record.spatialExtents.reduce(
225
225
  (prev, curr) => {
226
- const geom = GEOJSON.readGeometry(curr.geometry)
227
- return extend(prev, geom.getExtent())
226
+ if ('bbox' in curr) return extend(prev, curr.bbox)
227
+ else if ('geometry' in curr) {
228
+ const geom = GEOJSON.readGeometry(curr.geometry)
229
+ return extend(prev, geom.getExtent())
230
+ }
231
+ return prev
228
232
  },
229
233
  [Infinity, Infinity, -Infinity, -Infinity]
230
234
  )
@@ -1,9 +1,11 @@
1
1
  <gn-ui-results-table
2
2
  [records]="records$ | async"
3
- [recordHasDraft]="hasDraft"
3
+ [hasDraft]="hasDraft"
4
4
  [selectedRecordsIdentifiers]="selectedRecords$ | async"
5
5
  [sortOrder]="sortBy$ | async"
6
6
  (recordClick)="handleRecordClick($event)"
7
+ (duplicateRecord)="handleDuplicateRecord($event)"
8
+ (deleteRecord)="handleDeleteRecord($event)"
7
9
  (recordsSelectedChange)="handleRecordsSelectedChange($event[0], $event[1])"
8
10
  (sortByChange)="handleSortByChange($event[0], $event[1])"
9
11
  ></gn-ui-results-table>
@@ -1,4 +1,4 @@
1
- import { Component, EventEmitter, Output } from '@angular/core'
1
+ import { Component, EventEmitter, OnDestroy, Output } from '@angular/core'
2
2
  import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
3
3
  import { SearchFacade } from '../state/search.facade'
4
4
  import { SelectionService } from '../../../../../../libs/api/repository/src'
@@ -6,6 +6,9 @@ import { SearchService } from '../utils/service/search.service'
6
6
  import { RecordsRepositoryInterface } from '../../../../../../libs/common/domain/src/lib/repository/records-repository.interface'
7
7
  import { ResultsTableComponent } from '../../../../../../libs/ui/search/src'
8
8
  import { CommonModule } from '@angular/common'
9
+ import { Subscription } from 'rxjs'
10
+ import { NotificationsService } from '../../../../../../libs/feature/notifications/src'
11
+ import { TranslateService } from '@ngx-translate/core'
9
12
 
10
13
  @Component({
11
14
  selector: 'gn-ui-results-table-container',
@@ -14,8 +17,11 @@ import { CommonModule } from '@angular/common'
14
17
  standalone: true,
15
18
  imports: [CommonModule, ResultsTableComponent],
16
19
  })
17
- export class ResultsTableContainerComponent {
20
+ export class ResultsTableContainerComponent implements OnDestroy {
18
21
  @Output() recordClick = new EventEmitter<CatalogRecord>()
22
+ @Output() duplicateRecord = new EventEmitter<CatalogRecord>()
23
+
24
+ subscription = new Subscription()
19
25
 
20
26
  records$ = this.searchFacade.results$
21
27
  selectedRecords$ = this.selectionService.selectedRecordsIdentifiers$
@@ -28,13 +34,45 @@ export class ResultsTableContainerComponent {
28
34
  private searchFacade: SearchFacade,
29
35
  private searchService: SearchService,
30
36
  private selectionService: SelectionService,
31
- private recordsRepository: RecordsRepositoryInterface
37
+ private recordsRepository: RecordsRepositoryInterface,
38
+ private notificationsService: NotificationsService,
39
+ private translateService: TranslateService
32
40
  ) {}
33
41
 
34
42
  handleRecordClick(item: unknown) {
35
43
  this.recordClick.emit(item as CatalogRecord)
36
44
  }
37
45
 
46
+ handleDuplicateRecord(item: unknown) {
47
+ this.duplicateRecord.emit(item as CatalogRecord)
48
+ }
49
+
50
+ async handleDeleteRecord(item: unknown) {
51
+ const uniqueIdentifier = (item as CatalogRecord).uniqueIdentifier
52
+ this.subscription.add(
53
+ this.recordsRepository.deleteRecord(uniqueIdentifier).subscribe({
54
+ next: () => {
55
+ this.recordsRepository.clearRecordDraft(uniqueIdentifier)
56
+ this.searchFacade.requestNewResults()
57
+ },
58
+ error: (error) => {
59
+ this.notificationsService.showNotification({
60
+ type: 'error',
61
+ title: this.translateService.instant(
62
+ 'editor.record.deleteError.title'
63
+ ),
64
+ text: `${this.translateService.instant(
65
+ 'editor.record.deleteError.body'
66
+ )} ${error}`,
67
+ closeMessage: this.translateService.instant(
68
+ 'editor.record.deleteError.closeMessage'
69
+ ),
70
+ })
71
+ },
72
+ })
73
+ )
74
+ }
75
+
38
76
  handleSortByChange(col: string, order: 'asc' | 'desc') {
39
77
  this.searchService.setSortBy([order, col])
40
78
  }
@@ -46,4 +84,8 @@ export class ResultsTableContainerComponent {
46
84
  this.selectionService.selectRecords(records)
47
85
  }
48
86
  }
87
+
88
+ ngOnDestroy() {
89
+ this.subscription.unsubscribe()
90
+ }
49
91
  }
@@ -8,6 +8,7 @@ import {
8
8
  Paginate,
9
9
  RequestMoreOnAggregation,
10
10
  RequestMoreResults,
11
+ RequestNewResults,
11
12
  SetConfigAggregations,
12
13
  SetConfigFilters,
13
14
  SetConfigRequestFields,
@@ -151,6 +152,11 @@ export class SearchFacade {
151
152
  return this
152
153
  }
153
154
 
155
+ requestNewResults(): SearchFacade {
156
+ this.store.dispatch(new RequestNewResults(this.searchId))
157
+ return this
158
+ }
159
+
154
160
  requestMoreOnAggregation(key: string, increment: number): SearchFacade {
155
161
  this.store.dispatch(
156
162
  new RequestMoreOnAggregation(key, increment, this.searchId)
@@ -1,5 +1,6 @@
1
1
  export * from './lib/api-card/api-card.component'
2
2
  export * from './lib/avatar/avatar.component'
3
+ export * from './lib/confirmation-dialog/confirmation-dialog.component'
3
4
  export * from './lib/content-ghost/content-ghost.component'
4
5
  export * from './lib/download-item/download-item.component'
5
6
  export * from './lib/downloads-list/downloads-list.component'
@@ -0,0 +1,12 @@
1
+ <h1 mat-dialog-title>{{ data.title }}</h1>
2
+ <div mat-dialog-content>{{ data.message }}</div>
3
+ <div mat-dialog-actions>
4
+ <gn-ui-button (buttonClick)="onCancel()">{{ data.cancelText }}</gn-ui-button>
5
+ <gn-ui-button
6
+ (buttonClick)="onConfirm()"
7
+ cdkFocusInitial
8
+ class="ml-2"
9
+ data-cy="confirm-button"
10
+ >{{ data.confirmText }}</gn-ui-button
11
+ >
12
+ </div>
@@ -0,0 +1,37 @@
1
+ import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'
2
+ import {
3
+ MAT_DIALOG_DATA,
4
+ MatDialogModule,
5
+ MatDialogRef,
6
+ } from '@angular/material/dialog'
7
+ import { ButtonComponent } from '../../../../../../libs/ui/inputs/src'
8
+
9
+ export interface ConfirmationDialogData {
10
+ title: string
11
+ message: string
12
+ confirmText: string
13
+ cancelText: string
14
+ }
15
+
16
+ @Component({
17
+ selector: 'gn-ui-confirmation-dialog',
18
+ templateUrl: './confirmation-dialog.component.html',
19
+ styleUrls: ['./confirmation-dialog.component.css'],
20
+ changeDetection: ChangeDetectionStrategy.OnPush,
21
+ standalone: true,
22
+ imports: [MatDialogModule, ButtonComponent],
23
+ })
24
+ export class ConfirmationDialogComponent {
25
+ constructor(
26
+ public dialogRef: MatDialogRef<ConfirmationDialogComponent>,
27
+ @Inject(MAT_DIALOG_DATA) public data: ConfirmationDialogData
28
+ ) {}
29
+
30
+ onConfirm() {
31
+ this.dialogRef.close(true)
32
+ }
33
+
34
+ onCancel() {
35
+ this.dialogRef.close(false)
36
+ }
37
+ }
@@ -1,5 +1,8 @@
1
1
  <div class="h-full flex flex-col">
2
- <p class="flex-none mb-2 font-medium text-sm text-gray-900">
2
+ <p
3
+ *ngIf="helperText"
4
+ class="flex-none mb-2 font-medium text-sm text-gray-900"
5
+ >
3
6
  {{ helperText }}
4
7
  </p>
5
8
  <div class="flex-1" [hidden]="preview">
@@ -2,9 +2,10 @@
2
2
  :host ::ng-deep .markdown-body {
3
3
  -ms-text-size-adjust: 100%;
4
4
  -webkit-text-size-adjust: 100%;
5
- margin: 0px 0px 1.5rem 0px;
5
+ margin: 0;
6
6
  line-height: 1.5;
7
7
  word-wrap: break-word;
8
+ height: 100%;
8
9
  }
9
10
 
10
11
  /** Emphasis **/
@@ -1,7 +1,7 @@
1
1
  <div class="mb-6 md-description sm:mb-4 sm:pr-16">
2
2
  <gn-ui-content-ghost ghostClass="h-32" [showContent]="fieldReady('abstract')">
3
3
  <gn-ui-max-lines [maxLines]="6" *ngIf="metadata.abstract">
4
- <div>
4
+ <div class="mb-6">
5
5
  <gn-ui-markdown-parser
6
6
  [textContent]="metadata.abstract"
7
7
  ></gn-ui-markdown-parser>
@@ -47,19 +47,23 @@
47
47
  </ng-template>
48
48
  </ng-container>
49
49
  <ng-container *ngIf="legalConstraints.length">
50
- <gn-ui-markdown-parser
51
- *ngFor="let constraint of legalConstraints"
52
- [textContent]="constraint"
53
- >
54
- </gn-ui-markdown-parser>
50
+ <div class="mb-6">
51
+ <gn-ui-markdown-parser
52
+ *ngFor="let constraint of legalConstraints"
53
+ [textContent]="constraint"
54
+ >
55
+ </gn-ui-markdown-parser>
56
+ </div>
55
57
  </ng-container>
56
58
  <ng-container *ngIf="otherConstraints.length">
57
59
  <div gnUiLinkify *ngFor="let constraint of otherConstraints">
58
60
  <h5 translate class="font-medium text-black text-sm mb-[2px] mt-[16px]">
59
61
  record.metadata.otherConstraints
60
62
  </h5>
61
- <gn-ui-markdown-parser [textContent]="constraint">
62
- </gn-ui-markdown-parser>
63
+ <div class="mb-6">
64
+ <gn-ui-markdown-parser [textContent]="constraint">
65
+ </gn-ui-markdown-parser>
66
+ </div>
63
67
  </div>
64
68
  </ng-container>
65
69
 
@@ -1,22 +1,16 @@
1
- <div
2
- *ngIf="metadataQualityDisplay"
3
- class="mb-6 metadata-quality"
4
- (mouseenter)="showMenu()"
5
- (mouseleave)="hideMenu()"
6
- >
7
- <div class="min-w-[200px]" [class]="smaller ? 'leading-[8px]' : ''">
8
- <gn-ui-progress-bar
9
- (focus)="showMenu()"
10
- (blur)="hideMenu()"
11
- tabindex="0"
12
- [value]="qualityScore"
13
- type="primary"
14
- ></gn-ui-progress-bar>
15
- </div>
16
- <div
17
- class="absolute z-10 bg-white border border-black border-opacity-35 rounded-lg shadow-lg p-5 whitespace-nowrap"
18
- [class]="isMenuShown ? 'block' : 'hidden'"
19
- >
1
+ <div *ngIf="metadataQualityDisplay" class="mb-6 metadata-quality">
2
+ <gn-ui-popover [content]="popoverItems" theme="light-border">
3
+ <div class="min-w-[200px]" [class]="smaller ? 'leading-[8px]' : ''">
4
+ <gn-ui-progress-bar
5
+ tabindex="0"
6
+ [value]="qualityScore"
7
+ type="primary"
8
+ ></gn-ui-progress-bar>
9
+ </div>
10
+ </gn-ui-popover>
11
+ </div>
12
+ <ng-template #popoverItems>
13
+ <div class="p-2 py-4">
20
14
  <div class="mb-4 font-bold" translate>record.metadata.quality.details</div>
21
15
  <gn-ui-metadata-quality-item
22
16
  *ngFor="let e of items"
@@ -24,4 +18,4 @@
24
18
  [value]="e.value"
25
19
  ></gn-ui-metadata-quality-item>
26
20
  </div>
27
- </div>
21
+ </ng-template>
@@ -21,8 +21,6 @@ export class MetadataQualityComponent implements OnChanges {
21
21
 
22
22
  items: MetadataQualityItem[] = []
23
23
 
24
- isMenuShown = false
25
-
26
24
  get qualityScore() {
27
25
  const qualityScore = this.metadata?.extras?.qualityScore
28
26
  return typeof qualityScore === 'number'
@@ -36,14 +34,6 @@ export class MetadataQualityComponent implements OnChanges {
36
34
  )
37
35
  }
38
36
 
39
- showMenu() {
40
- this.isMenuShown = true
41
- }
42
-
43
- hideMenu() {
44
- this.isMenuShown = false
45
- }
46
-
47
37
  private add(name: string, value: boolean) {
48
38
  if (this.metadataQualityDisplay?.[name] !== false) {
49
39
  this.items.push({ name, value })
@@ -1,4 +1,4 @@
1
1
  <div class="ml-4 flex flex-row">
2
- <mat-icon class="material-symbols-outlined">{{ icon }}</mat-icon>
2
+ <mat-icon class="material-symbols-outlined min-w-fit">{{ icon }}</mat-icon>
3
3
  <p class="ml-2 text">{{ labelKey | translate }}</p>
4
4
  </div>
@@ -12,7 +12,9 @@
12
12
  class="sortable-list flex flex-col gap-3 p-2"
13
13
  (cdkDropListDropped)="drop($event)"
14
14
  >
15
- <ng-container *ngFor="let element of elements; index as index">
15
+ <ng-container
16
+ *ngFor="let element of elements; index as index; trackBy: trackByFn"
17
+ >
16
18
  <div class="sortable-box bg-white flex items-center" cdkDrag>
17
19
  <span
18
20
  cdkDragHandle
@@ -18,8 +18,8 @@ import { MatIconModule } from '@angular/material/icon'
18
18
  import { ButtonComponent } from '../../../../../../libs/ui/inputs/src'
19
19
 
20
20
  type DynamicElement = {
21
- component: Type<any>
22
- inputs: Record<string, any>
21
+ component: Type<unknown>
22
+ inputs: Record<string, unknown>
23
23
  }
24
24
 
25
25
  @Component({
@@ -53,4 +53,8 @@ export class SortableListComponent {
53
53
  this.elements = this.elements.filter((_, i) => i !== index)
54
54
  this.elementsChange.emit(this.elements)
55
55
  }
56
+
57
+ trackByFn(index: number) {
58
+ return index
59
+ }
56
60
  }
@@ -9,7 +9,7 @@ import { ContentGhostComponent } from './content-ghost/content-ghost.component'
9
9
  import { DownloadItemComponent } from './download-item/download-item.component'
10
10
  import { DownloadsListComponent } from './downloads-list/downloads-list.component'
11
11
  import { ApiCardComponent } from './api-card/api-card.component'
12
- import { UiWidgetsModule } from '../../../../../libs/ui/widgets/src'
12
+ import { PopoverComponent, UiWidgetsModule } from '../../../../../libs/ui/widgets/src'
13
13
  import { MaxLinesComponent, UiLayoutModule } from '../../../../../libs/ui/layout/src'
14
14
  import { TranslateModule } from '@ngx-translate/core'
15
15
  import { RelatedRecordCardComponent } from './related-record-card/related-record-card.component'
@@ -45,6 +45,7 @@ import { TimeSincePipe } from './user-feedback-item/time-since.pipe'
45
45
  UiInputsModule,
46
46
  FormsModule,
47
47
  NgOptimizedImage,
48
+ PopoverComponent,
48
49
  MarkdownParserComponent,
49
50
  ThumbnailComponent,
50
51
  TimeSincePipe,
@@ -12,7 +12,7 @@
12
12
  </span>
13
13
  </div>
14
14
  </div>
15
- <div class="flex-1">
15
+ <div class="flex-1 overflow-y-auto">
16
16
  <ng-content></ng-content>
17
17
  </div>
18
18
  </div>
@@ -30,6 +30,7 @@
30
30
  class="contents text-gray-900 cursor-pointer group"
31
31
  *ngFor="let item of items"
32
32
  (click)="handleRowClick(item)"
33
+ data-cy="table-row"
33
34
  >
34
35
  <div
35
36
  class="relative h-0"
@@ -0,0 +1,26 @@
1
+ <gn-ui-button
2
+ type="light"
3
+ [matMenuTriggerFor]="menu"
4
+ (buttonClick)="openMenu()"
5
+ data-test="record-menu-button"
6
+ >
7
+ <mat-icon class="material-symbols-outlined">more_vert</mat-icon>
8
+ </gn-ui-button>
9
+ <mat-menu #menu="matMenu">
10
+ <button
11
+ mat-menu-item
12
+ [disabled]="!canDuplicate"
13
+ (click)="duplicate.emit()"
14
+ data-test="record-menu-duplicate-button"
15
+ >
16
+ <span translate>record.action.duplicate</span>
17
+ </button>
18
+ <button
19
+ mat-menu-item
20
+ [disabled]="!canDelete"
21
+ (click)="openDeleteConfirmationDialog()"
22
+ data-test="record-menu-delete-button"
23
+ >
24
+ <span translate>record.action.delete</span>
25
+ </button>
26
+ </mat-menu>
@@ -0,0 +1,74 @@
1
+ import {
2
+ Component,
3
+ EventEmitter,
4
+ Input,
5
+ Output,
6
+ ViewChild,
7
+ } from '@angular/core'
8
+ import { MatDialog, MatDialogModule } from '@angular/material/dialog'
9
+ import { MatIconModule } from '@angular/material/icon'
10
+ import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu'
11
+ import { ConfirmationDialogComponent } from '../../../../../../../libs/ui/elements/src'
12
+ import { ButtonComponent } from '../../../../../../../libs/ui/inputs/src'
13
+ import { TranslateModule, TranslateService } from '@ngx-translate/core'
14
+
15
+ @Component({
16
+ selector: 'gn-ui-action-menu',
17
+ templateUrl: './action-menu.component.html',
18
+ styleUrls: ['./action-menu.component.css'],
19
+ standalone: true,
20
+ imports: [
21
+ MatIconModule,
22
+ ButtonComponent,
23
+ MatMenuModule,
24
+ MatDialogModule,
25
+ ConfirmationDialogComponent,
26
+ TranslateModule,
27
+ ],
28
+ })
29
+ export class ActionMenuComponent {
30
+ @Input() canDuplicate: boolean
31
+ @Input() canDelete: boolean
32
+ @Output() duplicate = new EventEmitter<void>()
33
+ @Output() delete = new EventEmitter<void>()
34
+
35
+ @ViewChild(MatMenuTrigger) trigger: MatMenuTrigger
36
+
37
+ constructor(
38
+ public dialog: MatDialog,
39
+ private translateService: TranslateService
40
+ ) {}
41
+
42
+ openMenu() {
43
+ this.trigger.openMenu()
44
+ }
45
+
46
+ openDeleteConfirmationDialog() {
47
+ const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
48
+ data: {
49
+ title: this.translateService.instant(
50
+ 'editor.record.delete.confirmation.title'
51
+ ),
52
+ message: this.translateService.instant(
53
+ 'editor.record.delete.confirmation.message'
54
+ ),
55
+ confirmText: this.translateService.instant(
56
+ 'editor.record.delete.confirmation.confirmText'
57
+ ),
58
+ cancelText: this.translateService.instant(
59
+ 'editor.record.delete.confirmation.cancelText'
60
+ ),
61
+ },
62
+ restoreFocus: false,
63
+ })
64
+
65
+ // Manually restore focus to the menu trigger since the element that
66
+ // opens the dialog won't be in the DOM any more when the dialog closes.
67
+ dialogRef.afterClosed().subscribe((confirmed) => {
68
+ this.trigger.focus()
69
+ if (confirmed) {
70
+ this.delete.emit()
71
+ }
72
+ })
73
+ }
74
+ }
@@ -37,7 +37,7 @@
37
37
  <div class="flex flex-row items-center gap-2 max-w-full">
38
38
  <span class="overflow-hidden text-ellipsis">{{ item.title }}</span>
39
39
  <gn-ui-badge
40
- *ngIf="recordHasDraft(item)"
40
+ *ngIf="hasDraft(item)"
41
41
  [style.--gn-ui-badge-padding]="'0.4em 0.6em'"
42
42
  [style.--gn-ui-badge-text-color]="'#3d2006'"
43
43
  [style.--gn-ui-badge-background-color]="'#ffbc7b'"
@@ -120,4 +120,18 @@
120
120
  {{ dateToString(item.recordUpdated) }}
121
121
  </ng-template>
122
122
  </gn-ui-interactive-table-column>
123
+
124
+ <!-- ACTION MENU COLUMN -->
125
+ <gn-ui-interactive-table-column>
126
+ <ng-template #header> </ng-template>
127
+ <ng-template #cell let-item>
128
+ <gn-ui-action-menu
129
+ [canDuplicate]="canDuplicate(item)"
130
+ [canDelete]="canDelete(item)"
131
+ (duplicate)="handleDuplicate(item)"
132
+ (delete)="handleDelete(item)"
133
+ >
134
+ </gn-ui-action-menu>
135
+ </ng-template>
136
+ </gn-ui-interactive-table-column>
123
137
  </gn-ui-interactive-table>
@@ -1,23 +1,24 @@
1
+ import { CommonModule } from '@angular/common'
1
2
  import { Component, EventEmitter, Input, Output } from '@angular/core'
3
+ import { MatIconModule } from '@angular/material/icon'
2
4
  import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
3
5
  import {
4
- FileFormat,
5
- getBadgeColor,
6
- getFileFormat,
7
- getFormatPriority,
8
- } from '../../../../../../libs/util/shared/src'
6
+ FieldSort,
7
+ SortByField,
8
+ } from '../../../../../../libs/common/domain/src/lib/model/search'
9
9
  import { BadgeComponent, UiInputsModule } from '../../../../../../libs/ui/inputs/src'
10
10
  import {
11
11
  InteractiveTableColumnComponent,
12
12
  InteractiveTableComponent,
13
13
  } from '../../../../../../libs/ui/layout/src'
14
- import { MatIconModule } from '@angular/material/icon'
15
- import { TranslateModule } from '@ngx-translate/core'
16
- import { CommonModule } from '@angular/common'
17
14
  import {
18
- FieldSort,
19
- SortByField,
20
- } from '../../../../../../libs/common/domain/src/lib/model/search'
15
+ FileFormat,
16
+ getBadgeColor,
17
+ getFileFormat,
18
+ getFormatPriority,
19
+ } from '../../../../../../libs/util/shared/src'
20
+ import { TranslateModule } from '@ngx-translate/core'
21
+ import { ActionMenuComponent } from './action-menu/action-menu.component'
21
22
 
22
23
  @Component({
23
24
  selector: 'gn-ui-results-table',
@@ -32,17 +33,22 @@ import {
32
33
  MatIconModule,
33
34
  TranslateModule,
34
35
  BadgeComponent,
36
+ ActionMenuComponent,
35
37
  ],
36
38
  })
37
39
  export class ResultsTableComponent {
38
40
  @Input() records: CatalogRecord[] = []
39
41
  @Input() selectedRecordsIdentifiers: string[] = []
40
42
  @Input() sortOrder: SortByField = null
41
- @Input() recordHasDraft: (record: CatalogRecord) => boolean = () => false
43
+ @Input() hasDraft: (record: CatalogRecord) => boolean = () => false
44
+ @Input() canDuplicate: (record: CatalogRecord) => boolean = () => true
45
+ @Input() canDelete: (record: CatalogRecord) => boolean = () => true
42
46
 
43
47
  // emits the column (field) as well as the order
44
48
  @Output() sortByChange = new EventEmitter<[string, 'asc' | 'desc']>()
45
49
  @Output() recordClick = new EventEmitter<CatalogRecord>()
50
+ @Output() duplicateRecord = new EventEmitter<CatalogRecord>()
51
+ @Output() deleteRecord = new EventEmitter<CatalogRecord>()
46
52
  @Output() recordsSelectedChange = new EventEmitter<
47
53
  [CatalogRecord[], boolean]
48
54
  >()
@@ -89,6 +95,14 @@ export class ResultsTableComponent {
89
95
  this.recordClick.emit(item as CatalogRecord)
90
96
  }
91
97
 
98
+ handleDuplicate(item: unknown) {
99
+ this.duplicateRecord.emit(item as CatalogRecord)
100
+ }
101
+
102
+ handleDelete(item: unknown) {
103
+ this.deleteRecord.emit(item as CatalogRecord)
104
+ }
105
+
92
106
  setSortBy(col: string, order: 'asc' | 'desc') {
93
107
  this.sortByChange.emit([col, order])
94
108
  }
@@ -1,5 +1,6 @@
1
1
  export * from './lib/ui-widgets.module'
2
2
  export * from './lib/progress-bar/progress-bar.component'
3
+ export * from './lib/popover/popover.component'
3
4
  export * from './lib/loading-mask/loading-mask.component'
4
5
  export * from './lib/color-scale/color-scale.component'
5
6
  export * from './lib/popup-alert/popup-alert.component'
@@ -0,0 +1,3 @@
1
+ <span #popoverContent>
2
+ <ng-content></ng-content>
3
+ </span>