geonetwork-ui 2.11.0-dev.80a8b0bfa → 2.11.0-dev.8aa6985e0
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 +836 -362
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +219 -78
- package/index.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +107 -34
- package/src/libs/common/domain/src/lib/model/search/filter.model.ts +20 -1
- package/src/libs/feature/map/src/index.ts +0 -1
- package/src/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.html +1 -0
- package/src/libs/feature/map/src/lib/feature-map.module.ts +1 -8
- package/src/libs/feature/notifications/src/lib/notifications.service.ts +6 -4
- package/src/libs/feature/router/src/lib/default/state/query-params.utils.ts +1 -1
- package/src/libs/feature/search/src/index.ts +2 -0
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +12 -4
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +58 -19
- package/src/libs/feature/{map/src/lib → search/src/lib/geocoding}/geocoding.service.ts +16 -7
- package/src/libs/feature/search/src/lib/location-search/location-search.component.html +8 -0
- package/src/libs/feature/search/src/lib/location-search/location-search.component.ts +40 -0
- package/src/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts +18 -8
- package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +9 -1
- package/src/libs/feature/search/src/lib/utils/service/fields.ts +61 -2
- package/src/libs/ui/elements/src/lib/service-capabilities/service-capabilities.component.ts +2 -1
- package/src/libs/ui/inputs/src/index.ts +2 -0
- package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.html +10 -1
- package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts +8 -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 +7 -4
- package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.ts +12 -9
- package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.model.ts +2 -2
- 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 +221 -0
- package/src/libs/ui/layout/src/lib/block-list/block-list.component.css +1 -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 +23 -0
- package/src/libs/util/app-config/src/lib/fixtures.ts +16 -1
- package/src/libs/util/app-config/src/lib/model.ts +15 -1
- 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/links/link-utils.ts +19 -10
- 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 +24 -0
- package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
- package/tailwind.base.css +5 -0
- package/translations/de.json +17 -3
- package/translations/en.json +16 -2
- package/translations/es.json +15 -1
- package/translations/fr.json +17 -3
- package/translations/it.json +15 -1
- package/translations/nl.json +15 -1
- package/translations/pt.json +15 -1
- package/translations/sk.json +15 -1
- package/src/libs/feature/map/src/lib/geocoding/geocoding.component.html +0 -41
- package/src/libs/feature/map/src/lib/geocoding/geocoding.component.ts +0 -112
- /package/src/libs/{feature/map/src/lib/geocoding/geocoding.component.css → ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.css} +0 -0
|
@@ -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,
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
MultilingualSearchField,
|
|
11
12
|
OrganizationSearchField,
|
|
12
13
|
OwnerSearchField,
|
|
14
|
+
ResourceCreationRevisionDateSearchField,
|
|
13
15
|
ResourceTypeLegacyField,
|
|
14
16
|
SimpleSearchField,
|
|
15
17
|
TranslatedSearchField,
|
|
@@ -42,7 +44,8 @@ marker('search.filters.producerOrg')
|
|
|
42
44
|
marker('search.filters.publisherOrg')
|
|
43
45
|
marker('search.filters.user')
|
|
44
46
|
marker('search.filters.changeDate')
|
|
45
|
-
|
|
47
|
+
marker('search.filters.resourceCreationRevisionDate')
|
|
48
|
+
marker('search.filters.spatialExtent')
|
|
46
49
|
@Injectable({
|
|
47
50
|
providedIn: 'root',
|
|
48
51
|
})
|
|
@@ -94,7 +97,12 @@ export class FieldsService {
|
|
|
94
97
|
),
|
|
95
98
|
user: new UserSearchField(this.injector),
|
|
96
99
|
changeDate: new DateRangeSearchField('changeDate', this.injector, 'desc'),
|
|
100
|
+
resourceCreationRevisionDate: new ResourceCreationRevisionDateSearchField(
|
|
101
|
+
this.injector,
|
|
102
|
+
'desc'
|
|
103
|
+
),
|
|
97
104
|
availableServices: new AvailableServicesField(this.injector),
|
|
105
|
+
spatialExtent: new BoundingBoxSearchField('spatialExtent', this.injector),
|
|
98
106
|
} as Record<string, AbstractSearchField>
|
|
99
107
|
|
|
100
108
|
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,61 @@ export class DateRangeSearchField extends SimpleSearchField {
|
|
|
431
435
|
}
|
|
432
436
|
}
|
|
433
437
|
|
|
438
|
+
// treats a resource's creation/revision dates as an interval (its min and max), so a record matches
|
|
439
|
+
// when that interval intersects the filter range, not just when a single date falls inside it
|
|
440
|
+
export class ResourceCreationRevisionDateSearchField extends DateRangeSearchField {
|
|
441
|
+
constructor(injector: Injector, order: 'asc' | 'desc' = 'desc') {
|
|
442
|
+
super('resourceCreationRevisionDate', injector, order)
|
|
443
|
+
const dateSourceFields = [
|
|
444
|
+
'creationDateForResource',
|
|
445
|
+
'revisionDateForResource',
|
|
446
|
+
]
|
|
447
|
+
const collectSortedDates = dateSourceFields
|
|
448
|
+
.map(
|
|
449
|
+
(field) => `if (doc.containsKey('${field}')) {
|
|
450
|
+
for (def date : doc['${field}']) { dates.add(date.millis); }
|
|
451
|
+
}`
|
|
452
|
+
)
|
|
453
|
+
.join('\n')
|
|
454
|
+
|
|
455
|
+
this.esService.registerRuntimeField(
|
|
456
|
+
'resourceCreationRevisionDateMin',
|
|
457
|
+
`def dates = [];
|
|
458
|
+
${collectSortedDates}
|
|
459
|
+
if (dates.length > 0) { dates.sort(null); emit(dates[0]); }`,
|
|
460
|
+
'date'
|
|
461
|
+
)
|
|
462
|
+
this.esService.registerRuntimeField(
|
|
463
|
+
'resourceCreationRevisionDateMax',
|
|
464
|
+
`def dates = [];
|
|
465
|
+
${collectSortedDates}
|
|
466
|
+
if (dates.length > 0) { dates.sort(null); emit(dates[dates.length - 1]); }`,
|
|
467
|
+
'date'
|
|
468
|
+
)
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
export class BoundingBoxSearchField extends SimpleSearchField {
|
|
473
|
+
getAvailableValues(): Observable<FieldAvailableValue[]> {
|
|
474
|
+
return of([])
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
getFiltersForValues(values: FieldValue[]): Observable<FieldFilters> {
|
|
478
|
+
return of({
|
|
479
|
+
[this.esFieldName]: values.map(Number) as BoundingBox,
|
|
480
|
+
})
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
getValuesForFilter(filters: FieldFilters): Observable<FieldValue[]> {
|
|
484
|
+
const filter = filters[this.esFieldName]
|
|
485
|
+
return of(isBoundingBox(filter) ? filter : [])
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
getType(): FieldType {
|
|
489
|
+
return 'spatialExtent'
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
434
493
|
marker('search.filters.availableServices.view')
|
|
435
494
|
marker('search.filters.availableServices.download')
|
|
436
495
|
|
|
@@ -128,7 +128,8 @@ export class ServiceCapabilitiesComponent implements OnInit {
|
|
|
128
128
|
this.loading = true
|
|
129
129
|
this.availableLayers = await getLayers(
|
|
130
130
|
this.apiLinks[0].url.href,
|
|
131
|
-
this.apiLinks[0].accessServiceProtocol
|
|
131
|
+
this.apiLinks[0].accessServiceProtocol,
|
|
132
|
+
true
|
|
132
133
|
)
|
|
133
134
|
this.loading = false
|
|
134
135
|
this.cdr.detectChanges()
|
|
@@ -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'
|
|
@@ -83,7 +83,16 @@
|
|
|
83
83
|
>
|
|
84
84
|
@for (suggestion of suggestions$ | async; track suggestion) {
|
|
85
85
|
<mat-option [value]="suggestion" class="p-2 suggestion text-start">
|
|
86
|
-
|
|
86
|
+
@if (displayWithTemplate) {
|
|
87
|
+
<ng-container
|
|
88
|
+
*ngTemplateOutlet="
|
|
89
|
+
displayWithTemplate;
|
|
90
|
+
context: { $implicit: suggestion }
|
|
91
|
+
"
|
|
92
|
+
></ng-container>
|
|
93
|
+
} @else {
|
|
94
|
+
{{ displayWithFnInternal(suggestion) }}
|
|
95
|
+
}
|
|
87
96
|
</mat-option>
|
|
88
97
|
}
|
|
89
98
|
</mat-autocomplete>
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
OnInit,
|
|
12
12
|
Output,
|
|
13
13
|
SimpleChanges,
|
|
14
|
+
TemplateRef,
|
|
14
15
|
ViewChild,
|
|
15
16
|
inject,
|
|
16
17
|
} from '@angular/core'
|
|
@@ -97,6 +98,13 @@ export class AutocompleteComponent
|
|
|
97
98
|
@ViewChild(MatAutocompleteTrigger) triggerRef: MatAutocompleteTrigger
|
|
98
99
|
@ViewChild(MatAutocomplete) autocomplete: MatAutocomplete
|
|
99
100
|
@ViewChild('searchInput') inputRef: ElementRef<HTMLInputElement>
|
|
101
|
+
/**
|
|
102
|
+
* Custom template to render each suggestion. Takes precedence over
|
|
103
|
+
* `displayWithFn` when set.
|
|
104
|
+
*/
|
|
105
|
+
@Input() displayWithTemplate?: TemplateRef<{
|
|
106
|
+
$implicit: AutocompleteItem
|
|
107
|
+
}>
|
|
100
108
|
|
|
101
109
|
searching: boolean
|
|
102
110
|
control = new UntypedFormControl()
|
|
@@ -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
|
}
|
|
@@ -1,30 +1,149 @@
|
|
|
1
1
|
<gn-ui-button
|
|
2
2
|
type="outline"
|
|
3
3
|
extraClass="bg-background w-full !p-[8px] !pl-[16px]"
|
|
4
|
-
|
|
4
|
+
[title]="title"
|
|
5
|
+
(buttonClick)="openOverlay()"
|
|
6
|
+
cdkOverlayOrigin
|
|
7
|
+
#overlayOrigin="cdkOverlayOrigin"
|
|
5
8
|
>
|
|
6
9
|
<div class="grow flex items-center mr-2 gap-2 overflow-hidden">
|
|
7
10
|
<div class="text-left font-medium truncate py-1">
|
|
8
11
|
{{ title }}
|
|
9
12
|
</div>
|
|
13
|
+
@if (selectedDatesCount) {
|
|
14
|
+
<div
|
|
15
|
+
class="gn-ui-multiselect-counter shrink-0 rounded-full font-bold text-[12px] w-5 h-5 flex items-center justify-center mr-1 selected-count"
|
|
16
|
+
>
|
|
17
|
+
{{ selectedDatesCount }}
|
|
18
|
+
</div>
|
|
19
|
+
}
|
|
10
20
|
</div>
|
|
21
|
+
<button
|
|
22
|
+
class="h-6 w-6 flex items-center justify-center"
|
|
23
|
+
data-test="dropdown-clear"
|
|
24
|
+
>
|
|
25
|
+
@if (selectedDatesCount && !overlayOpen) {
|
|
26
|
+
<ng-icon
|
|
27
|
+
class="shrink-0 opacity-40 hover:opacity-80 transition-colors clear-btn"
|
|
28
|
+
(click)="clearDates($event)"
|
|
29
|
+
name="matClose"
|
|
30
|
+
></ng-icon>
|
|
31
|
+
}
|
|
32
|
+
</button>
|
|
11
33
|
<ng-icon
|
|
12
|
-
|
|
34
|
+
data-test="dropdown-toggle"
|
|
35
|
+
[name]="overlayOpen ? 'matExpandLess' : 'matExpandMore'"
|
|
13
36
|
class="shrink-0 opacity-40"
|
|
14
37
|
>
|
|
15
38
|
</ng-icon>
|
|
16
39
|
</gn-ui-button>
|
|
17
|
-
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
40
|
+
|
|
41
|
+
<ng-template
|
|
42
|
+
cdkConnectedOverlay
|
|
43
|
+
cdkConnectedOverlayHasBackdrop
|
|
44
|
+
cdkConnectedOverlayBackdropClass="cdk-overlay-transparent-backdrop"
|
|
45
|
+
[cdkConnectedOverlayOrigin]="overlayOrigin"
|
|
46
|
+
[cdkConnectedOverlayOpen]="overlayOpen"
|
|
47
|
+
[cdkConnectedOverlayPositions]="overlayPositions"
|
|
48
|
+
[cdkConnectedOverlayScrollStrategy]="scrollStrategy"
|
|
49
|
+
(overlayOutsideClick)="closeOverlay()"
|
|
50
|
+
(detach)="closeOverlay()"
|
|
51
|
+
>
|
|
52
|
+
<div
|
|
53
|
+
class="w-[296px] flex flex-col gap-2 bg-white border border-gray-400 rounded px-4 py-3 shadow-lg text-main text-[16px] leading-[24px]"
|
|
54
|
+
data-test="date-range-panel"
|
|
55
|
+
>
|
|
56
|
+
<div class="flex flex-col justify-center border-b border-gray-300 pb-1">
|
|
57
|
+
<ng-container
|
|
58
|
+
[ngTemplateOutlet]="boundRow"
|
|
59
|
+
[ngTemplateOutletContext]="{
|
|
60
|
+
bound: 'start',
|
|
61
|
+
label: 'daterange.filter.from',
|
|
62
|
+
date: startDate,
|
|
63
|
+
}"
|
|
64
|
+
></ng-container>
|
|
65
|
+
@if (expandedBound === 'start') {
|
|
66
|
+
<div class="calendar" animate.enter="expand" animate.leave="collapse">
|
|
67
|
+
<mat-calendar
|
|
68
|
+
data-test="start-date-calendar"
|
|
69
|
+
[selected]="startDate"
|
|
70
|
+
[startAt]="startDate ?? endDate"
|
|
71
|
+
[maxDate]="endDate"
|
|
72
|
+
(selectedChange)="setStartDate($event)"
|
|
73
|
+
></mat-calendar>
|
|
74
|
+
</div>
|
|
75
|
+
}
|
|
76
|
+
</div>
|
|
77
|
+
<div class="flex flex-col justify-center">
|
|
78
|
+
<ng-container
|
|
79
|
+
[ngTemplateOutlet]="boundRow"
|
|
80
|
+
[ngTemplateOutletContext]="{
|
|
81
|
+
bound: 'end',
|
|
82
|
+
label: 'daterange.filter.to',
|
|
83
|
+
date: endDate,
|
|
84
|
+
}"
|
|
85
|
+
></ng-container>
|
|
86
|
+
@if (expandedBound === 'end') {
|
|
87
|
+
<div class="calendar" animate.enter="expand" animate.leave="collapse">
|
|
88
|
+
<mat-calendar
|
|
89
|
+
data-test="end-date-calendar"
|
|
90
|
+
[selected]="endDate"
|
|
91
|
+
[startAt]="endDate ?? startDate"
|
|
92
|
+
[minDate]="startDate"
|
|
93
|
+
(selectedChange)="setEndDate($event)"
|
|
94
|
+
></mat-calendar>
|
|
95
|
+
</div>
|
|
96
|
+
}
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</ng-template>
|
|
100
|
+
|
|
101
|
+
<ng-template #boundRow let-bound="bound" let-label="label" let-date="date">
|
|
102
|
+
<div class="flex items-center gap-1 w-full">
|
|
103
|
+
<span class="w-[80px] shrink-0" [translate]="label"></span>
|
|
104
|
+
<div
|
|
105
|
+
class="flex-1 min-w-0 flex items-center gap-1 border rounded-lg px-2 py-1 transition-colors"
|
|
106
|
+
[class]="
|
|
107
|
+
invalidBounds[bound]
|
|
108
|
+
? 'border-red-400 text-red-800 focus-within:border-red-800 hover:border-red-800'
|
|
109
|
+
: 'border-gray-400 focus-within:border-main hover:border-main'
|
|
110
|
+
"
|
|
111
|
+
>
|
|
112
|
+
<ng-icon
|
|
113
|
+
name="iconoirCalendar"
|
|
114
|
+
class="shrink-0"
|
|
115
|
+
aria-hidden="true"
|
|
116
|
+
></ng-icon>
|
|
117
|
+
<input
|
|
118
|
+
type="text"
|
|
119
|
+
inputmode="numeric"
|
|
120
|
+
autocomplete="off"
|
|
121
|
+
[gnUiAutofocus]="bound === expandedBound"
|
|
122
|
+
class="min-w-0 grow bg-transparent focus:outline-none"
|
|
123
|
+
[value]="date ? formatDate(date) : ''"
|
|
124
|
+
[placeholder]="'daterange.filter.datePlaceholder' | translate"
|
|
125
|
+
[attr.aria-label]="label | translate"
|
|
126
|
+
[attr.aria-invalid]="invalidBounds[bound] || null"
|
|
127
|
+
[attr.data-test]="bound + '-date-input'"
|
|
128
|
+
(change)="onDateInput(bound, $event)"
|
|
129
|
+
(blur)="normalizeDateInput(bound, $event)"
|
|
130
|
+
(click)="expandedBound !== bound && toggleBound(bound)"
|
|
131
|
+
/>
|
|
132
|
+
</div>
|
|
133
|
+
<div class="shrink-0 flex items-center justify-end">
|
|
134
|
+
<gn-ui-button
|
|
135
|
+
type="light"
|
|
136
|
+
[style.--gn-ui-button-padding]="0"
|
|
137
|
+
[style.--gn-ui-button-width]="'24px'"
|
|
138
|
+
[style.--gn-ui-button-height]="'24px'"
|
|
139
|
+
[attr.aria-expanded]="expandedBound === bound"
|
|
140
|
+
[attr.data-test]="bound + '-date-toggle'"
|
|
141
|
+
(buttonClick)="toggleBound(bound)"
|
|
142
|
+
>
|
|
143
|
+
<ng-icon
|
|
144
|
+
[name]="expandedBound === bound ? 'matExpandLess' : 'matExpandMore'"
|
|
145
|
+
></ng-icon>
|
|
146
|
+
</gn-ui-button>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</ng-template>
|