geonetwork-ui 2.11.0-dev.a83f0f51a → 2.11.0-dev.bc1a6d4e2
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 +1244 -677
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +219 -78
- package/index.d.ts.map +1 -1
- package/package.json +12 -11
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +43 -13
- package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +48 -7
- package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +40 -16
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +12 -0
- package/src/libs/common/domain/src/lib/model/search/filter.model.ts +13 -1
- package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +2 -2
- package/src/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts +3 -2
- package/src/libs/feature/dataviz/src/lib/geo-table-view/geo-table-view.component.ts +2 -1
- package/src/libs/feature/dataviz/src/lib/service/data.service.ts +22 -22
- package/src/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.html +1 -0
- package/src/libs/feature/notifications/src/lib/notifications.service.ts +6 -4
- package/src/libs/feature/record/src/lib/state/mdview.actions.ts +4 -8
- package/src/libs/feature/record/src/lib/state/mdview.effects.ts +7 -16
- package/src/libs/feature/record/src/lib/state/mdview.facade.ts +1 -3
- package/src/libs/feature/record/src/lib/state/mdview.reducer.ts +4 -9
- package/src/libs/feature/record/src/lib/state/mdview.selectors.ts +2 -7
- package/src/libs/feature/router/src/lib/default/state/query-params.utils.ts +1 -1
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +11 -4
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +58 -19
- package/src/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts +17 -8
- package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +3 -1
- package/src/libs/feature/search/src/lib/utils/service/fields.ts +27 -2
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.html +6 -7
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.ts +31 -19
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.fixtures.ts +2 -2
- package/src/libs/ui/inputs/src/index.ts +2 -0
- package/src/libs/ui/inputs/src/lib/date-adapter.providers.ts +30 -0
- package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +3 -24
- package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.css +37 -3
- package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.html +135 -16
- package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.ts +166 -29
- package/src/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.ts +3 -7
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.css +1 -0
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.html +20 -7
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.ts +46 -5
- package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.css +0 -0
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html +111 -0
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts +215 -0
- package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +6 -5
- package/src/libs/util/app-config/src/lib/app-config.ts +3 -0
- package/src/libs/util/app-config/src/lib/model.ts +1 -0
- package/src/libs/util/data-fetcher/src/lib/data-fetcher.ts +19 -22
- package/src/libs/util/data-fetcher/src/lib/engine/duckdb.ts +185 -0
- package/src/libs/util/data-fetcher/src/lib/engine/results.ts +63 -0
- package/src/libs/util/data-fetcher/src/lib/{sql-utils.ts → engine/sql-utils.ts} +28 -13
- package/src/libs/util/data-fetcher/src/lib/model.ts +2 -1
- package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +53 -38
- package/src/libs/util/data-fetcher/src/lib/readers/base.ts +11 -0
- package/src/libs/util/data-fetcher/src/lib/readers/csv.ts +9 -47
- package/src/libs/util/data-fetcher/src/lib/readers/excel.ts +27 -27
- package/src/libs/util/data-fetcher/src/lib/readers/geojson.ts +5 -24
- package/src/libs/util/data-fetcher/src/lib/readers/gml.ts +5 -49
- package/src/libs/util/data-fetcher/src/lib/readers/json.ts +5 -23
- package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +184 -128
- package/src/libs/util/data-fetcher/src/lib/utils.ts +0 -143
- package/src/libs/util/shared/src/index.ts +1 -0
- package/src/libs/util/shared/src/lib/autofocus.directive.ts +26 -0
- package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
- package/src/libs/util/shared/src/lib/utils/file.ts +15 -0
- package/src/libs/util/shared/src/lib/utils/geojson.ts +8 -0
- package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
- package/tailwind.base.css +5 -0
- package/translations/de.json +15 -2
- package/translations/en.json +14 -1
- package/translations/es.json +13 -0
- package/translations/fr.json +15 -2
- package/translations/it.json +13 -0
- package/translations/nl.json +13 -0
- package/translations/pt.json +13 -0
- package/translations/sk.json +13 -0
- package/src/libs/util/data-fetcher/src/lib/readers/base-cache.ts +0 -14
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
Choice,
|
|
10
10
|
DateRangeDropdownComponent,
|
|
11
11
|
DropdownMultiselectComponent,
|
|
12
|
+
SpatialExtentDropdownComponent,
|
|
13
|
+
SpatialExtentDropdownError,
|
|
12
14
|
} from '../../../../../../libs/ui/inputs/src'
|
|
13
15
|
import { Observable, of, switchMap } from 'rxjs'
|
|
14
16
|
import { catchError, filter, map, startWith } from 'rxjs/operators'
|
|
@@ -22,6 +24,10 @@ import {
|
|
|
22
24
|
} from '../utils/service/fields'
|
|
23
25
|
import { DateRange } from '../../../../../../libs/api/repository/src'
|
|
24
26
|
import { CommonModule } from '@angular/common'
|
|
27
|
+
import { BoundingBox } from '../../../../../../libs/util/shared/src'
|
|
28
|
+
import { NotificationsService } from '../../../../../../libs/feature/notifications/src'
|
|
29
|
+
import { TranslateService } from '@ngx-translate/core'
|
|
30
|
+
import { getOptionalSearchConfig } from '../../../../../../libs/util/app-config/src'
|
|
25
31
|
|
|
26
32
|
@Component({
|
|
27
33
|
selector: 'gn-ui-filter-dropdown',
|
|
@@ -33,18 +39,23 @@ import { CommonModule } from '@angular/common'
|
|
|
33
39
|
CommonModule,
|
|
34
40
|
DateRangeDropdownComponent,
|
|
35
41
|
DropdownMultiselectComponent,
|
|
42
|
+
SpatialExtentDropdownComponent,
|
|
36
43
|
],
|
|
37
44
|
})
|
|
38
45
|
export class FilterDropdownComponent implements OnInit {
|
|
39
46
|
private searchFacade = inject(SearchFacade)
|
|
40
47
|
private searchService = inject(SearchService)
|
|
41
48
|
private fieldsService = inject(FieldsService)
|
|
49
|
+
private notificationsService = inject(NotificationsService)
|
|
50
|
+
private translateService = inject(TranslateService)
|
|
42
51
|
|
|
43
52
|
@Input() fieldName: string
|
|
44
53
|
@Input() title: string
|
|
45
54
|
|
|
55
|
+
spatialExtentMaxFileSize =
|
|
56
|
+
getOptionalSearchConfig()?.SPATIAL_EXTENT_MAX_FILE_SIZE
|
|
57
|
+
|
|
46
58
|
fieldType: FieldType
|
|
47
|
-
dateRange: DateRange
|
|
48
59
|
choices$: Observable<Choice[]>
|
|
49
60
|
selected$ = this.searchFacade.searchFilters$.pipe(
|
|
50
61
|
switchMap((filters) =>
|
|
@@ -57,15 +68,54 @@ export class FilterDropdownComponent implements OnInit {
|
|
|
57
68
|
) as Observable<FieldValue[]>
|
|
58
69
|
|
|
59
70
|
selectedDateRange$ = this.selected$.pipe(
|
|
60
|
-
map((
|
|
71
|
+
map((selected) => (Array.isArray(selected) ? {} : (selected as DateRange)))
|
|
61
72
|
) as Observable<DateRange>
|
|
62
73
|
|
|
74
|
+
selectedBoundingBox$ = this.selected$.pipe(
|
|
75
|
+
map((selected) =>
|
|
76
|
+
Array.isArray(selected) && selected.length > 0
|
|
77
|
+
? (selected as BoundingBox)
|
|
78
|
+
: null
|
|
79
|
+
)
|
|
80
|
+
) as Observable<BoundingBox | null>
|
|
81
|
+
|
|
63
82
|
onSelectedValues(values: unknown[]) {
|
|
64
83
|
this.fieldsService
|
|
65
84
|
.buildFiltersFromFieldValues({ [this.fieldName]: values as FieldValue[] })
|
|
66
85
|
.subscribe((filters) => this.searchService.updateFilters(filters))
|
|
67
86
|
}
|
|
68
87
|
|
|
88
|
+
private spatialExtentErrorNotificationId: number | null = null
|
|
89
|
+
|
|
90
|
+
onBboxChange(bbox: BoundingBox | null) {
|
|
91
|
+
this.fieldsService
|
|
92
|
+
.buildFiltersFromFieldValues({
|
|
93
|
+
[this.fieldName]: bbox,
|
|
94
|
+
})
|
|
95
|
+
.subscribe((filters) => this.searchService.updateFilters(filters))
|
|
96
|
+
this.clearSpatialExtentErrorNotification()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
onSpatialExtentError(error: SpatialExtentDropdownError) {
|
|
100
|
+
this.clearSpatialExtentErrorNotification()
|
|
101
|
+
this.spatialExtentErrorNotificationId =
|
|
102
|
+
this.notificationsService.showNotification({
|
|
103
|
+
type: 'error',
|
|
104
|
+
title: this.translateService.instant(
|
|
105
|
+
'search.filters.spatialExtent.error.title'
|
|
106
|
+
),
|
|
107
|
+
text: this.translateService.instant(error.key, error.params),
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private clearSpatialExtentErrorNotification() {
|
|
112
|
+
if (this.spatialExtentErrorNotificationId === null) return
|
|
113
|
+
this.notificationsService.removeNotificationById(
|
|
114
|
+
this.spatialExtentErrorNotificationId
|
|
115
|
+
)
|
|
116
|
+
this.spatialExtentErrorNotificationId = null
|
|
117
|
+
}
|
|
118
|
+
|
|
69
119
|
ngOnInit() {
|
|
70
120
|
this.fieldType = this.fieldsService.getFieldType(this.fieldName)
|
|
71
121
|
this.choices$ = this.fieldsService.getAvailableValues(this.fieldName).pipe(
|
|
@@ -80,22 +130,11 @@ export class FilterDropdownComponent implements OnInit {
|
|
|
80
130
|
)
|
|
81
131
|
}
|
|
82
132
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (!end) return
|
|
90
|
-
this.dateRange = { ...this.dateRange, end }
|
|
91
|
-
if (this.dateRange.start && this.dateRange.end) {
|
|
92
|
-
this.fieldsService
|
|
93
|
-
.buildFiltersFromFieldValues({
|
|
94
|
-
[this.fieldName]: this.dateRange,
|
|
95
|
-
})
|
|
96
|
-
.subscribe((filters) => {
|
|
97
|
-
return this.searchService.updateFilters(filters)
|
|
98
|
-
})
|
|
99
|
-
}
|
|
133
|
+
onDateRangeChange(dateRange: DateRange) {
|
|
134
|
+
this.fieldsService
|
|
135
|
+
.buildFiltersFromFieldValues({
|
|
136
|
+
[this.fieldName]: dateRange,
|
|
137
|
+
})
|
|
138
|
+
.subscribe((filters) => this.searchService.updateFilters(filters))
|
|
100
139
|
}
|
|
101
140
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Component, Input, OnInit, inject } from '@angular/core'
|
|
2
|
-
import { CommonModule
|
|
2
|
+
import { CommonModule } from '@angular/common'
|
|
3
3
|
import {
|
|
4
4
|
catchError,
|
|
5
5
|
firstValueFrom,
|
|
@@ -15,12 +15,14 @@ import { FieldType, FieldValue } from '../utils/service/fields'
|
|
|
15
15
|
import { SearchFacade } from '../state/search.facade'
|
|
16
16
|
import { SearchService } from '../utils/service/search.service'
|
|
17
17
|
import { FieldsService } from '../utils/service/fields.service'
|
|
18
|
-
import { formatUserInfo } from '../../../../../../libs/util/shared/src'
|
|
18
|
+
import { DateService, formatUserInfo } from '../../../../../../libs/util/shared/src'
|
|
19
19
|
import { marker } from '@biesbjerg/ngx-translate-extract-marker'
|
|
20
20
|
|
|
21
21
|
marker('search.filters.summaryLabel.user')
|
|
22
22
|
marker('search.filters.summaryLabel.changeDate')
|
|
23
23
|
|
|
24
|
+
const OPEN_BOUND = '…'
|
|
25
|
+
|
|
24
26
|
interface DisplayedValue {
|
|
25
27
|
label: string
|
|
26
28
|
value: FieldValue | DateRange
|
|
@@ -32,13 +34,12 @@ interface DisplayedValue {
|
|
|
32
34
|
imports: [CommonModule, BadgeComponent],
|
|
33
35
|
templateUrl: './search-filters-summary-item.component.html',
|
|
34
36
|
styleUrls: ['./search-filters-summary-item.component.css'],
|
|
35
|
-
providers: [DatePipe],
|
|
36
37
|
})
|
|
37
38
|
export class SearchFiltersSummaryItemComponent implements OnInit {
|
|
38
39
|
private searchFacade = inject(SearchFacade)
|
|
39
40
|
private searchService = inject(SearchService)
|
|
40
41
|
private fieldsService = inject(FieldsService)
|
|
41
|
-
private
|
|
42
|
+
private dateService = inject(DateService)
|
|
42
43
|
private translate = inject(TranslateService)
|
|
43
44
|
|
|
44
45
|
@Input() fieldName: string
|
|
@@ -82,12 +83,10 @@ export class SearchFiltersSummaryItemComponent implements OnInit {
|
|
|
82
83
|
getReadableValues(fieldValues: FieldValue[] | DateRange[]): DisplayedValue[] {
|
|
83
84
|
return fieldValues.map((value) => {
|
|
84
85
|
if (this.fieldType === 'dateRange') {
|
|
86
|
+
const { start, end } = value as DateRange
|
|
85
87
|
return {
|
|
86
88
|
value,
|
|
87
|
-
label: `${this.
|
|
88
|
-
value.start,
|
|
89
|
-
'dd.MM.yyyy'
|
|
90
|
-
)} - ${this.datePipe.transform(value.end, 'dd.MM.yyyy')}`,
|
|
89
|
+
label: `${this.formatBound(start)} - ${this.formatBound(end)}`,
|
|
91
90
|
}
|
|
92
91
|
} else if (this.fieldName === 'user') {
|
|
93
92
|
return { value, label: formatUserInfo(value) }
|
|
@@ -97,6 +96,16 @@ export class SearchFiltersSummaryItemComponent implements OnInit {
|
|
|
97
96
|
})
|
|
98
97
|
}
|
|
99
98
|
|
|
99
|
+
private formatBound(date: Date | undefined): string {
|
|
100
|
+
return date
|
|
101
|
+
? this.dateService.formatDate(date, {
|
|
102
|
+
day: '2-digit',
|
|
103
|
+
month: '2-digit',
|
|
104
|
+
year: 'numeric',
|
|
105
|
+
})
|
|
106
|
+
: OPEN_BOUND
|
|
107
|
+
}
|
|
108
|
+
|
|
100
109
|
async removeFilterValue(fieldValue: FieldValue | DateRange) {
|
|
101
110
|
const currentFieldValues: DisplayedValue[] = await firstValueFrom(
|
|
102
111
|
this.fieldValues$
|
|
@@ -2,6 +2,7 @@ import { Injectable, Injector, inject } from '@angular/core'
|
|
|
2
2
|
import {
|
|
3
3
|
AbstractSearchField,
|
|
4
4
|
AvailableServicesField,
|
|
5
|
+
BoundingBoxSearchField,
|
|
5
6
|
DateRangeSearchField,
|
|
6
7
|
FieldValue,
|
|
7
8
|
FullTextSearchField,
|
|
@@ -42,7 +43,7 @@ marker('search.filters.producerOrg')
|
|
|
42
43
|
marker('search.filters.publisherOrg')
|
|
43
44
|
marker('search.filters.user')
|
|
44
45
|
marker('search.filters.changeDate')
|
|
45
|
-
|
|
46
|
+
marker('search.filters.spatialExtent')
|
|
46
47
|
@Injectable({
|
|
47
48
|
providedIn: 'root',
|
|
48
49
|
})
|
|
@@ -95,6 +96,7 @@ export class FieldsService {
|
|
|
95
96
|
user: new UserSearchField(this.injector),
|
|
96
97
|
changeDate: new DateRangeSearchField('changeDate', this.injector, 'desc'),
|
|
97
98
|
availableServices: new AvailableServicesField(this.injector),
|
|
99
|
+
spatialExtent: new BoundingBoxSearchField('spatialExtent', this.injector),
|
|
98
100
|
} as Record<string, AbstractSearchField>
|
|
99
101
|
|
|
100
102
|
get supportedFields() {
|
|
@@ -20,10 +20,14 @@ import {
|
|
|
20
20
|
isDateRange,
|
|
21
21
|
METADATA_LANGUAGE,
|
|
22
22
|
} from '../../../../../../../libs/api/repository/src'
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
BoundingBox,
|
|
25
|
+
formatUserInfo,
|
|
26
|
+
isBoundingBox,
|
|
27
|
+
} from '../../../../../../../libs/util/shared/src'
|
|
24
28
|
import { PossibleResourceTypes } from '../../../../../../../libs/api/metadata-converter/src'
|
|
25
29
|
|
|
26
|
-
export type FieldType = 'values' | 'dateRange'
|
|
30
|
+
export type FieldType = 'values' | 'dateRange' | 'spatialExtent'
|
|
27
31
|
|
|
28
32
|
export type FieldValue = string | number
|
|
29
33
|
export interface FieldAvailableValue {
|
|
@@ -431,6 +435,27 @@ export class DateRangeSearchField extends SimpleSearchField {
|
|
|
431
435
|
}
|
|
432
436
|
}
|
|
433
437
|
|
|
438
|
+
export class BoundingBoxSearchField extends SimpleSearchField {
|
|
439
|
+
getAvailableValues(): Observable<FieldAvailableValue[]> {
|
|
440
|
+
return of([])
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
getFiltersForValues(values: FieldValue[]): Observable<FieldFilters> {
|
|
444
|
+
return of({
|
|
445
|
+
[this.esFieldName]: values.map(Number) as BoundingBox,
|
|
446
|
+
})
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
getValuesForFilter(filters: FieldFilters): Observable<FieldValue[]> {
|
|
450
|
+
const filter = filters[this.esFieldName]
|
|
451
|
+
return of(isBoundingBox(filter) ? filter : [])
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
getType(): FieldType {
|
|
455
|
+
return 'spatialExtent'
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
434
459
|
marker('search.filters.availableServices.view')
|
|
435
460
|
marker('search.filters.availableServices.download')
|
|
436
461
|
|
|
@@ -6,33 +6,32 @@
|
|
|
6
6
|
matSort
|
|
7
7
|
(matSortChange)="setSort($event)"
|
|
8
8
|
[matSortDisableClear]="true"
|
|
9
|
-
*ngrxLet="properties$ as properties"
|
|
10
9
|
>
|
|
11
|
-
@for (
|
|
12
|
-
<ng-container [matColumnDef]="
|
|
10
|
+
@for (column of columns; track column) {
|
|
11
|
+
<ng-container [matColumnDef]="column.name">
|
|
13
12
|
<th
|
|
14
13
|
mat-header-cell
|
|
15
14
|
*matHeaderCellDef
|
|
16
15
|
mat-sort-header
|
|
17
16
|
class="text-sm text-black bg-white"
|
|
18
17
|
>
|
|
19
|
-
{{
|
|
18
|
+
{{ column.label }}
|
|
20
19
|
</th>
|
|
21
20
|
<td
|
|
22
21
|
mat-cell
|
|
23
22
|
*matCellDef="let element"
|
|
24
23
|
class="whitespace-nowrap pr-1 truncate"
|
|
25
24
|
>
|
|
26
|
-
{{ element[
|
|
25
|
+
{{ element[column.name] }}
|
|
27
26
|
</td>
|
|
28
27
|
</ng-container>
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
<tr mat-header-row *matHeaderRowDef="
|
|
30
|
+
<tr mat-header-row *matHeaderRowDef="columnNames; sticky: true"></tr>
|
|
32
31
|
<tr
|
|
33
32
|
[id]="getRowEltId(row.id)"
|
|
34
33
|
mat-row
|
|
35
|
-
*matRowDef="let row; columns:
|
|
34
|
+
*matRowDef="let row; columns: columnNames"
|
|
36
35
|
(click)="selected.emit(row)"
|
|
37
36
|
[class.active]="
|
|
38
37
|
activeId !== undefined && activeId !== null && row.id === activeId
|
|
@@ -6,14 +6,14 @@ import {
|
|
|
6
6
|
Component,
|
|
7
7
|
ElementRef,
|
|
8
8
|
EventEmitter,
|
|
9
|
+
inject,
|
|
9
10
|
Input,
|
|
10
11
|
OnChanges,
|
|
11
12
|
OnInit,
|
|
12
13
|
Output,
|
|
13
14
|
ViewChild,
|
|
14
|
-
inject,
|
|
15
15
|
} from '@angular/core'
|
|
16
|
-
import { MatSort, MatSortModule } from '@angular/material/sort'
|
|
16
|
+
import { MatSort, MatSortModule, Sort } from '@angular/material/sort'
|
|
17
17
|
import { MatTableModule } from '@angular/material/table'
|
|
18
18
|
import {
|
|
19
19
|
TranslateDirective,
|
|
@@ -29,12 +29,11 @@ import {
|
|
|
29
29
|
} from '@angular/material/paginator'
|
|
30
30
|
import { CustomMatPaginatorIntl } from './custom.mat.paginator.intl'
|
|
31
31
|
import { CommonModule } from '@angular/common'
|
|
32
|
-
import { BehaviorSubject
|
|
32
|
+
import { BehaviorSubject } from 'rxjs'
|
|
33
33
|
import {
|
|
34
34
|
LoadingMaskComponent,
|
|
35
35
|
PopupAlertComponent,
|
|
36
36
|
} from '../../../../../../libs/ui/widgets/src'
|
|
37
|
-
import { LetDirective } from '@ngrx/component'
|
|
38
37
|
|
|
39
38
|
const rowIdPrefix = 'table-item-'
|
|
40
39
|
|
|
@@ -46,6 +45,11 @@ export interface TableItemModel {
|
|
|
46
45
|
[key: string]: TableItemType
|
|
47
46
|
}
|
|
48
47
|
|
|
48
|
+
interface TableColumn {
|
|
49
|
+
name: string
|
|
50
|
+
label: string
|
|
51
|
+
}
|
|
52
|
+
|
|
49
53
|
@Component({
|
|
50
54
|
standalone: true,
|
|
51
55
|
imports: [
|
|
@@ -56,7 +60,6 @@ export interface TableItemModel {
|
|
|
56
60
|
CommonModule,
|
|
57
61
|
LoadingMaskComponent,
|
|
58
62
|
PopupAlertComponent,
|
|
59
|
-
LetDirective,
|
|
60
63
|
TranslatePipe,
|
|
61
64
|
TranslateDirective,
|
|
62
65
|
],
|
|
@@ -71,15 +74,21 @@ export class DataTableComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
71
74
|
private cdr = inject(ChangeDetectorRef)
|
|
72
75
|
private translateService = inject(TranslateService)
|
|
73
76
|
|
|
74
|
-
|
|
77
|
+
columnsFromFeatureCatalog: TableColumn[] = null
|
|
75
78
|
@Input() set featureAttributes(value: { value: string; label: string }[]) {
|
|
76
|
-
this.
|
|
77
|
-
|
|
79
|
+
this.columnsFromFeatureCatalog = value.map((attrs) => ({
|
|
80
|
+
name: attrs.value,
|
|
81
|
+
label: attrs.label,
|
|
82
|
+
}))
|
|
78
83
|
}
|
|
84
|
+
columnsFromDataset: TableColumn[] = []
|
|
79
85
|
@Input() set dataset(value: BaseReader) {
|
|
80
86
|
this.dataset_ = value
|
|
81
87
|
this.dataset_.load()
|
|
82
|
-
this.dataset_.info.then((info) =>
|
|
88
|
+
this.dataset_.info.then((info) => {
|
|
89
|
+
this.count = info.itemsCount
|
|
90
|
+
this.cdr.detectChanges()
|
|
91
|
+
})
|
|
83
92
|
}
|
|
84
93
|
@Input() activeId: TableItemId
|
|
85
94
|
@Output() selected = new EventEmitter<any>()
|
|
@@ -88,13 +97,19 @@ export class DataTableComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
88
97
|
@ViewChild(MatPaginator) paginator: MatPaginator
|
|
89
98
|
|
|
90
99
|
dataset_: BaseReader
|
|
91
|
-
properties$ = new BehaviorSubject<string[]>(null)
|
|
92
100
|
dataSource: DataTableDataSource
|
|
93
101
|
headerHeight: number
|
|
94
102
|
count: number
|
|
95
103
|
loading$ = new BehaviorSubject<boolean>(false)
|
|
96
104
|
error = null
|
|
97
105
|
|
|
106
|
+
get columns() {
|
|
107
|
+
return this.columnsFromFeatureCatalog ?? this.columnsFromDataset
|
|
108
|
+
}
|
|
109
|
+
get columnNames() {
|
|
110
|
+
return this.columns.map((c) => c.name)
|
|
111
|
+
}
|
|
112
|
+
|
|
98
113
|
ngOnInit() {
|
|
99
114
|
this.dataSource = new DataTableDataSource()
|
|
100
115
|
}
|
|
@@ -110,7 +125,7 @@ export class DataTableComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
110
125
|
this.setPagination()
|
|
111
126
|
}
|
|
112
127
|
|
|
113
|
-
setSort(sort:
|
|
128
|
+
setSort(sort: Sort) {
|
|
114
129
|
if (!this.dataset_) return
|
|
115
130
|
if (!sort.active) {
|
|
116
131
|
this.dataset_.orderBy()
|
|
@@ -132,15 +147,12 @@ export class DataTableComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
132
147
|
|
|
133
148
|
async readData() {
|
|
134
149
|
this.loading$.next(true)
|
|
135
|
-
// wait for properties to be read
|
|
136
|
-
const properties = await firstValueFrom(
|
|
137
|
-
this.properties$.pipe(filter((p) => !!p))
|
|
138
|
-
)
|
|
139
|
-
const propsWithoutGeom = properties.filter(
|
|
140
|
-
(p) => !p.toLowerCase().startsWith('geom')
|
|
141
|
-
)
|
|
142
|
-
this.dataset_.select(...propsWithoutGeom)
|
|
143
150
|
try {
|
|
151
|
+
// wait for properties to be read
|
|
152
|
+
if (!this.columnsFromFeatureCatalog) {
|
|
153
|
+
this.columnsFromDataset = await this.dataset_.properties
|
|
154
|
+
}
|
|
155
|
+
this.dataset_.select(...this.columnNames)
|
|
144
156
|
await this.dataSource.showData(this.dataset_.read())
|
|
145
157
|
this.error = null
|
|
146
158
|
} catch (error) {
|
|
@@ -4,6 +4,7 @@ export * from './lib/button/button.component'
|
|
|
4
4
|
export * from './lib/check-toggle/check-toggle.component'
|
|
5
5
|
export * from './lib/checkbox/checkbox.component'
|
|
6
6
|
export * from './lib/copy-text-button/copy-text-button.component'
|
|
7
|
+
export * from './lib/date-adapter.providers'
|
|
7
8
|
export * from './lib/date-picker/date-picker.component'
|
|
8
9
|
export * from './lib/date-range-dropdown/date-range-dropdown.component'
|
|
9
10
|
export * from './lib/date-range-picker/date-range-picker.component'
|
|
@@ -25,3 +26,4 @@ export * from './lib/url-input/url-input.component'
|
|
|
25
26
|
export * from './lib/viewport-intersector/viewport-intersector.component'
|
|
26
27
|
export * from './lib/search-feature-catalog/search-feature-catalog.component'
|
|
27
28
|
export * from './lib/date-range-inputs/date-range-inputs.component'
|
|
29
|
+
export * from './lib/spatial-extent-dropdown/spatial-extent-dropdown.component'
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { inject, Provider } from '@angular/core'
|
|
2
|
+
import {
|
|
3
|
+
DateAdapter,
|
|
4
|
+
MAT_DATE_FORMATS,
|
|
5
|
+
MAT_DATE_LOCALE,
|
|
6
|
+
} from '@angular/material/core'
|
|
7
|
+
import {
|
|
8
|
+
DateFnsAdapter,
|
|
9
|
+
MAT_DATE_FNS_FORMATS,
|
|
10
|
+
} from '@angular/material-date-fns-adapter'
|
|
11
|
+
import { enUS } from 'date-fns/locale/en-US'
|
|
12
|
+
import { DateService } from '../../../../../libs/util/shared/src'
|
|
13
|
+
|
|
14
|
+
export function provideLocalizedDateAdapter(): Provider[] {
|
|
15
|
+
return [
|
|
16
|
+
{ provide: MAT_DATE_LOCALE, useValue: enUS },
|
|
17
|
+
{ provide: MAT_DATE_FORMATS, useValue: MAT_DATE_FNS_FORMATS },
|
|
18
|
+
{
|
|
19
|
+
provide: DateAdapter,
|
|
20
|
+
useFactory: () => {
|
|
21
|
+
const dateService = inject(DateService)
|
|
22
|
+
const adapter = new DateFnsAdapter()
|
|
23
|
+
dateService
|
|
24
|
+
.getDateFnsLocale()
|
|
25
|
+
.then((locale) => adapter.setLocale(locale))
|
|
26
|
+
return adapter
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
]
|
|
30
|
+
}
|
|
@@ -2,15 +2,9 @@ import {
|
|
|
2
2
|
ChangeDetectionStrategy,
|
|
3
3
|
Component,
|
|
4
4
|
EventEmitter,
|
|
5
|
-
inject,
|
|
6
5
|
Input,
|
|
7
6
|
Output,
|
|
8
7
|
} from '@angular/core'
|
|
9
|
-
import {
|
|
10
|
-
DateAdapter,
|
|
11
|
-
MAT_DATE_LOCALE,
|
|
12
|
-
MatNativeDateModule,
|
|
13
|
-
} from '@angular/material/core'
|
|
14
8
|
import { MatDatepickerModule } from '@angular/material/datepicker'
|
|
15
9
|
import { ButtonComponent } from '../button/button.component'
|
|
16
10
|
import {
|
|
@@ -19,7 +13,7 @@ import {
|
|
|
19
13
|
provideNgIconsConfig,
|
|
20
14
|
} from '@ng-icons/core'
|
|
21
15
|
import { iconoirCalendar } from '@ng-icons/iconoir'
|
|
22
|
-
import {
|
|
16
|
+
import { provideLocalizedDateAdapter } from '../date-adapter.providers'
|
|
23
17
|
|
|
24
18
|
@Component({
|
|
25
19
|
selector: 'gn-ui-date-picker',
|
|
@@ -27,31 +21,16 @@ import { TranslateService } from '@ngx-translate/core'
|
|
|
27
21
|
styleUrls: ['./date-picker.component.css'],
|
|
28
22
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
29
23
|
standalone: true,
|
|
30
|
-
imports: [
|
|
31
|
-
MatNativeDateModule,
|
|
32
|
-
MatDatepickerModule,
|
|
33
|
-
ButtonComponent,
|
|
34
|
-
NgIconComponent,
|
|
35
|
-
],
|
|
24
|
+
imports: [MatDatepickerModule, ButtonComponent, NgIconComponent],
|
|
36
25
|
providers: [
|
|
37
26
|
provideIcons({ iconoirCalendar }),
|
|
38
27
|
provideNgIconsConfig({
|
|
39
28
|
size: '1.5rem',
|
|
40
29
|
}),
|
|
41
|
-
|
|
42
|
-
provide: MAT_DATE_LOCALE,
|
|
43
|
-
useFactory: (locale: string) => locale,
|
|
44
|
-
},
|
|
30
|
+
provideLocalizedDateAdapter(),
|
|
45
31
|
],
|
|
46
32
|
})
|
|
47
33
|
export class DatePickerComponent {
|
|
48
|
-
private dateAdapter = inject<DateAdapter<Date>>(DateAdapter)
|
|
49
|
-
private translate = inject(TranslateService)
|
|
50
|
-
|
|
51
34
|
@Input() date: Date
|
|
52
35
|
@Output() dateChange = new EventEmitter<Date>()
|
|
53
|
-
|
|
54
|
-
constructor() {
|
|
55
|
-
this.dateAdapter.setLocale(this.translate.getCurrentLang())
|
|
56
|
-
}
|
|
57
36
|
}
|
|
@@ -1,5 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
mat-calendar {
|
|
2
|
+
width: 100%;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.calendar {
|
|
6
|
+
display: grid;
|
|
7
|
+
grid-template-rows: 1fr;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.calendar > mat-calendar {
|
|
11
|
+
min-height: 0;
|
|
12
|
+
overflow: hidden;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.calendar.expand {
|
|
16
|
+
animation: open 200ms ease-out;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.calendar.collapse {
|
|
20
|
+
animation: open 200ms ease-in reverse;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@keyframes open {
|
|
24
|
+
from {
|
|
25
|
+
grid-template-rows: 0fr;
|
|
26
|
+
opacity: 0;
|
|
27
|
+
}
|
|
28
|
+
to {
|
|
29
|
+
grid-template-rows: 1fr;
|
|
30
|
+
opacity: 1;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@media (prefers-reduced-motion: reduce) {
|
|
35
|
+
.calendar.expand,
|
|
36
|
+
.calendar.collapse {
|
|
37
|
+
animation-duration: 1ms;
|
|
4
38
|
}
|
|
5
39
|
}
|