geonetwork-ui 2.5.0-dev.e060bf76e → 2.5.0-dev.e122921ef
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/esm2022/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.mjs +2 -2
- package/esm2022/libs/api/repository/src/lib/gn4/gn4-repository.mjs +4 -8
- package/esm2022/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.mjs +14 -13
- package/esm2022/libs/feature/editor/src/index.mjs +2 -1
- package/esm2022/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.mjs +2 -2
- package/esm2022/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.mjs +2 -2
- package/esm2022/libs/feature/editor/src/lib/components/import-record/import-record.component.mjs +5 -5
- package/esm2022/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.mjs +5 -3
- package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.mjs +2 -2
- package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.mjs +9 -12
- package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-link-resources/form-field-online-link-resources.component.mjs +2 -2
- package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.mjs +2 -2
- package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.mjs +2 -2
- package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/index.mjs +2 -1
- package/esm2022/libs/feature/map/src/lib/add-layer-from-catalog/add-layer-from-catalog.component.mjs +1 -1
- package/esm2022/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.mjs +12 -3
- package/esm2022/libs/ui/dataviz/src/lib/data-table/data-table.component.mjs +3 -3
- package/esm2022/libs/ui/elements/src/lib/image-input/image-input.component.mjs +32 -31
- package/esm2022/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.mjs +23 -3
- package/esm2022/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.mjs +53 -7
- package/esm2022/libs/ui/inputs/src/lib/file-input/file-input.component.mjs +2 -2
- package/esm2022/libs/ui/inputs/src/lib/url-input/url-input.component.mjs +6 -4
- package/esm2022/libs/ui/search/src/lib/record-preview-row/record-preview-row.component.mjs +3 -3
- package/esm2022/libs/ui/widgets/src/lib/progress-bar/progress-bar.component.mjs +10 -3
- package/esm2022/libs/util/data-fetcher/src/lib/utils.mjs +5 -3
- package/esm2022/translations/de.json +1 -1
- package/esm2022/translations/en.json +1 -1
- package/esm2022/translations/fr.json +1 -1
- package/esm2022/translations/it.json +1 -1
- package/fesm2022/geonetwork-ui.mjs +181 -103
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/libs/api/repository/src/lib/gn4/gn4-repository.d.ts.map +1 -1
- package/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.d.ts.map +1 -1
- package/libs/feature/editor/src/index.d.ts +1 -0
- package/libs/feature/editor/src/index.d.ts.map +1 -1
- package/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.d.ts +1 -0
- package/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.d.ts.map +1 -1
- package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.d.ts +1 -0
- package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.d.ts.map +1 -1
- package/libs/feature/editor/src/lib/components/record-form/form-field/index.d.ts +1 -0
- package/libs/feature/editor/src/lib/components/record-form/form-field/index.d.ts.map +1 -1
- package/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.d.ts +4 -1
- package/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.d.ts.map +1 -1
- package/libs/ui/elements/src/lib/image-input/image-input.component.d.ts +6 -6
- package/libs/ui/elements/src/lib/image-input/image-input.component.d.ts.map +1 -1
- package/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.d.ts.map +1 -1
- package/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.d.ts +8 -1
- package/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.d.ts.map +1 -1
- package/libs/ui/inputs/src/lib/url-input/url-input.component.d.ts +2 -1
- package/libs/ui/inputs/src/lib/url-input/url-input.component.d.ts.map +1 -1
- package/libs/ui/widgets/src/lib/progress-bar/progress-bar.component.d.ts +1 -1
- package/libs/ui/widgets/src/lib/progress-bar/progress-bar.component.d.ts.map +1 -1
- package/libs/util/data-fetcher/src/lib/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +1 -1
- package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +3 -11
- package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +24 -26
- package/src/libs/common/fixtures/src/lib/records.fixtures.ts +90 -0
- package/src/libs/feature/editor/src/index.ts +1 -0
- package/src/libs/feature/editor/src/lib/components/import-record/import-record.component.ts +3 -3
- package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.html +1 -0
- package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.ts +2 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.html +1 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.ts +10 -11
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts +1 -1
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/index.ts +1 -0
- package/src/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.html +2 -0
- package/src/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.ts +6 -0
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.html +3 -1
- package/src/libs/ui/elements/src/lib/image-input/image-input.component.html +20 -15
- package/src/libs/ui/elements/src/lib/image-input/image-input.component.ts +30 -33
- package/src/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.html +21 -9
- package/src/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.ts +13 -0
- package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.html +36 -19
- package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts +43 -2
- package/src/libs/ui/inputs/src/lib/url-input/url-input.component.ts +3 -2
- package/src/libs/ui/search/src/lib/record-preview-row/record-preview-row.component.html +1 -1
- package/src/libs/ui/widgets/src/lib/progress-bar/progress-bar.component.html +36 -11
- package/src/libs/ui/widgets/src/lib/progress-bar/progress-bar.component.ts +9 -1
- package/src/libs/util/data-fetcher/src/lib/utils.ts +4 -2
- package/translations/de.json +1 -1
- package/translations/en.json +1 -1
- package/translations/fr.json +1 -1
- package/translations/it.json +1 -1
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
(itemSelected)="handleItemSelection($event)"
|
|
6
6
|
(inputSubmitted)="handleInputSubmission($event)"
|
|
7
7
|
(inputCleared)="handleInputCleared()"
|
|
8
|
+
(isSearchActive)="handleSearchActive($event)"
|
|
8
9
|
[value]="searchInputValue$ | async"
|
|
9
10
|
[preventCompleteOnSelection]="true"
|
|
10
11
|
[autoFocus]="autoFocus"
|
|
11
12
|
[allowSubmit]="true"
|
|
12
13
|
[forceTrackPosition]="forceTrackPosition"
|
|
14
|
+
[enterButton]="enterButton"
|
|
13
15
|
></gn-ui-autocomplete>
|
|
@@ -29,8 +29,10 @@ export class FuzzySearchComponent implements OnInit {
|
|
|
29
29
|
@ViewChild(AutocompleteComponent) autocomplete: AutocompleteComponent
|
|
30
30
|
@Input() autoFocus = false
|
|
31
31
|
@Input() forceTrackPosition = false
|
|
32
|
+
@Input() enterButton = false
|
|
32
33
|
@Output() itemSelected = new EventEmitter<CatalogRecord>()
|
|
33
34
|
@Output() inputSubmitted = new EventEmitter<string>()
|
|
35
|
+
@Output() isSearchActive = new EventEmitter<boolean>()
|
|
34
36
|
searchInputValue$: Observable<{ title: string }>
|
|
35
37
|
|
|
36
38
|
displayWithFn: (record: CatalogRecord) => string = (record) => record.title
|
|
@@ -85,4 +87,8 @@ export class FuzzySearchComponent implements OnInit {
|
|
|
85
87
|
this.searchService.updateFilters({ any: '' })
|
|
86
88
|
}
|
|
87
89
|
}
|
|
90
|
+
|
|
91
|
+
handleSearchActive(event: boolean) {
|
|
92
|
+
this.isSearchActive.emit(event)
|
|
93
|
+
}
|
|
88
94
|
}
|
|
@@ -32,7 +32,9 @@
|
|
|
32
32
|
mat-row
|
|
33
33
|
*matRowDef="let row; columns: properties"
|
|
34
34
|
(click)="selected.emit(row)"
|
|
35
|
-
[class.active]="
|
|
35
|
+
[class.active]="
|
|
36
|
+
activeId !== undefined && activeId !== null && row.id === activeId
|
|
37
|
+
"
|
|
36
38
|
></tr>
|
|
37
39
|
</table>
|
|
38
40
|
<gn-ui-loading-mask
|
|
@@ -11,10 +11,13 @@
|
|
|
11
11
|
[value]="altText ?? ''"
|
|
12
12
|
(valueChange)="handleAltTextChange($event)"
|
|
13
13
|
extraClass="gn-ui-editor-textarea"
|
|
14
|
-
[disabled]="true"
|
|
15
14
|
></gn-ui-text-input>
|
|
16
15
|
<div class="flex flex-row gap-2 mt-2">
|
|
17
|
-
<gn-ui-button
|
|
16
|
+
<gn-ui-button
|
|
17
|
+
type="gray"
|
|
18
|
+
(buttonClick)="handleDelete()"
|
|
19
|
+
data-cy="delete-image"
|
|
20
|
+
>
|
|
18
21
|
<ng-icon class="me-1 text-primary" name="iconoirBin"></ng-icon>
|
|
19
22
|
{{ 'input.image.delete' | translate }}
|
|
20
23
|
</gn-ui-button>
|
|
@@ -38,20 +41,17 @@
|
|
|
38
41
|
[ngClass]="{
|
|
39
42
|
'border-primary-lighter bg-primary-white': dragFilesOver,
|
|
40
43
|
'border-gray-300': !dragFilesOver,
|
|
41
|
-
'cursor-pointer hover:border-gray-500':
|
|
42
|
-
!isUploadInProgress && !uploadError && !showUrlInput && !disabled,
|
|
44
|
+
'cursor-pointer hover:border-gray-500': !getIsActionBlocked(),
|
|
43
45
|
'cursor-not-allowed': disabled,
|
|
44
46
|
}"
|
|
45
|
-
[attr.tabindex]="
|
|
46
|
-
isUploadInProgress || uploadError || showUrlInput || disabled ? null : 0
|
|
47
|
-
"
|
|
47
|
+
[attr.tabindex]="getIsActionBlocked() ? null : 0"
|
|
48
48
|
(keydown.enter)="fileInput.click()"
|
|
49
49
|
(dragFilesOver)="handleDragFilesOver($event)"
|
|
50
50
|
(dropFiles)="handleDropFiles($event)"
|
|
51
51
|
>
|
|
52
52
|
<div
|
|
53
53
|
class="w-14 h-14 rounded-md bg-gray-50 grid"
|
|
54
|
-
*ngIf="!isUploadInProgress && !
|
|
54
|
+
*ngIf="!isUploadInProgress && !imageFileError"
|
|
55
55
|
>
|
|
56
56
|
<ng-icon
|
|
57
57
|
*ngIf="!dragFilesOver"
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
</div>
|
|
67
67
|
|
|
68
68
|
<div
|
|
69
|
-
*ngIf="isUploadInProgress && !
|
|
69
|
+
*ngIf="isUploadInProgress && !imageFileError"
|
|
70
70
|
class="w-14 h-14 grid items-center justify-center relative"
|
|
71
71
|
>
|
|
72
72
|
<div class="text-gray-100 absolute">
|
|
@@ -88,19 +88,23 @@
|
|
|
88
88
|
<span class="text-sm text-main font-bold">{{ uploadProgress }}%</span>
|
|
89
89
|
</div>
|
|
90
90
|
|
|
91
|
-
<div class="w-14 h-14 rounded-md bg-gray-50 grid" *ngIf="
|
|
91
|
+
<div class="w-14 h-14 rounded-md bg-gray-50 grid" *ngIf="imageFileError">
|
|
92
92
|
<ng-icon
|
|
93
93
|
name="iconoirMediaImageXmark"
|
|
94
94
|
class="place-self-center text-rose-500"
|
|
95
|
+
data-cy="imgErrorIcon"
|
|
95
96
|
></ng-icon>
|
|
96
97
|
</div>
|
|
97
98
|
|
|
98
99
|
<div class="flex flex-col items-center gap-1">
|
|
99
|
-
<p class="font-medium"
|
|
100
|
+
<p class="font-medium" data-cy="imgInputMsgPrimary">
|
|
101
|
+
{{ getPrimaryText() | translate }}
|
|
102
|
+
</p>
|
|
100
103
|
<p
|
|
101
104
|
class="text-sm"
|
|
105
|
+
data-cy="imgInputMsgSecondary"
|
|
102
106
|
[class]="
|
|
103
|
-
isUploadInProgress
|
|
107
|
+
isUploadInProgress
|
|
104
108
|
? 'font-bold text-blue-500 cursor-pointer'
|
|
105
109
|
: 'font-medium text-gray-500'
|
|
106
110
|
"
|
|
@@ -111,17 +115,17 @@
|
|
|
111
115
|
</div>
|
|
112
116
|
<input
|
|
113
117
|
#fileInput
|
|
118
|
+
accept="image/*"
|
|
114
119
|
type="file"
|
|
115
120
|
class="hidden"
|
|
116
121
|
(change)="handleFileInput($event)"
|
|
117
|
-
[disabled]="
|
|
118
|
-
showUrlInput || isUploadInProgress || uploadError || disabled
|
|
119
|
-
"
|
|
122
|
+
[disabled]="isUploadInProgress || disabled"
|
|
120
123
|
/>
|
|
121
124
|
</label>
|
|
122
125
|
|
|
123
126
|
<div *ngIf="!showUrlInput" class="flex-none mt-2">
|
|
124
127
|
<gn-ui-button
|
|
128
|
+
data-cy="imgUrlBtn"
|
|
125
129
|
(buttonClick)="displayUrlInput()"
|
|
126
130
|
type="gray"
|
|
127
131
|
[disabled]="disabled"
|
|
@@ -135,6 +139,7 @@
|
|
|
135
139
|
<gn-ui-url-input
|
|
136
140
|
*ngIf="showUrlInput"
|
|
137
141
|
class="mt-3.5"
|
|
142
|
+
data-cy="imgUrlInput"
|
|
138
143
|
(uploadClick)="downloadUrl($event)"
|
|
139
144
|
[disabled]="isUploadInProgress || disabled"
|
|
140
145
|
>
|
|
@@ -80,11 +80,9 @@ export class ImageInputComponent {
|
|
|
80
80
|
|
|
81
81
|
dragFilesOver = false
|
|
82
82
|
showUrlInput = false
|
|
83
|
-
|
|
83
|
+
imageFileError = this.uploadError
|
|
84
84
|
showAltTextInput = false
|
|
85
|
-
|
|
86
|
-
lastUploadType?: 'file' | 'url'
|
|
87
|
-
lastUploadContent?: string | File
|
|
85
|
+
pendingAltText: string
|
|
88
86
|
|
|
89
87
|
get isUploadInProgress() {
|
|
90
88
|
return this.uploadProgress !== undefined
|
|
@@ -95,8 +93,12 @@ export class ImageInputComponent {
|
|
|
95
93
|
private cd: ChangeDetectorRef
|
|
96
94
|
) {}
|
|
97
95
|
|
|
96
|
+
getIsActionBlocked() {
|
|
97
|
+
return this.isUploadInProgress || this.disabled
|
|
98
|
+
}
|
|
99
|
+
|
|
98
100
|
getPrimaryText() {
|
|
99
|
-
if (this.
|
|
101
|
+
if (this.imageFileError) {
|
|
100
102
|
return marker('input.image.uploadErrorLabel')
|
|
101
103
|
}
|
|
102
104
|
if (this.uploadProgress) {
|
|
@@ -106,8 +108,8 @@ export class ImageInputComponent {
|
|
|
106
108
|
}
|
|
107
109
|
|
|
108
110
|
getSecondaryText() {
|
|
109
|
-
if (this.
|
|
110
|
-
return
|
|
111
|
+
if (this.imageFileError) {
|
|
112
|
+
return '\u00A0' // (only to keep same spacing, next step is to handle "Retry")
|
|
111
113
|
}
|
|
112
114
|
if (this.uploadProgress) {
|
|
113
115
|
return marker('input.image.uploadProgressCancel')
|
|
@@ -123,19 +125,24 @@ export class ImageInputComponent {
|
|
|
123
125
|
}
|
|
124
126
|
|
|
125
127
|
handleDropFiles(files: File[]) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
this.resetErrors()
|
|
129
|
+
const validFiles = this.filterTypeImage(files)
|
|
130
|
+
if (validFiles.length > 0) {
|
|
131
|
+
this.showUrlInput = false
|
|
132
|
+
this.resizeAndEmit(validFiles[0])
|
|
133
|
+
} else {
|
|
134
|
+
this.imageFileError = true
|
|
131
135
|
}
|
|
132
136
|
}
|
|
133
137
|
|
|
134
138
|
handleFileInput(event: Event) {
|
|
139
|
+
this.resetErrors()
|
|
135
140
|
const inputFiles = Array.from((event.target as HTMLInputElement).files)
|
|
136
141
|
const validFiles = this.filterTypeImage(inputFiles)
|
|
137
142
|
if (validFiles.length > 0) {
|
|
138
143
|
this.resizeAndEmit(validFiles[0])
|
|
144
|
+
} else {
|
|
145
|
+
this.imageFileError = true
|
|
139
146
|
}
|
|
140
147
|
}
|
|
141
148
|
|
|
@@ -145,9 +152,8 @@ export class ImageInputComponent {
|
|
|
145
152
|
}
|
|
146
153
|
|
|
147
154
|
async downloadUrl(url: string) {
|
|
148
|
-
this.
|
|
155
|
+
this.resetErrors()
|
|
149
156
|
const name = url.split('/').pop()
|
|
150
|
-
|
|
151
157
|
try {
|
|
152
158
|
const response = await firstValueFrom(
|
|
153
159
|
this.http.head(url, { observe: 'response' })
|
|
@@ -164,47 +170,39 @@ export class ImageInputComponent {
|
|
|
164
170
|
this.fileChange.emit(file)
|
|
165
171
|
},
|
|
166
172
|
error: () => {
|
|
167
|
-
this.
|
|
173
|
+
this.imageFileError = true
|
|
168
174
|
this.cd.markForCheck()
|
|
169
175
|
this.urlChange.emit(url)
|
|
170
176
|
},
|
|
171
177
|
})
|
|
172
178
|
}
|
|
173
179
|
} catch {
|
|
174
|
-
this.
|
|
180
|
+
this.imageFileError = true
|
|
175
181
|
this.cd.markForCheck()
|
|
176
182
|
return
|
|
177
183
|
}
|
|
178
184
|
}
|
|
179
185
|
|
|
180
186
|
handleSecondaryTextClick(event: Event) {
|
|
181
|
-
if (this.
|
|
182
|
-
this.
|
|
183
|
-
} else if (this.uploadProgress) {
|
|
184
|
-
this.handleCancel()
|
|
187
|
+
if (this.uploadProgress) {
|
|
188
|
+
this.handleCancelUpload()
|
|
185
189
|
event.preventDefault()
|
|
186
190
|
}
|
|
187
191
|
}
|
|
188
192
|
|
|
189
|
-
|
|
193
|
+
handleCancelUpload() {
|
|
190
194
|
this.uploadCancel.emit()
|
|
191
195
|
}
|
|
192
196
|
|
|
193
|
-
handleRetry() {
|
|
194
|
-
switch (this.lastUploadType) {
|
|
195
|
-
case 'file':
|
|
196
|
-
this.fileChange.emit(this.lastUploadContent as File)
|
|
197
|
-
break
|
|
198
|
-
case 'url':
|
|
199
|
-
this.urlChange.emit(this.lastUploadContent as string)
|
|
200
|
-
break
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
197
|
handleDelete() {
|
|
205
198
|
this.delete.emit()
|
|
206
199
|
}
|
|
207
200
|
|
|
201
|
+
resetErrors() {
|
|
202
|
+
this.imageFileError = false
|
|
203
|
+
this.uploadError = false
|
|
204
|
+
}
|
|
205
|
+
|
|
208
206
|
toggleAltTextInput() {
|
|
209
207
|
this.showAltTextInput = !this.showAltTextInput
|
|
210
208
|
}
|
|
@@ -212,7 +210,6 @@ export class ImageInputComponent {
|
|
|
212
210
|
handleAltTextChange(altText: string) {
|
|
213
211
|
this.altTextChange.emit(altText)
|
|
214
212
|
}
|
|
215
|
-
|
|
216
213
|
private filterTypeImage(files: File[]) {
|
|
217
214
|
return files.filter((file) => {
|
|
218
215
|
return file.type.startsWith('image/')
|
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
<div *ngIf="metadataQualityDisplay" class="mb-6 metadata-quality">
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
<div
|
|
3
|
+
class="flex items-center"
|
|
4
|
+
[class]="smaller ? 'leading-[8px] min-w-[120px]' : 'min-w-[200px]'"
|
|
5
|
+
>
|
|
6
|
+
<gn-ui-progress-bar
|
|
7
|
+
tabindex="0"
|
|
8
|
+
[value]="qualityScore"
|
|
9
|
+
[type]="'light'"
|
|
10
|
+
class="flex-grow"
|
|
11
|
+
></gn-ui-progress-bar>
|
|
12
|
+
<gn-ui-popover
|
|
13
|
+
[content]="popoverItems"
|
|
14
|
+
theme="light-border"
|
|
15
|
+
[class]="smaller ? 'ml-2' : 'ml-2 mt-1'"
|
|
16
|
+
>
|
|
17
|
+
<ng-icon
|
|
18
|
+
name="matInfoOutline"
|
|
19
|
+
class="flex-shrink-0 text-gray-600"
|
|
20
|
+
></ng-icon>
|
|
21
|
+
</gn-ui-popover>
|
|
22
|
+
</div>
|
|
11
23
|
</div>
|
|
12
24
|
<ng-template #popoverItems>
|
|
13
25
|
<div class="p-2 py-4">
|
|
@@ -16,6 +16,9 @@ import {
|
|
|
16
16
|
} from '../../../../../../libs/ui/widgets/src'
|
|
17
17
|
import { CommonModule } from '@angular/common'
|
|
18
18
|
import { TranslateModule } from '@ngx-translate/core'
|
|
19
|
+
import { NgIcon } from '@ng-icons/core'
|
|
20
|
+
import { matInfoOutline } from '@ng-icons/material-icons/outline'
|
|
21
|
+
import { provideIcons, provideNgIconsConfig } from '@ng-icons/core'
|
|
19
22
|
|
|
20
23
|
@Component({
|
|
21
24
|
selector: 'gn-ui-metadata-quality',
|
|
@@ -29,6 +32,16 @@ import { TranslateModule } from '@ngx-translate/core'
|
|
|
29
32
|
ProgressBarComponent,
|
|
30
33
|
MetadataQualityItemComponent,
|
|
31
34
|
TranslateModule,
|
|
35
|
+
NgIcon,
|
|
36
|
+
],
|
|
37
|
+
providers: [
|
|
38
|
+
provideIcons({
|
|
39
|
+
matInfoOutline,
|
|
40
|
+
}),
|
|
41
|
+
provideNgIconsConfig({
|
|
42
|
+
size: '1.2em',
|
|
43
|
+
strokeWidth: '1.5px',
|
|
44
|
+
}),
|
|
32
45
|
],
|
|
33
46
|
})
|
|
34
47
|
export class MetadataQualityComponent implements OnChanges {
|
|
@@ -5,31 +5,48 @@
|
|
|
5
5
|
>
|
|
6
6
|
<ng-icon name="iconoirSearch" class="text-primary search"></ng-icon>
|
|
7
7
|
</div>
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
<div class="flex flex-row">
|
|
9
|
+
<input
|
|
10
|
+
#searchInput
|
|
11
|
+
type="text"
|
|
12
|
+
class="gn-ui-text-input"
|
|
13
|
+
(input)="handleInput($event)"
|
|
14
|
+
[placeholder]="placeholder"
|
|
15
|
+
[formControl]="control"
|
|
16
|
+
[matAutocomplete]="auto"
|
|
17
|
+
(keyup.enter)="handleEnter(searchInput.value)"
|
|
18
|
+
[ngClass]="{
|
|
19
|
+
'text-primary': searchActive && enterButton,
|
|
20
|
+
'text-gray-900': !searchActive,
|
|
21
|
+
'px-[--icon-width]': !allowSubmit,
|
|
22
|
+
}"
|
|
23
|
+
/>
|
|
24
|
+
<gn-ui-button
|
|
25
|
+
type="gray"
|
|
26
|
+
*ngIf="searchInput.value && displayEnterBtn"
|
|
27
|
+
extraClass="w-32 h-8 !opacity-100"
|
|
28
|
+
[ngStyle]="{ left: enterBtnPosition + 'px' }"
|
|
29
|
+
class="absolute"
|
|
30
|
+
disabled="true"
|
|
31
|
+
>
|
|
32
|
+
<ng-icon
|
|
33
|
+
name="iconoirLongArrowDownLeft"
|
|
34
|
+
class="!w-4 text-gray-900 font-bold"
|
|
35
|
+
></ng-icon>
|
|
36
|
+
<span translate class="text-bold text-gray-900 font-bold">
|
|
37
|
+
Enter to search
|
|
38
|
+
</span>
|
|
39
|
+
</gn-ui-button>
|
|
40
|
+
</div>
|
|
20
41
|
<gn-ui-button
|
|
21
|
-
type="
|
|
22
|
-
extraClass="
|
|
23
|
-
allowSubmit
|
|
24
|
-
? 'right-[calc(var(--icon-width)+var(--icon-padding))]'
|
|
25
|
-
: 'right-[--icon-padding]'
|
|
26
|
-
}}"
|
|
42
|
+
type="primary"
|
|
43
|
+
[extraClass]="getExtraClass()"
|
|
27
44
|
data-test="clear-btn"
|
|
28
45
|
*ngIf="searchInput.value"
|
|
29
46
|
aria-label="Clear"
|
|
30
47
|
(buttonClick)="clear()"
|
|
31
48
|
>
|
|
32
|
-
<ng-icon name="matClose"></ng-icon>
|
|
49
|
+
<ng-icon class="text-white" name="matClose"></ng-icon>
|
|
33
50
|
</gn-ui-button>
|
|
34
51
|
<gn-ui-button
|
|
35
52
|
type="light"
|
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
provideIcons,
|
|
42
42
|
provideNgIconsConfig,
|
|
43
43
|
} from '@ng-icons/core'
|
|
44
|
-
import { iconoirSearch } from '@ng-icons/iconoir'
|
|
44
|
+
import { iconoirLongArrowDownLeft, iconoirSearch } from '@ng-icons/iconoir'
|
|
45
45
|
import { matClose } from '@ng-icons/material-icons/baseline'
|
|
46
46
|
|
|
47
47
|
export type AutocompleteItem = unknown
|
|
@@ -65,9 +65,10 @@ export type AutocompleteItem = unknown
|
|
|
65
65
|
provideIcons({
|
|
66
66
|
iconoirSearch,
|
|
67
67
|
matClose,
|
|
68
|
+
iconoirLongArrowDownLeft,
|
|
68
69
|
}),
|
|
69
70
|
provideNgIconsConfig({
|
|
70
|
-
size: '1.
|
|
71
|
+
size: '1.75rem',
|
|
71
72
|
}),
|
|
72
73
|
],
|
|
73
74
|
})
|
|
@@ -75,6 +76,7 @@ export class AutocompleteComponent
|
|
|
75
76
|
implements OnInit, AfterViewInit, OnDestroy, OnChanges
|
|
76
77
|
{
|
|
77
78
|
@Input() placeholder: string
|
|
79
|
+
@Input() enterButton = false
|
|
78
80
|
@Input() action: (value: string) => Observable<AutocompleteItem[]>
|
|
79
81
|
@Input() value?: AutocompleteItem
|
|
80
82
|
@Input() clearOnSelection = false
|
|
@@ -87,6 +89,7 @@ export class AutocompleteComponent
|
|
|
87
89
|
@Output() itemSelected = new EventEmitter<AutocompleteItem>()
|
|
88
90
|
@Output() inputSubmitted = new EventEmitter<string>()
|
|
89
91
|
@Output() inputCleared = new EventEmitter<void>()
|
|
92
|
+
@Output() isSearchActive = new EventEmitter<boolean>()
|
|
90
93
|
@ViewChild(MatAutocompleteTrigger) triggerRef: MatAutocompleteTrigger
|
|
91
94
|
@ViewChild(MatAutocomplete) autocomplete: MatAutocomplete
|
|
92
95
|
@ViewChild('searchInput') inputRef: ElementRef<HTMLInputElement>
|
|
@@ -101,15 +104,36 @@ export class AutocompleteComponent
|
|
|
101
104
|
subscription = new Subscription()
|
|
102
105
|
private lastPosition: DOMRect | null = null
|
|
103
106
|
private intervalIdPosition: number | undefined
|
|
107
|
+
enterBtnPosition = 0
|
|
108
|
+
searchActive = false
|
|
104
109
|
|
|
105
110
|
@Input() displayWithFn: (item: AutocompleteItem) => string = (item) =>
|
|
106
111
|
item.toString()
|
|
107
112
|
|
|
113
|
+
get displayEnterBtn() {
|
|
114
|
+
return this.enterButton && this.allowSubmit && !this.searchActive
|
|
115
|
+
}
|
|
116
|
+
|
|
108
117
|
displayWithFnInternal = (item?: AutocompleteItem) => {
|
|
109
118
|
if (item === null || item === undefined) return null
|
|
110
119
|
return this.displayWithFn(item)
|
|
111
120
|
}
|
|
112
121
|
|
|
122
|
+
getExtraClass(): string {
|
|
123
|
+
if (this.allowSubmit) {
|
|
124
|
+
if (this.enterButton) {
|
|
125
|
+
return 'border rounded-lg absolute w-8 h-8 right-[calc(var(--icon-width)+var(--icon-padding))] inset-y-[--icon-padding]'
|
|
126
|
+
} else {
|
|
127
|
+
return 'border rounded-lg absolute w-8 h-8 right-[calc(var(--icon-width)+0.25*var(--icon-width))] inset-y-[calc(0.25*var(--icon-width))]'
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
if (!this.enterButton) {
|
|
131
|
+
return 'border rounded-lg absolute w-8 h-8 right-2 inset-y-2'
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return 'border rounded-lg absolute w-8 h-8'
|
|
135
|
+
}
|
|
136
|
+
|
|
113
137
|
constructor(private cdRef: ChangeDetectorRef) {}
|
|
114
138
|
ngOnChanges(changes: SimpleChanges): void {
|
|
115
139
|
const { value } = changes
|
|
@@ -117,6 +141,13 @@ export class AutocompleteComponent
|
|
|
117
141
|
const previousTextValue = this.displayWithFnInternal(value.previousValue)
|
|
118
142
|
const currentTextValue = this.displayWithFnInternal(value.currentValue)
|
|
119
143
|
if (previousTextValue !== currentTextValue) {
|
|
144
|
+
if (currentTextValue) {
|
|
145
|
+
this.searchActive = true
|
|
146
|
+
this.isSearchActive.emit(true)
|
|
147
|
+
} else {
|
|
148
|
+
this.searchActive = false
|
|
149
|
+
this.isSearchActive.emit(false)
|
|
150
|
+
}
|
|
120
151
|
this.updateInputValue(value.currentValue)
|
|
121
152
|
}
|
|
122
153
|
}
|
|
@@ -256,6 +287,8 @@ export class AutocompleteComponent
|
|
|
256
287
|
|
|
257
288
|
clear(): void {
|
|
258
289
|
this.inputRef.nativeElement.value = ''
|
|
290
|
+
this.searchActive = false
|
|
291
|
+
this.isSearchActive.emit(false)
|
|
259
292
|
this.inputCleared.emit()
|
|
260
293
|
this.selectionSubject
|
|
261
294
|
.pipe(take(1))
|
|
@@ -265,6 +298,8 @@ export class AutocompleteComponent
|
|
|
265
298
|
|
|
266
299
|
handleEnter(any: string) {
|
|
267
300
|
if (!this.cancelEnter && this.allowSubmit) {
|
|
301
|
+
this.isSearchActive.emit(true)
|
|
302
|
+
this.searchActive = true
|
|
268
303
|
this.inputSubmitted.emit(any)
|
|
269
304
|
}
|
|
270
305
|
}
|
|
@@ -294,4 +329,10 @@ export class AutocompleteComponent
|
|
|
294
329
|
this.control.setValue('')
|
|
295
330
|
}
|
|
296
331
|
}
|
|
332
|
+
|
|
333
|
+
handleInput(event: InputEvent) {
|
|
334
|
+
this.searchActive = false
|
|
335
|
+
this.isSearchActive.emit(false)
|
|
336
|
+
this.enterBtnPosition = event.target['value'].length * 8 + 80
|
|
337
|
+
}
|
|
297
338
|
}
|
|
@@ -48,6 +48,7 @@ export class UrlInputComponent implements OnChanges {
|
|
|
48
48
|
@Input() placeholder = 'https://'
|
|
49
49
|
@Input() disabled: boolean
|
|
50
50
|
@Input() showValidateButton = true
|
|
51
|
+
@Input() resetUrlOnChange: number
|
|
51
52
|
|
|
52
53
|
/**
|
|
53
54
|
* This will emit null if the field is emptied
|
|
@@ -60,8 +61,8 @@ export class UrlInputComponent implements OnChanges {
|
|
|
60
61
|
constructor(private cd: ChangeDetectorRef) {}
|
|
61
62
|
|
|
62
63
|
ngOnChanges(changes: SimpleChanges) {
|
|
63
|
-
if (changes['
|
|
64
|
-
this.inputValue =
|
|
64
|
+
if (changes['resetUrlOnChange']) {
|
|
65
|
+
this.inputValue = ''
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
68
|
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
class="col-start-2 row-start-4 sm:row-start-3 absolute right-[4em] sm:right-[5em]"
|
|
65
65
|
>
|
|
66
66
|
<gn-ui-metadata-quality
|
|
67
|
-
smaller="true"
|
|
67
|
+
[smaller]="true"
|
|
68
68
|
[metadata]="record"
|
|
69
69
|
[metadataQualityDisplay]="metadataQualityDisplay"
|
|
70
70
|
></gn-ui-metadata-quality>
|
|
@@ -1,12 +1,37 @@
|
|
|
1
|
-
<
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class="flex
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
<ng-container [ngSwitch]="type">
|
|
2
|
+
<!-- Light Theme -->
|
|
3
|
+
<ng-container *ngSwitchCase="'light'">
|
|
4
|
+
<div class="flex items-center relative">
|
|
5
|
+
<div
|
|
6
|
+
class="flex-shrink-0 {{ color.text }} text-xs font-medium mr-2
|
|
7
|
+
text-opacity-100 !text-slate-800"
|
|
8
|
+
>
|
|
9
|
+
{{ progress }}%
|
|
10
|
+
</div>
|
|
11
|
+
<div class="flex-grow h-[6px] w-full {{ color.outerBar }} rounded-full">
|
|
12
|
+
<div
|
|
13
|
+
[style.width.%]="progress"
|
|
14
|
+
class="{{ color.innerBar }} transition-width duration-500
|
|
15
|
+
ease-in-out rounded-full shadow-sm h-full"
|
|
16
|
+
></div>
|
|
17
|
+
</div>
|
|
10
18
|
</div>
|
|
11
|
-
</
|
|
12
|
-
|
|
19
|
+
</ng-container>
|
|
20
|
+
|
|
21
|
+
<!-- Default / Primary / Secondary Themes -->
|
|
22
|
+
<ng-container *ngSwitchDefault>
|
|
23
|
+
<div class="flex h-full {{ color.outerBar }} rounded-t-lg rounded-b-lg">
|
|
24
|
+
<div
|
|
25
|
+
[style.width.%]="progress"
|
|
26
|
+
class="flex {{ color.innerBar }} my-1 mx-1 transition-width
|
|
27
|
+
duration-500 ease-in-out rounded-t-md rounded-b-md shadow-xl"
|
|
28
|
+
>
|
|
29
|
+
<div
|
|
30
|
+
class="flex items-center pl-2 py-1 {{ color.text }} font-bold text-4"
|
|
31
|
+
>
|
|
32
|
+
{{ progress }}%
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</ng-container>
|
|
37
|
+
</ng-container>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Component, Input } from '@angular/core'
|
|
2
|
+
import { NgSwitch, NgSwitchCase, NgSwitchDefault } from '@angular/common'
|
|
2
3
|
|
|
3
4
|
interface ColorScheme {
|
|
4
5
|
outerBar: string
|
|
@@ -11,10 +12,11 @@ interface ColorScheme {
|
|
|
11
12
|
templateUrl: './progress-bar.component.html',
|
|
12
13
|
styleUrls: ['./progress-bar.component.css'],
|
|
13
14
|
standalone: true,
|
|
15
|
+
imports: [NgSwitch, NgSwitchCase, NgSwitchDefault],
|
|
14
16
|
})
|
|
15
17
|
export class ProgressBarComponent {
|
|
16
18
|
@Input() value = 0
|
|
17
|
-
@Input() type: 'primary' | 'secondary' | 'default' = 'default'
|
|
19
|
+
@Input() type: 'primary' | 'secondary' | 'default' | 'light' = 'default'
|
|
18
20
|
|
|
19
21
|
get progress() {
|
|
20
22
|
return this.value > 0 ? (this.value < 100 ? this.value : 100) : 0
|
|
@@ -40,6 +42,12 @@ export class ProgressBarComponent {
|
|
|
40
42
|
innerBar: 'bg-secondary-lighter',
|
|
41
43
|
text: 'text-white',
|
|
42
44
|
}
|
|
45
|
+
case 'light':
|
|
46
|
+
return {
|
|
47
|
+
outerBar: 'bg-primary-white',
|
|
48
|
+
innerBar: 'bg-primary',
|
|
49
|
+
text: 'text-main',
|
|
50
|
+
}
|
|
43
51
|
}
|
|
44
52
|
}
|
|
45
53
|
}
|
|
@@ -62,9 +62,11 @@ export function fetchDataAsText(
|
|
|
62
62
|
})
|
|
63
63
|
.then(async (response) => {
|
|
64
64
|
if (!response.ok) {
|
|
65
|
-
|
|
65
|
+
const clonedResponse = response.clone()
|
|
66
|
+
throw FetchError.http(response.status, await clonedResponse.text())
|
|
66
67
|
}
|
|
67
|
-
|
|
68
|
+
const clonedResponse = response.clone()
|
|
69
|
+
return clonedResponse.text()
|
|
68
70
|
})
|
|
69
71
|
|
|
70
72
|
return cacheActive ? useCache(fetchFactory, url, 'asText') : fetchFactory()
|
package/translations/de.json
CHANGED
|
@@ -472,7 +472,7 @@
|
|
|
472
472
|
"search.error.receivedError": "Ein Fehler ist aufgetreten",
|
|
473
473
|
"search.error.recordHasnolink": "",
|
|
474
474
|
"search.error.recordNotFound": "Der Datensatz mit der Kennung \"{ id }\" konnte nicht gefunden werden.",
|
|
475
|
-
"search.field.any.placeholder": "Suche
|
|
475
|
+
"search.field.any.placeholder": "Suche im katalog ...",
|
|
476
476
|
"search.field.sortBy": "Sortieren nach:",
|
|
477
477
|
"search.filters.availableServices.download": "",
|
|
478
478
|
"search.filters.availableServices.view": "",
|