geonetwork-ui 2.10.0-dev.a9cc01fc7 → 2.10.0-dev.b6696be21

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 (85) hide show
  1. package/fesm2022/geonetwork-ui.mjs +624 -266
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +82 -23
  4. package/index.d.ts.map +1 -1
  5. package/package.json +3 -3
  6. package/src/libs/api/metadata-converter/src/lib/dcat-ap/dcat-ap.converter.ts +9 -0
  7. package/src/libs/api/metadata-converter/src/lib/fixtures/eu.dcat-ap.records.ts +2 -0
  8. package/src/libs/api/metadata-converter/src/lib/fixtures/generic.records.ts +1 -0
  9. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+ongules.ts +7 -0
  10. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+roilaye.ts +1 -0
  11. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.ts +1 -0
  12. package/src/libs/api/metadata-converter/src/lib/fixtures/geocat-ch.records.ts +1 -0
  13. package/src/libs/api/metadata-converter/src/lib/fixtures/georhena.records.ts +1 -0
  14. package/src/libs/api/metadata-converter/src/lib/fixtures/metadata-for-i18n.records.ts +2 -0
  15. package/src/libs/api/metadata-converter/src/lib/fixtures/metawal.records.ts +1 -0
  16. package/src/libs/api/metadata-converter/src/lib/fixtures/opendataswiss.records.ts +1 -0
  17. package/src/libs/api/metadata-converter/src/lib/fixtures/sextant.records.ts +2 -0
  18. package/src/libs/api/metadata-converter/src/lib/fixtures/vlaanderen.dcat-ap.records.ts +1 -0
  19. package/src/libs/api/metadata-converter/src/lib/fixtures/wallonie.records.reuse.ts +8 -0
  20. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.converter.ts +1 -0
  21. package/src/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.ts +7 -0
  22. package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +8 -0
  23. package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +8 -0
  24. package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +11 -0
  25. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +33 -0
  26. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +33 -0
  27. package/src/libs/api/repository/src/lib/gn4/auth/auth.service.ts +4 -0
  28. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +54 -32
  29. package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +46 -0
  30. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +11 -5
  31. package/src/libs/common/domain/src/lib/model/user/group.model.ts +8 -0
  32. package/src/libs/common/domain/src/lib/model/user/index.ts +1 -0
  33. package/src/libs/common/domain/src/lib/platform.service.interface.ts +2 -0
  34. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +5 -0
  35. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.css +0 -0
  36. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.html +67 -0
  37. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.ts +39 -0
  38. package/src/libs/feature/editor/src/lib/components/record-form/form-field/field-focus.directive.ts +38 -0
  39. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-constraints-shortcuts/form-field-constraints-shortcuts.component.ts +0 -1
  40. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.html +9 -2
  41. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts +12 -0
  42. 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
  43. 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
  44. 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
  45. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-topics/form-field-topics.component.ts +2 -2
  46. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.css +37 -0
  47. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +1 -0
  48. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +5 -0
  49. package/src/libs/feature/editor/src/lib/components/record-form/form-field/index.ts +1 -0
  50. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.css +0 -3
  51. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.html +0 -1
  52. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +27 -25
  53. package/src/libs/feature/editor/src/lib/fields.config.ts +60 -47
  54. package/src/libs/feature/editor/src/lib/models/editor-config.model.ts +4 -0
  55. package/src/libs/feature/editor/src/lib/services/editor.service.ts +1 -1
  56. package/src/libs/feature/notify-reuse/src/index.ts +1 -0
  57. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.css +0 -0
  58. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.html +1 -0
  59. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +21 -0
  60. package/src/libs/ui/elements/src/index.ts +1 -0
  61. package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.html +96 -0
  62. package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.ts +45 -0
  63. package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.html +29 -4
  64. package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.ts +51 -7
  65. package/src/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.ts +2 -5
  66. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +3 -3
  67. package/src/libs/ui/inputs/src/lib/button/button.component.ts +4 -0
  68. package/src/libs/ui/inputs/src/lib/url-input/url-input.component.html +2 -2
  69. package/src/libs/ui/inputs/src/lib/url-input/url-input.component.ts +2 -1
  70. package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +2 -1
  71. package/src/libs/ui/map/src/lib/components/spatial-extent/spatial-extent.component.ts +11 -10
  72. package/src/libs/util/app-config/src/lib/app-config.ts +49 -0
  73. package/src/libs/util/app-config/src/lib/model.ts +7 -0
  74. package/src/libs/util/app-config/src/lib/parse-utils.ts +50 -1
  75. package/src/libs/util/shared/src/lib/links/link-utils.ts +1 -1
  76. package/src/libs/util/shared/src/lib/utils/user-display.ts +9 -0
  77. package/tailwind.base.css +6 -0
  78. package/translations/de.json +28 -20
  79. package/translations/en.json +28 -20
  80. package/translations/es.json +28 -20
  81. package/translations/fr.json +28 -20
  82. package/translations/it.json +28 -20
  83. package/translations/nl.json +28 -20
  84. package/translations/pt.json +28 -20
  85. package/translations/sk.json +28 -20
