geonetwork-ui 2.11.0-dev.96d0c6ce2 → 2.11.0-dev.9bd41a961
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 +1506 -938
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +295 -127
- 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/metadata-converter/src/lib/gn4/types/elasticsearch.model.ts +13 -1
- package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +130 -37
- 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 +20 -1
- package/src/libs/common/domain/src/lib/model/search/sort-by.model.ts +1 -5
- 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/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/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/router.service.ts +1 -1
- 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 +5 -1
- 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/sort-by/sort-by.component.ts +1 -1
- 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/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/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 +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/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 +8 -0
- package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
- package/tailwind.base.css +5 -0
- package/translations/de.json +18 -4
- package/translations/en.json +17 -3
- package/translations/es.json +15 -1
- package/translations/fr.json +18 -4
- package/translations/it.json +16 -2
- package/translations/nl.json +15 -1
- package/translations/pt.json +15 -1
- package/translations/sk.json +16 -2
- 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/util/data-fetcher/src/lib/readers/base-cache.ts +0 -14
- /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
|
@@ -6,9 +6,9 @@ import GeoJSON from 'ol/format/GeoJSON.js';
|
|
|
6
6
|
import { parse as parse$1 } from 'ol/xml.js';
|
|
7
7
|
import { format } from 'date-fns/format';
|
|
8
8
|
import { Namespace, Literal, lit, parse as parse$2, sym, BlankNode, graph } from 'rdflib';
|
|
9
|
-
import { lastValueFrom, fromEvent, startWith, map as map$2, shareReplay, filter, pairwise, of, switchMap, Subject, combineLatest, from, exhaustMap, throwError,
|
|
9
|
+
import { lastValueFrom, fromEvent, startWith, map as map$2, shareReplay, filter, pairwise, of, switchMap, Subject, forkJoin, combineLatest, from, exhaustMap, throwError, takeLast, firstValueFrom, merge, BehaviorSubject, timer, ReplaySubject, Subscription, first, distinctUntilChanged as distinctUntilChanged$1, animationFrameScheduler, debounceTime as debounceTime$1, Observable, buffer, tap as tap$2, combineLatestWith, take as take$1, catchError as catchError$1, EMPTY, mergeMap as mergeMap$1, withLatestFrom as withLatestFrom$1, takeUntil } from 'rxjs';
|
|
10
10
|
import * as i0 from '@angular/core';
|
|
11
|
-
import { InjectionToken, inject, Injectable, NgModule, Injector, provideAppInitializer, makeEnvironmentProviders, ElementRef,
|
|
11
|
+
import { InjectionToken, inject, Injectable, NgModule, Injector, provideAppInitializer, makeEnvironmentProviders, ElementRef, afterNextRender, booleanAttribute, Input, Directive, HostListener, Renderer2, DestroyRef, EventEmitter, Output, ViewChild, ChangeDetectionStrategy, Component, ViewEncapsulation, ChangeDetectorRef, HostBinding, ViewContainerRef, TemplateRef, ViewChildren, ContentChild, input, output, signal, viewChild, computed, ContentChildren, NgZone, ComponentFactoryResolver, viewChildren } from '@angular/core';
|
|
12
12
|
import { HttpClient, HttpHeaders, HttpParams, HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi, HttpEventType } from '@angular/common/http';
|
|
13
13
|
import * as i4 from '@ngx-translate/core';
|
|
14
14
|
import { TranslateLoader, TranslateCompiler, TranslateDefaultParser, TranslateParser, TranslateService, provideTranslateService, TranslateDirective, TranslatePipe, TranslateModule } from '@ngx-translate/core';
|
|
@@ -18,7 +18,7 @@ import { map as map$1, shareReplay as shareReplay$1, catchError, tap as tap$1, f
|
|
|
18
18
|
import { lt, valid, coerce, satisfies, ltr } from 'semver';
|
|
19
19
|
import chroma from 'chroma-js';
|
|
20
20
|
import * as i1$1 from '@angular/common';
|
|
21
|
-
import { Location, CommonModule,
|
|
21
|
+
import { Location, CommonModule, NgTemplateOutlet, NgClass, AsyncPipe } from '@angular/common';
|
|
22
22
|
import { formatDistance } from 'date-fns/formatDistance';
|
|
23
23
|
import { Scroll, NavigationEnd, Router, RouteReuseStrategy } from '@angular/router';
|
|
24
24
|
import { WmtsEndpoint, WmsEndpoint, WfsEndpoint, OgcApiEndpoint, sharedFetch, useCache, StacEndpoint, TmsEndpoint } from '@camptocamp/ogc-client';
|
|
@@ -26,7 +26,6 @@ import { getLength } from 'ol/sphere.js';
|
|
|
26
26
|
import { LineString } from 'ol/geom.js';
|
|
27
27
|
import * as i1 from '@ngrx/store';
|
|
28
28
|
import { createAction, props, createReducer, on, createFeatureSelector, createSelector, Store, select, StoreModule } from '@ngrx/store';
|
|
29
|
-
import { queryDataGouvFr, queryGeonames, queryGeoadmin } from '@geospatial-sdk/geocoding';
|
|
30
29
|
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
|
31
30
|
import { FeaturesClickEventType, FeaturesHoverEventType, MapClickEventType, MapExtentChangeEventType, SourceLoadErrorType, computeMapContextDiff, createViewFromLayer } from '@geospatial-sdk/core';
|
|
32
31
|
import { createMapFromContext, applyContextDiffToMap, listen } from '@geospatial-sdk/openlayers';
|
|
@@ -41,10 +40,10 @@ import * as TOML from '@ltd/j-toml';
|
|
|
41
40
|
import { Style, Stroke, Fill, Circle } from 'ol/style.js';
|
|
42
41
|
import CircleStyle from 'ol/style/Circle.js';
|
|
43
42
|
import EmblaCarousel from 'embla-carousel';
|
|
44
|
-
import { iconoirNavArrowLeft, iconoirNavArrowRight, iconoirLongArrowDownLeft, iconoirSearch, iconoirCalendar, iconoirArrowUp, iconoirLink, iconoirFramePlusIn, iconoirCloudUpload, iconoirReduce, iconoirNavArrowUp, iconoirNavArrowDown, iconoirExpand, iconoirSettings, iconoirDownload, iconoirPlus, iconoirBin, iconoirMediaImageXmark, iconoirMediaImage, iconoirAppWindow, iconoirCode, iconoirDatabase, iconoirAppleWallet, iconoirBank, iconoirList, iconoirTranslate, iconoirLock, iconoirUser, iconoirArrowLeft, iconoirLightBulbOn,
|
|
43
|
+
import { iconoirNavArrowLeft, iconoirNavArrowRight, iconoirLongArrowDownLeft, iconoirSearch, iconoirCalendar, iconoirArrowUp, iconoirLink, iconoirFramePlusIn, iconoirCloudUpload, iconoirTrash, iconoirSquareDashed, iconoirImport, iconoirCheckCircle, iconoirReduce, iconoirNavArrowUp, iconoirNavArrowDown, iconoirExpand, iconoirSettings, iconoirDownload, iconoirPlus, iconoirBin, iconoirMediaImageXmark, iconoirMediaImage, iconoirAppWindow, iconoirCode, iconoirDatabase, iconoirAppleWallet, iconoirBank, iconoirList, iconoirTranslate, iconoirLock, iconoirUser, iconoirArrowLeft, iconoirLightBulbOn, iconoirBadgeCheck, iconoirSystemShut, iconoirCircle, iconoirAttachment, iconoirRefresh } from '@ng-icons/iconoir';
|
|
45
44
|
import { MatButtonModule } from '@angular/material/button';
|
|
46
|
-
import * as i1$
|
|
47
|
-
import {
|
|
45
|
+
import * as i1$7 from '@angular/cdk/overlay';
|
|
46
|
+
import { ScrollStrategyOptions, OverlayModule, CdkConnectedOverlay, ScrollDispatcher, Overlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
|
|
48
47
|
import * as i1$3 from '@angular/forms';
|
|
49
48
|
import { UntypedFormControl, ReactiveFormsModule, FormsModule } from '@angular/forms';
|
|
50
49
|
import * as i1$2 from '@angular/material/autocomplete';
|
|
@@ -52,16 +51,18 @@ import { MatAutocompleteModule, MatAutocompleteTrigger, MatAutocomplete } from '
|
|
|
52
51
|
import tippy from 'tippy.js';
|
|
53
52
|
import * as i2 from '@angular/material/progress-spinner';
|
|
54
53
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
55
|
-
import { matClose, matContentCopy,
|
|
54
|
+
import { matClose, matContentCopy, matExpandMore, matExpandLess, matSearch, matStarBorder, matStar, matRemove, matAdd, matChevronRight, matChevronLeft, matArrowForwardIos, matArrowBackIos, matMoodBad, matQuestionMark, matFace, matZoomOutMap, matOpenInNew, matPersonOutline, matMailOutline, matWarningAmber, matCheck, matCheckCircleOutline, matWarning, matMoreVert } from '@ng-icons/material-icons/baseline';
|
|
56
55
|
import * as i1$4 from '@angular/material/checkbox';
|
|
57
56
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
58
57
|
import * as i1$5 from '@angular/material/tooltip';
|
|
59
58
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
60
|
-
import {
|
|
59
|
+
import { MAT_DATE_LOCALE, MAT_DATE_FORMATS, DateAdapter } from '@angular/material/core';
|
|
60
|
+
import { MAT_DATE_FNS_FORMATS, DateFnsAdapter } from '@angular/material-date-fns-adapter';
|
|
61
|
+
import { enUS } from 'date-fns/locale/en-US';
|
|
61
62
|
import * as i1$6 from '@angular/material/datepicker';
|
|
62
63
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
|
63
|
-
import * as i1$
|
|
64
|
-
import { NgxDropzoneModule } from 'ngx-dropzone';
|
|
64
|
+
import * as i1$8 from 'ngx-dropzone';
|
|
65
|
+
import { NgxDropzoneModule, NgxDropzoneComponent } from 'ngx-dropzone';
|
|
65
66
|
import * as i1$9 from '@angular/material/button-toggle';
|
|
66
67
|
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
|
67
68
|
import { moveItemInArray, CdkDropList, CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop';
|
|
@@ -72,6 +73,7 @@ import { MatTabsModule } from '@angular/material/tabs';
|
|
|
72
73
|
import * as i2$2 from '@ngrx/effects';
|
|
73
74
|
import { Actions, createEffect, ofType, EffectsModule } from '@ngrx/effects';
|
|
74
75
|
import { valid as valid$1 } from 'geojson-validation';
|
|
76
|
+
import { queryDataGouvFr, queryGeonames, queryGeoadmin } from '@geospatial-sdk/geocoding';
|
|
75
77
|
import { TemplatePortal } from '@angular/cdk/portal';
|
|
76
78
|
import { parse as parse$3 } from 'marked';
|
|
77
79
|
import * as i2$3 from '@angular/cdk/scrolling';
|
|
@@ -79,10 +81,10 @@ import { ScrollingModule, CdkScrollable } from '@angular/cdk/scrolling';
|
|
|
79
81
|
import Duration from 'duration-relativetimeformat';
|
|
80
82
|
import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
|
|
81
83
|
import { trigger, transition, animate, keyframes, style } from '@angular/animations';
|
|
82
|
-
import * as
|
|
83
|
-
import {
|
|
84
|
-
import
|
|
85
|
-
import
|
|
84
|
+
import * as duckdb from '@duckdb/duckdb-wasm';
|
|
85
|
+
import { Vector, DataType } from 'apache-arrow';
|
|
86
|
+
import WFS from 'ol/format/WFS';
|
|
87
|
+
import GeoJSON$1 from 'ol/format/GeoJSON';
|
|
86
88
|
import { Chart, BarController, BarElement, CategoryScale, LinearScale, LineController, LineElement, PointElement, PieController, ArcElement, ScatterController, Tooltip, Colors, Legend } from 'chart.js';
|
|
87
89
|
import * as i2$4 from '@angular/material/sort';
|
|
88
90
|
import { MatSortModule, MatSort } from '@angular/material/sort';
|
|
@@ -90,7 +92,6 @@ import * as i1$b from '@angular/material/table';
|
|
|
90
92
|
import { MatTableModule } from '@angular/material/table';
|
|
91
93
|
import * as i3 from '@angular/material/paginator';
|
|
92
94
|
import { MatPaginatorIntl, MatPaginatorModule, MatPaginator } from '@angular/material/paginator';
|
|
93
|
-
import { LetDirective } from '@ngrx/component';
|
|
94
95
|
import { Meta } from '@angular/platform-browser';
|
|
95
96
|
import { tablerFolderOpen } from '@ng-icons/tabler-icons';
|
|
96
97
|
import * as i3$1 from '@angular/material/radio';
|
|
@@ -18907,14 +18908,29 @@ class Gn4FieldMapper {
|
|
|
18907
18908
|
featureTypes: selectField(source, 'featureTypes'),
|
|
18908
18909
|
}, output),
|
|
18909
18910
|
related: (output, source) => {
|
|
18910
|
-
const
|
|
18911
|
+
const related = selectField(source, 'related');
|
|
18912
|
+
const fcatSource = selectField(getFirstValue(selectField(related, 'fcats')) ?? {}, '_source');
|
|
18911
18913
|
const featureCatalogIdentifier = selectField(fcatSource, 'uuid');
|
|
18912
|
-
const sourceOfLinks = getAsArray(selectField(
|
|
18914
|
+
const sourceOfLinks = getAsArray(selectField(related, 'hassources'));
|
|
18913
18915
|
const sourceOfIdentifiers = sourceOfLinks
|
|
18914
18916
|
.filter((link) => link['origin'] === 'catalog')
|
|
18915
18917
|
.map((link) => {
|
|
18916
18918
|
return selectField(selectField(link, '_source'), 'uuid');
|
|
18917
18919
|
});
|
|
18920
|
+
const siblingLinks = getAsArray(selectField(related, 'siblings'));
|
|
18921
|
+
const siblings = siblingLinks
|
|
18922
|
+
.filter((link) => link['origin'] === 'catalog')
|
|
18923
|
+
.map((link) => ({
|
|
18924
|
+
uniqueIdentifier: selectField(selectField(link, '_source'), 'uuid'),
|
|
18925
|
+
associationType: getAssociationTypeFromCode(selectField(selectField(link, 'properties'), 'associationType')),
|
|
18926
|
+
}));
|
|
18927
|
+
const associatedLinks = getAsArray(selectField(related, 'associated'));
|
|
18928
|
+
const associatedIdentifiers = associatedLinks
|
|
18929
|
+
.filter((link) => link['origin'] === 'catalog')
|
|
18930
|
+
.map((link) => {
|
|
18931
|
+
return selectField(selectField(link, '_source'), 'uuid');
|
|
18932
|
+
})
|
|
18933
|
+
.filter((uuid) => !siblings.some((sibling) => sibling.uniqueIdentifier === uuid));
|
|
18918
18934
|
const extraValues = {};
|
|
18919
18935
|
if (featureCatalogIdentifier) {
|
|
18920
18936
|
extraValues.featureCatalogIdentifier = featureCatalogIdentifier;
|
|
@@ -18922,6 +18938,12 @@ class Gn4FieldMapper {
|
|
|
18922
18938
|
if (sourceOfIdentifiers && sourceOfIdentifiers.length > 0) {
|
|
18923
18939
|
extraValues.sourceOfIdentifiers = sourceOfIdentifiers;
|
|
18924
18940
|
}
|
|
18941
|
+
if (associatedIdentifiers && associatedIdentifiers.length > 0) {
|
|
18942
|
+
extraValues.associatedIdentifiers = associatedIdentifiers;
|
|
18943
|
+
}
|
|
18944
|
+
if (siblings && siblings.length > 0) {
|
|
18945
|
+
extraValues.siblings = siblings;
|
|
18946
|
+
}
|
|
18925
18947
|
return Object.keys(extraValues).length > 0
|
|
18926
18948
|
? this.addExtra(extraValues, output)
|
|
18927
18949
|
: output;
|
|
@@ -19447,10 +19469,10 @@ class DateService {
|
|
|
19447
19469
|
const dateObj = this.getDateObject(date);
|
|
19448
19470
|
return { locale, dateObj };
|
|
19449
19471
|
}
|
|
19450
|
-
async
|
|
19472
|
+
async getDateFnsLocale() {
|
|
19451
19473
|
const lang = this.translateService.getCurrentLang() || DEFAULT_LANGUAGE;
|
|
19452
19474
|
const locales = await this.dateLocales;
|
|
19453
|
-
return locales[lang];
|
|
19475
|
+
return locales[lang] ?? locales[DEFAULT_LANGUAGE];
|
|
19454
19476
|
}
|
|
19455
19477
|
formatDate(date, options) {
|
|
19456
19478
|
const { locale, dateObj } = this.getLocaleAndDate(date);
|
|
@@ -19463,7 +19485,7 @@ class DateService {
|
|
|
19463
19485
|
async formatRelativeDateTime(date) {
|
|
19464
19486
|
const dateObj = this.getDateObject(date);
|
|
19465
19487
|
const now = new Date();
|
|
19466
|
-
const locale = await this.
|
|
19488
|
+
const locale = await this.getDateFnsLocale();
|
|
19467
19489
|
return formatDistance(dateObj, now, {
|
|
19468
19490
|
addSuffix: true,
|
|
19469
19491
|
locale: locale,
|
|
@@ -19498,6 +19520,18 @@ function propagateToDocumentOnly(event) {
|
|
|
19498
19520
|
}, 0);
|
|
19499
19521
|
}
|
|
19500
19522
|
|
|
19523
|
+
function isFileExtensionValid(fileName, acceptedExtensions) {
|
|
19524
|
+
return acceptedExtensions.some((ext) => fileName.toLowerCase().endsWith(ext));
|
|
19525
|
+
}
|
|
19526
|
+
function readFileAsText(file) {
|
|
19527
|
+
return new Promise((resolve, reject) => {
|
|
19528
|
+
const reader = new FileReader();
|
|
19529
|
+
reader.onload = () => resolve(reader.result);
|
|
19530
|
+
reader.onerror = () => reject(reader.error);
|
|
19531
|
+
reader.readAsText(file);
|
|
19532
|
+
});
|
|
19533
|
+
}
|
|
19534
|
+
|
|
19501
19535
|
function formatUserInfo(userInfo, displayCount = false) {
|
|
19502
19536
|
const infos = (typeof userInfo === 'string' ? userInfo : '').split('|');
|
|
19503
19537
|
const count = displayCount ? ` ${infos[3].split(' ')[1]}` : '';
|
|
@@ -19557,6 +19591,11 @@ function getGeometryFromGeoJSON(data) {
|
|
|
19557
19591
|
}
|
|
19558
19592
|
return null;
|
|
19559
19593
|
}
|
|
19594
|
+
function isBoundingBox(value) {
|
|
19595
|
+
return (Array.isArray(value) &&
|
|
19596
|
+
value.length === 4 &&
|
|
19597
|
+
value.every((item) => typeof item === 'number'));
|
|
19598
|
+
}
|
|
19560
19599
|
function getGeometryBoundingBox(geometry) {
|
|
19561
19600
|
// use the bounding box if specified in the GeoJSON object
|
|
19562
19601
|
if (geometry.bbox) {
|
|
@@ -20225,7 +20264,7 @@ function getLinkLabel(link) {
|
|
|
20225
20264
|
const label = link.description || ('name' in link ? link.name : '');
|
|
20226
20265
|
return format ? `${label} (${format})` : label;
|
|
20227
20266
|
}
|
|
20228
|
-
async function getLayers(url, serviceProtocol) {
|
|
20267
|
+
async function getLayers(url, serviceProtocol, deep = false) {
|
|
20229
20268
|
switch (serviceProtocol) {
|
|
20230
20269
|
case 'ogcFeatures': {
|
|
20231
20270
|
const layers = await new OgcApiEndpoint(url).allCollections;
|
|
@@ -20233,7 +20272,10 @@ async function getLayers(url, serviceProtocol) {
|
|
|
20233
20272
|
}
|
|
20234
20273
|
case 'wfs': {
|
|
20235
20274
|
const endpointWfs = await new WfsEndpoint(url).isReady();
|
|
20236
|
-
const featureTypes =
|
|
20275
|
+
const featureTypes = endpointWfs.getFeatureTypes();
|
|
20276
|
+
if (!deep) {
|
|
20277
|
+
return featureTypes;
|
|
20278
|
+
}
|
|
20237
20279
|
const layers = (await Promise.allSettled(featureTypes.map((collection) => {
|
|
20238
20280
|
return endpointWfs.getFeatureTypeFull(collection.name);
|
|
20239
20281
|
})))
|
|
@@ -20243,12 +20285,14 @@ async function getLayers(url, serviceProtocol) {
|
|
|
20243
20285
|
}
|
|
20244
20286
|
case 'wms': {
|
|
20245
20287
|
const endpointWms = await new WmsEndpoint(url).isReady();
|
|
20246
|
-
const
|
|
20288
|
+
const layersSummary = endpointWms
|
|
20247
20289
|
.getLayers()
|
|
20248
20290
|
.flatMap(wmsLayerFlatten)
|
|
20249
|
-
.filter((l) => l.name)
|
|
20250
|
-
|
|
20251
|
-
|
|
20291
|
+
.filter((l) => l.name);
|
|
20292
|
+
if (!deep) {
|
|
20293
|
+
return layersSummary;
|
|
20294
|
+
}
|
|
20295
|
+
const layers = layersSummary.map((collection) => endpointWms.getLayerByName(collection.name));
|
|
20252
20296
|
return layers;
|
|
20253
20297
|
}
|
|
20254
20298
|
case 'wmts': {
|
|
@@ -20342,6 +20386,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
20342
20386
|
}]
|
|
20343
20387
|
}] });
|
|
20344
20388
|
|
|
20389
|
+
class AutofocusDirective {
|
|
20390
|
+
constructor() {
|
|
20391
|
+
this.el = inject(ElementRef);
|
|
20392
|
+
this.injector = inject(Injector);
|
|
20393
|
+
}
|
|
20394
|
+
set autofocus(active) {
|
|
20395
|
+
if (!active)
|
|
20396
|
+
return;
|
|
20397
|
+
afterNextRender(() => this.el.nativeElement.focus(), {
|
|
20398
|
+
injector: this.injector,
|
|
20399
|
+
});
|
|
20400
|
+
}
|
|
20401
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AutofocusDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
20402
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "20.3.19", type: AutofocusDirective, isStandalone: true, selector: "[gnUiAutofocus]", inputs: { autofocus: ["gnUiAutofocus", "autofocus", booleanAttribute] }, ngImport: i0 }); }
|
|
20403
|
+
}
|
|
20404
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AutofocusDirective, decorators: [{
|
|
20405
|
+
type: Directive,
|
|
20406
|
+
args: [{
|
|
20407
|
+
selector: '[gnUiAutofocus]',
|
|
20408
|
+
standalone: true,
|
|
20409
|
+
}]
|
|
20410
|
+
}], propDecorators: { autofocus: [{
|
|
20411
|
+
type: Input,
|
|
20412
|
+
args: [{ alias: 'gnUiAutofocus', transform: booleanAttribute }]
|
|
20413
|
+
}] } });
|
|
20414
|
+
|
|
20345
20415
|
class ImageFallbackDirective {
|
|
20346
20416
|
constructor() {
|
|
20347
20417
|
this.el = inject(ElementRef);
|
|
@@ -20369,7 +20439,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
20369
20439
|
}] } });
|
|
20370
20440
|
|
|
20371
20441
|
var name = "geonetwork-ui";
|
|
20372
|
-
var version = "2.11.0-dev.
|
|
20442
|
+
var version = "2.11.0-dev.9bd41a961";
|
|
20373
20443
|
var engines = {
|
|
20374
20444
|
node: ">=24"
|
|
20375
20445
|
};
|
|
@@ -20390,6 +20460,7 @@ var peerDependencies = {
|
|
|
20390
20460
|
"@angular/core": "19.x || 20.x || 21.x",
|
|
20391
20461
|
"@angular/forms": "19.x || 20.x || 21.x",
|
|
20392
20462
|
"@angular/material": "19.x || 20.x || 21.x",
|
|
20463
|
+
"@angular/material-date-fns-adapter": "19.x || 20.x",
|
|
20393
20464
|
"@angular/platform-browser": "19.x || 20.x || 21.x",
|
|
20394
20465
|
"@angular/platform-browser-dynamic": "19.x || 20.x || 21.x",
|
|
20395
20466
|
"@angular/router": "19.x || 20.x || 21.x",
|
|
@@ -20399,20 +20470,21 @@ var peerDependencies = {
|
|
|
20399
20470
|
"@ngrx/store": "19.x || 20.x || 21.x",
|
|
20400
20471
|
"@ngrx/store-devtools": "19.x || 20.x || 21.x",
|
|
20401
20472
|
"@ngrx/operators": "19.x || 20.x || 21.x",
|
|
20402
|
-
"@ngx-translate/core": "
|
|
20403
|
-
"@ngx-translate/http-loader": "
|
|
20404
|
-
"flag-icons": "
|
|
20473
|
+
"@ngx-translate/core": "17.x",
|
|
20474
|
+
"@ngx-translate/http-loader": "17.x",
|
|
20475
|
+
"flag-icons": "~7.5.0",
|
|
20405
20476
|
rxjs: "7.x",
|
|
20406
20477
|
"zone.js": "*",
|
|
20407
20478
|
tailwindcss: "3.x"
|
|
20408
20479
|
};
|
|
20409
20480
|
var dependencies = {
|
|
20410
20481
|
"@biesbjerg/ngx-translate-extract-marker": "~1.0.0",
|
|
20411
|
-
"@camptocamp/ogc-client": "1.3.1-dev.
|
|
20412
|
-
"@
|
|
20413
|
-
"@geospatial-sdk/
|
|
20414
|
-
"@geospatial-sdk/
|
|
20415
|
-
"@geospatial-sdk/
|
|
20482
|
+
"@camptocamp/ogc-client": "1.3.1-dev.200c02a",
|
|
20483
|
+
"@duckdb/duckdb-wasm": "~1.32.0",
|
|
20484
|
+
"@geospatial-sdk/core": "0.0.5-dev.78",
|
|
20485
|
+
"@geospatial-sdk/geocoding": "0.0.5-dev.78",
|
|
20486
|
+
"@geospatial-sdk/legend": "0.0.5-dev.78",
|
|
20487
|
+
"@geospatial-sdk/openlayers": "0.0.5-dev.78",
|
|
20416
20488
|
"@ltd/j-toml": "~1.38.0",
|
|
20417
20489
|
"@messageformat/core": "~3.4.0",
|
|
20418
20490
|
"@ng-icons/core": "~32.5.0",
|
|
@@ -20420,7 +20492,7 @@ var dependencies = {
|
|
|
20420
20492
|
"@ng-icons/material-icons": "~32.5.0",
|
|
20421
20493
|
"@ng-icons/tabler-icons": "~32.4.0",
|
|
20422
20494
|
"@rgrove/parse-xml": "4.2.0",
|
|
20423
|
-
|
|
20495
|
+
"apache-arrow": "~17.0.0",
|
|
20424
20496
|
"chart.js": "~4.5.1",
|
|
20425
20497
|
"chroma-js": "~3.2.0",
|
|
20426
20498
|
"date-fns": "4.1.0",
|
|
@@ -20433,7 +20505,6 @@ var dependencies = {
|
|
|
20433
20505
|
"ngx-dropzone": "~3.1.0",
|
|
20434
20506
|
"ngx-translate-messageformat-compiler": "~7.2.0",
|
|
20435
20507
|
ol: "~10.8.0",
|
|
20436
|
-
papaparse: "~5.5.3",
|
|
20437
20508
|
proj4: "~2.20.4",
|
|
20438
20509
|
rdflib: "~2.3.5",
|
|
20439
20510
|
semver: "~7.7.4",
|
|
@@ -20631,8 +20702,8 @@ class ElasticsearchService {
|
|
|
20631
20702
|
if (!payload.runtime_mappings)
|
|
20632
20703
|
payload.runtime_mappings = {};
|
|
20633
20704
|
payload.runtime_mappings[fieldName] = {
|
|
20634
|
-
type:
|
|
20635
|
-
script: this.runtimeFields[fieldName],
|
|
20705
|
+
type: this.runtimeFields[fieldName].type,
|
|
20706
|
+
script: this.runtimeFields[fieldName].script,
|
|
20636
20707
|
};
|
|
20637
20708
|
};
|
|
20638
20709
|
const lookForField = (node) => {
|
|
@@ -20652,6 +20723,12 @@ class ElasticsearchService {
|
|
|
20652
20723
|
(node[runtimeField] === 'asc' || node[runtimeField] === 'desc')) {
|
|
20653
20724
|
addMapping(runtimeField);
|
|
20654
20725
|
}
|
|
20726
|
+
if (runtimeField in node &&
|
|
20727
|
+
typeof node[runtimeField] === 'object' &&
|
|
20728
|
+
node[runtimeField] !== null &&
|
|
20729
|
+
('gte' in node[runtimeField] || 'lte' in node[runtimeField])) {
|
|
20730
|
+
addMapping(runtimeField);
|
|
20731
|
+
}
|
|
20655
20732
|
if ('query' in node &&
|
|
20656
20733
|
typeof node.query === 'string' &&
|
|
20657
20734
|
node.query.indexOf(runtimeField + ':') > -1) {
|
|
@@ -20668,8 +20745,8 @@ class ElasticsearchService {
|
|
|
20668
20745
|
lookForField(payload.query);
|
|
20669
20746
|
return payload;
|
|
20670
20747
|
}
|
|
20671
|
-
registerRuntimeField(fieldName, expression) {
|
|
20672
|
-
this.runtimeFields[fieldName] = expression;
|
|
20748
|
+
registerRuntimeField(fieldName, expression, type = 'keyword') {
|
|
20749
|
+
this.runtimeFields[fieldName] = { script: expression, type };
|
|
20673
20750
|
}
|
|
20674
20751
|
getMetadataByIdsPayload(uuids) {
|
|
20675
20752
|
return {
|
|
@@ -20678,6 +20755,7 @@ class ElasticsearchService {
|
|
|
20678
20755
|
values: uuids,
|
|
20679
20756
|
},
|
|
20680
20757
|
},
|
|
20758
|
+
size: uuids.length,
|
|
20681
20759
|
};
|
|
20682
20760
|
}
|
|
20683
20761
|
getRelatedRecordPayload(record, size = 6, _source = [...ES_SOURCE_SUMMARY, 'createDate']) {
|
|
@@ -20728,10 +20806,28 @@ class ElasticsearchService {
|
|
|
20728
20806
|
};
|
|
20729
20807
|
}
|
|
20730
20808
|
buildPayloadSort(sortBy) {
|
|
20731
|
-
if (sortBy ===
|
|
20809
|
+
if (!sortBy || sortBy.length === 0)
|
|
20732
20810
|
return undefined;
|
|
20733
|
-
const fields = Array.isArray(sortBy[0])
|
|
20734
|
-
|
|
20811
|
+
const fields = Array.isArray(sortBy[0])
|
|
20812
|
+
? sortBy
|
|
20813
|
+
: [sortBy];
|
|
20814
|
+
return fields.map((field) => {
|
|
20815
|
+
// Sort by nested array of dates only works with the explicit syntax
|
|
20816
|
+
if (field[1].endsWith('.date')) {
|
|
20817
|
+
const nestedPath = field[1].slice(0, field[1].lastIndexOf('.date'));
|
|
20818
|
+
return {
|
|
20819
|
+
[field[1]]: {
|
|
20820
|
+
order: field[0],
|
|
20821
|
+
mode: field[0] === 'desc' ? 'max' : 'min',
|
|
20822
|
+
missing: '_last',
|
|
20823
|
+
nested: {
|
|
20824
|
+
path: nestedPath,
|
|
20825
|
+
},
|
|
20826
|
+
},
|
|
20827
|
+
};
|
|
20828
|
+
}
|
|
20829
|
+
return { [field[1]]: field[0] };
|
|
20830
|
+
});
|
|
20735
20831
|
}
|
|
20736
20832
|
injectLangInQueryStringFields(queryFieldsPriority) {
|
|
20737
20833
|
const queryLang = this.getQueryLang();
|
|
@@ -20767,7 +20863,49 @@ class ElasticsearchService {
|
|
|
20767
20863
|
isCurrentSearchLang() {
|
|
20768
20864
|
return this.metadataLang === 'current';
|
|
20769
20865
|
}
|
|
20770
|
-
|
|
20866
|
+
findSpatialFilterExtent(filters) {
|
|
20867
|
+
if (typeof filters === 'string') {
|
|
20868
|
+
return undefined;
|
|
20869
|
+
}
|
|
20870
|
+
return Object.values(filters).find(isBoundingBox);
|
|
20871
|
+
}
|
|
20872
|
+
// if a field registered min/max runtime fields, its dates form an interval
|
|
20873
|
+
// the record matches if that interval intersects the filter range
|
|
20874
|
+
buildDateRangeQuery(searchField, dateRange) {
|
|
20875
|
+
const minField = `${searchField}Min`;
|
|
20876
|
+
const maxField = `${searchField}Max`;
|
|
20877
|
+
if (minField in this.runtimeFields && maxField in this.runtimeFields) {
|
|
20878
|
+
const filter = [
|
|
20879
|
+
dateRange.start && {
|
|
20880
|
+
range: {
|
|
20881
|
+
[maxField]: {
|
|
20882
|
+
gte: formatDate(dateRange.start),
|
|
20883
|
+
format: 'yyyy-MM-dd',
|
|
20884
|
+
},
|
|
20885
|
+
},
|
|
20886
|
+
},
|
|
20887
|
+
dateRange.end && {
|
|
20888
|
+
range: {
|
|
20889
|
+
[minField]: {
|
|
20890
|
+
lte: formatDate(dateRange.end),
|
|
20891
|
+
format: 'yyyy-MM-dd',
|
|
20892
|
+
},
|
|
20893
|
+
},
|
|
20894
|
+
},
|
|
20895
|
+
].filter(Boolean);
|
|
20896
|
+
return { bool: { filter } };
|
|
20897
|
+
}
|
|
20898
|
+
return {
|
|
20899
|
+
range: {
|
|
20900
|
+
[searchField]: {
|
|
20901
|
+
...(dateRange.start && { gte: formatDate(dateRange.start) }),
|
|
20902
|
+
...(dateRange.end && { lte: formatDate(dateRange.end) }),
|
|
20903
|
+
format: 'yyyy-MM-dd',
|
|
20904
|
+
},
|
|
20905
|
+
},
|
|
20906
|
+
};
|
|
20907
|
+
}
|
|
20908
|
+
filtersToQuery(filters, spatialFilterExtent = this.findSpatialFilterExtent(filters)) {
|
|
20771
20909
|
const addQuote = (key) => (/^\/.+\/$/.test(key) ? key : `"${key}"`);
|
|
20772
20910
|
const makeQuery = (filter) => {
|
|
20773
20911
|
if (typeof filter === 'string') {
|
|
@@ -20786,7 +20924,9 @@ class ElasticsearchService {
|
|
|
20786
20924
|
? filters
|
|
20787
20925
|
: Object.keys(filters)
|
|
20788
20926
|
.filter((fieldname) => fieldname !== 'gn-ui-crossFieldFilter')
|
|
20927
|
+
.filter((fieldname) => !isBoundingBox(filters[fieldname]))
|
|
20789
20928
|
.filter((fieldname) => !isDateRange(filters[fieldname]))
|
|
20929
|
+
.filter((fieldname) => !Array.isArray(filters[fieldname]))
|
|
20790
20930
|
.filter((fieldname) => filters[fieldname] &&
|
|
20791
20931
|
JSON.stringify(filters[fieldname]) !== '{}')
|
|
20792
20932
|
.map((fieldname) => `${fieldname}:(${makeQuery(filters[fieldname])})`)
|
|
@@ -20794,31 +20934,26 @@ class ElasticsearchService {
|
|
|
20794
20934
|
if (filters['gn-ui-crossFieldFilter']) {
|
|
20795
20935
|
queryString = `${queryString} AND (${filters['gn-ui-crossFieldFilter']})`;
|
|
20796
20936
|
}
|
|
20797
|
-
const
|
|
20798
|
-
.filter(([, value]) => isDateRange(value))
|
|
20799
|
-
.map(([searchField, dateRange]) => {
|
|
20800
|
-
return {
|
|
20801
|
-
searchField,
|
|
20802
|
-
dateRange,
|
|
20803
|
-
};
|
|
20804
|
-
})[0];
|
|
20937
|
+
const queryRanges = Object.entries(filters).filter(([, value]) => isDateRange(value));
|
|
20805
20938
|
const queryParts = [
|
|
20806
20939
|
queryString && {
|
|
20807
20940
|
query_string: {
|
|
20808
20941
|
query: queryString,
|
|
20809
20942
|
},
|
|
20810
20943
|
},
|
|
20811
|
-
|
|
20812
|
-
|
|
20813
|
-
|
|
20814
|
-
|
|
20815
|
-
|
|
20816
|
-
|
|
20817
|
-
|
|
20818
|
-
|
|
20819
|
-
|
|
20820
|
-
|
|
20821
|
-
|
|
20944
|
+
...queryRanges.map(([searchField, dateRange]) => this.buildDateRangeQuery(searchField, dateRange)),
|
|
20945
|
+
spatialFilterExtent && {
|
|
20946
|
+
geo_shape: {
|
|
20947
|
+
geom: {
|
|
20948
|
+
shape: {
|
|
20949
|
+
type: 'envelope',
|
|
20950
|
+
// spatialFilterExtent is [minX, minY, maxX, maxY]; envelope coordinates are [top-left, bottom-right]
|
|
20951
|
+
coordinates: [
|
|
20952
|
+
[spatialFilterExtent[0], spatialFilterExtent[3]],
|
|
20953
|
+
[spatialFilterExtent[2], spatialFilterExtent[1]],
|
|
20954
|
+
],
|
|
20955
|
+
},
|
|
20956
|
+
relation: 'intersects',
|
|
20822
20957
|
},
|
|
20823
20958
|
},
|
|
20824
20959
|
},
|
|
@@ -20848,7 +20983,9 @@ class ElasticsearchService {
|
|
|
20848
20983
|
},
|
|
20849
20984
|
});
|
|
20850
20985
|
}
|
|
20851
|
-
|
|
20986
|
+
// a spatial extent filter takes precedence over the preference geometry for boosting
|
|
20987
|
+
const spatialFilterExtent = this.findSpatialFilterExtent(fieldSearchFilters);
|
|
20988
|
+
const queryFilters = this.filtersToQuery(fieldSearchFilters, spatialFilterExtent);
|
|
20852
20989
|
if (queryFilters) {
|
|
20853
20990
|
filter.push(...queryFilters);
|
|
20854
20991
|
}
|
|
@@ -20859,7 +20996,10 @@ class ElasticsearchService {
|
|
|
20859
20996
|
},
|
|
20860
20997
|
});
|
|
20861
20998
|
}
|
|
20862
|
-
|
|
20999
|
+
const boostGeometry = spatialFilterExtent
|
|
21000
|
+
? bboxToPolygon(spatialFilterExtent)
|
|
21001
|
+
: geometry;
|
|
21002
|
+
if (boostGeometry) {
|
|
20863
21003
|
// boosts applied using the filter geometry:
|
|
20864
21004
|
// * records completely within the geometry receive a boost of 5
|
|
20865
21005
|
// * records intersecting the geometry receive a boost of 2
|
|
@@ -20868,7 +21008,7 @@ class ElasticsearchService {
|
|
|
20868
21008
|
should.push({
|
|
20869
21009
|
geo_shape: {
|
|
20870
21010
|
geom: {
|
|
20871
|
-
shape:
|
|
21011
|
+
shape: boostGeometry,
|
|
20872
21012
|
relation: 'within',
|
|
20873
21013
|
},
|
|
20874
21014
|
boost: 5.0,
|
|
@@ -20876,7 +21016,7 @@ class ElasticsearchService {
|
|
|
20876
21016
|
}, {
|
|
20877
21017
|
geo_shape: {
|
|
20878
21018
|
geom: {
|
|
20879
|
-
shape:
|
|
21019
|
+
shape: boostGeometry,
|
|
20880
21020
|
relation: 'intersects',
|
|
20881
21021
|
},
|
|
20882
21022
|
boost: 2.0,
|
|
@@ -20885,7 +21025,7 @@ class ElasticsearchService {
|
|
|
20885
21025
|
// this will boost the results variably depending on their distance from the given geometry
|
|
20886
21026
|
// note: this takes into account the `location` field of a record; this is generally the center of all spatial extents
|
|
20887
21027
|
// combined, and thus the actual size/coverage of the record spatial extent isn't relevant here
|
|
20888
|
-
const bbox = getGeometryBoundingBox(
|
|
21028
|
+
const bbox = getGeometryBoundingBox(boostGeometry);
|
|
20889
21029
|
const center = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2];
|
|
20890
21030
|
const northToCenter = new LineString([
|
|
20891
21031
|
[center[0], bbox[3]],
|
|
@@ -21211,7 +21351,7 @@ class Gn4Repository {
|
|
|
21211
21351
|
}
|
|
21212
21352
|
getRecord(uniqueIdentifier) {
|
|
21213
21353
|
return this.gn4SearchApi
|
|
21214
|
-
.search('bucket', ['fcats', 'hassources'], JSON.stringify(this.gn4SearchHelper.getMetadataByIdsPayload([uniqueIdentifier])))
|
|
21354
|
+
.search('bucket', ['fcats', 'hassources', 'siblings', 'associated'], JSON.stringify(this.gn4SearchHelper.getMetadataByIdsPayload([uniqueIdentifier])))
|
|
21215
21355
|
.pipe(map$1((results) => results.hits.hits[0]), switchMap((record) => record ? this.gn4Mapper.readRecord(record) : of(null)));
|
|
21216
21356
|
}
|
|
21217
21357
|
getMultipleRecords(uniqueIdentifiers) {
|
|
@@ -21265,19 +21405,28 @@ class Gn4Repository {
|
|
|
21265
21405
|
.search('bucket', null, JSON.stringify(this.gn4SearchHelper.getRelatedRecordPayload(similarTo, 3)))
|
|
21266
21406
|
.pipe(switchMap((results) => this.gn4Mapper.readRecords(results.hits.hits)));
|
|
21267
21407
|
}
|
|
21268
|
-
|
|
21269
|
-
const
|
|
21270
|
-
|
|
21271
|
-
|
|
21272
|
-
|
|
21273
|
-
|
|
21274
|
-
|
|
21275
|
-
|
|
21276
|
-
|
|
21277
|
-
|
|
21278
|
-
|
|
21408
|
+
getLinkedRecords(record) {
|
|
21409
|
+
const siblings = (record.extras?.['siblings'] ?? []);
|
|
21410
|
+
const relations = [
|
|
21411
|
+
['source', (record.extras?.['sourcesIdentifiers'] ?? [])],
|
|
21412
|
+
['sourceOf', (record.extras?.['sourceOfIdentifiers'] ?? [])],
|
|
21413
|
+
['sibling', siblings.map(({ uniqueIdentifier }) => uniqueIdentifier)],
|
|
21414
|
+
[
|
|
21415
|
+
'associated',
|
|
21416
|
+
(record.extras?.['associatedIdentifiers'] ?? []),
|
|
21417
|
+
],
|
|
21418
|
+
];
|
|
21419
|
+
const requested = relations.filter(([, identifiers]) => identifiers.length > 0);
|
|
21420
|
+
if (requested.length === 0) {
|
|
21421
|
+
return of([]);
|
|
21279
21422
|
}
|
|
21280
|
-
return
|
|
21423
|
+
return forkJoin(requested.map(([relation, identifiers]) => this.getMultipleRecords(identifiers).pipe(map$1((records) => (records ?? []).map((record) => ({
|
|
21424
|
+
record,
|
|
21425
|
+
relation,
|
|
21426
|
+
associationType: relation === 'sibling'
|
|
21427
|
+
? siblings.find(({ uniqueIdentifier }) => uniqueIdentifier === record.uniqueIdentifier)?.associationType
|
|
21428
|
+
: undefined,
|
|
21429
|
+
}))), catchError(() => of([]))))).pipe(map$1((groups) => groups.flat()));
|
|
21281
21430
|
}
|
|
21282
21431
|
aggregate(params) {
|
|
21283
21432
|
// if aggregations are empty, return an empty object right away
|
|
@@ -22466,48 +22615,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
22466
22615
|
type: Injectable
|
|
22467
22616
|
}] });
|
|
22468
22617
|
|
|
22469
|
-
const GEOCODING_PROVIDER = new InjectionToken('geocoding-provider');
|
|
22470
|
-
class GeocodingService {
|
|
22471
|
-
constructor() {
|
|
22472
|
-
this.provider = inject(GEOCODING_PROVIDER);
|
|
22473
|
-
}
|
|
22474
|
-
query(text) {
|
|
22475
|
-
let queryObservable;
|
|
22476
|
-
switch (this.provider[0]) {
|
|
22477
|
-
case 'geoadmin':
|
|
22478
|
-
queryObservable = from(queryGeoadmin(text, this.provider[1]));
|
|
22479
|
-
break;
|
|
22480
|
-
case 'geonames':
|
|
22481
|
-
queryObservable = from(queryGeonames(text, this.provider[1]));
|
|
22482
|
-
break;
|
|
22483
|
-
case 'data-gouv-fr':
|
|
22484
|
-
queryObservable = from(queryDataGouvFr(text, this.provider[1]));
|
|
22485
|
-
break;
|
|
22486
|
-
default:
|
|
22487
|
-
return throwError(() => new Error(`Unsupported geocoding provider: ${this.provider[0]}`));
|
|
22488
|
-
}
|
|
22489
|
-
return queryObservable.pipe(catchError((error) => throwError(error)));
|
|
22490
|
-
}
|
|
22491
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
22492
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingService, providedIn: 'root' }); }
|
|
22493
|
-
}
|
|
22494
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingService, decorators: [{
|
|
22495
|
-
type: Injectable,
|
|
22496
|
-
args: [{
|
|
22497
|
-
providedIn: 'root',
|
|
22498
|
-
}]
|
|
22499
|
-
}] });
|
|
22500
|
-
|
|
22501
22618
|
class FeatureMapModule {
|
|
22502
22619
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FeatureMapModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
22503
22620
|
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.19", ngImport: i0, type: FeatureMapModule, imports: [i1.StoreFeatureModule] }); }
|
|
22504
|
-
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FeatureMapModule, providers: [
|
|
22505
|
-
MapFacade,
|
|
22506
|
-
{
|
|
22507
|
-
provide: GEOCODING_PROVIDER,
|
|
22508
|
-
useValue: ['geonames', { maxRows: 5 }],
|
|
22509
|
-
},
|
|
22510
|
-
], imports: [StoreModule.forFeature(MAP_FEATURE_KEY, mapReducer)] }); }
|
|
22621
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FeatureMapModule, providers: [MapFacade], imports: [StoreModule.forFeature(MAP_FEATURE_KEY, mapReducer)] }); }
|
|
22511
22622
|
}
|
|
22512
22623
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FeatureMapModule, decorators: [{
|
|
22513
22624
|
type: NgModule,
|
|
@@ -22515,13 +22626,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
22515
22626
|
imports: [
|
|
22516
22627
|
StoreModule.forFeature(MAP_FEATURE_KEY, mapReducer),
|
|
22517
22628
|
],
|
|
22518
|
-
providers: [
|
|
22519
|
-
MapFacade,
|
|
22520
|
-
{
|
|
22521
|
-
provide: GEOCODING_PROVIDER,
|
|
22522
|
-
useValue: ['geonames', { maxRows: 5 }],
|
|
22523
|
-
},
|
|
22524
|
-
],
|
|
22629
|
+
providers: [MapFacade],
|
|
22525
22630
|
}]
|
|
22526
22631
|
}] });
|
|
22527
22632
|
|
|
@@ -22538,9 +22643,10 @@ const VECTOR_STYLE_DEFAULT = new InjectionToken('vectorStyleDefault', {
|
|
|
22538
22643
|
});
|
|
22539
22644
|
|
|
22540
22645
|
const DEFAULT_BASEMAP_LAYER = {
|
|
22541
|
-
type: '
|
|
22542
|
-
|
|
22543
|
-
|
|
22646
|
+
type: 'maplibre-style',
|
|
22647
|
+
styleUrl: `https://basemaps.cartocdn.com/gl/positron-gl-style/style.json`,
|
|
22648
|
+
clickable: false,
|
|
22649
|
+
hoverable: false,
|
|
22544
22650
|
};
|
|
22545
22651
|
const DEFAULT_VIEW = {
|
|
22546
22652
|
center: [0, 15],
|
|
@@ -23255,6 +23361,7 @@ function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
23255
23361
|
'search_preset',
|
|
23256
23362
|
'advanced_filters',
|
|
23257
23363
|
'limit',
|
|
23364
|
+
'spatial_extent_max_file_size',
|
|
23258
23365
|
], warnings, errors);
|
|
23259
23366
|
const parsedSearchParams = parseMultiConfigSection(parsed, 'search_preset', ['name'], ['sort', 'filters'], warnings, errors);
|
|
23260
23367
|
searchConfig =
|
|
@@ -23272,6 +23379,7 @@ function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
23272
23379
|
})),
|
|
23273
23380
|
ADVANCED_FILTERS: parsedSearchSection.advanced_filters,
|
|
23274
23381
|
LIMIT: parsedSearchSection.limit,
|
|
23382
|
+
SPATIAL_EXTENT_MAX_FILE_SIZE: parsedSearchSection.spatial_extent_max_file_size,
|
|
23275
23383
|
};
|
|
23276
23384
|
const parsedMetadataQualitySection = parseConfigSection(parsed, 'metadata-quality', [], ['enabled'], warnings, errors);
|
|
23277
23385
|
metadataQualityConfig =
|
|
@@ -24329,7 +24437,7 @@ class AutocompleteComponent {
|
|
|
24329
24437
|
this.enterBtnPosition = event.target['value'].length * 8 + 80;
|
|
24330
24438
|
}
|
|
24331
24439
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AutocompleteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
24332
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: AutocompleteComponent, isStandalone: true, selector: "gn-ui-autocomplete", inputs: { placeholder: "placeholder", enterButton: "enterButton", action: "action", value: "value", clearOnSelection: "clearOnSelection", preventCompleteOnSelection: "preventCompleteOnSelection", autoFocus: "autoFocus", minCharacterCount: "minCharacterCount", allowSubmit: "allowSubmit", forceTrackPosition: "forceTrackPosition", displayWithFn: "displayWithFn" }, outputs: { itemSelected: "itemSelected", inputSubmitted: "inputSubmitted", inputCleared: "inputCleared", isSearchActive: "isSearchActive" }, providers: [
|
|
24440
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: AutocompleteComponent, isStandalone: true, selector: "gn-ui-autocomplete", inputs: { placeholder: "placeholder", enterButton: "enterButton", action: "action", value: "value", clearOnSelection: "clearOnSelection", preventCompleteOnSelection: "preventCompleteOnSelection", autoFocus: "autoFocus", minCharacterCount: "minCharacterCount", allowSubmit: "allowSubmit", forceTrackPosition: "forceTrackPosition", displayWithTemplate: "displayWithTemplate", displayWithFn: "displayWithFn" }, outputs: { itemSelected: "itemSelected", inputSubmitted: "inputSubmitted", inputCleared: "inputCleared", isSearchActive: "isSearchActive" }, providers: [
|
|
24333
24441
|
provideIcons({
|
|
24334
24442
|
iconoirSearch,
|
|
24335
24443
|
matClose,
|
|
@@ -24338,7 +24446,7 @@ class AutocompleteComponent {
|
|
|
24338
24446
|
provideNgIconsConfig({
|
|
24339
24447
|
size: '1.75rem',
|
|
24340
24448
|
}),
|
|
24341
|
-
], viewQueries: [{ propertyName: "triggerRef", first: true, predicate: MatAutocompleteTrigger, descendants: true }, { propertyName: "autocomplete", first: true, predicate: MatAutocomplete, descendants: true }, { propertyName: "inputRef", first: true, predicate: ["searchInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<span class=\"w-full inline-block relative\">\n @if (!allowSubmit) {\n <div\n class=\"absolute inset-y-[--icon-padding] left-[--icon-padding] w-[--icon-width] pointer-events-none\"\n >\n <ng-icon name=\"iconoirSearch\" class=\"text-primary search\"></ng-icon>\n </div>\n }\n <div class=\"flex flex-row\">\n <input\n #searchInput\n type=\"text\"\n class=\"gn-ui-text-input\"\n (input)=\"handleInput($event)\"\n [placeholder]=\"placeholder\"\n [formControl]=\"control\"\n [matAutocomplete]=\"auto\"\n (keyup.enter)=\"handleSearch()\"\n [ngClass]=\"{\n 'text-primary': searchActive && enterButton,\n 'text-gray-900': !searchActive,\n 'px-[--icon-width]': !allowSubmit,\n }\"\n />\n @if (searchInput.value && displayEnterBtn) {\n <gn-ui-button\n type=\"gray\"\n extraClass=\"w-32 h-8 !opacity-100\"\n [ngStyle]=\"{ left: enterBtnPosition + 'px' }\"\n class=\"absolute\"\n disabled=\"true\"\n >\n <ng-icon\n name=\"iconoirLongArrowDownLeft\"\n class=\"!w-4 text-gray-900 font-bold\"\n ></ng-icon>\n <span translate class=\"text-bold text-gray-900 font-bold\">\n Enter to search\n </span>\n </gn-ui-button>\n }\n </div>\n @if (searchInput.value) {\n <gn-ui-button\n type=\"primary\"\n [extraClass]=\"getExtraClass()\"\n data-test=\"clear-btn\"\n aria-label=\"Clear\"\n (buttonClick)=\"clear()\"\n >\n <ng-icon class=\"text-white\" name=\"matClose\"></ng-icon>\n </gn-ui-button>\n }\n @if (allowSubmit) {\n <gn-ui-button\n type=\"light\"\n extraClass=\"border-0 border-l-[1px] border-gray-300 hover:border-gray-500 text-primary hover:text-primary-darkest focus:text-primary-darkest absolute inset-y-[--icon-padding] right-[--icon-padding]\"\n aria-label=\"Trigger search\"\n data-test=\"autocomplete-submit-btn\"\n (buttonClick)=\"handleSearch()\"\n >\n <ng-icon name=\"iconoirSearch\"></ng-icon>\n </gn-ui-button>\n }\n\n @if (error) {\n <gn-ui-popup-alert\n class=\"absolute mt-2 w-full top-[100%] left-0\"\n icon=\"matErrorOutlineOutline\"\n position=\"top\"\n type=\"warning\"\n >\n <span translate>search.autocomplete.error</span>\n {{ error }}\n </gn-ui-popup-alert>\n }\n</span>\n\n<mat-autocomplete\n #auto=\"matAutocomplete\"\n (optionSelected)=\"handleSelection($event)\"\n [displayWith]=\"displayWithFnInternal\"\n>\n @for (suggestion of suggestions$ | async; track suggestion) {\n <mat-option [value]=\"suggestion\" class=\"p-2 suggestion text-start\">\n {{ displayWithFnInternal(suggestion) }}\n </mat-option>\n }\n</mat-autocomplete>\n", styles: ["span{--icon-padding: var(--gn-ui-text-input-border-size, 2px);--icon-width: calc(var(--gn-ui-text-input-padding, .6em) * 2 + 1.25em)}span>*:not(gn-ui-popup-alert){font-size:var(--gn-ui-text-input-font-size, 1em)}input:placeholder-shown{text-overflow:ellipsis}gn-ui-button{--gn-ui-button-rounded: 0;--gn-ui-button-width: var(--icon-width);--gn-ui-button-padding: 0;font-size:1em}ng-icon.search{margin-top:.6rem;margin-left:.6rem}gn-ui-button:last-of-type{--gn-ui-button-rounded: 0 var(--gn-ui-text-input-rounded, .25em) var(--gn-ui-text-input-rounded, .25em) 0}.mat-mdc-option.suggestion.mat-mdc-option-active{background-color:var(--color-primary-lightest)}\n"], dependencies: [{ kind: "component", type: PopupAlertComponent, selector: "gn-ui-popup-alert", inputs: ["icon", "type", "position"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i1$2.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i1$2.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i1$2.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
24449
|
+
], viewQueries: [{ propertyName: "triggerRef", first: true, predicate: MatAutocompleteTrigger, descendants: true }, { propertyName: "autocomplete", first: true, predicate: MatAutocomplete, descendants: true }, { propertyName: "inputRef", first: true, predicate: ["searchInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<span class=\"w-full inline-block relative\">\n @if (!allowSubmit) {\n <div\n class=\"absolute inset-y-[--icon-padding] left-[--icon-padding] w-[--icon-width] pointer-events-none\"\n >\n <ng-icon name=\"iconoirSearch\" class=\"text-primary search\"></ng-icon>\n </div>\n }\n <div class=\"flex flex-row\">\n <input\n #searchInput\n type=\"text\"\n class=\"gn-ui-text-input\"\n (input)=\"handleInput($event)\"\n [placeholder]=\"placeholder\"\n [formControl]=\"control\"\n [matAutocomplete]=\"auto\"\n (keyup.enter)=\"handleSearch()\"\n [ngClass]=\"{\n 'text-primary': searchActive && enterButton,\n 'text-gray-900': !searchActive,\n 'px-[--icon-width]': !allowSubmit,\n }\"\n />\n @if (searchInput.value && displayEnterBtn) {\n <gn-ui-button\n type=\"gray\"\n extraClass=\"w-32 h-8 !opacity-100\"\n [ngStyle]=\"{ left: enterBtnPosition + 'px' }\"\n class=\"absolute\"\n disabled=\"true\"\n >\n <ng-icon\n name=\"iconoirLongArrowDownLeft\"\n class=\"!w-4 text-gray-900 font-bold\"\n ></ng-icon>\n <span translate class=\"text-bold text-gray-900 font-bold\">\n Enter to search\n </span>\n </gn-ui-button>\n }\n </div>\n @if (searchInput.value) {\n <gn-ui-button\n type=\"primary\"\n [extraClass]=\"getExtraClass()\"\n data-test=\"clear-btn\"\n aria-label=\"Clear\"\n (buttonClick)=\"clear()\"\n >\n <ng-icon class=\"text-white\" name=\"matClose\"></ng-icon>\n </gn-ui-button>\n }\n @if (allowSubmit) {\n <gn-ui-button\n type=\"light\"\n extraClass=\"border-0 border-l-[1px] border-gray-300 hover:border-gray-500 text-primary hover:text-primary-darkest focus:text-primary-darkest absolute inset-y-[--icon-padding] right-[--icon-padding]\"\n aria-label=\"Trigger search\"\n data-test=\"autocomplete-submit-btn\"\n (buttonClick)=\"handleSearch()\"\n >\n <ng-icon name=\"iconoirSearch\"></ng-icon>\n </gn-ui-button>\n }\n\n @if (error) {\n <gn-ui-popup-alert\n class=\"absolute mt-2 w-full top-[100%] left-0\"\n icon=\"matErrorOutlineOutline\"\n position=\"top\"\n type=\"warning\"\n >\n <span translate>search.autocomplete.error</span>\n {{ error }}\n </gn-ui-popup-alert>\n }\n</span>\n\n<mat-autocomplete\n #auto=\"matAutocomplete\"\n (optionSelected)=\"handleSelection($event)\"\n [displayWith]=\"displayWithFnInternal\"\n>\n @for (suggestion of suggestions$ | async; track suggestion) {\n <mat-option [value]=\"suggestion\" class=\"p-2 suggestion text-start\">\n @if (displayWithTemplate) {\n <ng-container\n *ngTemplateOutlet=\"\n displayWithTemplate;\n context: { $implicit: suggestion }\n \"\n ></ng-container>\n } @else {\n {{ displayWithFnInternal(suggestion) }}\n }\n </mat-option>\n }\n</mat-autocomplete>\n", styles: ["span{--icon-padding: var(--gn-ui-text-input-border-size, 2px);--icon-width: calc(var(--gn-ui-text-input-padding, .6em) * 2 + 1.25em)}span>*:not(gn-ui-popup-alert){font-size:var(--gn-ui-text-input-font-size, 1em)}input:placeholder-shown{text-overflow:ellipsis}gn-ui-button{--gn-ui-button-rounded: 0;--gn-ui-button-width: var(--icon-width);--gn-ui-button-padding: 0;font-size:1em}ng-icon.search{margin-top:.6rem;margin-left:.6rem}gn-ui-button:last-of-type{--gn-ui-button-rounded: 0 var(--gn-ui-text-input-rounded, .25em) var(--gn-ui-text-input-rounded, .25em) 0}.mat-mdc-option.suggestion.mat-mdc-option-active{background-color:var(--color-primary-lightest)}\n"], dependencies: [{ kind: "component", type: PopupAlertComponent, selector: "gn-ui-popup-alert", inputs: ["icon", "type", "position"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i1$2.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i1$2.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i1$2.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
24342
24450
|
}
|
|
24343
24451
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AutocompleteComponent, decorators: [{
|
|
24344
24452
|
type: Component,
|
|
@@ -24359,7 +24467,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24359
24467
|
provideNgIconsConfig({
|
|
24360
24468
|
size: '1.75rem',
|
|
24361
24469
|
}),
|
|
24362
|
-
], template: "<span class=\"w-full inline-block relative\">\n @if (!allowSubmit) {\n <div\n class=\"absolute inset-y-[--icon-padding] left-[--icon-padding] w-[--icon-width] pointer-events-none\"\n >\n <ng-icon name=\"iconoirSearch\" class=\"text-primary search\"></ng-icon>\n </div>\n }\n <div class=\"flex flex-row\">\n <input\n #searchInput\n type=\"text\"\n class=\"gn-ui-text-input\"\n (input)=\"handleInput($event)\"\n [placeholder]=\"placeholder\"\n [formControl]=\"control\"\n [matAutocomplete]=\"auto\"\n (keyup.enter)=\"handleSearch()\"\n [ngClass]=\"{\n 'text-primary': searchActive && enterButton,\n 'text-gray-900': !searchActive,\n 'px-[--icon-width]': !allowSubmit,\n }\"\n />\n @if (searchInput.value && displayEnterBtn) {\n <gn-ui-button\n type=\"gray\"\n extraClass=\"w-32 h-8 !opacity-100\"\n [ngStyle]=\"{ left: enterBtnPosition + 'px' }\"\n class=\"absolute\"\n disabled=\"true\"\n >\n <ng-icon\n name=\"iconoirLongArrowDownLeft\"\n class=\"!w-4 text-gray-900 font-bold\"\n ></ng-icon>\n <span translate class=\"text-bold text-gray-900 font-bold\">\n Enter to search\n </span>\n </gn-ui-button>\n }\n </div>\n @if (searchInput.value) {\n <gn-ui-button\n type=\"primary\"\n [extraClass]=\"getExtraClass()\"\n data-test=\"clear-btn\"\n aria-label=\"Clear\"\n (buttonClick)=\"clear()\"\n >\n <ng-icon class=\"text-white\" name=\"matClose\"></ng-icon>\n </gn-ui-button>\n }\n @if (allowSubmit) {\n <gn-ui-button\n type=\"light\"\n extraClass=\"border-0 border-l-[1px] border-gray-300 hover:border-gray-500 text-primary hover:text-primary-darkest focus:text-primary-darkest absolute inset-y-[--icon-padding] right-[--icon-padding]\"\n aria-label=\"Trigger search\"\n data-test=\"autocomplete-submit-btn\"\n (buttonClick)=\"handleSearch()\"\n >\n <ng-icon name=\"iconoirSearch\"></ng-icon>\n </gn-ui-button>\n }\n\n @if (error) {\n <gn-ui-popup-alert\n class=\"absolute mt-2 w-full top-[100%] left-0\"\n icon=\"matErrorOutlineOutline\"\n position=\"top\"\n type=\"warning\"\n >\n <span translate>search.autocomplete.error</span>\n {{ error }}\n </gn-ui-popup-alert>\n }\n</span>\n\n<mat-autocomplete\n #auto=\"matAutocomplete\"\n (optionSelected)=\"handleSelection($event)\"\n [displayWith]=\"displayWithFnInternal\"\n>\n @for (suggestion of suggestions$ | async; track suggestion) {\n <mat-option [value]=\"suggestion\" class=\"p-2 suggestion text-start\">\n {{ displayWithFnInternal(suggestion) }}\n </mat-option>\n }\n</mat-autocomplete>\n", styles: ["span{--icon-padding: var(--gn-ui-text-input-border-size, 2px);--icon-width: calc(var(--gn-ui-text-input-padding, .6em) * 2 + 1.25em)}span>*:not(gn-ui-popup-alert){font-size:var(--gn-ui-text-input-font-size, 1em)}input:placeholder-shown{text-overflow:ellipsis}gn-ui-button{--gn-ui-button-rounded: 0;--gn-ui-button-width: var(--icon-width);--gn-ui-button-padding: 0;font-size:1em}ng-icon.search{margin-top:.6rem;margin-left:.6rem}gn-ui-button:last-of-type{--gn-ui-button-rounded: 0 var(--gn-ui-text-input-rounded, .25em) var(--gn-ui-text-input-rounded, .25em) 0}.mat-mdc-option.suggestion.mat-mdc-option-active{background-color:var(--color-primary-lightest)}\n"] }]
|
|
24470
|
+
], template: "<span class=\"w-full inline-block relative\">\n @if (!allowSubmit) {\n <div\n class=\"absolute inset-y-[--icon-padding] left-[--icon-padding] w-[--icon-width] pointer-events-none\"\n >\n <ng-icon name=\"iconoirSearch\" class=\"text-primary search\"></ng-icon>\n </div>\n }\n <div class=\"flex flex-row\">\n <input\n #searchInput\n type=\"text\"\n class=\"gn-ui-text-input\"\n (input)=\"handleInput($event)\"\n [placeholder]=\"placeholder\"\n [formControl]=\"control\"\n [matAutocomplete]=\"auto\"\n (keyup.enter)=\"handleSearch()\"\n [ngClass]=\"{\n 'text-primary': searchActive && enterButton,\n 'text-gray-900': !searchActive,\n 'px-[--icon-width]': !allowSubmit,\n }\"\n />\n @if (searchInput.value && displayEnterBtn) {\n <gn-ui-button\n type=\"gray\"\n extraClass=\"w-32 h-8 !opacity-100\"\n [ngStyle]=\"{ left: enterBtnPosition + 'px' }\"\n class=\"absolute\"\n disabled=\"true\"\n >\n <ng-icon\n name=\"iconoirLongArrowDownLeft\"\n class=\"!w-4 text-gray-900 font-bold\"\n ></ng-icon>\n <span translate class=\"text-bold text-gray-900 font-bold\">\n Enter to search\n </span>\n </gn-ui-button>\n }\n </div>\n @if (searchInput.value) {\n <gn-ui-button\n type=\"primary\"\n [extraClass]=\"getExtraClass()\"\n data-test=\"clear-btn\"\n aria-label=\"Clear\"\n (buttonClick)=\"clear()\"\n >\n <ng-icon class=\"text-white\" name=\"matClose\"></ng-icon>\n </gn-ui-button>\n }\n @if (allowSubmit) {\n <gn-ui-button\n type=\"light\"\n extraClass=\"border-0 border-l-[1px] border-gray-300 hover:border-gray-500 text-primary hover:text-primary-darkest focus:text-primary-darkest absolute inset-y-[--icon-padding] right-[--icon-padding]\"\n aria-label=\"Trigger search\"\n data-test=\"autocomplete-submit-btn\"\n (buttonClick)=\"handleSearch()\"\n >\n <ng-icon name=\"iconoirSearch\"></ng-icon>\n </gn-ui-button>\n }\n\n @if (error) {\n <gn-ui-popup-alert\n class=\"absolute mt-2 w-full top-[100%] left-0\"\n icon=\"matErrorOutlineOutline\"\n position=\"top\"\n type=\"warning\"\n >\n <span translate>search.autocomplete.error</span>\n {{ error }}\n </gn-ui-popup-alert>\n }\n</span>\n\n<mat-autocomplete\n #auto=\"matAutocomplete\"\n (optionSelected)=\"handleSelection($event)\"\n [displayWith]=\"displayWithFnInternal\"\n>\n @for (suggestion of suggestions$ | async; track suggestion) {\n <mat-option [value]=\"suggestion\" class=\"p-2 suggestion text-start\">\n @if (displayWithTemplate) {\n <ng-container\n *ngTemplateOutlet=\"\n displayWithTemplate;\n context: { $implicit: suggestion }\n \"\n ></ng-container>\n } @else {\n {{ displayWithFnInternal(suggestion) }}\n }\n </mat-option>\n }\n</mat-autocomplete>\n", styles: ["span{--icon-padding: var(--gn-ui-text-input-border-size, 2px);--icon-width: calc(var(--gn-ui-text-input-padding, .6em) * 2 + 1.25em)}span>*:not(gn-ui-popup-alert){font-size:var(--gn-ui-text-input-font-size, 1em)}input:placeholder-shown{text-overflow:ellipsis}gn-ui-button{--gn-ui-button-rounded: 0;--gn-ui-button-width: var(--icon-width);--gn-ui-button-padding: 0;font-size:1em}ng-icon.search{margin-top:.6rem;margin-left:.6rem}gn-ui-button:last-of-type{--gn-ui-button-rounded: 0 var(--gn-ui-text-input-rounded, .25em) var(--gn-ui-text-input-rounded, .25em) 0}.mat-mdc-option.suggestion.mat-mdc-option-active{background-color:var(--color-primary-lightest)}\n"] }]
|
|
24363
24471
|
}], propDecorators: { placeholder: [{
|
|
24364
24472
|
type: Input
|
|
24365
24473
|
}], enterButton: [{
|
|
@@ -24397,6 +24505,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24397
24505
|
}], inputRef: [{
|
|
24398
24506
|
type: ViewChild,
|
|
24399
24507
|
args: ['searchInput']
|
|
24508
|
+
}], displayWithTemplate: [{
|
|
24509
|
+
type: Input
|
|
24400
24510
|
}], displayWithFn: [{
|
|
24401
24511
|
type: Input
|
|
24402
24512
|
}] } });
|
|
@@ -24529,12 +24639,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24529
24639
|
type: Input
|
|
24530
24640
|
}] } });
|
|
24531
24641
|
|
|
24642
|
+
function provideLocalizedDateAdapter() {
|
|
24643
|
+
return [
|
|
24644
|
+
{ provide: MAT_DATE_LOCALE, useValue: enUS },
|
|
24645
|
+
{ provide: MAT_DATE_FORMATS, useValue: MAT_DATE_FNS_FORMATS },
|
|
24646
|
+
{
|
|
24647
|
+
provide: DateAdapter,
|
|
24648
|
+
useFactory: () => {
|
|
24649
|
+
const dateService = inject(DateService);
|
|
24650
|
+
const adapter = new DateFnsAdapter();
|
|
24651
|
+
dateService
|
|
24652
|
+
.getDateFnsLocale()
|
|
24653
|
+
.then((locale) => adapter.setLocale(locale));
|
|
24654
|
+
return adapter;
|
|
24655
|
+
},
|
|
24656
|
+
},
|
|
24657
|
+
];
|
|
24658
|
+
}
|
|
24659
|
+
|
|
24532
24660
|
class DatePickerComponent {
|
|
24533
24661
|
constructor() {
|
|
24534
|
-
this.dateAdapter = inject(DateAdapter);
|
|
24535
|
-
this.translate = inject(TranslateService);
|
|
24536
24662
|
this.dateChange = new EventEmitter();
|
|
24537
|
-
this.dateAdapter.setLocale(this.translate.getCurrentLang());
|
|
24538
24663
|
}
|
|
24539
24664
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
24540
24665
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: DatePickerComponent, isStandalone: true, selector: "gn-ui-date-picker", inputs: { date: "date" }, outputs: { dateChange: "dateChange" }, providers: [
|
|
@@ -24542,30 +24667,19 @@ class DatePickerComponent {
|
|
|
24542
24667
|
provideNgIconsConfig({
|
|
24543
24668
|
size: '1.5rem',
|
|
24544
24669
|
}),
|
|
24545
|
-
|
|
24546
|
-
|
|
24547
|
-
useFactory: (locale) => locale,
|
|
24548
|
-
},
|
|
24549
|
-
], ngImport: i0, template: "<span class=\"w-full inline-block relative\">\n <input\n class=\"gn-ui-text-input pr-[var(--text-padding)]\"\n [matDatepicker]=\"picker\"\n [value]=\"date\"\n (dateChange)=\"dateChange.emit($event.value)\"\n />\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n data-cy=\"date-picker-button\"\n extraClass=\"h-full\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-datepicker #picker></mat-datepicker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"], dependencies: [{ kind: "ngmodule", type: MatNativeDateModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i1$6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i1$6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
24670
|
+
provideLocalizedDateAdapter(),
|
|
24671
|
+
], ngImport: i0, template: "<span class=\"w-full inline-block relative\">\n <input\n class=\"gn-ui-text-input pr-[var(--text-padding)]\"\n [matDatepicker]=\"picker\"\n [value]=\"date\"\n (dateChange)=\"dateChange.emit($event.value)\"\n />\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n data-cy=\"date-picker-button\"\n extraClass=\"h-full\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-datepicker #picker></mat-datepicker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"], dependencies: [{ kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i1$6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i1$6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
24550
24672
|
}
|
|
24551
24673
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatePickerComponent, decorators: [{
|
|
24552
24674
|
type: Component,
|
|
24553
|
-
args: [{ selector: 'gn-ui-date-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
24554
|
-
MatNativeDateModule,
|
|
24555
|
-
MatDatepickerModule,
|
|
24556
|
-
ButtonComponent,
|
|
24557
|
-
NgIconComponent,
|
|
24558
|
-
], providers: [
|
|
24675
|
+
args: [{ selector: 'gn-ui-date-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [MatDatepickerModule, ButtonComponent, NgIconComponent], providers: [
|
|
24559
24676
|
provideIcons({ iconoirCalendar }),
|
|
24560
24677
|
provideNgIconsConfig({
|
|
24561
24678
|
size: '1.5rem',
|
|
24562
24679
|
}),
|
|
24563
|
-
|
|
24564
|
-
provide: MAT_DATE_LOCALE,
|
|
24565
|
-
useFactory: (locale) => locale,
|
|
24566
|
-
},
|
|
24680
|
+
provideLocalizedDateAdapter(),
|
|
24567
24681
|
], template: "<span class=\"w-full inline-block relative\">\n <input\n class=\"gn-ui-text-input pr-[var(--text-padding)]\"\n [matDatepicker]=\"picker\"\n [value]=\"date\"\n (dateChange)=\"dateChange.emit($event.value)\"\n />\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n data-cy=\"date-picker-button\"\n extraClass=\"h-full\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-datepicker #picker></mat-datepicker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"] }]
|
|
24568
|
-
}],
|
|
24682
|
+
}], propDecorators: { date: [{
|
|
24569
24683
|
type: Input
|
|
24570
24684
|
}], dateChange: [{
|
|
24571
24685
|
type: Output
|
|
@@ -24573,58 +24687,159 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24573
24687
|
|
|
24574
24688
|
class DateRangeDropdownComponent {
|
|
24575
24689
|
constructor() {
|
|
24576
|
-
this.
|
|
24690
|
+
this.scrollStrategies = inject(ScrollStrategyOptions);
|
|
24691
|
+
this.dateAdapter = inject(DateAdapter);
|
|
24692
|
+
this.dateFormats = inject(MAT_DATE_FORMATS);
|
|
24577
24693
|
this.cdr = inject(ChangeDetectorRef);
|
|
24578
|
-
this.
|
|
24579
|
-
this.
|
|
24580
|
-
this.
|
|
24694
|
+
this.dateRange = {};
|
|
24695
|
+
this.dateRangeChange = new EventEmitter();
|
|
24696
|
+
this.overlayPositions = [
|
|
24697
|
+
{
|
|
24698
|
+
originX: 'start',
|
|
24699
|
+
originY: 'bottom',
|
|
24700
|
+
overlayX: 'start',
|
|
24701
|
+
overlayY: 'top',
|
|
24702
|
+
offsetY: 8,
|
|
24703
|
+
},
|
|
24704
|
+
{
|
|
24705
|
+
originX: 'start',
|
|
24706
|
+
originY: 'top',
|
|
24707
|
+
overlayX: 'start',
|
|
24708
|
+
overlayY: 'bottom',
|
|
24709
|
+
offsetY: -8,
|
|
24710
|
+
},
|
|
24711
|
+
];
|
|
24712
|
+
this.scrollStrategy = this.scrollStrategies.reposition();
|
|
24713
|
+
this.overlayOpen = false;
|
|
24714
|
+
this.expandedBound = 'start';
|
|
24715
|
+
this.invalidBounds = { start: false, end: false };
|
|
24716
|
+
// redraw when the lazily loaded UI locale reaches the adapter
|
|
24717
|
+
this.dateAdapter.localeChanges
|
|
24718
|
+
.pipe(takeUntilDestroyed())
|
|
24719
|
+
.subscribe(() => this.cdr.markForCheck());
|
|
24581
24720
|
}
|
|
24582
|
-
|
|
24583
|
-
this.
|
|
24721
|
+
get startDate() {
|
|
24722
|
+
return this.dateRange.start;
|
|
24584
24723
|
}
|
|
24585
|
-
|
|
24586
|
-
|
|
24587
|
-
|
|
24588
|
-
|
|
24589
|
-
|
|
24590
|
-
|
|
24591
|
-
|
|
24592
|
-
|
|
24724
|
+
get endDate() {
|
|
24725
|
+
return this.dateRange.end;
|
|
24726
|
+
}
|
|
24727
|
+
get selectedDatesCount() {
|
|
24728
|
+
return (this.startDate ? 1 : 0) + (this.endDate ? 1 : 0);
|
|
24729
|
+
}
|
|
24730
|
+
openOverlay() {
|
|
24731
|
+
this.expandedBound = 'start';
|
|
24732
|
+
this.overlayOpen = true;
|
|
24733
|
+
}
|
|
24734
|
+
closeOverlay() {
|
|
24735
|
+
this.overlayOpen = false;
|
|
24736
|
+
}
|
|
24737
|
+
toggleBound(bound) {
|
|
24738
|
+
this.expandedBound = this.expandedBound === bound ? null : bound;
|
|
24739
|
+
}
|
|
24740
|
+
formatDate(date) {
|
|
24741
|
+
return this.dateAdapter.format(date, this.dateFormats.display.dateInput);
|
|
24742
|
+
}
|
|
24743
|
+
onDateInput(bound, event) {
|
|
24744
|
+
const typedText = event.target.value.trim();
|
|
24745
|
+
const date = typedText
|
|
24746
|
+
? this.dateAdapter.parse(typedText, this.dateFormats.parse.dateInput)
|
|
24747
|
+
: null;
|
|
24748
|
+
if (!this.isAcceptable(bound, typedText, date)) {
|
|
24749
|
+
// keep the text as typed and flag it; the filter stays on its last value
|
|
24750
|
+
this.invalidBounds = { ...this.invalidBounds, [bound]: true };
|
|
24751
|
+
return;
|
|
24752
|
+
}
|
|
24753
|
+
if (bound === 'start')
|
|
24754
|
+
this.setStartDate(date);
|
|
24755
|
+
else
|
|
24756
|
+
this.setEndDate(date);
|
|
24757
|
+
}
|
|
24758
|
+
normalizeDateInput(bound, event) {
|
|
24759
|
+
if (this.invalidBounds[bound])
|
|
24760
|
+
return;
|
|
24761
|
+
const date = bound === 'start' ? this.startDate : this.endDate;
|
|
24762
|
+
const input = event.target;
|
|
24763
|
+
input.value = date ? this.formatDate(date) : '';
|
|
24764
|
+
}
|
|
24765
|
+
isAcceptable(bound, typedText, date) {
|
|
24766
|
+
if (!typedText)
|
|
24767
|
+
return true;
|
|
24768
|
+
if (!date || !this.dateAdapter.isValid(date))
|
|
24769
|
+
return false;
|
|
24770
|
+
return bound === 'start'
|
|
24771
|
+
? !this.endDate || date <= this.endDate
|
|
24772
|
+
: !this.startDate || date >= this.startDate;
|
|
24773
|
+
}
|
|
24774
|
+
setStartDate(date) {
|
|
24775
|
+
this.invalidBounds = { ...this.invalidBounds, start: false };
|
|
24776
|
+
this.applyDateRange({ ...this.dateRange, start: date });
|
|
24777
|
+
this.expandedBound = 'end';
|
|
24778
|
+
}
|
|
24779
|
+
setEndDate(date) {
|
|
24780
|
+
this.invalidBounds = { ...this.invalidBounds, end: false };
|
|
24781
|
+
this.applyDateRange({ ...this.dateRange, end: date });
|
|
24782
|
+
this.expandedBound = null;
|
|
24783
|
+
}
|
|
24784
|
+
clearDates(event) {
|
|
24785
|
+
this.invalidBounds = { start: false, end: false };
|
|
24786
|
+
this.expandedBound = 'start';
|
|
24787
|
+
this.applyDateRange({});
|
|
24788
|
+
propagateToDocumentOnly(event);
|
|
24789
|
+
}
|
|
24790
|
+
applyDateRange(dateRange) {
|
|
24791
|
+
this.dateRange = {
|
|
24792
|
+
...(dateRange.start && { start: dateRange.start }),
|
|
24793
|
+
...(dateRange.end && { end: dateRange.end }),
|
|
24794
|
+
};
|
|
24795
|
+
this.dateRangeChange.emit(this.dateRange);
|
|
24593
24796
|
}
|
|
24594
24797
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangeDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
24595
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
24798
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DateRangeDropdownComponent, isStandalone: true, selector: "gn-ui-date-range-dropdown", inputs: { title: "title", dateRange: "dateRange" }, outputs: { dateRangeChange: "dateRangeChange" }, providers: [
|
|
24596
24799
|
provideIcons({
|
|
24597
|
-
|
|
24800
|
+
iconoirCalendar,
|
|
24801
|
+
matClose,
|
|
24598
24802
|
matExpandLess,
|
|
24803
|
+
matExpandMore,
|
|
24804
|
+
}),
|
|
24805
|
+
provideNgIconsConfig({
|
|
24806
|
+
size: '1.5rem',
|
|
24599
24807
|
}),
|
|
24600
|
-
|
|
24808
|
+
provideLocalizedDateAdapter(),
|
|
24809
|
+
], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }], ngImport: i0, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"openOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (selectedDatesCount) {\n <div\n 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\"\n >\n {{ selectedDatesCount }}\n </div>\n }\n </div>\n <button\n class=\"h-6 w-6 flex items-center justify-center\"\n data-test=\"dropdown-clear\"\n >\n @if (selectedDatesCount && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"clearDates($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n data-test=\"dropdown-toggle\"\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n 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]\"\n data-test=\"date-range-panel\"\n >\n <div class=\"flex flex-col justify-center border-b border-gray-300 pb-1\">\n <ng-container\n [ngTemplateOutlet]=\"boundRow\"\n [ngTemplateOutletContext]=\"{\n bound: 'start',\n label: 'daterange.filter.from',\n date: startDate,\n }\"\n ></ng-container>\n @if (expandedBound === 'start') {\n <div class=\"calendar\" animate.enter=\"expand\" animate.leave=\"collapse\">\n <mat-calendar\n data-test=\"start-date-calendar\"\n [selected]=\"startDate\"\n [startAt]=\"startDate ?? endDate\"\n [maxDate]=\"endDate\"\n (selectedChange)=\"setStartDate($event)\"\n ></mat-calendar>\n </div>\n }\n </div>\n <div class=\"flex flex-col justify-center\">\n <ng-container\n [ngTemplateOutlet]=\"boundRow\"\n [ngTemplateOutletContext]=\"{\n bound: 'end',\n label: 'daterange.filter.to',\n date: endDate,\n }\"\n ></ng-container>\n @if (expandedBound === 'end') {\n <div class=\"calendar\" animate.enter=\"expand\" animate.leave=\"collapse\">\n <mat-calendar\n data-test=\"end-date-calendar\"\n [selected]=\"endDate\"\n [startAt]=\"endDate ?? startDate\"\n [minDate]=\"startDate\"\n (selectedChange)=\"setEndDate($event)\"\n ></mat-calendar>\n </div>\n }\n </div>\n </div>\n</ng-template>\n\n<ng-template #boundRow let-bound=\"bound\" let-label=\"label\" let-date=\"date\">\n <div class=\"flex items-center gap-1 w-full\">\n <span class=\"w-[80px] shrink-0\" [translate]=\"label\"></span>\n <div\n class=\"flex-1 min-w-0 flex items-center gap-1 border rounded-lg px-2 py-1 transition-colors\"\n [class]=\"\n invalidBounds[bound]\n ? 'border-red-400 text-red-800 focus-within:border-red-800 hover:border-red-800'\n : 'border-gray-400 focus-within:border-main hover:border-main'\n \"\n >\n <ng-icon\n name=\"iconoirCalendar\"\n class=\"shrink-0\"\n aria-hidden=\"true\"\n ></ng-icon>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n autocomplete=\"off\"\n [gnUiAutofocus]=\"bound === expandedBound\"\n class=\"min-w-0 grow bg-transparent focus:outline-none\"\n [value]=\"date ? formatDate(date) : ''\"\n [placeholder]=\"'daterange.filter.datePlaceholder' | translate\"\n [attr.aria-label]=\"label | translate\"\n [attr.aria-invalid]=\"invalidBounds[bound] || null\"\n [attr.data-test]=\"bound + '-date-input'\"\n (change)=\"onDateInput(bound, $event)\"\n (blur)=\"normalizeDateInput(bound, $event)\"\n (click)=\"expandedBound !== bound && toggleBound(bound)\"\n />\n </div>\n <div class=\"shrink-0 flex items-center justify-end\">\n <gn-ui-button\n type=\"light\"\n [style.--gn-ui-button-padding]=\"0\"\n [style.--gn-ui-button-width]=\"'24px'\"\n [style.--gn-ui-button-height]=\"'24px'\"\n [attr.aria-expanded]=\"expandedBound === bound\"\n [attr.data-test]=\"bound + '-date-toggle'\"\n (buttonClick)=\"toggleBound(bound)\"\n >\n <ng-icon\n [name]=\"expandedBound === bound ? 'matExpandLess' : 'matExpandMore'\"\n ></ng-icon>\n </gn-ui-button>\n </div>\n </div>\n</ng-template>\n", styles: ["mat-calendar{width:100%}.calendar{display:grid;grid-template-rows:1fr}.calendar>mat-calendar{min-height:0;overflow:hidden}.calendar.expand{animation:open .2s ease-out}.calendar.collapse{animation:open .2s ease-in reverse}@keyframes open{0%{grid-template-rows:0fr;opacity:0}to{grid-template-rows:1fr;opacity:1}}@media (prefers-reduced-motion: reduce){.calendar.expand,.calendar.collapse{animation-duration:1ms}}\n"], dependencies: [{ kind: "directive", type: AutofocusDirective, selector: "[gnUiAutofocus]", inputs: ["gnUiAutofocus"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i1$6.MatCalendar, selector: "mat-calendar", inputs: ["headerComponent", "startAt", "startView", "selected", "minDate", "maxDate", "dateFilter", "dateClass", "comparisonStart", "comparisonEnd", "startDateAccessibleName", "endDateAccessibleName"], outputs: ["selectedChange", "yearSelected", "monthSelected", "viewChanged", "_userSelection", "_userDragDrop"], exportAs: ["matCalendar"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
24601
24810
|
}
|
|
24602
24811
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangeDropdownComponent, decorators: [{
|
|
24603
24812
|
type: Component,
|
|
24604
24813
|
args: [{ selector: 'gn-ui-date-range-dropdown', standalone: true, imports: [
|
|
24605
|
-
|
|
24606
|
-
MatNativeDateModule,
|
|
24607
|
-
MatDatepickerModule,
|
|
24814
|
+
AutofocusDirective,
|
|
24608
24815
|
ButtonComponent,
|
|
24816
|
+
MatDatepickerModule,
|
|
24817
|
+
NgIcon,
|
|
24818
|
+
NgTemplateOutlet,
|
|
24819
|
+
OverlayModule,
|
|
24820
|
+
TranslateDirective,
|
|
24821
|
+
TranslatePipe,
|
|
24609
24822
|
], providers: [
|
|
24610
24823
|
provideIcons({
|
|
24611
|
-
|
|
24824
|
+
iconoirCalendar,
|
|
24825
|
+
matClose,
|
|
24612
24826
|
matExpandLess,
|
|
24827
|
+
matExpandMore,
|
|
24613
24828
|
}),
|
|
24614
|
-
|
|
24615
|
-
|
|
24616
|
-
|
|
24617
|
-
|
|
24829
|
+
provideNgIconsConfig({
|
|
24830
|
+
size: '1.5rem',
|
|
24831
|
+
}),
|
|
24832
|
+
provideLocalizedDateAdapter(),
|
|
24833
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"openOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (selectedDatesCount) {\n <div\n 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\"\n >\n {{ selectedDatesCount }}\n </div>\n }\n </div>\n <button\n class=\"h-6 w-6 flex items-center justify-center\"\n data-test=\"dropdown-clear\"\n >\n @if (selectedDatesCount && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"clearDates($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n data-test=\"dropdown-toggle\"\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n 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]\"\n data-test=\"date-range-panel\"\n >\n <div class=\"flex flex-col justify-center border-b border-gray-300 pb-1\">\n <ng-container\n [ngTemplateOutlet]=\"boundRow\"\n [ngTemplateOutletContext]=\"{\n bound: 'start',\n label: 'daterange.filter.from',\n date: startDate,\n }\"\n ></ng-container>\n @if (expandedBound === 'start') {\n <div class=\"calendar\" animate.enter=\"expand\" animate.leave=\"collapse\">\n <mat-calendar\n data-test=\"start-date-calendar\"\n [selected]=\"startDate\"\n [startAt]=\"startDate ?? endDate\"\n [maxDate]=\"endDate\"\n (selectedChange)=\"setStartDate($event)\"\n ></mat-calendar>\n </div>\n }\n </div>\n <div class=\"flex flex-col justify-center\">\n <ng-container\n [ngTemplateOutlet]=\"boundRow\"\n [ngTemplateOutletContext]=\"{\n bound: 'end',\n label: 'daterange.filter.to',\n date: endDate,\n }\"\n ></ng-container>\n @if (expandedBound === 'end') {\n <div class=\"calendar\" animate.enter=\"expand\" animate.leave=\"collapse\">\n <mat-calendar\n data-test=\"end-date-calendar\"\n [selected]=\"endDate\"\n [startAt]=\"endDate ?? startDate\"\n [minDate]=\"startDate\"\n (selectedChange)=\"setEndDate($event)\"\n ></mat-calendar>\n </div>\n }\n </div>\n </div>\n</ng-template>\n\n<ng-template #boundRow let-bound=\"bound\" let-label=\"label\" let-date=\"date\">\n <div class=\"flex items-center gap-1 w-full\">\n <span class=\"w-[80px] shrink-0\" [translate]=\"label\"></span>\n <div\n class=\"flex-1 min-w-0 flex items-center gap-1 border rounded-lg px-2 py-1 transition-colors\"\n [class]=\"\n invalidBounds[bound]\n ? 'border-red-400 text-red-800 focus-within:border-red-800 hover:border-red-800'\n : 'border-gray-400 focus-within:border-main hover:border-main'\n \"\n >\n <ng-icon\n name=\"iconoirCalendar\"\n class=\"shrink-0\"\n aria-hidden=\"true\"\n ></ng-icon>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n autocomplete=\"off\"\n [gnUiAutofocus]=\"bound === expandedBound\"\n class=\"min-w-0 grow bg-transparent focus:outline-none\"\n [value]=\"date ? formatDate(date) : ''\"\n [placeholder]=\"'daterange.filter.datePlaceholder' | translate\"\n [attr.aria-label]=\"label | translate\"\n [attr.aria-invalid]=\"invalidBounds[bound] || null\"\n [attr.data-test]=\"bound + '-date-input'\"\n (change)=\"onDateInput(bound, $event)\"\n (blur)=\"normalizeDateInput(bound, $event)\"\n (click)=\"expandedBound !== bound && toggleBound(bound)\"\n />\n </div>\n <div class=\"shrink-0 flex items-center justify-end\">\n <gn-ui-button\n type=\"light\"\n [style.--gn-ui-button-padding]=\"0\"\n [style.--gn-ui-button-width]=\"'24px'\"\n [style.--gn-ui-button-height]=\"'24px'\"\n [attr.aria-expanded]=\"expandedBound === bound\"\n [attr.data-test]=\"bound + '-date-toggle'\"\n (buttonClick)=\"toggleBound(bound)\"\n >\n <ng-icon\n [name]=\"expandedBound === bound ? 'matExpandLess' : 'matExpandMore'\"\n ></ng-icon>\n </gn-ui-button>\n </div>\n </div>\n</ng-template>\n", styles: ["mat-calendar{width:100%}.calendar{display:grid;grid-template-rows:1fr}.calendar>mat-calendar{min-height:0;overflow:hidden}.calendar.expand{animation:open .2s ease-out}.calendar.collapse{animation:open .2s ease-in reverse}@keyframes open{0%{grid-template-rows:0fr;opacity:0}to{grid-template-rows:1fr;opacity:1}}@media (prefers-reduced-motion: reduce){.calendar.expand,.calendar.collapse{animation-duration:1ms}}\n"] }]
|
|
24834
|
+
}], ctorParameters: () => [], propDecorators: { title: [{
|
|
24618
24835
|
type: Input
|
|
24619
|
-
}],
|
|
24836
|
+
}], dateRange: [{
|
|
24620
24837
|
type: Input
|
|
24621
|
-
}],
|
|
24622
|
-
type: Output
|
|
24623
|
-
}], endDateChange: [{
|
|
24838
|
+
}], dateRangeChange: [{
|
|
24624
24839
|
type: Output
|
|
24625
|
-
}],
|
|
24840
|
+
}], overlayOrigin: [{
|
|
24626
24841
|
type: ViewChild,
|
|
24627
|
-
args: ['
|
|
24842
|
+
args: ['overlayOrigin']
|
|
24628
24843
|
}] } });
|
|
24629
24844
|
|
|
24630
24845
|
class DateRangePickerComponent {
|
|
@@ -24638,20 +24853,17 @@ class DateRangePickerComponent {
|
|
|
24638
24853
|
provideNgIconsConfig({
|
|
24639
24854
|
size: '1.5rem',
|
|
24640
24855
|
}),
|
|
24641
|
-
|
|
24856
|
+
provideLocalizedDateAdapter(),
|
|
24857
|
+
], ngImport: i0, template: "<span class=\"w-full inline-block relative\">\n <mat-date-range-input [rangePicker]=\"picker\" class=\"gn-ui-text-input\">\n <input\n matStartDate\n placeholder=\"Start date\"\n [value]=\"startDate\"\n (dateInput)=\"startDateChange.emit($event.value)\"\n />\n <input\n matEndDate\n placeholder=\"End date\"\n [value]=\"endDate\"\n (dateInput)=\"endDateChange.emit($event.value)\"\n />\n </mat-date-range-input>\n\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n extraClass=\"h-full\"\n data-cy=\"date-picker-button\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-date-range-picker #picker></mat-date-range-picker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"], dependencies: [{ kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i1$6.MatDateRangeInput, selector: "mat-date-range-input", inputs: ["rangePicker", "required", "dateFilter", "min", "max", "disabled", "separator", "comparisonStart", "comparisonEnd"], exportAs: ["matDateRangeInput"] }, { kind: "directive", type: i1$6.MatStartDate, selector: "input[matStartDate]", outputs: ["dateChange", "dateInput"] }, { kind: "directive", type: i1$6.MatEndDate, selector: "input[matEndDate]", outputs: ["dateChange", "dateInput"] }, { kind: "component", type: i1$6.MatDateRangePicker, selector: "mat-date-range-picker", exportAs: ["matDateRangePicker"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
24642
24858
|
}
|
|
24643
24859
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangePickerComponent, decorators: [{
|
|
24644
24860
|
type: Component,
|
|
24645
|
-
args: [{ selector: 'gn-ui-date-range-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
24646
|
-
MatNativeDateModule,
|
|
24647
|
-
MatDatepickerModule,
|
|
24648
|
-
ButtonComponent,
|
|
24649
|
-
NgIconComponent,
|
|
24650
|
-
], providers: [
|
|
24861
|
+
args: [{ selector: 'gn-ui-date-range-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [MatDatepickerModule, ButtonComponent, NgIconComponent], providers: [
|
|
24651
24862
|
provideIcons({ iconoirCalendar }),
|
|
24652
24863
|
provideNgIconsConfig({
|
|
24653
24864
|
size: '1.5rem',
|
|
24654
24865
|
}),
|
|
24866
|
+
provideLocalizedDateAdapter(),
|
|
24655
24867
|
], template: "<span class=\"w-full inline-block relative\">\n <mat-date-range-input [rangePicker]=\"picker\" class=\"gn-ui-text-input\">\n <input\n matStartDate\n placeholder=\"Start date\"\n [value]=\"startDate\"\n (dateInput)=\"startDateChange.emit($event.value)\"\n />\n <input\n matEndDate\n placeholder=\"End date\"\n [value]=\"endDate\"\n (dateInput)=\"endDateChange.emit($event.value)\"\n />\n </mat-date-range-input>\n\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n extraClass=\"h-full\"\n data-cy=\"date-picker-button\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-date-range-picker #picker></mat-date-range-picker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"] }]
|
|
24656
24868
|
}], propDecorators: { startDate: [{
|
|
24657
24869
|
type: Input
|
|
@@ -24669,28 +24881,68 @@ class DragAndDropFileInputComponent {
|
|
|
24669
24881
|
constructor() {
|
|
24670
24882
|
this.placeholder = placeholder;
|
|
24671
24883
|
this.accept = '*';
|
|
24884
|
+
this.maxFileSizeMb = null;
|
|
24885
|
+
this.icon = null;
|
|
24886
|
+
this.dropzoneBackgroundColor = null;
|
|
24887
|
+
this.textClass = '';
|
|
24888
|
+
this.extraClass = '';
|
|
24889
|
+
this.showFileName = true;
|
|
24672
24890
|
this.fileChange = new EventEmitter();
|
|
24891
|
+
this.errorChange = new EventEmitter();
|
|
24673
24892
|
this.selectedFile = null;
|
|
24674
24893
|
}
|
|
24675
24894
|
get fileName() {
|
|
24676
24895
|
return this.selectedFile && this.selectedFile.name;
|
|
24677
24896
|
}
|
|
24897
|
+
get maxFileSizeBytes() {
|
|
24898
|
+
return typeof this.maxFileSizeMb === 'number'
|
|
24899
|
+
? megabytesToBytes(this.maxFileSizeMb)
|
|
24900
|
+
: null;
|
|
24901
|
+
}
|
|
24678
24902
|
selectFile(event) {
|
|
24903
|
+
if (event.rejectedFiles?.length) {
|
|
24904
|
+
const reason = event.rejectedFiles[0].reason;
|
|
24905
|
+
this.errorChange.emit(reason === 'size' ? 'file-too-large' : 'invalid-extension');
|
|
24906
|
+
return;
|
|
24907
|
+
}
|
|
24679
24908
|
this.selectedFile = event.addedFiles[0];
|
|
24680
24909
|
this.fileChange.emit(this.selectedFile);
|
|
24681
24910
|
}
|
|
24911
|
+
openFileSelector() {
|
|
24912
|
+
this.dropzone.showFileSelector();
|
|
24913
|
+
}
|
|
24914
|
+
clear() {
|
|
24915
|
+
this.selectedFile = null;
|
|
24916
|
+
}
|
|
24682
24917
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DragAndDropFileInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
24683
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DragAndDropFileInputComponent, isStandalone: true, selector: "gn-ui-drag-and-drop-file-input", inputs: { placeholder: "placeholder", accept: "accept" }, outputs: { fileChange: "fileChange" }, ngImport: i0, template: "<div
|
|
24918
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DragAndDropFileInputComponent, isStandalone: true, selector: "gn-ui-drag-and-drop-file-input", inputs: { placeholder: "placeholder", accept: "accept", maxFileSizeMb: "maxFileSizeMb", icon: "icon", dropzoneBackgroundColor: "dropzoneBackgroundColor", textClass: "textClass", extraClass: "extraClass", showFileName: "showFileName" }, outputs: { fileChange: "fileChange", errorChange: "errorChange" }, viewQueries: [{ propertyName: "dropzone", first: true, predicate: NgxDropzoneComponent, descendants: true }], ngImport: i0, template: "<div\n class=\"flex h-full items-center\"\n [class]=\"extraClass\"\n (click)=\"openFileSelector()\"\n>\n <ngx-dropzone\n class=\"flex-1 overflow-hidden\"\n [multiple]=\"false\"\n [disableClick]=\"true\"\n [accept]=\"accept\"\n [maxFileSize]=\"maxFileSizeBytes\"\n [style.background-color]=\"dropzoneBackgroundColor\"\n (change)=\"selectFile($event)\"\n >\n @if (!fileName || !showFileName) {\n <div [class]=\"textClass\">\n {{ placeholder }}\n </div>\n }\n\n @if (fileName && showFileName) {\n <div [class]=\"textClass\">\n {{ fileName }}\n </div>\n }\n </ngx-dropzone>\n\n @if (icon) {\n <ng-icon [name]=\"icon\" class=\"shrink-0 text-gray-950\"></ng-icon>\n }\n</div>\n", styles: ["ngx-dropzone{height:auto;border:none;overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: NgxDropzoneModule }, { kind: "component", type: i1$8.NgxDropzoneComponent, selector: "ngx-dropzone, [ngx-dropzone]", inputs: ["accept", "disabled", "multiple", "maxFileSize", "expandable", "disableClick", "processDirectoryDrop", "id", "aria-label", "aria-labelledby", "aria-describedby"], outputs: ["change"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }] }); }
|
|
24684
24919
|
}
|
|
24685
24920
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DragAndDropFileInputComponent, decorators: [{
|
|
24686
24921
|
type: Component,
|
|
24687
|
-
args: [{ selector: 'gn-ui-drag-and-drop-file-input', standalone: true, imports: [NgxDropzoneModule], template: "<div
|
|
24922
|
+
args: [{ selector: 'gn-ui-drag-and-drop-file-input', standalone: true, imports: [NgxDropzoneModule, NgIcon], template: "<div\n class=\"flex h-full items-center\"\n [class]=\"extraClass\"\n (click)=\"openFileSelector()\"\n>\n <ngx-dropzone\n class=\"flex-1 overflow-hidden\"\n [multiple]=\"false\"\n [disableClick]=\"true\"\n [accept]=\"accept\"\n [maxFileSize]=\"maxFileSizeBytes\"\n [style.background-color]=\"dropzoneBackgroundColor\"\n (change)=\"selectFile($event)\"\n >\n @if (!fileName || !showFileName) {\n <div [class]=\"textClass\">\n {{ placeholder }}\n </div>\n }\n\n @if (fileName && showFileName) {\n <div [class]=\"textClass\">\n {{ fileName }}\n </div>\n }\n </ngx-dropzone>\n\n @if (icon) {\n <ng-icon [name]=\"icon\" class=\"shrink-0 text-gray-950\"></ng-icon>\n }\n</div>\n", styles: ["ngx-dropzone{height:auto;border:none;overflow:hidden}\n"] }]
|
|
24688
24923
|
}], propDecorators: { placeholder: [{
|
|
24689
24924
|
type: Input
|
|
24690
24925
|
}], accept: [{
|
|
24691
24926
|
type: Input
|
|
24927
|
+
}], maxFileSizeMb: [{
|
|
24928
|
+
type: Input
|
|
24929
|
+
}], icon: [{
|
|
24930
|
+
type: Input
|
|
24931
|
+
}], dropzoneBackgroundColor: [{
|
|
24932
|
+
type: Input
|
|
24933
|
+
}], textClass: [{
|
|
24934
|
+
type: Input
|
|
24935
|
+
}], extraClass: [{
|
|
24936
|
+
type: Input
|
|
24937
|
+
}], showFileName: [{
|
|
24938
|
+
type: Input
|
|
24692
24939
|
}], fileChange: [{
|
|
24693
24940
|
type: Output
|
|
24941
|
+
}], errorChange: [{
|
|
24942
|
+
type: Output
|
|
24943
|
+
}], dropzone: [{
|
|
24944
|
+
type: ViewChild,
|
|
24945
|
+
args: [NgxDropzoneComponent]
|
|
24694
24946
|
}] } });
|
|
24695
24947
|
|
|
24696
24948
|
class DropdownMultiselectComponent {
|
|
@@ -24739,7 +24991,7 @@ class DropdownMultiselectComponent {
|
|
|
24739
24991
|
}
|
|
24740
24992
|
setFocus() {
|
|
24741
24993
|
setTimeout(() => {
|
|
24742
|
-
this.searchFieldInput
|
|
24994
|
+
this.searchFieldInput?.nativeElement.focus();
|
|
24743
24995
|
}, 0);
|
|
24744
24996
|
}
|
|
24745
24997
|
openOverlay() {
|
|
@@ -24845,7 +25097,10 @@ class DropdownMultiselectComponent {
|
|
|
24845
25097
|
matExpandMore,
|
|
24846
25098
|
matExpandLess,
|
|
24847
25099
|
}),
|
|
24848
|
-
|
|
25100
|
+
provideNgIconsConfig({
|
|
25101
|
+
size: '1.5rem',
|
|
25102
|
+
}),
|
|
25103
|
+
], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "overlayContainer", first: true, predicate: ["overlayContainer"], descendants: true, read: ElementRef }, { propertyName: "searchFieldInput", first: true, predicate: ["searchFieldInput"], descendants: true }, { propertyName: "checkboxes", predicate: ["checkBox"], descendants: true, read: ElementRef }], ngImport: i0, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelectedChoices) {\n <div\n 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\"\n >\n {{ selected.length }}\n </div>\n }\n </div>\n <button\n class=\"h-6 w-6 flex items-center justify-center\"\n data-test=\"dropdown-clear\"\n >\n @if (hasSelectedChoices && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"clearSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container\"\n [style.max-height]=\"overlayMaxHeight\"\n [style.min-width]=\"overlayMinWidth\"\n role=\"listbox\"\n tabindex=\"-1\"\n [attr.id]=\"id\"\n [attr.aria-multiselectable]=\"true\"\n [attr.aria-label]=\"title\"\n (keydown)=\"handleOverlayKeydown($event)\"\n #overlayContainer\n >\n <div\n class=\"border border-gray-300 rounded mb-2 mx-2 min-h-[44px] flex flex-row gap-1 flex-wrap p-2 focus-within:rounded focus-within:border-2 focus-within:border-primary\"\n >\n @for (selected of selectedChoices; track selected) {\n <button\n type=\"button\"\n [title]=\"selected.label\"\n class=\"max-w-full bg-main text-white rounded pr-[7px] flex gap-1 items-center opacity-70 hover:opacity-100 focus:opacity-100 transition-opacity\"\n (click)=\"select(selected, false)\"\n >\n <div class=\"text-sm truncate leading-[26px] px-2\">\n {{ selected.label }}\n </div>\n <div\n class=\"flex items-center justify-center rounded-full bg-white text-main h-[13px] w-[13px] pt-px -mt-px shrink-0\"\n >\n <ng-icon\n name=\"matClose\"\n class=\"!h-[12px] !w-[11px] text-[12px]\"\n ></ng-icon>\n </div>\n </button>\n }\n\n @if (allowSearch) {\n <div class=\"w-[50%] relative grow shrink\">\n <input\n #searchFieldInput\n class=\"w-full px-2 truncate text-[14px] h-full overlaySearchInput focus:outline-none\"\n [(ngModel)]=\"searchInputValue\"\n [placeholder]=\"'multiselect.filter.placeholder' | translate\"\n />\n @if (!!searchInputValue) {\n <button\n class=\"absolute top-1/2 -translate-y-1/2 right-0 px-[7px] leading-tight clear-search-input mr-2\"\n (click)=\"clearSearchInputValue($event)\"\n >\n <ng-icon class=\"!h-[10px] !w-[12px] text-[12px]\" name=\"matClose\">\n </ng-icon>\n </button>\n }\n </div>\n }\n </div>\n\n @for (choice of filteredChoicesByText; track choice) {\n <label\n [title]=\"choice.label\"\n class=\"flex px-5 py-1 w-full text-gray-900 cursor-pointer hover:text-primary-darkest hover:bg-gray-50 focus-within:text-primary-darkest focus-within:bg-gray-50 transition-colors\"\n >\n <input\n class=\"w-[18px] h-[18px] align-text-top shrink-0\"\n type=\"checkbox\"\n #checkBox\n [checked]=\"isSelected(choice)\"\n (change)=\"select(choice, checkBox.checked)\"\n />\n <span class=\"ml-[8px] text-[14px] truncate\">\n {{ choice.label }}\n </span>\n </label>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
24849
25104
|
}
|
|
24850
25105
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DropdownMultiselectComponent, decorators: [{
|
|
24851
25106
|
type: Component,
|
|
@@ -24855,7 +25110,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24855
25110
|
matExpandMore,
|
|
24856
25111
|
matExpandLess,
|
|
24857
25112
|
}),
|
|
24858
|
-
|
|
25113
|
+
provideNgIconsConfig({
|
|
25114
|
+
size: '1.5rem',
|
|
25115
|
+
}),
|
|
25116
|
+
], standalone: true, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelectedChoices) {\n <div\n 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\"\n >\n {{ selected.length }}\n </div>\n }\n </div>\n <button\n class=\"h-6 w-6 flex items-center justify-center\"\n data-test=\"dropdown-clear\"\n >\n @if (hasSelectedChoices && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"clearSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container\"\n [style.max-height]=\"overlayMaxHeight\"\n [style.min-width]=\"overlayMinWidth\"\n role=\"listbox\"\n tabindex=\"-1\"\n [attr.id]=\"id\"\n [attr.aria-multiselectable]=\"true\"\n [attr.aria-label]=\"title\"\n (keydown)=\"handleOverlayKeydown($event)\"\n #overlayContainer\n >\n <div\n class=\"border border-gray-300 rounded mb-2 mx-2 min-h-[44px] flex flex-row gap-1 flex-wrap p-2 focus-within:rounded focus-within:border-2 focus-within:border-primary\"\n >\n @for (selected of selectedChoices; track selected) {\n <button\n type=\"button\"\n [title]=\"selected.label\"\n class=\"max-w-full bg-main text-white rounded pr-[7px] flex gap-1 items-center opacity-70 hover:opacity-100 focus:opacity-100 transition-opacity\"\n (click)=\"select(selected, false)\"\n >\n <div class=\"text-sm truncate leading-[26px] px-2\">\n {{ selected.label }}\n </div>\n <div\n class=\"flex items-center justify-center rounded-full bg-white text-main h-[13px] w-[13px] pt-px -mt-px shrink-0\"\n >\n <ng-icon\n name=\"matClose\"\n class=\"!h-[12px] !w-[11px] text-[12px]\"\n ></ng-icon>\n </div>\n </button>\n }\n\n @if (allowSearch) {\n <div class=\"w-[50%] relative grow shrink\">\n <input\n #searchFieldInput\n class=\"w-full px-2 truncate text-[14px] h-full overlaySearchInput focus:outline-none\"\n [(ngModel)]=\"searchInputValue\"\n [placeholder]=\"'multiselect.filter.placeholder' | translate\"\n />\n @if (!!searchInputValue) {\n <button\n class=\"absolute top-1/2 -translate-y-1/2 right-0 px-[7px] leading-tight clear-search-input mr-2\"\n (click)=\"clearSearchInputValue($event)\"\n >\n <ng-icon class=\"!h-[10px] !w-[12px] text-[12px]\" name=\"matClose\">\n </ng-icon>\n </button>\n }\n </div>\n }\n </div>\n\n @for (choice of filteredChoicesByText; track choice) {\n <label\n [title]=\"choice.label\"\n class=\"flex px-5 py-1 w-full text-gray-900 cursor-pointer hover:text-primary-darkest hover:bg-gray-50 focus-within:text-primary-darkest focus-within:bg-gray-50 transition-colors\"\n >\n <input\n class=\"w-[18px] h-[18px] align-text-top shrink-0\"\n type=\"checkbox\"\n #checkBox\n [checked]=\"isSelected(choice)\"\n (change)=\"select(choice, checkBox.checked)\"\n />\n <span class=\"ml-[8px] text-[14px] truncate\">\n {{ choice.label }}\n </span>\n </label>\n }\n </div>\n</ng-template>\n" }]
|
|
24859
25117
|
}], propDecorators: { title: [{
|
|
24860
25118
|
type: Input
|
|
24861
25119
|
}], choices: [{
|
|
@@ -25027,7 +25285,7 @@ class DropdownSelectorComponent {
|
|
|
25027
25285
|
provideNgIconsConfig({
|
|
25028
25286
|
size: '1.5em',
|
|
25029
25287
|
}),
|
|
25030
|
-
], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "choiceInputs", predicate: ["choiceInputs"], descendants: true, read: ElementRef }], ngImport: i0, template: "<div class=\"flex flex-col sm:flex-row sm:items-center relative w-full\">\n @if (showTitle) {\n <span\n class=\"tracking-wide text-sm mb-2 sm:mb-0 sm:mr-2 whitespace-nowrap\"\n [attr.for]=\"id\"\n >\n {{ title }}\n </span>\n }\n <gn-ui-button\n type=\"outline\"\n class=\"grow min-w-0\"\n [style.--gn-ui-button-padding]=\"'8px 8px 8px 16px'\"\n extraClass=\"bg-background flex flex-row w-full {{ extraBtnClass }}\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n (keydown)=\"handleTriggerKeydown($event)\"\n [disabled]=\"disabled\"\n >\n <div class=\"grow truncate py-1 mr-2 text-left\">\n {{ getChoiceLabel() | translate }}\n </div>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n </gn-ui-button>\n</div>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container\"\n [style.max-height]=\"overlayMaxHeight\"\n [style.min-width]=\"overlayWidth\"\n role=\"listbox\"\n tabindex=\"-1\"\n [attr.id]=\"id\"\n [attr.aria-multiselectable]=\"true\"\n [attr.aria-label]=\"title\"\n (keydown)=\"handleOverlayKeydown($event)\"\n >\n @for (choice of choices; track choice) {\n <button\n #choiceInputs\n type=\"button\"\n [title]=\"choice.label | translate\"\n class=\"flex px-5 py-1 w-full text-start cursor-pointer transition-colors\"\n [ngClass]=\"\n isSelected(choice)\n ? 'text-white bg-primary hover:text-white hover:bg-primary-darker focus:text-white focus:bg-primary-darker'\n : 'text-gray-900 hover:text-primary-darkest hover:bg-gray-50 focus:text-primary-darkest focus:bg-gray-50'\n \"\n (click)=\"onSelectValue(choice)\"\n (keydown)=\"selectIfEnter($event, choice)\"\n [attr.data-cy-value]=\"choice.value.toString()\"\n [attr.data-cy-active]=\"isSelected(choice) ? 'true' : undefined\"\n >\n <span class=\"text-[14px]\">\n {{ choice.label | translate }}\n </span>\n </button>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$
|
|
25288
|
+
], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "choiceInputs", predicate: ["choiceInputs"], descendants: true, read: ElementRef }], ngImport: i0, template: "<div class=\"flex flex-col sm:flex-row sm:items-center relative w-full\">\n @if (showTitle) {\n <span\n class=\"tracking-wide text-sm mb-2 sm:mb-0 sm:mr-2 whitespace-nowrap\"\n [attr.for]=\"id\"\n >\n {{ title }}\n </span>\n }\n <gn-ui-button\n type=\"outline\"\n class=\"grow min-w-0\"\n [style.--gn-ui-button-padding]=\"'8px 8px 8px 16px'\"\n extraClass=\"bg-background flex flex-row w-full {{ extraBtnClass }}\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n (keydown)=\"handleTriggerKeydown($event)\"\n [disabled]=\"disabled\"\n >\n <div class=\"grow truncate py-1 mr-2 text-left\">\n {{ getChoiceLabel() | translate }}\n </div>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n </gn-ui-button>\n</div>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container\"\n [style.max-height]=\"overlayMaxHeight\"\n [style.min-width]=\"overlayWidth\"\n role=\"listbox\"\n tabindex=\"-1\"\n [attr.id]=\"id\"\n [attr.aria-multiselectable]=\"true\"\n [attr.aria-label]=\"title\"\n (keydown)=\"handleOverlayKeydown($event)\"\n >\n @for (choice of choices; track choice) {\n <button\n #choiceInputs\n type=\"button\"\n [title]=\"choice.label | translate\"\n class=\"flex px-5 py-1 w-full text-start cursor-pointer transition-colors\"\n [ngClass]=\"\n isSelected(choice)\n ? 'text-white bg-primary hover:text-white hover:bg-primary-darker focus:text-white focus:bg-primary-darker'\n : 'text-gray-900 hover:text-primary-darkest hover:bg-gray-50 focus:text-primary-darkest focus:bg-gray-50'\n \"\n (click)=\"onSelectValue(choice)\"\n (keydown)=\"selectIfEnter($event, choice)\"\n [attr.data-cy-value]=\"choice.value.toString()\"\n [attr.data-cy-active]=\"isSelected(choice) ? 'true' : undefined\"\n >\n <span class=\"text-[14px]\">\n {{ choice.label | translate }}\n </span>\n </button>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
25031
25289
|
}
|
|
25032
25290
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DropdownSelectorComponent, decorators: [{
|
|
25033
25291
|
type: Component,
|
|
@@ -25767,6 +26025,175 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
25767
26025
|
type: Output
|
|
25768
26026
|
}] } });
|
|
25769
26027
|
|
|
26028
|
+
marker('search.filters.spatialExtent.import');
|
|
26029
|
+
marker('search.filters.spatialExtent.helpText');
|
|
26030
|
+
marker('search.filters.spatialExtent.error.title');
|
|
26031
|
+
const LABEL_FROM_FILE = marker('search.filters.spatialExtent.bboxFromFile');
|
|
26032
|
+
const LABEL_FROM_FILE_DELETE = marker('search.filters.spatialExtent.bboxFromFileDelete');
|
|
26033
|
+
const LABEL_INITIAL = marker('search.filters.spatialExtent.bboxInitial');
|
|
26034
|
+
const LABEL_INITIAL_DELETE = marker('search.filters.spatialExtent.bboxInitialDelete');
|
|
26035
|
+
class SpatialExtentDropdownComponent {
|
|
26036
|
+
constructor() {
|
|
26037
|
+
this.cd = inject(ChangeDetectorRef);
|
|
26038
|
+
this.scrollStrategies = inject(ScrollStrategyOptions);
|
|
26039
|
+
this.maxFileSizeMb = null;
|
|
26040
|
+
this.bboxChange = new EventEmitter();
|
|
26041
|
+
this.errorChange = new EventEmitter();
|
|
26042
|
+
this.bbox = null;
|
|
26043
|
+
this.fileName = '';
|
|
26044
|
+
this.overlayPositions = [
|
|
26045
|
+
{
|
|
26046
|
+
originX: 'start',
|
|
26047
|
+
originY: 'bottom',
|
|
26048
|
+
overlayX: 'start',
|
|
26049
|
+
overlayY: 'top',
|
|
26050
|
+
offsetY: 8,
|
|
26051
|
+
},
|
|
26052
|
+
{
|
|
26053
|
+
originX: 'start',
|
|
26054
|
+
originY: 'top',
|
|
26055
|
+
overlayX: 'start',
|
|
26056
|
+
overlayY: 'bottom',
|
|
26057
|
+
offsetY: -8,
|
|
26058
|
+
},
|
|
26059
|
+
];
|
|
26060
|
+
this.scrollStrategy = this.scrollStrategies.reposition();
|
|
26061
|
+
this.overlayOpen = false;
|
|
26062
|
+
this.overlayMinWidth = 'none';
|
|
26063
|
+
this.errorKey = null;
|
|
26064
|
+
}
|
|
26065
|
+
set initialBbox(value) {
|
|
26066
|
+
if (!this.bbox && value) {
|
|
26067
|
+
this.bbox = value;
|
|
26068
|
+
}
|
|
26069
|
+
}
|
|
26070
|
+
get hasSelection() {
|
|
26071
|
+
return !!this.bbox;
|
|
26072
|
+
}
|
|
26073
|
+
get selectionLabelKey() {
|
|
26074
|
+
return this.fileName ? LABEL_FROM_FILE : LABEL_INITIAL;
|
|
26075
|
+
}
|
|
26076
|
+
get selectionDeleteLabelKey() {
|
|
26077
|
+
return this.fileName ? LABEL_FROM_FILE_DELETE : LABEL_INITIAL_DELETE;
|
|
26078
|
+
}
|
|
26079
|
+
get selectionLabelParams() {
|
|
26080
|
+
return { fileName: this.fileName };
|
|
26081
|
+
}
|
|
26082
|
+
openOverlay() {
|
|
26083
|
+
this.overlayMinWidth =
|
|
26084
|
+
this.overlayOrigin.elementRef.nativeElement.getBoundingClientRect()
|
|
26085
|
+
.width + 'px';
|
|
26086
|
+
this.overlayOpen = true;
|
|
26087
|
+
}
|
|
26088
|
+
closeOverlay() {
|
|
26089
|
+
this.overlayOpen = false;
|
|
26090
|
+
}
|
|
26091
|
+
toggleOverlay() {
|
|
26092
|
+
if (this.overlayOpen) {
|
|
26093
|
+
this.closeOverlay();
|
|
26094
|
+
}
|
|
26095
|
+
else {
|
|
26096
|
+
this.openOverlay();
|
|
26097
|
+
}
|
|
26098
|
+
}
|
|
26099
|
+
async handleFileSelected(file) {
|
|
26100
|
+
this.errorKey = null;
|
|
26101
|
+
let content;
|
|
26102
|
+
try {
|
|
26103
|
+
content = await readFileAsText(file);
|
|
26104
|
+
const parsed = JSON.parse(content);
|
|
26105
|
+
const geometry = getGeometryFromGeoJSON(parsed);
|
|
26106
|
+
if (!geometry) {
|
|
26107
|
+
this.setError(marker('search.filters.spatialExtent.error.noGeometry'));
|
|
26108
|
+
return;
|
|
26109
|
+
}
|
|
26110
|
+
const bbox = getGeometryBoundingBox(geometry);
|
|
26111
|
+
this.bbox = bbox;
|
|
26112
|
+
this.fileName = file.name;
|
|
26113
|
+
this.bboxChange.emit(bbox);
|
|
26114
|
+
this.cd.markForCheck();
|
|
26115
|
+
}
|
|
26116
|
+
catch {
|
|
26117
|
+
this.setError(marker('search.filters.spatialExtent.error.invalidFormat'));
|
|
26118
|
+
return;
|
|
26119
|
+
}
|
|
26120
|
+
}
|
|
26121
|
+
handleFileError(error) {
|
|
26122
|
+
if (error === 'file-too-large') {
|
|
26123
|
+
this.setError(marker('search.filters.spatialExtent.error.fileTooLarge'), {
|
|
26124
|
+
maxSize: this.maxFileSizeMb,
|
|
26125
|
+
});
|
|
26126
|
+
}
|
|
26127
|
+
else {
|
|
26128
|
+
this.setError(marker('search.filters.spatialExtent.error.invalidFormat'));
|
|
26129
|
+
}
|
|
26130
|
+
}
|
|
26131
|
+
setError(errorKey, params) {
|
|
26132
|
+
this.errorKey = errorKey;
|
|
26133
|
+
this.errorChange.emit({ key: errorKey, params });
|
|
26134
|
+
this.cd.markForCheck();
|
|
26135
|
+
}
|
|
26136
|
+
removeSelection(event) {
|
|
26137
|
+
this.bbox = null;
|
|
26138
|
+
this.fileName = '';
|
|
26139
|
+
this.errorKey = null;
|
|
26140
|
+
this.fileInput?.clear();
|
|
26141
|
+
this.bboxChange.emit(null);
|
|
26142
|
+
propagateToDocumentOnly(event);
|
|
26143
|
+
}
|
|
26144
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
26145
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: SpatialExtentDropdownComponent, isStandalone: true, selector: "gn-ui-spatial-extent-dropdown", inputs: { title: "title", maxFileSizeMb: "maxFileSizeMb", initialBbox: "initialBbox" }, outputs: { bboxChange: "bboxChange", errorChange: "errorChange" }, providers: [
|
|
26146
|
+
provideIcons({
|
|
26147
|
+
iconoirCheckCircle,
|
|
26148
|
+
iconoirImport,
|
|
26149
|
+
iconoirSquareDashed,
|
|
26150
|
+
iconoirTrash,
|
|
26151
|
+
matClose,
|
|
26152
|
+
matExpandLess,
|
|
26153
|
+
matExpandMore,
|
|
26154
|
+
}),
|
|
26155
|
+
], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "fileInput", first: true, predicate: DragAndDropFileInputComponent, descendants: true }], ngImport: i0, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelection) {\n <div\n class=\"bg-primary-lighter text-white shrink-0 rounded-full font-bold text-[12px] w-[22px] h-[22px] flex items-center justify-center\"\n >\n 1\n </div>\n }\n </div>\n <button class=\"h-6 w-6 flex items-center\" data-cy=\"clearSelection\">\n @if (hasSelection && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"removeSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-400 rounded shadow-lg pt-[12px] px-[16px] overlay-container w-[280px] flex flex-col gap-2\"\n [style.min-width]=\"overlayMinWidth\"\n >\n <div class=\"text-xs text-black\">\n {{ 'search.filters.spatialExtent.helpText' | translate }}\n </div>\n\n <div\n [class]=\"\n (hasSelection\n ? 'border-b-[1px] border-gray-400 pb-[20px]'\n : 'pb-[8px]') + ' text-black'\n \"\n >\n <gn-ui-drag-and-drop-file-input\n accept=\".json,.geojson\"\n [placeholder]=\"'search.filters.spatialExtent.import' | translate\"\n [maxFileSizeMb]=\"maxFileSizeMb\"\n [showFileName]=\"false\"\n textClass=\"text-black truncate\"\n dropzoneBackgroundColor=\"transparent\"\n icon=\"iconoirImport\"\n extraClass=\"gn-ui-gray-outline-input w-full !h-[32px] px-[8px] py-[4px] justify-between\"\n (fileChange)=\"handleFileSelected($event)\"\n (errorChange)=\"handleFileError($event)\"\n data-cy=\"importGeojson\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n\n @if (hasSelection) {\n <gn-ui-button\n [type]=\"'primary-light'\"\n [title]=\"selectionDeleteLabelKey | translate: selectionLabelParams\"\n (buttonClick)=\"removeSelection($event)\"\n extraClass=\"group gap-x-2 w-full h-[28px] px-[8px] py-[4px] mb-[12px] bg-primary-lighter text-white\"\n data-test=\"spatial-extent-selected-item\"\n >\n <ng-icon\n name=\"iconoirSquareDashed\"\n class=\"shrink-0\"\n size=\"15px\"\n ></ng-icon>\n <span class=\"group-hover:hidden truncate text-sm m-auto\">\n {{ selectionLabelKey | translate: selectionLabelParams }}\n </span>\n <span class=\"hidden group-hover:block truncate text-sm m-auto\">\n {{ selectionDeleteLabelKey | translate: selectionLabelParams }}\n </span>\n <ng-icon\n name=\"iconoirCheckCircle\"\n class=\"group-hover:hidden shrink-0\"\n size=\"14px\"\n ></ng-icon>\n <ng-icon\n name=\"iconoirTrash\"\n class=\"!hidden group-hover:!block shrink-0\"\n size=\"15px\"\n ></ng-icon>\n </gn-ui-button>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: DragAndDropFileInputComponent, selector: "gn-ui-drag-and-drop-file-input", inputs: ["placeholder", "accept", "maxFileSizeMb", "icon", "dropzoneBackgroundColor", "textClass", "extraClass", "showFileName"], outputs: ["fileChange", "errorChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
26156
|
+
}
|
|
26157
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, decorators: [{
|
|
26158
|
+
type: Component,
|
|
26159
|
+
args: [{ selector: 'gn-ui-spatial-extent-dropdown', standalone: true, imports: [
|
|
26160
|
+
ButtonComponent,
|
|
26161
|
+
NgIcon,
|
|
26162
|
+
OverlayModule,
|
|
26163
|
+
TranslatePipe,
|
|
26164
|
+
DragAndDropFileInputComponent,
|
|
26165
|
+
], providers: [
|
|
26166
|
+
provideIcons({
|
|
26167
|
+
iconoirCheckCircle,
|
|
26168
|
+
iconoirImport,
|
|
26169
|
+
iconoirSquareDashed,
|
|
26170
|
+
iconoirTrash,
|
|
26171
|
+
matClose,
|
|
26172
|
+
matExpandLess,
|
|
26173
|
+
matExpandMore,
|
|
26174
|
+
}),
|
|
26175
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelection) {\n <div\n class=\"bg-primary-lighter text-white shrink-0 rounded-full font-bold text-[12px] w-[22px] h-[22px] flex items-center justify-center\"\n >\n 1\n </div>\n }\n </div>\n <button class=\"h-6 w-6 flex items-center\" data-cy=\"clearSelection\">\n @if (hasSelection && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"removeSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-400 rounded shadow-lg pt-[12px] px-[16px] overlay-container w-[280px] flex flex-col gap-2\"\n [style.min-width]=\"overlayMinWidth\"\n >\n <div class=\"text-xs text-black\">\n {{ 'search.filters.spatialExtent.helpText' | translate }}\n </div>\n\n <div\n [class]=\"\n (hasSelection\n ? 'border-b-[1px] border-gray-400 pb-[20px]'\n : 'pb-[8px]') + ' text-black'\n \"\n >\n <gn-ui-drag-and-drop-file-input\n accept=\".json,.geojson\"\n [placeholder]=\"'search.filters.spatialExtent.import' | translate\"\n [maxFileSizeMb]=\"maxFileSizeMb\"\n [showFileName]=\"false\"\n textClass=\"text-black truncate\"\n dropzoneBackgroundColor=\"transparent\"\n icon=\"iconoirImport\"\n extraClass=\"gn-ui-gray-outline-input w-full !h-[32px] px-[8px] py-[4px] justify-between\"\n (fileChange)=\"handleFileSelected($event)\"\n (errorChange)=\"handleFileError($event)\"\n data-cy=\"importGeojson\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n\n @if (hasSelection) {\n <gn-ui-button\n [type]=\"'primary-light'\"\n [title]=\"selectionDeleteLabelKey | translate: selectionLabelParams\"\n (buttonClick)=\"removeSelection($event)\"\n extraClass=\"group gap-x-2 w-full h-[28px] px-[8px] py-[4px] mb-[12px] bg-primary-lighter text-white\"\n data-test=\"spatial-extent-selected-item\"\n >\n <ng-icon\n name=\"iconoirSquareDashed\"\n class=\"shrink-0\"\n size=\"15px\"\n ></ng-icon>\n <span class=\"group-hover:hidden truncate text-sm m-auto\">\n {{ selectionLabelKey | translate: selectionLabelParams }}\n </span>\n <span class=\"hidden group-hover:block truncate text-sm m-auto\">\n {{ selectionDeleteLabelKey | translate: selectionLabelParams }}\n </span>\n <ng-icon\n name=\"iconoirCheckCircle\"\n class=\"group-hover:hidden shrink-0\"\n size=\"14px\"\n ></ng-icon>\n <ng-icon\n name=\"iconoirTrash\"\n class=\"!hidden group-hover:!block shrink-0\"\n size=\"15px\"\n ></ng-icon>\n </gn-ui-button>\n }\n </div>\n</ng-template>\n" }]
|
|
26176
|
+
}], propDecorators: { title: [{
|
|
26177
|
+
type: Input
|
|
26178
|
+
}], maxFileSizeMb: [{
|
|
26179
|
+
type: Input
|
|
26180
|
+
}], initialBbox: [{
|
|
26181
|
+
type: Input
|
|
26182
|
+
}], bboxChange: [{
|
|
26183
|
+
type: Output
|
|
26184
|
+
}], errorChange: [{
|
|
26185
|
+
type: Output
|
|
26186
|
+
}], overlayOrigin: [{
|
|
26187
|
+
type: ViewChild,
|
|
26188
|
+
args: ['overlayOrigin']
|
|
26189
|
+
}], overlay: [{
|
|
26190
|
+
type: ViewChild,
|
|
26191
|
+
args: [CdkConnectedOverlay]
|
|
26192
|
+
}], fileInput: [{
|
|
26193
|
+
type: ViewChild,
|
|
26194
|
+
args: [DragAndDropFileInputComponent]
|
|
26195
|
+
}] } });
|
|
26196
|
+
|
|
25770
26197
|
class CellPopinComponent {
|
|
25771
26198
|
constructor() {
|
|
25772
26199
|
this.scrollDispatcher = inject(ScrollDispatcher);
|
|
@@ -25843,7 +26270,7 @@ class CellPopinComponent {
|
|
|
25843
26270
|
this.isOpen = false;
|
|
25844
26271
|
}
|
|
25845
26272
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CellPopinComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
25846
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: CellPopinComponent, isStandalone: true, selector: "gn-ui-cell-popin", inputs: { extraClass: "extraClass", cdkScrollContainer: "cdkScrollContainer", scrollContainer: "scrollContainer", activePopin: "activePopin" }, providers: [provideIcons({ iconoirReduce })], viewQueries: [{ propertyName: "anchorRef", first: true, predicate: ["anchorRef"], descendants: true }], ngImport: i0, template: "<div\n class=\"h-full flex items-center justify-center\"\n cdkOverlayOrigin\n #anchorRef\n #trigger=\"cdkOverlayOrigin\"\n [class]=\"extraClass\"\n data-cy=\"cell-popin\"\n>\n <ng-content select=\"[cellContent]\"></ng-content>\n @if (activePopin) {\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayPush]=\"false\"\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayWidth]=\"'auto'\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n [cdkConnectedOverlayGrowAfterOpen]=\"true\"\n (detach)=\"closeOverlay()\"\n >\n @if (activePopin && isVisible) {\n <div\n class=\"bg-white shadow-lg border border-gray-300 relative\"\n data-cy=\"cell-popin-content\"\n >\n <ng-content select=\"[popinContent]\"></ng-content>\n <gn-ui-button\n data-cy=\"cell-popin-close\"\n [style.--gn-ui-button-background]=\"'bg-transparent'\"\n class=\"absolute top-2 right-2\"\n type=\"light\"\n (buttonClick)=\"closeOverlay()\"\n extraClass=\"w-10 h-8 px-1\"\n >\n <ng-icon name=\"iconoirReduce\" size=\"24\"></ng-icon>\n </gn-ui-button>\n </div>\n }\n </ng-template>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$
|
|
26273
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: CellPopinComponent, isStandalone: true, selector: "gn-ui-cell-popin", inputs: { extraClass: "extraClass", cdkScrollContainer: "cdkScrollContainer", scrollContainer: "scrollContainer", activePopin: "activePopin" }, providers: [provideIcons({ iconoirReduce })], viewQueries: [{ propertyName: "anchorRef", first: true, predicate: ["anchorRef"], descendants: true }], ngImport: i0, template: "<div\n class=\"h-full flex items-center justify-center\"\n cdkOverlayOrigin\n #anchorRef\n #trigger=\"cdkOverlayOrigin\"\n [class]=\"extraClass\"\n data-cy=\"cell-popin\"\n>\n <ng-content select=\"[cellContent]\"></ng-content>\n @if (activePopin) {\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayPush]=\"false\"\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayWidth]=\"'auto'\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n [cdkConnectedOverlayGrowAfterOpen]=\"true\"\n (detach)=\"closeOverlay()\"\n >\n @if (activePopin && isVisible) {\n <div\n class=\"bg-white shadow-lg border border-gray-300 relative\"\n data-cy=\"cell-popin-content\"\n >\n <ng-content select=\"[popinContent]\"></ng-content>\n <gn-ui-button\n data-cy=\"cell-popin-close\"\n [style.--gn-ui-button-background]=\"'bg-transparent'\"\n class=\"absolute top-2 right-2\"\n type=\"light\"\n (buttonClick)=\"closeOverlay()\"\n extraClass=\"w-10 h-8 px-1\"\n >\n <ng-icon name=\"iconoirReduce\" size=\"24\"></ng-icon>\n </gn-ui-button>\n </div>\n }\n </ng-template>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }] }); }
|
|
25847
26274
|
}
|
|
25848
26275
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CellPopinComponent, decorators: [{
|
|
25849
26276
|
type: Component,
|
|
@@ -27465,6 +27892,66 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
27465
27892
|
}]
|
|
27466
27893
|
}] });
|
|
27467
27894
|
|
|
27895
|
+
const GEOCODING_PROVIDER = new InjectionToken('geocoding-provider', {
|
|
27896
|
+
providedIn: 'root',
|
|
27897
|
+
factory: () => ['geonames', { maxRows: 5 }],
|
|
27898
|
+
});
|
|
27899
|
+
class GeocodingService {
|
|
27900
|
+
constructor() {
|
|
27901
|
+
this.provider = inject(GEOCODING_PROVIDER);
|
|
27902
|
+
}
|
|
27903
|
+
query(text) {
|
|
27904
|
+
let queryObservable;
|
|
27905
|
+
switch (this.provider[0]) {
|
|
27906
|
+
case 'geoadmin':
|
|
27907
|
+
queryObservable = from(queryGeoadmin(text, this.provider[1]));
|
|
27908
|
+
break;
|
|
27909
|
+
case 'geonames':
|
|
27910
|
+
queryObservable = from(queryGeonames(text, this.provider[1]));
|
|
27911
|
+
break;
|
|
27912
|
+
case 'data-gouv-fr':
|
|
27913
|
+
queryObservable = from(queryDataGouvFr(text, this.provider[1]));
|
|
27914
|
+
break;
|
|
27915
|
+
default:
|
|
27916
|
+
return throwError(() => new Error(`Unsupported geocoding provider: ${this.provider[0]}`));
|
|
27917
|
+
}
|
|
27918
|
+
return queryObservable.pipe(catchError((error) => throwError(error)));
|
|
27919
|
+
}
|
|
27920
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
27921
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingService, providedIn: 'root' }); }
|
|
27922
|
+
}
|
|
27923
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingService, decorators: [{
|
|
27924
|
+
type: Injectable,
|
|
27925
|
+
args: [{
|
|
27926
|
+
providedIn: 'root',
|
|
27927
|
+
}]
|
|
27928
|
+
}] });
|
|
27929
|
+
|
|
27930
|
+
class LocationSearchComponent {
|
|
27931
|
+
constructor() {
|
|
27932
|
+
this.geocodingService = inject(GeocodingService);
|
|
27933
|
+
this.placeholder = '';
|
|
27934
|
+
this.resultSelected = new EventEmitter();
|
|
27935
|
+
this.displayWithFn = (item) => item.label;
|
|
27936
|
+
this.searchAction = (text) => this.geocodingService.query(text);
|
|
27937
|
+
}
|
|
27938
|
+
handleItemSelected(item) {
|
|
27939
|
+
this.resultSelected.emit(item);
|
|
27940
|
+
}
|
|
27941
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LocationSearchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
27942
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: LocationSearchComponent, isStandalone: true, selector: "gn-ui-location-search", inputs: { placeholder: "placeholder", displayWithTemplate: "displayWithTemplate" }, outputs: { resultSelected: "resultSelected" }, ngImport: i0, template: "<gn-ui-autocomplete\n [action]=\"searchAction\"\n [displayWithFn]=\"displayWithFn\"\n [placeholder]=\"placeholder\"\n [displayWithTemplate]=\"displayWithTemplate\"\n (itemSelected)=\"handleItemSelected($event)\"\n>\n</gn-ui-autocomplete>\n", dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithTemplate", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
27943
|
+
}
|
|
27944
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LocationSearchComponent, decorators: [{
|
|
27945
|
+
type: Component,
|
|
27946
|
+
args: [{ selector: 'gn-ui-location-search', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [AutocompleteComponent], template: "<gn-ui-autocomplete\n [action]=\"searchAction\"\n [displayWithFn]=\"displayWithFn\"\n [placeholder]=\"placeholder\"\n [displayWithTemplate]=\"displayWithTemplate\"\n (itemSelected)=\"handleItemSelected($event)\"\n>\n</gn-ui-autocomplete>\n" }]
|
|
27947
|
+
}], propDecorators: { placeholder: [{
|
|
27948
|
+
type: Input
|
|
27949
|
+
}], displayWithTemplate: [{
|
|
27950
|
+
type: Input
|
|
27951
|
+
}], resultSelected: [{
|
|
27952
|
+
type: Output
|
|
27953
|
+
}] } });
|
|
27954
|
+
|
|
27468
27955
|
// expects the replacement key ${uuid}
|
|
27469
27956
|
const RECORD_DATASET_URL_TOKEN = new InjectionToken('record-dataset-url-token');
|
|
27470
27957
|
const RECORD_SERVICE_URL_TOKEN = new InjectionToken('record-service-url-token');
|
|
@@ -27933,6 +28420,45 @@ class DateRangeSearchField extends SimpleSearchField {
|
|
|
27933
28420
|
return 'dateRange';
|
|
27934
28421
|
}
|
|
27935
28422
|
}
|
|
28423
|
+
// treats a resource's creation/revision dates as an interval (its min and max), so a record matches
|
|
28424
|
+
// when that interval intersects the filter range, not just when a single date falls inside it
|
|
28425
|
+
class ResourceCreationRevisionDateSearchField extends DateRangeSearchField {
|
|
28426
|
+
constructor(injector, order = 'desc') {
|
|
28427
|
+
super('resourceCreationRevisionDate', injector, order);
|
|
28428
|
+
const dateSourceFields = [
|
|
28429
|
+
'creationDateForResource',
|
|
28430
|
+
'revisionDateForResource',
|
|
28431
|
+
];
|
|
28432
|
+
const collectSortedDates = dateSourceFields
|
|
28433
|
+
.map((field) => `if (doc.containsKey('${field}')) {
|
|
28434
|
+
for (def date : doc['${field}']) { dates.add(date.millis); }
|
|
28435
|
+
}`)
|
|
28436
|
+
.join('\n');
|
|
28437
|
+
this.esService.registerRuntimeField('resourceCreationRevisionDateMin', `def dates = [];
|
|
28438
|
+
${collectSortedDates}
|
|
28439
|
+
if (dates.length > 0) { dates.sort(null); emit(dates[0]); }`, 'date');
|
|
28440
|
+
this.esService.registerRuntimeField('resourceCreationRevisionDateMax', `def dates = [];
|
|
28441
|
+
${collectSortedDates}
|
|
28442
|
+
if (dates.length > 0) { dates.sort(null); emit(dates[dates.length - 1]); }`, 'date');
|
|
28443
|
+
}
|
|
28444
|
+
}
|
|
28445
|
+
class BoundingBoxSearchField extends SimpleSearchField {
|
|
28446
|
+
getAvailableValues() {
|
|
28447
|
+
return of([]);
|
|
28448
|
+
}
|
|
28449
|
+
getFiltersForValues(values) {
|
|
28450
|
+
return of({
|
|
28451
|
+
[this.esFieldName]: values.map(Number),
|
|
28452
|
+
});
|
|
28453
|
+
}
|
|
28454
|
+
getValuesForFilter(filters) {
|
|
28455
|
+
const filter = filters[this.esFieldName];
|
|
28456
|
+
return of(isBoundingBox(filter) ? filter : []);
|
|
28457
|
+
}
|
|
28458
|
+
getType() {
|
|
28459
|
+
return 'spatialExtent';
|
|
28460
|
+
}
|
|
28461
|
+
}
|
|
27936
28462
|
marker('search.filters.availableServices.view');
|
|
27937
28463
|
marker('search.filters.availableServices.download');
|
|
27938
28464
|
class AvailableServicesField extends SimpleSearchField {
|
|
@@ -28077,6 +28603,8 @@ marker('search.filters.producerOrg');
|
|
|
28077
28603
|
marker('search.filters.publisherOrg');
|
|
28078
28604
|
marker('search.filters.user');
|
|
28079
28605
|
marker('search.filters.changeDate');
|
|
28606
|
+
marker('search.filters.resourceCreationRevisionDate');
|
|
28607
|
+
marker('search.filters.spatialExtent');
|
|
28080
28608
|
class FieldsService {
|
|
28081
28609
|
constructor() {
|
|
28082
28610
|
this.injector = inject(Injector);
|
|
@@ -28099,7 +28627,9 @@ class FieldsService {
|
|
|
28099
28627
|
publisherOrg: new MultilingualSearchField('distributorOrgForResourceObject', this.injector, 'asc', 'key'),
|
|
28100
28628
|
user: new UserSearchField(this.injector),
|
|
28101
28629
|
changeDate: new DateRangeSearchField('changeDate', this.injector, 'desc'),
|
|
28630
|
+
resourceCreationRevisionDate: new ResourceCreationRevisionDateSearchField(this.injector, 'desc'),
|
|
28102
28631
|
availableServices: new AvailableServicesField(this.injector),
|
|
28632
|
+
spatialExtent: new BoundingBoxSearchField('spatialExtent', this.injector),
|
|
28103
28633
|
};
|
|
28104
28634
|
}
|
|
28105
28635
|
get supportedFields() {
|
|
@@ -29362,7 +29892,7 @@ class ContactPillComponent {
|
|
|
29362
29892
|
this.overlayOpen = false;
|
|
29363
29893
|
}
|
|
29364
29894
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ContactPillComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
29365
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: ContactPillComponent, isStandalone: true, selector: "gn-ui-contact-pill", inputs: { contact: "contact" }, ngImport: i0, template: "<gn-ui-button\n [type]=\"overlayOpen ? 'gray-light' : 'primary-light'\"\n extraClass=\"group w-full min-h-12 gap-3 justify-between py-2 pl-5 pr-4 rounded\"\n data-test=\"contact-pill\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <span\n class=\"font-title font-medium text-base leading-tight truncate\"\n [class]=\"!overlayOpen ? 'text-primary-black group-hover:text-white' : ''\"\n [title]=\"displayName\"\n >{{ displayName }}</span\n >\n <div\n class=\"gn-ui-card-icon items-center justify-center w-10 h-8\"\n [class]=\"\n !overlayOpen ? 'group-hover:border-white group-hover:text-white' : ''\n \"\n >\n @if (overlayOpen) {\n <ng-icon class=\"!w-6 !h-6 !text-[24px]\" name=\"matClose\"></ng-icon>\n } @else {\n <ng-icon class=\"!w-6 !h-6 !text-[24px]\" name=\"matInfoOutline\"></ng-icon>\n }\n </div>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayOffsetX]=\"overlayOffsetX\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div [style.width.px]=\"overlayWidth\">\n <gn-ui-contact-details [contact]=\"contact\"></gn-ui-contact-details>\n </div>\n</ng-template>\n", dependencies: [{ kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$
|
|
29895
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: ContactPillComponent, isStandalone: true, selector: "gn-ui-contact-pill", inputs: { contact: "contact" }, ngImport: i0, template: "<gn-ui-button\n [type]=\"overlayOpen ? 'gray-light' : 'primary-light'\"\n extraClass=\"group w-full min-h-12 gap-3 justify-between py-2 pl-5 pr-4 rounded\"\n data-test=\"contact-pill\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <span\n class=\"font-title font-medium text-base leading-tight truncate\"\n [class]=\"!overlayOpen ? 'text-primary-black group-hover:text-white' : ''\"\n [title]=\"displayName\"\n >{{ displayName }}</span\n >\n <div\n class=\"gn-ui-card-icon items-center justify-center w-10 h-8\"\n [class]=\"\n !overlayOpen ? 'group-hover:border-white group-hover:text-white' : ''\n \"\n >\n @if (overlayOpen) {\n <ng-icon class=\"!w-6 !h-6 !text-[24px]\" name=\"matClose\"></ng-icon>\n } @else {\n <ng-icon class=\"!w-6 !h-6 !text-[24px]\" name=\"matInfoOutline\"></ng-icon>\n }\n </div>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayOffsetX]=\"overlayOffsetX\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div [style.width.px]=\"overlayWidth\">\n <gn-ui-contact-details [contact]=\"contact\"></gn-ui-contact-details>\n </div>\n</ng-template>\n", dependencies: [{ kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: ContactDetailsComponent, selector: "gn-ui-contact-details", inputs: ["contact"] }], viewProviders: [provideIcons({ matClose, matInfoOutline })], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
29366
29896
|
}
|
|
29367
29897
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ContactPillComponent, decorators: [{
|
|
29368
29898
|
type: Component,
|
|
@@ -30088,7 +30618,7 @@ class ServiceCapabilitiesComponent {
|
|
|
30088
30618
|
if (this.apiLinks.length > 0 &&
|
|
30089
30619
|
this.apiLinks[0].accessServiceProtocol !== 'ogcFeatures') {
|
|
30090
30620
|
this.loading = true;
|
|
30091
|
-
this.availableLayers = await getLayers(this.apiLinks[0].url.href, this.apiLinks[0].accessServiceProtocol);
|
|
30621
|
+
this.availableLayers = await getLayers(this.apiLinks[0].url.href, this.apiLinks[0].accessServiceProtocol, true);
|
|
30092
30622
|
this.loading = false;
|
|
30093
30623
|
this.cdr.detectChanges();
|
|
30094
30624
|
this.filteredLayers = this.availableLayers;
|
|
@@ -31284,19 +31814,118 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31284
31814
|
type: Output
|
|
31285
31815
|
}] } });
|
|
31286
31816
|
|
|
31817
|
+
class NotificationsService {
|
|
31818
|
+
constructor() {
|
|
31819
|
+
this.notifications$ = new BehaviorSubject([]);
|
|
31820
|
+
}
|
|
31821
|
+
showNotification(content, timeoutMs, error) {
|
|
31822
|
+
error && console.error(error);
|
|
31823
|
+
const id = Math.floor(Math.random() * 1000000);
|
|
31824
|
+
this.notifications$.next([...this.notifications$.value, { ...content, id }]);
|
|
31825
|
+
if (typeof timeoutMs !== 'undefined') {
|
|
31826
|
+
setTimeout(() => {
|
|
31827
|
+
this.removeNotificationById(id);
|
|
31828
|
+
}, timeoutMs);
|
|
31829
|
+
}
|
|
31830
|
+
return id;
|
|
31831
|
+
}
|
|
31832
|
+
removeNotificationById(id) {
|
|
31833
|
+
this.notifications$.next(this.notifications$.value.filter((n) => n.id !== id));
|
|
31834
|
+
}
|
|
31835
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
31836
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, providedIn: 'root' }); }
|
|
31837
|
+
}
|
|
31838
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, decorators: [{
|
|
31839
|
+
type: Injectable,
|
|
31840
|
+
args: [{
|
|
31841
|
+
providedIn: 'root',
|
|
31842
|
+
}]
|
|
31843
|
+
}] });
|
|
31844
|
+
|
|
31845
|
+
class NotificationsContainerComponent {
|
|
31846
|
+
constructor() {
|
|
31847
|
+
this.notificationsService = inject(NotificationsService);
|
|
31848
|
+
}
|
|
31849
|
+
trackById(index, notification) {
|
|
31850
|
+
return notification.id;
|
|
31851
|
+
}
|
|
31852
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31853
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: NotificationsContainerComponent, isStandalone: true, selector: "gn-ui-notifications-container", ngImport: i0, template: "<div class=\"flex flex-col gap-6 p-6 items-start pointer-events-none\">\n @for (\n notification of notificationsService.notifications$ | async;\n track trackById($index, notification)\n ) {\n <gn-ui-notification\n class=\"max-w-full pointer-events-auto\"\n [text]=\"notification.text\"\n [type]=\"notification.type\"\n [title]=\"notification.title\"\n [closeMessage]=\"notification.closeMessage\"\n (notificationClose)=\"\n notificationsService.removeNotificationById(notification.id)\n \"\n [@enterExit]\n ></gn-ui-notification>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: NotificationComponent, selector: "gn-ui-notification", inputs: ["type", "title", "text", "closeMessage"], outputs: ["notificationClose"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], animations: [
|
|
31854
|
+
trigger('enterExit', [
|
|
31855
|
+
transition(':enter', [
|
|
31856
|
+
animate('150ms', keyframes([
|
|
31857
|
+
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31858
|
+
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31859
|
+
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31860
|
+
])),
|
|
31861
|
+
]),
|
|
31862
|
+
transition(':leave', [
|
|
31863
|
+
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31864
|
+
]),
|
|
31865
|
+
]),
|
|
31866
|
+
], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31867
|
+
}
|
|
31868
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, decorators: [{
|
|
31869
|
+
type: Component,
|
|
31870
|
+
args: [{ selector: 'gn-ui-notifications-container', standalone: true, imports: [CommonModule, NotificationComponent], changeDetection: ChangeDetectionStrategy.OnPush, animations: [
|
|
31871
|
+
trigger('enterExit', [
|
|
31872
|
+
transition(':enter', [
|
|
31873
|
+
animate('150ms', keyframes([
|
|
31874
|
+
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31875
|
+
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31876
|
+
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31877
|
+
])),
|
|
31878
|
+
]),
|
|
31879
|
+
transition(':leave', [
|
|
31880
|
+
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31881
|
+
]),
|
|
31882
|
+
]),
|
|
31883
|
+
], template: "<div class=\"flex flex-col gap-6 p-6 items-start pointer-events-none\">\n @for (\n notification of notificationsService.notifications$ | async;\n track trackById($index, notification)\n ) {\n <gn-ui-notification\n class=\"max-w-full pointer-events-auto\"\n [text]=\"notification.text\"\n [type]=\"notification.type\"\n [title]=\"notification.title\"\n [closeMessage]=\"notification.closeMessage\"\n (notificationClose)=\"\n notificationsService.removeNotificationById(notification.id)\n \"\n [@enterExit]\n ></gn-ui-notification>\n }\n</div>\n" }]
|
|
31884
|
+
}] });
|
|
31885
|
+
|
|
31287
31886
|
class FilterDropdownComponent {
|
|
31288
31887
|
constructor() {
|
|
31289
31888
|
this.searchFacade = inject(SearchFacade);
|
|
31290
31889
|
this.searchService = inject(SearchService);
|
|
31291
31890
|
this.fieldsService = inject(FieldsService);
|
|
31891
|
+
this.notificationsService = inject(NotificationsService);
|
|
31892
|
+
this.translateService = inject(TranslateService);
|
|
31893
|
+
this.spatialExtentMaxFileSize = getOptionalSearchConfig()?.SPATIAL_EXTENT_MAX_FILE_SIZE;
|
|
31292
31894
|
this.selected$ = this.searchFacade.searchFilters$.pipe(switchMap((filters) => this.fieldsService.readFieldValuesFromFilters(filters)), map$1((fieldValues) => fieldValues[this.fieldName]), filter$1((selected) => !!selected), startWith$1([]), catchError(() => of([])));
|
|
31293
|
-
this.selectedDateRange$ = this.selected$.pipe(map$1((
|
|
31895
|
+
this.selectedDateRange$ = this.selected$.pipe(map$1((selected) => (Array.isArray(selected) ? {} : selected)));
|
|
31896
|
+
this.selectedBoundingBox$ = this.selected$.pipe(map$1((selected) => Array.isArray(selected) && selected.length > 0
|
|
31897
|
+
? selected
|
|
31898
|
+
: null));
|
|
31899
|
+
this.spatialExtentErrorNotificationId = null;
|
|
31294
31900
|
}
|
|
31295
31901
|
onSelectedValues(values) {
|
|
31296
31902
|
this.fieldsService
|
|
31297
31903
|
.buildFiltersFromFieldValues({ [this.fieldName]: values })
|
|
31298
31904
|
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
31299
31905
|
}
|
|
31906
|
+
onBboxChange(bbox) {
|
|
31907
|
+
this.fieldsService
|
|
31908
|
+
.buildFiltersFromFieldValues({
|
|
31909
|
+
[this.fieldName]: bbox,
|
|
31910
|
+
})
|
|
31911
|
+
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
31912
|
+
this.clearSpatialExtentErrorNotification();
|
|
31913
|
+
}
|
|
31914
|
+
onSpatialExtentError(error) {
|
|
31915
|
+
this.clearSpatialExtentErrorNotification();
|
|
31916
|
+
this.spatialExtentErrorNotificationId =
|
|
31917
|
+
this.notificationsService.showNotification({
|
|
31918
|
+
type: 'error',
|
|
31919
|
+
title: this.translateService.instant('search.filters.spatialExtent.error.title'),
|
|
31920
|
+
text: this.translateService.instant(error.key, error.params),
|
|
31921
|
+
});
|
|
31922
|
+
}
|
|
31923
|
+
clearSpatialExtentErrorNotification() {
|
|
31924
|
+
if (this.spatialExtentErrorNotificationId === null)
|
|
31925
|
+
return;
|
|
31926
|
+
this.notificationsService.removeNotificationById(this.spatialExtentErrorNotificationId);
|
|
31927
|
+
this.spatialExtentErrorNotificationId = null;
|
|
31928
|
+
}
|
|
31300
31929
|
ngOnInit() {
|
|
31301
31930
|
this.fieldType = this.fieldsService.getFieldType(this.fieldName);
|
|
31302
31931
|
this.choices$ = this.fieldsService.getAvailableValues(this.fieldName).pipe(startWith$1([]), map$1((values) => values.map((v) => ({
|
|
@@ -31304,27 +31933,15 @@ class FilterDropdownComponent {
|
|
|
31304
31933
|
value: v.value.toString(), // converting to string for the dropdown
|
|
31305
31934
|
}))), catchError(() => of([])));
|
|
31306
31935
|
}
|
|
31307
|
-
|
|
31308
|
-
|
|
31309
|
-
|
|
31310
|
-
|
|
31311
|
-
|
|
31312
|
-
|
|
31313
|
-
if (!end)
|
|
31314
|
-
return;
|
|
31315
|
-
this.dateRange = { ...this.dateRange, end };
|
|
31316
|
-
if (this.dateRange.start && this.dateRange.end) {
|
|
31317
|
-
this.fieldsService
|
|
31318
|
-
.buildFiltersFromFieldValues({
|
|
31319
|
-
[this.fieldName]: this.dateRange,
|
|
31320
|
-
})
|
|
31321
|
-
.subscribe((filters) => {
|
|
31322
|
-
return this.searchService.updateFilters(filters);
|
|
31323
|
-
});
|
|
31324
|
-
}
|
|
31936
|
+
onDateRangeChange(dateRange) {
|
|
31937
|
+
this.fieldsService
|
|
31938
|
+
.buildFiltersFromFieldValues({
|
|
31939
|
+
[this.fieldName]: dateRange,
|
|
31940
|
+
})
|
|
31941
|
+
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
31325
31942
|
}
|
|
31326
31943
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31327
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FilterDropdownComponent, isStandalone: true, selector: "gn-ui-filter-dropdown", inputs: { fieldName: "fieldName", title: "title" }, ngImport: i0, template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [
|
|
31944
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FilterDropdownComponent, isStandalone: true, selector: "gn-ui-filter-dropdown", inputs: { fieldName: "fieldName", title: "title" }, ngImport: i0, template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [dateRange]=\"(selectedDateRange$ | async) ?? {}\"\n (dateRangeChange)=\"onDateRangeChange($event)\"\n [attr.data-cy-field]=\"fieldName\"\n ></gn-ui-date-range-dropdown>\n} @else if (fieldType === 'spatialExtent') {\n <gn-ui-spatial-extent-dropdown\n [title]=\"title\"\n [initialBbox]=\"(selectedBoundingBox$ | async) ?? null\"\n [maxFileSizeMb]=\"spatialExtentMaxFileSize\"\n (bboxChange)=\"onBboxChange($event)\"\n (errorChange)=\"onSpatialExtentError($event)\"\n [attr.data-cy-field]=\"fieldName\"\n ></gn-ui-spatial-extent-dropdown>\n} @else {\n <gn-ui-dropdown-multiselect\n class=\"w-full\"\n [title]=\"title\"\n [maxRows]=\"6\"\n [choices]=\"(choices$ | async) || []\"\n [selected]=\"selected$ | async\"\n [allowSearch]=\"true\"\n (selectValues)=\"onSelectedValues($event)\"\n [attr.data-cy-field]=\"fieldName\"\n >\n </gn-ui-dropdown-multiselect>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DateRangeDropdownComponent, selector: "gn-ui-date-range-dropdown", inputs: ["title", "dateRange"], outputs: ["dateRangeChange"] }, { kind: "component", type: DropdownMultiselectComponent, selector: "gn-ui-dropdown-multiselect", inputs: ["title", "choices", "selected", "allowSearch", "maxRows", "searchInputValue"], outputs: ["selectValues"] }, { kind: "component", type: SpatialExtentDropdownComponent, selector: "gn-ui-spatial-extent-dropdown", inputs: ["title", "maxFileSizeMb", "initialBbox"], outputs: ["bboxChange", "errorChange"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31328
31945
|
}
|
|
31329
31946
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, decorators: [{
|
|
31330
31947
|
type: Component,
|
|
@@ -31332,7 +31949,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31332
31949
|
CommonModule,
|
|
31333
31950
|
DateRangeDropdownComponent,
|
|
31334
31951
|
DropdownMultiselectComponent,
|
|
31335
|
-
|
|
31952
|
+
SpatialExtentDropdownComponent,
|
|
31953
|
+
], template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [dateRange]=\"(selectedDateRange$ | async) ?? {}\"\n (dateRangeChange)=\"onDateRangeChange($event)\"\n [attr.data-cy-field]=\"fieldName\"\n ></gn-ui-date-range-dropdown>\n} @else if (fieldType === 'spatialExtent') {\n <gn-ui-spatial-extent-dropdown\n [title]=\"title\"\n [initialBbox]=\"(selectedBoundingBox$ | async) ?? null\"\n [maxFileSizeMb]=\"spatialExtentMaxFileSize\"\n (bboxChange)=\"onBboxChange($event)\"\n (errorChange)=\"onSpatialExtentError($event)\"\n [attr.data-cy-field]=\"fieldName\"\n ></gn-ui-spatial-extent-dropdown>\n} @else {\n <gn-ui-dropdown-multiselect\n class=\"w-full\"\n [title]=\"title\"\n [maxRows]=\"6\"\n [choices]=\"(choices$ | async) || []\"\n [selected]=\"selected$ | async\"\n [allowSearch]=\"true\"\n (selectValues)=\"onSelectedValues($event)\"\n [attr.data-cy-field]=\"fieldName\"\n >\n </gn-ui-dropdown-multiselect>\n}\n" }]
|
|
31336
31954
|
}], propDecorators: { fieldName: [{
|
|
31337
31955
|
type: Input
|
|
31338
31956
|
}], title: [{
|
|
@@ -31393,7 +32011,7 @@ class FuzzySearchComponent {
|
|
|
31393
32011
|
this.isSearchActive.emit(event);
|
|
31394
32012
|
}
|
|
31395
32013
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FuzzySearchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31396
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: FuzzySearchComponent, isStandalone: true, selector: "gn-ui-fuzzy-search", inputs: { autoFocus: "autoFocus", forceTrackPosition: "forceTrackPosition", enterButton: "enterButton", placeholder: "placeholder" }, outputs: { itemSelected: "itemSelected", inputSubmitted: "inputSubmitted", isSearchActive: "isSearchActive" }, viewQueries: [{ propertyName: "autocomplete", first: true, predicate: AutocompleteComponent, descendants: true }], ngImport: i0, template: "<gn-ui-autocomplete\n [placeholder]=\"placeholder ?? ('search.field.any.placeholder' | translate)\"\n [displayWithFn]=\"displayWithFn\"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"handleItemSelection($event)\"\n (inputSubmitted)=\"handleInputSubmission($event)\"\n (inputCleared)=\"handleInputCleared()\"\n (isSearchActive)=\"handleSearchActive($event)\"\n [value]=\"searchInputValue$ | async\"\n [preventCompleteOnSelection]=\"true\"\n [autoFocus]=\"autoFocus\"\n [allowSubmit]=\"true\"\n [forceTrackPosition]=\"forceTrackPosition\"\n [enterButton]=\"enterButton\"\n></gn-ui-autocomplete>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
32014
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: FuzzySearchComponent, isStandalone: true, selector: "gn-ui-fuzzy-search", inputs: { autoFocus: "autoFocus", forceTrackPosition: "forceTrackPosition", enterButton: "enterButton", placeholder: "placeholder" }, outputs: { itemSelected: "itemSelected", inputSubmitted: "inputSubmitted", isSearchActive: "isSearchActive" }, viewQueries: [{ propertyName: "autocomplete", first: true, predicate: AutocompleteComponent, descendants: true }], ngImport: i0, template: "<gn-ui-autocomplete\n [placeholder]=\"placeholder ?? ('search.field.any.placeholder' | translate)\"\n [displayWithFn]=\"displayWithFn\"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"handleItemSelection($event)\"\n (inputSubmitted)=\"handleInputSubmission($event)\"\n (inputCleared)=\"handleInputCleared()\"\n (isSearchActive)=\"handleSearchActive($event)\"\n [value]=\"searchInputValue$ | async\"\n [preventCompleteOnSelection]=\"true\"\n [autoFocus]=\"autoFocus\"\n [allowSubmit]=\"true\"\n [forceTrackPosition]=\"forceTrackPosition\"\n [enterButton]=\"enterButton\"\n></gn-ui-autocomplete>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithTemplate", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31397
32015
|
}
|
|
31398
32016
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FuzzySearchComponent, decorators: [{
|
|
31399
32017
|
type: Component,
|
|
@@ -31802,11 +32420,7 @@ const SortByEnum = {
|
|
|
31802
32420
|
RELEVANCY: ['desc', '_score'],
|
|
31803
32421
|
QUALITY_SCORE: ['desc', 'qualityScore'],
|
|
31804
32422
|
CHANGE_DATE: ['desc', 'changeDate'],
|
|
31805
|
-
|
|
31806
|
-
['desc', 'revisionDateForResource'],
|
|
31807
|
-
['desc', 'publicationDateForResource'],
|
|
31808
|
-
['desc', 'creationDateForResource'],
|
|
31809
|
-
],
|
|
32423
|
+
RESOURCE_DATE: ['desc', 'resourceDate.date'],
|
|
31810
32424
|
};
|
|
31811
32425
|
|
|
31812
32426
|
class SortByComponent {
|
|
@@ -31820,7 +32434,7 @@ class SortByComponent {
|
|
|
31820
32434
|
},
|
|
31821
32435
|
{
|
|
31822
32436
|
label: marker('results.sortBy.dateStamp'),
|
|
31823
|
-
value: SortByEnum.
|
|
32437
|
+
value: SortByEnum.RESOURCE_DATE,
|
|
31824
32438
|
},
|
|
31825
32439
|
{
|
|
31826
32440
|
label: marker('results.sortBy.popularity'),
|
|
@@ -31872,74 +32486,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31872
32486
|
args: ['gnUiSearchStateContainer']
|
|
31873
32487
|
}] } });
|
|
31874
32488
|
|
|
31875
|
-
class NotificationsService {
|
|
31876
|
-
constructor() {
|
|
31877
|
-
this.notifications$ = new BehaviorSubject([]);
|
|
31878
|
-
}
|
|
31879
|
-
showNotification(content, timeoutMs, error) {
|
|
31880
|
-
error && console.error(error);
|
|
31881
|
-
const id = Math.floor(Math.random() * 1000000);
|
|
31882
|
-
this.notifications$.next([...this.notifications$.value, { ...content, id }]);
|
|
31883
|
-
if (typeof timeoutMs === 'undefined')
|
|
31884
|
-
return;
|
|
31885
|
-
setTimeout(() => {
|
|
31886
|
-
this.removeNotificationById(id);
|
|
31887
|
-
}, timeoutMs);
|
|
31888
|
-
}
|
|
31889
|
-
removeNotificationById(id) {
|
|
31890
|
-
this.notifications$.next(this.notifications$.value.filter((n) => n.id !== id));
|
|
31891
|
-
}
|
|
31892
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
31893
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, providedIn: 'root' }); }
|
|
31894
|
-
}
|
|
31895
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, decorators: [{
|
|
31896
|
-
type: Injectable,
|
|
31897
|
-
args: [{
|
|
31898
|
-
providedIn: 'root',
|
|
31899
|
-
}]
|
|
31900
|
-
}] });
|
|
31901
|
-
|
|
31902
|
-
class NotificationsContainerComponent {
|
|
31903
|
-
constructor() {
|
|
31904
|
-
this.notificationsService = inject(NotificationsService);
|
|
31905
|
-
}
|
|
31906
|
-
trackById(index, notification) {
|
|
31907
|
-
return notification.id;
|
|
31908
|
-
}
|
|
31909
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31910
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: NotificationsContainerComponent, isStandalone: true, selector: "gn-ui-notifications-container", ngImport: i0, template: "<div class=\"flex flex-col gap-6 p-6 items-start pointer-events-none\">\n @for (\n notification of notificationsService.notifications$ | async;\n track trackById($index, notification)\n ) {\n <gn-ui-notification\n class=\"max-w-full pointer-events-auto\"\n [text]=\"notification.text\"\n [type]=\"notification.type\"\n [title]=\"notification.title\"\n [closeMessage]=\"notification.closeMessage\"\n (notificationClose)=\"\n notificationsService.removeNotificationById(notification.id)\n \"\n [@enterExit]\n ></gn-ui-notification>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: NotificationComponent, selector: "gn-ui-notification", inputs: ["type", "title", "text", "closeMessage"], outputs: ["notificationClose"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], animations: [
|
|
31911
|
-
trigger('enterExit', [
|
|
31912
|
-
transition(':enter', [
|
|
31913
|
-
animate('150ms', keyframes([
|
|
31914
|
-
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31915
|
-
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31916
|
-
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31917
|
-
])),
|
|
31918
|
-
]),
|
|
31919
|
-
transition(':leave', [
|
|
31920
|
-
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31921
|
-
]),
|
|
31922
|
-
]),
|
|
31923
|
-
], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31924
|
-
}
|
|
31925
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, decorators: [{
|
|
31926
|
-
type: Component,
|
|
31927
|
-
args: [{ selector: 'gn-ui-notifications-container', standalone: true, imports: [CommonModule, NotificationComponent], changeDetection: ChangeDetectionStrategy.OnPush, animations: [
|
|
31928
|
-
trigger('enterExit', [
|
|
31929
|
-
transition(':enter', [
|
|
31930
|
-
animate('150ms', keyframes([
|
|
31931
|
-
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31932
|
-
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31933
|
-
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31934
|
-
])),
|
|
31935
|
-
]),
|
|
31936
|
-
transition(':leave', [
|
|
31937
|
-
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31938
|
-
]),
|
|
31939
|
-
]),
|
|
31940
|
-
], template: "<div class=\"flex flex-col gap-6 p-6 items-start pointer-events-none\">\n @for (\n notification of notificationsService.notifications$ | async;\n track trackById($index, notification)\n ) {\n <gn-ui-notification\n class=\"max-w-full pointer-events-auto\"\n [text]=\"notification.text\"\n [type]=\"notification.type\"\n [title]=\"notification.title\"\n [closeMessage]=\"notification.closeMessage\"\n (notificationClose)=\"\n notificationsService.removeNotificationById(notification.id)\n \"\n [@enterExit]\n ></gn-ui-notification>\n }\n</div>\n" }]
|
|
31941
|
-
}] });
|
|
31942
|
-
|
|
31943
32489
|
class ResultsTableContainerComponent {
|
|
31944
32490
|
constructor() {
|
|
31945
32491
|
this.searchFacade = inject(SearchFacade);
|
|
@@ -32024,12 +32570,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
32024
32570
|
|
|
32025
32571
|
marker('search.filters.summaryLabel.user');
|
|
32026
32572
|
marker('search.filters.summaryLabel.changeDate');
|
|
32573
|
+
marker('search.filters.summaryLabel.resourceCreationRevisionDate');
|
|
32574
|
+
const OPEN_BOUND = '…';
|
|
32027
32575
|
class SearchFiltersSummaryItemComponent {
|
|
32028
32576
|
constructor() {
|
|
32029
32577
|
this.searchFacade = inject(SearchFacade);
|
|
32030
32578
|
this.searchService = inject(SearchService);
|
|
32031
32579
|
this.fieldsService = inject(FieldsService);
|
|
32032
|
-
this.
|
|
32580
|
+
this.dateService = inject(DateService);
|
|
32033
32581
|
this.translate = inject(TranslateService);
|
|
32034
32582
|
this.fieldValues$ = this.searchFacade.searchFilters$.pipe(switchMap((filters) => this.fieldsService.readFieldValuesFromFilters(filters)), map$2((fieldValues) => Array.isArray(fieldValues[this.fieldName])
|
|
32035
32583
|
? fieldValues[this.fieldName]
|
|
@@ -32056,9 +32604,10 @@ class SearchFiltersSummaryItemComponent {
|
|
|
32056
32604
|
getReadableValues(fieldValues) {
|
|
32057
32605
|
return fieldValues.map((value) => {
|
|
32058
32606
|
if (this.fieldType === 'dateRange') {
|
|
32607
|
+
const { start, end } = value;
|
|
32059
32608
|
return {
|
|
32060
32609
|
value,
|
|
32061
|
-
label: `${this.
|
|
32610
|
+
label: `${this.formatBound(start)} - ${this.formatBound(end)}`,
|
|
32062
32611
|
};
|
|
32063
32612
|
}
|
|
32064
32613
|
else if (this.fieldName === 'user') {
|
|
@@ -32069,6 +32618,15 @@ class SearchFiltersSummaryItemComponent {
|
|
|
32069
32618
|
}
|
|
32070
32619
|
});
|
|
32071
32620
|
}
|
|
32621
|
+
formatBound(date) {
|
|
32622
|
+
return date
|
|
32623
|
+
? this.dateService.formatDate(date, {
|
|
32624
|
+
day: '2-digit',
|
|
32625
|
+
month: '2-digit',
|
|
32626
|
+
year: 'numeric',
|
|
32627
|
+
})
|
|
32628
|
+
: OPEN_BOUND;
|
|
32629
|
+
}
|
|
32072
32630
|
async removeFilterValue(fieldValue) {
|
|
32073
32631
|
const currentFieldValues = await firstValueFrom(this.fieldValues$);
|
|
32074
32632
|
const updatedFieldValues = currentFieldValues
|
|
@@ -32081,11 +32639,11 @@ class SearchFiltersSummaryItemComponent {
|
|
|
32081
32639
|
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
32082
32640
|
}
|
|
32083
32641
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SearchFiltersSummaryItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
32084
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: SearchFiltersSummaryItemComponent, isStandalone: true, selector: "gn-ui-search-filters-summary-item", inputs: { fieldName: "fieldName" },
|
|
32642
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: SearchFiltersSummaryItemComponent, isStandalone: true, selector: "gn-ui-search-filters-summary-item", inputs: { fieldName: "fieldName" }, ngImport: i0, template: "@if ((fieldValues$ | async)?.length > 0) {\n <div class=\"flex flex-row items-center gap-2\">\n <span class=\"text-gray-800\" data-cy=\"filterSummaryLabel\">{{\n translatedLabel\n }}</span>\n @for (fieldValue of fieldValues$ | async; track fieldValue) {\n <gn-ui-badge\n class=\"gn-ui-badge\"\n [style.--gn-ui-badge-rounded]=\"'8px'\"\n [style.--gn-ui-badge-padding]=\"'3px 4px'\"\n [style.--gn-ui-badge-text-color]=\"'black'\"\n [style.--gn-ui-badge-background-color]=\"'#F2F2F2'\"\n [style.--gn-ui-badge-font-weight]=\"'700'\"\n [removable]=\"true\"\n (badgeRemoveClicked)=\"removeFilterValue(fieldValue.value)\"\n >{{ fieldValue.label }}</gn-ui-badge\n >\n }\n </div>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: BadgeComponent, selector: "gn-ui-badge", inputs: ["clickable", "removable"], outputs: ["badgeRemoveClicked", "badgeClicked"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }] }); }
|
|
32085
32643
|
}
|
|
32086
32644
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SearchFiltersSummaryItemComponent, decorators: [{
|
|
32087
32645
|
type: Component,
|
|
32088
|
-
args: [{ selector: 'gn-ui-search-filters-summary-item', standalone: true, imports: [CommonModule, BadgeComponent],
|
|
32646
|
+
args: [{ selector: 'gn-ui-search-filters-summary-item', standalone: true, imports: [CommonModule, BadgeComponent], template: "@if ((fieldValues$ | async)?.length > 0) {\n <div class=\"flex flex-row items-center gap-2\">\n <span class=\"text-gray-800\" data-cy=\"filterSummaryLabel\">{{\n translatedLabel\n }}</span>\n @for (fieldValue of fieldValues$ | async; track fieldValue) {\n <gn-ui-badge\n class=\"gn-ui-badge\"\n [style.--gn-ui-badge-rounded]=\"'8px'\"\n [style.--gn-ui-badge-padding]=\"'3px 4px'\"\n [style.--gn-ui-badge-text-color]=\"'black'\"\n [style.--gn-ui-badge-background-color]=\"'#F2F2F2'\"\n [style.--gn-ui-badge-font-weight]=\"'700'\"\n [removable]=\"true\"\n (badgeRemoveClicked)=\"removeFilterValue(fieldValue.value)\"\n >{{ fieldValue.label }}</gn-ui-badge\n >\n }\n </div>\n}\n" }]
|
|
32089
32647
|
}], propDecorators: { fieldName: [{
|
|
32090
32648
|
type: Input
|
|
32091
32649
|
}] } });
|
|
@@ -32329,11 +32887,11 @@ class AddLayerFromFileComponent {
|
|
|
32329
32887
|
}, 5000);
|
|
32330
32888
|
}
|
|
32331
32889
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AddLayerFromFileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
32332
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: AddLayerFromFileComponent, isStandalone: true, selector: "gn-ui-add-layer-from-file", ngImport: i0, template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n", styles: [""], dependencies: [{ kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: DragAndDropFileInputComponent, selector: "gn-ui-drag-and-drop-file-input", inputs: ["placeholder", "accept"], outputs: ["fileChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
32890
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: AddLayerFromFileComponent, isStandalone: true, selector: "gn-ui-add-layer-from-file", ngImport: i0, template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n textClass=\"text-gray-900 pl-2 py-2\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n", styles: [""], dependencies: [{ kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: DragAndDropFileInputComponent, selector: "gn-ui-drag-and-drop-file-input", inputs: ["placeholder", "accept", "maxFileSizeMb", "icon", "dropzoneBackgroundColor", "textClass", "extraClass", "showFileName"], outputs: ["fileChange", "errorChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
32333
32891
|
}
|
|
32334
32892
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AddLayerFromFileComponent, decorators: [{
|
|
32335
32893
|
type: Component,
|
|
32336
|
-
args: [{ selector: 'gn-ui-add-layer-from-file', standalone: true, imports: [TranslateDirective, TranslatePipe, DragAndDropFileInputComponent], template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n" }]
|
|
32894
|
+
args: [{ selector: 'gn-ui-add-layer-from-file', standalone: true, imports: [TranslateDirective, TranslatePipe, DragAndDropFileInputComponent], template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n textClass=\"text-gray-900 pl-2 py-2\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n" }]
|
|
32337
32895
|
}] });
|
|
32338
32896
|
|
|
32339
32897
|
class LayersPanelComponent {
|
|
@@ -32394,95 +32952,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
32394
32952
|
], template: "<div\n class=\"bg-white rounded shadow shadow-lg relative h-full w-[400px] overflow-hidden\"\n>\n <div class=\"p-3 border-b border-gray-300 flex items-center\">\n <ng-icon class=\"mr-2\" name=\"matLayersOutline\"></ng-icon>\n <span translate>map.layers.list</span>\n </div>\n <div class=\"flex flex-col px-4 divide-y divide-y-gray-50\">\n @for (layer of layers$ | async; track layer; let index = $index) {\n <div class=\"flex flex-row py-3\">\n <ng-icon class=\"-ml-2 mr-2 shrink-0\" name=\"matChevronRight\"></ng-icon>\n <span class=\"mr-2 grow\">{{ layer.label }}</span>\n <a\n href\n class=\"underline text-sm text-red-700 hover:text-red-900\"\n (click)=\"deleteLayer(index); $event.preventDefault()\"\n >\n delete\n </a>\n </div>\n }\n </div>\n <gn-ui-expandable-panel-button [titleTemplate]=\"addLayerTitle\">\n <mat-tab-group animationDuration=\"200ms\" class=\"h-full\">\n <mat-tab [label]=\"'map.add.layer.catalog' | translate\">\n <div class=\"p-3\">\n <gn-ui-add-layer-from-catalog></gn-ui-add-layer-from-catalog>\n </div>\n </mat-tab>\n <mat-tab [label]=\"'map.add.layer.wms' | translate\" bodyClass=\"h-full\">\n <div class=\"p-3\">\n <gn-ui-add-layer-from-wms></gn-ui-add-layer-from-wms>\n </div>\n </mat-tab>\n <mat-tab [label]=\"'map.add.layer.wfs' | translate\" bodyClass=\"h-full\">\n <div class=\"p-3\">\n <gn-ui-add-layer-from-wfs></gn-ui-add-layer-from-wfs>\n </div>\n </mat-tab>\n <mat-tab [label]=\"'map.add.layer.ogc.api' | translate\" bodyClass=\"h-full\">\n <div class=\"p-3\">\n <gn-ui-add-layer-from-ogc-api\n [ogcUrl]=\"ogcUrl\"\n (layerAdded)=\"addLayer($event)\"\n ></gn-ui-add-layer-from-ogc-api>\n </div>\n </mat-tab>\n <mat-tab [label]=\"'map.add.layer.file' | translate\" bodyClass=\"h-full\">\n <div class=\"p-3\">\n <gn-ui-add-layer-from-file></gn-ui-add-layer-from-file>\n </div>\n </mat-tab>\n </mat-tab-group>\n </gn-ui-expandable-panel-button>\n</div>\n\n<ng-template #addLayerTitle>\n <ng-icon class=\"mr-4\" name=\"matAddCircleOutlineOutline\"> </ng-icon>\n <span translate>map.add.layer</span>\n</ng-template>\n" }]
|
|
32395
32953
|
}] });
|
|
32396
32954
|
|
|
32397
|
-
class GeocodingComponent {
|
|
32398
|
-
constructor() {
|
|
32399
|
-
this.mapFacade = inject(MapFacade);
|
|
32400
|
-
this.geocodingService = inject(GeocodingService);
|
|
32401
|
-
this.searchText = '';
|
|
32402
|
-
this.results = [];
|
|
32403
|
-
this.searchTextChanged = new Subject();
|
|
32404
|
-
this.destroy$ = new Subject();
|
|
32405
|
-
this.errorMessage = null;
|
|
32406
|
-
this.searchTextChanged
|
|
32407
|
-
.pipe(debounceTime(300), switchMap$1((searchText) => {
|
|
32408
|
-
return from(this.geocodingService.query(searchText)).pipe(catchError$1((error) => {
|
|
32409
|
-
this.errorMessage =
|
|
32410
|
-
'An error occurred while searching. Please try again.';
|
|
32411
|
-
console.error(error);
|
|
32412
|
-
return [];
|
|
32413
|
-
}));
|
|
32414
|
-
}), takeUntil(this.destroy$))
|
|
32415
|
-
.subscribe((results) => {
|
|
32416
|
-
this.results = results;
|
|
32417
|
-
});
|
|
32418
|
-
}
|
|
32419
|
-
ngOnDestroy() {
|
|
32420
|
-
this.destroy$.next();
|
|
32421
|
-
this.destroy$.complete();
|
|
32422
|
-
}
|
|
32423
|
-
onSearchChange(searchText) {
|
|
32424
|
-
if (!searchText) {
|
|
32425
|
-
this.clearSearch();
|
|
32426
|
-
return;
|
|
32427
|
-
}
|
|
32428
|
-
else {
|
|
32429
|
-
this.searchTextChanged.next(searchText);
|
|
32430
|
-
}
|
|
32431
|
-
}
|
|
32432
|
-
clearSearch() {
|
|
32433
|
-
this.searchText = '';
|
|
32434
|
-
this.results = [];
|
|
32435
|
-
this.errorMessage = null;
|
|
32436
|
-
}
|
|
32437
|
-
async zoomToLocation(result) {
|
|
32438
|
-
const geometry = result.geom;
|
|
32439
|
-
if (geometry.type === 'Point') {
|
|
32440
|
-
await this.zoomToPoint(geometry.coordinates);
|
|
32441
|
-
}
|
|
32442
|
-
else if (geometry.type === 'Polygon') {
|
|
32443
|
-
await this.zoomToPolygon(geometry.coordinates);
|
|
32444
|
-
}
|
|
32445
|
-
else {
|
|
32446
|
-
console.error(`Unsupported geometry type: ${geometry.type}`);
|
|
32447
|
-
}
|
|
32448
|
-
}
|
|
32449
|
-
async zoomToPoint(pointCoords) {
|
|
32450
|
-
const context = await firstValueFrom(this.mapFacade.context$);
|
|
32451
|
-
const view = {
|
|
32452
|
-
center: pointCoords,
|
|
32453
|
-
zoom: 10,
|
|
32454
|
-
};
|
|
32455
|
-
this.mapFacade.applyContext({
|
|
32456
|
-
...context,
|
|
32457
|
-
view,
|
|
32458
|
-
});
|
|
32459
|
-
}
|
|
32460
|
-
async zoomToPolygon(polygonCoords) {
|
|
32461
|
-
const context = await firstValueFrom(this.mapFacade.context$);
|
|
32462
|
-
const view = {
|
|
32463
|
-
geometry: {
|
|
32464
|
-
type: 'Polygon',
|
|
32465
|
-
coordinates: polygonCoords,
|
|
32466
|
-
},
|
|
32467
|
-
};
|
|
32468
|
-
this.mapFacade.applyContext({
|
|
32469
|
-
...context,
|
|
32470
|
-
view,
|
|
32471
|
-
});
|
|
32472
|
-
}
|
|
32473
|
-
onEnterPress() {
|
|
32474
|
-
if (this.results && this.results.length > 0) {
|
|
32475
|
-
this.zoomToLocation(this.results[0]);
|
|
32476
|
-
}
|
|
32477
|
-
}
|
|
32478
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
32479
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: GeocodingComponent, isStandalone: true, selector: "gn-ui-geocoding", ngImport: i0, template: "<gn-ui-search-input\n [(value)]=\"searchText\"\n (valueChange)=\"onSearchChange($event)\"\n (keyup.enter)=\"onEnterPress()\"\n [placeholder]=\"'map.geocoding.placeholder' | translate\"\n>\n</gn-ui-search-input>\n@if (results && results.length) {\n <ul\n class=\"bg-gray-50 border border-gray-200 w-full mt-2 shadow-sm rounded-lg\"\n >\n @for (result of results; track result) {\n <li\n (click)=\"zoomToLocation(result)\"\n class=\"flex items-center pl-8 pr-4 py-2 border-b border-gray-200 relative cursor-pointer hover:bg-blue-100 hover:text-gray-800 transition duration-300 ease-in-out\"\n >\n <svg\n class=\"stroke-current text-blue-500 absolute w-5 h-5 left-3 top-3\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z\"\n />\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M15 11a3 3 0 11-6 0 3 3 0 016 0z\"\n />\n </svg>\n <span class=\"font-sans font-semibold ml-4\">{{ result.label }}</span>\n </li>\n }\n </ul>\n}\n", styles: [""], dependencies: [{ kind: "component", type: SearchInputComponent, selector: "gn-ui-search-input", inputs: ["value", "placeholder"], outputs: ["valueChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
32480
|
-
}
|
|
32481
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GeocodingComponent, decorators: [{
|
|
32482
|
-
type: Component,
|
|
32483
|
-
args: [{ selector: 'gn-ui-geocoding', standalone: true, imports: [SearchInputComponent, TranslatePipe], template: "<gn-ui-search-input\n [(value)]=\"searchText\"\n (valueChange)=\"onSearchChange($event)\"\n (keyup.enter)=\"onEnterPress()\"\n [placeholder]=\"'map.geocoding.placeholder' | translate\"\n>\n</gn-ui-search-input>\n@if (results && results.length) {\n <ul\n class=\"bg-gray-50 border border-gray-200 w-full mt-2 shadow-sm rounded-lg\"\n >\n @for (result of results; track result) {\n <li\n (click)=\"zoomToLocation(result)\"\n class=\"flex items-center pl-8 pr-4 py-2 border-b border-gray-200 relative cursor-pointer hover:bg-blue-100 hover:text-gray-800 transition duration-300 ease-in-out\"\n >\n <svg\n class=\"stroke-current text-blue-500 absolute w-5 h-5 left-3 top-3\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z\"\n />\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M15 11a3 3 0 11-6 0 3 3 0 016 0z\"\n />\n </svg>\n <span class=\"font-sans font-semibold ml-4\">{{ result.label }}</span>\n </li>\n }\n </ul>\n}\n" }]
|
|
32484
|
-
}], ctorParameters: () => [] });
|
|
32485
|
-
|
|
32486
32955
|
/*
|
|
32487
32956
|
Metadata actions
|
|
32488
32957
|
*/
|
|
@@ -32498,8 +32967,7 @@ const closeMetadata = createAction('[Metadata view] close');
|
|
|
32498
32967
|
Related actions
|
|
32499
32968
|
*/
|
|
32500
32969
|
const setRelated = createAction('[Metadata view] Set related records', props());
|
|
32501
|
-
const
|
|
32502
|
-
const setSourceOf = createAction('[Metadata view] Set has sources', props());
|
|
32970
|
+
const setLinkedRecords = createAction('[Metadata view] Set associated records', props());
|
|
32503
32971
|
/*
|
|
32504
32972
|
ChartConfig actions
|
|
32505
32973
|
*/
|
|
@@ -32531,9 +32999,8 @@ var mdview_actions = /*#__PURE__*/Object.freeze({
|
|
|
32531
32999
|
loadUserFeedbacksSuccess: loadUserFeedbacksSuccess,
|
|
32532
33000
|
setChartConfig: setChartConfig,
|
|
32533
33001
|
setIncompleteMetadata: setIncompleteMetadata,
|
|
32534
|
-
|
|
32535
|
-
|
|
32536
|
-
setSources: setSources
|
|
33002
|
+
setLinkedRecords: setLinkedRecords,
|
|
33003
|
+
setRelated: setRelated
|
|
32537
33004
|
});
|
|
32538
33005
|
|
|
32539
33006
|
const METADATA_VIEW_FEATURE_STATE_KEY = 'metadataView';
|
|
@@ -32576,12 +33043,9 @@ on(loadFullMetadata, (state) => ({
|
|
|
32576
33043
|
on(setRelated, (state, { related }) => ({
|
|
32577
33044
|
...state,
|
|
32578
33045
|
related,
|
|
32579
|
-
})), on(
|
|
32580
|
-
...state,
|
|
32581
|
-
sources,
|
|
32582
|
-
})), on(setSourceOf, (state, { sourceOf }) => ({
|
|
33046
|
+
})), on(setLinkedRecords, (state, { linkedRecords }) => ({
|
|
32583
33047
|
...state,
|
|
32584
|
-
|
|
33048
|
+
linkedRecords,
|
|
32585
33049
|
})),
|
|
32586
33050
|
/*
|
|
32587
33051
|
ChartConfig reducers
|
|
@@ -32643,8 +33107,7 @@ const getMetadataError = createSelector(getMdViewState, (state) => state.error);
|
|
|
32643
33107
|
Related selectors
|
|
32644
33108
|
*/
|
|
32645
33109
|
const getRelated = createSelector(getMdViewState, (state) => state.related);
|
|
32646
|
-
const
|
|
32647
|
-
const getSourceOf = createSelector(getMdViewState, (state) => state.sourceOf);
|
|
33110
|
+
const getLinkedRecords = createSelector(getMdViewState, (state) => state.linkedRecords);
|
|
32648
33111
|
/*
|
|
32649
33112
|
Metadata selectors
|
|
32650
33113
|
*/
|
|
@@ -32745,6 +33208,453 @@ function parseHeaders(httpHeaders) {
|
|
|
32745
33208
|
return result;
|
|
32746
33209
|
}
|
|
32747
33210
|
|
|
33211
|
+
const GEOMETRY_COLUMN_ALIAS = '__geometry__';
|
|
33212
|
+
function fieldToSql(name) {
|
|
33213
|
+
return `"${name.replace(/"/g, '""')}"`; // escape double quotes in field names
|
|
33214
|
+
}
|
|
33215
|
+
function filterToSql(filter) {
|
|
33216
|
+
const operator = filter[0];
|
|
33217
|
+
const args = filter.slice(1);
|
|
33218
|
+
function valueToSql(value) {
|
|
33219
|
+
return typeof value === 'number' ? value : `'${value}'`;
|
|
33220
|
+
}
|
|
33221
|
+
switch (operator) {
|
|
33222
|
+
case '<':
|
|
33223
|
+
case '<=':
|
|
33224
|
+
case '>':
|
|
33225
|
+
case '>=':
|
|
33226
|
+
case '=':
|
|
33227
|
+
case '!=':
|
|
33228
|
+
case 'like':
|
|
33229
|
+
return `${fieldToSql(args[0])} ${operator.toUpperCase()} ${valueToSql(args[1])}`;
|
|
33230
|
+
case 'in': {
|
|
33231
|
+
const values = args.slice(1);
|
|
33232
|
+
return `${fieldToSql(args[0])} IN (${values.map(valueToSql).join(', ')})`;
|
|
33233
|
+
}
|
|
33234
|
+
case 'and':
|
|
33235
|
+
case 'or': {
|
|
33236
|
+
const children = args
|
|
33237
|
+
.map(filterToSql)
|
|
33238
|
+
.join(` ${operator.toUpperCase()} `);
|
|
33239
|
+
return `(${children})`;
|
|
33240
|
+
}
|
|
33241
|
+
case 'not':
|
|
33242
|
+
return `NOT (${filterToSql(args[0])})`;
|
|
33243
|
+
}
|
|
33244
|
+
throw new Error(`Could not generate SQL query, operator not recognized: ${operator}`);
|
|
33245
|
+
}
|
|
33246
|
+
function aggregationToSql(aggregation) {
|
|
33247
|
+
const operation = aggregation[0];
|
|
33248
|
+
const field = aggregation[1];
|
|
33249
|
+
switch (operation) {
|
|
33250
|
+
case 'average':
|
|
33251
|
+
return `CAST(AVG(${fieldToSql(field)}) AS DOUBLE) as ${fieldToSql(`average(${field})`)}`;
|
|
33252
|
+
case 'sum':
|
|
33253
|
+
case 'max':
|
|
33254
|
+
case 'min':
|
|
33255
|
+
return `CAST(${operation.toUpperCase()}(${fieldToSql(field)}) AS DOUBLE) as ${fieldToSql(`${operation}(${field})`)}`;
|
|
33256
|
+
case 'count':
|
|
33257
|
+
return 'CAST(COUNT(*) AS INTEGER) as "count()"'; // we don't need Bigint precision here
|
|
33258
|
+
}
|
|
33259
|
+
}
|
|
33260
|
+
/**
|
|
33261
|
+
* Leave arguments at null if not used
|
|
33262
|
+
* @param tableName
|
|
33263
|
+
* @param selected
|
|
33264
|
+
* @param filter
|
|
33265
|
+
* @param sort
|
|
33266
|
+
* @param startIndex
|
|
33267
|
+
* @param count
|
|
33268
|
+
* @param groupBy
|
|
33269
|
+
* @param aggregations
|
|
33270
|
+
*/
|
|
33271
|
+
function generateSqlQuery(tableName, selected = null, filter = null, sort = null, startIndex = null, count = null, groupBy = null, aggregations = null, geometryColumn = null) {
|
|
33272
|
+
let sqlSelect = 'SELECT *';
|
|
33273
|
+
const sqlFrom = ` FROM ${tableName}`;
|
|
33274
|
+
let sqlOrderBy = '';
|
|
33275
|
+
let sqlWhere = '';
|
|
33276
|
+
let sqlLimit = '';
|
|
33277
|
+
let sqlGroupBy = '';
|
|
33278
|
+
if (selected !== null) {
|
|
33279
|
+
sqlSelect = `SELECT ${selected.map(fieldToSql).join(', ')}`;
|
|
33280
|
+
}
|
|
33281
|
+
if (geometryColumn !== null) {
|
|
33282
|
+
sqlSelect += `, ST_AsGeoJSON(${fieldToSql(geometryColumn)}) as ${GEOMETRY_COLUMN_ALIAS}`;
|
|
33283
|
+
}
|
|
33284
|
+
if (filter !== null) {
|
|
33285
|
+
sqlWhere = ` WHERE ${filterToSql(filter)}`;
|
|
33286
|
+
}
|
|
33287
|
+
if (sort?.length) {
|
|
33288
|
+
sqlOrderBy = ` ORDER BY ${sort
|
|
33289
|
+
.map((sort) => `${fieldToSql(sort[1])} ${sort[0].toUpperCase()}`)
|
|
33290
|
+
.join(', ')}`;
|
|
33291
|
+
}
|
|
33292
|
+
if (startIndex !== null && count !== null) {
|
|
33293
|
+
sqlLimit = ` LIMIT ${count} OFFSET ${startIndex}`;
|
|
33294
|
+
}
|
|
33295
|
+
if (groupBy !== null && aggregations !== null) {
|
|
33296
|
+
sqlSelect = `SELECT ${aggregations.map(aggregationToSql).join(', ')}`;
|
|
33297
|
+
const groupedByDistinct = groupBy.filter((group) => group[0] === 'distinct');
|
|
33298
|
+
const sqlGroupByFields = groupedByDistinct
|
|
33299
|
+
.map((group) => fieldToSql(group[1]))
|
|
33300
|
+
.join(', ');
|
|
33301
|
+
const sqlGroupBySelect = groupedByDistinct
|
|
33302
|
+
.map((group) => `${fieldToSql(group[1])} as ${fieldToSql(`distinct(${group[1]})`)}`)
|
|
33303
|
+
.join(', ');
|
|
33304
|
+
if (sqlGroupByFields && sqlGroupBySelect) {
|
|
33305
|
+
sqlGroupBy = ` GROUP BY ${sqlGroupByFields}`;
|
|
33306
|
+
sqlSelect += `, ${sqlGroupBySelect}`;
|
|
33307
|
+
}
|
|
33308
|
+
}
|
|
33309
|
+
return sqlSelect + sqlFrom + sqlGroupBy + sqlWhere + sqlOrderBy + sqlLimit;
|
|
33310
|
+
}
|
|
33311
|
+
|
|
33312
|
+
function arrowTableToDataItems(table) {
|
|
33313
|
+
const fields = table.schema.fields;
|
|
33314
|
+
return table.toArray().map((row) => {
|
|
33315
|
+
const rowJson = row.toJSON();
|
|
33316
|
+
const feature = {
|
|
33317
|
+
type: 'Feature',
|
|
33318
|
+
geometry: null,
|
|
33319
|
+
properties: {},
|
|
33320
|
+
};
|
|
33321
|
+
const keys = Object.keys(rowJson);
|
|
33322
|
+
for (let i = 0; i < keys.length; i++) {
|
|
33323
|
+
const key = keys[i];
|
|
33324
|
+
const dataType = fields[i].type;
|
|
33325
|
+
let value = rowJson[key];
|
|
33326
|
+
// this might happen if we get an array inside a field
|
|
33327
|
+
if (value instanceof Vector) {
|
|
33328
|
+
value = Array.from(value);
|
|
33329
|
+
}
|
|
33330
|
+
// cast bigints to ints
|
|
33331
|
+
if (typeof value === 'bigint') {
|
|
33332
|
+
value = Number(value);
|
|
33333
|
+
}
|
|
33334
|
+
// rename columns with empty name
|
|
33335
|
+
if (!key) {
|
|
33336
|
+
feature.properties['unknown'] = value;
|
|
33337
|
+
continue;
|
|
33338
|
+
}
|
|
33339
|
+
// assign properties that look like an id to the geojson `id` field
|
|
33340
|
+
if (/^(object|feature)?_?id$/.test(key.toLowerCase()) &&
|
|
33341
|
+
(typeof value == 'string' || typeof value === 'number')) {
|
|
33342
|
+
feature.id = value;
|
|
33343
|
+
}
|
|
33344
|
+
// if a date is in timestamp (number) format, convert it to native
|
|
33345
|
+
if (typeof value === 'number' &&
|
|
33346
|
+
(DataType.isTimestamp(dataType) || DataType.isDate(dataType))) {
|
|
33347
|
+
value = new Date(value);
|
|
33348
|
+
}
|
|
33349
|
+
// if a binary field (most likely a geometry column): skip
|
|
33350
|
+
if (DataType.isBinary(dataType)) {
|
|
33351
|
+
continue;
|
|
33352
|
+
}
|
|
33353
|
+
// geometry column
|
|
33354
|
+
if (key === GEOMETRY_COLUMN_ALIAS && DataType.isUtf8(dataType)) {
|
|
33355
|
+
feature.geometry = JSON.parse(value);
|
|
33356
|
+
continue;
|
|
33357
|
+
}
|
|
33358
|
+
feature.properties[key] = value;
|
|
33359
|
+
}
|
|
33360
|
+
return feature;
|
|
33361
|
+
});
|
|
33362
|
+
}
|
|
33363
|
+
|
|
33364
|
+
// init code taken from https://github.com/duckdb/duckdb-wasm/blob/main/packages/duckdb-wasm/README.md
|
|
33365
|
+
const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();
|
|
33366
|
+
// from https://duckdb.org/docs/current/sql/data_types/overview
|
|
33367
|
+
const typesMapping = {
|
|
33368
|
+
INTEGER: 'number',
|
|
33369
|
+
SMALLINT: 'number',
|
|
33370
|
+
TINYINT: 'number',
|
|
33371
|
+
BIGINT: 'number',
|
|
33372
|
+
HUGEINT: 'number',
|
|
33373
|
+
UINTEGER: 'number',
|
|
33374
|
+
USMALLINT: 'number',
|
|
33375
|
+
UTINYINT: 'number',
|
|
33376
|
+
UBIGINT: 'number',
|
|
33377
|
+
UHUGEINT: 'number',
|
|
33378
|
+
DOUBLE: 'number',
|
|
33379
|
+
BIGNUM: 'number',
|
|
33380
|
+
DECIMAL: 'number',
|
|
33381
|
+
NUMERIC: 'number',
|
|
33382
|
+
FLOAT: 'number',
|
|
33383
|
+
REAL: 'number',
|
|
33384
|
+
'TIMESTAMP WITH TIME ZONE': 'date',
|
|
33385
|
+
TIMESTAMP: 'date',
|
|
33386
|
+
DATE: 'date',
|
|
33387
|
+
CHAR: 'string',
|
|
33388
|
+
VARCHAR: 'string',
|
|
33389
|
+
TEXT: 'string',
|
|
33390
|
+
UUID: 'string',
|
|
33391
|
+
BLOB: 'string',
|
|
33392
|
+
BIT: 'string',
|
|
33393
|
+
INTERVAL: 'string',
|
|
33394
|
+
LIST: 'string',
|
|
33395
|
+
BOOLEAN: 'boolean',
|
|
33396
|
+
};
|
|
33397
|
+
class Engine {
|
|
33398
|
+
async makeInit() {
|
|
33399
|
+
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);
|
|
33400
|
+
let worker_url = bundle.mainWorker;
|
|
33401
|
+
// this is necessary to let browsers execute WASM code coming from a cross-origin host
|
|
33402
|
+
if (worker_url.startsWith('https://')) {
|
|
33403
|
+
worker_url = URL.createObjectURL(new Blob([`importScripts("${worker_url}");`], {
|
|
33404
|
+
type: 'text/javascript',
|
|
33405
|
+
}));
|
|
33406
|
+
}
|
|
33407
|
+
const worker = new Worker(worker_url);
|
|
33408
|
+
const logger = new duckdb.ConsoleLogger(duckdb.LogLevel.WARNING);
|
|
33409
|
+
this.db = new duckdb.AsyncDuckDB(logger, worker);
|
|
33410
|
+
await this.db.instantiate(bundle.mainModule, bundle.pthreadWorker);
|
|
33411
|
+
// setup duckdb options
|
|
33412
|
+
const conn = await this.db.connect();
|
|
33413
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
33414
|
+
await conn.query(`INSTALL spatial; LOAD spatial;`);
|
|
33415
|
+
await conn.query(`SET TimeZone = '${timezone}';`);
|
|
33416
|
+
await conn.query(`SET threads = 1;`);
|
|
33417
|
+
// await conn.query(`SET memory_limit = '2GB';`) // used for experimenting when we encounter memory issues
|
|
33418
|
+
await conn.query(`SET preserve_insertion_order = false;`); // this reduces memory usage when loading a dataset
|
|
33419
|
+
conn.close();
|
|
33420
|
+
return this;
|
|
33421
|
+
}
|
|
33422
|
+
isReady() {
|
|
33423
|
+
if (!this.init_) {
|
|
33424
|
+
this.init_ = this.makeInit();
|
|
33425
|
+
}
|
|
33426
|
+
return this.init_;
|
|
33427
|
+
}
|
|
33428
|
+
/**
|
|
33429
|
+
* Returns information about a dataset once it's loaded:
|
|
33430
|
+
* - a list of properties description
|
|
33431
|
+
* - the name of the dataset geometry column (null if no geometry present)
|
|
33432
|
+
* @param datasetId name of the table under which the dataset will be stored
|
|
33433
|
+
* @param loadQuery duckdb-specific query for creating a table out of the data
|
|
33434
|
+
* @param forceReload if true, any existing data will be dropped and redownloaded
|
|
33435
|
+
*/
|
|
33436
|
+
async loadFile(datasetId, loadQuery, forceReload = false) {
|
|
33437
|
+
const conn = await this.db.connect();
|
|
33438
|
+
let results;
|
|
33439
|
+
// either we want to recreate the table, or we keep it if it already exists
|
|
33440
|
+
const safeLoadQuery = forceReload
|
|
33441
|
+
? `DROP TABLE IF EXISTS ${datasetId};
|
|
33442
|
+
${loadQuery}`
|
|
33443
|
+
: loadQuery.replace(/CREATE TABLE(?! IF NOT EXISTS)/gi, 'CREATE TABLE IF NOT EXISTS');
|
|
33444
|
+
// create the table
|
|
33445
|
+
try {
|
|
33446
|
+
results = await conn.query(safeLoadQuery);
|
|
33447
|
+
}
|
|
33448
|
+
catch (e) {
|
|
33449
|
+
throw new FetchError('parse', `DuckDB encountered an error when loading the data: ${e.message}`);
|
|
33450
|
+
}
|
|
33451
|
+
// read rows count
|
|
33452
|
+
results = await conn.query(`SELECT count(*) FROM ${datasetId}`);
|
|
33453
|
+
const { 'count_star()': recordsCount } = results.toArray()[0].toJSON();
|
|
33454
|
+
// read columns
|
|
33455
|
+
results = await conn.query(`SELECT * FROM information_schema.columns WHERE table_name = '${datasetId}';`);
|
|
33456
|
+
let geometryColumn = null;
|
|
33457
|
+
const properties = results
|
|
33458
|
+
.toArray()
|
|
33459
|
+
.map((row) => {
|
|
33460
|
+
const rowObj = row.toJSON();
|
|
33461
|
+
if (rowObj['data_type'] === 'GEOMETRY') {
|
|
33462
|
+
// the geometry is not part of the properties
|
|
33463
|
+
// note: right now we only keep one geometry column name, but if there are multiple they will
|
|
33464
|
+
// all get discarded
|
|
33465
|
+
geometryColumn = rowObj['column_name'];
|
|
33466
|
+
return null;
|
|
33467
|
+
}
|
|
33468
|
+
return {
|
|
33469
|
+
name: rowObj['column_name'],
|
|
33470
|
+
label: rowObj['column_name'],
|
|
33471
|
+
type: typesMapping[rowObj['data_type']] ?? 'other',
|
|
33472
|
+
};
|
|
33473
|
+
})
|
|
33474
|
+
.filter((prop) => prop !== null);
|
|
33475
|
+
conn.close();
|
|
33476
|
+
return {
|
|
33477
|
+
properties,
|
|
33478
|
+
geometryColumn,
|
|
33479
|
+
rowsCount: Number(recordsCount),
|
|
33480
|
+
};
|
|
33481
|
+
}
|
|
33482
|
+
// register a Uint8 buffer using a handle in the duckdb instance
|
|
33483
|
+
async registerData(name, buffer) {
|
|
33484
|
+
return this.db.registerFileBuffer(name, buffer);
|
|
33485
|
+
}
|
|
33486
|
+
/**
|
|
33487
|
+
* @param query duckdb-specific query for fetching items
|
|
33488
|
+
*/
|
|
33489
|
+
async queryItems(query) {
|
|
33490
|
+
const conn = await this.db.connect();
|
|
33491
|
+
const results = await conn.query(query);
|
|
33492
|
+
conn.close();
|
|
33493
|
+
return arrowTableToDataItems(results);
|
|
33494
|
+
}
|
|
33495
|
+
close() {
|
|
33496
|
+
this.db?.terminate();
|
|
33497
|
+
}
|
|
33498
|
+
}
|
|
33499
|
+
let engine = null;
|
|
33500
|
+
async function getEngine() {
|
|
33501
|
+
if (!engine) {
|
|
33502
|
+
engine = new Engine();
|
|
33503
|
+
}
|
|
33504
|
+
return engine.isReady();
|
|
33505
|
+
}
|
|
33506
|
+
|
|
33507
|
+
class BaseReader {
|
|
33508
|
+
constructor(url) {
|
|
33509
|
+
this.url = url;
|
|
33510
|
+
this.selected = null;
|
|
33511
|
+
this.groupedBy = null;
|
|
33512
|
+
this.aggregations = null;
|
|
33513
|
+
this.filter = null;
|
|
33514
|
+
this.sort = null;
|
|
33515
|
+
this.startIndex = null;
|
|
33516
|
+
this.count = null;
|
|
33517
|
+
this.loadPromise_ = Promise.resolve();
|
|
33518
|
+
this.cacheEnabled = false;
|
|
33519
|
+
}
|
|
33520
|
+
enableCache(enabled) {
|
|
33521
|
+
this.cacheEnabled = enabled;
|
|
33522
|
+
}
|
|
33523
|
+
load() {
|
|
33524
|
+
throw new Error('not implemented');
|
|
33525
|
+
}
|
|
33526
|
+
get isLoaded() {
|
|
33527
|
+
return this.loadPromise_;
|
|
33528
|
+
}
|
|
33529
|
+
get properties() {
|
|
33530
|
+
throw new Error('not implemented');
|
|
33531
|
+
}
|
|
33532
|
+
get info() {
|
|
33533
|
+
throw new Error('not implemented');
|
|
33534
|
+
}
|
|
33535
|
+
read() {
|
|
33536
|
+
throw new Error('not implemented');
|
|
33537
|
+
}
|
|
33538
|
+
selectAll() {
|
|
33539
|
+
this.groupedBy = null;
|
|
33540
|
+
this.aggregations = null;
|
|
33541
|
+
this.selected = null;
|
|
33542
|
+
this.filter = null;
|
|
33543
|
+
this.startIndex = null;
|
|
33544
|
+
this.count = null;
|
|
33545
|
+
return this;
|
|
33546
|
+
}
|
|
33547
|
+
select(...selectedFields) {
|
|
33548
|
+
this.selected = selectedFields;
|
|
33549
|
+
this.aggregations = null; // clear aggregations & groups when selecting fields
|
|
33550
|
+
this.groupedBy = null;
|
|
33551
|
+
return this;
|
|
33552
|
+
}
|
|
33553
|
+
groupBy(...groupBy) {
|
|
33554
|
+
this.groupedBy = groupBy;
|
|
33555
|
+
this.selected = null; // clear normal field selection when aggregating
|
|
33556
|
+
return this;
|
|
33557
|
+
}
|
|
33558
|
+
aggregate(...aggregations) {
|
|
33559
|
+
this.aggregations = aggregations;
|
|
33560
|
+
return this;
|
|
33561
|
+
}
|
|
33562
|
+
where(filter) {
|
|
33563
|
+
this.filter = filter;
|
|
33564
|
+
return this;
|
|
33565
|
+
}
|
|
33566
|
+
orderBy(...fieldSorts) {
|
|
33567
|
+
this.sort = fieldSorts;
|
|
33568
|
+
return this;
|
|
33569
|
+
}
|
|
33570
|
+
limit(startIndex, count) {
|
|
33571
|
+
this.startIndex = startIndex;
|
|
33572
|
+
this.count = count;
|
|
33573
|
+
return this;
|
|
33574
|
+
}
|
|
33575
|
+
}
|
|
33576
|
+
|
|
33577
|
+
/**
|
|
33578
|
+
* This reader handles file formats supported natively by DuckDB
|
|
33579
|
+
*/
|
|
33580
|
+
class BaseFileReader extends BaseReader {
|
|
33581
|
+
// a table id should not exceed 63 chars
|
|
33582
|
+
generateDatasetId() {
|
|
33583
|
+
// generate a hash out of the url
|
|
33584
|
+
let hash = 0;
|
|
33585
|
+
for (const char of this.url) {
|
|
33586
|
+
hash = (hash << 5) - hash + char.charCodeAt(0);
|
|
33587
|
+
hash = hash >>> 0; // make it unsigned
|
|
33588
|
+
}
|
|
33589
|
+
return `datafetcher_${hash.toString(16)}`;
|
|
33590
|
+
}
|
|
33591
|
+
async getLoadQuery() {
|
|
33592
|
+
throw new Error('not implemented');
|
|
33593
|
+
}
|
|
33594
|
+
async load() {
|
|
33595
|
+
this.datasetId = this.generateDatasetId();
|
|
33596
|
+
this.loadPromise_ = getEngine()
|
|
33597
|
+
.then((engine) => {
|
|
33598
|
+
this.engine = engine;
|
|
33599
|
+
return this.getLoadQuery();
|
|
33600
|
+
})
|
|
33601
|
+
.then((loadQuery) => this.engine.loadFile(this.datasetId, loadQuery, !this.cacheEnabled))
|
|
33602
|
+
.then((datasetInfo) => {
|
|
33603
|
+
this.properties_ = datasetInfo.properties;
|
|
33604
|
+
this.geometryColumn = datasetInfo.geometryColumn;
|
|
33605
|
+
this.rowsCount = datasetInfo.rowsCount;
|
|
33606
|
+
// returns void for the loaded promise
|
|
33607
|
+
});
|
|
33608
|
+
}
|
|
33609
|
+
get properties() {
|
|
33610
|
+
return this.isLoaded.then(() => this.properties_);
|
|
33611
|
+
}
|
|
33612
|
+
get info() {
|
|
33613
|
+
return this.isLoaded.then(() => ({
|
|
33614
|
+
itemsCount: this.rowsCount,
|
|
33615
|
+
hasGeometry: !!this.geometryColumn,
|
|
33616
|
+
}));
|
|
33617
|
+
}
|
|
33618
|
+
async read() {
|
|
33619
|
+
await this.isLoaded;
|
|
33620
|
+
// if only certain fields are selected, omit the geometry
|
|
33621
|
+
const geometryColumn = this.selected === null ? this.geometryColumn : null;
|
|
33622
|
+
const query = generateSqlQuery(this.datasetId, this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations, geometryColumn);
|
|
33623
|
+
return this.engine.queryItems(query);
|
|
33624
|
+
}
|
|
33625
|
+
}
|
|
33626
|
+
|
|
33627
|
+
class CsvReader extends BaseFileReader {
|
|
33628
|
+
async getLoadQuery() {
|
|
33629
|
+
// first we get a list of columns hich have a detected type of VARCHAR
|
|
33630
|
+
// then we make sure that for those columns, we don't cast an empty string to null; instead we want to keep the empty string
|
|
33631
|
+
return `
|
|
33632
|
+
SET VARIABLE strColumns = (SELECT list(name) FROM (SELECT unnest(Columns, recursive := true) FROM sniff_csv("${this.url}")) WHERE type = 'VARCHAR');
|
|
33633
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM read_csv("${this.url}",
|
|
33634
|
+
force_not_null = getvariable('strColumns'),
|
|
33635
|
+
auto_type_candidates = ['NULL', 'BOOLEAN', 'INTEGER', 'DOUBLE', 'DATE', 'VARCHAR']
|
|
33636
|
+
);`;
|
|
33637
|
+
}
|
|
33638
|
+
}
|
|
33639
|
+
|
|
33640
|
+
class JsonReader extends BaseFileReader {
|
|
33641
|
+
async getLoadQuery() {
|
|
33642
|
+
return `
|
|
33643
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM read_json("${this.url}",
|
|
33644
|
+
maximum_object_size = 536870912 -- 500MB
|
|
33645
|
+
);`;
|
|
33646
|
+
}
|
|
33647
|
+
}
|
|
33648
|
+
|
|
33649
|
+
class GeojsonReader extends BaseFileReader {
|
|
33650
|
+
async getLoadQuery() {
|
|
33651
|
+
return `
|
|
33652
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM st_read("${this.url}",
|
|
33653
|
+
allowed_drivers = ['GeoJSON']
|
|
33654
|
+
);`;
|
|
33655
|
+
}
|
|
33656
|
+
}
|
|
33657
|
+
|
|
32748
33658
|
async function inferDatasetType(url, typeHint) {
|
|
32749
33659
|
const fileExtensionMatches = new URL(url, typeof window !== 'undefined' ? window.location.toString() : undefined).pathname.match(/\.(.+)$/);
|
|
32750
33660
|
const fileExtension = fileExtensionMatches && fileExtensionMatches.length
|
|
@@ -32809,127 +33719,6 @@ function fetchDataAsArrayBuffer(url, cacheActive) {
|
|
|
32809
33719
|
return new Uint8Array(array).buffer;
|
|
32810
33720
|
});
|
|
32811
33721
|
}
|
|
32812
|
-
function tryParseDate(input) {
|
|
32813
|
-
if (typeof input !== 'string')
|
|
32814
|
-
return null;
|
|
32815
|
-
function tryIso(value) {
|
|
32816
|
-
const parsed = parseISO(value);
|
|
32817
|
-
return isNaN(parsed.getDate()) ? null : parsed;
|
|
32818
|
-
}
|
|
32819
|
-
function tryFormat(value, format) {
|
|
32820
|
-
const parsed = parse$4(value, format, new Date());
|
|
32821
|
-
return isNaN(parsed.getDate()) ? null : parsed;
|
|
32822
|
-
}
|
|
32823
|
-
return (tryIso(input) ||
|
|
32824
|
-
tryFormat(input, 'dd/MM/yyyy') ||
|
|
32825
|
-
tryFormat(input, 'dd.MM.yyyy') ||
|
|
32826
|
-
tryFormat(input, 'MM/dd/yyyy') ||
|
|
32827
|
-
null);
|
|
32828
|
-
}
|
|
32829
|
-
function tryParseNumber(input) {
|
|
32830
|
-
if (isNaN(input))
|
|
32831
|
-
return null;
|
|
32832
|
-
const parsed = parseFloat(input);
|
|
32833
|
-
return isNaN(parsed) ? null : parsed;
|
|
32834
|
-
}
|
|
32835
|
-
function jsonToGeojsonFeature(object) {
|
|
32836
|
-
const { id, properties } = Object.keys(object)
|
|
32837
|
-
.map((property) => (property ? property : 'unknown')) //prevent empty strings
|
|
32838
|
-
.reduce((prev, curr) => curr.toLowerCase().endsWith('id')
|
|
32839
|
-
? {
|
|
32840
|
-
...prev,
|
|
32841
|
-
id: object[curr],
|
|
32842
|
-
}
|
|
32843
|
-
: {
|
|
32844
|
-
...prev,
|
|
32845
|
-
properties: { ...prev.properties, [curr]: object[curr] },
|
|
32846
|
-
}, { id: undefined, properties: {} });
|
|
32847
|
-
return {
|
|
32848
|
-
type: 'Feature',
|
|
32849
|
-
geometry: null,
|
|
32850
|
-
properties,
|
|
32851
|
-
...(id !== undefined && { id }),
|
|
32852
|
-
};
|
|
32853
|
-
}
|
|
32854
|
-
function mutateProperties(items, mutators) {
|
|
32855
|
-
const mutatorKeys = Object.keys(mutators);
|
|
32856
|
-
for (let i = 0, ii = items.length; i < ii; i++) {
|
|
32857
|
-
const item = items[i];
|
|
32858
|
-
for (const mutatorField of mutatorKeys) {
|
|
32859
|
-
if (!(mutatorField in item.properties))
|
|
32860
|
-
continue;
|
|
32861
|
-
item.properties[mutatorField] = mutators[mutatorField](item.properties[mutatorField]);
|
|
32862
|
-
}
|
|
32863
|
-
}
|
|
32864
|
-
return items;
|
|
32865
|
-
}
|
|
32866
|
-
const SAMPLE_SIZE = 20;
|
|
32867
|
-
/**
|
|
32868
|
-
* This will infer field types from a list of data items and cast the values accordingly
|
|
32869
|
-
* @param items
|
|
32870
|
-
* @param inferTypes
|
|
32871
|
-
*/
|
|
32872
|
-
function processItemProperties(items, inferTypes = false) {
|
|
32873
|
-
const foundFields = {};
|
|
32874
|
-
for (let i = 0, ii = Math.min(SAMPLE_SIZE, items.length); i < ii; i++) {
|
|
32875
|
-
const item = items[i];
|
|
32876
|
-
const fields = Object.keys(item.properties);
|
|
32877
|
-
for (const field of fields) {
|
|
32878
|
-
if (!(field in foundFields)) {
|
|
32879
|
-
foundFields[field] = {
|
|
32880
|
-
label: field,
|
|
32881
|
-
name: field,
|
|
32882
|
-
type: null,
|
|
32883
|
-
};
|
|
32884
|
-
}
|
|
32885
|
-
const value = item.properties[field];
|
|
32886
|
-
const info = foundFields[field];
|
|
32887
|
-
if (value === undefined || value === '' || value === null)
|
|
32888
|
-
continue;
|
|
32889
|
-
if (!inferTypes) {
|
|
32890
|
-
if (info.type === null && typeof value === 'number') {
|
|
32891
|
-
info.type = 'number';
|
|
32892
|
-
}
|
|
32893
|
-
else if (info.type === 'number' && typeof value !== 'number') {
|
|
32894
|
-
info.type = 'string';
|
|
32895
|
-
}
|
|
32896
|
-
continue;
|
|
32897
|
-
}
|
|
32898
|
-
const parsedNumber = tryParseNumber(value);
|
|
32899
|
-
if (info.type === null && parsedNumber !== null) {
|
|
32900
|
-
info.type = 'number';
|
|
32901
|
-
continue;
|
|
32902
|
-
}
|
|
32903
|
-
else if (info.type === 'number' && parsedNumber === null) {
|
|
32904
|
-
info.type = 'string';
|
|
32905
|
-
continue;
|
|
32906
|
-
}
|
|
32907
|
-
const parsedDate = tryParseDate(value);
|
|
32908
|
-
if (info.type === null && parsedDate !== null) {
|
|
32909
|
-
info.type = 'date';
|
|
32910
|
-
}
|
|
32911
|
-
else if (info.type === 'date' && parsedDate === null) {
|
|
32912
|
-
info.type = 'string';
|
|
32913
|
-
}
|
|
32914
|
-
}
|
|
32915
|
-
}
|
|
32916
|
-
const properties = [];
|
|
32917
|
-
const mutators = {};
|
|
32918
|
-
for (const field in foundFields) {
|
|
32919
|
-
const info = foundFields[field];
|
|
32920
|
-
if (info.type === 'number') {
|
|
32921
|
-
mutators[field] = tryParseNumber;
|
|
32922
|
-
}
|
|
32923
|
-
else if (info.type === 'date') {
|
|
32924
|
-
mutators[field] = tryParseDate;
|
|
32925
|
-
}
|
|
32926
|
-
properties.push({ ...info, type: info.type || 'string' });
|
|
32927
|
-
}
|
|
32928
|
-
if (inferTypes) {
|
|
32929
|
-
mutateProperties(items, mutators);
|
|
32930
|
-
}
|
|
32931
|
-
return { items, properties };
|
|
32932
|
-
}
|
|
32933
33722
|
/**
|
|
32934
33723
|
* This creates a Proxy that allows reading and writing to the data item properties
|
|
32935
33724
|
* as if it was a simple array of JSON objects
|
|
@@ -32951,333 +33740,71 @@ function getJsonDataItemsProxy(items) {
|
|
|
32951
33740
|
});
|
|
32952
33741
|
}
|
|
32953
33742
|
|
|
32954
|
-
|
|
32955
|
-
|
|
32956
|
-
|
|
32957
|
-
|
|
32958
|
-
|
|
32959
|
-
|
|
32960
|
-
|
|
32961
|
-
|
|
32962
|
-
|
|
32963
|
-
|
|
32964
|
-
|
|
32965
|
-
|
|
32966
|
-
|
|
32967
|
-
|
|
32968
|
-
|
|
32969
|
-
|
|
32970
|
-
|
|
32971
|
-
return `[${args[0]}] IN (${values.map(valueToSql).join(', ')})`;
|
|
32972
|
-
}
|
|
32973
|
-
case 'and':
|
|
32974
|
-
case 'or': {
|
|
32975
|
-
const children = args
|
|
32976
|
-
.map(filterToSql)
|
|
32977
|
-
.join(` ${operator.toUpperCase()} `);
|
|
32978
|
-
return `(${children})`;
|
|
32979
|
-
}
|
|
32980
|
-
case 'not':
|
|
32981
|
-
return `NOT (${filterToSql(args[0])})`;
|
|
32982
|
-
}
|
|
32983
|
-
throw new Error(`Could not generate SQL query, operator not recognized: ${operator}`);
|
|
32984
|
-
}
|
|
32985
|
-
function aggregationToSql(aggregation) {
|
|
32986
|
-
const operation = aggregation[0];
|
|
32987
|
-
const field = aggregation[1];
|
|
32988
|
-
switch (operation) {
|
|
32989
|
-
case 'average':
|
|
32990
|
-
return `AVG([${field}]) as [average(${field})]`;
|
|
32991
|
-
case 'sum':
|
|
32992
|
-
case 'max':
|
|
32993
|
-
case 'min':
|
|
32994
|
-
return `${operation.toUpperCase()}([${field}]) as [${operation}(${field})]`;
|
|
32995
|
-
case 'count':
|
|
32996
|
-
return 'COUNT(*) as [count()]';
|
|
32997
|
-
}
|
|
32998
|
-
}
|
|
32999
|
-
/**
|
|
33000
|
-
* Leave arguments at null if not used
|
|
33001
|
-
* @param selected
|
|
33002
|
-
* @param filter
|
|
33003
|
-
* @param sort
|
|
33004
|
-
* @param startIndex
|
|
33005
|
-
* @param count
|
|
33006
|
-
* @param groupBy
|
|
33007
|
-
* @param aggregations
|
|
33008
|
-
*/
|
|
33009
|
-
function generateSqlQuery(selected = null, filter = null, sort = null, startIndex = null, count = null, groupBy = null, aggregations = null) {
|
|
33010
|
-
let sqlSelect = 'SELECT *';
|
|
33011
|
-
const sqlFrom = ' FROM ?';
|
|
33012
|
-
let sqlOrderBy = '';
|
|
33013
|
-
let sqlWhere = '';
|
|
33014
|
-
let sqlLimit = '';
|
|
33015
|
-
let sqlGroupBy = '';
|
|
33016
|
-
if (selected !== null) {
|
|
33017
|
-
sqlSelect = `SELECT ${selected.map((name) => `[${name}]`).join(', ')}`;
|
|
33018
|
-
}
|
|
33019
|
-
if (filter !== null) {
|
|
33020
|
-
sqlWhere = ` WHERE ${filterToSql(filter)}`;
|
|
33021
|
-
}
|
|
33022
|
-
if (sort?.length) {
|
|
33023
|
-
sqlOrderBy = ` ORDER BY ${sort
|
|
33024
|
-
.map((sort) => `[${sort[1]}] ${sort[0].toUpperCase()}`)
|
|
33025
|
-
.join(', ')}`;
|
|
33026
|
-
}
|
|
33027
|
-
if (startIndex !== null && count !== null) {
|
|
33028
|
-
sqlLimit = ` LIMIT ${count} OFFSET ${startIndex}`;
|
|
33029
|
-
}
|
|
33030
|
-
if (groupBy !== null && aggregations !== null) {
|
|
33031
|
-
sqlSelect = `SELECT ${aggregations.map(aggregationToSql).join(', ')}`;
|
|
33032
|
-
const groupedByDistinct = groupBy.filter((group) => group[0] === 'distinct');
|
|
33033
|
-
const sqlGroupByFields = groupedByDistinct
|
|
33034
|
-
.map((group) => `[${group[1]}]`)
|
|
33035
|
-
.join(', ');
|
|
33036
|
-
const sqlGroupBySelect = groupedByDistinct
|
|
33037
|
-
.map((group) => `[${group[1]}] as [distinct(${group[1]})]`)
|
|
33038
|
-
.join(', ');
|
|
33039
|
-
if (sqlGroupByFields && sqlGroupBySelect) {
|
|
33040
|
-
sqlGroupBy = ` GROUP BY ${sqlGroupByFields}`;
|
|
33041
|
-
sqlSelect += `, ${sqlGroupBySelect}`;
|
|
33743
|
+
class ExcelReader extends BaseFileReader {
|
|
33744
|
+
async getLoadQuery() {
|
|
33745
|
+
// we download the file as an array buffer first, in order to be able to check if it's an XLS file
|
|
33746
|
+
let buffer = await fetchDataAsArrayBuffer(this.url, this.cacheEnabled);
|
|
33747
|
+
const bufferHandle = `B${this.datasetId}`;
|
|
33748
|
+
// checking against the magic number at the beginning of XLS files, see https://en.wikipedia.org/wiki/List_of_file_signatures
|
|
33749
|
+
const magicNumber = new Uint8Array(buffer, 0, 8); // first 8 bytes
|
|
33750
|
+
const isXls = Array.from(magicNumber)
|
|
33751
|
+
.map((n) => n.toString(16).toUpperCase())
|
|
33752
|
+
.join(' ') === 'D0 CF 11 E0 A1 B1 1A E1';
|
|
33753
|
+
// uh oh, this is an XLS file (not supported by duckdb); convert it to CSV using the xlsx package
|
|
33754
|
+
if (isXls) {
|
|
33755
|
+
buffer = await import('xlsx').then(({ read, utils }) => {
|
|
33756
|
+
const workbook = read(buffer);
|
|
33757
|
+
const json = utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]]);
|
|
33758
|
+
return new TextEncoder().encode(JSON.stringify(json)).buffer;
|
|
33759
|
+
});
|
|
33042
33760
|
}
|
|
33043
|
-
|
|
33044
|
-
|
|
33045
|
-
|
|
33046
|
-
|
|
33047
|
-
class BaseReader {
|
|
33048
|
-
constructor(url) {
|
|
33049
|
-
this.url = url;
|
|
33050
|
-
this.selected = null;
|
|
33051
|
-
this.groupedBy = null;
|
|
33052
|
-
this.aggregations = null;
|
|
33053
|
-
this.filter = null;
|
|
33054
|
-
this.sort = null;
|
|
33055
|
-
this.startIndex = null;
|
|
33056
|
-
this.count = null;
|
|
33057
|
-
}
|
|
33058
|
-
load() {
|
|
33059
|
-
throw new Error('not implemented');
|
|
33060
|
-
}
|
|
33061
|
-
get properties() {
|
|
33062
|
-
throw new Error('not implemented');
|
|
33063
|
-
}
|
|
33064
|
-
get info() {
|
|
33065
|
-
throw new Error('not implemented');
|
|
33066
|
-
}
|
|
33067
|
-
read() {
|
|
33068
|
-
throw new Error('not implemented');
|
|
33069
|
-
}
|
|
33070
|
-
selectAll() {
|
|
33071
|
-
this.groupedBy = null;
|
|
33072
|
-
this.aggregations = null;
|
|
33073
|
-
this.selected = null;
|
|
33074
|
-
this.filter = null;
|
|
33075
|
-
this.startIndex = null;
|
|
33076
|
-
this.count = null;
|
|
33077
|
-
return this;
|
|
33078
|
-
}
|
|
33079
|
-
select(...selectedFields) {
|
|
33080
|
-
this.selected = selectedFields;
|
|
33081
|
-
this.aggregations = null; // clear aggregations & groups when selecting fields
|
|
33082
|
-
this.groupedBy = null;
|
|
33083
|
-
return this;
|
|
33084
|
-
}
|
|
33085
|
-
groupBy(...groupBy) {
|
|
33086
|
-
this.groupedBy = groupBy;
|
|
33087
|
-
this.selected = null; // clear normal field selection when aggregating
|
|
33088
|
-
return this;
|
|
33089
|
-
}
|
|
33090
|
-
aggregate(...aggregations) {
|
|
33091
|
-
this.aggregations = aggregations;
|
|
33092
|
-
return this;
|
|
33093
|
-
}
|
|
33094
|
-
where(filter) {
|
|
33095
|
-
this.filter = filter;
|
|
33096
|
-
return this;
|
|
33097
|
-
}
|
|
33098
|
-
orderBy(...fieldSorts) {
|
|
33099
|
-
this.sort = fieldSorts;
|
|
33100
|
-
return this;
|
|
33101
|
-
}
|
|
33102
|
-
limit(startIndex, count) {
|
|
33103
|
-
this.startIndex = startIndex;
|
|
33104
|
-
this.count = count;
|
|
33105
|
-
return this;
|
|
33106
|
-
}
|
|
33107
|
-
}
|
|
33108
|
-
|
|
33109
|
-
class BaseCacheReader extends BaseReader {
|
|
33110
|
-
constructor(url, cacheActive = true) {
|
|
33111
|
-
super(url);
|
|
33112
|
-
this.url = url;
|
|
33113
|
-
this.cacheActive = cacheActive;
|
|
33114
|
-
}
|
|
33115
|
-
setCacheActive(value) {
|
|
33116
|
-
this.cacheActive = value;
|
|
33117
|
-
}
|
|
33118
|
-
}
|
|
33119
|
-
|
|
33120
|
-
class BaseFileReader extends BaseCacheReader {
|
|
33121
|
-
getData() {
|
|
33122
|
-
throw new Error('not implemented');
|
|
33123
|
-
}
|
|
33124
|
-
load() {
|
|
33125
|
-
this.parseResult_ = this.getData();
|
|
33126
|
-
}
|
|
33127
|
-
get properties() {
|
|
33128
|
-
return this.parseResult_.then((result) => result.properties);
|
|
33129
|
-
}
|
|
33130
|
-
get info() {
|
|
33131
|
-
return this.parseResult_.then((result) => ({
|
|
33132
|
-
itemsCount: result.items.length,
|
|
33133
|
-
}));
|
|
33134
|
-
}
|
|
33135
|
-
async read() {
|
|
33136
|
-
const items = (await this.parseResult_).items;
|
|
33137
|
-
// no query defined: return the full results as is
|
|
33138
|
-
if (this.groupedBy == null &&
|
|
33139
|
-
this.aggregations == null &&
|
|
33140
|
-
this.selected == null &&
|
|
33141
|
-
this.sort == null &&
|
|
33142
|
-
this.filter == null &&
|
|
33143
|
-
this.startIndex == null &&
|
|
33144
|
-
this.count == null) {
|
|
33145
|
-
return items;
|
|
33146
|
-
}
|
|
33147
|
-
const jsonItems = getJsonDataItemsProxy(items);
|
|
33148
|
-
const query = generateSqlQuery(this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations);
|
|
33149
|
-
const result = await import('alasql').then((module) => module.default(query, [jsonItems]));
|
|
33150
|
-
return result.map(jsonToGeojsonFeature);
|
|
33151
|
-
}
|
|
33152
|
-
}
|
|
33153
|
-
|
|
33154
|
-
function parseCsv(text) {
|
|
33155
|
-
// first parse the header to guess the delimiter
|
|
33156
|
-
// note that we do that to not rely on Papaparse logic for guessing delimiter
|
|
33157
|
-
let delimiter;
|
|
33158
|
-
try {
|
|
33159
|
-
const header = text.split('\n')[0];
|
|
33160
|
-
const result = Papa.parse(header, {
|
|
33161
|
-
header: false,
|
|
33162
|
-
});
|
|
33163
|
-
delimiter = result.meta.delimiter;
|
|
33164
|
-
}
|
|
33165
|
-
catch (e) {
|
|
33166
|
-
throw new Error('CSV parsing failed: the delimiter could not be guessed');
|
|
33167
|
-
}
|
|
33168
|
-
const parsed = Papa.parse(text, {
|
|
33169
|
-
header: true,
|
|
33170
|
-
skipEmptyLines: true,
|
|
33171
|
-
delimiter,
|
|
33172
|
-
});
|
|
33173
|
-
if (parsed.errors.length) {
|
|
33174
|
-
throw new Error('CSV parsing failed for the following reasons:\n' +
|
|
33175
|
-
parsed.errors
|
|
33176
|
-
.map((error) => `* ${error.message} at row ${error.row}, column ${error.index}`)
|
|
33177
|
-
.join('\n'));
|
|
33178
|
-
}
|
|
33179
|
-
const items = parsed.data.map(jsonToGeojsonFeature);
|
|
33180
|
-
return processItemProperties(items, true);
|
|
33181
|
-
}
|
|
33182
|
-
class CsvReader extends BaseFileReader {
|
|
33183
|
-
getData() {
|
|
33184
|
-
return fetchDataAsText(this.url, this.cacheActive).then(parseCsv);
|
|
33761
|
+
const duckDbFn = isXls ? 'read_json' : 'read_xlsx';
|
|
33762
|
+
await this.engine.registerData(bufferHandle, new Uint8Array(buffer));
|
|
33763
|
+
return `
|
|
33764
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM ${duckDbFn}("${bufferHandle}", ignore_errors = true);`;
|
|
33185
33765
|
}
|
|
33186
33766
|
}
|
|
33187
33767
|
|
|
33188
|
-
|
|
33189
|
-
|
|
33190
|
-
|
|
33191
|
-
|
|
33192
|
-
|
|
33193
|
-
|
|
33194
|
-
if (!Array.isArray(parsed)) {
|
|
33195
|
-
throw new Error('Could not parse JSON, expected an array at root level');
|
|
33196
|
-
}
|
|
33197
|
-
return processItemProperties(parsed.map(jsonToGeojsonFeature));
|
|
33198
|
-
}
|
|
33199
|
-
class JsonReader extends BaseFileReader {
|
|
33200
|
-
getData() {
|
|
33201
|
-
return fetchDataAsText(this.url, this.cacheActive).then(parseJson);
|
|
33768
|
+
class GmlReader extends BaseFileReader {
|
|
33769
|
+
async getLoadQuery() {
|
|
33770
|
+
return `
|
|
33771
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM st_read("${this.url}",
|
|
33772
|
+
allowed_drivers = ['GML']
|
|
33773
|
+
);`;
|
|
33202
33774
|
}
|
|
33203
33775
|
}
|
|
33204
33776
|
|
|
33205
|
-
|
|
33206
|
-
* This parser supports both Geojson Feature collections or arrays
|
|
33207
|
-
* of Features
|
|
33208
|
-
* @param text
|
|
33209
|
-
*/
|
|
33777
|
+
const formatGeojson = new GeoJSON$1();
|
|
33210
33778
|
function parseGeojson(text) {
|
|
33211
33779
|
const parsed = JSON.parse(text);
|
|
33212
33780
|
const features = parsed.type === 'FeatureCollection' ? parsed.features : parsed;
|
|
33213
33781
|
if (!Array.isArray(features)) {
|
|
33214
33782
|
throw new Error('Could not parse GeoJSON, expected a features collection or an array of features at root level');
|
|
33215
33783
|
}
|
|
33216
|
-
return
|
|
33217
|
-
}
|
|
33218
|
-
class GeojsonReader extends BaseFileReader {
|
|
33219
|
-
getData() {
|
|
33220
|
-
return fetchDataAsText(this.url, this.cacheActive).then(parseGeojson);
|
|
33221
|
-
}
|
|
33222
|
-
}
|
|
33223
|
-
|
|
33224
|
-
/**
|
|
33225
|
-
* This will read the first sheet of the excel workbook and expect the first
|
|
33226
|
-
* line to contain the properties names
|
|
33227
|
-
* @param buffer
|
|
33228
|
-
*/
|
|
33229
|
-
function parseExcel(buffer) {
|
|
33230
|
-
return import('xlsx').then(({ read, utils }) => {
|
|
33231
|
-
const workbook = read(buffer);
|
|
33232
|
-
const sheet = workbook.Sheets[workbook.SheetNames[0]];
|
|
33233
|
-
let json = utils.sheet_to_json(sheet);
|
|
33234
|
-
if (!json.length) {
|
|
33235
|
-
json = [];
|
|
33236
|
-
}
|
|
33237
|
-
return processItemProperties(json.map(jsonToGeojsonFeature), true);
|
|
33238
|
-
});
|
|
33239
|
-
}
|
|
33240
|
-
class ExcelReader extends BaseFileReader {
|
|
33241
|
-
getData() {
|
|
33242
|
-
return fetchDataAsArrayBuffer(this.url, this.cacheActive).then(parseExcel);
|
|
33243
|
-
}
|
|
33784
|
+
return features;
|
|
33244
33785
|
}
|
|
33245
|
-
|
|
33246
|
-
|
|
33247
|
-
const
|
|
33248
|
-
const regex = new RegExp(`xmlns:${splittedNamespace[0]}=["']([^'"]*)["']`);
|
|
33786
|
+
function parseGml(text, featureType, version) {
|
|
33787
|
+
const parts = featureType.split(':');
|
|
33788
|
+
const regex = new RegExp(`xmlns:${parts[0]}=["']([^'"]*)["']`);
|
|
33249
33789
|
const match = regex.exec(text);
|
|
33250
33790
|
if (match && match.length >= 2) {
|
|
33251
|
-
const
|
|
33791
|
+
const wfs = new WFS({
|
|
33252
33792
|
featureNS: match[1],
|
|
33253
|
-
featureType:
|
|
33793
|
+
featureType: parts[1],
|
|
33254
33794
|
version: version,
|
|
33255
33795
|
});
|
|
33256
33796
|
let features;
|
|
33257
33797
|
try {
|
|
33258
|
-
features =
|
|
33798
|
+
features = wfs.readFeatures(text);
|
|
33259
33799
|
}
|
|
33260
33800
|
catch (e) {
|
|
33261
|
-
throw Error(
|
|
33801
|
+
throw Error(`Couldn't parse WFS with GML features: ${e.message}`);
|
|
33262
33802
|
}
|
|
33263
|
-
const geojsonItem =
|
|
33264
|
-
return
|
|
33803
|
+
const geojsonItem = formatGeojson.writeFeaturesObject(features);
|
|
33804
|
+
return geojsonItem.features;
|
|
33265
33805
|
}
|
|
33266
33806
|
throw Error("Couldn't retrieve namespace url");
|
|
33267
33807
|
}
|
|
33268
|
-
class GmlReader extends BaseFileReader {
|
|
33269
|
-
constructor(url, namespace, version, cacheActive = true) {
|
|
33270
|
-
super(url);
|
|
33271
|
-
this.url = url;
|
|
33272
|
-
this.namespace = namespace;
|
|
33273
|
-
this.version = version;
|
|
33274
|
-
this.cacheActive = cacheActive;
|
|
33275
|
-
}
|
|
33276
|
-
getData() {
|
|
33277
|
-
return fetchDataAsText(this.url, this.cacheActive).then((text) => parseGml(text, this.namespace, this.version));
|
|
33278
|
-
}
|
|
33279
|
-
}
|
|
33280
|
-
|
|
33281
33808
|
async function getWfsEndpoint(wfsUrl) {
|
|
33282
33809
|
try {
|
|
33283
33810
|
return await new WfsEndpoint(wfsUrl).isReady();
|
|
@@ -33305,21 +33832,65 @@ async function getWfsEndpoint(wfsUrl) {
|
|
|
33305
33832
|
}
|
|
33306
33833
|
}
|
|
33307
33834
|
}
|
|
33308
|
-
class WfsReader extends
|
|
33309
|
-
constructor(url,
|
|
33310
|
-
super(url
|
|
33311
|
-
this.endpoint =
|
|
33312
|
-
this.
|
|
33313
|
-
|
|
33835
|
+
class WfsReader extends BaseReader {
|
|
33836
|
+
constructor(url, featureTypeName) {
|
|
33837
|
+
super(url);
|
|
33838
|
+
this.endpoint = getWfsEndpoint(url);
|
|
33839
|
+
this.featureType = this.endpoint
|
|
33840
|
+
.then((endpoint) => {
|
|
33841
|
+
const featureTypes = endpoint.getFeatureTypes();
|
|
33842
|
+
return endpoint.getFeatureTypeFull(featureTypes.length === 1 && !featureTypeName
|
|
33843
|
+
? featureTypes[0].name
|
|
33844
|
+
: featureTypeName);
|
|
33845
|
+
})
|
|
33846
|
+
.then((featureType) => {
|
|
33847
|
+
if (!featureType) {
|
|
33848
|
+
throw new Error('wfs.featuretype.notfound');
|
|
33849
|
+
}
|
|
33850
|
+
return featureType;
|
|
33851
|
+
});
|
|
33852
|
+
}
|
|
33853
|
+
get backupReader() {
|
|
33854
|
+
if (this.backupReader_) {
|
|
33855
|
+
return this.backupReader_;
|
|
33856
|
+
}
|
|
33857
|
+
this.backupReader_ = Promise.all([this.endpoint, this.featureType]).then(([endpoint, featureType]) => {
|
|
33858
|
+
let reader;
|
|
33859
|
+
if (endpoint.supportsJson(featureType.name)) {
|
|
33860
|
+
reader = new GeojsonReader(endpoint.getFeatureUrl(featureType.name, {
|
|
33861
|
+
asJson: true,
|
|
33862
|
+
outputCrs: 'EPSG:4326',
|
|
33863
|
+
}));
|
|
33864
|
+
}
|
|
33865
|
+
else {
|
|
33866
|
+
if (featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')) &&
|
|
33867
|
+
(featureType.defaultCrs === 'EPSG:4326' ||
|
|
33868
|
+
featureType.otherCrs?.includes('EPSG:4326'))) {
|
|
33869
|
+
reader = new GmlReader(endpoint.getFeatureUrl(featureType.name, {
|
|
33870
|
+
outputFormat: featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')),
|
|
33871
|
+
outputCrs: 'EPSG:4326',
|
|
33872
|
+
}));
|
|
33873
|
+
}
|
|
33874
|
+
}
|
|
33875
|
+
reader.enableCache(this.cacheEnabled);
|
|
33876
|
+
reader.load();
|
|
33877
|
+
return reader;
|
|
33878
|
+
});
|
|
33879
|
+
return this.backupReader_;
|
|
33314
33880
|
}
|
|
33315
33881
|
get properties() {
|
|
33316
|
-
return this.
|
|
33317
|
-
.getFeatureTypeFull(this.featureTypeName)
|
|
33318
|
-
.then((featureType) => Object.keys(featureType.properties).map((prop) => {
|
|
33882
|
+
return this.featureType.then((featureType) => Object.keys(featureType.properties).map((prop) => {
|
|
33319
33883
|
const originalType = featureType.properties[prop];
|
|
33320
|
-
|
|
33321
|
-
|
|
33322
|
-
|
|
33884
|
+
let type;
|
|
33885
|
+
if (originalType === 'float' || originalType === 'integer') {
|
|
33886
|
+
type = 'number';
|
|
33887
|
+
}
|
|
33888
|
+
else if (originalType === 'boolean') {
|
|
33889
|
+
type = 'string'; // we don't handle booleans yet in the data fetcher
|
|
33890
|
+
}
|
|
33891
|
+
else {
|
|
33892
|
+
type = originalType;
|
|
33893
|
+
}
|
|
33323
33894
|
return {
|
|
33324
33895
|
name: prop,
|
|
33325
33896
|
label: prop,
|
|
@@ -33328,83 +33899,63 @@ class WfsReader extends BaseCacheReader {
|
|
|
33328
33899
|
}));
|
|
33329
33900
|
}
|
|
33330
33901
|
get info() {
|
|
33331
|
-
return this.
|
|
33902
|
+
return this.featureType.then((result) => ({
|
|
33332
33903
|
itemsCount: result.objectCount,
|
|
33904
|
+
hasGeometry: !!result.geometryName,
|
|
33333
33905
|
}));
|
|
33334
33906
|
}
|
|
33335
|
-
|
|
33336
|
-
|
|
33337
|
-
const featureTypes = wfsEndpoint.getFeatureTypes();
|
|
33338
|
-
const featureType = wfsEndpoint.getFeatureTypeSummary(featureTypes.length === 1 && !featureTypeName
|
|
33339
|
-
? featureTypes[0].name
|
|
33340
|
-
: featureTypeName);
|
|
33341
|
-
if (!featureType) {
|
|
33342
|
-
throw new Error('wfs.featuretype.notfound');
|
|
33343
|
-
}
|
|
33344
|
-
if (wfsEndpoint.supportsStartIndex()) {
|
|
33345
|
-
return new WfsReader(wfsUrlEndpoint, wfsEndpoint, featureType.name);
|
|
33346
|
-
}
|
|
33347
|
-
else if (wfsEndpoint.supportsJson(featureType.name)) {
|
|
33348
|
-
return new GeojsonReader(wfsEndpoint.getFeatureUrl(featureType.name, {
|
|
33349
|
-
asJson: true,
|
|
33350
|
-
outputCrs: 'EPSG:4326',
|
|
33351
|
-
}));
|
|
33352
|
-
}
|
|
33353
|
-
else {
|
|
33354
|
-
if (featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')) &&
|
|
33355
|
-
(featureType.defaultCrs === 'EPSG:4326' ||
|
|
33356
|
-
featureType.otherCrs?.includes('EPSG:4326'))) {
|
|
33357
|
-
return new GmlReader(wfsEndpoint.getFeatureUrl(featureType.name, {
|
|
33358
|
-
outputFormat: featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')),
|
|
33359
|
-
outputCrs: 'EPSG:4326',
|
|
33360
|
-
}), featureType.name, wfsEndpoint.getVersion());
|
|
33361
|
-
}
|
|
33362
|
-
throw new Error('wfs.geojsongml.notsupported');
|
|
33363
|
-
}
|
|
33907
|
+
load() {
|
|
33908
|
+
// Nothing to load for Wfs
|
|
33364
33909
|
}
|
|
33365
|
-
async
|
|
33366
|
-
|
|
33367
|
-
|
|
33910
|
+
async read() {
|
|
33911
|
+
const endpoint = await this.endpoint;
|
|
33912
|
+
const featureType = await this.featureType;
|
|
33913
|
+
// if we can't use the WFS protocol we fall back to the backup reader
|
|
33914
|
+
if (this.aggregations || this.groupedBy || !endpoint.supportsStartIndex()) {
|
|
33915
|
+
const backupReader = await this.backupReader;
|
|
33916
|
+
backupReader.selectAll();
|
|
33917
|
+
if (this.selected) {
|
|
33918
|
+
backupReader.select(...this.selected);
|
|
33919
|
+
}
|
|
33920
|
+
if (this.aggregations) {
|
|
33921
|
+
backupReader.aggregate(...this.aggregations);
|
|
33922
|
+
}
|
|
33923
|
+
if (this.groupedBy) {
|
|
33924
|
+
backupReader.groupBy(...this.groupedBy);
|
|
33925
|
+
}
|
|
33926
|
+
if (this.sort) {
|
|
33927
|
+
backupReader.orderBy(...this.sort);
|
|
33928
|
+
}
|
|
33929
|
+
if (this.startIndex !== null && this.count !== null) {
|
|
33930
|
+
backupReader.limit(this.startIndex, this.count);
|
|
33931
|
+
}
|
|
33932
|
+
return backupReader.read();
|
|
33368
33933
|
}
|
|
33369
|
-
const asJson =
|
|
33934
|
+
const asJson = endpoint.supportsJson(featureType.name);
|
|
33370
33935
|
const attributes = this.selected ?? undefined;
|
|
33371
|
-
let
|
|
33936
|
+
let sortBy = null;
|
|
33937
|
+
if (this.sort) {
|
|
33938
|
+
const mapSort = (s) => [s[0] === 'desc' ? 'D' : 'A', s[1]];
|
|
33939
|
+
sortBy = Array.isArray(this.sort[0])
|
|
33940
|
+
? this.sort.map(mapSort)
|
|
33941
|
+
: mapSort(this.sort);
|
|
33942
|
+
}
|
|
33943
|
+
const url = endpoint.getFeatureUrl(featureType.name, {
|
|
33372
33944
|
...(this.startIndex !== null && { startIndex: this.startIndex }),
|
|
33373
33945
|
...(this.count !== null && { maxFeatures: this.count }),
|
|
33374
33946
|
asJson,
|
|
33375
33947
|
outputCrs: 'EPSG:4326',
|
|
33376
33948
|
attributes,
|
|
33377
|
-
|
|
33949
|
+
sortBy,
|
|
33378
33950
|
});
|
|
33379
|
-
|
|
33380
|
-
const finalUrl = new URL(url);
|
|
33381
|
-
const sorts = this.sort
|
|
33382
|
-
.map((fieldSort) => `${fieldSort[1]}+${fieldSort[0] === 'asc' ? 'A' : 'D'}`)
|
|
33383
|
-
.join(',');
|
|
33384
|
-
// Direct update on string url to prevent encoding of +A and +D
|
|
33385
|
-
url = `${url}${finalUrl.search ? '&' : ''}SORTBY=${sorts}`;
|
|
33386
|
-
}
|
|
33387
|
-
return fetchDataAsText(url, this.cacheActive).then((text) => asJson
|
|
33951
|
+
return fetchDataAsText(url, this.cacheEnabled).then((text) => asJson
|
|
33388
33952
|
? parseGeojson(text)
|
|
33389
|
-
: parseGml(text,
|
|
33390
|
-
}
|
|
33391
|
-
async getQueryData() {
|
|
33392
|
-
const items = (await this.getData()).items;
|
|
33393
|
-
const jsonItems = getJsonDataItemsProxy(items);
|
|
33394
|
-
const query = generateSqlQuery(this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations);
|
|
33395
|
-
const result = await import('alasql').then((module) => module.default(query, [jsonItems]));
|
|
33396
|
-
return result.map(jsonToGeojsonFeature);
|
|
33397
|
-
}
|
|
33398
|
-
load() {
|
|
33399
|
-
// Nothing to load for Wfs
|
|
33400
|
-
}
|
|
33401
|
-
async read() {
|
|
33402
|
-
return (await this.getData(this.aggregations, this.groupedBy)).items;
|
|
33953
|
+
: parseGml(text, featureType.name, endpoint.getVersion()));
|
|
33403
33954
|
}
|
|
33404
33955
|
}
|
|
33405
33956
|
|
|
33406
|
-
async function openDataset(url,
|
|
33407
|
-
const fileType = await inferDatasetType(url, typeHint);
|
|
33957
|
+
async function openDataset(url, options) {
|
|
33958
|
+
const fileType = await inferDatasetType(url, options?.typeHint);
|
|
33408
33959
|
let reader;
|
|
33409
33960
|
try {
|
|
33410
33961
|
switch (fileType) {
|
|
@@ -33421,18 +33972,18 @@ async function openDataset(url, typeHint, options, cacheActive) {
|
|
|
33421
33972
|
reader = new ExcelReader(url);
|
|
33422
33973
|
break;
|
|
33423
33974
|
case 'gml':
|
|
33424
|
-
reader = new GmlReader(url
|
|
33975
|
+
reader = new GmlReader(url);
|
|
33425
33976
|
break;
|
|
33426
33977
|
case 'wfs':
|
|
33427
|
-
reader =
|
|
33978
|
+
reader = new WfsReader(url, options?.wfsFeatureType);
|
|
33428
33979
|
break;
|
|
33429
33980
|
}
|
|
33430
|
-
reader.
|
|
33981
|
+
reader.enableCache(options?.enableCache ?? true);
|
|
33431
33982
|
reader.load();
|
|
33432
33983
|
return reader;
|
|
33433
33984
|
}
|
|
33434
33985
|
catch (e) {
|
|
33435
|
-
//WfsReader may already raise a FetchError
|
|
33986
|
+
// WfsReader may already raise a FetchError
|
|
33436
33987
|
if (e instanceof FetchError)
|
|
33437
33988
|
throw e;
|
|
33438
33989
|
else
|
|
@@ -33443,13 +33994,13 @@ async function openDataset(url, typeHint, options, cacheActive) {
|
|
|
33443
33994
|
* This fetches the full dataset at the given URL and parses it according to its mime type.
|
|
33444
33995
|
* All items in the dataset are converted to GeoJSON features, even if they do not bear any spatial geometry.
|
|
33445
33996
|
* File type can be either inferred (from the HTTP headers or the URL), or hinted using the 2nd argument
|
|
33446
|
-
* File type is determined
|
|
33997
|
+
* File type is determined like so:
|
|
33447
33998
|
* 1. if a type hint is given, use it
|
|
33448
33999
|
* 2. otherwise, look for a Content-Type header in the response with a supported mime type
|
|
33449
34000
|
* 3. if no valid mime type was found, look for an explicit file extension in the url (.csv, .geojson etc.)
|
|
33450
34001
|
*/
|
|
33451
|
-
async function readDataset(url,
|
|
33452
|
-
const reader = await openDataset(url,
|
|
34002
|
+
async function readDataset(url, options) {
|
|
34003
|
+
const reader = await openDataset(url, options);
|
|
33453
34004
|
try {
|
|
33454
34005
|
return await reader.read();
|
|
33455
34006
|
}
|
|
@@ -33680,9 +34231,11 @@ class DataService {
|
|
|
33680
34231
|
getDataset(link, cacheActive) {
|
|
33681
34232
|
if (link.type === 'service' && link.accessServiceProtocol === 'wfs') {
|
|
33682
34233
|
const wfsUrlEndpoint = this.proxy.getProxiedUrl(link.url.toString());
|
|
33683
|
-
return from(openDataset(wfsUrlEndpoint,
|
|
34234
|
+
return from(openDataset(wfsUrlEndpoint, {
|
|
34235
|
+
typeHint: 'wfs',
|
|
33684
34236
|
wfsFeatureType: link.name,
|
|
33685
|
-
|
|
34237
|
+
enableCache: cacheActive,
|
|
34238
|
+
}));
|
|
33686
34239
|
}
|
|
33687
34240
|
else if (link.type === 'download') {
|
|
33688
34241
|
const linkProxifiedUrl = this.proxy.getProxiedUrl(link.url.toString());
|
|
@@ -33690,12 +34243,15 @@ class DataService {
|
|
|
33690
34243
|
const supportedType = SupportedTypes.indexOf(format) > -1
|
|
33691
34244
|
? format
|
|
33692
34245
|
: undefined;
|
|
33693
|
-
return from(openDataset(linkProxifiedUrl,
|
|
34246
|
+
return from(openDataset(linkProxifiedUrl, {
|
|
34247
|
+
typeHint: supportedType,
|
|
34248
|
+
enableCache: cacheActive,
|
|
34249
|
+
})).pipe();
|
|
33694
34250
|
}
|
|
33695
34251
|
else if (link.type === 'service' &&
|
|
33696
34252
|
link.accessServiceProtocol === 'esriRest') {
|
|
33697
34253
|
const url = this.getDownloadUrlFromEsriRest(link.url.toString(), 'geojson');
|
|
33698
|
-
return from(openDataset(url, 'geojson',
|
|
34254
|
+
return from(openDataset(url, { typeHint: 'geojson', enableCache: cacheActive })).pipe();
|
|
33699
34255
|
}
|
|
33700
34256
|
else if (link.type === 'service' &&
|
|
33701
34257
|
link.accessServiceProtocol === 'ogcFeatures') {
|
|
@@ -33709,7 +34265,10 @@ class DataService {
|
|
|
33709
34265
|
}
|
|
33710
34266
|
const urlWithoutLimit = new URL(geojsonUrl);
|
|
33711
34267
|
urlWithoutLimit.searchParams.delete('limit');
|
|
33712
|
-
return openDataset(urlWithoutLimit.toString(),
|
|
34268
|
+
return openDataset(urlWithoutLimit.toString(), {
|
|
34269
|
+
typeHint: 'geojson',
|
|
34270
|
+
enableCache: cacheActive,
|
|
34271
|
+
});
|
|
33713
34272
|
}));
|
|
33714
34273
|
}
|
|
33715
34274
|
return throwError(() => 'protocol not supported');
|
|
@@ -33961,20 +34520,31 @@ class DataTableComponent {
|
|
|
33961
34520
|
this.eltRef = inject(ElementRef);
|
|
33962
34521
|
this.cdr = inject(ChangeDetectorRef);
|
|
33963
34522
|
this.translateService = inject(TranslateService);
|
|
33964
|
-
this.
|
|
34523
|
+
this.columnsFromFeatureCatalog = null;
|
|
34524
|
+
this.columnsFromDataset = [];
|
|
33965
34525
|
this.selected = new EventEmitter();
|
|
33966
|
-
this.properties$ = new BehaviorSubject(null);
|
|
33967
34526
|
this.loading$ = new BehaviorSubject(false);
|
|
33968
34527
|
this.error = null;
|
|
33969
34528
|
}
|
|
33970
34529
|
set featureAttributes(value) {
|
|
33971
|
-
this.
|
|
33972
|
-
|
|
34530
|
+
this.columnsFromFeatureCatalog = value.map((attrs) => ({
|
|
34531
|
+
name: attrs.value,
|
|
34532
|
+
label: attrs.label,
|
|
34533
|
+
}));
|
|
33973
34534
|
}
|
|
33974
34535
|
set dataset(value) {
|
|
33975
34536
|
this.dataset_ = value;
|
|
33976
34537
|
this.dataset_.load();
|
|
33977
|
-
this.dataset_.info.then((info) =>
|
|
34538
|
+
this.dataset_.info.then((info) => {
|
|
34539
|
+
this.count = info.itemsCount;
|
|
34540
|
+
this.cdr.detectChanges();
|
|
34541
|
+
});
|
|
34542
|
+
}
|
|
34543
|
+
get columns() {
|
|
34544
|
+
return this.columnsFromFeatureCatalog ?? this.columnsFromDataset;
|
|
34545
|
+
}
|
|
34546
|
+
get columnNames() {
|
|
34547
|
+
return this.columns.map((c) => c.name);
|
|
33978
34548
|
}
|
|
33979
34549
|
ngOnInit() {
|
|
33980
34550
|
this.dataSource = new DataTableDataSource();
|
|
@@ -34009,11 +34579,12 @@ class DataTableComponent {
|
|
|
34009
34579
|
}
|
|
34010
34580
|
async readData() {
|
|
34011
34581
|
this.loading$.next(true);
|
|
34012
|
-
// wait for properties to be read
|
|
34013
|
-
const properties = await firstValueFrom(this.properties$.pipe(filter((p) => !!p)));
|
|
34014
|
-
const propsWithoutGeom = properties.filter((p) => !p.toLowerCase().startsWith('geom'));
|
|
34015
|
-
this.dataset_.select(...propsWithoutGeom);
|
|
34016
34582
|
try {
|
|
34583
|
+
// wait for properties to be read
|
|
34584
|
+
if (!this.columnsFromFeatureCatalog) {
|
|
34585
|
+
this.columnsFromDataset = await this.dataset_.properties;
|
|
34586
|
+
}
|
|
34587
|
+
this.dataset_.select(...this.columnNames);
|
|
34017
34588
|
await this.dataSource.showData(this.dataset_.read());
|
|
34018
34589
|
this.error = null;
|
|
34019
34590
|
}
|
|
@@ -34043,7 +34614,7 @@ class DataTableComponent {
|
|
|
34043
34614
|
}
|
|
34044
34615
|
}
|
|
34045
34616
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DataTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
34046
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DataTableComponent, isStandalone: true, selector: "gn-ui-data-table", inputs: { featureAttributes: "featureAttributes", dataset: "dataset", activeId: "activeId" }, outputs: { selected: "selected" }, providers: [{ provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl }], viewQueries: [{ propertyName: "sort", first: true, predicate: MatSort, descendants: true }, { propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col border border-gray-300 rounded-lg bg-white h-full\">\n <div class=\"flex-1 overflow-y-hidden overflow-x-auto rounded-lg relative\">\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"setSort($event)\"\n [matSortDisableClear]=\"true\"\n
|
|
34617
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DataTableComponent, isStandalone: true, selector: "gn-ui-data-table", inputs: { featureAttributes: "featureAttributes", dataset: "dataset", activeId: "activeId" }, outputs: { selected: "selected" }, providers: [{ provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl }], viewQueries: [{ propertyName: "sort", first: true, predicate: MatSort, descendants: true }, { propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col border border-gray-300 rounded-lg bg-white h-full\">\n <div class=\"flex-1 overflow-y-hidden overflow-x-auto rounded-lg relative\">\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"setSort($event)\"\n [matSortDisableClear]=\"true\"\n >\n @for (column of columns; track column) {\n <ng-container [matColumnDef]=\"column.name\">\n <th\n mat-header-cell\n *matHeaderCellDef\n mat-sort-header\n class=\"text-sm text-black bg-white\"\n >\n {{ column.label }}\n </th>\n <td\n mat-cell\n *matCellDef=\"let element\"\n class=\"whitespace-nowrap pr-1 truncate\"\n >\n {{ element[column.name] }}\n </td>\n </ng-container>\n }\n\n <tr mat-header-row *matHeaderRowDef=\"columnNames; sticky: true\"></tr>\n <tr\n [id]=\"getRowEltId(row.id)\"\n mat-row\n *matRowDef=\"let row; columns: columnNames\"\n (click)=\"selected.emit(row)\"\n [class.active]=\"\n activeId !== undefined && activeId !== null && row.id === activeId\n \"\n ></tr>\n </table>\n @if (loading$ | async) {\n <gn-ui-loading-mask\n class=\"sticky inset-0\"\n [message]=\"'table.loading.data' | translate\"\n ></gn-ui-loading-mask>\n }\n @if (error) {\n <gn-ui-popup-alert\n type=\"warning\"\n icon=\"matErrorOutlineOutline\"\n class=\"absolute m-2 inset-0 z-[100]\"\n >\n <span translate>{{ error }}</span>\n </gn-ui-popup-alert>\n }\n </div>\n <div class=\"flex justify-between items-center overflow-hidden\">\n <div class=\"text-gray-900 px-4 py-2 text-sm\">\n <span class=\"count font-extrabold text-primary\">{{ count }}</span\n > <span translate>table.object.count</span>.\n </div>\n\n <mat-paginator\n class=\"my-[-16px]\"\n (page)=\"setPagination()\"\n [length]=\"count\"\n [pageSize]=\"10\"\n [showFirstLastButtons]=\"true\"\n [hidePageSize]=\"true\"\n ></mat-paginator>\n </div>\n</div>\n", styles: ["table{width:100%;background:#fff}th.mat-mdc-header-cell,td.mat-mdc-cell,td.mat-mdc-footer-cell{padding-right:20px}tr.mat-mdc-row,tr.mat-mdc-footer-row{height:36px}tr:hover{background:#f5f5f5}tr.mat-mdc-header-row{height:48px}[mat-header-cell]{color:#0000008a;font-size:12px;font-weight:500}tr{cursor:pointer}.active .mat-mdc-cell{color:var(--color-primary)}.mat-mdc-paginator{background:none}\n"], dependencies: [{ kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i1$b.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i1$b.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i1$b.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i1$b.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i1$b.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i1$b.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i1$b.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i1$b.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i1$b.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i1$b.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i2$4.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i2$4.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: LoadingMaskComponent, selector: "gn-ui-loading-mask", inputs: ["message"] }, { kind: "component", type: PopupAlertComponent, selector: "gn-ui-popup-alert", inputs: ["icon", "type", "position"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
34047
34618
|
}
|
|
34048
34619
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DataTableComponent, decorators: [{
|
|
34049
34620
|
type: Component,
|
|
@@ -34055,10 +34626,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
34055
34626
|
CommonModule,
|
|
34056
34627
|
LoadingMaskComponent,
|
|
34057
34628
|
PopupAlertComponent,
|
|
34058
|
-
LetDirective,
|
|
34059
34629
|
TranslatePipe,
|
|
34060
34630
|
TranslateDirective,
|
|
34061
|
-
], providers: [{ provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl }], selector: 'gn-ui-data-table', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex flex-col border border-gray-300 rounded-lg bg-white h-full\">\n <div class=\"flex-1 overflow-y-hidden overflow-x-auto rounded-lg relative\">\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"setSort($event)\"\n [matSortDisableClear]=\"true\"\n
|
|
34631
|
+
], providers: [{ provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl }], selector: 'gn-ui-data-table', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex flex-col border border-gray-300 rounded-lg bg-white h-full\">\n <div class=\"flex-1 overflow-y-hidden overflow-x-auto rounded-lg relative\">\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"setSort($event)\"\n [matSortDisableClear]=\"true\"\n >\n @for (column of columns; track column) {\n <ng-container [matColumnDef]=\"column.name\">\n <th\n mat-header-cell\n *matHeaderCellDef\n mat-sort-header\n class=\"text-sm text-black bg-white\"\n >\n {{ column.label }}\n </th>\n <td\n mat-cell\n *matCellDef=\"let element\"\n class=\"whitespace-nowrap pr-1 truncate\"\n >\n {{ element[column.name] }}\n </td>\n </ng-container>\n }\n\n <tr mat-header-row *matHeaderRowDef=\"columnNames; sticky: true\"></tr>\n <tr\n [id]=\"getRowEltId(row.id)\"\n mat-row\n *matRowDef=\"let row; columns: columnNames\"\n (click)=\"selected.emit(row)\"\n [class.active]=\"\n activeId !== undefined && activeId !== null && row.id === activeId\n \"\n ></tr>\n </table>\n @if (loading$ | async) {\n <gn-ui-loading-mask\n class=\"sticky inset-0\"\n [message]=\"'table.loading.data' | translate\"\n ></gn-ui-loading-mask>\n }\n @if (error) {\n <gn-ui-popup-alert\n type=\"warning\"\n icon=\"matErrorOutlineOutline\"\n class=\"absolute m-2 inset-0 z-[100]\"\n >\n <span translate>{{ error }}</span>\n </gn-ui-popup-alert>\n }\n </div>\n <div class=\"flex justify-between items-center overflow-hidden\">\n <div class=\"text-gray-900 px-4 py-2 text-sm\">\n <span class=\"count font-extrabold text-primary\">{{ count }}</span\n > <span translate>table.object.count</span>.\n </div>\n\n <mat-paginator\n class=\"my-[-16px]\"\n (page)=\"setPagination()\"\n [length]=\"count\"\n [pageSize]=\"10\"\n [showFirstLastButtons]=\"true\"\n [hidePageSize]=\"true\"\n ></mat-paginator>\n </div>\n</div>\n", styles: ["table{width:100%;background:#fff}th.mat-mdc-header-cell,td.mat-mdc-cell,td.mat-mdc-footer-cell{padding-right:20px}tr.mat-mdc-row,tr.mat-mdc-footer-row{height:36px}tr:hover{background:#f5f5f5}tr.mat-mdc-header-row{height:48px}[mat-header-cell]{color:#0000008a;font-size:12px;font-weight:500}tr{cursor:pointer}.active .mat-mdc-cell{color:var(--color-primary)}.mat-mdc-paginator{background:none}\n"] }]
|
|
34062
34632
|
}], propDecorators: { featureAttributes: [{
|
|
34063
34633
|
type: Input
|
|
34064
34634
|
}], dataset: [{
|
|
@@ -34161,7 +34731,7 @@ class ChartViewComponent {
|
|
|
34161
34731
|
}), shareReplay$1(1));
|
|
34162
34732
|
this.properties$ = combineLatest([this.dataset$, this.featureCatalog$]).pipe(switchMap$1(([dataset, catalog]) => this.setProperties(dataset, catalog)), shareReplay$1(1));
|
|
34163
34733
|
this.yChoices$ = this.properties$.pipe(map$1((properties) => properties
|
|
34164
|
-
.filter((prop) => prop.type === 'number'
|
|
34734
|
+
.filter((prop) => prop.type === 'number')
|
|
34165
34735
|
.map((prop) => ({ value: prop.name, label: prop.label || prop.name }))), tap$1((choices) => {
|
|
34166
34736
|
if (!choices.find((choice) => choice.value === this.yProperty$.value)) {
|
|
34167
34737
|
const newProp = choices[0]?.value || '';
|
|
@@ -34188,6 +34758,7 @@ class ChartViewComponent {
|
|
|
34188
34758
|
this.aggregation$,
|
|
34189
34759
|
]).pipe(filter$1(([_, x, y]) => !!x || !!y), switchMap$1(([dataset, xProp, yProp, aggregation]) => {
|
|
34190
34760
|
const fieldAgg = aggregation === 'count' ? ['count'] : [aggregation, yProp];
|
|
34761
|
+
this.loading = true;
|
|
34191
34762
|
return dataset
|
|
34192
34763
|
.groupBy(['distinct', xProp])
|
|
34193
34764
|
.aggregate(fieldAgg)
|
|
@@ -34429,6 +35000,7 @@ class GeoTableViewComponent {
|
|
|
34429
35000
|
}));
|
|
34430
35001
|
}
|
|
34431
35002
|
async initMapContext() {
|
|
35003
|
+
this.dataset.load();
|
|
34432
35004
|
this.dataset.selectAll();
|
|
34433
35005
|
return {
|
|
34434
35006
|
layers: [
|
|
@@ -34602,8 +35174,7 @@ class MdViewFacade {
|
|
|
34602
35174
|
}));
|
|
34603
35175
|
this.error$ = this.store.pipe(select(getMetadataError));
|
|
34604
35176
|
this.related$ = this.store.pipe(select(getRelated));
|
|
34605
|
-
this.
|
|
34606
|
-
this.sourceOf$ = this.store.pipe(select(getSourceOf));
|
|
35177
|
+
this.linkedRecords$ = this.store.pipe(select(getLinkedRecords));
|
|
34607
35178
|
this.chartConfig$ = this.store.pipe(select(getChartConfig));
|
|
34608
35179
|
this.allLinks$ = this.metadata$.pipe(map$1((record) => 'onlineResources' in record ? record.onlineResources : []), shareReplay$1(1));
|
|
34609
35180
|
this.resourceDoi$ = this.metadata$.pipe(map$1((record) => {
|
|
@@ -34722,12 +35293,9 @@ class MdViewEffects {
|
|
|
34722
35293
|
this.loadRelatedRecords$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getSimilarRecords(full)), map$1((related) => {
|
|
34723
35294
|
return setRelated({ related });
|
|
34724
35295
|
}), catchError(() => of(setRelated({ related: null })))));
|
|
34725
|
-
this.
|
|
34726
|
-
return
|
|
34727
|
-
}), catchError(() => of(
|
|
34728
|
-
this.loadSourceOf$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getSourceOf(full)), map$1((sourceOf) => {
|
|
34729
|
-
return setSourceOf({ sourceOf });
|
|
34730
|
-
}), catchError(() => of(setSourceOf({ sourceOf: null })))));
|
|
35296
|
+
this.loadLinkedRecords$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getLinkedRecords(full)), map$1((linkedRecords) => {
|
|
35297
|
+
return setLinkedRecords({ linkedRecords });
|
|
35298
|
+
}), catchError(() => of(setLinkedRecords({ linkedRecords: null })))));
|
|
34731
35299
|
/*
|
|
34732
35300
|
UserFeedback effects
|
|
34733
35301
|
*/
|
|
@@ -37428,7 +37996,7 @@ class GenericKeywordsComponent {
|
|
|
37428
37996
|
this.deletedKeyword.emit(keyword);
|
|
37429
37997
|
}
|
|
37430
37998
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GenericKeywordsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
37431
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: GenericKeywordsComponent, isStandalone: true, selector: "gn-ui-generic-keywords", inputs: { keywords: "keywords", keywordTypes: "keywordTypes", placeholder: "placeholder", allowSubmit: "allowSubmit" }, outputs: { changedKeywords: "changedKeywords", addedKeyword: "addedKeyword", deletedKeyword: "deletedKeyword" }, ngImport: i0, template: "<div class=\"flex flex-col gap-4\">\n <gn-ui-autocomplete\n [placeholder]=\"placeholder\"\n [displayWithFn]=\"displayWithFn\"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"handleItemSelection($event)\"\n [preventCompleteOnSelection]=\"false\"\n [minCharacterCount]=\"1\"\n [allowSubmit]=\"false\"\n [clearOnSelection]=\"true\"\n ></gn-ui-autocomplete>\n <div class=\"flex gap-2 flex-wrap\">\n @for (keyword of keywords; track keyword) {\n <gn-ui-keyword-badge\n [keyword]=\"keyword\"\n [editable]=\"true\"\n (keywordRemove)=\"removeKeyword($event)\"\n ></gn-ui-keyword-badge>\n }\n </div>\n</div>\n", dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "component", type: KeywordBadgeComponent, selector: "gn-ui-keyword-badge", inputs: ["keyword", "editable"], outputs: ["keywordClick", "keywordRemove"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
37999
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: GenericKeywordsComponent, isStandalone: true, selector: "gn-ui-generic-keywords", inputs: { keywords: "keywords", keywordTypes: "keywordTypes", placeholder: "placeholder", allowSubmit: "allowSubmit" }, outputs: { changedKeywords: "changedKeywords", addedKeyword: "addedKeyword", deletedKeyword: "deletedKeyword" }, ngImport: i0, template: "<div class=\"flex flex-col gap-4\">\n <gn-ui-autocomplete\n [placeholder]=\"placeholder\"\n [displayWithFn]=\"displayWithFn\"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"handleItemSelection($event)\"\n [preventCompleteOnSelection]=\"false\"\n [minCharacterCount]=\"1\"\n [allowSubmit]=\"false\"\n [clearOnSelection]=\"true\"\n ></gn-ui-autocomplete>\n <div class=\"flex gap-2 flex-wrap\">\n @for (keyword of keywords; track keyword) {\n <gn-ui-keyword-badge\n [keyword]=\"keyword\"\n [editable]=\"true\"\n (keywordRemove)=\"removeKeyword($event)\"\n ></gn-ui-keyword-badge>\n }\n </div>\n</div>\n", dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithTemplate", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "component", type: KeywordBadgeComponent, selector: "gn-ui-keyword-badge", inputs: ["keyword", "editable"], outputs: ["keywordClick", "keywordRemove"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
37432
38000
|
}
|
|
37433
38001
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: GenericKeywordsComponent, decorators: [{
|
|
37434
38002
|
type: Component,
|
|
@@ -38020,7 +38588,7 @@ class FormFieldContactsForResourceComponent {
|
|
|
38020
38588
|
provideNgIconsConfig({
|
|
38021
38589
|
size: '1.5rem',
|
|
38022
38590
|
}),
|
|
38023
|
-
], usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col gap-3\">\n <div class=\"flex flex-row flex-wrap gap-2\" data-test=\"rolesToPick\">\n @for (role of rolesToPick; track role) {\n <gn-ui-button type=\"gray\" (buttonClick)=\"addRoleToDisplay(role)\">\n <ng-icon name=\"iconoirPlus\" class=\"text-primary\"></ng-icon>\n \n <span translate>{{ roleToLabel(role) }}</span>\n </gn-ui-button>\n }\n </div>\n @if (value.length === 0) {\n <div\n class=\"p-4 border border-primary bg-primary-lightest rounded-lg\"\n translate\n >\n editor.record.form.field.contactsForResource.noContact\n </div>\n }\n @if (roleSectionsToDisplay && roleSectionsToDisplay.length > 0) {\n <div class=\"mt-8\" data-test=\"displayedRoles\">\n @for (\n role of roleSectionsToDisplay;\n track role;\n let index = $index;\n let isLast = $last\n ) {\n <div class=\"flex flex-col gap-4\">\n <div class=\"flex flex-row justify-between\">\n <span class=\"font-bold text-base\" translate>{{\n roleToLabel(role)\n }}</span>\n </div>\n @if (role !== 'unspecified' && role !== 'other') {\n <gn-ui-autocomplete\n [placeholder]=\"\n 'editor.record.form.field.contactsForResource.placeholder'\n | translate\n \"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"addContact($event, role)\"\n [displayWithFn]=\"displayWithFn\"\n [minCharacterCount]=\"1\"\n [clearOnSelection]=\"true\"\n [allowSubmit]=\"false\"\n >\n </gn-ui-autocomplete>\n }\n @if (contactsForRessourceByRole.get(role); as contacts) {\n <gn-ui-sortable-list\n [items]=\"contacts\"\n (itemsOrderChange)=\"handleContactsChanged($event, role)\"\n [elementTemplate]=\"contactTemplate\"\n ></gn-ui-sortable-list>\n <ng-template #contactTemplate let-contact>\n <gn-ui-contact-card [contact]=\"contact\"></gn-ui-contact-card>\n </ng-template>\n }\n @if (!isLast) {\n <hr class=\"border-t-[#D6D3D1] mt-4 mb-6\" />\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: [":host{--gn-ui-button-padding: 8px 8px;--gn-ui-button-rounded: 8px}\n"], dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "component", type: ContactCardComponent, selector: "gn-ui-contact-card", inputs: ["contact"] }, { kind: "component", type: SortableListComponent, selector: "gn-ui-sortable-list", inputs: ["elementTemplate", "items"], outputs: ["itemsOrderChange"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
38591
|
+
], usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col gap-3\">\n <div class=\"flex flex-row flex-wrap gap-2\" data-test=\"rolesToPick\">\n @for (role of rolesToPick; track role) {\n <gn-ui-button type=\"gray\" (buttonClick)=\"addRoleToDisplay(role)\">\n <ng-icon name=\"iconoirPlus\" class=\"text-primary\"></ng-icon>\n \n <span translate>{{ roleToLabel(role) }}</span>\n </gn-ui-button>\n }\n </div>\n @if (value.length === 0) {\n <div\n class=\"p-4 border border-primary bg-primary-lightest rounded-lg\"\n translate\n >\n editor.record.form.field.contactsForResource.noContact\n </div>\n }\n @if (roleSectionsToDisplay && roleSectionsToDisplay.length > 0) {\n <div class=\"mt-8\" data-test=\"displayedRoles\">\n @for (\n role of roleSectionsToDisplay;\n track role;\n let index = $index;\n let isLast = $last\n ) {\n <div class=\"flex flex-col gap-4\">\n <div class=\"flex flex-row justify-between\">\n <span class=\"font-bold text-base\" translate>{{\n roleToLabel(role)\n }}</span>\n </div>\n @if (role !== 'unspecified' && role !== 'other') {\n <gn-ui-autocomplete\n [placeholder]=\"\n 'editor.record.form.field.contactsForResource.placeholder'\n | translate\n \"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"addContact($event, role)\"\n [displayWithFn]=\"displayWithFn\"\n [minCharacterCount]=\"1\"\n [clearOnSelection]=\"true\"\n [allowSubmit]=\"false\"\n >\n </gn-ui-autocomplete>\n }\n @if (contactsForRessourceByRole.get(role); as contacts) {\n <gn-ui-sortable-list\n [items]=\"contacts\"\n (itemsOrderChange)=\"handleContactsChanged($event, role)\"\n [elementTemplate]=\"contactTemplate\"\n ></gn-ui-sortable-list>\n <ng-template #contactTemplate let-contact>\n <gn-ui-contact-card [contact]=\"contact\"></gn-ui-contact-card>\n </ng-template>\n }\n @if (!isLast) {\n <hr class=\"border-t-[#D6D3D1] mt-4 mb-6\" />\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: [":host{--gn-ui-button-padding: 8px 8px;--gn-ui-button-rounded: 8px}\n"], dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithTemplate", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "component", type: ContactCardComponent, selector: "gn-ui-contact-card", inputs: ["contact"] }, { kind: "component", type: SortableListComponent, selector: "gn-ui-sortable-list", inputs: ["elementTemplate", "items"], outputs: ["itemsOrderChange"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
38024
38592
|
}
|
|
38025
38593
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FormFieldContactsForResourceComponent, decorators: [{
|
|
38026
38594
|
type: Component,
|
|
@@ -38120,7 +38688,7 @@ class FormFieldContactsComponent {
|
|
|
38120
38688
|
this.subscription.unsubscribe();
|
|
38121
38689
|
}
|
|
38122
38690
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FormFieldContactsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
38123
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FormFieldContactsComponent, isStandalone: true, selector: "gn-ui-form-field-contacts", inputs: { value: "value", modelSpecifier: "modelSpecifier" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col gap-3\">\n <gn-ui-autocomplete\n [placeholder]=\"'editor.record.form.field.contacts.placeholder' | translate\"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"addContact($event)\"\n [displayWithFn]=\"displayWithFn\"\n [minCharacterCount]=\"1\"\n [clearOnSelection]=\"true\"\n >\n </gn-ui-autocomplete>\n\n @if (contacts.length > 0) {\n <gn-ui-sortable-list\n [items]=\"contacts\"\n (itemsOrderChange)=\"handleContactsChanged($event)\"\n [elementTemplate]=\"contactTemplate\"\n ></gn-ui-sortable-list>\n <ng-template #contactTemplate let-contact let-index=\"index\">\n @if (modelSpecifier === 'contact:editableDetails') {\n <gn-ui-contact-details-form\n [contact]=\"contact\"\n (contactChange)=\"handleContactChanged($event, index)\"\n ></gn-ui-contact-details-form>\n } @else {\n <gn-ui-contact-card [contact]=\"contact\"></gn-ui-contact-card>\n }\n </ng-template>\n } @else {\n <div\n class=\"p-4 text-sm border border-primary bg-primary-lightest rounded-lg\"\n translate\n >\n editor.record.form.field.contacts.noContact\n </div>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: ContactCardComponent, selector: "gn-ui-contact-card", inputs: ["contact"] }, { kind: "component", type: ContactDetailsFormComponent, selector: "gn-ui-contact-details-form", inputs: ["contact"], outputs: ["contactChange"] }, { kind: "component", type: SortableListComponent, selector: "gn-ui-sortable-list", inputs: ["elementTemplate", "items"], outputs: ["itemsOrderChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
38691
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FormFieldContactsComponent, isStandalone: true, selector: "gn-ui-form-field-contacts", inputs: { value: "value", modelSpecifier: "modelSpecifier" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col gap-3\">\n <gn-ui-autocomplete\n [placeholder]=\"'editor.record.form.field.contacts.placeholder' | translate\"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"addContact($event)\"\n [displayWithFn]=\"displayWithFn\"\n [minCharacterCount]=\"1\"\n [clearOnSelection]=\"true\"\n >\n </gn-ui-autocomplete>\n\n @if (contacts.length > 0) {\n <gn-ui-sortable-list\n [items]=\"contacts\"\n (itemsOrderChange)=\"handleContactsChanged($event)\"\n [elementTemplate]=\"contactTemplate\"\n ></gn-ui-sortable-list>\n <ng-template #contactTemplate let-contact let-index=\"index\">\n @if (modelSpecifier === 'contact:editableDetails') {\n <gn-ui-contact-details-form\n [contact]=\"contact\"\n (contactChange)=\"handleContactChanged($event, index)\"\n ></gn-ui-contact-details-form>\n } @else {\n <gn-ui-contact-card [contact]=\"contact\"></gn-ui-contact-card>\n }\n </ng-template>\n } @else {\n <div\n class=\"p-4 text-sm border border-primary bg-primary-lightest rounded-lg\"\n translate\n >\n editor.record.form.field.contacts.noContact\n </div>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithTemplate", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: ContactCardComponent, selector: "gn-ui-contact-card", inputs: ["contact"] }, { kind: "component", type: ContactDetailsFormComponent, selector: "gn-ui-contact-details-form", inputs: ["contact"], outputs: ["contactChange"] }, { kind: "component", type: SortableListComponent, selector: "gn-ui-sortable-list", inputs: ["elementTemplate", "items"], outputs: ["itemsOrderChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
38124
38692
|
}
|
|
38125
38693
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FormFieldContactsComponent, decorators: [{
|
|
38126
38694
|
type: Component,
|
|
@@ -38473,7 +39041,7 @@ class OnlineServiceResourceInputComponent {
|
|
|
38473
39041
|
provideNgIconsConfig({
|
|
38474
39042
|
size: '1.5em',
|
|
38475
39043
|
}),
|
|
38476
|
-
], ngImport: i0, template: "<div class=\"flex flex-row justify-between\">\n <h3 class=\"text-[16px] font-bold text-main mb-[12px]\" translate>\n editor.record.form.field.onlineResource.edit.protocol\n </h3>\n @if (protocolHint) {\n <ng-icon\n name=\"matHelpOutline\"\n size=\"16\"\n class=\"m-2\"\n [matTooltip]=\"protocolHint\"\n matTooltipPosition=\"above\"\n ></ng-icon>\n }\n</div>\n<div class=\"flex flex-row items-center gap-4 h-[48px]\">\n <mat-radio-group\n aria-labelledby=\"example-radio-group-label\"\n class=\"flex flex-row gap-[8px]\"\n [(ngModel)]=\"_service.accessServiceProtocol\"\n [disabled]=\"disabled\"\n (change)=\"resetAllFormFields()\"\n >\n @for (protocolOption of availableProtocolOptions; track protocolOption) {\n <mat-radio-button [value]=\"protocolOption.value\">\n {{ protocolOption.label | translate }}\n </mat-radio-button>\n }\n </mat-radio-group>\n</div>\n\n<div class=\"flex flex-col gap-4\">\n <gn-ui-url-input\n class=\"w-full\"\n (uploadClick)=\"handleUploadClick($event)\"\n (valueChange)=\"handleUrlValueChange($event)\"\n [disabled]=\"disabled\"\n [value]=\"_service.url?.toString()\"\n [showValidateButton]=\"activeLayerSuggestion\"\n [resetUrlOnChange]=\"resetUrlOnChange\"\n >\n @if (activeLayerSuggestion) {\n <ng-content>\n <ng-icon name=\"iconoirRefresh\"></ng-icon>\n </ng-content>\n }\n </gn-ui-url-input>\n\n @if (loading) {\n <div class=\"flex justify-center w-full py-4\">\n <gn-ui-spinning-loader></gn-ui-spinning-loader>\n </div>\n }\n @if (errorMessage) {\n <p class=\"text-sm text-red-500 pl-4\" translate>\n editor.record.form.field.onlineResource.edit.identifier.error\n </p>\n }\n\n @if (\n !activeLayerSuggestion ||\n (_service.url && errorMessage) ||\n (modifyMode && (layers$ | async).length === 0)\n ) {\n <gn-ui-text-input\n class=\"grow border-b border-gray-300 pb-4\"\n [(value)]=\"_service.identifierInService\"\n [placeholder]=\"getIdentifierPlaceholder() | translate\"\n data-cy=\"identifier-in-service\"\n [disabled]=\"disabled\"\n ></gn-ui-text-input>\n }\n @if (activeLayerSuggestion && (layers$ | async)?.length > 0) {\n <gn-ui-autocomplete\n class=\"border-b border-gray-300 pb-4\"\n [placeholder]=\"\n 'editor.record.form.field.onlineResource.edit.identifier.select.label'\n | translate\n \"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"handleSelectValue($event)\"\n [displayWithFn]=\"displayWithFn\"\n [minCharacterCount]=\"0\"\n [clearOnSelection]=\"false\"\n >\n </gn-ui-autocomplete>\n }\n @if (\n !modifyMode &&\n ((activeLayerSuggestion && (layers$ | async)) ||\n !activeLayerSuggestion ||\n (_service.url && errorMessage))\n ) {\n <gn-ui-button\n (buttonClick)=\"submit()\"\n [disabled]=\"disabled || !_service.identifierInService\"\n type=\"primary\"\n >\n <span class=\"text-white font-bold\" translate>\n editor.record.form.field.onlineResource.edit.identifier.submit</span\n >\n </gn-ui-button>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i1$5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$1.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$1.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: SpinningLoaderComponent, selector: "gn-ui-spinning-loader" }, { kind: "component", type: TextInputComponent, selector: "gn-ui-text-input", inputs: ["value", "extraClass", "placeholder", "required", "disabled", "type"], outputs: ["valueChange"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: UrlInputComponent, selector: "gn-ui-url-input", inputs: ["value", "extraClass", "placeholder", "disabled", "showValidateButton", "resetUrlOnChange"], outputs: ["valueChange", "uploadClick"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
39044
|
+
], ngImport: i0, template: "<div class=\"flex flex-row justify-between\">\n <h3 class=\"text-[16px] font-bold text-main mb-[12px]\" translate>\n editor.record.form.field.onlineResource.edit.protocol\n </h3>\n @if (protocolHint) {\n <ng-icon\n name=\"matHelpOutline\"\n size=\"16\"\n class=\"m-2\"\n [matTooltip]=\"protocolHint\"\n matTooltipPosition=\"above\"\n ></ng-icon>\n }\n</div>\n<div class=\"flex flex-row items-center gap-4 h-[48px]\">\n <mat-radio-group\n aria-labelledby=\"example-radio-group-label\"\n class=\"flex flex-row gap-[8px]\"\n [(ngModel)]=\"_service.accessServiceProtocol\"\n [disabled]=\"disabled\"\n (change)=\"resetAllFormFields()\"\n >\n @for (protocolOption of availableProtocolOptions; track protocolOption) {\n <mat-radio-button [value]=\"protocolOption.value\">\n {{ protocolOption.label | translate }}\n </mat-radio-button>\n }\n </mat-radio-group>\n</div>\n\n<div class=\"flex flex-col gap-4\">\n <gn-ui-url-input\n class=\"w-full\"\n (uploadClick)=\"handleUploadClick($event)\"\n (valueChange)=\"handleUrlValueChange($event)\"\n [disabled]=\"disabled\"\n [value]=\"_service.url?.toString()\"\n [showValidateButton]=\"activeLayerSuggestion\"\n [resetUrlOnChange]=\"resetUrlOnChange\"\n >\n @if (activeLayerSuggestion) {\n <ng-content>\n <ng-icon name=\"iconoirRefresh\"></ng-icon>\n </ng-content>\n }\n </gn-ui-url-input>\n\n @if (loading) {\n <div class=\"flex justify-center w-full py-4\">\n <gn-ui-spinning-loader></gn-ui-spinning-loader>\n </div>\n }\n @if (errorMessage) {\n <p class=\"text-sm text-red-500 pl-4\" translate>\n editor.record.form.field.onlineResource.edit.identifier.error\n </p>\n }\n\n @if (\n !activeLayerSuggestion ||\n (_service.url && errorMessage) ||\n (modifyMode && (layers$ | async).length === 0)\n ) {\n <gn-ui-text-input\n class=\"grow border-b border-gray-300 pb-4\"\n [(value)]=\"_service.identifierInService\"\n [placeholder]=\"getIdentifierPlaceholder() | translate\"\n data-cy=\"identifier-in-service\"\n [disabled]=\"disabled\"\n ></gn-ui-text-input>\n }\n @if (activeLayerSuggestion && (layers$ | async)?.length > 0) {\n <gn-ui-autocomplete\n class=\"border-b border-gray-300 pb-4\"\n [placeholder]=\"\n 'editor.record.form.field.onlineResource.edit.identifier.select.label'\n | translate\n \"\n [action]=\"autoCompleteAction\"\n (itemSelected)=\"handleSelectValue($event)\"\n [displayWithFn]=\"displayWithFn\"\n [minCharacterCount]=\"0\"\n [clearOnSelection]=\"false\"\n >\n </gn-ui-autocomplete>\n }\n @if (\n !modifyMode &&\n ((activeLayerSuggestion && (layers$ | async)) ||\n !activeLayerSuggestion ||\n (_service.url && errorMessage))\n ) {\n <gn-ui-button\n (buttonClick)=\"submit()\"\n [disabled]=\"disabled || !_service.identifierInService\"\n type=\"primary\"\n >\n <span class=\"text-white font-bold\" translate>\n editor.record.form.field.onlineResource.edit.identifier.submit</span\n >\n </gn-ui-button>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: AutocompleteComponent, selector: "gn-ui-autocomplete", inputs: ["placeholder", "enterButton", "action", "value", "clearOnSelection", "preventCompleteOnSelection", "autoFocus", "minCharacterCount", "allowSubmit", "forceTrackPosition", "displayWithTemplate", "displayWithFn"], outputs: ["itemSelected", "inputSubmitted", "inputCleared", "isSearchActive"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i1$5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$1.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$1.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: SpinningLoaderComponent, selector: "gn-ui-spinning-loader" }, { kind: "component", type: TextInputComponent, selector: "gn-ui-text-input", inputs: ["value", "extraClass", "placeholder", "required", "disabled", "type"], outputs: ["valueChange"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: UrlInputComponent, selector: "gn-ui-url-input", inputs: ["value", "extraClass", "placeholder", "disabled", "showValidateButton", "resetUrlOnChange"], outputs: ["valueChange", "uploadClick"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
38477
39045
|
}
|
|
38478
39046
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: OnlineServiceResourceInputComponent, decorators: [{
|
|
38479
39047
|
type: Component,
|
|
@@ -39946,7 +40514,7 @@ class RouterService {
|
|
|
39946
40514
|
return ROUTER_ROUTE_ORGANIZATION;
|
|
39947
40515
|
}
|
|
39948
40516
|
getDefaultSort() {
|
|
39949
|
-
return SortByEnum.
|
|
40517
|
+
return SortByEnum.RESOURCE_DATE;
|
|
39950
40518
|
}
|
|
39951
40519
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: RouterService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
39952
40520
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: RouterService, providedIn: 'root' }); }
|
|
@@ -40006,7 +40574,7 @@ function flattenQueryParams(params) {
|
|
|
40006
40574
|
const start = flattened[key].start;
|
|
40007
40575
|
const end = flattened[key].end;
|
|
40008
40576
|
flattened[key] = [
|
|
40009
|
-
`${start ? formatDate(start) : ''}..${formatDate(end)
|
|
40577
|
+
`${start ? formatDate(start) : ''}..${end ? formatDate(end) : ''}`,
|
|
40010
40578
|
];
|
|
40011
40579
|
}
|
|
40012
40580
|
}
|
|
@@ -40363,5 +40931,5 @@ const CHART_TYPE_VALUES = [
|
|
|
40363
40931
|
* Generated bundle index. Do not edit.
|
|
40364
40932
|
*/
|
|
40365
40933
|
|
|
40366
|
-
export { ADD_RESULTS, ADD_SEARCH, AVAILABLE_LICENSES, AbstractAction, AbstractSearchField, ActionMenuComponent, AddLayerFromCatalogComponent, AddLayerRecordPreviewComponent, AddResults, AddSearch, AnchorLinkDirective, ApiCardComponent, ApplicationBannerComponent, AuthService, AutocompleteComponent, AvailableServicesField, AvatarComponent, AvatarServiceInterface, BASEMAP_LAYERS, BadgeComponent, BaseConverter, BaseFileReader, BaseReader, BlockListComponent, ButtonComponent, CHART_TYPE_VALUES, CLEAR_ERROR, CLEAR_RESULTS, CarouselComponent, CatalogTitleComponent, CellPopinComponent, ChartComponent, ChartViewComponent, CheckToggleComponent, CheckboxComponent, ClearError, ClearResults, ColorScaleComponent, ConfirmationDialogComponent, ContactDetailsComponent, ContactDetailsFormComponent, ContactPillComponent, ContentGhostComponent, CopyTextButtonComponent, DEFAULT_CONFIGURATION, DEFAULT_GN4_LOGIN_URL, DEFAULT_GN4_LOGOUT_URL, DEFAULT_GN4_SETTINGS_URL, DEFAULT_LANG, DEFAULT_PAGE_SIZE, DEFAULT_RECORD_CONVERTER, DEFAULT_RESULTS_LAYOUT_CONFIG, DEFAULT_SEARCH_KEY, DEFAULT_SPATIAL_EXTENT_STYLE, DISABLE_AUTH, DISABLE_DRAFT, DO_NOT_USE_DEFAULT_BASEMAP, DataService, DataTableComponent, DataViewComponent, DataViewPermalinkComponent, DataViewShareComponent, DataViewWebComponentComponent, DatePickerComponent, DateRangeDropdownComponent, DateRangeInputsComponent, DateRangePickerComponent, DateRangeSearchField, DateService, DcatApConverter, DefaultRouterModule, DownloadItemComponent, DownloadsListComponent, DragAndDropFileInputComponent, DropdownMultiselectComponent, DropdownSelectorComponent, EDITOR_FEATURE_KEY, ES_QUERY_FIELDS_PRIORITY, ES_RESOURCES_VALUES, ES_SOURCE_SUMMARY, EXTERNAL_VIEWER_OPEN_NEW_TAB, EXTERNAL_VIEWER_URL_TEMPLATE, EditableLabelDirective, EditorFacade, EditorService, ElasticsearchService, ErrorComponent, ErrorType, ExpandablePanelButtonComponent, ExpandablePanelComponent, ExternalLinkCardComponent, ExternalViewerButtonComponent, FIELDS_BRIEF, FIELDS_SUMMARY, FILTER_GEOMETRY, FILTER_SUMMARY_IGNORE_LIST, FORMATS, FacetBlockComponent, FacetItemComponent, FacetListComponent, FacetsContainerComponent, FavoriteStarComponent, FavoritesService, FeatureCatalogListComponent, FeatureDetailComponent, FeatureEditorModule, FeatureMapModule, FeatureRecordModule, FeatureSearchModule, FetchError, FieldFocusDirective, FieldsService, FigureComponent, FigureContainerComponent, FileInputComponent, FileTranslateLoader, FilesDropDirective, FilterDropdownComponent, FormFieldArrayComponent, FormFieldComponent, FormFieldDateComponent, FormFieldFileComponent, FormFieldKeywordsComponent, FormFieldLicenseComponent, FormFieldObjectComponent, FormFieldRichComponent, FormFieldSimpleComponent, FormFieldSpatialExtentComponent, FormFieldTemporalExtentsComponent, FormFieldTopicsComponent, FormFieldWrapperComponent, FullTextSearchField, FuzzySearchComponent, GEONETWORK_UI_TAG_NAME, GEONETWORK_UI_VERSION, GeoDataBadgeComponent, GeoTableViewComponent, GeocodingComponent, GeojsonReader, Gn4Converter, Gn4PlatformMapper, Gn4PlatformService, Gn4Repository, Gn4SettingsService, GnUiHumanizeDateDirective, GpfApiDlComponent, GravatarService, I18nInterceptor, ISO_TOPICS, ImageFallbackDirective, ImageInputComponent, ImageOverlayPreviewComponent, ImportRecordComponent, InlineFilterComponent, InteractiveTableColumnComponent, InteractiveTableComponent, InternalLinkCardComponent, IsSpatialSearchField, Iso191153Converter, Iso19139Converter, KeywordBadgeComponent, KindBadgeComponent, LANGUAGES_LIST, LANGUAGE_NAMES, LANGUAGE_STORAGE_KEY, LANG_2_TO_3_MAPPER, LOGIN_URL, LOGOUT_URL, LONLAT_CRS_CODES, LanguageSwitcherComponent, LayersPanelComponent, LicenseSearchField, LinkClassifierService, LinkUsage, LoadingMaskComponent, LogService, MAP_FEATURE_KEY, MAP_VIEW_CONSTRAINTS, MAX_UPLOAD_SIZE_MB, METADATA_LANGUAGE, MapContainerComponent, MapFacade, MapLegendComponent, MapStateContainerComponent, MapStyleService, MapUtilsService, MapViewComponent, MarkdownEditorComponent, MarkdownParserComponent, MaxLinesComponent, mdview_actions as MdViewActions, MdViewFacade, MetadataCatalogComponent, MetadataContactComponent, MetadataDoiComponent, MetadataInfoComponent, MetadataLinkType, MetadataMapperContext, MetadataQualityComponent, MetadataQualityItemComponent, MetadataQualityPanelComponent, ModalDialogComponent, MultilingualPanelComponent, MultilingualSearchField, MyOrgService, NAMESPACES, NOT_APPLICABLE_CONSTRAINT, NOT_KNOWN_CONSTRAINT, NotificationComponent, NotificationsContainerComponent, NotificationsService, OPEN_DATA_LICENSE, ORGANIZATIONS_STRATEGY, ORGANIZATION_PAGE_URL_TOKEN, ORGANIZATION_URL_TOKEN, OnlineResourceCardComponent, OnlineServiceResourceInputComponent, OrganisationPreviewComponent, OrganisationsComponent, OrganisationsFilterComponent, OrganisationsResultComponent, OrganizationSearchField, OrganizationsFromGroupsService, OrganizationsFromMetadataService, OrganizationsServiceInterface, OwnerSearchField, PAGINATE, PARSE_DELIMITER, PATCH_RESULTS_AGGREGATIONS, PROXY_PATH, Paginate, PaginationButtonsComponent, PaginationComponent, PaginationDotsComponent, PatchResultsAggregations, PlatformServiceInterface, PopoverComponent, PopupAlertComponent, PossibleResourceTypes, PossibleResourceTypesDefinition, PreviousNextButtonsComponent, ProgressBarComponent, ProxyService, QUERY_FIELDS, RECORD_DATASET_URL_TOKEN, RECORD_REUSE_URL_TOKEN, RECORD_SERVICE_URL_TOKEN, REQUEST_MORE_ON_AGGREGATION, REQUEST_MORE_RESULTS, REQUEST_NEW_RESULTS, RESULTS_LAYOUT_CONFIG, REUSE_LIGHT_CONFIGURATION, ROUTER_CONFIG, ROUTER_ROUTE_DATASET, ROUTER_ROUTE_ORGANIZATION, ROUTER_ROUTE_REUSE, ROUTER_ROUTE_SEARCH, ROUTER_ROUTE_SERVICE, ROUTER_STATE_KEY, ROUTE_PARAMS, RecordApiFormComponent, RecordFormComponent, RecordKindField, RecordMetaComponent, RecordMetricComponent, RecordPreviewCardComponent, RecordPreviewComponent, RecordPreviewFeedComponent, RecordPreviewListComponent, RecordPreviewRowComponent, RecordPreviewTextComponent, RecordPreviewTitleComponent, RecordStatusValues, RecordsMetricsComponent, RecordsRepositoryInterface, RecordsService, RequestMoreOnAggregation, RequestMoreResults, RequestNewResults, ResourceTypeLegacyField, ResultsHitsContainerComponent, ResultsHitsNumberComponent, ResultsHitsSearchKindComponent, ResultsLayoutComponent, ResultsLayoutConfigItem, ResultsListComponent, ResultsListContainerComponent, ResultsListItemComponent, ResultsTableComponent, ResultsTableContainerComponent, ReusePresentationForms, RoleLabels, RoleValues, RouterEffects, RouterFacade, RouterService, SEARCH_FEATURE_KEY, SETTINGS_URL, SET_CONFIG_AGGREGATIONS, SET_CONFIG_FILTERS, SET_CONFIG_REQUEST_FIELDS, SET_ERROR, SET_FAVORITES_ONLY, SET_FILTERS, SET_INCLUDE_ON_AGGREGATION, SET_PAGE_SIZE, SET_RESULTS_AGGREGATIONS, SET_RESULTS_HITS, SET_RESULTS_LAYOUT, SET_SEARCH, SET_SORT_BY, SET_SPATIAL_FILTER_ENABLED, SPATIAL_SCOPES, SearchEffects, SearchFacade, SearchFeatureCatalogComponent, SearchFiltersSummaryComponent, SearchFiltersSummaryItemComponent, SearchInputComponent, SearchRouterContainerDirective, SearchService, SearchStateContainerDirective, SelectionService, ServiceCapabilitiesComponent, SetConfigAggregations, SetConfigFilters, SetConfigRequestFields, SetError, SetFavoritesOnly, SetFilters, SetIncludeOnAggregation, SetPageSize, SetResultsAggregations, SetResultsHits, SetResultsLayout, SetSearch, SetSortBy, SetSpatialFilterEnabled, SimpleSearchField, SiteTitleComponent, SortByComponent, SortByEnum, SortableListComponent, SourceLabelComponent, SourcesService, SpatialExtentComponent, SpinningLoaderComponent, StacItemsResultGridComponent, StacViewComponent, StarToggleComponent, StickyHeaderComponent, SupportedTypes, SwitchToggleComponent, THUMBNAIL_PLACEHOLDER, TRANSLATE_DEBUG_CONFIG, TRANSLATE_DEFAULT_CONFIG, TRANSLATE_WITH_OVERRIDES_CONFIG, TableViewComponent, TextAreaComponent, TextInputComponent, ThemeService, ThumbnailComponent, TranslatedSearchField, TruncatedTextComponent, UPDATE_CONFIG_AGGREGATIONS, UPDATE_FILTERS, UPDATE_REQUEST_AGGREGATION_TERM, UpdateConfigAggregations, UpdateFilters, UrlInputComponent, UserFeedbackItemComponent, UserPreviewComponent, UserSearchField, VECTOR_STYLE_DEFAULT, ViewportIntersectorComponent, WEB_COMPONENT_EMBEDDER_URL, XmlParseError, _reset, allChildrenElement, appConfigWithTranslationFixture, appendChildTree, appendChildren, assertValidXml, associationTypeValues, bboxToPolygon, blockModelFixture, bytesToMegabytes, canEditRecord, checkFileFormat, clearSelectedFeatures, createChild, createDocument, createElement, createFuzzyFilter, createNestedChild, createNestedElement, createSpatialExtentLayer, currentPage, defaultMapStyleFixture, defaultMapStyleHlFixture, downgradeImage, downsizeImage, draftSaveSuccess, dragPanCondition, dropEmptyTranslations, editorReducer, emptyBlockModelFixture, findChildElement, findChildOrCreate, findChildrenElement, findConverterForDocument, findNestedChildOrCreate, findNestedElement, findNestedElements, findParent, firstChildElement, formatDate, formatUserInfo, getAddressLines, getAllKeysValidator, getArrayItem, getAsArray, getAsUrl, getBadgeColor, getCustomTranslations, getError, getFavoritesOnly, getFileFormat, getFileFormatFromServiceOutput, getFirstValue, getFormatPriority, getGeometryBoundingBox, getGeometryFromGeoJSON, getGlobalConfig, getIndividualDisplayName, getIsMobile, getJsonDataItemsProxy, getKeywordHierarchyPath, getLayers, getLinkId, getLinkLabel, getLinkPriority, getMapContext, getMapContextLayerFromConfig, getMapState, getMetadataQualityConfig, getMimeTypeForFormat, getNamespace, getOptionalEditorConfig, getOptionalMapConfig, getOptionalSearchConfig, getPageSize, getQualityValidators, getResourceType, getReusePresentationForm, getReuseType, getRootElement, getSearchConfigAggregations, getSearchFilters, getSearchResults, getSearchResultsAggregations, getSearchResultsHits, getSearchResultsLayout, getSearchResultsLoading, getSearchSortBy, getSearchState, getSearchStateSearch, getSelectedFeatures, getSpatialFilterEnabled, getTemporalRangeUnion, getThemeConfig, handleScrollOnNavigation, hasRecordChangedSinceDraft, hasRecordChangedSinceDraftSuccess, initSearch, initialEditorState, initialMapState, initialState, isConfigLoaded, isDateRange, isFormatInQueryParam, isPublished, itemModelFixture, kindToCodeListValue, loadAppConfig, malformedConfigFixture, mapConfigFixture, mapContact, mapKeywords, mapLogo, mapOrganization, mapReducer, markRecordAsChanged, matchesNoApplicableConstraint, matchesNoKnownConstraint, megabytesToBytes, mimeTypeToFormat, minimalAppConfigFixture, missingMandatoryConfigFixture, mouseWheelZoomCondition, noDuplicateFileName, okAppConfigFixture, openDataset, openRecord, organizationsServiceFactory, parse, parseXmlString, placeholder, prioritizePageScroll, propagateToDocumentOnly, provideGn4, provideI18n, provideRepositoryUrl, readAttribute, readDataset, readDatasetHeaders, readText, reducer$2 as reducer, reducerSearch, removeAllChildren, removeChildren, removeChildrenByName, removeSearchParams, removeWhitespace, renameElements, saveRecord, saveRecordFailure, saveRecordSuccess, selectCanEditRecord, selectCurrentPage, selectEditorConfig, selectEditorState, selectFallback, selectFallbackFields, selectField, selectHasRecordChanged, selectIsPublished, selectRecord, selectRecordChangedSinceSave, selectRecordSaveError, selectRecordSaving, selectRecordSections, selectRecordSource, selectTranslatedField, selectTranslatedValue, setContext, setCurrentPage, setEditorConfiguration, setFieldVisibility, setFocusedField, setSelectedFeatures, setTextContent, sortByFromString, sortByToString, sortByToStrings, spatialExtentToGeometry, spatialExtentsToFeatureCollection, stripHtml, stripNamespace, toDate, toIndividual, toLang2, toLang3, totalPages, undoRecordDraft, unrecognizedKeysConfigFixture, updateFrequencyCodeValues, updateLanguages, updateRecordField, updateRecordLanguages, wmsLayerFlatten, writeAttribute, wrongLanguageCodeConfigFixture, xmlToString };
|
|
40934
|
+
export { ADD_RESULTS, ADD_SEARCH, AVAILABLE_LICENSES, AbstractAction, AbstractSearchField, ActionMenuComponent, AddLayerFromCatalogComponent, AddLayerRecordPreviewComponent, AddResults, AddSearch, AnchorLinkDirective, ApiCardComponent, ApplicationBannerComponent, AuthService, AutocompleteComponent, AutofocusDirective, AvailableServicesField, AvatarComponent, AvatarServiceInterface, BASEMAP_LAYERS, BadgeComponent, BaseConverter, BaseFileReader, BaseReader, BlockListComponent, BoundingBoxSearchField, ButtonComponent, CHART_TYPE_VALUES, CLEAR_ERROR, CLEAR_RESULTS, CarouselComponent, CatalogTitleComponent, CellPopinComponent, ChartComponent, ChartViewComponent, CheckToggleComponent, CheckboxComponent, ClearError, ClearResults, ColorScaleComponent, ConfirmationDialogComponent, ContactDetailsComponent, ContactDetailsFormComponent, ContactPillComponent, ContentGhostComponent, CopyTextButtonComponent, DEFAULT_BASEMAP_LAYER, DEFAULT_CONFIGURATION, DEFAULT_GN4_LOGIN_URL, DEFAULT_GN4_LOGOUT_URL, DEFAULT_GN4_SETTINGS_URL, DEFAULT_LANG, DEFAULT_PAGE_SIZE, DEFAULT_RECORD_CONVERTER, DEFAULT_RESULTS_LAYOUT_CONFIG, DEFAULT_SEARCH_KEY, DEFAULT_SPATIAL_EXTENT_STYLE, DISABLE_AUTH, DISABLE_DRAFT, DO_NOT_USE_DEFAULT_BASEMAP, DataService, DataTableComponent, DataViewComponent, DataViewPermalinkComponent, DataViewShareComponent, DataViewWebComponentComponent, DatePickerComponent, DateRangeDropdownComponent, DateRangeInputsComponent, DateRangePickerComponent, DateRangeSearchField, DateService, DcatApConverter, DefaultRouterModule, DownloadItemComponent, DownloadsListComponent, DragAndDropFileInputComponent, DropdownMultiselectComponent, DropdownSelectorComponent, EDITOR_FEATURE_KEY, ES_QUERY_FIELDS_PRIORITY, ES_RESOURCES_VALUES, ES_SOURCE_SUMMARY, EXTERNAL_VIEWER_OPEN_NEW_TAB, EXTERNAL_VIEWER_URL_TEMPLATE, EditableLabelDirective, EditorFacade, EditorService, ElasticsearchService, ErrorComponent, ErrorType, ExpandablePanelButtonComponent, ExpandablePanelComponent, ExternalLinkCardComponent, ExternalViewerButtonComponent, FIELDS_BRIEF, FIELDS_SUMMARY, FILTER_GEOMETRY, FILTER_SUMMARY_IGNORE_LIST, FORMATS, FacetBlockComponent, FacetItemComponent, FacetListComponent, FacetsContainerComponent, FavoriteStarComponent, FavoritesService, FeatureCatalogListComponent, FeatureDetailComponent, FeatureEditorModule, FeatureMapModule, FeatureRecordModule, FeatureSearchModule, FetchError, FieldFocusDirective, FieldsService, FigureComponent, FigureContainerComponent, FileInputComponent, FileTranslateLoader, FilesDropDirective, FilterDropdownComponent, FormFieldArrayComponent, FormFieldComponent, FormFieldDateComponent, FormFieldFileComponent, FormFieldKeywordsComponent, FormFieldLicenseComponent, FormFieldObjectComponent, FormFieldRichComponent, FormFieldSimpleComponent, FormFieldSpatialExtentComponent, FormFieldTemporalExtentsComponent, FormFieldTopicsComponent, FormFieldWrapperComponent, FullTextSearchField, FuzzySearchComponent, GEOCODING_PROVIDER, GEONETWORK_UI_TAG_NAME, GEONETWORK_UI_VERSION, GeoDataBadgeComponent, GeoTableViewComponent, GeocodingService, GeojsonReader, Gn4Converter, Gn4PlatformMapper, Gn4PlatformService, Gn4Repository, Gn4SettingsService, GnUiHumanizeDateDirective, GpfApiDlComponent, GravatarService, I18nInterceptor, ISO_TOPICS, ImageFallbackDirective, ImageInputComponent, ImageOverlayPreviewComponent, ImportRecordComponent, InlineFilterComponent, InteractiveTableColumnComponent, InteractiveTableComponent, InternalLinkCardComponent, IsSpatialSearchField, Iso191153Converter, Iso19139Converter, KeywordBadgeComponent, KindBadgeComponent, LANGUAGES_LIST, LANGUAGE_NAMES, LANGUAGE_STORAGE_KEY, LANG_2_TO_3_MAPPER, LOGIN_URL, LOGOUT_URL, LONLAT_CRS_CODES, LanguageSwitcherComponent, LayersPanelComponent, LicenseSearchField, LinkClassifierService, LinkUsage, LoadingMaskComponent, LocationSearchComponent, LogService, MAP_FEATURE_KEY, MAP_VIEW_CONSTRAINTS, MAX_UPLOAD_SIZE_MB, METADATA_LANGUAGE, MapContainerComponent, MapFacade, MapLegendComponent, MapStateContainerComponent, MapStyleService, MapUtilsService, MapViewComponent, MarkdownEditorComponent, MarkdownParserComponent, MaxLinesComponent, mdview_actions as MdViewActions, MdViewFacade, MetadataCatalogComponent, MetadataContactComponent, MetadataDoiComponent, MetadataInfoComponent, MetadataLinkType, MetadataMapperContext, MetadataQualityComponent, MetadataQualityItemComponent, MetadataQualityPanelComponent, ModalDialogComponent, MultilingualPanelComponent, MultilingualSearchField, MyOrgService, NAMESPACES, NOT_APPLICABLE_CONSTRAINT, NOT_KNOWN_CONSTRAINT, NotificationComponent, NotificationsContainerComponent, NotificationsService, OPEN_DATA_LICENSE, ORGANIZATIONS_STRATEGY, ORGANIZATION_PAGE_URL_TOKEN, ORGANIZATION_URL_TOKEN, OnlineResourceCardComponent, OnlineServiceResourceInputComponent, OrganisationPreviewComponent, OrganisationsComponent, OrganisationsFilterComponent, OrganisationsResultComponent, OrganizationSearchField, OrganizationsFromGroupsService, OrganizationsFromMetadataService, OrganizationsServiceInterface, OwnerSearchField, PAGINATE, PARSE_DELIMITER, PATCH_RESULTS_AGGREGATIONS, PROXY_PATH, Paginate, PaginationButtonsComponent, PaginationComponent, PaginationDotsComponent, PatchResultsAggregations, PlatformServiceInterface, PopoverComponent, PopupAlertComponent, PossibleResourceTypes, PossibleResourceTypesDefinition, PreviousNextButtonsComponent, ProgressBarComponent, ProxyService, QUERY_FIELDS, RECORD_DATASET_URL_TOKEN, RECORD_REUSE_URL_TOKEN, RECORD_SERVICE_URL_TOKEN, REQUEST_MORE_ON_AGGREGATION, REQUEST_MORE_RESULTS, REQUEST_NEW_RESULTS, RESULTS_LAYOUT_CONFIG, REUSE_LIGHT_CONFIGURATION, ROUTER_CONFIG, ROUTER_ROUTE_DATASET, ROUTER_ROUTE_ORGANIZATION, ROUTER_ROUTE_REUSE, ROUTER_ROUTE_SEARCH, ROUTER_ROUTE_SERVICE, ROUTER_STATE_KEY, ROUTE_PARAMS, RecordApiFormComponent, RecordFormComponent, RecordKindField, RecordMetaComponent, RecordMetricComponent, RecordPreviewCardComponent, RecordPreviewComponent, RecordPreviewFeedComponent, RecordPreviewListComponent, RecordPreviewRowComponent, RecordPreviewTextComponent, RecordPreviewTitleComponent, RecordStatusValues, RecordsMetricsComponent, RecordsRepositoryInterface, RecordsService, RequestMoreOnAggregation, RequestMoreResults, RequestNewResults, ResourceCreationRevisionDateSearchField, ResourceTypeLegacyField, ResultsHitsContainerComponent, ResultsHitsNumberComponent, ResultsHitsSearchKindComponent, ResultsLayoutComponent, ResultsLayoutConfigItem, ResultsListComponent, ResultsListContainerComponent, ResultsListItemComponent, ResultsTableComponent, ResultsTableContainerComponent, ReusePresentationForms, RoleLabels, RoleValues, RouterEffects, RouterFacade, RouterService, SEARCH_FEATURE_KEY, SETTINGS_URL, SET_CONFIG_AGGREGATIONS, SET_CONFIG_FILTERS, SET_CONFIG_REQUEST_FIELDS, SET_ERROR, SET_FAVORITES_ONLY, SET_FILTERS, SET_INCLUDE_ON_AGGREGATION, SET_PAGE_SIZE, SET_RESULTS_AGGREGATIONS, SET_RESULTS_HITS, SET_RESULTS_LAYOUT, SET_SEARCH, SET_SORT_BY, SET_SPATIAL_FILTER_ENABLED, SPATIAL_SCOPES, SearchEffects, SearchFacade, SearchFeatureCatalogComponent, SearchFiltersSummaryComponent, SearchFiltersSummaryItemComponent, SearchInputComponent, SearchRouterContainerDirective, SearchService, SearchStateContainerDirective, SelectionService, ServiceCapabilitiesComponent, SetConfigAggregations, SetConfigFilters, SetConfigRequestFields, SetError, SetFavoritesOnly, SetFilters, SetIncludeOnAggregation, SetPageSize, SetResultsAggregations, SetResultsHits, SetResultsLayout, SetSearch, SetSortBy, SetSpatialFilterEnabled, SimpleSearchField, SiteTitleComponent, SortByComponent, SortByEnum, SortableListComponent, SourceLabelComponent, SourcesService, SpatialExtentComponent, SpatialExtentDropdownComponent, SpinningLoaderComponent, StacItemsResultGridComponent, StacViewComponent, StarToggleComponent, StickyHeaderComponent, SupportedTypes, SwitchToggleComponent, THUMBNAIL_PLACEHOLDER, TRANSLATE_DEBUG_CONFIG, TRANSLATE_DEFAULT_CONFIG, TRANSLATE_WITH_OVERRIDES_CONFIG, TableViewComponent, TextAreaComponent, TextInputComponent, ThemeService, ThumbnailComponent, TranslatedSearchField, TruncatedTextComponent, UPDATE_CONFIG_AGGREGATIONS, UPDATE_FILTERS, UPDATE_REQUEST_AGGREGATION_TERM, UpdateConfigAggregations, UpdateFilters, UrlInputComponent, UserFeedbackItemComponent, UserPreviewComponent, UserSearchField, VECTOR_STYLE_DEFAULT, ViewportIntersectorComponent, WEB_COMPONENT_EMBEDDER_URL, XmlParseError, _reset, allChildrenElement, appConfigWithTranslationFixture, appendChildTree, appendChildren, assertValidXml, associationTypeValues, bboxToPolygon, blockModelFixture, bytesToMegabytes, canEditRecord, checkFileFormat, clearSelectedFeatures, createChild, createDocument, createElement, createFuzzyFilter, createNestedChild, createNestedElement, createSpatialExtentLayer, currentPage, defaultMapStyleFixture, defaultMapStyleHlFixture, downgradeImage, downsizeImage, draftSaveSuccess, dragPanCondition, dropEmptyTranslations, editorReducer, emptyBlockModelFixture, findChildElement, findChildOrCreate, findChildrenElement, findConverterForDocument, findNestedChildOrCreate, findNestedElement, findNestedElements, findParent, firstChildElement, formatDate, formatUserInfo, getAddressLines, getAllKeysValidator, getArrayItem, getAsArray, getAsUrl, getBadgeColor, getCustomTranslations, getError, getFavoritesOnly, getFileFormat, getFileFormatFromServiceOutput, getFirstValue, getFormatPriority, getGeometryBoundingBox, getGeometryFromGeoJSON, getGlobalConfig, getIndividualDisplayName, getIsMobile, getJsonDataItemsProxy, getKeywordHierarchyPath, getLayers, getLinkId, getLinkLabel, getLinkPriority, getMapContext, getMapContextLayerFromConfig, getMapState, getMetadataQualityConfig, getMimeTypeForFormat, getNamespace, getOptionalEditorConfig, getOptionalMapConfig, getOptionalSearchConfig, getPageSize, getQualityValidators, getResourceType, getReusePresentationForm, getReuseType, getRootElement, getSearchConfigAggregations, getSearchFilters, getSearchResults, getSearchResultsAggregations, getSearchResultsHits, getSearchResultsLayout, getSearchResultsLoading, getSearchSortBy, getSearchState, getSearchStateSearch, getSelectedFeatures, getSpatialFilterEnabled, getTemporalRangeUnion, getThemeConfig, handleScrollOnNavigation, hasRecordChangedSinceDraft, hasRecordChangedSinceDraftSuccess, initSearch, initialEditorState, initialMapState, initialState, isBoundingBox, isConfigLoaded, isDateRange, isFileExtensionValid, isFormatInQueryParam, isPublished, itemModelFixture, kindToCodeListValue, loadAppConfig, malformedConfigFixture, mapConfigFixture, mapContact, mapKeywords, mapLogo, mapOrganization, mapReducer, markRecordAsChanged, matchesNoApplicableConstraint, matchesNoKnownConstraint, megabytesToBytes, mimeTypeToFormat, minimalAppConfigFixture, missingMandatoryConfigFixture, mouseWheelZoomCondition, noDuplicateFileName, okAppConfigFixture, openDataset, openRecord, organizationsServiceFactory, parse, parseXmlString, placeholder, prioritizePageScroll, propagateToDocumentOnly, provideGn4, provideI18n, provideLocalizedDateAdapter, provideRepositoryUrl, readAttribute, readDataset, readDatasetHeaders, readFileAsText, readText, reducer$2 as reducer, reducerSearch, removeAllChildren, removeChildren, removeChildrenByName, removeSearchParams, removeWhitespace, renameElements, saveRecord, saveRecordFailure, saveRecordSuccess, selectCanEditRecord, selectCurrentPage, selectEditorConfig, selectEditorState, selectFallback, selectFallbackFields, selectField, selectHasRecordChanged, selectIsPublished, selectRecord, selectRecordChangedSinceSave, selectRecordSaveError, selectRecordSaving, selectRecordSections, selectRecordSource, selectTranslatedField, selectTranslatedValue, setContext, setCurrentPage, setEditorConfiguration, setFieldVisibility, setFocusedField, setSelectedFeatures, setTextContent, sortByFromString, sortByToString, sortByToStrings, spatialExtentToGeometry, spatialExtentsToFeatureCollection, stripHtml, stripNamespace, toDate, toIndividual, toLang2, toLang3, totalPages, undoRecordDraft, unrecognizedKeysConfigFixture, updateFrequencyCodeValues, updateLanguages, updateRecordField, updateRecordLanguages, wmsLayerFlatten, writeAttribute, wrongLanguageCodeConfigFixture, xmlToString };
|
|
40367
40935
|
//# sourceMappingURL=geonetwork-ui.mjs.map
|