geonetwork-ui 2.10.0-dev.f143bfdfa → 2.10.0-dev.fb2893f44
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.
- package/fesm2022/geonetwork-ui.mjs +321 -5767
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +78 -63
- package/index.d.ts.map +1 -1
- package/material-styles.css +483 -0
- package/package.json +23 -33
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +2 -2
- package/src/libs/api/metadata-converter/src/lib/gn4/metadata-url.service.ts +2 -2
- package/src/libs/api/repository/src/lib/gn4/auth/auth.service.ts +4 -4
- package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +2 -2
- package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-groups.service.ts +1 -1
- package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.ts +1 -1
- package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +2 -2
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +0 -5
- package/src/libs/feature/catalog/src/lib/sources/sources.service.ts +5 -2
- package/src/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.ts +0 -2
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/field-focus.directive.ts +4 -1
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-constraints-shortcuts/form-field-constraints-shortcuts.component.ts +6 -7
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-topics/form-field-topics.component.ts +2 -2
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.css +0 -34
- package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.css +35 -0
- package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.html +5 -1
- package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +35 -19
- package/src/libs/feature/editor/src/lib/fields.config.ts +60 -47
- package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +33 -0
- package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +9 -1
- package/src/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.ts +2 -2
- package/src/libs/ui/dataviz/src/index.ts +0 -1
- package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.css +13 -0
- package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.html +13 -3
- package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.ts +43 -6
- package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.ts +2 -2
- package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +2 -2
- package/src/libs/ui/map/src/lib/components/spatial-extent/spatial-extent.component.ts +4 -54
- package/src/libs/ui/map/src/lib/map-utils.ts +48 -0
- package/src/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.ts +1 -1
- package/src/libs/util/app-config/src/lib/app-config.ts +21 -9
- package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +1 -2
- package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +6 -3
- package/src/libs/util/i18n/src/index.ts +0 -1
- package/src/libs/util/i18n/src/lib/i18n.constants.ts +17 -11
- package/src/libs/util/i18n/src/lib/i18n.interceptor.ts +2 -2
- package/src/libs/util/i18n/src/lib/i18n.providers.ts +14 -17
- package/src/libs/util/i18n/src/lib/test.translate.loader.ts +48 -0
- package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
- package/src/libs/util/shared/src/lib/utils/geojson.ts +58 -1
- package/style.css +0 -1
- package/tailwind.base.css +2 -0
- package/translations/de.json +19 -19
- package/translations/en.json +19 -19
- package/translations/es.json +19 -19
- package/translations/fr.json +19 -19
- package/translations/it.json +19 -19
- package/translations/nl.json +19 -19
- package/translations/pt.json +19 -19
- package/translations/sk.json +19 -19
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { inject, Injectable } from '@angular/core'
|
|
2
2
|
import { SourcesApiService } from '../../../../../../libs/data-access/gn4/src'
|
|
3
3
|
import { Observable } from 'rxjs'
|
|
4
4
|
import { filter, map, shareReplay } from 'rxjs/operators'
|
|
@@ -21,7 +21,10 @@ export class SourcesService {
|
|
|
21
21
|
return this.sources$.pipe(
|
|
22
22
|
map((sources) => sources.filter((source) => source.uuid === uuid)[0]),
|
|
23
23
|
filter((source) => !!source),
|
|
24
|
-
map(
|
|
24
|
+
map(
|
|
25
|
+
(source) =>
|
|
26
|
+
source.label[toLang3(this.translateService.getCurrentLang())]
|
|
27
|
+
)
|
|
25
28
|
)
|
|
26
29
|
}
|
|
27
30
|
}
|
package/src/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.ts
CHANGED
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
|
|
9
9
|
import { FormFieldRichComponent } from '../record-form/form-field/form-field-rich/form-field-rich.component'
|
|
10
10
|
import { ButtonComponent, UrlInputComponent } from '../../../../../../../libs/ui/inputs/src'
|
|
11
|
-
import { MatIconModule } from '@angular/material/icon'
|
|
12
11
|
import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
|
|
13
12
|
import { Constraint } from '../../../../../../../libs/common/domain/src/lib/model/record'
|
|
14
13
|
import {
|
|
@@ -26,7 +25,6 @@ import { marker } from '@biesbjerg/ngx-translate-extract-marker'
|
|
|
26
25
|
FormFieldRichComponent,
|
|
27
26
|
UrlInputComponent,
|
|
28
27
|
ButtonComponent,
|
|
29
|
-
MatIconModule,
|
|
30
28
|
TranslateDirective,
|
|
31
29
|
TranslatePipe,
|
|
32
30
|
NgIconComponent,
|
package/src/libs/feature/editor/src/lib/components/record-form/form-field/field-focus.directive.ts
CHANGED
|
@@ -10,7 +10,7 @@ export class FieldFocusDirective {
|
|
|
10
10
|
|
|
11
11
|
private el = inject(ElementRef)
|
|
12
12
|
|
|
13
|
-
public focusField() {
|
|
13
|
+
public focusField(focusInnerTarget = true) {
|
|
14
14
|
setTimeout(() => {
|
|
15
15
|
const host = this.el.nativeElement as HTMLElement
|
|
16
16
|
const glowClass = this.gnUiFieldFocusGlowClass
|
|
@@ -25,6 +25,9 @@ export class FieldFocusDirective {
|
|
|
25
25
|
)
|
|
26
26
|
|
|
27
27
|
host.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
28
|
+
if (!focusInnerTarget) {
|
|
29
|
+
return
|
|
30
|
+
}
|
|
28
31
|
const target =
|
|
29
32
|
host.querySelector<HTMLElement>(
|
|
30
33
|
'input, textarea, select, [contenteditable="true"]'
|
|
@@ -131,13 +131,12 @@ export class FormFieldConstraintsShortcutsComponent implements OnDestroy {
|
|
|
131
131
|
map((c) => c.length > 0),
|
|
132
132
|
distinctUntilChanged()
|
|
133
133
|
)
|
|
134
|
-
combineLatest([
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
})
|
|
134
|
+
combineLatest([isConstraintNotEmpty$, this.anyToggleActivated$])
|
|
135
|
+
.pipe(takeUntil(this.onDestroy$))
|
|
136
|
+
.subscribe(([isNotEmpty, anyToggleActivated]) => {
|
|
137
|
+
const visible = isNotEmpty && !anyToggleActivated
|
|
138
|
+
this.editorFacade.setFieldVisibility({ model }, visible)
|
|
139
|
+
})
|
|
141
140
|
}
|
|
142
141
|
hideEmptyConstraints(this.securityConstraints$, 'securityConstraints')
|
|
143
142
|
hideEmptyConstraints(this.otherConstraints$, 'otherConstraints')
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
DropdownMultiselectComponent,
|
|
6
6
|
} from '../../../../../../../../../libs/ui/inputs/src'
|
|
7
7
|
import { TranslatePipe, TranslateService } from '@ngx-translate/core'
|
|
8
|
-
import {
|
|
8
|
+
import { ISO_TOPICS } from '../../../../fields.config'
|
|
9
9
|
|
|
10
10
|
@Component({
|
|
11
11
|
selector: 'gn-ui-form-field-topics',
|
|
@@ -22,7 +22,7 @@ export class FormFieldTopicsComponent {
|
|
|
22
22
|
this.topics = topics
|
|
23
23
|
}
|
|
24
24
|
@Output() valueChange: EventEmitter<string[]> = new EventEmitter()
|
|
25
|
-
availableTopics =
|
|
25
|
+
availableTopics = ISO_TOPICS.map((topic) => {
|
|
26
26
|
return {
|
|
27
27
|
label: this.translateService.instant(topic.label),
|
|
28
28
|
value: topic.value,
|
package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.css
CHANGED
|
@@ -1,37 +1,3 @@
|
|
|
1
1
|
:host {
|
|
2
2
|
scroll-margin-top: 90px;
|
|
3
3
|
}
|
|
4
|
-
|
|
5
|
-
:host.gn-ui-field-focus-glow {
|
|
6
|
-
position: relative;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
:host.gn-ui-field-focus-glow::after {
|
|
10
|
-
content: '';
|
|
11
|
-
position: absolute;
|
|
12
|
-
inset: -8px;
|
|
13
|
-
border-radius: 8px;
|
|
14
|
-
background-color: color-mix(in srgb, var(--color-primary) 12%, transparent);
|
|
15
|
-
pointer-events: none;
|
|
16
|
-
animation: gn-ui-field-focus-glow 3s ease-out forwards;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
:host.gn-ui-field-focus-glow::before {
|
|
20
|
-
content: '';
|
|
21
|
-
position: absolute;
|
|
22
|
-
inset: -16px;
|
|
23
|
-
border: 2px dashed var(--color-primary);
|
|
24
|
-
border-radius: 10px;
|
|
25
|
-
pointer-events: none;
|
|
26
|
-
animation: gn-ui-field-focus-glow 3s ease-out forwards;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
@keyframes gn-ui-field-focus-glow {
|
|
30
|
-
0%,
|
|
31
|
-
55% {
|
|
32
|
-
opacity: 1;
|
|
33
|
-
}
|
|
34
|
-
100% {
|
|
35
|
-
opacity: 0;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/* Glow applied by FieldFocusDirective (gnUiFieldFocus) to a focused field or,
|
|
2
|
+
when the field is hidden, to its whole section. */
|
|
3
|
+
.gn-ui-field-focus-glow {
|
|
4
|
+
position: relative;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.gn-ui-field-focus-glow::after {
|
|
8
|
+
content: '';
|
|
9
|
+
position: absolute;
|
|
10
|
+
inset: -8px;
|
|
11
|
+
border-radius: 8px;
|
|
12
|
+
background-color: color-mix(in srgb, var(--color-primary) 12%, transparent);
|
|
13
|
+
pointer-events: none;
|
|
14
|
+
animation: gn-ui-field-focus-glow 3s ease-out forwards;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.gn-ui-field-focus-glow::before {
|
|
18
|
+
content: '';
|
|
19
|
+
position: absolute;
|
|
20
|
+
inset: -16px;
|
|
21
|
+
border: 2px dashed var(--color-primary);
|
|
22
|
+
border-radius: 10px;
|
|
23
|
+
pointer-events: none;
|
|
24
|
+
animation: gn-ui-field-focus-glow 3s ease-out forwards;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@keyframes gn-ui-field-focus-glow {
|
|
28
|
+
0%,
|
|
29
|
+
55% {
|
|
30
|
+
opacity: 1;
|
|
31
|
+
}
|
|
32
|
+
100% {
|
|
33
|
+
opacity: 0;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
track sectionTracker($index, section)
|
|
5
5
|
) {
|
|
6
6
|
@if (!section.hidden) {
|
|
7
|
-
<div
|
|
7
|
+
<div
|
|
8
|
+
class="flex flex-col gap-6 border p-8 rounded-[8px] shadow scroll-mt-[90px]"
|
|
9
|
+
gnUiFieldFocus
|
|
10
|
+
#sectionFocus="fieldFocus"
|
|
11
|
+
>
|
|
8
12
|
<div class="flex flex-col gap-2">
|
|
9
13
|
@if (section.labelKey) {
|
|
10
14
|
<div class="text-2xl font-title text-black" translate>
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from '@angular/core'
|
|
10
10
|
import { EditorFacade } from '../../+state/editor.facade'
|
|
11
11
|
import { EditorFieldValue } from '../../models'
|
|
12
|
-
import { FormFieldComponent } from './form-field'
|
|
12
|
+
import { FieldFocusDirective, FormFieldComponent } from './form-field'
|
|
13
13
|
import { TranslateDirective } from '@ngx-translate/core'
|
|
14
14
|
import {
|
|
15
15
|
EditorFieldWithValue,
|
|
@@ -25,7 +25,12 @@ import { switchMap } from 'rxjs/operators'
|
|
|
25
25
|
styleUrls: ['./record-form.component.css'],
|
|
26
26
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
27
27
|
standalone: true,
|
|
28
|
-
imports: [
|
|
28
|
+
imports: [
|
|
29
|
+
CommonModule,
|
|
30
|
+
FormFieldComponent,
|
|
31
|
+
FieldFocusDirective,
|
|
32
|
+
TranslateDirective,
|
|
33
|
+
],
|
|
29
34
|
})
|
|
30
35
|
export class RecordFormComponent implements OnInit, OnDestroy {
|
|
31
36
|
facade = inject(EditorFacade)
|
|
@@ -36,17 +41,22 @@ export class RecordFormComponent implements OnInit, OnDestroy {
|
|
|
36
41
|
)
|
|
37
42
|
|
|
38
43
|
focusFieldWithPage$ = this.facade.focusedField$.pipe(
|
|
39
|
-
switchMap(
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
switchMap(async (field) => {
|
|
45
|
+
const location = await this.getFieldLocation(field)
|
|
46
|
+
return [field, location?.page ?? null, location?.section ?? -1] as const
|
|
47
|
+
})
|
|
42
48
|
)
|
|
43
49
|
|
|
44
50
|
formFields = viewChildren(FormFieldComponent)
|
|
51
|
+
sectionFocusDirectives = viewChildren<FieldFocusDirective>('sectionFocus')
|
|
45
52
|
|
|
46
|
-
focusField(model: CatalogRecordKeys) {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
focusField(model: CatalogRecordKeys, sectionIndex: number) {
|
|
54
|
+
const field = this.formFields().find((f) => f.model === model)
|
|
55
|
+
if (field) {
|
|
56
|
+
field.fieldFocus.focusField()
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
this.sectionFocusDirectives()[sectionIndex]?.focusField(false)
|
|
50
60
|
}
|
|
51
61
|
|
|
52
62
|
ngOnInit() {
|
|
@@ -55,15 +65,15 @@ export class RecordFormComponent implements OnInit, OnDestroy {
|
|
|
55
65
|
.pipe(
|
|
56
66
|
withLatestFrom(
|
|
57
67
|
this.facade.currentPage$,
|
|
58
|
-
([field, fieldPage], currentPage) =>
|
|
59
|
-
[field, fieldPage, currentPage] as const
|
|
68
|
+
([field, fieldPage, fieldSection], currentPage) =>
|
|
69
|
+
[field, fieldPage, fieldSection, currentPage] as const
|
|
60
70
|
)
|
|
61
71
|
)
|
|
62
|
-
.subscribe(([field, fieldPage, currentPage]) => {
|
|
72
|
+
.subscribe(([field, fieldPage, fieldSection, currentPage]) => {
|
|
63
73
|
if (fieldPage !== null && fieldPage !== currentPage) {
|
|
64
74
|
this.facade.setCurrentPage(fieldPage)
|
|
65
75
|
}
|
|
66
|
-
setTimeout(() => this.focusField(field))
|
|
76
|
+
setTimeout(() => this.focusField(field, fieldSection))
|
|
67
77
|
})
|
|
68
78
|
)
|
|
69
79
|
}
|
|
@@ -87,13 +97,19 @@ export class RecordFormComponent implements OnInit, OnDestroy {
|
|
|
87
97
|
return section.labelKey
|
|
88
98
|
}
|
|
89
99
|
|
|
90
|
-
async
|
|
100
|
+
async getFieldLocation(
|
|
101
|
+
model: CatalogRecordKeys
|
|
102
|
+
): Promise<{ page: number; section: number } | null> {
|
|
91
103
|
const config = await firstValueFrom(this.facade.editorConfig$)
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
section.fields.some((field) => field.model === model)
|
|
95
|
-
)
|
|
104
|
+
const page = config.pages.findIndex((p) =>
|
|
105
|
+
p.sections.some((s) => s.fields.some((f) => f.model === model))
|
|
96
106
|
)
|
|
97
|
-
|
|
107
|
+
if (page < 0) {
|
|
108
|
+
return null
|
|
109
|
+
}
|
|
110
|
+
const section = config.pages[page].sections
|
|
111
|
+
.filter((s) => !s.hidden)
|
|
112
|
+
.findIndex((s) => s.fields.some((f) => f.model === model))
|
|
113
|
+
return { page, section }
|
|
98
114
|
}
|
|
99
115
|
}
|
|
@@ -4,10 +4,12 @@ import {
|
|
|
4
4
|
EditorField,
|
|
5
5
|
EditorSection,
|
|
6
6
|
} from './models/editor-config.model'
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
import { Keyword } from '../../../../../libs/common/domain/src/lib/model/record'
|
|
8
|
+
|
|
9
|
+
export interface ISOTopic {
|
|
10
|
+
value: string
|
|
11
|
+
label: string
|
|
12
|
+
}
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* This file contains the configuration of the fields that will be displayed in the editor.
|
|
@@ -359,75 +361,86 @@ export const SPATIAL_SCOPES: Keyword[] = [
|
|
|
359
361
|
]
|
|
360
362
|
|
|
361
363
|
/************************************************************
|
|
362
|
-
***************
|
|
364
|
+
*************** ISO TOPICS **************
|
|
363
365
|
************************************************************
|
|
364
366
|
*/
|
|
365
367
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
+
// label keys mirror the ISO MD_TopicCategoryCode value for consistency
|
|
369
|
+
// TODO: correctly handle code lists; for instance, this code list is specific to ISO 19139
|
|
370
|
+
export const ISO_TOPICS: ISOTopic[] = [
|
|
371
|
+
{ value: 'biota', label: marker('editor.record.form.topics.iso.biota') },
|
|
368
372
|
{
|
|
369
373
|
value: 'boundaries',
|
|
370
|
-
label: 'editor.record.form.topics.
|
|
374
|
+
label: marker('editor.record.form.topics.iso.boundaries'),
|
|
371
375
|
},
|
|
372
376
|
{
|
|
373
377
|
value: 'climatologyMeteorologyAtmosphere',
|
|
374
|
-
label:
|
|
378
|
+
label: marker(
|
|
379
|
+
'editor.record.form.topics.iso.climatologyMeteorologyAtmosphere'
|
|
380
|
+
),
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
value: 'economy',
|
|
384
|
+
label: marker('editor.record.form.topics.iso.economy'),
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
value: 'elevation',
|
|
388
|
+
label: marker('editor.record.form.topics.iso.elevation'),
|
|
375
389
|
},
|
|
376
|
-
{ value: 'economy', label: 'editor.record.form.topics.inspire.economy' },
|
|
377
|
-
{ value: 'elevation', label: 'editor.record.form.topics.inspire.elevation' },
|
|
378
390
|
{
|
|
379
391
|
value: 'environment',
|
|
380
|
-
label: 'editor.record.form.topics.
|
|
392
|
+
label: marker('editor.record.form.topics.iso.environment'),
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
value: 'farming',
|
|
396
|
+
label: marker('editor.record.form.topics.iso.farming'),
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
value: 'geoscientificInformation',
|
|
400
|
+
label: marker('editor.record.form.topics.iso.geoscientificInformation'),
|
|
381
401
|
},
|
|
382
|
-
{ value: 'farming', label: 'editor.record.form.topics.inspire.farming' },
|
|
383
402
|
{
|
|
384
|
-
value: '
|
|
385
|
-
label: 'editor.record.form.topics.
|
|
403
|
+
value: 'health',
|
|
404
|
+
label: marker('editor.record.form.topics.iso.health'),
|
|
386
405
|
},
|
|
387
|
-
{ value: 'health', label: 'editor.record.form.topics.inspire.health' },
|
|
388
406
|
{
|
|
389
407
|
value: 'imageryBaseMapsEarthCover',
|
|
390
|
-
label: 'editor.record.form.topics.
|
|
408
|
+
label: marker('editor.record.form.topics.iso.imageryBaseMapsEarthCover'),
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
value: 'inlandWaters',
|
|
412
|
+
label: marker('editor.record.form.topics.iso.inlandWaters'),
|
|
391
413
|
},
|
|
392
|
-
{ value: 'inlandWaters', label: 'editor.record.form.topics.inspire.waters' },
|
|
393
414
|
{
|
|
394
415
|
value: 'intelligenceMilitary',
|
|
395
|
-
label: 'editor.record.form.topics.
|
|
416
|
+
label: marker('editor.record.form.topics.iso.intelligenceMilitary'),
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
value: 'location',
|
|
420
|
+
label: marker('editor.record.form.topics.iso.location'),
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
value: 'oceans',
|
|
424
|
+
label: marker('editor.record.form.topics.iso.oceans'),
|
|
396
425
|
},
|
|
397
|
-
{ value: 'Location', label: 'editor.record.form.topics.inspire.location' },
|
|
398
|
-
{ value: 'Oceans', label: 'editor.record.form.topics.inspire.oceans' },
|
|
399
426
|
{
|
|
400
427
|
value: 'planningCadastre',
|
|
401
|
-
label: 'editor.record.form.topics.
|
|
428
|
+
label: marker('editor.record.form.topics.iso.planningCadastre'),
|
|
402
429
|
},
|
|
403
|
-
{ value: 'Society', label: 'editor.record.form.topics.inspire.society' },
|
|
404
|
-
{ value: 'Structure', label: 'editor.record.form.topics.inspire.structure' },
|
|
405
430
|
{
|
|
406
|
-
value: '
|
|
407
|
-
label: 'editor.record.form.topics.
|
|
431
|
+
value: 'society',
|
|
432
|
+
label: marker('editor.record.form.topics.iso.society'),
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
value: 'structure',
|
|
436
|
+
label: marker('editor.record.form.topics.iso.structure'),
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
value: 'transportation',
|
|
440
|
+
label: marker('editor.record.form.topics.iso.transportation'),
|
|
408
441
|
},
|
|
409
442
|
{
|
|
410
443
|
value: 'utilitiesCommunication',
|
|
411
|
-
label: 'editor.record.form.topics.
|
|
444
|
+
label: marker('editor.record.form.topics.iso.utilitiesCommunication'),
|
|
412
445
|
},
|
|
413
446
|
]
|
|
414
|
-
|
|
415
|
-
marker('editor.record.form.topics.inspire.biota')
|
|
416
|
-
marker('editor.record.form.topics.inspire.boundaries')
|
|
417
|
-
marker('editor.record.form.topics.inspire.climatology')
|
|
418
|
-
marker('editor.record.form.topics.inspire.economy')
|
|
419
|
-
marker('editor.record.form.topics.inspire.elevation')
|
|
420
|
-
marker('editor.record.form.topics.inspire.environnement')
|
|
421
|
-
marker('editor.record.form.topics.inspire.farming')
|
|
422
|
-
marker('editor.record.form.topics.inspire.geoscientific')
|
|
423
|
-
marker('editor.record.form.topics.inspire.health')
|
|
424
|
-
marker('editor.record.form.topics.inspire.imagery')
|
|
425
|
-
marker('editor.record.form.topics.inspire.intelligence')
|
|
426
|
-
marker('editor.record.form.topics.inspire.location')
|
|
427
|
-
marker('editor.record.form.topics.inspire.oceans')
|
|
428
|
-
marker('editor.record.form.topics.inspire.planning')
|
|
429
|
-
marker('editor.record.form.topics.inspire.society')
|
|
430
|
-
marker('editor.record.form.topics.inspire.structure')
|
|
431
|
-
marker('editor.record.form.topics.inspire.transportation')
|
|
432
|
-
marker('editor.record.form.topics.inspire.utilities')
|
|
433
|
-
marker('editor.record.form.topics.inspire.waters')
|
|
@@ -2,6 +2,23 @@ import { Injectable } from '@angular/core'
|
|
|
2
2
|
import { extend } from 'ol/extent.js'
|
|
3
3
|
import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
4
4
|
import { BoundingBox, getGeometryBoundingBox } from '../../../../../../libs/util/shared/src'
|
|
5
|
+
import { MapContextLayer } from '@geospatial-sdk/core'
|
|
6
|
+
import {
|
|
7
|
+
createSpatialExtentLayer,
|
|
8
|
+
SpatialExtentLayerStyle,
|
|
9
|
+
} from '../../../../../../libs/ui/map/src'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Style of the extent overlay drawn on top of the previewed data: a dashed
|
|
13
|
+
* black outline over a very light fill, so the extent stays readable without
|
|
14
|
+
* hiding the data underneath.
|
|
15
|
+
*/
|
|
16
|
+
const RECORD_EXTENT_OVERLAY_STYLE: SpatialExtentLayerStyle = {
|
|
17
|
+
'stroke-color': 'rgba(0, 0, 0, 0.6)',
|
|
18
|
+
'stroke-width': 2,
|
|
19
|
+
'stroke-line-dash': [8, 6],
|
|
20
|
+
'fill-color': 'rgba(0, 0, 0, 0.03)',
|
|
21
|
+
}
|
|
5
22
|
|
|
6
23
|
@Injectable({
|
|
7
24
|
providedIn: 'root',
|
|
@@ -26,4 +43,20 @@ export class MapUtilsService {
|
|
|
26
43
|
[Infinity, Infinity, -Infinity, -Infinity]
|
|
27
44
|
)
|
|
28
45
|
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Builds a non-interactive overlay layer drawing the spatial extent(s)
|
|
49
|
+
* declared in the record's metadata (bounding boxes and/or geometries).
|
|
50
|
+
* Returns null when the record has no usable spatial extent.
|
|
51
|
+
*
|
|
52
|
+
* This is purely for display and is independent from the map's initial view,
|
|
53
|
+
* which is derived separately (see {@link getRecordExtent}).
|
|
54
|
+
*/
|
|
55
|
+
getRecordExtentLayer(record: Partial<CatalogRecord>): MapContextLayer | null {
|
|
56
|
+
return createSpatialExtentLayer(record.spatialExtents ?? [], {
|
|
57
|
+
label: 'Spatial extent',
|
|
58
|
+
clickable: false,
|
|
59
|
+
style: RECORD_EXTENT_OVERLAY_STYLE,
|
|
60
|
+
})
|
|
61
|
+
}
|
|
29
62
|
}
|
|
@@ -431,11 +431,19 @@ export class MapViewComponent implements AfterViewInit {
|
|
|
431
431
|
}),
|
|
432
432
|
withLatestFrom(this.mdViewFacade.metadata$),
|
|
433
433
|
map(([context, metadata]) => {
|
|
434
|
-
|
|
434
|
+
// overlay the record's declared spatial extent on top of the data layers
|
|
435
|
+
const extentLayer = this.mapUtils.getRecordExtentLayer(metadata)
|
|
436
|
+
const layers = extentLayer
|
|
437
|
+
? [...context.layers, extentLayer]
|
|
438
|
+
: context.layers
|
|
439
|
+
// the view (initial zoom) is derived from the data layer or, as a
|
|
440
|
+
// fallback, from the record extent — independently from the overlay above
|
|
441
|
+
if (context.view) return { ...context, layers }
|
|
435
442
|
const extent = this.mapUtils.getRecordExtent(metadata)
|
|
436
443
|
const view = extent ? { extent } : null
|
|
437
444
|
return {
|
|
438
445
|
...context,
|
|
446
|
+
layers,
|
|
439
447
|
view,
|
|
440
448
|
}
|
|
441
449
|
}),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component,
|
|
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.
|
|
29
|
+
return this.translate.getCurrentLang()
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
changeLanguage(value: unknown) {
|
package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.css
CHANGED
|
@@ -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
|
+
}
|
package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.html
CHANGED
|
@@ -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(
|
|
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>
|
package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.ts
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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,5 +1,5 @@
|
|
|
1
1
|
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
|
2
|
-
import {
|
|
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
|
|
17
|
+
return parse(this.textContent ?? '')
|
|
18
18
|
}
|
|
19
19
|
}
|