@@ -0,0 +1,67 @@
1
+ <div class="flex flex-col gap-7">
2
+ <div class="grid grid-cols-2 gap-4">
3
+ <div class="min-w-0">
4
+ <h3 class="text-[16px] font-bold text-main mb-[12px]" translate>
5
+ editor.record.form.field.contactDetails.lastName
6
+ </h3>
7
+ <gn-ui-text-input
8
+ class="block w-full"
9
+ extraClass="w-full"
10
+ [(value)]="contact.lastName"
11
+ (valueChange)="emitContactChange()"
12
+ [placeholder]="
13
+ 'editor.record.form.field.contactDetails.lastName.placeholder'
14
+ | translate
15
+ "
16
+ data-test="contactDetailsLastName"
17
+ ></gn-ui-text-input>
18
+ </div>
19
+ <div class="min-w-0">
20
+ <h3 class="text-[16px] font-bold text-main mb-[12px]" translate>
21
+ editor.record.form.field.contactDetails.firstName
22
+ </h3>
23
+ <gn-ui-text-input
24
+ class="block w-full"
25
+ extraClass="w-full"
26
+ [(value)]="contact.firstName"
27
+ (valueChange)="emitContactChange()"
28
+ [placeholder]="
29
+ 'editor.record.form.field.contactDetails.firstName.placeholder'
30
+ | translate
31
+ "
32
+ data-test="contactDetailsFirstName"
33
+ ></gn-ui-text-input>
34
+ </div>
35
+ </div>
36
+ <div>
37
+ <h3 class="text-[16px] font-bold text-main mb-[12px]" translate>
38
+ editor.record.form.field.contactDetails.email
39
+ </h3>
40
+ <gn-ui-text-input
41
+ class="block w-full"
42
+ extraClass="w-full"
43
+ [value]="contact.organization?.email ?? ''"
44
+ (valueChange)="handleOrganizationChange({ email: $event })"
45
+ [placeholder]="
46
+ 'editor.record.form.field.contactDetails.email.placeholder' | translate
47
+ "
48
+ data-test="contactDetailsEmail"
49
+ ></gn-ui-text-input>
50
+ </div>
51
+ <div>
52
+ <h3 class="text-[16px] font-bold text-main mb-[12px]" translate>
53
+ editor.record.form.field.contactDetails.organization
54
+ </h3>
55
+ <gn-ui-text-input
56
+ class="block w-full"
57
+ extraClass="w-full"
58
+ [value]="contact.organization?.name ?? ''"
59
+ (valueChange)="handleOrganizationChange({ name: $event })"
60
+ [placeholder]="
61
+ 'editor.record.form.field.contactDetails.organization.placeholder'
62
+ | translate
63
+ "
64
+ data-test="contactDetailsOrganization"
65
+ ></gn-ui-text-input>
66
+ </div>
67
+ </div>
@@ -0,0 +1,39 @@
1
+ import {
2
+ ChangeDetectionStrategy,
3
+ Component,
4
+ EventEmitter,
5
+ Input,
6
+ Output,
7
+ } from '@angular/core'
8
+ import {
9
+ Individual,
10
+ Organization,
11
+ } from '../../../../../../../libs/common/domain/src/lib/model/record'
12
+ import { TextInputComponent } from '../../../../../../../libs/ui/inputs/src'
13
+ import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
14
+
15
+ @Component({
16
+ selector: 'gn-ui-contact-details-form',
17
+ templateUrl: './contact-details-form.component.html',
18
+ styleUrls: ['./contact-details-form.component.css'],
19
+ changeDetection: ChangeDetectionStrategy.OnPush,
20
+ standalone: true,
21
+ imports: [TextInputComponent, TranslateDirective, TranslatePipe],
22
+ })
23
+ export class ContactDetailsFormComponent {
24
+ @Input() contact: Individual
25
+ @Output() contactChange = new EventEmitter<Individual>()
26
+
27
+ emitContactChange() {
28
+ this.contactChange.emit(this.contact)
29
+ }
30
+
31
+ handleOrganizationChange(change: Partial<Organization>) {
32
+ this.contact.organization = {
33
+ ...(this.contact.organization ?? ({} as Organization)),
34
+ ...change,
35
+ } as Organization
36
+
37
+ this.emitContactChange()
38
+ }
39
+ }
@@ -0,0 +1,38 @@
1
+ import { Directive, ElementRef, inject, Input } from '@angular/core'
2
+
3
+ @Directive({
4
+ selector: '[gnUiFieldFocus]',
5
+ standalone: true,
6
+ exportAs: 'fieldFocus',
7
+ })
8
+ export class FieldFocusDirective {
9
+ @Input() gnUiFieldFocusGlowClass = 'gn-ui-field-focus-glow'
10
+
11
+ private el = inject(ElementRef)
12
+
13
+ public focusField() {
14
+ setTimeout(() => {
15
+ const host = this.el.nativeElement as HTMLElement
16
+ const glowClass = this.gnUiFieldFocusGlowClass
17
+
18
+ host.classList.remove(glowClass)
19
+ void host.offsetWidth
20
+ host.classList.add(glowClass)
21
+ host.addEventListener(
22
+ 'animationend',
23
+ () => host.classList.remove(glowClass),
24
+ { once: true }
25
+ )
26
+
27
+ host.scrollIntoView({ behavior: 'smooth', block: 'start' })
28
+ const target =
29
+ host.querySelector<HTMLElement>(
30
+ 'input, textarea, select, [contenteditable="true"]'
31
+ ) ??
32
+ host.querySelector<HTMLElement>(
33
+ 'button:not([disabled]), [tabindex]:not([tabindex="-1"])'
34
+ )
35
+ target?.focus({ preventScroll: true })
36
+ })
37
+ }
38
+ }
@@ -139,7 +139,6 @@ export class FormFieldConstraintsShortcutsComponent implements OnDestroy {
139
139
  this.editorFacade.setFieldVisibility({ model }, visible)
140
140
  })
