geonetwork-ui 2.11.0-dev.5c2e5295e → 2.11.0-dev.5f9922c2e
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 +1216 -677
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +212 -78
- package/index.d.ts.map +1 -1
- package/package.json +11 -11
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +43 -13
- package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +48 -7
- package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +40 -16
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +12 -0
- package/src/libs/common/domain/src/lib/model/search/filter.model.ts +13 -1
- package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +2 -2
- package/src/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts +3 -2
- package/src/libs/feature/dataviz/src/lib/geo-table-view/geo-table-view.component.ts +2 -1
- package/src/libs/feature/dataviz/src/lib/service/data.service.ts +22 -22
- package/src/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.html +1 -0
- package/src/libs/feature/notifications/src/lib/notifications.service.ts +6 -4
- package/src/libs/feature/record/src/lib/state/mdview.actions.ts +4 -8
- package/src/libs/feature/record/src/lib/state/mdview.effects.ts +7 -16
- package/src/libs/feature/record/src/lib/state/mdview.facade.ts +1 -3
- package/src/libs/feature/record/src/lib/state/mdview.reducer.ts +4 -9
- package/src/libs/feature/record/src/lib/state/mdview.selectors.ts +2 -7
- package/src/libs/feature/router/src/lib/default/state/query-params.utils.ts +1 -1
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +9 -4
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +46 -19
- package/src/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts +17 -8
- package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +3 -1
- package/src/libs/feature/search/src/lib/utils/service/fields.ts +27 -2
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.html +6 -7
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.ts +31 -19
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.fixtures.ts +2 -2
- package/src/libs/ui/inputs/src/index.ts +2 -0
- package/src/libs/ui/inputs/src/lib/date-adapter.providers.ts +30 -0
- package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +3 -24
- package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.css +37 -3
- package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.html +135 -16
- package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.ts +166 -29
- package/src/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.ts +3 -7
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.css +1 -0
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.html +20 -7
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.ts +46 -5
- package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.css +0 -0
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html +117 -0
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts +191 -0
- package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +4 -5
- package/src/libs/util/app-config/src/lib/app-config.ts +3 -0
- package/src/libs/util/app-config/src/lib/model.ts +1 -0
- package/src/libs/util/data-fetcher/src/lib/data-fetcher.ts +19 -22
- package/src/libs/util/data-fetcher/src/lib/engine/duckdb.ts +185 -0
- package/src/libs/util/data-fetcher/src/lib/engine/results.ts +63 -0
- package/src/libs/util/data-fetcher/src/lib/{sql-utils.ts → engine/sql-utils.ts} +28 -13
- package/src/libs/util/data-fetcher/src/lib/model.ts +2 -1
- package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +53 -38
- package/src/libs/util/data-fetcher/src/lib/readers/base.ts +11 -0
- package/src/libs/util/data-fetcher/src/lib/readers/csv.ts +9 -47
- package/src/libs/util/data-fetcher/src/lib/readers/excel.ts +27 -27
- package/src/libs/util/data-fetcher/src/lib/readers/geojson.ts +5 -24
- package/src/libs/util/data-fetcher/src/lib/readers/gml.ts +5 -49
- package/src/libs/util/data-fetcher/src/lib/readers/json.ts +5 -23
- package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +184 -128
- package/src/libs/util/data-fetcher/src/lib/utils.ts +0 -143
- package/src/libs/util/shared/src/index.ts +1 -0
- package/src/libs/util/shared/src/lib/autofocus.directive.ts +26 -0
- package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
- package/src/libs/util/shared/src/lib/utils/file.ts +15 -0
- package/src/libs/util/shared/src/lib/utils/geojson.ts +8 -0
- package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
- package/tailwind.base.css +5 -0
- package/translations/de.json +14 -3
- package/translations/en.json +13 -2
- package/translations/es.json +11 -0
- package/translations/fr.json +14 -3
- package/translations/it.json +12 -1
- package/translations/nl.json +11 -0
- package/translations/pt.json +11 -0
- package/translations/sk.json +12 -1
- package/src/libs/util/data-fetcher/src/lib/readers/base-cache.ts +0 -14
|
@@ -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, takeUntil, EMPTY, mergeMap as mergeMap$1, withLatestFrom as withLatestFrom$1 } 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';
|
|
@@ -41,10 +41,10 @@ import * as TOML from '@ltd/j-toml';
|
|
|
41
41
|
import { Style, Stroke, Fill, Circle } from 'ol/style.js';
|
|
42
42
|
import CircleStyle from 'ol/style/Circle.js';
|
|
43
43
|
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,
|
|
44
|
+
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
45
|
import { MatButtonModule } from '@angular/material/button';
|
|
46
|
-
import * as i1$
|
|
47
|
-
import {
|
|
46
|
+
import * as i1$7 from '@angular/cdk/overlay';
|
|
47
|
+
import { ScrollStrategyOptions, OverlayModule, CdkConnectedOverlay, ScrollDispatcher, Overlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
|
|
48
48
|
import * as i1$3 from '@angular/forms';
|
|
49
49
|
import { UntypedFormControl, ReactiveFormsModule, FormsModule } from '@angular/forms';
|
|
50
50
|
import * as i1$2 from '@angular/material/autocomplete';
|
|
@@ -52,16 +52,18 @@ import { MatAutocompleteModule, MatAutocompleteTrigger, MatAutocomplete } from '
|
|
|
52
52
|
import tippy from 'tippy.js';
|
|
53
53
|
import * as i2 from '@angular/material/progress-spinner';
|
|
54
54
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
55
|
-
import { matClose, matContentCopy,
|
|
55
|
+
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
56
|
import * as i1$4 from '@angular/material/checkbox';
|
|
57
57
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
58
58
|
import * as i1$5 from '@angular/material/tooltip';
|
|
59
59
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
60
|
-
import {
|
|
60
|
+
import { MAT_DATE_LOCALE, MAT_DATE_FORMATS, DateAdapter } from '@angular/material/core';
|
|
61
|
+
import { MAT_DATE_FNS_FORMATS, DateFnsAdapter } from '@angular/material-date-fns-adapter';
|
|
62
|
+
import { enUS } from 'date-fns/locale/en-US';
|
|
61
63
|
import * as i1$6 from '@angular/material/datepicker';
|
|
62
64
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
|
63
|
-
import * as i1$
|
|
64
|
-
import { NgxDropzoneModule } from 'ngx-dropzone';
|
|
65
|
+
import * as i1$8 from 'ngx-dropzone';
|
|
66
|
+
import { NgxDropzoneModule, NgxDropzoneComponent } from 'ngx-dropzone';
|
|
65
67
|
import * as i1$9 from '@angular/material/button-toggle';
|
|
66
68
|
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
|
67
69
|
import { moveItemInArray, CdkDropList, CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop';
|
|
@@ -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) {
|
|
@@ -20342,6 +20381,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
20342
20381
|
}]
|
|
20343
20382
|
}] });
|
|
20344
20383
|
|
|
20384
|
+
class AutofocusDirective {
|
|
20385
|
+
constructor() {
|
|
20386
|
+
this.el = inject(ElementRef);
|
|
20387
|
+
this.injector = inject(Injector);
|
|
20388
|
+
}
|
|
20389
|
+
set autofocus(active) {
|
|
20390
|
+
if (!active)
|
|
20391
|
+
return;
|
|
20392
|
+
afterNextRender(() => this.el.nativeElement.focus(), {
|
|
20393
|
+
injector: this.injector,
|
|
20394
|
+
});
|
|
20395
|
+
}
|
|
20396
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AutofocusDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
20397
|
+
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 }); }
|
|
20398
|
+
}
|
|
20399
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AutofocusDirective, decorators: [{
|
|
20400
|
+
type: Directive,
|
|
20401
|
+
args: [{
|
|
20402
|
+
selector: '[gnUiAutofocus]',
|
|
20403
|
+
standalone: true,
|
|
20404
|
+
}]
|
|
20405
|
+
}], propDecorators: { autofocus: [{
|
|
20406
|
+
type: Input,
|
|
20407
|
+
args: [{ alias: 'gnUiAutofocus', transform: booleanAttribute }]
|
|
20408
|
+
}] } });
|
|
20409
|
+
|
|
20345
20410
|
class ImageFallbackDirective {
|
|
20346
20411
|
constructor() {
|
|
20347
20412
|
this.el = inject(ElementRef);
|
|
@@ -20369,7 +20434,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
20369
20434
|
}] } });
|
|
20370
20435
|
|
|
20371
20436
|
var name = "geonetwork-ui";
|
|
20372
|
-
var version = "2.11.0-dev.
|
|
20437
|
+
var version = "2.11.0-dev.5f9922c2e";
|
|
20373
20438
|
var engines = {
|
|
20374
20439
|
node: ">=24"
|
|
20375
20440
|
};
|
|
@@ -20399,20 +20464,21 @@ var peerDependencies = {
|
|
|
20399
20464
|
"@ngrx/store": "19.x || 20.x || 21.x",
|
|
20400
20465
|
"@ngrx/store-devtools": "19.x || 20.x || 21.x",
|
|
20401
20466
|
"@ngrx/operators": "19.x || 20.x || 21.x",
|
|
20402
|
-
"@ngx-translate/core": "
|
|
20403
|
-
"@ngx-translate/http-loader": "
|
|
20404
|
-
"flag-icons": "
|
|
20467
|
+
"@ngx-translate/core": "17.x",
|
|
20468
|
+
"@ngx-translate/http-loader": "17.x",
|
|
20469
|
+
"flag-icons": "~7.5.0",
|
|
20405
20470
|
rxjs: "7.x",
|
|
20406
20471
|
"zone.js": "*",
|
|
20407
20472
|
tailwindcss: "3.x"
|
|
20408
20473
|
};
|
|
20409
20474
|
var dependencies = {
|
|
20410
20475
|
"@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/
|
|
20476
|
+
"@camptocamp/ogc-client": "1.3.1-dev.200c02a",
|
|
20477
|
+
"@duckdb/duckdb-wasm": "~1.32.0",
|
|
20478
|
+
"@geospatial-sdk/core": "0.0.5-dev.78",
|
|
20479
|
+
"@geospatial-sdk/geocoding": "0.0.5-dev.78",
|
|
20480
|
+
"@geospatial-sdk/legend": "0.0.5-dev.78",
|
|
20481
|
+
"@geospatial-sdk/openlayers": "0.0.5-dev.78",
|
|
20416
20482
|
"@ltd/j-toml": "~1.38.0",
|
|
20417
20483
|
"@messageformat/core": "~3.4.0",
|
|
20418
20484
|
"@ng-icons/core": "~32.5.0",
|
|
@@ -20420,7 +20486,7 @@ var dependencies = {
|
|
|
20420
20486
|
"@ng-icons/material-icons": "~32.5.0",
|
|
20421
20487
|
"@ng-icons/tabler-icons": "~32.4.0",
|
|
20422
20488
|
"@rgrove/parse-xml": "4.2.0",
|
|
20423
|
-
|
|
20489
|
+
"apache-arrow": "~17.0.0",
|
|
20424
20490
|
"chart.js": "~4.5.1",
|
|
20425
20491
|
"chroma-js": "~3.2.0",
|
|
20426
20492
|
"date-fns": "4.1.0",
|
|
@@ -20433,7 +20499,6 @@ var dependencies = {
|
|
|
20433
20499
|
"ngx-dropzone": "~3.1.0",
|
|
20434
20500
|
"ngx-translate-messageformat-compiler": "~7.2.0",
|
|
20435
20501
|
ol: "~10.8.0",
|
|
20436
|
-
papaparse: "~5.5.3",
|
|
20437
20502
|
proj4: "~2.20.4",
|
|
20438
20503
|
rdflib: "~2.3.5",
|
|
20439
20504
|
semver: "~7.7.4",
|
|
@@ -20678,6 +20743,7 @@ class ElasticsearchService {
|
|
|
20678
20743
|
values: uuids,
|
|
20679
20744
|
},
|
|
20680
20745
|
},
|
|
20746
|
+
size: uuids.length,
|
|
20681
20747
|
};
|
|
20682
20748
|
}
|
|
20683
20749
|
getRelatedRecordPayload(record, size = 6, _source = [...ES_SOURCE_SUMMARY, 'createDate']) {
|
|
@@ -20785,7 +20851,13 @@ class ElasticsearchService {
|
|
|
20785
20851
|
isCurrentSearchLang() {
|
|
20786
20852
|
return this.metadataLang === 'current';
|
|
20787
20853
|
}
|
|
20788
|
-
|
|
20854
|
+
findSpatialFilterExtent(filters) {
|
|
20855
|
+
if (typeof filters === 'string') {
|
|
20856
|
+
return undefined;
|
|
20857
|
+
}
|
|
20858
|
+
return Object.values(filters).find(isBoundingBox);
|
|
20859
|
+
}
|
|
20860
|
+
filtersToQuery(filters, spatialFilterExtent = this.findSpatialFilterExtent(filters)) {
|
|
20789
20861
|
const addQuote = (key) => (/^\/.+\/$/.test(key) ? key : `"${key}"`);
|
|
20790
20862
|
const makeQuery = (filter) => {
|
|
20791
20863
|
if (typeof filter === 'string') {
|
|
@@ -20804,7 +20876,9 @@ class ElasticsearchService {
|
|
|
20804
20876
|
? filters
|
|
20805
20877
|
: Object.keys(filters)
|
|
20806
20878
|
.filter((fieldname) => fieldname !== 'gn-ui-crossFieldFilter')
|
|
20879
|
+
.filter((fieldname) => !isBoundingBox(filters[fieldname]))
|
|
20807
20880
|
.filter((fieldname) => !isDateRange(filters[fieldname]))
|
|
20881
|
+
.filter((fieldname) => !Array.isArray(filters[fieldname]))
|
|
20808
20882
|
.filter((fieldname) => filters[fieldname] &&
|
|
20809
20883
|
JSON.stringify(filters[fieldname]) !== '{}')
|
|
20810
20884
|
.map((fieldname) => `${fieldname}:(${makeQuery(filters[fieldname])})`)
|
|
@@ -20840,6 +20914,21 @@ class ElasticsearchService {
|
|
|
20840
20914
|
},
|
|
20841
20915
|
},
|
|
20842
20916
|
},
|
|
20917
|
+
spatialFilterExtent && {
|
|
20918
|
+
geo_shape: {
|
|
20919
|
+
geom: {
|
|
20920
|
+
shape: {
|
|
20921
|
+
type: 'envelope',
|
|
20922
|
+
// spatialFilterExtent is [minX, minY, maxX, maxY]; envelope coordinates are [top-left, bottom-right]
|
|
20923
|
+
coordinates: [
|
|
20924
|
+
[spatialFilterExtent[0], spatialFilterExtent[3]],
|
|
20925
|
+
[spatialFilterExtent[2], spatialFilterExtent[1]],
|
|
20926
|
+
],
|
|
20927
|
+
},
|
|
20928
|
+
relation: 'intersects',
|
|
20929
|
+
},
|
|
20930
|
+
},
|
|
20931
|
+
},
|
|
20843
20932
|
].filter(Boolean);
|
|
20844
20933
|
return queryParts.length > 0 ? queryParts : undefined;
|
|
20845
20934
|
}
|
|
@@ -20866,7 +20955,9 @@ class ElasticsearchService {
|
|
|
20866
20955
|
},
|
|
20867
20956
|
});
|
|
20868
20957
|
}
|
|
20869
|
-
|
|
20958
|
+
// a spatial extent filter takes precedence over the preference geometry for boosting
|
|
20959
|
+
const spatialFilterExtent = this.findSpatialFilterExtent(fieldSearchFilters);
|
|
20960
|
+
const queryFilters = this.filtersToQuery(fieldSearchFilters, spatialFilterExtent);
|
|
20870
20961
|
if (queryFilters) {
|
|
20871
20962
|
filter.push(...queryFilters);
|
|
20872
20963
|
}
|
|
@@ -20877,7 +20968,10 @@ class ElasticsearchService {
|
|
|
20877
20968
|
},
|
|
20878
20969
|
});
|
|
20879
20970
|
}
|
|
20880
|
-
|
|
20971
|
+
const boostGeometry = spatialFilterExtent
|
|
20972
|
+
? bboxToPolygon(spatialFilterExtent)
|
|
20973
|
+
: geometry;
|
|
20974
|
+
if (boostGeometry) {
|
|
20881
20975
|
// boosts applied using the filter geometry:
|
|
20882
20976
|
// * records completely within the geometry receive a boost of 5
|
|
20883
20977
|
// * records intersecting the geometry receive a boost of 2
|
|
@@ -20886,7 +20980,7 @@ class ElasticsearchService {
|
|
|
20886
20980
|
should.push({
|
|
20887
20981
|
geo_shape: {
|
|
20888
20982
|
geom: {
|
|
20889
|
-
shape:
|
|
20983
|
+
shape: boostGeometry,
|
|
20890
20984
|
relation: 'within',
|
|
20891
20985
|
},
|
|
20892
20986
|
boost: 5.0,
|
|
@@ -20894,7 +20988,7 @@ class ElasticsearchService {
|
|
|
20894
20988
|
}, {
|
|
20895
20989
|
geo_shape: {
|
|
20896
20990
|
geom: {
|
|
20897
|
-
shape:
|
|
20991
|
+
shape: boostGeometry,
|
|
20898
20992
|
relation: 'intersects',
|
|
20899
20993
|
},
|
|
20900
20994
|
boost: 2.0,
|
|
@@ -20903,7 +20997,7 @@ class ElasticsearchService {
|
|
|
20903
20997
|
// this will boost the results variably depending on their distance from the given geometry
|
|
20904
20998
|
// note: this takes into account the `location` field of a record; this is generally the center of all spatial extents
|
|
20905
20999
|
// combined, and thus the actual size/coverage of the record spatial extent isn't relevant here
|
|
20906
|
-
const bbox = getGeometryBoundingBox(
|
|
21000
|
+
const bbox = getGeometryBoundingBox(boostGeometry);
|
|
20907
21001
|
const center = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2];
|
|
20908
21002
|
const northToCenter = new LineString([
|
|
20909
21003
|
[center[0], bbox[3]],
|
|
@@ -21229,7 +21323,7 @@ class Gn4Repository {
|
|
|
21229
21323
|
}
|
|
21230
21324
|
getRecord(uniqueIdentifier) {
|
|
21231
21325
|
return this.gn4SearchApi
|
|
21232
|
-
.search('bucket', ['fcats', 'hassources'], JSON.stringify(this.gn4SearchHelper.getMetadataByIdsPayload([uniqueIdentifier])))
|
|
21326
|
+
.search('bucket', ['fcats', 'hassources', 'siblings', 'associated'], JSON.stringify(this.gn4SearchHelper.getMetadataByIdsPayload([uniqueIdentifier])))
|
|
21233
21327
|
.pipe(map$1((results) => results.hits.hits[0]), switchMap((record) => record ? this.gn4Mapper.readRecord(record) : of(null)));
|
|
21234
21328
|
}
|
|
21235
21329
|
getMultipleRecords(uniqueIdentifiers) {
|
|
@@ -21283,19 +21377,28 @@ class Gn4Repository {
|
|
|
21283
21377
|
.search('bucket', null, JSON.stringify(this.gn4SearchHelper.getRelatedRecordPayload(similarTo, 3)))
|
|
21284
21378
|
.pipe(switchMap((results) => this.gn4Mapper.readRecords(results.hits.hits)));
|
|
21285
21379
|
}
|
|
21286
|
-
|
|
21287
|
-
const
|
|
21288
|
-
|
|
21289
|
-
|
|
21290
|
-
|
|
21291
|
-
|
|
21292
|
-
|
|
21293
|
-
|
|
21294
|
-
|
|
21295
|
-
|
|
21296
|
-
|
|
21380
|
+
getLinkedRecords(record) {
|
|
21381
|
+
const siblings = (record.extras?.['siblings'] ?? []);
|
|
21382
|
+
const relations = [
|
|
21383
|
+
['source', (record.extras?.['sourcesIdentifiers'] ?? [])],
|
|
21384
|
+
['sourceOf', (record.extras?.['sourceOfIdentifiers'] ?? [])],
|
|
21385
|
+
['sibling', siblings.map(({ uniqueIdentifier }) => uniqueIdentifier)],
|
|
21386
|
+
[
|
|
21387
|
+
'associated',
|
|
21388
|
+
(record.extras?.['associatedIdentifiers'] ?? []),
|
|
21389
|
+
],
|
|
21390
|
+
];
|
|
21391
|
+
const requested = relations.filter(([, identifiers]) => identifiers.length > 0);
|
|
21392
|
+
if (requested.length === 0) {
|
|
21393
|
+
return of([]);
|
|
21297
21394
|
}
|
|
21298
|
-
return
|
|
21395
|
+
return forkJoin(requested.map(([relation, identifiers]) => this.getMultipleRecords(identifiers).pipe(map$1((records) => (records ?? []).map((record) => ({
|
|
21396
|
+
record,
|
|
21397
|
+
relation,
|
|
21398
|
+
associationType: relation === 'sibling'
|
|
21399
|
+
? siblings.find(({ uniqueIdentifier }) => uniqueIdentifier === record.uniqueIdentifier)?.associationType
|
|
21400
|
+
: undefined,
|
|
21401
|
+
}))), catchError(() => of([]))))).pipe(map$1((groups) => groups.flat()));
|
|
21299
21402
|
}
|
|
21300
21403
|
aggregate(params) {
|
|
21301
21404
|
// if aggregations are empty, return an empty object right away
|
|
@@ -22556,9 +22659,8 @@ const VECTOR_STYLE_DEFAULT = new InjectionToken('vectorStyleDefault', {
|
|
|
22556
22659
|
});
|
|
22557
22660
|
|
|
22558
22661
|
const DEFAULT_BASEMAP_LAYER = {
|
|
22559
|
-
type: '
|
|
22560
|
-
|
|
22561
|
-
attributions: `<span>© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, © <a href="https://carto.com/">Carto</a></span>`,
|
|
22662
|
+
type: 'maplibre-style',
|
|
22663
|
+
styleUrl: `https://basemaps.cartocdn.com/gl/positron-gl-style/style.json`,
|
|
22562
22664
|
};
|
|
22563
22665
|
const DEFAULT_VIEW = {
|
|
22564
22666
|
center: [0, 15],
|
|
@@ -23273,6 +23375,7 @@ function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
23273
23375
|
'search_preset',
|
|
23274
23376
|
'advanced_filters',
|
|
23275
23377
|
'limit',
|
|
23378
|
+
'spatial_extent_max_file_size',
|
|
23276
23379
|
], warnings, errors);
|
|
23277
23380
|
const parsedSearchParams = parseMultiConfigSection(parsed, 'search_preset', ['name'], ['sort', 'filters'], warnings, errors);
|
|
23278
23381
|
searchConfig =
|
|
@@ -23290,6 +23393,7 @@ function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
23290
23393
|
})),
|
|
23291
23394
|
ADVANCED_FILTERS: parsedSearchSection.advanced_filters,
|
|
23292
23395
|
LIMIT: parsedSearchSection.limit,
|
|
23396
|
+
SPATIAL_EXTENT_MAX_FILE_SIZE: parsedSearchSection.spatial_extent_max_file_size,
|
|
23293
23397
|
};
|
|
23294
23398
|
const parsedMetadataQualitySection = parseConfigSection(parsed, 'metadata-quality', [], ['enabled'], warnings, errors);
|
|
23295
23399
|
metadataQualityConfig =
|
|
@@ -24547,12 +24651,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24547
24651
|
type: Input
|
|
24548
24652
|
}] } });
|
|
24549
24653
|
|
|
24654
|
+
function provideLocalizedDateAdapter() {
|
|
24655
|
+
return [
|
|
24656
|
+
{ provide: MAT_DATE_LOCALE, useValue: enUS },
|
|
24657
|
+
{ provide: MAT_DATE_FORMATS, useValue: MAT_DATE_FNS_FORMATS },
|
|
24658
|
+
{
|
|
24659
|
+
provide: DateAdapter,
|
|
24660
|
+
useFactory: () => {
|
|
24661
|
+
const dateService = inject(DateService);
|
|
24662
|
+
const adapter = new DateFnsAdapter();
|
|
24663
|
+
dateService
|
|
24664
|
+
.getDateFnsLocale()
|
|
24665
|
+
.then((locale) => adapter.setLocale(locale));
|
|
24666
|
+
return adapter;
|
|
24667
|
+
},
|
|
24668
|
+
},
|
|
24669
|
+
];
|
|
24670
|
+
}
|
|
24671
|
+
|
|
24550
24672
|
class DatePickerComponent {
|
|
24551
24673
|
constructor() {
|
|
24552
|
-
this.dateAdapter = inject(DateAdapter);
|
|
24553
|
-
this.translate = inject(TranslateService);
|
|
24554
24674
|
this.dateChange = new EventEmitter();
|
|
24555
|
-
this.dateAdapter.setLocale(this.translate.getCurrentLang());
|
|
24556
24675
|
}
|
|
24557
24676
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
24558
24677
|
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: [
|
|
@@ -24560,30 +24679,19 @@ class DatePickerComponent {
|
|
|
24560
24679
|
provideNgIconsConfig({
|
|
24561
24680
|
size: '1.5rem',
|
|
24562
24681
|
}),
|
|
24563
|
-
|
|
24564
|
-
|
|
24565
|
-
useFactory: (locale) => locale,
|
|
24566
|
-
},
|
|
24567
|
-
], 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 }); }
|
|
24682
|
+
provideLocalizedDateAdapter(),
|
|
24683
|
+
], 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 }); }
|
|
24568
24684
|
}
|
|
24569
24685
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatePickerComponent, decorators: [{
|
|
24570
24686
|
type: Component,
|
|
24571
|
-
args: [{ selector: 'gn-ui-date-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
24572
|
-
MatNativeDateModule,
|
|
24573
|
-
MatDatepickerModule,
|
|
24574
|
-
ButtonComponent,
|
|
24575
|
-
NgIconComponent,
|
|
24576
|
-
], providers: [
|
|
24687
|
+
args: [{ selector: 'gn-ui-date-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [MatDatepickerModule, ButtonComponent, NgIconComponent], providers: [
|
|
24577
24688
|
provideIcons({ iconoirCalendar }),
|
|
24578
24689
|
provideNgIconsConfig({
|
|
24579
24690
|
size: '1.5rem',
|
|
24580
24691
|
}),
|
|
24581
|
-
|
|
24582
|
-
provide: MAT_DATE_LOCALE,
|
|
24583
|
-
useFactory: (locale) => locale,
|
|
24584
|
-
},
|
|
24692
|
+
provideLocalizedDateAdapter(),
|
|
24585
24693
|
], 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"] }]
|
|
24586
|
-
}],
|
|
24694
|
+
}], propDecorators: { date: [{
|
|
24587
24695
|
type: Input
|
|
24588
24696
|
}], dateChange: [{
|
|
24589
24697
|
type: Output
|
|
@@ -24591,58 +24699,159 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24591
24699
|
|
|
24592
24700
|
class DateRangeDropdownComponent {
|
|
24593
24701
|
constructor() {
|
|
24594
|
-
this.
|
|
24702
|
+
this.scrollStrategies = inject(ScrollStrategyOptions);
|
|
24703
|
+
this.dateAdapter = inject(DateAdapter);
|
|
24704
|
+
this.dateFormats = inject(MAT_DATE_FORMATS);
|
|
24595
24705
|
this.cdr = inject(ChangeDetectorRef);
|
|
24596
|
-
this.
|
|
24597
|
-
this.
|
|
24598
|
-
this.
|
|
24706
|
+
this.dateRange = {};
|
|
24707
|
+
this.dateRangeChange = new EventEmitter();
|
|
24708
|
+
this.overlayPositions = [
|
|
24709
|
+
{
|
|
24710
|
+
originX: 'start',
|
|
24711
|
+
originY: 'bottom',
|
|
24712
|
+
overlayX: 'start',
|
|
24713
|
+
overlayY: 'top',
|
|
24714
|
+
offsetY: 8,
|
|
24715
|
+
},
|
|
24716
|
+
{
|
|
24717
|
+
originX: 'start',
|
|
24718
|
+
originY: 'top',
|
|
24719
|
+
overlayX: 'start',
|
|
24720
|
+
overlayY: 'bottom',
|
|
24721
|
+
offsetY: -8,
|
|
24722
|
+
},
|
|
24723
|
+
];
|
|
24724
|
+
this.scrollStrategy = this.scrollStrategies.reposition();
|
|
24725
|
+
this.overlayOpen = false;
|
|
24726
|
+
this.expandedBound = 'start';
|
|
24727
|
+
this.invalidBounds = { start: false, end: false };
|
|
24728
|
+
// redraw when the lazily loaded UI locale reaches the adapter
|
|
24729
|
+
this.dateAdapter.localeChanges
|
|
24730
|
+
.pipe(takeUntilDestroyed())
|
|
24731
|
+
.subscribe(() => this.cdr.markForCheck());
|
|
24599
24732
|
}
|
|
24600
|
-
|
|
24601
|
-
this.
|
|
24733
|
+
get startDate() {
|
|
24734
|
+
return this.dateRange.start;
|
|
24602
24735
|
}
|
|
24603
|
-
|
|
24604
|
-
|
|
24605
|
-
|
|
24606
|
-
|
|
24607
|
-
|
|
24608
|
-
|
|
24609
|
-
|
|
24610
|
-
|
|
24736
|
+
get endDate() {
|
|
24737
|
+
return this.dateRange.end;
|
|
24738
|
+
}
|
|
24739
|
+
get selectedDatesCount() {
|
|
24740
|
+
return (this.startDate ? 1 : 0) + (this.endDate ? 1 : 0);
|
|
24741
|
+
}
|
|
24742
|
+
openOverlay() {
|
|
24743
|
+
this.expandedBound = 'start';
|
|
24744
|
+
this.overlayOpen = true;
|
|
24745
|
+
}
|
|
24746
|
+
closeOverlay() {
|
|
24747
|
+
this.overlayOpen = false;
|
|
24748
|
+
}
|
|
24749
|
+
toggleBound(bound) {
|
|
24750
|
+
this.expandedBound = this.expandedBound === bound ? null : bound;
|
|
24751
|
+
}
|
|
24752
|
+
formatDate(date) {
|
|
24753
|
+
return this.dateAdapter.format(date, this.dateFormats.display.dateInput);
|
|
24754
|
+
}
|
|
24755
|
+
onDateInput(bound, event) {
|
|
24756
|
+
const typedText = event.target.value.trim();
|
|
24757
|
+
const date = typedText
|
|
24758
|
+
? this.dateAdapter.parse(typedText, this.dateFormats.parse.dateInput)
|
|
24759
|
+
: null;
|
|
24760
|
+
if (!this.isAcceptable(bound, typedText, date)) {
|
|
24761
|
+
// keep the text as typed and flag it; the filter stays on its last value
|
|
24762
|
+
this.invalidBounds = { ...this.invalidBounds, [bound]: true };
|
|
24763
|
+
return;
|
|
24764
|
+
}
|
|
24765
|
+
if (bound === 'start')
|
|
24766
|
+
this.setStartDate(date);
|
|
24767
|
+
else
|
|
24768
|
+
this.setEndDate(date);
|
|
24769
|
+
}
|
|
24770
|
+
normalizeDateInput(bound, event) {
|
|
24771
|
+
if (this.invalidBounds[bound])
|
|
24772
|
+
return;
|
|
24773
|
+
const date = bound === 'start' ? this.startDate : this.endDate;
|
|
24774
|
+
const input = event.target;
|
|
24775
|
+
input.value = date ? this.formatDate(date) : '';
|
|
24776
|
+
}
|
|
24777
|
+
isAcceptable(bound, typedText, date) {
|
|
24778
|
+
if (!typedText)
|
|
24779
|
+
return true;
|
|
24780
|
+
if (!date || !this.dateAdapter.isValid(date))
|
|
24781
|
+
return false;
|
|
24782
|
+
return bound === 'start'
|
|
24783
|
+
? !this.endDate || date <= this.endDate
|
|
24784
|
+
: !this.startDate || date >= this.startDate;
|
|
24785
|
+
}
|
|
24786
|
+
setStartDate(date) {
|
|
24787
|
+
this.invalidBounds = { ...this.invalidBounds, start: false };
|
|
24788
|
+
this.applyDateRange({ ...this.dateRange, start: date });
|
|
24789
|
+
this.expandedBound = 'end';
|
|
24790
|
+
}
|
|
24791
|
+
setEndDate(date) {
|
|
24792
|
+
this.invalidBounds = { ...this.invalidBounds, end: false };
|
|
24793
|
+
this.applyDateRange({ ...this.dateRange, end: date });
|
|
24794
|
+
this.expandedBound = null;
|
|
24795
|
+
}
|
|
24796
|
+
clearDates(event) {
|
|
24797
|
+
this.invalidBounds = { start: false, end: false };
|
|
24798
|
+
this.expandedBound = 'start';
|
|
24799
|
+
this.applyDateRange({});
|
|
24800
|
+
propagateToDocumentOnly(event);
|
|
24801
|
+
}
|
|
24802
|
+
applyDateRange(dateRange) {
|
|
24803
|
+
this.dateRange = {
|
|
24804
|
+
...(dateRange.start && { start: dateRange.start }),
|
|
24805
|
+
...(dateRange.end && { end: dateRange.end }),
|
|
24806
|
+
};
|
|
24807
|
+
this.dateRangeChange.emit(this.dateRange);
|
|
24611
24808
|
}
|
|
24612
24809
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangeDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
24613
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
24810
|
+
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: [
|
|
24614
24811
|
provideIcons({
|
|
24615
|
-
|
|
24812
|
+
iconoirCalendar,
|
|
24813
|
+
matClose,
|
|
24616
24814
|
matExpandLess,
|
|
24815
|
+
matExpandMore,
|
|
24816
|
+
}),
|
|
24817
|
+
provideNgIconsConfig({
|
|
24818
|
+
size: '1.5rem',
|
|
24617
24819
|
}),
|
|
24618
|
-
|
|
24820
|
+
provideLocalizedDateAdapter(),
|
|
24821
|
+
], 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 }); }
|
|
24619
24822
|
}
|
|
24620
24823
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangeDropdownComponent, decorators: [{
|
|
24621
24824
|
type: Component,
|
|
24622
24825
|
args: [{ selector: 'gn-ui-date-range-dropdown', standalone: true, imports: [
|
|
24623
|
-
|
|
24624
|
-
MatNativeDateModule,
|
|
24625
|
-
MatDatepickerModule,
|
|
24826
|
+
AutofocusDirective,
|
|
24626
24827
|
ButtonComponent,
|
|
24828
|
+
MatDatepickerModule,
|
|
24829
|
+
NgIcon,
|
|
24830
|
+
NgTemplateOutlet,
|
|
24831
|
+
OverlayModule,
|
|
24832
|
+
TranslateDirective,
|
|
24833
|
+
TranslatePipe,
|
|
24627
24834
|
], providers: [
|
|
24628
24835
|
provideIcons({
|
|
24629
|
-
|
|
24836
|
+
iconoirCalendar,
|
|
24837
|
+
matClose,
|
|
24630
24838
|
matExpandLess,
|
|
24839
|
+
matExpandMore,
|
|
24631
24840
|
}),
|
|
24632
|
-
|
|
24633
|
-
|
|
24634
|
-
|
|
24635
|
-
|
|
24841
|
+
provideNgIconsConfig({
|
|
24842
|
+
size: '1.5rem',
|
|
24843
|
+
}),
|
|
24844
|
+
provideLocalizedDateAdapter(),
|
|
24845
|
+
], 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"] }]
|
|
24846
|
+
}], ctorParameters: () => [], propDecorators: { title: [{
|
|
24636
24847
|
type: Input
|
|
24637
|
-
}],
|
|
24848
|
+
}], dateRange: [{
|
|
24638
24849
|
type: Input
|
|
24639
|
-
}],
|
|
24640
|
-
type: Output
|
|
24641
|
-
}], endDateChange: [{
|
|
24850
|
+
}], dateRangeChange: [{
|
|
24642
24851
|
type: Output
|
|
24643
|
-
}],
|
|
24852
|
+
}], overlayOrigin: [{
|
|
24644
24853
|
type: ViewChild,
|
|
24645
|
-
args: ['
|
|
24854
|
+
args: ['overlayOrigin']
|
|
24646
24855
|
}] } });
|
|
24647
24856
|
|
|
24648
24857
|
class DateRangePickerComponent {
|
|
@@ -24656,20 +24865,17 @@ class DateRangePickerComponent {
|
|
|
24656
24865
|
provideNgIconsConfig({
|
|
24657
24866
|
size: '1.5rem',
|
|
24658
24867
|
}),
|
|
24659
|
-
|
|
24868
|
+
provideLocalizedDateAdapter(),
|
|
24869
|
+
], 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 }); }
|
|
24660
24870
|
}
|
|
24661
24871
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangePickerComponent, decorators: [{
|
|
24662
24872
|
type: Component,
|
|
24663
|
-
args: [{ selector: 'gn-ui-date-range-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
24664
|
-
MatNativeDateModule,
|
|
24665
|
-
MatDatepickerModule,
|
|
24666
|
-
ButtonComponent,
|
|
24667
|
-
NgIconComponent,
|
|
24668
|
-
], providers: [
|
|
24873
|
+
args: [{ selector: 'gn-ui-date-range-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [MatDatepickerModule, ButtonComponent, NgIconComponent], providers: [
|
|
24669
24874
|
provideIcons({ iconoirCalendar }),
|
|
24670
24875
|
provideNgIconsConfig({
|
|
24671
24876
|
size: '1.5rem',
|
|
24672
24877
|
}),
|
|
24878
|
+
provideLocalizedDateAdapter(),
|
|
24673
24879
|
], 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"] }]
|
|
24674
24880
|
}], propDecorators: { startDate: [{
|
|
24675
24881
|
type: Input
|
|
@@ -24687,28 +24893,68 @@ class DragAndDropFileInputComponent {
|
|
|
24687
24893
|
constructor() {
|
|
24688
24894
|
this.placeholder = placeholder;
|
|
24689
24895
|
this.accept = '*';
|
|
24896
|
+
this.maxFileSizeMb = null;
|
|
24897
|
+
this.icon = null;
|
|
24898
|
+
this.dropzoneBackgroundColor = null;
|
|
24899
|
+
this.textClass = '';
|
|
24900
|
+
this.extraClass = '';
|
|
24901
|
+
this.showFileName = true;
|
|
24690
24902
|
this.fileChange = new EventEmitter();
|
|
24903
|
+
this.errorChange = new EventEmitter();
|
|
24691
24904
|
this.selectedFile = null;
|
|
24692
24905
|
}
|
|
24693
24906
|
get fileName() {
|
|
24694
24907
|
return this.selectedFile && this.selectedFile.name;
|
|
24695
24908
|
}
|
|
24909
|
+
get maxFileSizeBytes() {
|
|
24910
|
+
return typeof this.maxFileSizeMb === 'number'
|
|
24911
|
+
? megabytesToBytes(this.maxFileSizeMb)
|
|
24912
|
+
: null;
|
|
24913
|
+
}
|
|
24696
24914
|
selectFile(event) {
|
|
24915
|
+
if (event.rejectedFiles?.length) {
|
|
24916
|
+
const reason = event.rejectedFiles[0].reason;
|
|
24917
|
+
this.errorChange.emit(reason === 'size' ? 'file-too-large' : 'invalid-extension');
|
|
24918
|
+
return;
|
|
24919
|
+
}
|
|
24697
24920
|
this.selectedFile = event.addedFiles[0];
|
|
24698
24921
|
this.fileChange.emit(this.selectedFile);
|
|
24699
24922
|
}
|
|
24923
|
+
openFileSelector() {
|
|
24924
|
+
this.dropzone.showFileSelector();
|
|
24925
|
+
}
|
|
24926
|
+
clear() {
|
|
24927
|
+
this.selectedFile = null;
|
|
24928
|
+
}
|
|
24700
24929
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DragAndDropFileInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
24701
|
-
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
|
|
24930
|
+
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"] }] }); }
|
|
24702
24931
|
}
|
|
24703
24932
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DragAndDropFileInputComponent, decorators: [{
|
|
24704
24933
|
type: Component,
|
|
24705
|
-
args: [{ selector: 'gn-ui-drag-and-drop-file-input', standalone: true, imports: [NgxDropzoneModule], template: "<div
|
|
24934
|
+
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"] }]
|
|
24706
24935
|
}], propDecorators: { placeholder: [{
|
|
24707
24936
|
type: Input
|
|
24708
24937
|
}], accept: [{
|
|
24709
24938
|
type: Input
|
|
24939
|
+
}], maxFileSizeMb: [{
|
|
24940
|
+
type: Input
|
|
24941
|
+
}], icon: [{
|
|
24942
|
+
type: Input
|
|
24943
|
+
}], dropzoneBackgroundColor: [{
|
|
24944
|
+
type: Input
|
|
24945
|
+
}], textClass: [{
|
|
24946
|
+
type: Input
|
|
24947
|
+
}], extraClass: [{
|
|
24948
|
+
type: Input
|
|
24949
|
+
}], showFileName: [{
|
|
24950
|
+
type: Input
|
|
24710
24951
|
}], fileChange: [{
|
|
24711
24952
|
type: Output
|
|
24953
|
+
}], errorChange: [{
|
|
24954
|
+
type: Output
|
|
24955
|
+
}], dropzone: [{
|
|
24956
|
+
type: ViewChild,
|
|
24957
|
+
args: [NgxDropzoneComponent]
|
|
24712
24958
|
}] } });
|
|
24713
24959
|
|
|
24714
24960
|
class DropdownMultiselectComponent {
|
|
@@ -24863,7 +25109,7 @@ class DropdownMultiselectComponent {
|
|
|
24863
25109
|
matExpandMore,
|
|
24864
25110
|
matExpandLess,
|
|
24865
25111
|
}),
|
|
24866
|
-
], 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 class=\"h-6 w-6\" data-
|
|
25112
|
+
], 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 class=\"h-6 w-6\" data-test=\"dropdown-clear\">\n @if (hasSelectedChoices && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 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-[2px] 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 mb-1\"\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 }); }
|
|
24867
25113
|
}
|
|
24868
25114
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DropdownMultiselectComponent, decorators: [{
|
|
24869
25115
|
type: Component,
|
|
@@ -24873,7 +25119,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24873
25119
|
matExpandMore,
|
|
24874
25120
|
matExpandLess,
|
|
24875
25121
|
}),
|
|
24876
|
-
], 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 class=\"h-6 w-6\" data-
|
|
25122
|
+
], 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 class=\"h-6 w-6\" data-test=\"dropdown-clear\">\n @if (hasSelectedChoices && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 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-[2px] 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 mb-1\"\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" }]
|
|
24877
25123
|
}], propDecorators: { title: [{
|
|
24878
25124
|
type: Input
|
|
24879
25125
|
}], choices: [{
|
|
@@ -25045,7 +25291,7 @@ class DropdownSelectorComponent {
|
|
|
25045
25291
|
provideNgIconsConfig({
|
|
25046
25292
|
size: '1.5em',
|
|
25047
25293
|
}),
|
|
25048
|
-
], 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$
|
|
25294
|
+
], 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 }); }
|
|
25049
25295
|
}
|
|
25050
25296
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DropdownSelectorComponent, decorators: [{
|
|
25051
25297
|
type: Component,
|
|
@@ -25785,6 +26031,157 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
25785
26031
|
type: Output
|
|
25786
26032
|
}] } });
|
|
25787
26033
|
|
|
26034
|
+
marker('search.filters.spatialExtent.import');
|
|
26035
|
+
marker('search.filters.spatialExtent.helpText');
|
|
26036
|
+
marker('search.filters.spatialExtent.error.title');
|
|
26037
|
+
marker('search.filters.spatialExtent.bboxPrefix');
|
|
26038
|
+
marker('search.filters.spatialExtent.bboxDelete');
|
|
26039
|
+
class SpatialExtentDropdownComponent {
|
|
26040
|
+
constructor() {
|
|
26041
|
+
this.cd = inject(ChangeDetectorRef);
|
|
26042
|
+
this.scrollStrategies = inject(ScrollStrategyOptions);
|
|
26043
|
+
this.maxFileSizeMb = null;
|
|
26044
|
+
this.bboxChange = new EventEmitter();
|
|
26045
|
+
this.errorChange = new EventEmitter();
|
|
26046
|
+
this.bbox = null;
|
|
26047
|
+
this.fileName = '';
|
|
26048
|
+
this.overlayPositions = [
|
|
26049
|
+
{
|
|
26050
|
+
originX: 'start',
|
|
26051
|
+
originY: 'bottom',
|
|
26052
|
+
overlayX: 'start',
|
|
26053
|
+
overlayY: 'top',
|
|
26054
|
+
offsetY: 8,
|
|
26055
|
+
},
|
|
26056
|
+
{
|
|
26057
|
+
originX: 'start',
|
|
26058
|
+
originY: 'top',
|
|
26059
|
+
overlayX: 'start',
|
|
26060
|
+
overlayY: 'bottom',
|
|
26061
|
+
offsetY: -8,
|
|
26062
|
+
},
|
|
26063
|
+
];
|
|
26064
|
+
this.scrollStrategy = this.scrollStrategies.reposition();
|
|
26065
|
+
this.overlayOpen = false;
|
|
26066
|
+
this.overlayMinWidth = 'none';
|
|
26067
|
+
this.errorKey = null;
|
|
26068
|
+
}
|
|
26069
|
+
get hasSelection() {
|
|
26070
|
+
return !!this.bbox;
|
|
26071
|
+
}
|
|
26072
|
+
openOverlay() {
|
|
26073
|
+
this.overlayMinWidth =
|
|
26074
|
+
this.overlayOrigin.elementRef.nativeElement.getBoundingClientRect()
|
|
26075
|
+
.width + 'px';
|
|
26076
|
+
this.overlayOpen = true;
|
|
26077
|
+
}
|
|
26078
|
+
closeOverlay() {
|
|
26079
|
+
this.overlayOpen = false;
|
|
26080
|
+
}
|
|
26081
|
+
toggleOverlay() {
|
|
26082
|
+
if (this.overlayOpen) {
|
|
26083
|
+
this.closeOverlay();
|
|
26084
|
+
}
|
|
26085
|
+
else {
|
|
26086
|
+
this.openOverlay();
|
|
26087
|
+
}
|
|
26088
|
+
}
|
|
26089
|
+
async handleFileSelected(file) {
|
|
26090
|
+
this.errorKey = null;
|
|
26091
|
+
let content;
|
|
26092
|
+
try {
|
|
26093
|
+
content = await readFileAsText(file);
|
|
26094
|
+
const parsed = JSON.parse(content);
|
|
26095
|
+
const geometry = getGeometryFromGeoJSON(parsed);
|
|
26096
|
+
if (!geometry) {
|
|
26097
|
+
this.setError(marker('search.filters.spatialExtent.error.noGeometry'));
|
|
26098
|
+
return;
|
|
26099
|
+
}
|
|
26100
|
+
const bbox = getGeometryBoundingBox(geometry);
|
|
26101
|
+
this.bbox = bbox;
|
|
26102
|
+
this.fileName = file.name;
|
|
26103
|
+
this.bboxChange.emit(bbox);
|
|
26104
|
+
this.cd.markForCheck();
|
|
26105
|
+
}
|
|
26106
|
+
catch {
|
|
26107
|
+
this.setError(marker('search.filters.spatialExtent.error.invalidFormat'));
|
|
26108
|
+
return;
|
|
26109
|
+
}
|
|
26110
|
+
}
|
|
26111
|
+
handleFileError(error) {
|
|
26112
|
+
if (error === 'file-too-large') {
|
|
26113
|
+
this.setError(marker('search.filters.spatialExtent.error.fileTooLarge'), {
|
|
26114
|
+
maxSize: this.maxFileSizeMb,
|
|
26115
|
+
});
|
|
26116
|
+
}
|
|
26117
|
+
else {
|
|
26118
|
+
this.setError(marker('search.filters.spatialExtent.error.invalidFormat'));
|
|
26119
|
+
}
|
|
26120
|
+
}
|
|
26121
|
+
setError(errorKey, params) {
|
|
26122
|
+
this.errorKey = errorKey;
|
|
26123
|
+
this.errorChange.emit({ key: errorKey, params });
|
|
26124
|
+
this.cd.markForCheck();
|
|
26125
|
+
}
|
|
26126
|
+
removeSelection(event) {
|
|
26127
|
+
this.bbox = null;
|
|
26128
|
+
this.fileName = '';
|
|
26129
|
+
this.errorKey = null;
|
|
26130
|
+
this.fileInput?.clear();
|
|
26131
|
+
this.bboxChange.emit(null);
|
|
26132
|
+
propagateToDocumentOnly(event);
|
|
26133
|
+
}
|
|
26134
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
26135
|
+
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" }, outputs: { bboxChange: "bboxChange", errorChange: "errorChange" }, providers: [
|
|
26136
|
+
provideIcons({
|
|
26137
|
+
iconoirCheckCircle,
|
|
26138
|
+
iconoirImport,
|
|
26139
|
+
iconoirSquareDashed,
|
|
26140
|
+
iconoirTrash,
|
|
26141
|
+
matClose,
|
|
26142
|
+
matExpandLess,
|
|
26143
|
+
matExpandMore,
|
|
26144
|
+
}),
|
|
26145
|
+
], 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]=\"\n ('search.filters.spatialExtent.bboxDelete' | translate) +\n ' ' +\n fileName\n \"\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 {{ 'search.filters.spatialExtent.bboxPrefix' | translate }}\n {{ fileName }}\n </span>\n <span class=\"hidden group-hover:block truncate text-sm m-auto\">\n {{ 'search.filters.spatialExtent.bboxDelete' | translate }}\n {{ fileName }}\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 }); }
|
|
26146
|
+
}
|
|
26147
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, decorators: [{
|
|
26148
|
+
type: Component,
|
|
26149
|
+
args: [{ selector: 'gn-ui-spatial-extent-dropdown', standalone: true, imports: [
|
|
26150
|
+
ButtonComponent,
|
|
26151
|
+
NgIcon,
|
|
26152
|
+
OverlayModule,
|
|
26153
|
+
TranslatePipe,
|
|
26154
|
+
DragAndDropFileInputComponent,
|
|
26155
|
+
], providers: [
|
|
26156
|
+
provideIcons({
|
|
26157
|
+
iconoirCheckCircle,
|
|
26158
|
+
iconoirImport,
|
|
26159
|
+
iconoirSquareDashed,
|
|
26160
|
+
iconoirTrash,
|
|
26161
|
+
matClose,
|
|
26162
|
+
matExpandLess,
|
|
26163
|
+
matExpandMore,
|
|
26164
|
+
}),
|
|
26165
|
+
], 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]=\"\n ('search.filters.spatialExtent.bboxDelete' | translate) +\n ' ' +\n fileName\n \"\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 {{ 'search.filters.spatialExtent.bboxPrefix' | translate }}\n {{ fileName }}\n </span>\n <span class=\"hidden group-hover:block truncate text-sm m-auto\">\n {{ 'search.filters.spatialExtent.bboxDelete' | translate }}\n {{ fileName }}\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" }]
|
|
26166
|
+
}], propDecorators: { title: [{
|
|
26167
|
+
type: Input
|
|
26168
|
+
}], maxFileSizeMb: [{
|
|
26169
|
+
type: Input
|
|
26170
|
+
}], bboxChange: [{
|
|
26171
|
+
type: Output
|
|
26172
|
+
}], errorChange: [{
|
|
26173
|
+
type: Output
|
|
26174
|
+
}], overlayOrigin: [{
|
|
26175
|
+
type: ViewChild,
|
|
26176
|
+
args: ['overlayOrigin']
|
|
26177
|
+
}], overlay: [{
|
|
26178
|
+
type: ViewChild,
|
|
26179
|
+
args: [CdkConnectedOverlay]
|
|
26180
|
+
}], fileInput: [{
|
|
26181
|
+
type: ViewChild,
|
|
26182
|
+
args: [DragAndDropFileInputComponent]
|
|
26183
|
+
}] } });
|
|
26184
|
+
|
|
25788
26185
|
class CellPopinComponent {
|
|
25789
26186
|
constructor() {
|
|
25790
26187
|
this.scrollDispatcher = inject(ScrollDispatcher);
|
|
@@ -25861,7 +26258,7 @@ class CellPopinComponent {
|
|
|
25861
26258
|
this.isOpen = false;
|
|
25862
26259
|
}
|
|
25863
26260
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CellPopinComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
25864
|
-
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$
|
|
26261
|
+
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"] }] }); }
|
|
25865
26262
|
}
|
|
25866
26263
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CellPopinComponent, decorators: [{
|
|
25867
26264
|
type: Component,
|
|
@@ -27951,6 +28348,23 @@ class DateRangeSearchField extends SimpleSearchField {
|
|
|
27951
28348
|
return 'dateRange';
|
|
27952
28349
|
}
|
|
27953
28350
|
}
|
|
28351
|
+
class BoundingBoxSearchField extends SimpleSearchField {
|
|
28352
|
+
getAvailableValues() {
|
|
28353
|
+
return of([]);
|
|
28354
|
+
}
|
|
28355
|
+
getFiltersForValues(values) {
|
|
28356
|
+
return of({
|
|
28357
|
+
[this.esFieldName]: values.map(Number),
|
|
28358
|
+
});
|
|
28359
|
+
}
|
|
28360
|
+
getValuesForFilter(filters) {
|
|
28361
|
+
const filter = filters[this.esFieldName];
|
|
28362
|
+
return of(isBoundingBox(filter) ? filter : []);
|
|
28363
|
+
}
|
|
28364
|
+
getType() {
|
|
28365
|
+
return 'spatialExtent';
|
|
28366
|
+
}
|
|
28367
|
+
}
|
|
27954
28368
|
marker('search.filters.availableServices.view');
|
|
27955
28369
|
marker('search.filters.availableServices.download');
|
|
27956
28370
|
class AvailableServicesField extends SimpleSearchField {
|
|
@@ -28095,6 +28509,7 @@ marker('search.filters.producerOrg');
|
|
|
28095
28509
|
marker('search.filters.publisherOrg');
|
|
28096
28510
|
marker('search.filters.user');
|
|
28097
28511
|
marker('search.filters.changeDate');
|
|
28512
|
+
marker('search.filters.spatialExtent');
|
|
28098
28513
|
class FieldsService {
|
|
28099
28514
|
constructor() {
|
|
28100
28515
|
this.injector = inject(Injector);
|
|
@@ -28118,6 +28533,7 @@ class FieldsService {
|
|
|
28118
28533
|
user: new UserSearchField(this.injector),
|
|
28119
28534
|
changeDate: new DateRangeSearchField('changeDate', this.injector, 'desc'),
|
|
28120
28535
|
availableServices: new AvailableServicesField(this.injector),
|
|
28536
|
+
spatialExtent: new BoundingBoxSearchField('spatialExtent', this.injector),
|
|
28121
28537
|
};
|
|
28122
28538
|
}
|
|
28123
28539
|
get supportedFields() {
|
|
@@ -29380,7 +29796,7 @@ class ContactPillComponent {
|
|
|
29380
29796
|
this.overlayOpen = false;
|
|
29381
29797
|
}
|
|
29382
29798
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ContactPillComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
29383
|
-
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$
|
|
29799
|
+
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 }); }
|
|
29384
29800
|
}
|
|
29385
29801
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ContactPillComponent, decorators: [{
|
|
29386
29802
|
type: Component,
|
|
@@ -31302,19 +31718,111 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31302
31718
|
type: Output
|
|
31303
31719
|
}] } });
|
|
31304
31720
|
|
|
31721
|
+
class NotificationsService {
|
|
31722
|
+
constructor() {
|
|
31723
|
+
this.notifications$ = new BehaviorSubject([]);
|
|
31724
|
+
}
|
|
31725
|
+
showNotification(content, timeoutMs, error) {
|
|
31726
|
+
error && console.error(error);
|
|
31727
|
+
const id = Math.floor(Math.random() * 1000000);
|
|
31728
|
+
this.notifications$.next([...this.notifications$.value, { ...content, id }]);
|
|
31729
|
+
if (typeof timeoutMs !== 'undefined') {
|
|
31730
|
+
setTimeout(() => {
|
|
31731
|
+
this.removeNotificationById(id);
|
|
31732
|
+
}, timeoutMs);
|
|
31733
|
+
}
|
|
31734
|
+
return id;
|
|
31735
|
+
}
|
|
31736
|
+
removeNotificationById(id) {
|
|
31737
|
+
this.notifications$.next(this.notifications$.value.filter((n) => n.id !== id));
|
|
31738
|
+
}
|
|
31739
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
31740
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, providedIn: 'root' }); }
|
|
31741
|
+
}
|
|
31742
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, decorators: [{
|
|
31743
|
+
type: Injectable,
|
|
31744
|
+
args: [{
|
|
31745
|
+
providedIn: 'root',
|
|
31746
|
+
}]
|
|
31747
|
+
}] });
|
|
31748
|
+
|
|
31749
|
+
class NotificationsContainerComponent {
|
|
31750
|
+
constructor() {
|
|
31751
|
+
this.notificationsService = inject(NotificationsService);
|
|
31752
|
+
}
|
|
31753
|
+
trackById(index, notification) {
|
|
31754
|
+
return notification.id;
|
|
31755
|
+
}
|
|
31756
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31757
|
+
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: [
|
|
31758
|
+
trigger('enterExit', [
|
|
31759
|
+
transition(':enter', [
|
|
31760
|
+
animate('150ms', keyframes([
|
|
31761
|
+
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31762
|
+
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31763
|
+
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31764
|
+
])),
|
|
31765
|
+
]),
|
|
31766
|
+
transition(':leave', [
|
|
31767
|
+
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31768
|
+
]),
|
|
31769
|
+
]),
|
|
31770
|
+
], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31771
|
+
}
|
|
31772
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, decorators: [{
|
|
31773
|
+
type: Component,
|
|
31774
|
+
args: [{ selector: 'gn-ui-notifications-container', standalone: true, imports: [CommonModule, NotificationComponent], changeDetection: ChangeDetectionStrategy.OnPush, animations: [
|
|
31775
|
+
trigger('enterExit', [
|
|
31776
|
+
transition(':enter', [
|
|
31777
|
+
animate('150ms', keyframes([
|
|
31778
|
+
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31779
|
+
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31780
|
+
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31781
|
+
])),
|
|
31782
|
+
]),
|
|
31783
|
+
transition(':leave', [
|
|
31784
|
+
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31785
|
+
]),
|
|
31786
|
+
]),
|
|
31787
|
+
], 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" }]
|
|
31788
|
+
}] });
|
|
31789
|
+
|
|
31305
31790
|
class FilterDropdownComponent {
|
|
31306
31791
|
constructor() {
|
|
31307
31792
|
this.searchFacade = inject(SearchFacade);
|
|
31308
31793
|
this.searchService = inject(SearchService);
|
|
31309
31794
|
this.fieldsService = inject(FieldsService);
|
|
31795
|
+
this.notificationsService = inject(NotificationsService);
|
|
31796
|
+
this.translateService = inject(TranslateService);
|
|
31797
|
+
this.spatialExtentMaxFileSize = getOptionalSearchConfig()?.SPATIAL_EXTENT_MAX_FILE_SIZE;
|
|
31310
31798
|
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([])));
|
|
31311
|
-
this.selectedDateRange$ = this.selected$.pipe(map$1((
|
|
31799
|
+
this.selectedDateRange$ = this.selected$.pipe(map$1((selected) => (Array.isArray(selected) ? {} : selected)));
|
|
31800
|
+
this.spatialExtentErrorNotificationId = null;
|
|
31312
31801
|
}
|
|
31313
31802
|
onSelectedValues(values) {
|
|
31314
31803
|
this.fieldsService
|
|
31315
31804
|
.buildFiltersFromFieldValues({ [this.fieldName]: values })
|
|
31316
31805
|
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
31317
31806
|
}
|
|
31807
|
+
onBboxChange(bbox) {
|
|
31808
|
+
console.log(bbox);
|
|
31809
|
+
this.clearSpatialExtentErrorNotification();
|
|
31810
|
+
}
|
|
31811
|
+
onSpatialExtentError(error) {
|
|
31812
|
+
this.clearSpatialExtentErrorNotification();
|
|
31813
|
+
this.spatialExtentErrorNotificationId =
|
|
31814
|
+
this.notificationsService.showNotification({
|
|
31815
|
+
type: 'error',
|
|
31816
|
+
title: this.translateService.instant('search.filters.spatialExtent.error.title'),
|
|
31817
|
+
text: this.translateService.instant(error.key, error.params),
|
|
31818
|
+
});
|
|
31819
|
+
}
|
|
31820
|
+
clearSpatialExtentErrorNotification() {
|
|
31821
|
+
if (this.spatialExtentErrorNotificationId === null)
|
|
31822
|
+
return;
|
|
31823
|
+
this.notificationsService.removeNotificationById(this.spatialExtentErrorNotificationId);
|
|
31824
|
+
this.spatialExtentErrorNotificationId = null;
|
|
31825
|
+
}
|
|
31318
31826
|
ngOnInit() {
|
|
31319
31827
|
this.fieldType = this.fieldsService.getFieldType(this.fieldName);
|
|
31320
31828
|
this.choices$ = this.fieldsService.getAvailableValues(this.fieldName).pipe(startWith$1([]), map$1((values) => values.map((v) => ({
|
|
@@ -31322,27 +31830,15 @@ class FilterDropdownComponent {
|
|
|
31322
31830
|
value: v.value.toString(), // converting to string for the dropdown
|
|
31323
31831
|
}))), catchError(() => of([])));
|
|
31324
31832
|
}
|
|
31325
|
-
|
|
31326
|
-
|
|
31327
|
-
|
|
31328
|
-
|
|
31329
|
-
|
|
31330
|
-
|
|
31331
|
-
if (!end)
|
|
31332
|
-
return;
|
|
31333
|
-
this.dateRange = { ...this.dateRange, end };
|
|
31334
|
-
if (this.dateRange.start && this.dateRange.end) {
|
|
31335
|
-
this.fieldsService
|
|
31336
|
-
.buildFiltersFromFieldValues({
|
|
31337
|
-
[this.fieldName]: this.dateRange,
|
|
31338
|
-
})
|
|
31339
|
-
.subscribe((filters) => {
|
|
31340
|
-
return this.searchService.updateFilters(filters);
|
|
31341
|
-
});
|
|
31342
|
-
}
|
|
31833
|
+
onDateRangeChange(dateRange) {
|
|
31834
|
+
this.fieldsService
|
|
31835
|
+
.buildFiltersFromFieldValues({
|
|
31836
|
+
[this.fieldName]: dateRange,
|
|
31837
|
+
})
|
|
31838
|
+
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
31343
31839
|
}
|
|
31344
31840
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31345
|
-
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 [
|
|
31841
|
+
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 ></gn-ui-date-range-dropdown>\n} @else if (fieldType === 'spatialExtent') {\n <gn-ui-spatial-extent-dropdown\n [title]=\"title\"\n [maxFileSizeMb]=\"spatialExtentMaxFileSize\"\n (bboxChange)=\"onBboxChange($event)\"\n (errorChange)=\"onSpatialExtentError($event)\"\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"], outputs: ["bboxChange", "errorChange"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31346
31842
|
}
|
|
31347
31843
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, decorators: [{
|
|
31348
31844
|
type: Component,
|
|
@@ -31350,7 +31846,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31350
31846
|
CommonModule,
|
|
31351
31847
|
DateRangeDropdownComponent,
|
|
31352
31848
|
DropdownMultiselectComponent,
|
|
31353
|
-
|
|
31849
|
+
SpatialExtentDropdownComponent,
|
|
31850
|
+
], template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [dateRange]=\"(selectedDateRange$ | async) ?? {}\"\n (dateRangeChange)=\"onDateRangeChange($event)\"\n ></gn-ui-date-range-dropdown>\n} @else if (fieldType === 'spatialExtent') {\n <gn-ui-spatial-extent-dropdown\n [title]=\"title\"\n [maxFileSizeMb]=\"spatialExtentMaxFileSize\"\n (bboxChange)=\"onBboxChange($event)\"\n (errorChange)=\"onSpatialExtentError($event)\"\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" }]
|
|
31354
31851
|
}], propDecorators: { fieldName: [{
|
|
31355
31852
|
type: Input
|
|
31356
31853
|
}], title: [{
|
|
@@ -31886,74 +32383,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31886
32383
|
args: ['gnUiSearchStateContainer']
|
|
31887
32384
|
}] } });
|
|
31888
32385
|
|
|
31889
|
-
class NotificationsService {
|
|
31890
|
-
constructor() {
|
|
31891
|
-
this.notifications$ = new BehaviorSubject([]);
|
|
31892
|
-
}
|
|
31893
|
-
showNotification(content, timeoutMs, error) {
|
|
31894
|
-
error && console.error(error);
|
|
31895
|
-
const id = Math.floor(Math.random() * 1000000);
|
|
31896
|
-
this.notifications$.next([...this.notifications$.value, { ...content, id }]);
|
|
31897
|
-
if (typeof timeoutMs === 'undefined')
|
|
31898
|
-
return;
|
|
31899
|
-
setTimeout(() => {
|
|
31900
|
-
this.removeNotificationById(id);
|
|
31901
|
-
}, timeoutMs);
|
|
31902
|
-
}
|
|
31903
|
-
removeNotificationById(id) {
|
|
31904
|
-
this.notifications$.next(this.notifications$.value.filter((n) => n.id !== id));
|
|
31905
|
-
}
|
|
31906
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
31907
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, providedIn: 'root' }); }
|
|
31908
|
-
}
|
|
31909
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, decorators: [{
|
|
31910
|
-
type: Injectable,
|
|
31911
|
-
args: [{
|
|
31912
|
-
providedIn: 'root',
|
|
31913
|
-
}]
|
|
31914
|
-
}] });
|
|
31915
|
-
|
|
31916
|
-
class NotificationsContainerComponent {
|
|
31917
|
-
constructor() {
|
|
31918
|
-
this.notificationsService = inject(NotificationsService);
|
|
31919
|
-
}
|
|
31920
|
-
trackById(index, notification) {
|
|
31921
|
-
return notification.id;
|
|
31922
|
-
}
|
|
31923
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31924
|
-
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: [
|
|
31925
|
-
trigger('enterExit', [
|
|
31926
|
-
transition(':enter', [
|
|
31927
|
-
animate('150ms', keyframes([
|
|
31928
|
-
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31929
|
-
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31930
|
-
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31931
|
-
])),
|
|
31932
|
-
]),
|
|
31933
|
-
transition(':leave', [
|
|
31934
|
-
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31935
|
-
]),
|
|
31936
|
-
]),
|
|
31937
|
-
], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31938
|
-
}
|
|
31939
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, decorators: [{
|
|
31940
|
-
type: Component,
|
|
31941
|
-
args: [{ selector: 'gn-ui-notifications-container', standalone: true, imports: [CommonModule, NotificationComponent], changeDetection: ChangeDetectionStrategy.OnPush, animations: [
|
|
31942
|
-
trigger('enterExit', [
|
|
31943
|
-
transition(':enter', [
|
|
31944
|
-
animate('150ms', keyframes([
|
|
31945
|
-
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31946
|
-
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31947
|
-
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31948
|
-
])),
|
|
31949
|
-
]),
|
|
31950
|
-
transition(':leave', [
|
|
31951
|
-
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31952
|
-
]),
|
|
31953
|
-
]),
|
|
31954
|
-
], 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" }]
|
|
31955
|
-
}] });
|
|
31956
|
-
|
|
31957
32386
|
class ResultsTableContainerComponent {
|
|
31958
32387
|
constructor() {
|
|
31959
32388
|
this.searchFacade = inject(SearchFacade);
|
|
@@ -32038,12 +32467,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
32038
32467
|
|
|
32039
32468
|
marker('search.filters.summaryLabel.user');
|
|
32040
32469
|
marker('search.filters.summaryLabel.changeDate');
|
|
32470
|
+
const OPEN_BOUND = '…';
|
|
32041
32471
|
class SearchFiltersSummaryItemComponent {
|
|
32042
32472
|
constructor() {
|
|
32043
32473
|
this.searchFacade = inject(SearchFacade);
|
|
32044
32474
|
this.searchService = inject(SearchService);
|
|
32045
32475
|
this.fieldsService = inject(FieldsService);
|
|
32046
|
-
this.
|
|
32476
|
+
this.dateService = inject(DateService);
|
|
32047
32477
|
this.translate = inject(TranslateService);
|
|
32048
32478
|
this.fieldValues$ = this.searchFacade.searchFilters$.pipe(switchMap((filters) => this.fieldsService.readFieldValuesFromFilters(filters)), map$2((fieldValues) => Array.isArray(fieldValues[this.fieldName])
|
|
32049
32479
|
? fieldValues[this.fieldName]
|
|
@@ -32070,9 +32500,10 @@ class SearchFiltersSummaryItemComponent {
|
|
|
32070
32500
|
getReadableValues(fieldValues) {
|
|
32071
32501
|
return fieldValues.map((value) => {
|
|
32072
32502
|
if (this.fieldType === 'dateRange') {
|
|
32503
|
+
const { start, end } = value;
|
|
32073
32504
|
return {
|
|
32074
32505
|
value,
|
|
32075
|
-
label: `${this.
|
|
32506
|
+
label: `${this.formatBound(start)} - ${this.formatBound(end)}`,
|
|
32076
32507
|
};
|
|
32077
32508
|
}
|
|
32078
32509
|
else if (this.fieldName === 'user') {
|
|
@@ -32083,6 +32514,15 @@ class SearchFiltersSummaryItemComponent {
|
|
|
32083
32514
|
}
|
|
32084
32515
|
});
|
|
32085
32516
|
}
|
|
32517
|
+
formatBound(date) {
|
|
32518
|
+
return date
|
|
32519
|
+
? this.dateService.formatDate(date, {
|
|
32520
|
+
day: '2-digit',
|
|
32521
|
+
month: '2-digit',
|
|
32522
|
+
year: 'numeric',
|
|
32523
|
+
})
|
|
32524
|
+
: OPEN_BOUND;
|
|
32525
|
+
}
|
|
32086
32526
|
async removeFilterValue(fieldValue) {
|
|
32087
32527
|
const currentFieldValues = await firstValueFrom(this.fieldValues$);
|
|
32088
32528
|
const updatedFieldValues = currentFieldValues
|
|
@@ -32095,11 +32535,11 @@ class SearchFiltersSummaryItemComponent {
|
|
|
32095
32535
|
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
32096
32536
|
}
|
|
32097
32537
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SearchFiltersSummaryItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
32098
|
-
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" },
|
|
32538
|
+
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" }] }); }
|
|
32099
32539
|
}
|
|
32100
32540
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SearchFiltersSummaryItemComponent, decorators: [{
|
|
32101
32541
|
type: Component,
|
|
32102
|
-
args: [{ selector: 'gn-ui-search-filters-summary-item', standalone: true, imports: [CommonModule, BadgeComponent],
|
|
32542
|
+
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" }]
|
|
32103
32543
|
}], propDecorators: { fieldName: [{
|
|
32104
32544
|
type: Input
|
|
32105
32545
|
}] } });
|
|
@@ -32343,11 +32783,11 @@ class AddLayerFromFileComponent {
|
|
|
32343
32783
|
}, 5000);
|
|
32344
32784
|
}
|
|
32345
32785
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AddLayerFromFileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
32346
|
-
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" }] }); }
|
|
32786
|
+
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" }] }); }
|
|
32347
32787
|
}
|
|
32348
32788
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AddLayerFromFileComponent, decorators: [{
|
|
32349
32789
|
type: Component,
|
|
32350
|
-
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" }]
|
|
32790
|
+
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" }]
|
|
32351
32791
|
}] });
|
|
32352
32792
|
|
|
32353
32793
|
class LayersPanelComponent {
|
|
@@ -32512,8 +32952,7 @@ const closeMetadata = createAction('[Metadata view] close');
|
|
|
32512
32952
|
Related actions
|
|
32513
32953
|
*/
|
|
32514
32954
|
const setRelated = createAction('[Metadata view] Set related records', props());
|
|
32515
|
-
const
|
|
32516
|
-
const setSourceOf = createAction('[Metadata view] Set has sources', props());
|
|
32955
|
+
const setLinkedRecords = createAction('[Metadata view] Set associated records', props());
|
|
32517
32956
|
/*
|
|
32518
32957
|
ChartConfig actions
|
|
32519
32958
|
*/
|
|
@@ -32545,9 +32984,8 @@ var mdview_actions = /*#__PURE__*/Object.freeze({
|
|
|
32545
32984
|
loadUserFeedbacksSuccess: loadUserFeedbacksSuccess,
|
|
32546
32985
|
setChartConfig: setChartConfig,
|
|
32547
32986
|
setIncompleteMetadata: setIncompleteMetadata,
|
|
32548
|
-
|
|
32549
|
-
|
|
32550
|
-
setSources: setSources
|
|
32987
|
+
setLinkedRecords: setLinkedRecords,
|
|
32988
|
+
setRelated: setRelated
|
|
32551
32989
|
});
|
|
32552
32990
|
|
|
32553
32991
|
const METADATA_VIEW_FEATURE_STATE_KEY = 'metadataView';
|
|
@@ -32590,12 +33028,9 @@ on(loadFullMetadata, (state) => ({
|
|
|
32590
33028
|
on(setRelated, (state, { related }) => ({
|
|
32591
33029
|
...state,
|
|
32592
33030
|
related,
|
|
32593
|
-
})), on(
|
|
32594
|
-
...state,
|
|
32595
|
-
sources,
|
|
32596
|
-
})), on(setSourceOf, (state, { sourceOf }) => ({
|
|
33031
|
+
})), on(setLinkedRecords, (state, { linkedRecords }) => ({
|
|
32597
33032
|
...state,
|
|
32598
|
-
|
|
33033
|
+
linkedRecords,
|
|
32599
33034
|
})),
|
|
32600
33035
|
/*
|
|
32601
33036
|
ChartConfig reducers
|
|
@@ -32657,8 +33092,7 @@ const getMetadataError = createSelector(getMdViewState, (state) => state.error);
|
|
|
32657
33092
|
Related selectors
|
|
32658
33093
|
*/
|
|
32659
33094
|
const getRelated = createSelector(getMdViewState, (state) => state.related);
|
|
32660
|
-
const
|
|
32661
|
-
const getSourceOf = createSelector(getMdViewState, (state) => state.sourceOf);
|
|
33095
|
+
const getLinkedRecords = createSelector(getMdViewState, (state) => state.linkedRecords);
|
|
32662
33096
|
/*
|
|
32663
33097
|
Metadata selectors
|
|
32664
33098
|
*/
|
|
@@ -32759,212 +33193,10 @@ function parseHeaders(httpHeaders) {
|
|
|
32759
33193
|
return result;
|
|
32760
33194
|
}
|
|
32761
33195
|
|
|
32762
|
-
|
|
32763
|
-
|
|
32764
|
-
|
|
32765
|
-
? fileExtensionMatches[1].toLowerCase()
|
|
32766
|
-
: null;
|
|
32767
|
-
// 1. type hint
|
|
32768
|
-
if (typeHint)
|
|
32769
|
-
return Promise.resolve(typeHint);
|
|
32770
|
-
// 2. content-type header
|
|
32771
|
-
const headers = await fetchHeaders(url);
|
|
32772
|
-
if ('supportedType' in headers)
|
|
32773
|
-
return headers.supportedType;
|
|
32774
|
-
// 3. file extension from url
|
|
32775
|
-
else if (SupportedTypes.indexOf(fileExtension) > -1)
|
|
32776
|
-
return fileExtension;
|
|
32777
|
-
// no type inferred or hinted
|
|
32778
|
-
if ('mimeType' in headers)
|
|
32779
|
-
throw FetchError.unsupportedType(headers.mimeType);
|
|
32780
|
-
else
|
|
32781
|
-
throw FetchError.unknownType();
|
|
32782
|
-
}
|
|
32783
|
-
function fetchHeaders(url) {
|
|
32784
|
-
return sharedFetch(url, 'HEAD')
|
|
32785
|
-
.catch((error) => {
|
|
32786
|
-
throw FetchError.corsOrNetwork(error.message);
|
|
32787
|
-
})
|
|
32788
|
-
.then((response) => {
|
|
32789
|
-
if (!response.ok) {
|
|
32790
|
-
throw FetchError.http(response.status);
|
|
32791
|
-
}
|
|
32792
|
-
return parseHeaders(response.headers);
|
|
32793
|
-
});
|
|
33196
|
+
const GEOMETRY_COLUMN_ALIAS = '__geometry__';
|
|
33197
|
+
function fieldToSql(name) {
|
|
33198
|
+
return `"${name.replace(/"/g, '""')}"`; // escape double quotes in field names
|
|
32794
33199
|
}
|
|
32795
|
-
function fetchDataAsText(url, cacheActive) {
|
|
32796
|
-
const fetchFactory = () => sharedFetch(url)
|
|
32797
|
-
.catch((error) => {
|
|
32798
|
-
throw FetchError.corsOrNetwork(error.message);
|
|
32799
|
-
})
|
|
32800
|
-
.then(async (response) => {
|
|
32801
|
-
if (!response.ok) {
|
|
32802
|
-
const clonedResponse = response.clone();
|
|
32803
|
-
throw FetchError.http(response.status, await clonedResponse.text());
|
|
32804
|
-
}
|
|
32805
|
-
const clonedResponse = response.clone();
|
|
32806
|
-
return clonedResponse.text();
|
|
32807
|
-
});
|
|
32808
|
-
return cacheActive ? useCache(fetchFactory, url, 'asText') : fetchFactory();
|
|
32809
|
-
}
|
|
32810
|
-
function fetchDataAsArrayBuffer(url, cacheActive) {
|
|
32811
|
-
const fetchFactory = () => sharedFetch(url)
|
|
32812
|
-
.catch((error) => {
|
|
32813
|
-
throw FetchError.corsOrNetwork(error.message);
|
|
32814
|
-
})
|
|
32815
|
-
.then(async (response) => {
|
|
32816
|
-
if (!response.ok) {
|
|
32817
|
-
throw FetchError.http(response.status, await response.text());
|
|
32818
|
-
}
|
|
32819
|
-
// convert to a numeric array so that we can store the response in cache
|
|
32820
|
-
return Array.from(new Uint8Array(await response.arrayBuffer()));
|
|
32821
|
-
});
|
|
32822
|
-
return (cacheActive ? useCache(fetchFactory, url, 'asArrayBuffer') : fetchFactory()).then((array) => {
|
|
32823
|
-
return new Uint8Array(array).buffer;
|
|
32824
|
-
});
|
|
32825
|
-
}
|
|
32826
|
-
function tryParseDate(input) {
|
|
32827
|
-
if (typeof input !== 'string')
|
|
32828
|
-
return null;
|
|
32829
|
-
function tryIso(value) {
|
|
32830
|
-
const parsed = parseISO(value);
|
|
32831
|
-
return isNaN(parsed.getDate()) ? null : parsed;
|
|
32832
|
-
}
|
|
32833
|
-
function tryFormat(value, format) {
|
|
32834
|
-
const parsed = parse$4(value, format, new Date());
|
|
32835
|
-
return isNaN(parsed.getDate()) ? null : parsed;
|
|
32836
|
-
}
|
|
32837
|
-
return (tryIso(input) ||
|
|
32838
|
-
tryFormat(input, 'dd/MM/yyyy') ||
|
|
32839
|
-
tryFormat(input, 'dd.MM.yyyy') ||
|
|
32840
|
-
tryFormat(input, 'MM/dd/yyyy') ||
|
|
32841
|
-
null);
|
|
32842
|
-
}
|
|
32843
|
-
function tryParseNumber(input) {
|
|
32844
|
-
if (isNaN(input))
|
|
32845
|
-
return null;
|
|
32846
|
-
const parsed = parseFloat(input);
|
|
32847
|
-
return isNaN(parsed) ? null : parsed;
|
|
32848
|
-
}
|
|
32849
|
-
function jsonToGeojsonFeature(object) {
|
|
32850
|
-
const { id, properties } = Object.keys(object)
|
|
32851
|
-
.map((property) => (property ? property : 'unknown')) //prevent empty strings
|
|
32852
|
-
.reduce((prev, curr) => curr.toLowerCase().endsWith('id')
|
|
32853
|
-
? {
|
|
32854
|
-
...prev,
|
|
32855
|
-
id: object[curr],
|
|
32856
|
-
}
|
|
32857
|
-
: {
|
|
32858
|
-
...prev,
|
|
32859
|
-
properties: { ...prev.properties, [curr]: object[curr] },
|
|
32860
|
-
}, { id: undefined, properties: {} });
|
|
32861
|
-
return {
|
|
32862
|
-
type: 'Feature',
|
|
32863
|
-
geometry: null,
|
|
32864
|
-
properties,
|
|
32865
|
-
...(id !== undefined && { id }),
|
|
32866
|
-
};
|
|
32867
|
-
}
|
|
32868
|
-
function mutateProperties(items, mutators) {
|
|
32869
|
-
const mutatorKeys = Object.keys(mutators);
|
|
32870
|
-
for (let i = 0, ii = items.length; i < ii; i++) {
|
|
32871
|
-
const item = items[i];
|
|
32872
|
-
for (const mutatorField of mutatorKeys) {
|
|
32873
|
-
if (!(mutatorField in item.properties))
|
|
32874
|
-
continue;
|
|
32875
|
-
item.properties[mutatorField] = mutators[mutatorField](item.properties[mutatorField]);
|
|
32876
|
-
}
|
|
32877
|
-
}
|
|
32878
|
-
return items;
|
|
32879
|
-
}
|
|
32880
|
-
const SAMPLE_SIZE = 20;
|
|
32881
|
-
/**
|
|
32882
|
-
* This will infer field types from a list of data items and cast the values accordingly
|
|
32883
|
-
* @param items
|
|
32884
|
-
* @param inferTypes
|
|
32885
|
-
*/
|
|
32886
|
-
function processItemProperties(items, inferTypes = false) {
|
|
32887
|
-
const foundFields = {};
|
|
32888
|
-
for (let i = 0, ii = Math.min(SAMPLE_SIZE, items.length); i < ii; i++) {
|
|
32889
|
-
const item = items[i];
|
|
32890
|
-
const fields = Object.keys(item.properties);
|
|
32891
|
-
for (const field of fields) {
|
|
32892
|
-
if (!(field in foundFields)) {
|
|
32893
|
-
foundFields[field] = {
|
|
32894
|
-
label: field,
|
|
32895
|
-
name: field,
|
|
32896
|
-
type: null,
|
|
32897
|
-
};
|
|
32898
|
-
}
|
|
32899
|
-
const value = item.properties[field];
|
|
32900
|
-
const info = foundFields[field];
|
|
32901
|
-
if (value === undefined || value === '' || value === null)
|
|
32902
|
-
continue;
|
|
32903
|
-
if (!inferTypes) {
|
|
32904
|
-
if (info.type === null && typeof value === 'number') {
|
|
32905
|
-
info.type = 'number';
|
|
32906
|
-
}
|
|
32907
|
-
else if (info.type === 'number' && typeof value !== 'number') {
|
|
32908
|
-
info.type = 'string';
|
|
32909
|
-
}
|
|
32910
|
-
continue;
|
|
32911
|
-
}
|
|
32912
|
-
const parsedNumber = tryParseNumber(value);
|
|
32913
|
-
if (info.type === null && parsedNumber !== null) {
|
|
32914
|
-
info.type = 'number';
|
|
32915
|
-
continue;
|
|
32916
|
-
}
|
|
32917
|
-
else if (info.type === 'number' && parsedNumber === null) {
|
|
32918
|
-
info.type = 'string';
|
|
32919
|
-
continue;
|
|
32920
|
-
}
|
|
32921
|
-
const parsedDate = tryParseDate(value);
|
|
32922
|
-
if (info.type === null && parsedDate !== null) {
|
|
32923
|
-
info.type = 'date';
|
|
32924
|
-
}
|
|
32925
|
-
else if (info.type === 'date' && parsedDate === null) {
|
|
32926
|
-
info.type = 'string';
|
|
32927
|
-
}
|
|
32928
|
-
}
|
|
32929
|
-
}
|
|
32930
|
-
const properties = [];
|
|
32931
|
-
const mutators = {};
|
|
32932
|
-
for (const field in foundFields) {
|
|
32933
|
-
const info = foundFields[field];
|
|
32934
|
-
if (info.type === 'number') {
|
|
32935
|
-
mutators[field] = tryParseNumber;
|
|
32936
|
-
}
|
|
32937
|
-
else if (info.type === 'date') {
|
|
32938
|
-
mutators[field] = tryParseDate;
|
|
32939
|
-
}
|
|
32940
|
-
properties.push({ ...info, type: info.type || 'string' });
|
|
32941
|
-
}
|
|
32942
|
-
if (inferTypes) {
|
|
32943
|
-
mutateProperties(items, mutators);
|
|
32944
|
-
}
|
|
32945
|
-
return { items, properties };
|
|
32946
|
-
}
|
|
32947
|
-
/**
|
|
32948
|
-
* This creates a Proxy that allows reading and writing to the data item properties
|
|
32949
|
-
* as if it was a simple array of JSON objects
|
|
32950
|
-
* @param items
|
|
32951
|
-
*/
|
|
32952
|
-
function getJsonDataItemsProxy(items) {
|
|
32953
|
-
return new Proxy(items, {
|
|
32954
|
-
get(target, p) {
|
|
32955
|
-
if (typeof p === 'string' &&
|
|
32956
|
-
!Number.isNaN(parseInt(p)) &&
|
|
32957
|
-
target[p]?.properties) {
|
|
32958
|
-
return target[p].properties;
|
|
32959
|
-
}
|
|
32960
|
-
return target[p];
|
|
32961
|
-
},
|
|
32962
|
-
set() {
|
|
32963
|
-
throw new Error('This object is read-only');
|
|
32964
|
-
},
|
|
32965
|
-
});
|
|
32966
|
-
}
|
|
32967
|
-
|
|
32968
33200
|
function filterToSql(filter) {
|
|
32969
33201
|
const operator = filter[0];
|
|
32970
33202
|
const args = filter.slice(1);
|
|
@@ -32979,10 +33211,10 @@ function filterToSql(filter) {
|
|
|
32979
33211
|
case '=':
|
|
32980
33212
|
case '!=':
|
|
32981
33213
|
case 'like':
|
|
32982
|
-
return
|
|
33214
|
+
return `${fieldToSql(args[0])} ${operator.toUpperCase()} ${valueToSql(args[1])}`;
|
|
32983
33215
|
case 'in': {
|
|
32984
33216
|
const values = args.slice(1);
|
|
32985
|
-
return
|
|
33217
|
+
return `${fieldToSql(args[0])} IN (${values.map(valueToSql).join(', ')})`;
|
|
32986
33218
|
}
|
|
32987
33219
|
case 'and':
|
|
32988
33220
|
case 'or': {
|
|
@@ -33001,17 +33233,18 @@ function aggregationToSql(aggregation) {
|
|
|
33001
33233
|
const field = aggregation[1];
|
|
33002
33234
|
switch (operation) {
|
|
33003
33235
|
case 'average':
|
|
33004
|
-
return `AVG(
|
|
33236
|
+
return `CAST(AVG(${fieldToSql(field)}) AS DOUBLE) as ${fieldToSql(`average(${field})`)}`;
|
|
33005
33237
|
case 'sum':
|
|
33006
33238
|
case 'max':
|
|
33007
33239
|
case 'min':
|
|
33008
|
-
return
|
|
33240
|
+
return `CAST(${operation.toUpperCase()}(${fieldToSql(field)}) AS DOUBLE) as ${fieldToSql(`${operation}(${field})`)}`;
|
|
33009
33241
|
case 'count':
|
|
33010
|
-
return 'COUNT(*) as
|
|
33242
|
+
return 'CAST(COUNT(*) AS INTEGER) as "count()"'; // we don't need Bigint precision here
|
|
33011
33243
|
}
|
|
33012
33244
|
}
|
|
33013
33245
|
/**
|
|
33014
33246
|
* Leave arguments at null if not used
|
|
33247
|
+
* @param tableName
|
|
33015
33248
|
* @param selected
|
|
33016
33249
|
* @param filter
|
|
33017
33250
|
* @param sort
|
|
@@ -33020,22 +33253,25 @@ function aggregationToSql(aggregation) {
|
|
|
33020
33253
|
* @param groupBy
|
|
33021
33254
|
* @param aggregations
|
|
33022
33255
|
*/
|
|
33023
|
-
function generateSqlQuery(selected = null, filter = null, sort = null, startIndex = null, count = null, groupBy = null, aggregations = null) {
|
|
33256
|
+
function generateSqlQuery(tableName, selected = null, filter = null, sort = null, startIndex = null, count = null, groupBy = null, aggregations = null, geometryColumn = null) {
|
|
33024
33257
|
let sqlSelect = 'SELECT *';
|
|
33025
|
-
const sqlFrom =
|
|
33258
|
+
const sqlFrom = ` FROM ${tableName}`;
|
|
33026
33259
|
let sqlOrderBy = '';
|
|
33027
33260
|
let sqlWhere = '';
|
|
33028
33261
|
let sqlLimit = '';
|
|
33029
33262
|
let sqlGroupBy = '';
|
|
33030
33263
|
if (selected !== null) {
|
|
33031
|
-
sqlSelect = `SELECT ${selected.map(
|
|
33264
|
+
sqlSelect = `SELECT ${selected.map(fieldToSql).join(', ')}`;
|
|
33265
|
+
}
|
|
33266
|
+
if (geometryColumn !== null) {
|
|
33267
|
+
sqlSelect += `, ST_AsGeoJSON(${fieldToSql(geometryColumn)}) as ${GEOMETRY_COLUMN_ALIAS}`;
|
|
33032
33268
|
}
|
|
33033
33269
|
if (filter !== null) {
|
|
33034
33270
|
sqlWhere = ` WHERE ${filterToSql(filter)}`;
|
|
33035
33271
|
}
|
|
33036
33272
|
if (sort?.length) {
|
|
33037
33273
|
sqlOrderBy = ` ORDER BY ${sort
|
|
33038
|
-
.map((sort) =>
|
|
33274
|
+
.map((sort) => `${fieldToSql(sort[1])} ${sort[0].toUpperCase()}`)
|
|
33039
33275
|
.join(', ')}`;
|
|
33040
33276
|
}
|
|
33041
33277
|
if (startIndex !== null && count !== null) {
|
|
@@ -33045,17 +33281,212 @@ function generateSqlQuery(selected = null, filter = null, sort = null, startInde
|
|
|
33045
33281
|
sqlSelect = `SELECT ${aggregations.map(aggregationToSql).join(', ')}`;
|
|
33046
33282
|
const groupedByDistinct = groupBy.filter((group) => group[0] === 'distinct');
|
|
33047
33283
|
const sqlGroupByFields = groupedByDistinct
|
|
33048
|
-
.map((group) =>
|
|
33284
|
+
.map((group) => fieldToSql(group[1]))
|
|
33049
33285
|
.join(', ');
|
|
33050
33286
|
const sqlGroupBySelect = groupedByDistinct
|
|
33051
|
-
.map((group) =>
|
|
33287
|
+
.map((group) => `${fieldToSql(group[1])} as ${fieldToSql(`distinct(${group[1]})`)}`)
|
|
33052
33288
|
.join(', ');
|
|
33053
33289
|
if (sqlGroupByFields && sqlGroupBySelect) {
|
|
33054
33290
|
sqlGroupBy = ` GROUP BY ${sqlGroupByFields}`;
|
|
33055
33291
|
sqlSelect += `, ${sqlGroupBySelect}`;
|
|
33056
33292
|
}
|
|
33057
33293
|
}
|
|
33058
|
-
return sqlSelect + sqlFrom + sqlGroupBy +
|
|
33294
|
+
return sqlSelect + sqlFrom + sqlGroupBy + sqlWhere + sqlOrderBy + sqlLimit;
|
|
33295
|
+
}
|
|
33296
|
+
|
|
33297
|
+
function arrowTableToDataItems(table) {
|
|
33298
|
+
const fields = table.schema.fields;
|
|
33299
|
+
return table.toArray().map((row) => {
|
|
33300
|
+
const rowJson = row.toJSON();
|
|
33301
|
+
const feature = {
|
|
33302
|
+
type: 'Feature',
|
|
33303
|
+
geometry: null,
|
|
33304
|
+
properties: {},
|
|
33305
|
+
};
|
|
33306
|
+
const keys = Object.keys(rowJson);
|
|
33307
|
+
for (let i = 0; i < keys.length; i++) {
|
|
33308
|
+
const key = keys[i];
|
|
33309
|
+
const dataType = fields[i].type;
|
|
33310
|
+
let value = rowJson[key];
|
|
33311
|
+
// this might happen if we get an array inside a field
|
|
33312
|
+
if (value instanceof Vector) {
|
|
33313
|
+
value = Array.from(value);
|
|
33314
|
+
}
|
|
33315
|
+
// cast bigints to ints
|
|
33316
|
+
if (typeof value === 'bigint') {
|
|
33317
|
+
value = Number(value);
|
|
33318
|
+
}
|
|
33319
|
+
// rename columns with empty name
|
|
33320
|
+
if (!key) {
|
|
33321
|
+
feature.properties['unknown'] = value;
|
|
33322
|
+
continue;
|
|
33323
|
+
}
|
|
33324
|
+
// assign properties that look like an id to the geojson `id` field
|
|
33325
|
+
if (/^(object|feature)?_?id$/.test(key.toLowerCase()) &&
|
|
33326
|
+
(typeof value == 'string' || typeof value === 'number')) {
|
|
33327
|
+
feature.id = value;
|
|
33328
|
+
}
|
|
33329
|
+
// if a date is in timestamp (number) format, convert it to native
|
|
33330
|
+
if (typeof value === 'number' &&
|
|
33331
|
+
(DataType.isTimestamp(dataType) || DataType.isDate(dataType))) {
|
|
33332
|
+
value = new Date(value);
|
|
33333
|
+
}
|
|
33334
|
+
// if a binary field (most likely a geometry column): skip
|
|
33335
|
+
if (DataType.isBinary(dataType)) {
|
|
33336
|
+
continue;
|
|
33337
|
+
}
|
|
33338
|
+
// geometry column
|
|
33339
|
+
if (key === GEOMETRY_COLUMN_ALIAS && DataType.isUtf8(dataType)) {
|
|
33340
|
+
feature.geometry = JSON.parse(value);
|
|
33341
|
+
continue;
|
|
33342
|
+
}
|
|
33343
|
+
feature.properties[key] = value;
|
|
33344
|
+
}
|
|
33345
|
+
return feature;
|
|
33346
|
+
});
|
|
33347
|
+
}
|
|
33348
|
+
|
|
33349
|
+
// init code taken from https://github.com/duckdb/duckdb-wasm/blob/main/packages/duckdb-wasm/README.md
|
|
33350
|
+
const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();
|
|
33351
|
+
// from https://duckdb.org/docs/current/sql/data_types/overview
|
|
33352
|
+
const typesMapping = {
|
|
33353
|
+
INTEGER: 'number',
|
|
33354
|
+
SMALLINT: 'number',
|
|
33355
|
+
TINYINT: 'number',
|
|
33356
|
+
BIGINT: 'number',
|
|
33357
|
+
HUGEINT: 'number',
|
|
33358
|
+
UINTEGER: 'number',
|
|
33359
|
+
USMALLINT: 'number',
|
|
33360
|
+
UTINYINT: 'number',
|
|
33361
|
+
UBIGINT: 'number',
|
|
33362
|
+
UHUGEINT: 'number',
|
|
33363
|
+
DOUBLE: 'number',
|
|
33364
|
+
BIGNUM: 'number',
|
|
33365
|
+
DECIMAL: 'number',
|
|
33366
|
+
NUMERIC: 'number',
|
|
33367
|
+
FLOAT: 'number',
|
|
33368
|
+
REAL: 'number',
|
|
33369
|
+
'TIMESTAMP WITH TIME ZONE': 'date',
|
|
33370
|
+
TIMESTAMP: 'date',
|
|
33371
|
+
DATE: 'date',
|
|
33372
|
+
CHAR: 'string',
|
|
33373
|
+
VARCHAR: 'string',
|
|
33374
|
+
TEXT: 'string',
|
|
33375
|
+
UUID: 'string',
|
|
33376
|
+
BLOB: 'string',
|
|
33377
|
+
BIT: 'string',
|
|
33378
|
+
INTERVAL: 'string',
|
|
33379
|
+
LIST: 'string',
|
|
33380
|
+
BOOLEAN: 'boolean',
|
|
33381
|
+
};
|
|
33382
|
+
class Engine {
|
|
33383
|
+
async makeInit() {
|
|
33384
|
+
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);
|
|
33385
|
+
let worker_url = bundle.mainWorker;
|
|
33386
|
+
// this is necessary to let browsers execute WASM code coming from a cross-origin host
|
|
33387
|
+
if (worker_url.startsWith('https://')) {
|
|
33388
|
+
worker_url = URL.createObjectURL(new Blob([`importScripts("${worker_url}");`], {
|
|
33389
|
+
type: 'text/javascript',
|
|
33390
|
+
}));
|
|
33391
|
+
}
|
|
33392
|
+
const worker = new Worker(worker_url);
|
|
33393
|
+
const logger = new duckdb.ConsoleLogger(duckdb.LogLevel.WARNING);
|
|
33394
|
+
this.db = new duckdb.AsyncDuckDB(logger, worker);
|
|
33395
|
+
await this.db.instantiate(bundle.mainModule, bundle.pthreadWorker);
|
|
33396
|
+
// setup duckdb options
|
|
33397
|
+
const conn = await this.db.connect();
|
|
33398
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
33399
|
+
await conn.query(`INSTALL spatial; LOAD spatial;`);
|
|
33400
|
+
await conn.query(`SET TimeZone = '${timezone}';`);
|
|
33401
|
+
await conn.query(`SET threads = 1;`);
|
|
33402
|
+
// await conn.query(`SET memory_limit = '2GB';`) // used for experimenting when we encounter memory issues
|
|
33403
|
+
await conn.query(`SET preserve_insertion_order = false;`); // this reduces memory usage when loading a dataset
|
|
33404
|
+
conn.close();
|
|
33405
|
+
return this;
|
|
33406
|
+
}
|
|
33407
|
+
isReady() {
|
|
33408
|
+
if (!this.init_) {
|
|
33409
|
+
this.init_ = this.makeInit();
|
|
33410
|
+
}
|
|
33411
|
+
return this.init_;
|
|
33412
|
+
}
|
|
33413
|
+
/**
|
|
33414
|
+
* Returns information about a dataset once it's loaded:
|
|
33415
|
+
* - a list of properties description
|
|
33416
|
+
* - the name of the dataset geometry column (null if no geometry present)
|
|
33417
|
+
* @param datasetId name of the table under which the dataset will be stored
|
|
33418
|
+
* @param loadQuery duckdb-specific query for creating a table out of the data
|
|
33419
|
+
* @param forceReload if true, any existing data will be dropped and redownloaded
|
|
33420
|
+
*/
|
|
33421
|
+
async loadFile(datasetId, loadQuery, forceReload = false) {
|
|
33422
|
+
const conn = await this.db.connect();
|
|
33423
|
+
let results;
|
|
33424
|
+
// either we want to recreate the table, or we keep it if it already exists
|
|
33425
|
+
const safeLoadQuery = forceReload
|
|
33426
|
+
? `DROP TABLE IF EXISTS ${datasetId};
|
|
33427
|
+
${loadQuery}`
|
|
33428
|
+
: loadQuery.replace(/CREATE TABLE(?! IF NOT EXISTS)/gi, 'CREATE TABLE IF NOT EXISTS');
|
|
33429
|
+
// create the table
|
|
33430
|
+
try {
|
|
33431
|
+
results = await conn.query(safeLoadQuery);
|
|
33432
|
+
}
|
|
33433
|
+
catch (e) {
|
|
33434
|
+
throw new FetchError('parse', `DuckDB encountered an error when loading the data: ${e.message}`);
|
|
33435
|
+
}
|
|
33436
|
+
// read rows count
|
|
33437
|
+
results = await conn.query(`SELECT count(*) FROM ${datasetId}`);
|
|
33438
|
+
const { 'count_star()': recordsCount } = results.toArray()[0].toJSON();
|
|
33439
|
+
// read columns
|
|
33440
|
+
results = await conn.query(`SELECT * FROM information_schema.columns WHERE table_name = '${datasetId}';`);
|
|
33441
|
+
let geometryColumn = null;
|
|
33442
|
+
const properties = results
|
|
33443
|
+
.toArray()
|
|
33444
|
+
.map((row) => {
|
|
33445
|
+
const rowObj = row.toJSON();
|
|
33446
|
+
if (rowObj['data_type'] === 'GEOMETRY') {
|
|
33447
|
+
// the geometry is not part of the properties
|
|
33448
|
+
// note: right now we only keep one geometry column name, but if there are multiple they will
|
|
33449
|
+
// all get discarded
|
|
33450
|
+
geometryColumn = rowObj['column_name'];
|
|
33451
|
+
return null;
|
|
33452
|
+
}
|
|
33453
|
+
return {
|
|
33454
|
+
name: rowObj['column_name'],
|
|
33455
|
+
label: rowObj['column_name'],
|
|
33456
|
+
type: typesMapping[rowObj['data_type']] ?? 'other',
|
|
33457
|
+
};
|
|
33458
|
+
})
|
|
33459
|
+
.filter((prop) => prop !== null);
|
|
33460
|
+
conn.close();
|
|
33461
|
+
return {
|
|
33462
|
+
properties,
|
|
33463
|
+
geometryColumn,
|
|
33464
|
+
rowsCount: Number(recordsCount),
|
|
33465
|
+
};
|
|
33466
|
+
}
|
|
33467
|
+
// register a Uint8 buffer using a handle in the duckdb instance
|
|
33468
|
+
async registerData(name, buffer) {
|
|
33469
|
+
return this.db.registerFileBuffer(name, buffer);
|
|
33470
|
+
}
|
|
33471
|
+
/**
|
|
33472
|
+
* @param query duckdb-specific query for fetching items
|
|
33473
|
+
*/
|
|
33474
|
+
async queryItems(query) {
|
|
33475
|
+
const conn = await this.db.connect();
|
|
33476
|
+
const results = await conn.query(query);
|
|
33477
|
+
conn.close();
|
|
33478
|
+
return arrowTableToDataItems(results);
|
|
33479
|
+
}
|
|
33480
|
+
close() {
|
|
33481
|
+
this.db?.terminate();
|
|
33482
|
+
}
|
|
33483
|
+
}
|
|
33484
|
+
let engine = null;
|
|
33485
|
+
async function getEngine() {
|
|
33486
|
+
if (!engine) {
|
|
33487
|
+
engine = new Engine();
|
|
33488
|
+
}
|
|
33489
|
+
return engine.isReady();
|
|
33059
33490
|
}
|
|
33060
33491
|
|
|
33061
33492
|
class BaseReader {
|
|
@@ -33068,10 +33499,18 @@ class BaseReader {
|
|
|
33068
33499
|
this.sort = null;
|
|
33069
33500
|
this.startIndex = null;
|
|
33070
33501
|
this.count = null;
|
|
33502
|
+
this.loadPromise_ = Promise.resolve();
|
|
33503
|
+
this.cacheEnabled = false;
|
|
33504
|
+
}
|
|
33505
|
+
enableCache(enabled) {
|
|
33506
|
+
this.cacheEnabled = enabled;
|
|
33071
33507
|
}
|
|
33072
33508
|
load() {
|
|
33073
33509
|
throw new Error('not implemented');
|
|
33074
33510
|
}
|
|
33511
|
+
get isLoaded() {
|
|
33512
|
+
return this.loadPromise_;
|
|
33513
|
+
}
|
|
33075
33514
|
get properties() {
|
|
33076
33515
|
throw new Error('not implemented');
|
|
33077
33516
|
}
|
|
@@ -33120,178 +33559,237 @@ class BaseReader {
|
|
|
33120
33559
|
}
|
|
33121
33560
|
}
|
|
33122
33561
|
|
|
33123
|
-
|
|
33124
|
-
|
|
33125
|
-
|
|
33126
|
-
|
|
33127
|
-
|
|
33128
|
-
|
|
33129
|
-
|
|
33130
|
-
|
|
33131
|
-
|
|
33132
|
-
|
|
33133
|
-
|
|
33134
|
-
|
|
33135
|
-
|
|
33562
|
+
/**
|
|
33563
|
+
* This reader handles file formats supported natively by DuckDB
|
|
33564
|
+
*/
|
|
33565
|
+
class BaseFileReader extends BaseReader {
|
|
33566
|
+
// a table id should not exceed 63 chars
|
|
33567
|
+
generateDatasetId() {
|
|
33568
|
+
// generate a hash out of the url
|
|
33569
|
+
let hash = 0;
|
|
33570
|
+
for (const char of this.url) {
|
|
33571
|
+
hash = (hash << 5) - hash + char.charCodeAt(0);
|
|
33572
|
+
hash = hash >>> 0; // make it unsigned
|
|
33573
|
+
}
|
|
33574
|
+
return `datafetcher_${hash.toString(16)}`;
|
|
33575
|
+
}
|
|
33576
|
+
async getLoadQuery() {
|
|
33136
33577
|
throw new Error('not implemented');
|
|
33137
33578
|
}
|
|
33138
|
-
load() {
|
|
33139
|
-
this.
|
|
33579
|
+
async load() {
|
|
33580
|
+
this.datasetId = this.generateDatasetId();
|
|
33581
|
+
this.loadPromise_ = getEngine()
|
|
33582
|
+
.then((engine) => {
|
|
33583
|
+
this.engine = engine;
|
|
33584
|
+
return this.getLoadQuery();
|
|
33585
|
+
})
|
|
33586
|
+
.then((loadQuery) => this.engine.loadFile(this.datasetId, loadQuery, !this.cacheEnabled))
|
|
33587
|
+
.then((datasetInfo) => {
|
|
33588
|
+
this.properties_ = datasetInfo.properties;
|
|
33589
|
+
this.geometryColumn = datasetInfo.geometryColumn;
|
|
33590
|
+
this.rowsCount = datasetInfo.rowsCount;
|
|
33591
|
+
// returns void for the loaded promise
|
|
33592
|
+
});
|
|
33140
33593
|
}
|
|
33141
33594
|
get properties() {
|
|
33142
|
-
return this.
|
|
33595
|
+
return this.isLoaded.then(() => this.properties_);
|
|
33143
33596
|
}
|
|
33144
33597
|
get info() {
|
|
33145
|
-
return this.
|
|
33146
|
-
itemsCount:
|
|
33598
|
+
return this.isLoaded.then(() => ({
|
|
33599
|
+
itemsCount: this.rowsCount,
|
|
33600
|
+
hasGeometry: !!this.geometryColumn,
|
|
33147
33601
|
}));
|
|
33148
33602
|
}
|
|
33149
33603
|
async read() {
|
|
33150
|
-
|
|
33151
|
-
//
|
|
33152
|
-
|
|
33153
|
-
|
|
33154
|
-
|
|
33155
|
-
this.sort == null &&
|
|
33156
|
-
this.filter == null &&
|
|
33157
|
-
this.startIndex == null &&
|
|
33158
|
-
this.count == null) {
|
|
33159
|
-
return items;
|
|
33160
|
-
}
|
|
33161
|
-
const jsonItems = getJsonDataItemsProxy(items);
|
|
33162
|
-
const query = generateSqlQuery(this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations);
|
|
33163
|
-
const result = await import('alasql').then((module) => module.default(query, [jsonItems]));
|
|
33164
|
-
return result.map(jsonToGeojsonFeature);
|
|
33165
|
-
}
|
|
33166
|
-
}
|
|
33167
|
-
|
|
33168
|
-
function parseCsv(text) {
|
|
33169
|
-
// first parse the header to guess the delimiter
|
|
33170
|
-
// note that we do that to not rely on Papaparse logic for guessing delimiter
|
|
33171
|
-
let delimiter;
|
|
33172
|
-
try {
|
|
33173
|
-
const header = text.split('\n')[0];
|
|
33174
|
-
const result = Papa.parse(header, {
|
|
33175
|
-
header: false,
|
|
33176
|
-
});
|
|
33177
|
-
delimiter = result.meta.delimiter;
|
|
33178
|
-
}
|
|
33179
|
-
catch (e) {
|
|
33180
|
-
throw new Error('CSV parsing failed: the delimiter could not be guessed');
|
|
33604
|
+
await this.isLoaded;
|
|
33605
|
+
// if only certain fields are selected, omit the geometry
|
|
33606
|
+
const geometryColumn = this.selected === null ? this.geometryColumn : null;
|
|
33607
|
+
const query = generateSqlQuery(this.datasetId, this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations, geometryColumn);
|
|
33608
|
+
return this.engine.queryItems(query);
|
|
33181
33609
|
}
|
|
33182
|
-
const parsed = Papa.parse(text, {
|
|
33183
|
-
header: true,
|
|
33184
|
-
skipEmptyLines: true,
|
|
33185
|
-
delimiter,
|
|
33186
|
-
});
|
|
33187
|
-
if (parsed.errors.length) {
|
|
33188
|
-
throw new Error('CSV parsing failed for the following reasons:\n' +
|
|
33189
|
-
parsed.errors
|
|
33190
|
-
.map((error) => `* ${error.message} at row ${error.row}, column ${error.index}`)
|
|
33191
|
-
.join('\n'));
|
|
33192
|
-
}
|
|
33193
|
-
const items = parsed.data.map(jsonToGeojsonFeature);
|
|
33194
|
-
return processItemProperties(items, true);
|
|
33195
33610
|
}
|
|
33611
|
+
|
|
33196
33612
|
class CsvReader extends BaseFileReader {
|
|
33197
|
-
|
|
33198
|
-
|
|
33613
|
+
async getLoadQuery() {
|
|
33614
|
+
// first we get a list of columns hich have a detected type of VARCHAR
|
|
33615
|
+
// 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
|
|
33616
|
+
return `
|
|
33617
|
+
SET VARIABLE strColumns = (SELECT list(name) FROM (SELECT unnest(Columns, recursive := true) FROM sniff_csv("${this.url}")) WHERE type = 'VARCHAR');
|
|
33618
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM read_csv("${this.url}",
|
|
33619
|
+
force_not_null = getvariable('strColumns'),
|
|
33620
|
+
auto_type_candidates = ['NULL', 'BOOLEAN', 'INTEGER', 'DOUBLE', 'DATE', 'VARCHAR']
|
|
33621
|
+
);`;
|
|
33199
33622
|
}
|
|
33200
33623
|
}
|
|
33201
33624
|
|
|
33202
|
-
/**
|
|
33203
|
-
* This parser only supports arrays of simple flat objects with properties
|
|
33204
|
-
* @param text
|
|
33205
|
-
*/
|
|
33206
|
-
function parseJson(text) {
|
|
33207
|
-
const parsed = JSON.parse(text);
|
|
33208
|
-
if (!Array.isArray(parsed)) {
|
|
33209
|
-
throw new Error('Could not parse JSON, expected an array at root level');
|
|
33210
|
-
}
|
|
33211
|
-
return processItemProperties(parsed.map(jsonToGeojsonFeature));
|
|
33212
|
-
}
|
|
33213
33625
|
class JsonReader extends BaseFileReader {
|
|
33214
|
-
|
|
33215
|
-
return
|
|
33626
|
+
async getLoadQuery() {
|
|
33627
|
+
return `
|
|
33628
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM read_json("${this.url}",
|
|
33629
|
+
maximum_object_size = 536870912 -- 500MB
|
|
33630
|
+
);`;
|
|
33216
33631
|
}
|
|
33217
33632
|
}
|
|
33218
33633
|
|
|
33219
|
-
/**
|
|
33220
|
-
* This parser supports both Geojson Feature collections or arrays
|
|
33221
|
-
* of Features
|
|
33222
|
-
* @param text
|
|
33223
|
-
*/
|
|
33224
|
-
function parseGeojson(text) {
|
|
33225
|
-
const parsed = JSON.parse(text);
|
|
33226
|
-
const features = parsed.type === 'FeatureCollection' ? parsed.features : parsed;
|
|
33227
|
-
if (!Array.isArray(features)) {
|
|
33228
|
-
throw new Error('Could not parse GeoJSON, expected a features collection or an array of features at root level');
|
|
33229
|
-
}
|
|
33230
|
-
return processItemProperties(features);
|
|
33231
|
-
}
|
|
33232
33634
|
class GeojsonReader extends BaseFileReader {
|
|
33233
|
-
|
|
33234
|
-
return
|
|
33635
|
+
async getLoadQuery() {
|
|
33636
|
+
return `
|
|
33637
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM st_read("${this.url}",
|
|
33638
|
+
allowed_drivers = ['GeoJSON']
|
|
33639
|
+
);`;
|
|
33235
33640
|
}
|
|
33236
33641
|
}
|
|
33237
33642
|
|
|
33643
|
+
async function inferDatasetType(url, typeHint) {
|
|
33644
|
+
const fileExtensionMatches = new URL(url, typeof window !== 'undefined' ? window.location.toString() : undefined).pathname.match(/\.(.+)$/);
|
|
33645
|
+
const fileExtension = fileExtensionMatches && fileExtensionMatches.length
|
|
33646
|
+
? fileExtensionMatches[1].toLowerCase()
|
|
33647
|
+
: null;
|
|
33648
|
+
// 1. type hint
|
|
33649
|
+
if (typeHint)
|
|
33650
|
+
return Promise.resolve(typeHint);
|
|
33651
|
+
// 2. content-type header
|
|
33652
|
+
const headers = await fetchHeaders(url);
|
|
33653
|
+
if ('supportedType' in headers)
|
|
33654
|
+
return headers.supportedType;
|
|
33655
|
+
// 3. file extension from url
|
|
33656
|
+
else if (SupportedTypes.indexOf(fileExtension) > -1)
|
|
33657
|
+
return fileExtension;
|
|
33658
|
+
// no type inferred or hinted
|
|
33659
|
+
if ('mimeType' in headers)
|
|
33660
|
+
throw FetchError.unsupportedType(headers.mimeType);
|
|
33661
|
+
else
|
|
33662
|
+
throw FetchError.unknownType();
|
|
33663
|
+
}
|
|
33664
|
+
function fetchHeaders(url) {
|
|
33665
|
+
return sharedFetch(url, 'HEAD')
|
|
33666
|
+
.catch((error) => {
|
|
33667
|
+
throw FetchError.corsOrNetwork(error.message);
|
|
33668
|
+
})
|
|
33669
|
+
.then((response) => {
|
|
33670
|
+
if (!response.ok) {
|
|
33671
|
+
throw FetchError.http(response.status);
|
|
33672
|
+
}
|
|
33673
|
+
return parseHeaders(response.headers);
|
|
33674
|
+
});
|
|
33675
|
+
}
|
|
33676
|
+
function fetchDataAsText(url, cacheActive) {
|
|
33677
|
+
const fetchFactory = () => sharedFetch(url)
|
|
33678
|
+
.catch((error) => {
|
|
33679
|
+
throw FetchError.corsOrNetwork(error.message);
|
|
33680
|
+
})
|
|
33681
|
+
.then(async (response) => {
|
|
33682
|
+
if (!response.ok) {
|
|
33683
|
+
const clonedResponse = response.clone();
|
|
33684
|
+
throw FetchError.http(response.status, await clonedResponse.text());
|
|
33685
|
+
}
|
|
33686
|
+
const clonedResponse = response.clone();
|
|
33687
|
+
return clonedResponse.text();
|
|
33688
|
+
});
|
|
33689
|
+
return cacheActive ? useCache(fetchFactory, url, 'asText') : fetchFactory();
|
|
33690
|
+
}
|
|
33691
|
+
function fetchDataAsArrayBuffer(url, cacheActive) {
|
|
33692
|
+
const fetchFactory = () => sharedFetch(url)
|
|
33693
|
+
.catch((error) => {
|
|
33694
|
+
throw FetchError.corsOrNetwork(error.message);
|
|
33695
|
+
})
|
|
33696
|
+
.then(async (response) => {
|
|
33697
|
+
if (!response.ok) {
|
|
33698
|
+
throw FetchError.http(response.status, await response.text());
|
|
33699
|
+
}
|
|
33700
|
+
// convert to a numeric array so that we can store the response in cache
|
|
33701
|
+
return Array.from(new Uint8Array(await response.arrayBuffer()));
|
|
33702
|
+
});
|
|
33703
|
+
return (cacheActive ? useCache(fetchFactory, url, 'asArrayBuffer') : fetchFactory()).then((array) => {
|
|
33704
|
+
return new Uint8Array(array).buffer;
|
|
33705
|
+
});
|
|
33706
|
+
}
|
|
33238
33707
|
/**
|
|
33239
|
-
* This
|
|
33240
|
-
*
|
|
33241
|
-
* @param
|
|
33708
|
+
* This creates a Proxy that allows reading and writing to the data item properties
|
|
33709
|
+
* as if it was a simple array of JSON objects
|
|
33710
|
+
* @param items
|
|
33242
33711
|
*/
|
|
33243
|
-
function
|
|
33244
|
-
return
|
|
33245
|
-
|
|
33246
|
-
|
|
33247
|
-
|
|
33248
|
-
|
|
33249
|
-
|
|
33250
|
-
|
|
33251
|
-
|
|
33712
|
+
function getJsonDataItemsProxy(items) {
|
|
33713
|
+
return new Proxy(items, {
|
|
33714
|
+
get(target, p) {
|
|
33715
|
+
if (typeof p === 'string' &&
|
|
33716
|
+
!Number.isNaN(parseInt(p)) &&
|
|
33717
|
+
target[p]?.properties) {
|
|
33718
|
+
return target[p].properties;
|
|
33719
|
+
}
|
|
33720
|
+
return target[p];
|
|
33721
|
+
},
|
|
33722
|
+
set() {
|
|
33723
|
+
throw new Error('This object is read-only');
|
|
33724
|
+
},
|
|
33252
33725
|
});
|
|
33253
33726
|
}
|
|
33727
|
+
|
|
33254
33728
|
class ExcelReader extends BaseFileReader {
|
|
33255
|
-
|
|
33256
|
-
|
|
33729
|
+
async getLoadQuery() {
|
|
33730
|
+
// we download the file as an array buffer first, in order to be able to check if it's an XLS file
|
|
33731
|
+
let buffer = await fetchDataAsArrayBuffer(this.url, this.cacheEnabled);
|
|
33732
|
+
const bufferHandle = `B${this.datasetId}`;
|
|
33733
|
+
// checking against the magic number at the beginning of XLS files, see https://en.wikipedia.org/wiki/List_of_file_signatures
|
|
33734
|
+
const magicNumber = new Uint8Array(buffer, 0, 8); // first 8 bytes
|
|
33735
|
+
const isXls = Array.from(magicNumber)
|
|
33736
|
+
.map((n) => n.toString(16).toUpperCase())
|
|
33737
|
+
.join(' ') === 'D0 CF 11 E0 A1 B1 1A E1';
|
|
33738
|
+
// uh oh, this is an XLS file (not supported by duckdb); convert it to CSV using the xlsx package
|
|
33739
|
+
if (isXls) {
|
|
33740
|
+
buffer = await import('xlsx').then(({ read, utils }) => {
|
|
33741
|
+
const workbook = read(buffer);
|
|
33742
|
+
const json = utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]]);
|
|
33743
|
+
return new TextEncoder().encode(JSON.stringify(json)).buffer;
|
|
33744
|
+
});
|
|
33745
|
+
}
|
|
33746
|
+
const duckDbFn = isXls ? 'read_json' : 'read_xlsx';
|
|
33747
|
+
await this.engine.registerData(bufferHandle, new Uint8Array(buffer));
|
|
33748
|
+
return `
|
|
33749
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM ${duckDbFn}("${bufferHandle}", ignore_errors = true);`;
|
|
33750
|
+
}
|
|
33751
|
+
}
|
|
33752
|
+
|
|
33753
|
+
class GmlReader extends BaseFileReader {
|
|
33754
|
+
async getLoadQuery() {
|
|
33755
|
+
return `
|
|
33756
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM st_read("${this.url}",
|
|
33757
|
+
allowed_drivers = ['GML']
|
|
33758
|
+
);`;
|
|
33257
33759
|
}
|
|
33258
33760
|
}
|
|
33259
33761
|
|
|
33260
|
-
|
|
33261
|
-
|
|
33262
|
-
const
|
|
33762
|
+
const formatGeojson = new GeoJSON$1();
|
|
33763
|
+
function parseGeojson(text) {
|
|
33764
|
+
const parsed = JSON.parse(text);
|
|
33765
|
+
const features = parsed.type === 'FeatureCollection' ? parsed.features : parsed;
|
|
33766
|
+
if (!Array.isArray(features)) {
|
|
33767
|
+
throw new Error('Could not parse GeoJSON, expected a features collection or an array of features at root level');
|
|
33768
|
+
}
|
|
33769
|
+
return features;
|
|
33770
|
+
}
|
|
33771
|
+
function parseGml(text, featureType, version) {
|
|
33772
|
+
const parts = featureType.split(':');
|
|
33773
|
+
const regex = new RegExp(`xmlns:${parts[0]}=["']([^'"]*)["']`);
|
|
33263
33774
|
const match = regex.exec(text);
|
|
33264
33775
|
if (match && match.length >= 2) {
|
|
33265
|
-
const
|
|
33776
|
+
const wfs = new WFS({
|
|
33266
33777
|
featureNS: match[1],
|
|
33267
|
-
featureType:
|
|
33778
|
+
featureType: parts[1],
|
|
33268
33779
|
version: version,
|
|
33269
33780
|
});
|
|
33270
33781
|
let features;
|
|
33271
33782
|
try {
|
|
33272
|
-
features =
|
|
33783
|
+
features = wfs.readFeatures(text);
|
|
33273
33784
|
}
|
|
33274
33785
|
catch (e) {
|
|
33275
|
-
throw Error(
|
|
33786
|
+
throw Error(`Couldn't parse WFS with GML features: ${e.message}`);
|
|
33276
33787
|
}
|
|
33277
|
-
const geojsonItem =
|
|
33278
|
-
return
|
|
33788
|
+
const geojsonItem = formatGeojson.writeFeaturesObject(features);
|
|
33789
|
+
return geojsonItem.features;
|
|
33279
33790
|
}
|
|
33280
33791
|
throw Error("Couldn't retrieve namespace url");
|
|
33281
33792
|
}
|
|
33282
|
-
class GmlReader extends BaseFileReader {
|
|
33283
|
-
constructor(url, namespace, version, cacheActive = true) {
|
|
33284
|
-
super(url);
|
|
33285
|
-
this.url = url;
|
|
33286
|
-
this.namespace = namespace;
|
|
33287
|
-
this.version = version;
|
|
33288
|
-
this.cacheActive = cacheActive;
|
|
33289
|
-
}
|
|
33290
|
-
getData() {
|
|
33291
|
-
return fetchDataAsText(this.url, this.cacheActive).then((text) => parseGml(text, this.namespace, this.version));
|
|
33292
|
-
}
|
|
33293
|
-
}
|
|
33294
|
-
|
|
33295
33793
|
async function getWfsEndpoint(wfsUrl) {
|
|
33296
33794
|
try {
|
|
33297
33795
|
return await new WfsEndpoint(wfsUrl).isReady();
|
|
@@ -33319,21 +33817,65 @@ async function getWfsEndpoint(wfsUrl) {
|
|
|
33319
33817
|
}
|
|
33320
33818
|
}
|
|
33321
33819
|
}
|
|
33322
|
-
class WfsReader extends
|
|
33323
|
-
constructor(url,
|
|
33324
|
-
super(url
|
|
33325
|
-
this.endpoint =
|
|
33326
|
-
this.
|
|
33327
|
-
|
|
33820
|
+
class WfsReader extends BaseReader {
|
|
33821
|
+
constructor(url, featureTypeName) {
|
|
33822
|
+
super(url);
|
|
33823
|
+
this.endpoint = getWfsEndpoint(url);
|
|
33824
|
+
this.featureType = this.endpoint
|
|
33825
|
+
.then((endpoint) => {
|
|
33826
|
+
const featureTypes = endpoint.getFeatureTypes();
|
|
33827
|
+
return endpoint.getFeatureTypeFull(featureTypes.length === 1 && !featureTypeName
|
|
33828
|
+
? featureTypes[0].name
|
|
33829
|
+
: featureTypeName);
|
|
33830
|
+
})
|
|
33831
|
+
.then((featureType) => {
|
|
33832
|
+
if (!featureType) {
|
|
33833
|
+
throw new Error('wfs.featuretype.notfound');
|
|
33834
|
+
}
|
|
33835
|
+
return featureType;
|
|
33836
|
+
});
|
|
33837
|
+
}
|
|
33838
|
+
get backupReader() {
|
|
33839
|
+
if (this.backupReader_) {
|
|
33840
|
+
return this.backupReader_;
|
|
33841
|
+
}
|
|
33842
|
+
this.backupReader_ = Promise.all([this.endpoint, this.featureType]).then(([endpoint, featureType]) => {
|
|
33843
|
+
let reader;
|
|
33844
|
+
if (endpoint.supportsJson(featureType.name)) {
|
|
33845
|
+
reader = new GeojsonReader(endpoint.getFeatureUrl(featureType.name, {
|
|
33846
|
+
asJson: true,
|
|
33847
|
+
outputCrs: 'EPSG:4326',
|
|
33848
|
+
}));
|
|
33849
|
+
}
|
|
33850
|
+
else {
|
|
33851
|
+
if (featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')) &&
|
|
33852
|
+
(featureType.defaultCrs === 'EPSG:4326' ||
|
|
33853
|
+
featureType.otherCrs?.includes('EPSG:4326'))) {
|
|
33854
|
+
reader = new GmlReader(endpoint.getFeatureUrl(featureType.name, {
|
|
33855
|
+
outputFormat: featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')),
|
|
33856
|
+
outputCrs: 'EPSG:4326',
|
|
33857
|
+
}));
|
|
33858
|
+
}
|
|
33859
|
+
}
|
|
33860
|
+
reader.enableCache(this.cacheEnabled);
|
|
33861
|
+
reader.load();
|
|
33862
|
+
return reader;
|
|
33863
|
+
});
|
|
33864
|
+
return this.backupReader_;
|
|
33328
33865
|
}
|
|
33329
33866
|
get properties() {
|
|
33330
|
-
return this.
|
|
33331
|
-
.getFeatureTypeFull(this.featureTypeName)
|
|
33332
|
-
.then((featureType) => Object.keys(featureType.properties).map((prop) => {
|
|
33867
|
+
return this.featureType.then((featureType) => Object.keys(featureType.properties).map((prop) => {
|
|
33333
33868
|
const originalType = featureType.properties[prop];
|
|
33334
|
-
|
|
33335
|
-
|
|
33336
|
-
|
|
33869
|
+
let type;
|
|
33870
|
+
if (originalType === 'float' || originalType === 'integer') {
|
|
33871
|
+
type = 'number';
|
|
33872
|
+
}
|
|
33873
|
+
else if (originalType === 'boolean') {
|
|
33874
|
+
type = 'string'; // we don't handle booleans yet in the data fetcher
|
|
33875
|
+
}
|
|
33876
|
+
else {
|
|
33877
|
+
type = originalType;
|
|
33878
|
+
}
|
|
33337
33879
|
return {
|
|
33338
33880
|
name: prop,
|
|
33339
33881
|
label: prop,
|
|
@@ -33342,83 +33884,63 @@ class WfsReader extends BaseCacheReader {
|
|
|
33342
33884
|
}));
|
|
33343
33885
|
}
|
|
33344
33886
|
get info() {
|
|
33345
|
-
return this.
|
|
33887
|
+
return this.featureType.then((result) => ({
|
|
33346
33888
|
itemsCount: result.objectCount,
|
|
33889
|
+
hasGeometry: !!result.geometryName,
|
|
33347
33890
|
}));
|
|
33348
33891
|
}
|
|
33349
|
-
|
|
33350
|
-
|
|
33351
|
-
const featureTypes = wfsEndpoint.getFeatureTypes();
|
|
33352
|
-
const featureType = wfsEndpoint.getFeatureTypeSummary(featureTypes.length === 1 && !featureTypeName
|
|
33353
|
-
? featureTypes[0].name
|
|
33354
|
-
: featureTypeName);
|
|
33355
|
-
if (!featureType) {
|
|
33356
|
-
throw new Error('wfs.featuretype.notfound');
|
|
33357
|
-
}
|
|
33358
|
-
if (wfsEndpoint.supportsStartIndex()) {
|
|
33359
|
-
return new WfsReader(wfsUrlEndpoint, wfsEndpoint, featureType.name);
|
|
33360
|
-
}
|
|
33361
|
-
else if (wfsEndpoint.supportsJson(featureType.name)) {
|
|
33362
|
-
return new GeojsonReader(wfsEndpoint.getFeatureUrl(featureType.name, {
|
|
33363
|
-
asJson: true,
|
|
33364
|
-
outputCrs: 'EPSG:4326',
|
|
33365
|
-
}));
|
|
33366
|
-
}
|
|
33367
|
-
else {
|
|
33368
|
-
if (featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')) &&
|
|
33369
|
-
(featureType.defaultCrs === 'EPSG:4326' ||
|
|
33370
|
-
featureType.otherCrs?.includes('EPSG:4326'))) {
|
|
33371
|
-
return new GmlReader(wfsEndpoint.getFeatureUrl(featureType.name, {
|
|
33372
|
-
outputFormat: featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')),
|
|
33373
|
-
outputCrs: 'EPSG:4326',
|
|
33374
|
-
}), featureType.name, wfsEndpoint.getVersion());
|
|
33375
|
-
}
|
|
33376
|
-
throw new Error('wfs.geojsongml.notsupported');
|
|
33377
|
-
}
|
|
33892
|
+
load() {
|
|
33893
|
+
// Nothing to load for Wfs
|
|
33378
33894
|
}
|
|
33379
|
-
async
|
|
33380
|
-
|
|
33381
|
-
|
|
33895
|
+
async read() {
|
|
33896
|
+
const endpoint = await this.endpoint;
|
|
33897
|
+
const featureType = await this.featureType;
|
|
33898
|
+
// if we can't use the WFS protocol we fall back to the backup reader
|
|
33899
|
+
if (this.aggregations || this.groupedBy || !endpoint.supportsStartIndex()) {
|
|
33900
|
+
const backupReader = await this.backupReader;
|
|
33901
|
+
backupReader.selectAll();
|
|
33902
|
+
if (this.selected) {
|
|
33903
|
+
backupReader.select(...this.selected);
|
|
33904
|
+
}
|
|
33905
|
+
if (this.aggregations) {
|
|
33906
|
+
backupReader.aggregate(...this.aggregations);
|
|
33907
|
+
}
|
|
33908
|
+
if (this.groupedBy) {
|
|
33909
|
+
backupReader.groupBy(...this.groupedBy);
|
|
33910
|
+
}
|
|
33911
|
+
if (this.sort) {
|
|
33912
|
+
backupReader.orderBy(...this.sort);
|
|
33913
|
+
}
|
|
33914
|
+
if (this.startIndex !== null && this.count !== null) {
|
|
33915
|
+
backupReader.limit(this.startIndex, this.count);
|
|
33916
|
+
}
|
|
33917
|
+
return backupReader.read();
|
|
33382
33918
|
}
|
|
33383
|
-
const asJson =
|
|
33919
|
+
const asJson = endpoint.supportsJson(featureType.name);
|
|
33384
33920
|
const attributes = this.selected ?? undefined;
|
|
33385
|
-
let
|
|
33921
|
+
let sortBy = null;
|
|
33922
|
+
if (this.sort) {
|
|
33923
|
+
const mapSort = (s) => [s[0] === 'desc' ? 'D' : 'A', s[1]];
|
|
33924
|
+
sortBy = Array.isArray(this.sort[0])
|
|
33925
|
+
? this.sort.map(mapSort)
|
|
33926
|
+
: mapSort(this.sort);
|
|
33927
|
+
}
|
|
33928
|
+
const url = endpoint.getFeatureUrl(featureType.name, {
|
|
33386
33929
|
...(this.startIndex !== null && { startIndex: this.startIndex }),
|
|
33387
33930
|
...(this.count !== null && { maxFeatures: this.count }),
|
|
33388
33931
|
asJson,
|
|
33389
33932
|
outputCrs: 'EPSG:4326',
|
|
33390
33933
|
attributes,
|
|
33391
|
-
|
|
33934
|
+
sortBy,
|
|
33392
33935
|
});
|
|
33393
|
-
|
|
33394
|
-
const finalUrl = new URL(url);
|
|
33395
|
-
const sorts = this.sort
|
|
33396
|
-
.map((fieldSort) => `${fieldSort[1]}+${fieldSort[0] === 'asc' ? 'A' : 'D'}`)
|
|
33397
|
-
.join(',');
|
|
33398
|
-
// Direct update on string url to prevent encoding of +A and +D
|
|
33399
|
-
url = `${url}${finalUrl.search ? '&' : ''}SORTBY=${sorts}`;
|
|
33400
|
-
}
|
|
33401
|
-
return fetchDataAsText(url, this.cacheActive).then((text) => asJson
|
|
33936
|
+
return fetchDataAsText(url, this.cacheEnabled).then((text) => asJson
|
|
33402
33937
|
? parseGeojson(text)
|
|
33403
|
-
: parseGml(text,
|
|
33404
|
-
}
|
|
33405
|
-
async getQueryData() {
|
|
33406
|
-
const items = (await this.getData()).items;
|
|
33407
|
-
const jsonItems = getJsonDataItemsProxy(items);
|
|
33408
|
-
const query = generateSqlQuery(this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations);
|
|
33409
|
-
const result = await import('alasql').then((module) => module.default(query, [jsonItems]));
|
|
33410
|
-
return result.map(jsonToGeojsonFeature);
|
|
33411
|
-
}
|
|
33412
|
-
load() {
|
|
33413
|
-
// Nothing to load for Wfs
|
|
33414
|
-
}
|
|
33415
|
-
async read() {
|
|
33416
|
-
return (await this.getData(this.aggregations, this.groupedBy)).items;
|
|
33938
|
+
: parseGml(text, featureType.name, endpoint.getVersion()));
|
|
33417
33939
|
}
|
|
33418
33940
|
}
|
|
33419
33941
|
|
|
33420
|
-
async function openDataset(url,
|
|
33421
|
-
const fileType = await inferDatasetType(url, typeHint);
|
|
33942
|
+
async function openDataset(url, options) {
|
|
33943
|
+
const fileType = await inferDatasetType(url, options?.typeHint);
|
|
33422
33944
|
let reader;
|
|
33423
33945
|
try {
|
|
33424
33946
|
switch (fileType) {
|
|
@@ -33435,18 +33957,18 @@ async function openDataset(url, typeHint, options, cacheActive) {
|
|
|
33435
33957
|
reader = new ExcelReader(url);
|
|
33436
33958
|
break;
|
|
33437
33959
|
case 'gml':
|
|
33438
|
-
reader = new GmlReader(url
|
|
33960
|
+
reader = new GmlReader(url);
|
|
33439
33961
|
break;
|
|
33440
33962
|
case 'wfs':
|
|
33441
|
-
reader =
|
|
33963
|
+
reader = new WfsReader(url, options?.wfsFeatureType);
|
|
33442
33964
|
break;
|
|
33443
33965
|
}
|
|
33444
|
-
reader.
|
|
33966
|
+
reader.enableCache(options?.enableCache ?? true);
|
|
33445
33967
|
reader.load();
|
|
33446
33968
|
return reader;
|
|
33447
33969
|
}
|
|
33448
33970
|
catch (e) {
|
|
33449
|
-
//WfsReader may already raise a FetchError
|
|
33971
|
+
// WfsReader may already raise a FetchError
|
|
33450
33972
|
if (e instanceof FetchError)
|
|
33451
33973
|
throw e;
|
|
33452
33974
|
else
|
|
@@ -33457,13 +33979,13 @@ async function openDataset(url, typeHint, options, cacheActive) {
|
|
|
33457
33979
|
* This fetches the full dataset at the given URL and parses it according to its mime type.
|
|
33458
33980
|
* All items in the dataset are converted to GeoJSON features, even if they do not bear any spatial geometry.
|
|
33459
33981
|
* File type can be either inferred (from the HTTP headers or the URL), or hinted using the 2nd argument
|
|
33460
|
-
* File type is determined
|
|
33982
|
+
* File type is determined like so:
|
|
33461
33983
|
* 1. if a type hint is given, use it
|
|
33462
33984
|
* 2. otherwise, look for a Content-Type header in the response with a supported mime type
|
|
33463
33985
|
* 3. if no valid mime type was found, look for an explicit file extension in the url (.csv, .geojson etc.)
|
|
33464
33986
|
*/
|
|
33465
|
-
async function readDataset(url,
|
|
33466
|
-
const reader = await openDataset(url,
|
|
33987
|
+
async function readDataset(url, options) {
|
|
33988
|
+
const reader = await openDataset(url, options);
|
|
33467
33989
|
try {
|
|
33468
33990
|
return await reader.read();
|
|
33469
33991
|
}
|
|
@@ -33694,9 +34216,11 @@ class DataService {
|
|
|
33694
34216
|
getDataset(link, cacheActive) {
|
|
33695
34217
|
if (link.type === 'service' && link.accessServiceProtocol === 'wfs') {
|
|
33696
34218
|
const wfsUrlEndpoint = this.proxy.getProxiedUrl(link.url.toString());
|
|
33697
|
-
return from(openDataset(wfsUrlEndpoint,
|
|
34219
|
+
return from(openDataset(wfsUrlEndpoint, {
|
|
34220
|
+
typeHint: 'wfs',
|
|
33698
34221
|
wfsFeatureType: link.name,
|
|
33699
|
-
|
|
34222
|
+
enableCache: cacheActive,
|
|
34223
|
+
}));
|
|
33700
34224
|
}
|
|
33701
34225
|
else if (link.type === 'download') {
|
|
33702
34226
|
const linkProxifiedUrl = this.proxy.getProxiedUrl(link.url.toString());
|
|
@@ -33704,12 +34228,15 @@ class DataService {
|
|
|
33704
34228
|
const supportedType = SupportedTypes.indexOf(format) > -1
|
|
33705
34229
|
? format
|
|
33706
34230
|
: undefined;
|
|
33707
|
-
return from(openDataset(linkProxifiedUrl,
|
|
34231
|
+
return from(openDataset(linkProxifiedUrl, {
|
|
34232
|
+
typeHint: supportedType,
|
|
34233
|
+
enableCache: cacheActive,
|
|
34234
|
+
})).pipe();
|
|
33708
34235
|
}
|
|
33709
34236
|
else if (link.type === 'service' &&
|
|
33710
34237
|
link.accessServiceProtocol === 'esriRest') {
|
|
33711
34238
|
const url = this.getDownloadUrlFromEsriRest(link.url.toString(), 'geojson');
|
|
33712
|
-
return from(openDataset(url, 'geojson',
|
|
34239
|
+
return from(openDataset(url, { typeHint: 'geojson', enableCache: cacheActive })).pipe();
|
|
33713
34240
|
}
|
|
33714
34241
|
else if (link.type === 'service' &&
|
|
33715
34242
|
link.accessServiceProtocol === 'ogcFeatures') {
|
|
@@ -33723,7 +34250,10 @@ class DataService {
|
|
|
33723
34250
|
}
|
|
33724
34251
|
const urlWithoutLimit = new URL(geojsonUrl);
|
|
33725
34252
|
urlWithoutLimit.searchParams.delete('limit');
|
|
33726
|
-
return openDataset(urlWithoutLimit.toString(),
|
|
34253
|
+
return openDataset(urlWithoutLimit.toString(), {
|
|
34254
|
+
typeHint: 'geojson',
|
|
34255
|
+
enableCache: cacheActive,
|
|
34256
|
+
});
|
|
33727
34257
|
}));
|
|
33728
34258
|
}
|
|
33729
34259
|
return throwError(() => 'protocol not supported');
|
|
@@ -33975,20 +34505,31 @@ class DataTableComponent {
|
|
|
33975
34505
|
this.eltRef = inject(ElementRef);
|
|
33976
34506
|
this.cdr = inject(ChangeDetectorRef);
|
|
33977
34507
|
this.translateService = inject(TranslateService);
|
|
33978
|
-
this.
|
|
34508
|
+
this.columnsFromFeatureCatalog = null;
|
|
34509
|
+
this.columnsFromDataset = [];
|
|
33979
34510
|
this.selected = new EventEmitter();
|
|
33980
|
-
this.properties$ = new BehaviorSubject(null);
|
|
33981
34511
|
this.loading$ = new BehaviorSubject(false);
|
|
33982
34512
|
this.error = null;
|
|
33983
34513
|
}
|
|
33984
34514
|
set featureAttributes(value) {
|
|
33985
|
-
this.
|
|
33986
|
-
|
|
34515
|
+
this.columnsFromFeatureCatalog = value.map((attrs) => ({
|
|
34516
|
+
name: attrs.value,
|
|
34517
|
+
label: attrs.label,
|
|
34518
|
+
}));
|
|
33987
34519
|
}
|
|
33988
34520
|
set dataset(value) {
|
|
33989
34521
|
this.dataset_ = value;
|
|
33990
34522
|
this.dataset_.load();
|
|
33991
|
-
this.dataset_.info.then((info) =>
|
|
34523
|
+
this.dataset_.info.then((info) => {
|
|
34524
|
+
this.count = info.itemsCount;
|
|
34525
|
+
this.cdr.detectChanges();
|
|
34526
|
+
});
|
|
34527
|
+
}
|
|
34528
|
+
get columns() {
|
|
34529
|
+
return this.columnsFromFeatureCatalog ?? this.columnsFromDataset;
|
|
34530
|
+
}
|
|
34531
|
+
get columnNames() {
|
|
34532
|
+
return this.columns.map((c) => c.name);
|
|
33992
34533
|
}
|
|
33993
34534
|
ngOnInit() {
|
|
33994
34535
|
this.dataSource = new DataTableDataSource();
|
|
@@ -34023,11 +34564,12 @@ class DataTableComponent {
|
|
|
34023
34564
|
}
|
|
34024
34565
|
async readData() {
|
|
34025
34566
|
this.loading$.next(true);
|
|
34026
|
-
// wait for properties to be read
|
|
34027
|
-
const properties = await firstValueFrom(this.properties$.pipe(filter((p) => !!p)));
|
|
34028
|
-
const propsWithoutGeom = properties.filter((p) => !p.toLowerCase().startsWith('geom'));
|
|
34029
|
-
this.dataset_.select(...propsWithoutGeom);
|
|
34030
34567
|
try {
|
|
34568
|
+
// wait for properties to be read
|
|
34569
|
+
if (!this.columnsFromFeatureCatalog) {
|
|
34570
|
+
this.columnsFromDataset = await this.dataset_.properties;
|
|
34571
|
+
}
|
|
34572
|
+
this.dataset_.select(...this.columnNames);
|
|
34031
34573
|
await this.dataSource.showData(this.dataset_.read());
|
|
34032
34574
|
this.error = null;
|
|
34033
34575
|
}
|
|
@@ -34057,7 +34599,7 @@ class DataTableComponent {
|
|
|
34057
34599
|
}
|
|
34058
34600
|
}
|
|
34059
34601
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DataTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
34060
|
-
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
|
|
34602
|
+
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 }); }
|
|
34061
34603
|
}
|
|
34062
34604
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DataTableComponent, decorators: [{
|
|
34063
34605
|
type: Component,
|
|
@@ -34069,10 +34611,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
34069
34611
|
CommonModule,
|
|
34070
34612
|
LoadingMaskComponent,
|
|
34071
34613
|
PopupAlertComponent,
|
|
34072
|
-
LetDirective,
|
|
34073
34614
|
TranslatePipe,
|
|
34074
34615
|
TranslateDirective,
|
|
34075
|
-
], 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
|
|
34616
|
+
], 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"] }]
|
|
34076
34617
|
}], propDecorators: { featureAttributes: [{
|
|
34077
34618
|
type: Input
|
|
34078
34619
|
}], dataset: [{
|
|
@@ -34175,7 +34716,7 @@ class ChartViewComponent {
|
|
|
34175
34716
|
}), shareReplay$1(1));
|
|
34176
34717
|
this.properties$ = combineLatest([this.dataset$, this.featureCatalog$]).pipe(switchMap$1(([dataset, catalog]) => this.setProperties(dataset, catalog)), shareReplay$1(1));
|
|
34177
34718
|
this.yChoices$ = this.properties$.pipe(map$1((properties) => properties
|
|
34178
|
-
.filter((prop) => prop.type === 'number'
|
|
34719
|
+
.filter((prop) => prop.type === 'number')
|
|
34179
34720
|
.map((prop) => ({ value: prop.name, label: prop.label || prop.name }))), tap$1((choices) => {
|
|
34180
34721
|
if (!choices.find((choice) => choice.value === this.yProperty$.value)) {
|
|
34181
34722
|
const newProp = choices[0]?.value || '';
|
|
@@ -34202,6 +34743,7 @@ class ChartViewComponent {
|
|
|
34202
34743
|
this.aggregation$,
|
|
34203
34744
|
]).pipe(filter$1(([_, x, y]) => !!x || !!y), switchMap$1(([dataset, xProp, yProp, aggregation]) => {
|
|
34204
34745
|
const fieldAgg = aggregation === 'count' ? ['count'] : [aggregation, yProp];
|
|
34746
|
+
this.loading = true;
|
|
34205
34747
|
return dataset
|
|
34206
34748
|
.groupBy(['distinct', xProp])
|
|
34207
34749
|
.aggregate(fieldAgg)
|
|
@@ -34443,6 +34985,7 @@ class GeoTableViewComponent {
|
|
|
34443
34985
|
}));
|
|
34444
34986
|
}
|
|
34445
34987
|
async initMapContext() {
|
|
34988
|
+
this.dataset.load();
|
|
34446
34989
|
this.dataset.selectAll();
|
|
34447
34990
|
return {
|
|
34448
34991
|
layers: [
|
|
@@ -34616,8 +35159,7 @@ class MdViewFacade {
|
|
|
34616
35159
|
}));
|
|
34617
35160
|
this.error$ = this.store.pipe(select(getMetadataError));
|
|
34618
35161
|
this.related$ = this.store.pipe(select(getRelated));
|
|
34619
|
-
this.
|
|
34620
|
-
this.sourceOf$ = this.store.pipe(select(getSourceOf));
|
|
35162
|
+
this.linkedRecords$ = this.store.pipe(select(getLinkedRecords));
|
|
34621
35163
|
this.chartConfig$ = this.store.pipe(select(getChartConfig));
|
|
34622
35164
|
this.allLinks$ = this.metadata$.pipe(map$1((record) => 'onlineResources' in record ? record.onlineResources : []), shareReplay$1(1));
|
|
34623
35165
|
this.resourceDoi$ = this.metadata$.pipe(map$1((record) => {
|
|
@@ -34736,12 +35278,9 @@ class MdViewEffects {
|
|
|
34736
35278
|
this.loadRelatedRecords$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getSimilarRecords(full)), map$1((related) => {
|
|
34737
35279
|
return setRelated({ related });
|
|
34738
35280
|
}), catchError(() => of(setRelated({ related: null })))));
|
|
34739
|
-
this.
|
|
34740
|
-
return
|
|
34741
|
-
}), catchError(() => of(
|
|
34742
|
-
this.loadSourceOf$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getSourceOf(full)), map$1((sourceOf) => {
|
|
34743
|
-
return setSourceOf({ sourceOf });
|
|
34744
|
-
}), catchError(() => of(setSourceOf({ sourceOf: null })))));
|
|
35281
|
+
this.loadLinkedRecords$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getLinkedRecords(full)), map$1((linkedRecords) => {
|
|
35282
|
+
return setLinkedRecords({ linkedRecords });
|
|
35283
|
+
}), catchError(() => of(setLinkedRecords({ linkedRecords: null })))));
|
|
34745
35284
|
/*
|
|
34746
35285
|
UserFeedback effects
|
|
34747
35286
|
*/
|
|
@@ -40020,7 +40559,7 @@ function flattenQueryParams(params) {
|
|
|
40020
40559
|
const start = flattened[key].start;
|
|
40021
40560
|
const end = flattened[key].end;
|
|
40022
40561
|
flattened[key] = [
|
|
40023
|
-
`${start ? formatDate(start) : ''}..${formatDate(end)
|
|
40562
|
+
`${start ? formatDate(start) : ''}..${end ? formatDate(end) : ''}`,
|
|
40024
40563
|
];
|
|
40025
40564
|
}
|
|
40026
40565
|
}
|
|
@@ -40377,5 +40916,5 @@ const CHART_TYPE_VALUES = [
|
|
|
40377
40916
|
* Generated bundle index. Do not edit.
|
|
40378
40917
|
*/
|
|
40379
40918
|
|
|
40380
|
-
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 };
|
|
40919
|
+
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, 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, 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 };
|
|
40381
40920
|
//# sourceMappingURL=geonetwork-ui.mjs.map
|