141
141
  }
142
- hideEmptyConstraints(this.legalConstraints$, 'legalConstraints')
143
142
  hideEmptyConstraints(this.securityConstraints$, 'securityConstraints')
144
143
  hideEmptyConstraints(this.otherConstraints$, 'otherConstraints')
145
144
  })
@@ -15,8 +15,15 @@
15
15
  (itemsOrderChange)="handleContactsChanged($event)"
16
16
  [elementTemplate]="contactTemplate"
17
17
  ></gn-ui-sortable-list>
18
- <ng-template #contactTemplate let-contact>
19
- <gn-ui-contact-card [contact]="contact"></gn-ui-contact-card>
18
+ <ng-template #contactTemplate let-contact let-index="index">
19
+ @if (modelSpecifier === 'contact:editableDetails') {
20
+ <gn-ui-contact-details-form
21
+ [contact]="contact"
22
+ (contactChange)="handleContactChanged($event, index)"
23
+ ></gn-ui-contact-details-form>
24
+ } @else {
25
+ <gn-ui-contact-card [contact]="contact"></gn-ui-contact-card>
26
+ }
20
27
  </ng-template>
21
28
  } @else {
22
29
  <div
@@ -28,6 +28,7 @@ import { UserModel } from '../../../../../../../../../libs/common/domain/src/lib
28
28
  import { PlatformServiceInterface } from '../../../../../../../../../libs/common/domain/src/lib/platform.service.interface'
29
29
  import { OrganizationsServiceInterface } from '../../../../../../../../../libs/common/domain/src/lib/organizations.service.interface'
30
30
  import { ContactCardComponent } from '../../../contact-card/contact-card.component'
31
+ import { ContactDetailsFormComponent } from '../../../contact-details/contact-details-form.component'
31
32
  import {
32
33
  createFuzzyFilter,
33
34
  getIndividualDisplayName,
@@ -35,6 +36,7 @@ import {
35
36
  } from '../../../../../../../../../libs/util/shared/src'
36
37
  import { map } from 'rxjs/operators'
37
38
  import { SortableListComponent } from '../../../../../../../../../libs/ui/layout/src'
39
+ import { FieldModelSpecifier } from '../../../../models/editor-config.model'
38
40
 
39
41
  @Component({
40
42
  selector: 'gn-ui-form-field-contacts',
@@ -47,6 +49,7 @@ import { SortableListComponent } from '../../../../../../../../../libs/ui/layout
47
49
  TranslateDirective,
48
50
  TranslatePipe,
49
51
  ContactCardComponent,
52
+ ContactDetailsFormComponent,
50
53
  SortableListComponent,
51
54
  ],
52
55
  })
@@ -56,6 +59,7 @@ export class FormFieldContactsComponent implements OnDestroy, OnChanges {
56
59
  private changeDetectorRef = inject(ChangeDetectorRef)
57
60
 
58
61
  @Input() value: Individual[]
62
+ @Input() modelSpecifier: FieldModelSpecifier
59
63
  @Output() valueChange: EventEmitter<Individual[]> = new EventEmitter()
60
64
 
61
65
  contacts: Individual[] = []
@@ -117,6 +121,14 @@ export class FormFieldContactsComponent implements OnDestroy, OnChanges {
117
121
  this.valueChange.emit(contacts)
118
122
  }
119
123
 
124
+ handleContactChanged(updatedContact: Individual, index: number) {
125
+ const contacts = this.contacts.map((contact, i) =>
126
+ i === index ? updatedContact : contact
127
+ )
128
+
129
+ this.handleContactsChanged(contacts)
130
+ }
131
+
120
132
  /**
121
133
  * gn-ui-autocomplete
122
134
  */
@@ -0,0 +1,5 @@
1
+ <gn-ui-url-input
2
+ [value]="displayUrl"
3
+ [showValidateButton]="false"
4
+ (valueChange)="handleUrlChange($event)"
5
+ ></gn-ui-url-input>
@@ -0,0 +1,89 @@
1
+ import {
2
+ ChangeDetectionStrategy,
3
+ Component,
4
+ EventEmitter,
5
+ Input,
6
+ Output,
7
+ inject,
8
+ } from '@angular/core'
9
+ import { marker } from '@biesbjerg/ngx-translate-extract-marker'
10
+ import {
11
+ OnlineLinkResource,
12
+ OnlineResource,
13
+ } from '../../../../../../../../../libs/common/domain/src/lib/model/record'
14
+ import { NotificationsService } from '../../../../../../../../../libs/feature/notifications/src'
15
+ import { UrlInputComponent } from '../../../../../../../../../libs/ui/inputs/src'
16
+ import { TranslateService } from '@ngx-translate/core'
17
+
18
+ marker('editor.record.form.field.onlineLinkageResource.defaultName')
19
+
20
+ @Component({
21
+ selector: 'gn-ui-form-field-online-single-link-resource',
22
+ templateUrl: './form-field-online-single-link-resource.component.html',
23
+ styleUrls: ['./form-field-online-single-link-resource.component.css'],
24
+ changeDetection: ChangeDetectionStrategy.OnPush,
25
+ standalone: true,
26
+ imports: [UrlInputComponent],
27
+ })
28
+ export class FormFieldOnlineSingleLinkResourceComponent {
29
+ private notificationsService = inject(NotificationsService)
30
+ private translateService = inject(TranslateService)
31
+
32
+ @Input() set value(onlineResources: Array<OnlineResource>) {
33
+ this.allResources = onlineResources ?? []
34
+ const firstResource = this.allResources[0]
35
+ this.displayUrl = firstResource?.url?.toString() ?? ''
36
+ }
37
+ @Output() valueChange: EventEmitter<Array<OnlineResource>> =
38
+ new EventEmitter()
39
+
40
+ private allResources: OnlineResource[] = []
41
+ displayUrl = ''
42
+
43
+ handleUrlChange(url: string | null) {
44
+ if (!url) return
45
+
46
+ try {
47
+ const parsedUrl = new URL(url)
48
+
49
+ if (this.allResources.length === 0) {
50
+ const defaultName = this.translateService.instant(
51
+ 'editor.record.form.field.onlineLinkageResource.defaultName'
52
+ )
53
+ const newResource: OnlineLinkResource = {
54
+ type: 'link',
55
+ url: parsedUrl,
56
+ name: defaultName,
57
+ }
58
+ this.valueChange.emit([newResource])
59
+ } else {
60
+ const updatedFirst: OnlineResource = {
61
+ ...this.allResources[0],
62
+ url: parsedUrl,
63
+ }
64
+ this.valueChange.emit([updatedFirst, ...this.allResources.slice(1)])
65
+ }
66
+ } catch (error) {
67
+ this.handleError(error as Error)
68
+ }
69
+ }
70
+
71
+ private handleError(error: Error) {
72
+ this.notificationsService.showNotification(
73
+ {
74
+ type: 'error',
75
+ title: this.translateService.instant(
76
+ 'editor.record.onlineResourceError.title'
77
+ ),
78
+ text: `${this.translateService.instant(
79
+ 'editor.record.onlineResourceError.body'
80
+ )} ${error.message}`,
81
+ closeMessage: this.translateService.instant(
82
+ 'editor.record.onlineResourceError.closeMessage'
83
+ ),
84
+ },
85
+ undefined,
86
+ error
87
+ )
88
+ }
89
+ }
@@ -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 { INSPIRE_TOPICS } from '../../../../fields.config'
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 = INSPIRE_TOPICS.map((topic) => {
25
+ availableTopics = ISO_TOPICS.map((topic) => {
26
26
  return {
27
27
  label: this.translateService.instant(topic.label),
28
28
  value: topic.value,
@@ -0,0 +1,37 @@
1
+ :host {
2
+ scroll-margin-top: 90px;
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
+ }
@@ -150,6 +150,7 @@
150
150
  <ng-container *ngSwitchCase="'contacts'">
151
151
  <gn-ui-form-field-contacts
152
152
  [value]="valueAsIndividuals"
153
+ [modelSpecifier]="modelSpecifier"
153
154
  (valueChange)="valueChange.emit($event)"
154
155
  ></gn-ui-form-field-contacts>
155
156
  </ng-container>
@@ -4,6 +4,7 @@ import {
4
4
  Component,
5
5
  ElementRef,
6
6
  EventEmitter,
7
+ inject,
7
8
  Input,
8
9
  Output,
9
10
  ViewChild,
@@ -22,6 +23,7 @@ import {
22
23
  import { FormFieldWrapperComponent } from '../../../../../../../../libs/ui/layout/src'
23
24
  import { TranslatePipe } from '@ngx-translate/core'
24
25
  import {
26
+ FieldFocusDirective,
25
27
  FormFieldDateComponent,
26
28
  FormFieldLicenseComponent,
27
29
  FormFieldTemporalExtentsComponent,
@@ -77,6 +79,7 @@ import { FormFieldTopicsComponent } from './form-field-topics/form-field-topics.
77
79
  FormFieldTopicsComponent,
78
80
  TextFieldModule,
79
81
  ],
82
+ hostDirectives: [FieldFocusDirective],
80
83
  })
81
84
  export class FormFieldComponent {
82
85
  @Input() uniqueIdentifier: string
@@ -92,6 +95,8 @@ export class FormFieldComponent {
92
95
  @ViewChild('titleInput') titleInput: ElementRef
93
96
  isOpenData = false
94
97
 
98
+ fieldFocus = inject(FieldFocusDirective)
99
+
95
100
  toggleIsOpenData(event: boolean) {
96
101
  this.isOpenData = event
97
102
  }
@@ -1,3 +1,4 @@
1
+ export * from './field-focus.directive'
1
2
  export * from './form-field-keywords/form-field-keywords.component'
2
3
  export * from './form-field-license/form-field-license.component'
3
4
  export * from './form-field-date/form-field-date.component'
@@ -1,3 +0,0 @@
1
- gn-ui-form-field {
2
- scroll-margin-top: 90px;
3
- }
@@ -24,7 +24,6 @@
24
24
  ) {
25
25
  @if (!field.config.hidden) {
26
26
  <gn-ui-form-field
27
- [id]="anchorIdPrefix + field.config.model"
28
27
  [ngClass]="
29
28
  field.config.gridColumnSpan === 1
30
29
  ? 'col-span-1'
@@ -2,10 +2,10 @@ import { CommonModule } from '@angular/common'
2
2
  import {
3
3
  ChangeDetectionStrategy,
4
4
  Component,
5
- ElementRef,
6
5
  inject,
7
6
  OnDestroy,
8
7
  OnInit,
8
+ viewChildren,
9
9
  } from '@angular/core'
10
10
  import { EditorFacade } from '../../+state/editor.facade'
11
11
  import { EditorFieldValue } from '../../models'
@@ -15,8 +15,9 @@ import {
15
15
  EditorFieldWithValue,
16
16
  EditorSectionWithValues,
17
17
  } from '../../+state/editor.models'
18
- import { filter, firstValueFrom, map, Subscription, switchMap } from 'rxjs'
18
+ import { firstValueFrom, map, Subscription, withLatestFrom } from 'rxjs'
19
19
  import { CatalogRecordKeys } from '../../../../../../../libs/common/domain/src/lib/model/record'
20
+ import { switchMap } from 'rxjs/operators'
20
21
 
21
22
  @Component({
22
23
  selector: 'gn-ui-record-form',
@@ -27,41 +28,42 @@ import { CatalogRecordKeys } from '../../../../../../../libs/common/domain/src/l
27
28
  imports: [CommonModule, FormFieldComponent, TranslateDirective],
28
29
  })
29
30
  export class RecordFormComponent implements OnInit, OnDestroy {
30
- anchorIdPrefix = 'gn-ui--field-'
31
31
  facade = inject(EditorFacade)
32
- private el = inject(ElementRef)
33
32
  subscription = new Subscription()
34
33
 
35
34
  recordUniqueIdentifier$ = this.facade.record$.pipe(
36
35
  map((record) => record.uniqueIdentifier)
37
36
  )
38
37
 
38
+ focusFieldWithPage$ = this.facade.focusedField$.pipe(
39
+ switchMap(
40
+ async (field) => [field, await this.getPageIndexForField(field)] as const
41
+ )
42
+ )
43
+
44
+ formFields = viewChildren(FormFieldComponent)
45
+
46
+ focusField(model: CatalogRecordKeys) {
47
+ const fields = this.formFields()
48
+ const field = fields.find((f) => f.model === model)
49
+ field?.fieldFocus.focusField()
50
+ }
51
+
39
52
  ngOnInit() {
40
53
  this.subscription.add(
41
- this.facade.focusedField$
54
+ this.focusFieldWithPage$
42
55
  .pipe(
43
- filter((field) => !!field),
44
- switchMap(async (field) => ({
45
- field: field as CatalogRecordKeys,
46
- pageIndex: await this.getPageIndexForField(
47
- field as CatalogRecordKeys
48
- ),
49
- }))
56
+ withLatestFrom(
57
+ this.facade.currentPage$,
58
+ ([field, fieldPage], currentPage) =>
59
+ [field, fieldPage, currentPage] as const
60
+ )
50
61
  )
51
- .subscribe(async ({ field, pageIndex }) => {
52
- const currentPage = await firstValueFrom(this.facade.currentPage$)
53
- if (pageIndex !== null && pageIndex !== currentPage) {
54
- this.facade.setCurrentPage(pageIndex)
55
- this.el.nativeElement.scrollIntoView({
56
- behavior: 'instant',
57
- block: 'start',
58
- })
62
+ .subscribe(([field, fieldPage, currentPage]) => {
63
+ if (fieldPage !== null && fieldPage !== currentPage) {
64
+ this.facade.setCurrentPage(fieldPage)
59
65
  }
60
- setTimeout(() =>
61
- document
62
- .getElementById(this.anchorIdPrefix + field)
63
- ?.scrollIntoView({ behavior: 'instant', block: 'start' })
64
- )
66
+ setTimeout(() => this.focusField(field))
65
67
  })
66
68
  )
67
69
  }