geonetwork-ui 2.11.0-dev.a83f0f51a → 2.11.0-dev.bc1a6d4e2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/geonetwork-ui.mjs +1244 -677
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +219 -78
- package/index.d.ts.map +1 -1
- package/package.json +12 -11
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +43 -13
- package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +48 -7
- package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +40 -16
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +12 -0
- package/src/libs/common/domain/src/lib/model/search/filter.model.ts +13 -1
- package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +2 -2
- package/src/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts +3 -2
- package/src/libs/feature/dataviz/src/lib/geo-table-view/geo-table-view.component.ts +2 -1
- package/src/libs/feature/dataviz/src/lib/service/data.service.ts +22 -22
- package/src/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.html +1 -0
- package/src/libs/feature/notifications/src/lib/notifications.service.ts +6 -4
- package/src/libs/feature/record/src/lib/state/mdview.actions.ts +4 -8
- package/src/libs/feature/record/src/lib/state/mdview.effects.ts +7 -16
- package/src/libs/feature/record/src/lib/state/mdview.facade.ts +1 -3
- package/src/libs/feature/record/src/lib/state/mdview.reducer.ts +4 -9
- package/src/libs/feature/record/src/lib/state/mdview.selectors.ts +2 -7
- package/src/libs/feature/router/src/lib/default/state/query-params.utils.ts +1 -1
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +11 -4
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +58 -19
- package/src/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts +17 -8
- package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +3 -1
- package/src/libs/feature/search/src/lib/utils/service/fields.ts +27 -2
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.html +6 -7
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.ts +31 -19
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.fixtures.ts +2 -2
- package/src/libs/ui/inputs/src/index.ts +2 -0
- package/src/libs/ui/inputs/src/lib/date-adapter.providers.ts +30 -0
- package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +3 -24
- package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.css +37 -3
- package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.html +135 -16
- package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.ts +166 -29
- package/src/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.ts +3 -7
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.css +1 -0
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.html +20 -7
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.ts +46 -5
- package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.css +0 -0
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html +111 -0
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts +215 -0
- package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +6 -5
- package/src/libs/util/app-config/src/lib/app-config.ts +3 -0
- package/src/libs/util/app-config/src/lib/model.ts +1 -0
- package/src/libs/util/data-fetcher/src/lib/data-fetcher.ts +19 -22
- package/src/libs/util/data-fetcher/src/lib/engine/duckdb.ts +185 -0
- package/src/libs/util/data-fetcher/src/lib/engine/results.ts +63 -0
- package/src/libs/util/data-fetcher/src/lib/{sql-utils.ts → engine/sql-utils.ts} +28 -13
- package/src/libs/util/data-fetcher/src/lib/model.ts +2 -1
- package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +53 -38
- package/src/libs/util/data-fetcher/src/lib/readers/base.ts +11 -0
- package/src/libs/util/data-fetcher/src/lib/readers/csv.ts +9 -47
- package/src/libs/util/data-fetcher/src/lib/readers/excel.ts +27 -27
- package/src/libs/util/data-fetcher/src/lib/readers/geojson.ts +5 -24
- package/src/libs/util/data-fetcher/src/lib/readers/gml.ts +5 -49
- package/src/libs/util/data-fetcher/src/lib/readers/json.ts +5 -23
- package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +184 -128
- package/src/libs/util/data-fetcher/src/lib/utils.ts +0 -143
- package/src/libs/util/shared/src/index.ts +1 -0
- package/src/libs/util/shared/src/lib/autofocus.directive.ts +26 -0
- package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
- package/src/libs/util/shared/src/lib/utils/file.ts +15 -0
- package/src/libs/util/shared/src/lib/utils/geojson.ts +8 -0
- package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
- package/tailwind.base.css +5 -0
- package/translations/de.json +15 -2
- package/translations/en.json +14 -1
- package/translations/es.json +13 -0
- package/translations/fr.json +15 -2
- package/translations/it.json +13 -0
- package/translations/nl.json +13 -0
- package/translations/pt.json +13 -0
- package/translations/sk.json +13 -0
- package/src/libs/util/data-fetcher/src/lib/readers/base-cache.ts +0 -14
|
@@ -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.bc1a6d4e2";
|
|
20373
20438
|
var engines = {
|
|
20374
20439
|
node: ">=24"
|
|
20375
20440
|
};
|
|
@@ -20390,6 +20455,7 @@ var peerDependencies = {
|
|
|
20390
20455
|
"@angular/core": "19.x || 20.x || 21.x",
|
|
20391
20456
|
"@angular/forms": "19.x || 20.x || 21.x",
|
|
20392
20457
|
"@angular/material": "19.x || 20.x || 21.x",
|
|
20458
|
+
"@angular/material-date-fns-adapter": "19.x || 20.x",
|
|
20393
20459
|
"@angular/platform-browser": "19.x || 20.x || 21.x",
|
|
20394
20460
|
"@angular/platform-browser-dynamic": "19.x || 20.x || 21.x",
|
|
20395
20461
|
"@angular/router": "19.x || 20.x || 21.x",
|
|
@@ -20399,20 +20465,21 @@ var peerDependencies = {
|
|
|
20399
20465
|
"@ngrx/store": "19.x || 20.x || 21.x",
|
|
20400
20466
|
"@ngrx/store-devtools": "19.x || 20.x || 21.x",
|
|
20401
20467
|
"@ngrx/operators": "19.x || 20.x || 21.x",
|
|
20402
|
-
"@ngx-translate/core": "
|
|
20403
|
-
"@ngx-translate/http-loader": "
|
|
20404
|
-
"flag-icons": "
|
|
20468
|
+
"@ngx-translate/core": "17.x",
|
|
20469
|
+
"@ngx-translate/http-loader": "17.x",
|
|
20470
|
+
"flag-icons": "~7.5.0",
|
|
20405
20471
|
rxjs: "7.x",
|
|
20406
20472
|
"zone.js": "*",
|
|
20407
20473
|
tailwindcss: "3.x"
|
|
20408
20474
|
};
|
|
20409
20475
|
var dependencies = {
|
|
20410
20476
|
"@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/
|
|
20477
|
+
"@camptocamp/ogc-client": "1.3.1-dev.200c02a",
|
|
20478
|
+
"@duckdb/duckdb-wasm": "~1.32.0",
|
|
20479
|
+
"@geospatial-sdk/core": "0.0.5-dev.78",
|
|
20480
|
+
"@geospatial-sdk/geocoding": "0.0.5-dev.78",
|
|
20481
|
+
"@geospatial-sdk/legend": "0.0.5-dev.78",
|
|
20482
|
+
"@geospatial-sdk/openlayers": "0.0.5-dev.78",
|
|
20416
20483
|
"@ltd/j-toml": "~1.38.0",
|
|
20417
20484
|
"@messageformat/core": "~3.4.0",
|
|
20418
20485
|
"@ng-icons/core": "~32.5.0",
|
|
@@ -20420,7 +20487,7 @@ var dependencies = {
|
|
|
20420
20487
|
"@ng-icons/material-icons": "~32.5.0",
|
|
20421
20488
|
"@ng-icons/tabler-icons": "~32.4.0",
|
|
20422
20489
|
"@rgrove/parse-xml": "4.2.0",
|
|
20423
|
-
|
|
20490
|
+
"apache-arrow": "~17.0.0",
|
|
20424
20491
|
"chart.js": "~4.5.1",
|
|
20425
20492
|
"chroma-js": "~3.2.0",
|
|
20426
20493
|
"date-fns": "4.1.0",
|
|
@@ -20433,7 +20500,6 @@ var dependencies = {
|
|
|
20433
20500
|
"ngx-dropzone": "~3.1.0",
|
|
20434
20501
|
"ngx-translate-messageformat-compiler": "~7.2.0",
|
|
20435
20502
|
ol: "~10.8.0",
|
|
20436
|
-
papaparse: "~5.5.3",
|
|
20437
20503
|
proj4: "~2.20.4",
|
|
20438
20504
|
rdflib: "~2.3.5",
|
|
20439
20505
|
semver: "~7.7.4",
|
|
@@ -20678,6 +20744,7 @@ class ElasticsearchService {
|
|
|
20678
20744
|
values: uuids,
|
|
20679
20745
|
},
|
|
20680
20746
|
},
|
|
20747
|
+
size: uuids.length,
|
|
20681
20748
|
};
|
|
20682
20749
|
}
|
|
20683
20750
|
getRelatedRecordPayload(record, size = 6, _source = [...ES_SOURCE_SUMMARY, 'createDate']) {
|
|
@@ -20785,7 +20852,13 @@ class ElasticsearchService {
|
|
|
20785
20852
|
isCurrentSearchLang() {
|
|
20786
20853
|
return this.metadataLang === 'current';
|
|
20787
20854
|
}
|
|
20788
|
-
|
|
20855
|
+
findSpatialFilterExtent(filters) {
|
|
20856
|
+
if (typeof filters === 'string') {
|
|
20857
|
+
return undefined;
|
|
20858
|
+
}
|
|
20859
|
+
return Object.values(filters).find(isBoundingBox);
|
|
20860
|
+
}
|
|
20861
|
+
filtersToQuery(filters, spatialFilterExtent = this.findSpatialFilterExtent(filters)) {
|
|
20789
20862
|
const addQuote = (key) => (/^\/.+\/$/.test(key) ? key : `"${key}"`);
|
|
20790
20863
|
const makeQuery = (filter) => {
|
|
20791
20864
|
if (typeof filter === 'string') {
|
|
@@ -20804,7 +20877,9 @@ class ElasticsearchService {
|
|
|
20804
20877
|
? filters
|
|
20805
20878
|
: Object.keys(filters)
|
|
20806
20879
|
.filter((fieldname) => fieldname !== 'gn-ui-crossFieldFilter')
|
|
20880
|
+
.filter((fieldname) => !isBoundingBox(filters[fieldname]))
|
|
20807
20881
|
.filter((fieldname) => !isDateRange(filters[fieldname]))
|
|
20882
|
+
.filter((fieldname) => !Array.isArray(filters[fieldname]))
|
|
20808
20883
|
.filter((fieldname) => filters[fieldname] &&
|
|
20809
20884
|
JSON.stringify(filters[fieldname]) !== '{}')
|
|
20810
20885
|
.map((fieldname) => `${fieldname}:(${makeQuery(filters[fieldname])})`)
|
|
@@ -20840,6 +20915,21 @@ class ElasticsearchService {
|
|
|
20840
20915
|
},
|
|
20841
20916
|
},
|
|
20842
20917
|
},
|
|
20918
|
+
spatialFilterExtent && {
|
|
20919
|
+
geo_shape: {
|
|
20920
|
+
geom: {
|
|
20921
|
+
shape: {
|
|
20922
|
+
type: 'envelope',
|
|
20923
|
+
// spatialFilterExtent is [minX, minY, maxX, maxY]; envelope coordinates are [top-left, bottom-right]
|
|
20924
|
+
coordinates: [
|
|
20925
|
+
[spatialFilterExtent[0], spatialFilterExtent[3]],
|
|
20926
|
+
[spatialFilterExtent[2], spatialFilterExtent[1]],
|
|
20927
|
+
],
|
|
20928
|
+
},
|
|
20929
|
+
relation: 'intersects',
|
|
20930
|
+
},
|
|
20931
|
+
},
|
|
20932
|
+
},
|
|
20843
20933
|
].filter(Boolean);
|
|
20844
20934
|
return queryParts.length > 0 ? queryParts : undefined;
|
|
20845
20935
|
}
|
|
@@ -20866,7 +20956,9 @@ class ElasticsearchService {
|
|
|
20866
20956
|
},
|
|
20867
20957
|
});
|
|
20868
20958
|
}
|
|
20869
|
-
|
|
20959
|
+
// a spatial extent filter takes precedence over the preference geometry for boosting
|
|
20960
|
+
const spatialFilterExtent = this.findSpatialFilterExtent(fieldSearchFilters);
|
|
20961
|
+
const queryFilters = this.filtersToQuery(fieldSearchFilters, spatialFilterExtent);
|
|
20870
20962
|
if (queryFilters) {
|
|
20871
20963
|
filter.push(...queryFilters);
|
|
20872
20964
|
}
|
|
@@ -20877,7 +20969,10 @@ class ElasticsearchService {
|
|
|
20877
20969
|
},
|
|
20878
20970
|
});
|
|
20879
20971
|
}
|
|
20880
|
-
|
|
20972
|
+
const boostGeometry = spatialFilterExtent
|
|
20973
|
+
? bboxToPolygon(spatialFilterExtent)
|
|
20974
|
+
: geometry;
|
|
20975
|
+
if (boostGeometry) {
|
|
20881
20976
|
// boosts applied using the filter geometry:
|
|
20882
20977
|
// * records completely within the geometry receive a boost of 5
|
|
20883
20978
|
// * records intersecting the geometry receive a boost of 2
|
|
@@ -20886,7 +20981,7 @@ class ElasticsearchService {
|
|
|
20886
20981
|
should.push({
|
|
20887
20982
|
geo_shape: {
|
|
20888
20983
|
geom: {
|
|
20889
|
-
shape:
|
|
20984
|
+
shape: boostGeometry,
|
|
20890
20985
|
relation: 'within',
|
|
20891
20986
|
},
|
|
20892
20987
|
boost: 5.0,
|
|
@@ -20894,7 +20989,7 @@ class ElasticsearchService {
|
|
|
20894
20989
|
}, {
|
|
20895
20990
|
geo_shape: {
|
|
20896
20991
|
geom: {
|
|
20897
|
-
shape:
|
|
20992
|
+
shape: boostGeometry,
|
|
20898
20993
|
relation: 'intersects',
|
|
20899
20994
|
},
|
|
20900
20995
|
boost: 2.0,
|
|
@@ -20903,7 +20998,7 @@ class ElasticsearchService {
|
|
|
20903
20998
|
// this will boost the results variably depending on their distance from the given geometry
|
|
20904
20999
|
// note: this takes into account the `location` field of a record; this is generally the center of all spatial extents
|
|
20905
21000
|
// combined, and thus the actual size/coverage of the record spatial extent isn't relevant here
|
|
20906
|
-
const bbox = getGeometryBoundingBox(
|
|
21001
|
+
const bbox = getGeometryBoundingBox(boostGeometry);
|
|
20907
21002
|
const center = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2];
|
|
20908
21003
|
const northToCenter = new LineString([
|
|
20909
21004
|
[center[0], bbox[3]],
|
|
@@ -21229,7 +21324,7 @@ class Gn4Repository {
|
|
|
21229
21324
|
}
|
|
21230
21325
|
getRecord(uniqueIdentifier) {
|
|
21231
21326
|
return this.gn4SearchApi
|
|
21232
|
-
.search('bucket', ['fcats', 'hassources'], JSON.stringify(this.gn4SearchHelper.getMetadataByIdsPayload([uniqueIdentifier])))
|
|
21327
|
+
.search('bucket', ['fcats', 'hassources', 'siblings', 'associated'], JSON.stringify(this.gn4SearchHelper.getMetadataByIdsPayload([uniqueIdentifier])))
|
|
21233
21328
|
.pipe(map$1((results) => results.hits.hits[0]), switchMap((record) => record ? this.gn4Mapper.readRecord(record) : of(null)));
|
|
21234
21329
|
}
|
|
21235
21330
|
getMultipleRecords(uniqueIdentifiers) {
|
|
@@ -21283,19 +21378,28 @@ class Gn4Repository {
|
|
|
21283
21378
|
.search('bucket', null, JSON.stringify(this.gn4SearchHelper.getRelatedRecordPayload(similarTo, 3)))
|
|
21284
21379
|
.pipe(switchMap((results) => this.gn4Mapper.readRecords(results.hits.hits)));
|
|
21285
21380
|
}
|
|
21286
|
-
|
|
21287
|
-
const
|
|
21288
|
-
|
|
21289
|
-
|
|
21290
|
-
|
|
21291
|
-
|
|
21292
|
-
|
|
21293
|
-
|
|
21294
|
-
|
|
21295
|
-
|
|
21296
|
-
|
|
21381
|
+
getLinkedRecords(record) {
|
|
21382
|
+
const siblings = (record.extras?.['siblings'] ?? []);
|
|
21383
|
+
const relations = [
|
|
21384
|
+
['source', (record.extras?.['sourcesIdentifiers'] ?? [])],
|
|
21385
|
+
['sourceOf', (record.extras?.['sourceOfIdentifiers'] ?? [])],
|
|
21386
|
+
['sibling', siblings.map(({ uniqueIdentifier }) => uniqueIdentifier)],
|
|
21387
|
+
[
|
|
21388
|
+
'associated',
|
|
21389
|
+
(record.extras?.['associatedIdentifiers'] ?? []),
|
|
21390
|
+
],
|
|
21391
|
+
];
|
|
21392
|
+
const requested = relations.filter(([, identifiers]) => identifiers.length > 0);
|
|
21393
|
+
if (requested.length === 0) {
|
|
21394
|
+
return of([]);
|
|
21297
21395
|
}
|
|
21298
|
-
return
|
|
21396
|
+
return forkJoin(requested.map(([relation, identifiers]) => this.getMultipleRecords(identifiers).pipe(map$1((records) => (records ?? []).map((record) => ({
|
|
21397
|
+
record,
|
|
21398
|
+
relation,
|
|
21399
|
+
associationType: relation === 'sibling'
|
|
21400
|
+
? siblings.find(({ uniqueIdentifier }) => uniqueIdentifier === record.uniqueIdentifier)?.associationType
|
|
21401
|
+
: undefined,
|
|
21402
|
+
}))), catchError(() => of([]))))).pipe(map$1((groups) => groups.flat()));
|
|
21299
21403
|
}
|
|
21300
21404
|
aggregate(params) {
|
|
21301
21405
|
// if aggregations are empty, return an empty object right away
|
|
@@ -22556,9 +22660,10 @@ const VECTOR_STYLE_DEFAULT = new InjectionToken('vectorStyleDefault', {
|
|
|
22556
22660
|
});
|
|
22557
22661
|
|
|
22558
22662
|
const DEFAULT_BASEMAP_LAYER = {
|
|
22559
|
-
type: '
|
|
22560
|
-
|
|
22561
|
-
|
|
22663
|
+
type: 'maplibre-style',
|
|
22664
|
+
styleUrl: `https://basemaps.cartocdn.com/gl/positron-gl-style/style.json`,
|
|
22665
|
+
clickable: false,
|
|
22666
|
+
hoverable: false,
|
|
22562
22667
|
};
|
|
22563
22668
|
const DEFAULT_VIEW = {
|
|
22564
22669
|
center: [0, 15],
|
|
@@ -23273,6 +23378,7 @@ function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
23273
23378
|
'search_preset',
|
|
23274
23379
|
'advanced_filters',
|
|
23275
23380
|
'limit',
|
|
23381
|
+
'spatial_extent_max_file_size',
|
|
23276
23382
|
], warnings, errors);
|
|
23277
23383
|
const parsedSearchParams = parseMultiConfigSection(parsed, 'search_preset', ['name'], ['sort', 'filters'], warnings, errors);
|
|
23278
23384
|
searchConfig =
|
|
@@ -23290,6 +23396,7 @@ function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
23290
23396
|
})),
|
|
23291
23397
|
ADVANCED_FILTERS: parsedSearchSection.advanced_filters,
|
|
23292
23398
|
LIMIT: parsedSearchSection.limit,
|
|
23399
|
+
SPATIAL_EXTENT_MAX_FILE_SIZE: parsedSearchSection.spatial_extent_max_file_size,
|
|
23293
23400
|
};
|
|
23294
23401
|
const parsedMetadataQualitySection = parseConfigSection(parsed, 'metadata-quality', [], ['enabled'], warnings, errors);
|
|
23295
23402
|
metadataQualityConfig =
|
|
@@ -24547,12 +24654,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24547
24654
|
type: Input
|
|
24548
24655
|
}] } });
|
|
24549
24656
|
|
|
24657
|
+
function provideLocalizedDateAdapter() {
|
|
24658
|
+
return [
|
|
24659
|
+
{ provide: MAT_DATE_LOCALE, useValue: enUS },
|
|
24660
|
+
{ provide: MAT_DATE_FORMATS, useValue: MAT_DATE_FNS_FORMATS },
|
|
24661
|
+
{
|
|
24662
|
+
provide: DateAdapter,
|
|
24663
|
+
useFactory: () => {
|
|
24664
|
+
const dateService = inject(DateService);
|
|
24665
|
+
const adapter = new DateFnsAdapter();
|
|
24666
|
+
dateService
|
|
24667
|
+
.getDateFnsLocale()
|
|
24668
|
+
.then((locale) => adapter.setLocale(locale));
|
|
24669
|
+
return adapter;
|
|
24670
|
+
},
|
|
24671
|
+
},
|
|
24672
|
+
];
|
|
24673
|
+
}
|
|
24674
|
+
|
|
24550
24675
|
class DatePickerComponent {
|
|
24551
24676
|
constructor() {
|
|
24552
|
-
this.dateAdapter = inject(DateAdapter);
|
|
24553
|
-
this.translate = inject(TranslateService);
|
|
24554
24677
|
this.dateChange = new EventEmitter();
|
|
24555
|
-
this.dateAdapter.setLocale(this.translate.getCurrentLang());
|
|
24556
24678
|
}
|
|
24557
24679
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
24558
24680
|
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 +24682,19 @@ class DatePickerComponent {
|
|
|
24560
24682
|
provideNgIconsConfig({
|
|
24561
24683
|
size: '1.5rem',
|
|
24562
24684
|
}),
|
|
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 }); }
|
|
24685
|
+
provideLocalizedDateAdapter(),
|
|
24686
|
+
], 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
24687
|
}
|
|
24569
24688
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatePickerComponent, decorators: [{
|
|
24570
24689
|
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: [
|
|
24690
|
+
args: [{ selector: 'gn-ui-date-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [MatDatepickerModule, ButtonComponent, NgIconComponent], providers: [
|
|
24577
24691
|
provideIcons({ iconoirCalendar }),
|
|
24578
24692
|
provideNgIconsConfig({
|
|
24579
24693
|
size: '1.5rem',
|
|
24580
24694
|
}),
|
|
24581
|
-
|
|
24582
|
-
provide: MAT_DATE_LOCALE,
|
|
24583
|
-
useFactory: (locale) => locale,
|
|
24584
|
-
},
|
|
24695
|
+
provideLocalizedDateAdapter(),
|
|
24585
24696
|
], 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
|
-
}],
|
|
24697
|
+
}], propDecorators: { date: [{
|
|
24587
24698
|
type: Input
|
|
24588
24699
|
}], dateChange: [{
|
|
24589
24700
|
type: Output
|
|
@@ -24591,58 +24702,159 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24591
24702
|
|
|
24592
24703
|
class DateRangeDropdownComponent {
|
|
24593
24704
|
constructor() {
|
|
24594
|
-
this.
|
|
24705
|
+
this.scrollStrategies = inject(ScrollStrategyOptions);
|
|
24706
|
+
this.dateAdapter = inject(DateAdapter);
|
|
24707
|
+
this.dateFormats = inject(MAT_DATE_FORMATS);
|
|
24595
24708
|
this.cdr = inject(ChangeDetectorRef);
|
|
24596
|
-
this.
|
|
24597
|
-
this.
|
|
24598
|
-
this.
|
|
24709
|
+
this.dateRange = {};
|
|
24710
|
+
this.dateRangeChange = new EventEmitter();
|
|
24711
|
+
this.overlayPositions = [
|
|
24712
|
+
{
|
|
24713
|
+
originX: 'start',
|
|
24714
|
+
originY: 'bottom',
|
|
24715
|
+
overlayX: 'start',
|
|
24716
|
+
overlayY: 'top',
|
|
24717
|
+
offsetY: 8,
|
|
24718
|
+
},
|
|
24719
|
+
{
|
|
24720
|
+
originX: 'start',
|
|
24721
|
+
originY: 'top',
|
|
24722
|
+
overlayX: 'start',
|
|
24723
|
+
overlayY: 'bottom',
|
|
24724
|
+
offsetY: -8,
|
|
24725
|
+
},
|
|
24726
|
+
];
|
|
24727
|
+
this.scrollStrategy = this.scrollStrategies.reposition();
|
|
24728
|
+
this.overlayOpen = false;
|
|
24729
|
+
this.expandedBound = 'start';
|
|
24730
|
+
this.invalidBounds = { start: false, end: false };
|
|
24731
|
+
// redraw when the lazily loaded UI locale reaches the adapter
|
|
24732
|
+
this.dateAdapter.localeChanges
|
|
24733
|
+
.pipe(takeUntilDestroyed())
|
|
24734
|
+
.subscribe(() => this.cdr.markForCheck());
|
|
24599
24735
|
}
|
|
24600
|
-
|
|
24601
|
-
this.
|
|
24736
|
+
get startDate() {
|
|
24737
|
+
return this.dateRange.start;
|
|
24602
24738
|
}
|
|
24603
|
-
|
|
24604
|
-
|
|
24605
|
-
|
|
24606
|
-
|
|
24607
|
-
|
|
24608
|
-
|
|
24609
|
-
|
|
24610
|
-
|
|
24739
|
+
get endDate() {
|
|
24740
|
+
return this.dateRange.end;
|
|
24741
|
+
}
|
|
24742
|
+
get selectedDatesCount() {
|
|
24743
|
+
return (this.startDate ? 1 : 0) + (this.endDate ? 1 : 0);
|
|
24744
|
+
}
|
|
24745
|
+
openOverlay() {
|
|
24746
|
+
this.expandedBound = 'start';
|
|
24747
|
+
this.overlayOpen = true;
|
|
24748
|
+
}
|
|
24749
|
+
closeOverlay() {
|
|
24750
|
+
this.overlayOpen = false;
|
|
24751
|
+
}
|
|
24752
|
+
toggleBound(bound) {
|
|
24753
|
+
this.expandedBound = this.expandedBound === bound ? null : bound;
|
|
24754
|
+
}
|
|
24755
|
+
formatDate(date) {
|
|
24756
|
+
return this.dateAdapter.format(date, this.dateFormats.display.dateInput);
|
|
24757
|
+
}
|
|
24758
|
+
onDateInput(bound, event) {
|
|
24759
|
+
const typedText = event.target.value.trim();
|
|
24760
|
+
const date = typedText
|
|
24761
|
+
? this.dateAdapter.parse(typedText, this.dateFormats.parse.dateInput)
|
|
24762
|
+
: null;
|
|
24763
|
+
if (!this.isAcceptable(bound, typedText, date)) {
|
|
24764
|
+
// keep the text as typed and flag it; the filter stays on its last value
|
|
24765
|
+
this.invalidBounds = { ...this.invalidBounds, [bound]: true };
|
|
24766
|
+
return;
|
|
24767
|
+
}
|
|
24768
|
+
if (bound === 'start')
|
|
24769
|
+
this.setStartDate(date);
|
|
24770
|
+
else
|
|
24771
|
+
this.setEndDate(date);
|
|
24772
|
+
}
|
|
24773
|
+
normalizeDateInput(bound, event) {
|
|
24774
|
+
if (this.invalidBounds[bound])
|
|
24775
|
+
return;
|
|
24776
|
+
const date = bound === 'start' ? this.startDate : this.endDate;
|
|
24777
|
+
const input = event.target;
|
|
24778
|
+
input.value = date ? this.formatDate(date) : '';
|
|
24779
|
+
}
|
|
24780
|
+
isAcceptable(bound, typedText, date) {
|
|
24781
|
+
if (!typedText)
|
|
24782
|
+
return true;
|
|
24783
|
+
if (!date || !this.dateAdapter.isValid(date))
|
|
24784
|
+
return false;
|
|
24785
|
+
return bound === 'start'
|
|
24786
|
+
? !this.endDate || date <= this.endDate
|
|
24787
|
+
: !this.startDate || date >= this.startDate;
|
|
24788
|
+
}
|
|
24789
|
+
setStartDate(date) {
|
|
24790
|
+
this.invalidBounds = { ...this.invalidBounds, start: false };
|
|
24791
|
+
this.applyDateRange({ ...this.dateRange, start: date });
|
|
24792
|
+
this.expandedBound = 'end';
|
|
24793
|
+
}
|
|
24794
|
+
setEndDate(date) {
|
|
24795
|
+
this.invalidBounds = { ...this.invalidBounds, end: false };
|
|
24796
|
+
this.applyDateRange({ ...this.dateRange, end: date });
|
|
24797
|
+
this.expandedBound = null;
|
|
24798
|
+
}
|
|
24799
|
+
clearDates(event) {
|
|
24800
|
+
this.invalidBounds = { start: false, end: false };
|
|
24801
|
+
this.expandedBound = 'start';
|
|
24802
|
+
this.applyDateRange({});
|
|
24803
|
+
propagateToDocumentOnly(event);
|
|
24804
|
+
}
|
|
24805
|
+
applyDateRange(dateRange) {
|
|
24806
|
+
this.dateRange = {
|
|
24807
|
+
...(dateRange.start && { start: dateRange.start }),
|
|
24808
|
+
...(dateRange.end && { end: dateRange.end }),
|
|
24809
|
+
};
|
|
24810
|
+
this.dateRangeChange.emit(this.dateRange);
|
|
24611
24811
|
}
|
|
24612
24812
|
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: "
|
|
24813
|
+
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
24814
|
provideIcons({
|
|
24615
|
-
|
|
24815
|
+
iconoirCalendar,
|
|
24816
|
+
matClose,
|
|
24616
24817
|
matExpandLess,
|
|
24818
|
+
matExpandMore,
|
|
24819
|
+
}),
|
|
24820
|
+
provideNgIconsConfig({
|
|
24821
|
+
size: '1.5rem',
|
|
24617
24822
|
}),
|
|
24618
|
-
|
|
24823
|
+
provideLocalizedDateAdapter(),
|
|
24824
|
+
], 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
24825
|
}
|
|
24620
24826
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangeDropdownComponent, decorators: [{
|
|
24621
24827
|
type: Component,
|
|
24622
24828
|
args: [{ selector: 'gn-ui-date-range-dropdown', standalone: true, imports: [
|
|
24623
|
-
|
|
24624
|
-
MatNativeDateModule,
|
|
24625
|
-
MatDatepickerModule,
|
|
24829
|
+
AutofocusDirective,
|
|
24626
24830
|
ButtonComponent,
|
|
24831
|
+
MatDatepickerModule,
|
|
24832
|
+
NgIcon,
|
|
24833
|
+
NgTemplateOutlet,
|
|
24834
|
+
OverlayModule,
|
|
24835
|
+
TranslateDirective,
|
|
24836
|
+
TranslatePipe,
|
|
24627
24837
|
], providers: [
|
|
24628
24838
|
provideIcons({
|
|
24629
|
-
|
|
24839
|
+
iconoirCalendar,
|
|
24840
|
+
matClose,
|
|
24630
24841
|
matExpandLess,
|
|
24842
|
+
matExpandMore,
|
|
24631
24843
|
}),
|
|
24632
|
-
|
|
24633
|
-
|
|
24844
|
+
provideNgIconsConfig({
|
|
24845
|
+
size: '1.5rem',
|
|
24846
|
+
}),
|
|
24847
|
+
provideLocalizedDateAdapter(),
|
|
24848
|
+
], 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"] }]
|
|
24849
|
+
}], ctorParameters: () => [], propDecorators: { title: [{
|
|
24634
24850
|
type: Input
|
|
24635
|
-
}],
|
|
24851
|
+
}], dateRange: [{
|
|
24636
24852
|
type: Input
|
|
24637
|
-
}],
|
|
24638
|
-
type: Input
|
|
24639
|
-
}], startDateChange: [{
|
|
24640
|
-
type: Output
|
|
24641
|
-
}], endDateChange: [{
|
|
24853
|
+
}], dateRangeChange: [{
|
|
24642
24854
|
type: Output
|
|
24643
|
-
}],
|
|
24855
|
+
}], overlayOrigin: [{
|
|
24644
24856
|
type: ViewChild,
|
|
24645
|
-
args: ['
|
|
24857
|
+
args: ['overlayOrigin']
|
|
24646
24858
|
}] } });
|
|
24647
24859
|
|
|
24648
24860
|
class DateRangePickerComponent {
|
|
@@ -24656,20 +24868,17 @@ class DateRangePickerComponent {
|
|
|
24656
24868
|
provideNgIconsConfig({
|
|
24657
24869
|
size: '1.5rem',
|
|
24658
24870
|
}),
|
|
24659
|
-
|
|
24871
|
+
provideLocalizedDateAdapter(),
|
|
24872
|
+
], 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
24873
|
}
|
|
24661
24874
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangePickerComponent, decorators: [{
|
|
24662
24875
|
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: [
|
|
24876
|
+
args: [{ selector: 'gn-ui-date-range-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [MatDatepickerModule, ButtonComponent, NgIconComponent], providers: [
|
|
24669
24877
|
provideIcons({ iconoirCalendar }),
|
|
24670
24878
|
provideNgIconsConfig({
|
|
24671
24879
|
size: '1.5rem',
|
|
24672
24880
|
}),
|
|
24881
|
+
provideLocalizedDateAdapter(),
|
|
24673
24882
|
], 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
24883
|
}], propDecorators: { startDate: [{
|
|
24675
24884
|
type: Input
|
|
@@ -24687,28 +24896,68 @@ class DragAndDropFileInputComponent {
|
|
|
24687
24896
|
constructor() {
|
|
24688
24897
|
this.placeholder = placeholder;
|
|
24689
24898
|
this.accept = '*';
|
|
24899
|
+
this.maxFileSizeMb = null;
|
|
24900
|
+
this.icon = null;
|
|
24901
|
+
this.dropzoneBackgroundColor = null;
|
|
24902
|
+
this.textClass = '';
|
|
24903
|
+
this.extraClass = '';
|
|
24904
|
+
this.showFileName = true;
|
|
24690
24905
|
this.fileChange = new EventEmitter();
|
|
24906
|
+
this.errorChange = new EventEmitter();
|
|
24691
24907
|
this.selectedFile = null;
|
|
24692
24908
|
}
|
|
24693
24909
|
get fileName() {
|
|
24694
24910
|
return this.selectedFile && this.selectedFile.name;
|
|
24695
24911
|
}
|
|
24912
|
+
get maxFileSizeBytes() {
|
|
24913
|
+
return typeof this.maxFileSizeMb === 'number'
|
|
24914
|
+
? megabytesToBytes(this.maxFileSizeMb)
|
|
24915
|
+
: null;
|
|
24916
|
+
}
|
|
24696
24917
|
selectFile(event) {
|
|
24918
|
+
if (event.rejectedFiles?.length) {
|
|
24919
|
+
const reason = event.rejectedFiles[0].reason;
|
|
24920
|
+
this.errorChange.emit(reason === 'size' ? 'file-too-large' : 'invalid-extension');
|
|
24921
|
+
return;
|
|
24922
|
+
}
|
|
24697
24923
|
this.selectedFile = event.addedFiles[0];
|
|
24698
24924
|
this.fileChange.emit(this.selectedFile);
|
|
24699
24925
|
}
|
|
24926
|
+
openFileSelector() {
|
|
24927
|
+
this.dropzone.showFileSelector();
|
|
24928
|
+
}
|
|
24929
|
+
clear() {
|
|
24930
|
+
this.selectedFile = null;
|
|
24931
|
+
}
|
|
24700
24932
|
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
|
|
24933
|
+
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
24934
|
}
|
|
24703
24935
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DragAndDropFileInputComponent, decorators: [{
|
|
24704
24936
|
type: Component,
|
|
24705
|
-
args: [{ selector: 'gn-ui-drag-and-drop-file-input', standalone: true, imports: [NgxDropzoneModule], template: "<div
|
|
24937
|
+
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
24938
|
}], propDecorators: { placeholder: [{
|
|
24707
24939
|
type: Input
|
|
24708
24940
|
}], accept: [{
|
|
24709
24941
|
type: Input
|
|
24942
|
+
}], maxFileSizeMb: [{
|
|
24943
|
+
type: Input
|
|
24944
|
+
}], icon: [{
|
|
24945
|
+
type: Input
|
|
24946
|
+
}], dropzoneBackgroundColor: [{
|
|
24947
|
+
type: Input
|
|
24948
|
+
}], textClass: [{
|
|
24949
|
+
type: Input
|
|
24950
|
+
}], extraClass: [{
|
|
24951
|
+
type: Input
|
|
24952
|
+
}], showFileName: [{
|
|
24953
|
+
type: Input
|
|
24710
24954
|
}], fileChange: [{
|
|
24711
24955
|
type: Output
|
|
24956
|
+
}], errorChange: [{
|
|
24957
|
+
type: Output
|
|
24958
|
+
}], dropzone: [{
|
|
24959
|
+
type: ViewChild,
|
|
24960
|
+
args: [NgxDropzoneComponent]
|
|
24712
24961
|
}] } });
|
|
24713
24962
|
|
|
24714
24963
|
class DropdownMultiselectComponent {
|
|
@@ -24863,7 +25112,7 @@ class DropdownMultiselectComponent {
|
|
|
24863
25112
|
matExpandMore,
|
|
24864
25113
|
matExpandLess,
|
|
24865
25114
|
}),
|
|
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-
|
|
25115
|
+
], 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
25116
|
}
|
|
24868
25117
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DropdownMultiselectComponent, decorators: [{
|
|
24869
25118
|
type: Component,
|
|
@@ -24873,7 +25122,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24873
25122
|
matExpandMore,
|
|
24874
25123
|
matExpandLess,
|
|
24875
25124
|
}),
|
|
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-
|
|
25125
|
+
], 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
25126
|
}], propDecorators: { title: [{
|
|
24878
25127
|
type: Input
|
|
24879
25128
|
}], choices: [{
|
|
@@ -25045,7 +25294,7 @@ class DropdownSelectorComponent {
|
|
|
25045
25294
|
provideNgIconsConfig({
|
|
25046
25295
|
size: '1.5em',
|
|
25047
25296
|
}),
|
|
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$
|
|
25297
|
+
], 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
25298
|
}
|
|
25050
25299
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DropdownSelectorComponent, decorators: [{
|
|
25051
25300
|
type: Component,
|
|
@@ -25785,6 +26034,175 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
25785
26034
|
type: Output
|
|
25786
26035
|
}] } });
|
|
25787
26036
|
|
|
26037
|
+
marker('search.filters.spatialExtent.import');
|
|
26038
|
+
marker('search.filters.spatialExtent.helpText');
|
|
26039
|
+
marker('search.filters.spatialExtent.error.title');
|
|
26040
|
+
const LABEL_FROM_FILE = marker('search.filters.spatialExtent.bboxFromFile');
|
|
26041
|
+
const LABEL_FROM_FILE_DELETE = marker('search.filters.spatialExtent.bboxFromFileDelete');
|
|
26042
|
+
const LABEL_INITIAL = marker('search.filters.spatialExtent.bboxInitial');
|
|
26043
|
+
const LABEL_INITIAL_DELETE = marker('search.filters.spatialExtent.bboxInitialDelete');
|
|
26044
|
+
class SpatialExtentDropdownComponent {
|
|
26045
|
+
constructor() {
|
|
26046
|
+
this.cd = inject(ChangeDetectorRef);
|
|
26047
|
+
this.scrollStrategies = inject(ScrollStrategyOptions);
|
|
26048
|
+
this.maxFileSizeMb = null;
|
|
26049
|
+
this.bboxChange = new EventEmitter();
|
|
26050
|
+
this.errorChange = new EventEmitter();
|
|
26051
|
+
this.bbox = null;
|
|
26052
|
+
this.fileName = '';
|
|
26053
|
+
this.overlayPositions = [
|
|
26054
|
+
{
|
|
26055
|
+
originX: 'start',
|
|
26056
|
+
originY: 'bottom',
|
|
26057
|
+
overlayX: 'start',
|
|
26058
|
+
overlayY: 'top',
|
|
26059
|
+
offsetY: 8,
|
|
26060
|
+
},
|
|
26061
|
+
{
|
|
26062
|
+
originX: 'start',
|
|
26063
|
+
originY: 'top',
|
|
26064
|
+
overlayX: 'start',
|
|
26065
|
+
overlayY: 'bottom',
|
|
26066
|
+
offsetY: -8,
|
|
26067
|
+
},
|
|
26068
|
+
];
|
|
26069
|
+
this.scrollStrategy = this.scrollStrategies.reposition();
|
|
26070
|
+
this.overlayOpen = false;
|
|
26071
|
+
this.overlayMinWidth = 'none';
|
|
26072
|
+
this.errorKey = null;
|
|
26073
|
+
}
|
|
26074
|
+
set initialBbox(value) {
|
|
26075
|
+
if (!this.bbox && value) {
|
|
26076
|
+
this.bbox = value;
|
|
26077
|
+
}
|
|
26078
|
+
}
|
|
26079
|
+
get hasSelection() {
|
|
26080
|
+
return !!this.bbox;
|
|
26081
|
+
}
|
|
26082
|
+
get selectionLabelKey() {
|
|
26083
|
+
return this.fileName ? LABEL_FROM_FILE : LABEL_INITIAL;
|
|
26084
|
+
}
|
|
26085
|
+
get selectionDeleteLabelKey() {
|
|
26086
|
+
return this.fileName ? LABEL_FROM_FILE_DELETE : LABEL_INITIAL_DELETE;
|
|
26087
|
+
}
|
|
26088
|
+
get selectionLabelParams() {
|
|
26089
|
+
return { fileName: this.fileName };
|
|
26090
|
+
}
|
|
26091
|
+
openOverlay() {
|
|
26092
|
+
this.overlayMinWidth =
|
|
26093
|
+
this.overlayOrigin.elementRef.nativeElement.getBoundingClientRect()
|
|
26094
|
+
.width + 'px';
|
|
26095
|
+
this.overlayOpen = true;
|
|
26096
|
+
}
|
|
26097
|
+
closeOverlay() {
|
|
26098
|
+
this.overlayOpen = false;
|
|
26099
|
+
}
|
|
26100
|
+
toggleOverlay() {
|
|
26101
|
+
if (this.overlayOpen) {
|
|
26102
|
+
this.closeOverlay();
|
|
26103
|
+
}
|
|
26104
|
+
else {
|
|
26105
|
+
this.openOverlay();
|
|
26106
|
+
}
|
|
26107
|
+
}
|
|
26108
|
+
async handleFileSelected(file) {
|
|
26109
|
+
this.errorKey = null;
|
|
26110
|
+
let content;
|
|
26111
|
+
try {
|
|
26112
|
+
content = await readFileAsText(file);
|
|
26113
|
+
const parsed = JSON.parse(content);
|
|
26114
|
+
const geometry = getGeometryFromGeoJSON(parsed);
|
|
26115
|
+
if (!geometry) {
|
|
26116
|
+
this.setError(marker('search.filters.spatialExtent.error.noGeometry'));
|
|
26117
|
+
return;
|
|
26118
|
+
}
|
|
26119
|
+
const bbox = getGeometryBoundingBox(geometry);
|
|
26120
|
+
this.bbox = bbox;
|
|
26121
|
+
this.fileName = file.name;
|
|
26122
|
+
this.bboxChange.emit(bbox);
|
|
26123
|
+
this.cd.markForCheck();
|
|
26124
|
+
}
|
|
26125
|
+
catch {
|
|
26126
|
+
this.setError(marker('search.filters.spatialExtent.error.invalidFormat'));
|
|
26127
|
+
return;
|
|
26128
|
+
}
|
|
26129
|
+
}
|
|
26130
|
+
handleFileError(error) {
|
|
26131
|
+
if (error === 'file-too-large') {
|
|
26132
|
+
this.setError(marker('search.filters.spatialExtent.error.fileTooLarge'), {
|
|
26133
|
+
maxSize: this.maxFileSizeMb,
|
|
26134
|
+
});
|
|
26135
|
+
}
|
|
26136
|
+
else {
|
|
26137
|
+
this.setError(marker('search.filters.spatialExtent.error.invalidFormat'));
|
|
26138
|
+
}
|
|
26139
|
+
}
|
|
26140
|
+
setError(errorKey, params) {
|
|
26141
|
+
this.errorKey = errorKey;
|
|
26142
|
+
this.errorChange.emit({ key: errorKey, params });
|
|
26143
|
+
this.cd.markForCheck();
|
|
26144
|
+
}
|
|
26145
|
+
removeSelection(event) {
|
|
26146
|
+
this.bbox = null;
|
|
26147
|
+
this.fileName = '';
|
|
26148
|
+
this.errorKey = null;
|
|
26149
|
+
this.fileInput?.clear();
|
|
26150
|
+
this.bboxChange.emit(null);
|
|
26151
|
+
propagateToDocumentOnly(event);
|
|
26152
|
+
}
|
|
26153
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
26154
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: SpatialExtentDropdownComponent, isStandalone: true, selector: "gn-ui-spatial-extent-dropdown", inputs: { title: "title", maxFileSizeMb: "maxFileSizeMb", initialBbox: "initialBbox" }, outputs: { bboxChange: "bboxChange", errorChange: "errorChange" }, providers: [
|
|
26155
|
+
provideIcons({
|
|
26156
|
+
iconoirCheckCircle,
|
|
26157
|
+
iconoirImport,
|
|
26158
|
+
iconoirSquareDashed,
|
|
26159
|
+
iconoirTrash,
|
|
26160
|
+
matClose,
|
|
26161
|
+
matExpandLess,
|
|
26162
|
+
matExpandMore,
|
|
26163
|
+
}),
|
|
26164
|
+
], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "fileInput", first: true, predicate: DragAndDropFileInputComponent, descendants: true }], ngImport: i0, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelection) {\n <div\n class=\"bg-primary-lighter text-white shrink-0 rounded-full font-bold text-[12px] w-[22px] h-[22px] flex items-center justify-center\"\n >\n 1\n </div>\n }\n </div>\n <button class=\"h-6 w-6 flex items-center\" data-cy=\"clearSelection\">\n @if (hasSelection && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"removeSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-400 rounded shadow-lg pt-[12px] px-[16px] overlay-container w-[280px] flex flex-col gap-2\"\n [style.min-width]=\"overlayMinWidth\"\n >\n <div class=\"text-xs text-black\">\n {{ 'search.filters.spatialExtent.helpText' | translate }}\n </div>\n\n <div\n [class]=\"\n (hasSelection\n ? 'border-b-[1px] border-gray-400 pb-[20px]'\n : 'pb-[8px]') + ' text-black'\n \"\n >\n <gn-ui-drag-and-drop-file-input\n accept=\".json,.geojson\"\n [placeholder]=\"'search.filters.spatialExtent.import' | translate\"\n [maxFileSizeMb]=\"maxFileSizeMb\"\n [showFileName]=\"false\"\n textClass=\"text-black truncate\"\n dropzoneBackgroundColor=\"transparent\"\n icon=\"iconoirImport\"\n extraClass=\"gn-ui-gray-outline-input w-full !h-[32px] px-[8px] py-[4px] justify-between\"\n (fileChange)=\"handleFileSelected($event)\"\n (errorChange)=\"handleFileError($event)\"\n data-cy=\"importGeojson\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n\n @if (hasSelection) {\n <gn-ui-button\n [type]=\"'primary-light'\"\n [title]=\"selectionDeleteLabelKey | translate: selectionLabelParams\"\n (buttonClick)=\"removeSelection($event)\"\n extraClass=\"group gap-x-2 w-full h-[28px] px-[8px] py-[4px] mb-[12px] bg-primary-lighter text-white\"\n data-test=\"spatial-extent-selected-item\"\n >\n <ng-icon\n name=\"iconoirSquareDashed\"\n class=\"shrink-0\"\n size=\"15px\"\n ></ng-icon>\n <span class=\"group-hover:hidden truncate text-sm m-auto\">\n {{ selectionLabelKey | translate: selectionLabelParams }}\n </span>\n <span class=\"hidden group-hover:block truncate text-sm m-auto\">\n {{ selectionDeleteLabelKey | translate: selectionLabelParams }}\n </span>\n <ng-icon\n name=\"iconoirCheckCircle\"\n class=\"group-hover:hidden shrink-0\"\n size=\"14px\"\n ></ng-icon>\n <ng-icon\n name=\"iconoirTrash\"\n class=\"!hidden group-hover:!block shrink-0\"\n size=\"15px\"\n ></ng-icon>\n </gn-ui-button>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: DragAndDropFileInputComponent, selector: "gn-ui-drag-and-drop-file-input", inputs: ["placeholder", "accept", "maxFileSizeMb", "icon", "dropzoneBackgroundColor", "textClass", "extraClass", "showFileName"], outputs: ["fileChange", "errorChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
26165
|
+
}
|
|
26166
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, decorators: [{
|
|
26167
|
+
type: Component,
|
|
26168
|
+
args: [{ selector: 'gn-ui-spatial-extent-dropdown', standalone: true, imports: [
|
|
26169
|
+
ButtonComponent,
|
|
26170
|
+
NgIcon,
|
|
26171
|
+
OverlayModule,
|
|
26172
|
+
TranslatePipe,
|
|
26173
|
+
DragAndDropFileInputComponent,
|
|
26174
|
+
], providers: [
|
|
26175
|
+
provideIcons({
|
|
26176
|
+
iconoirCheckCircle,
|
|
26177
|
+
iconoirImport,
|
|
26178
|
+
iconoirSquareDashed,
|
|
26179
|
+
iconoirTrash,
|
|
26180
|
+
matClose,
|
|
26181
|
+
matExpandLess,
|
|
26182
|
+
matExpandMore,
|
|
26183
|
+
}),
|
|
26184
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelection) {\n <div\n class=\"bg-primary-lighter text-white shrink-0 rounded-full font-bold text-[12px] w-[22px] h-[22px] flex items-center justify-center\"\n >\n 1\n </div>\n }\n </div>\n <button class=\"h-6 w-6 flex items-center\" data-cy=\"clearSelection\">\n @if (hasSelection && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"removeSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-400 rounded shadow-lg pt-[12px] px-[16px] overlay-container w-[280px] flex flex-col gap-2\"\n [style.min-width]=\"overlayMinWidth\"\n >\n <div class=\"text-xs text-black\">\n {{ 'search.filters.spatialExtent.helpText' | translate }}\n </div>\n\n <div\n [class]=\"\n (hasSelection\n ? 'border-b-[1px] border-gray-400 pb-[20px]'\n : 'pb-[8px]') + ' text-black'\n \"\n >\n <gn-ui-drag-and-drop-file-input\n accept=\".json,.geojson\"\n [placeholder]=\"'search.filters.spatialExtent.import' | translate\"\n [maxFileSizeMb]=\"maxFileSizeMb\"\n [showFileName]=\"false\"\n textClass=\"text-black truncate\"\n dropzoneBackgroundColor=\"transparent\"\n icon=\"iconoirImport\"\n extraClass=\"gn-ui-gray-outline-input w-full !h-[32px] px-[8px] py-[4px] justify-between\"\n (fileChange)=\"handleFileSelected($event)\"\n (errorChange)=\"handleFileError($event)\"\n data-cy=\"importGeojson\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n\n @if (hasSelection) {\n <gn-ui-button\n [type]=\"'primary-light'\"\n [title]=\"selectionDeleteLabelKey | translate: selectionLabelParams\"\n (buttonClick)=\"removeSelection($event)\"\n extraClass=\"group gap-x-2 w-full h-[28px] px-[8px] py-[4px] mb-[12px] bg-primary-lighter text-white\"\n data-test=\"spatial-extent-selected-item\"\n >\n <ng-icon\n name=\"iconoirSquareDashed\"\n class=\"shrink-0\"\n size=\"15px\"\n ></ng-icon>\n <span class=\"group-hover:hidden truncate text-sm m-auto\">\n {{ selectionLabelKey | translate: selectionLabelParams }}\n </span>\n <span class=\"hidden group-hover:block truncate text-sm m-auto\">\n {{ selectionDeleteLabelKey | translate: selectionLabelParams }}\n </span>\n <ng-icon\n name=\"iconoirCheckCircle\"\n class=\"group-hover:hidden shrink-0\"\n size=\"14px\"\n ></ng-icon>\n <ng-icon\n name=\"iconoirTrash\"\n class=\"!hidden group-hover:!block shrink-0\"\n size=\"15px\"\n ></ng-icon>\n </gn-ui-button>\n }\n </div>\n</ng-template>\n" }]
|
|
26185
|
+
}], propDecorators: { title: [{
|
|
26186
|
+
type: Input
|
|
26187
|
+
}], maxFileSizeMb: [{
|
|
26188
|
+
type: Input
|
|
26189
|
+
}], initialBbox: [{
|
|
26190
|
+
type: Input
|
|
26191
|
+
}], bboxChange: [{
|
|
26192
|
+
type: Output
|
|
26193
|
+
}], errorChange: [{
|
|
26194
|
+
type: Output
|
|
26195
|
+
}], overlayOrigin: [{
|
|
26196
|
+
type: ViewChild,
|
|
26197
|
+
args: ['overlayOrigin']
|
|
26198
|
+
}], overlay: [{
|
|
26199
|
+
type: ViewChild,
|
|
26200
|
+
args: [CdkConnectedOverlay]
|
|
26201
|
+
}], fileInput: [{
|
|
26202
|
+
type: ViewChild,
|
|
26203
|
+
args: [DragAndDropFileInputComponent]
|
|
26204
|
+
}] } });
|
|
26205
|
+
|
|
25788
26206
|
class CellPopinComponent {
|
|
25789
26207
|
constructor() {
|
|
25790
26208
|
this.scrollDispatcher = inject(ScrollDispatcher);
|
|
@@ -25861,7 +26279,7 @@ class CellPopinComponent {
|
|
|
25861
26279
|
this.isOpen = false;
|
|
25862
26280
|
}
|
|
25863
26281
|
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$
|
|
26282
|
+
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
26283
|
}
|
|
25866
26284
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CellPopinComponent, decorators: [{
|
|
25867
26285
|
type: Component,
|
|
@@ -27951,6 +28369,23 @@ class DateRangeSearchField extends SimpleSearchField {
|
|
|
27951
28369
|
return 'dateRange';
|
|
27952
28370
|
}
|
|
27953
28371
|
}
|
|
28372
|
+
class BoundingBoxSearchField extends SimpleSearchField {
|
|
28373
|
+
getAvailableValues() {
|
|
28374
|
+
return of([]);
|
|
28375
|
+
}
|
|
28376
|
+
getFiltersForValues(values) {
|
|
28377
|
+
return of({
|
|
28378
|
+
[this.esFieldName]: values.map(Number),
|
|
28379
|
+
});
|
|
28380
|
+
}
|
|
28381
|
+
getValuesForFilter(filters) {
|
|
28382
|
+
const filter = filters[this.esFieldName];
|
|
28383
|
+
return of(isBoundingBox(filter) ? filter : []);
|
|
28384
|
+
}
|
|
28385
|
+
getType() {
|
|
28386
|
+
return 'spatialExtent';
|
|
28387
|
+
}
|
|
28388
|
+
}
|
|
27954
28389
|
marker('search.filters.availableServices.view');
|
|
27955
28390
|
marker('search.filters.availableServices.download');
|
|
27956
28391
|
class AvailableServicesField extends SimpleSearchField {
|
|
@@ -28095,6 +28530,7 @@ marker('search.filters.producerOrg');
|
|
|
28095
28530
|
marker('search.filters.publisherOrg');
|
|
28096
28531
|
marker('search.filters.user');
|
|
28097
28532
|
marker('search.filters.changeDate');
|
|
28533
|
+
marker('search.filters.spatialExtent');
|
|
28098
28534
|
class FieldsService {
|
|
28099
28535
|
constructor() {
|
|
28100
28536
|
this.injector = inject(Injector);
|
|
@@ -28118,6 +28554,7 @@ class FieldsService {
|
|
|
28118
28554
|
user: new UserSearchField(this.injector),
|
|
28119
28555
|
changeDate: new DateRangeSearchField('changeDate', this.injector, 'desc'),
|
|
28120
28556
|
availableServices: new AvailableServicesField(this.injector),
|
|
28557
|
+
spatialExtent: new BoundingBoxSearchField('spatialExtent', this.injector),
|
|
28121
28558
|
};
|
|
28122
28559
|
}
|
|
28123
28560
|
get supportedFields() {
|
|
@@ -29380,7 +29817,7 @@ class ContactPillComponent {
|
|
|
29380
29817
|
this.overlayOpen = false;
|
|
29381
29818
|
}
|
|
29382
29819
|
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$
|
|
29820
|
+
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
29821
|
}
|
|
29385
29822
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ContactPillComponent, decorators: [{
|
|
29386
29823
|
type: Component,
|
|
@@ -31302,19 +31739,118 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31302
31739
|
type: Output
|
|
31303
31740
|
}] } });
|
|
31304
31741
|
|
|
31742
|
+
class NotificationsService {
|
|
31743
|
+
constructor() {
|
|
31744
|
+
this.notifications$ = new BehaviorSubject([]);
|
|
31745
|
+
}
|
|
31746
|
+
showNotification(content, timeoutMs, error) {
|
|
31747
|
+
error && console.error(error);
|
|
31748
|
+
const id = Math.floor(Math.random() * 1000000);
|
|
31749
|
+
this.notifications$.next([...this.notifications$.value, { ...content, id }]);
|
|
31750
|
+
if (typeof timeoutMs !== 'undefined') {
|
|
31751
|
+
setTimeout(() => {
|
|
31752
|
+
this.removeNotificationById(id);
|
|
31753
|
+
}, timeoutMs);
|
|
31754
|
+
}
|
|
31755
|
+
return id;
|
|
31756
|
+
}
|
|
31757
|
+
removeNotificationById(id) {
|
|
31758
|
+
this.notifications$.next(this.notifications$.value.filter((n) => n.id !== id));
|
|
31759
|
+
}
|
|
31760
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
31761
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, providedIn: 'root' }); }
|
|
31762
|
+
}
|
|
31763
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, decorators: [{
|
|
31764
|
+
type: Injectable,
|
|
31765
|
+
args: [{
|
|
31766
|
+
providedIn: 'root',
|
|
31767
|
+
}]
|
|
31768
|
+
}] });
|
|
31769
|
+
|
|
31770
|
+
class NotificationsContainerComponent {
|
|
31771
|
+
constructor() {
|
|
31772
|
+
this.notificationsService = inject(NotificationsService);
|
|
31773
|
+
}
|
|
31774
|
+
trackById(index, notification) {
|
|
31775
|
+
return notification.id;
|
|
31776
|
+
}
|
|
31777
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31778
|
+
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: [
|
|
31779
|
+
trigger('enterExit', [
|
|
31780
|
+
transition(':enter', [
|
|
31781
|
+
animate('150ms', keyframes([
|
|
31782
|
+
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31783
|
+
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31784
|
+
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31785
|
+
])),
|
|
31786
|
+
]),
|
|
31787
|
+
transition(':leave', [
|
|
31788
|
+
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31789
|
+
]),
|
|
31790
|
+
]),
|
|
31791
|
+
], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31792
|
+
}
|
|
31793
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, decorators: [{
|
|
31794
|
+
type: Component,
|
|
31795
|
+
args: [{ selector: 'gn-ui-notifications-container', standalone: true, imports: [CommonModule, NotificationComponent], changeDetection: ChangeDetectionStrategy.OnPush, animations: [
|
|
31796
|
+
trigger('enterExit', [
|
|
31797
|
+
transition(':enter', [
|
|
31798
|
+
animate('150ms', keyframes([
|
|
31799
|
+
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31800
|
+
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31801
|
+
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31802
|
+
])),
|
|
31803
|
+
]),
|
|
31804
|
+
transition(':leave', [
|
|
31805
|
+
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31806
|
+
]),
|
|
31807
|
+
]),
|
|
31808
|
+
], 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" }]
|
|
31809
|
+
}] });
|
|
31810
|
+
|
|
31305
31811
|
class FilterDropdownComponent {
|
|
31306
31812
|
constructor() {
|
|
31307
31813
|
this.searchFacade = inject(SearchFacade);
|
|
31308
31814
|
this.searchService = inject(SearchService);
|
|
31309
31815
|
this.fieldsService = inject(FieldsService);
|
|
31816
|
+
this.notificationsService = inject(NotificationsService);
|
|
31817
|
+
this.translateService = inject(TranslateService);
|
|
31818
|
+
this.spatialExtentMaxFileSize = getOptionalSearchConfig()?.SPATIAL_EXTENT_MAX_FILE_SIZE;
|
|
31310
31819
|
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((
|
|
31820
|
+
this.selectedDateRange$ = this.selected$.pipe(map$1((selected) => (Array.isArray(selected) ? {} : selected)));
|
|
31821
|
+
this.selectedBoundingBox$ = this.selected$.pipe(map$1((selected) => Array.isArray(selected) && selected.length > 0
|
|
31822
|
+
? selected
|
|
31823
|
+
: null));
|
|
31824
|
+
this.spatialExtentErrorNotificationId = null;
|
|
31312
31825
|
}
|
|
31313
31826
|
onSelectedValues(values) {
|
|
31314
31827
|
this.fieldsService
|
|
31315
31828
|
.buildFiltersFromFieldValues({ [this.fieldName]: values })
|
|
31316
31829
|
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
31317
31830
|
}
|
|
31831
|
+
onBboxChange(bbox) {
|
|
31832
|
+
this.fieldsService
|
|
31833
|
+
.buildFiltersFromFieldValues({
|
|
31834
|
+
[this.fieldName]: bbox,
|
|
31835
|
+
})
|
|
31836
|
+
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
31837
|
+
this.clearSpatialExtentErrorNotification();
|
|
31838
|
+
}
|
|
31839
|
+
onSpatialExtentError(error) {
|
|
31840
|
+
this.clearSpatialExtentErrorNotification();
|
|
31841
|
+
this.spatialExtentErrorNotificationId =
|
|
31842
|
+
this.notificationsService.showNotification({
|
|
31843
|
+
type: 'error',
|
|
31844
|
+
title: this.translateService.instant('search.filters.spatialExtent.error.title'),
|
|
31845
|
+
text: this.translateService.instant(error.key, error.params),
|
|
31846
|
+
});
|
|
31847
|
+
}
|
|
31848
|
+
clearSpatialExtentErrorNotification() {
|
|
31849
|
+
if (this.spatialExtentErrorNotificationId === null)
|
|
31850
|
+
return;
|
|
31851
|
+
this.notificationsService.removeNotificationById(this.spatialExtentErrorNotificationId);
|
|
31852
|
+
this.spatialExtentErrorNotificationId = null;
|
|
31853
|
+
}
|
|
31318
31854
|
ngOnInit() {
|
|
31319
31855
|
this.fieldType = this.fieldsService.getFieldType(this.fieldName);
|
|
31320
31856
|
this.choices$ = this.fieldsService.getAvailableValues(this.fieldName).pipe(startWith$1([]), map$1((values) => values.map((v) => ({
|
|
@@ -31322,27 +31858,15 @@ class FilterDropdownComponent {
|
|
|
31322
31858
|
value: v.value.toString(), // converting to string for the dropdown
|
|
31323
31859
|
}))), catchError(() => of([])));
|
|
31324
31860
|
}
|
|
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
|
-
}
|
|
31861
|
+
onDateRangeChange(dateRange) {
|
|
31862
|
+
this.fieldsService
|
|
31863
|
+
.buildFiltersFromFieldValues({
|
|
31864
|
+
[this.fieldName]: dateRange,
|
|
31865
|
+
})
|
|
31866
|
+
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
31343
31867
|
}
|
|
31344
31868
|
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 [
|
|
31869
|
+
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 [initialBbox]=\"(selectedBoundingBox$ | async) ?? null\"\n [maxFileSizeMb]=\"spatialExtentMaxFileSize\"\n (bboxChange)=\"onBboxChange($event)\"\n (errorChange)=\"onSpatialExtentError($event)\"\n [attr.data-cy-field]=\"fieldName\"\n ></gn-ui-spatial-extent-dropdown>\n} @else {\n <gn-ui-dropdown-multiselect\n class=\"w-full\"\n [title]=\"title\"\n [maxRows]=\"6\"\n [choices]=\"(choices$ | async) || []\"\n [selected]=\"selected$ | async\"\n [allowSearch]=\"true\"\n (selectValues)=\"onSelectedValues($event)\"\n [attr.data-cy-field]=\"fieldName\"\n >\n </gn-ui-dropdown-multiselect>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DateRangeDropdownComponent, selector: "gn-ui-date-range-dropdown", inputs: ["title", "dateRange"], outputs: ["dateRangeChange"] }, { kind: "component", type: DropdownMultiselectComponent, selector: "gn-ui-dropdown-multiselect", inputs: ["title", "choices", "selected", "allowSearch", "maxRows", "searchInputValue"], outputs: ["selectValues"] }, { kind: "component", type: SpatialExtentDropdownComponent, selector: "gn-ui-spatial-extent-dropdown", inputs: ["title", "maxFileSizeMb", "initialBbox"], outputs: ["bboxChange", "errorChange"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31346
31870
|
}
|
|
31347
31871
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, decorators: [{
|
|
31348
31872
|
type: Component,
|
|
@@ -31350,7 +31874,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31350
31874
|
CommonModule,
|
|
31351
31875
|
DateRangeDropdownComponent,
|
|
31352
31876
|
DropdownMultiselectComponent,
|
|
31353
|
-
|
|
31877
|
+
SpatialExtentDropdownComponent,
|
|
31878
|
+
], 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 [initialBbox]=\"(selectedBoundingBox$ | async) ?? null\"\n [maxFileSizeMb]=\"spatialExtentMaxFileSize\"\n (bboxChange)=\"onBboxChange($event)\"\n (errorChange)=\"onSpatialExtentError($event)\"\n [attr.data-cy-field]=\"fieldName\"\n ></gn-ui-spatial-extent-dropdown>\n} @else {\n <gn-ui-dropdown-multiselect\n class=\"w-full\"\n [title]=\"title\"\n [maxRows]=\"6\"\n [choices]=\"(choices$ | async) || []\"\n [selected]=\"selected$ | async\"\n [allowSearch]=\"true\"\n (selectValues)=\"onSelectedValues($event)\"\n [attr.data-cy-field]=\"fieldName\"\n >\n </gn-ui-dropdown-multiselect>\n}\n" }]
|
|
31354
31879
|
}], propDecorators: { fieldName: [{
|
|
31355
31880
|
type: Input
|
|
31356
31881
|
}], title: [{
|
|
@@ -31886,74 +32411,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31886
32411
|
args: ['gnUiSearchStateContainer']
|
|
31887
32412
|
}] } });
|
|
31888
32413
|
|
|
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
32414
|
class ResultsTableContainerComponent {
|
|
31958
32415
|
constructor() {
|
|
31959
32416
|
this.searchFacade = inject(SearchFacade);
|
|
@@ -32038,12 +32495,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
32038
32495
|
|
|
32039
32496
|
marker('search.filters.summaryLabel.user');
|
|
32040
32497
|
marker('search.filters.summaryLabel.changeDate');
|
|
32498
|
+
const OPEN_BOUND = '…';
|
|
32041
32499
|
class SearchFiltersSummaryItemComponent {
|
|
32042
32500
|
constructor() {
|
|
32043
32501
|
this.searchFacade = inject(SearchFacade);
|
|
32044
32502
|
this.searchService = inject(SearchService);
|
|
32045
32503
|
this.fieldsService = inject(FieldsService);
|
|
32046
|
-
this.
|
|
32504
|
+
this.dateService = inject(DateService);
|
|
32047
32505
|
this.translate = inject(TranslateService);
|
|
32048
32506
|
this.fieldValues$ = this.searchFacade.searchFilters$.pipe(switchMap((filters) => this.fieldsService.readFieldValuesFromFilters(filters)), map$2((fieldValues) => Array.isArray(fieldValues[this.fieldName])
|
|
32049
32507
|
? fieldValues[this.fieldName]
|
|
@@ -32070,9 +32528,10 @@ class SearchFiltersSummaryItemComponent {
|
|
|
32070
32528
|
getReadableValues(fieldValues) {
|
|
32071
32529
|
return fieldValues.map((value) => {
|
|
32072
32530
|
if (this.fieldType === 'dateRange') {
|
|
32531
|
+
const { start, end } = value;
|
|
32073
32532
|
return {
|
|
32074
32533
|
value,
|
|
32075
|
-
label: `${this.
|
|
32534
|
+
label: `${this.formatBound(start)} - ${this.formatBound(end)}`,
|
|
32076
32535
|
};
|
|
32077
32536
|
}
|
|
32078
32537
|
else if (this.fieldName === 'user') {
|
|
@@ -32083,6 +32542,15 @@ class SearchFiltersSummaryItemComponent {
|
|
|
32083
32542
|
}
|
|
32084
32543
|
});
|
|
32085
32544
|
}
|
|
32545
|
+
formatBound(date) {
|
|
32546
|
+
return date
|
|
32547
|
+
? this.dateService.formatDate(date, {
|
|
32548
|
+
day: '2-digit',
|
|
32549
|
+
month: '2-digit',
|
|
32550
|
+
year: 'numeric',
|
|
32551
|
+
})
|
|
32552
|
+
: OPEN_BOUND;
|
|
32553
|
+
}
|
|
32086
32554
|
async removeFilterValue(fieldValue) {
|
|
32087
32555
|
const currentFieldValues = await firstValueFrom(this.fieldValues$);
|
|
32088
32556
|
const updatedFieldValues = currentFieldValues
|
|
@@ -32095,11 +32563,11 @@ class SearchFiltersSummaryItemComponent {
|
|
|
32095
32563
|
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
32096
32564
|
}
|
|
32097
32565
|
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" },
|
|
32566
|
+
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
32567
|
}
|
|
32100
32568
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SearchFiltersSummaryItemComponent, decorators: [{
|
|
32101
32569
|
type: Component,
|
|
32102
|
-
args: [{ selector: 'gn-ui-search-filters-summary-item', standalone: true, imports: [CommonModule, BadgeComponent],
|
|
32570
|
+
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
32571
|
}], propDecorators: { fieldName: [{
|
|
32104
32572
|
type: Input
|
|
32105
32573
|
}] } });
|
|
@@ -32343,11 +32811,11 @@ class AddLayerFromFileComponent {
|
|
|
32343
32811
|
}, 5000);
|
|
32344
32812
|
}
|
|
32345
32813
|
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" }] }); }
|
|
32814
|
+
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
32815
|
}
|
|
32348
32816
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AddLayerFromFileComponent, decorators: [{
|
|
32349
32817
|
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" }]
|
|
32818
|
+
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
32819
|
}] });
|
|
32352
32820
|
|
|
32353
32821
|
class LayersPanelComponent {
|
|
@@ -32512,8 +32980,7 @@ const closeMetadata = createAction('[Metadata view] close');
|
|
|
32512
32980
|
Related actions
|
|
32513
32981
|
*/
|
|
32514
32982
|
const setRelated = createAction('[Metadata view] Set related records', props());
|
|
32515
|
-
const
|
|
32516
|
-
const setSourceOf = createAction('[Metadata view] Set has sources', props());
|
|
32983
|
+
const setLinkedRecords = createAction('[Metadata view] Set associated records', props());
|
|
32517
32984
|
/*
|
|
32518
32985
|
ChartConfig actions
|
|
32519
32986
|
*/
|
|
@@ -32545,9 +33012,8 @@ var mdview_actions = /*#__PURE__*/Object.freeze({
|
|
|
32545
33012
|
loadUserFeedbacksSuccess: loadUserFeedbacksSuccess,
|
|
32546
33013
|
setChartConfig: setChartConfig,
|
|
32547
33014
|
setIncompleteMetadata: setIncompleteMetadata,
|
|
32548
|
-
|
|
32549
|
-
|
|
32550
|
-
setSources: setSources
|
|
33015
|
+
setLinkedRecords: setLinkedRecords,
|
|
33016
|
+
setRelated: setRelated
|
|
32551
33017
|
});
|
|
32552
33018
|
|
|
32553
33019
|
const METADATA_VIEW_FEATURE_STATE_KEY = 'metadataView';
|
|
@@ -32590,12 +33056,9 @@ on(loadFullMetadata, (state) => ({
|
|
|
32590
33056
|
on(setRelated, (state, { related }) => ({
|
|
32591
33057
|
...state,
|
|
32592
33058
|
related,
|
|
32593
|
-
})), on(
|
|
32594
|
-
...state,
|
|
32595
|
-
sources,
|
|
32596
|
-
})), on(setSourceOf, (state, { sourceOf }) => ({
|
|
33059
|
+
})), on(setLinkedRecords, (state, { linkedRecords }) => ({
|
|
32597
33060
|
...state,
|
|
32598
|
-
|
|
33061
|
+
linkedRecords,
|
|
32599
33062
|
})),
|
|
32600
33063
|
/*
|
|
32601
33064
|
ChartConfig reducers
|
|
@@ -32657,8 +33120,7 @@ const getMetadataError = createSelector(getMdViewState, (state) => state.error);
|
|
|
32657
33120
|
Related selectors
|
|
32658
33121
|
*/
|
|
32659
33122
|
const getRelated = createSelector(getMdViewState, (state) => state.related);
|
|
32660
|
-
const
|
|
32661
|
-
const getSourceOf = createSelector(getMdViewState, (state) => state.sourceOf);
|
|
33123
|
+
const getLinkedRecords = createSelector(getMdViewState, (state) => state.linkedRecords);
|
|
32662
33124
|
/*
|
|
32663
33125
|
Metadata selectors
|
|
32664
33126
|
*/
|
|
@@ -32759,212 +33221,10 @@ function parseHeaders(httpHeaders) {
|
|
|
32759
33221
|
return result;
|
|
32760
33222
|
}
|
|
32761
33223
|
|
|
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
|
-
});
|
|
32794
|
-
}
|
|
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
|
-
});
|
|
33224
|
+
const GEOMETRY_COLUMN_ALIAS = '__geometry__';
|
|
33225
|
+
function fieldToSql(name) {
|
|
33226
|
+
return `"${name.replace(/"/g, '""')}"`; // escape double quotes in field names
|
|
32825
33227
|
}
|
|
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
33228
|
function filterToSql(filter) {
|
|
32969
33229
|
const operator = filter[0];
|
|
32970
33230
|
const args = filter.slice(1);
|
|
@@ -32979,10 +33239,10 @@ function filterToSql(filter) {
|
|
|
32979
33239
|
case '=':
|
|
32980
33240
|
case '!=':
|
|
32981
33241
|
case 'like':
|
|
32982
|
-
return
|
|
33242
|
+
return `${fieldToSql(args[0])} ${operator.toUpperCase()} ${valueToSql(args[1])}`;
|
|
32983
33243
|
case 'in': {
|
|
32984
33244
|
const values = args.slice(1);
|
|
32985
|
-
return
|
|
33245
|
+
return `${fieldToSql(args[0])} IN (${values.map(valueToSql).join(', ')})`;
|
|
32986
33246
|
}
|
|
32987
33247
|
case 'and':
|
|
32988
33248
|
case 'or': {
|
|
@@ -33001,17 +33261,18 @@ function aggregationToSql(aggregation) {
|
|
|
33001
33261
|
const field = aggregation[1];
|
|
33002
33262
|
switch (operation) {
|
|
33003
33263
|
case 'average':
|
|
33004
|
-
return `AVG(
|
|
33264
|
+
return `CAST(AVG(${fieldToSql(field)}) AS DOUBLE) as ${fieldToSql(`average(${field})`)}`;
|
|
33005
33265
|
case 'sum':
|
|
33006
33266
|
case 'max':
|
|
33007
33267
|
case 'min':
|
|
33008
|
-
return
|
|
33268
|
+
return `CAST(${operation.toUpperCase()}(${fieldToSql(field)}) AS DOUBLE) as ${fieldToSql(`${operation}(${field})`)}`;
|
|
33009
33269
|
case 'count':
|
|
33010
|
-
return 'COUNT(*) as
|
|
33270
|
+
return 'CAST(COUNT(*) AS INTEGER) as "count()"'; // we don't need Bigint precision here
|
|
33011
33271
|
}
|
|
33012
33272
|
}
|
|
33013
33273
|
/**
|
|
33014
33274
|
* Leave arguments at null if not used
|
|
33275
|
+
* @param tableName
|
|
33015
33276
|
* @param selected
|
|
33016
33277
|
* @param filter
|
|
33017
33278
|
* @param sort
|
|
@@ -33020,22 +33281,25 @@ function aggregationToSql(aggregation) {
|
|
|
33020
33281
|
* @param groupBy
|
|
33021
33282
|
* @param aggregations
|
|
33022
33283
|
*/
|
|
33023
|
-
function generateSqlQuery(selected = null, filter = null, sort = null, startIndex = null, count = null, groupBy = null, aggregations = null) {
|
|
33284
|
+
function generateSqlQuery(tableName, selected = null, filter = null, sort = null, startIndex = null, count = null, groupBy = null, aggregations = null, geometryColumn = null) {
|
|
33024
33285
|
let sqlSelect = 'SELECT *';
|
|
33025
|
-
const sqlFrom =
|
|
33286
|
+
const sqlFrom = ` FROM ${tableName}`;
|
|
33026
33287
|
let sqlOrderBy = '';
|
|
33027
33288
|
let sqlWhere = '';
|
|
33028
33289
|
let sqlLimit = '';
|
|
33029
33290
|
let sqlGroupBy = '';
|
|
33030
33291
|
if (selected !== null) {
|
|
33031
|
-
sqlSelect = `SELECT ${selected.map(
|
|
33292
|
+
sqlSelect = `SELECT ${selected.map(fieldToSql).join(', ')}`;
|
|
33293
|
+
}
|
|
33294
|
+
if (geometryColumn !== null) {
|
|
33295
|
+
sqlSelect += `, ST_AsGeoJSON(${fieldToSql(geometryColumn)}) as ${GEOMETRY_COLUMN_ALIAS}`;
|
|
33032
33296
|
}
|
|
33033
33297
|
if (filter !== null) {
|
|
33034
33298
|
sqlWhere = ` WHERE ${filterToSql(filter)}`;
|
|
33035
33299
|
}
|
|
33036
33300
|
if (sort?.length) {
|
|
33037
33301
|
sqlOrderBy = ` ORDER BY ${sort
|
|
33038
|
-
.map((sort) =>
|
|
33302
|
+
.map((sort) => `${fieldToSql(sort[1])} ${sort[0].toUpperCase()}`)
|
|
33039
33303
|
.join(', ')}`;
|
|
33040
33304
|
}
|
|
33041
33305
|
if (startIndex !== null && count !== null) {
|
|
@@ -33045,17 +33309,212 @@ function generateSqlQuery(selected = null, filter = null, sort = null, startInde
|
|
|
33045
33309
|
sqlSelect = `SELECT ${aggregations.map(aggregationToSql).join(', ')}`;
|
|
33046
33310
|
const groupedByDistinct = groupBy.filter((group) => group[0] === 'distinct');
|
|
33047
33311
|
const sqlGroupByFields = groupedByDistinct
|
|
33048
|
-
.map((group) =>
|
|
33312
|
+
.map((group) => fieldToSql(group[1]))
|
|
33049
33313
|
.join(', ');
|
|
33050
33314
|
const sqlGroupBySelect = groupedByDistinct
|
|
33051
|
-
.map((group) =>
|
|
33315
|
+
.map((group) => `${fieldToSql(group[1])} as ${fieldToSql(`distinct(${group[1]})`)}`)
|
|
33052
33316
|
.join(', ');
|
|
33053
33317
|
if (sqlGroupByFields && sqlGroupBySelect) {
|
|
33054
33318
|
sqlGroupBy = ` GROUP BY ${sqlGroupByFields}`;
|
|
33055
33319
|
sqlSelect += `, ${sqlGroupBySelect}`;
|
|
33056
33320
|
}
|
|
33057
33321
|
}
|
|
33058
|
-
return sqlSelect + sqlFrom + sqlGroupBy +
|
|
33322
|
+
return sqlSelect + sqlFrom + sqlGroupBy + sqlWhere + sqlOrderBy + sqlLimit;
|
|
33323
|
+
}
|
|
33324
|
+
|
|
33325
|
+
function arrowTableToDataItems(table) {
|
|
33326
|
+
const fields = table.schema.fields;
|
|
33327
|
+
return table.toArray().map((row) => {
|
|
33328
|
+
const rowJson = row.toJSON();
|
|
33329
|
+
const feature = {
|
|
33330
|
+
type: 'Feature',
|
|
33331
|
+
geometry: null,
|
|
33332
|
+
properties: {},
|
|
33333
|
+
};
|
|
33334
|
+
const keys = Object.keys(rowJson);
|
|
33335
|
+
for (let i = 0; i < keys.length; i++) {
|
|
33336
|
+
const key = keys[i];
|
|
33337
|
+
const dataType = fields[i].type;
|
|
33338
|
+
let value = rowJson[key];
|
|
33339
|
+
// this might happen if we get an array inside a field
|
|
33340
|
+
if (value instanceof Vector) {
|
|
33341
|
+
value = Array.from(value);
|
|
33342
|
+
}
|
|
33343
|
+
// cast bigints to ints
|
|
33344
|
+
if (typeof value === 'bigint') {
|
|
33345
|
+
value = Number(value);
|
|
33346
|
+
}
|
|
33347
|
+
// rename columns with empty name
|
|
33348
|
+
if (!key) {
|
|
33349
|
+
feature.properties['unknown'] = value;
|
|
33350
|
+
continue;
|
|
33351
|
+
}
|
|
33352
|
+
// assign properties that look like an id to the geojson `id` field
|
|
33353
|
+
if (/^(object|feature)?_?id$/.test(key.toLowerCase()) &&
|
|
33354
|
+
(typeof value == 'string' || typeof value === 'number')) {
|
|
33355
|
+
feature.id = value;
|
|
33356
|
+
}
|
|
33357
|
+
// if a date is in timestamp (number) format, convert it to native
|
|
33358
|
+
if (typeof value === 'number' &&
|
|
33359
|
+
(DataType.isTimestamp(dataType) || DataType.isDate(dataType))) {
|
|
33360
|
+
value = new Date(value);
|
|
33361
|
+
}
|
|
33362
|
+
// if a binary field (most likely a geometry column): skip
|
|
33363
|
+
if (DataType.isBinary(dataType)) {
|
|
33364
|
+
continue;
|
|
33365
|
+
}
|
|
33366
|
+
// geometry column
|
|
33367
|
+
if (key === GEOMETRY_COLUMN_ALIAS && DataType.isUtf8(dataType)) {
|
|
33368
|
+
feature.geometry = JSON.parse(value);
|
|
33369
|
+
continue;
|
|
33370
|
+
}
|
|
33371
|
+
feature.properties[key] = value;
|
|
33372
|
+
}
|
|
33373
|
+
return feature;
|
|
33374
|
+
});
|
|
33375
|
+
}
|
|
33376
|
+
|
|
33377
|
+
// init code taken from https://github.com/duckdb/duckdb-wasm/blob/main/packages/duckdb-wasm/README.md
|
|
33378
|
+
const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();
|
|
33379
|
+
// from https://duckdb.org/docs/current/sql/data_types/overview
|
|
33380
|
+
const typesMapping = {
|
|
33381
|
+
INTEGER: 'number',
|
|
33382
|
+
SMALLINT: 'number',
|
|
33383
|
+
TINYINT: 'number',
|
|
33384
|
+
BIGINT: 'number',
|
|
33385
|
+
HUGEINT: 'number',
|
|
33386
|
+
UINTEGER: 'number',
|
|
33387
|
+
USMALLINT: 'number',
|
|
33388
|
+
UTINYINT: 'number',
|
|
33389
|
+
UBIGINT: 'number',
|
|
33390
|
+
UHUGEINT: 'number',
|
|
33391
|
+
DOUBLE: 'number',
|
|
33392
|
+
BIGNUM: 'number',
|
|
33393
|
+
DECIMAL: 'number',
|
|
33394
|
+
NUMERIC: 'number',
|
|
33395
|
+
FLOAT: 'number',
|
|
33396
|
+
REAL: 'number',
|
|
33397
|
+
'TIMESTAMP WITH TIME ZONE': 'date',
|
|
33398
|
+
TIMESTAMP: 'date',
|
|
33399
|
+
DATE: 'date',
|
|
33400
|
+
CHAR: 'string',
|
|
33401
|
+
VARCHAR: 'string',
|
|
33402
|
+
TEXT: 'string',
|
|
33403
|
+
UUID: 'string',
|
|
33404
|
+
BLOB: 'string',
|
|
33405
|
+
BIT: 'string',
|
|
33406
|
+
INTERVAL: 'string',
|
|
33407
|
+
LIST: 'string',
|
|
33408
|
+
BOOLEAN: 'boolean',
|
|
33409
|
+
};
|
|
33410
|
+
class Engine {
|
|
33411
|
+
async makeInit() {
|
|
33412
|
+
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);
|
|
33413
|
+
let worker_url = bundle.mainWorker;
|
|
33414
|
+
// this is necessary to let browsers execute WASM code coming from a cross-origin host
|
|
33415
|
+
if (worker_url.startsWith('https://')) {
|
|
33416
|
+
worker_url = URL.createObjectURL(new Blob([`importScripts("${worker_url}");`], {
|
|
33417
|
+
type: 'text/javascript',
|
|
33418
|
+
}));
|
|
33419
|
+
}
|
|
33420
|
+
const worker = new Worker(worker_url);
|
|
33421
|
+
const logger = new duckdb.ConsoleLogger(duckdb.LogLevel.WARNING);
|
|
33422
|
+
this.db = new duckdb.AsyncDuckDB(logger, worker);
|
|
33423
|
+
await this.db.instantiate(bundle.mainModule, bundle.pthreadWorker);
|
|
33424
|
+
// setup duckdb options
|
|
33425
|
+
const conn = await this.db.connect();
|
|
33426
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
33427
|
+
await conn.query(`INSTALL spatial; LOAD spatial;`);
|
|
33428
|
+
await conn.query(`SET TimeZone = '${timezone}';`);
|
|
33429
|
+
await conn.query(`SET threads = 1;`);
|
|
33430
|
+
// await conn.query(`SET memory_limit = '2GB';`) // used for experimenting when we encounter memory issues
|
|
33431
|
+
await conn.query(`SET preserve_insertion_order = false;`); // this reduces memory usage when loading a dataset
|
|
33432
|
+
conn.close();
|
|
33433
|
+
return this;
|
|
33434
|
+
}
|
|
33435
|
+
isReady() {
|
|
33436
|
+
if (!this.init_) {
|
|
33437
|
+
this.init_ = this.makeInit();
|
|
33438
|
+
}
|
|
33439
|
+
return this.init_;
|
|
33440
|
+
}
|
|
33441
|
+
/**
|
|
33442
|
+
* Returns information about a dataset once it's loaded:
|
|
33443
|
+
* - a list of properties description
|
|
33444
|
+
* - the name of the dataset geometry column (null if no geometry present)
|
|
33445
|
+
* @param datasetId name of the table under which the dataset will be stored
|
|
33446
|
+
* @param loadQuery duckdb-specific query for creating a table out of the data
|
|
33447
|
+
* @param forceReload if true, any existing data will be dropped and redownloaded
|
|
33448
|
+
*/
|
|
33449
|
+
async loadFile(datasetId, loadQuery, forceReload = false) {
|
|
33450
|
+
const conn = await this.db.connect();
|
|
33451
|
+
let results;
|
|
33452
|
+
// either we want to recreate the table, or we keep it if it already exists
|
|
33453
|
+
const safeLoadQuery = forceReload
|
|
33454
|
+
? `DROP TABLE IF EXISTS ${datasetId};
|
|
33455
|
+
${loadQuery}`
|
|
33456
|
+
: loadQuery.replace(/CREATE TABLE(?! IF NOT EXISTS)/gi, 'CREATE TABLE IF NOT EXISTS');
|
|
33457
|
+
// create the table
|
|
33458
|
+
try {
|
|
33459
|
+
results = await conn.query(safeLoadQuery);
|
|
33460
|
+
}
|
|
33461
|
+
catch (e) {
|
|
33462
|
+
throw new FetchError('parse', `DuckDB encountered an error when loading the data: ${e.message}`);
|
|
33463
|
+
}
|
|
33464
|
+
// read rows count
|
|
33465
|
+
results = await conn.query(`SELECT count(*) FROM ${datasetId}`);
|
|
33466
|
+
const { 'count_star()': recordsCount } = results.toArray()[0].toJSON();
|
|
33467
|
+
// read columns
|
|
33468
|
+
results = await conn.query(`SELECT * FROM information_schema.columns WHERE table_name = '${datasetId}';`);
|
|
33469
|
+
let geometryColumn = null;
|
|
33470
|
+
const properties = results
|
|
33471
|
+
.toArray()
|
|
33472
|
+
.map((row) => {
|
|
33473
|
+
const rowObj = row.toJSON();
|
|
33474
|
+
if (rowObj['data_type'] === 'GEOMETRY') {
|
|
33475
|
+
// the geometry is not part of the properties
|
|
33476
|
+
// note: right now we only keep one geometry column name, but if there are multiple they will
|
|
33477
|
+
// all get discarded
|
|
33478
|
+
geometryColumn = rowObj['column_name'];
|
|
33479
|
+
return null;
|
|
33480
|
+
}
|
|
33481
|
+
return {
|
|
33482
|
+
name: rowObj['column_name'],
|
|
33483
|
+
label: rowObj['column_name'],
|
|
33484
|
+
type: typesMapping[rowObj['data_type']] ?? 'other',
|
|
33485
|
+
};
|
|
33486
|
+
})
|
|
33487
|
+
.filter((prop) => prop !== null);
|
|
33488
|
+
conn.close();
|
|
33489
|
+
return {
|
|
33490
|
+
properties,
|
|
33491
|
+
geometryColumn,
|
|
33492
|
+
rowsCount: Number(recordsCount),
|
|
33493
|
+
};
|
|
33494
|
+
}
|
|
33495
|
+
// register a Uint8 buffer using a handle in the duckdb instance
|
|
33496
|
+
async registerData(name, buffer) {
|
|
33497
|
+
return this.db.registerFileBuffer(name, buffer);
|
|
33498
|
+
}
|
|
33499
|
+
/**
|
|
33500
|
+
* @param query duckdb-specific query for fetching items
|
|
33501
|
+
*/
|
|
33502
|
+
async queryItems(query) {
|
|
33503
|
+
const conn = await this.db.connect();
|
|
33504
|
+
const results = await conn.query(query);
|
|
33505
|
+
conn.close();
|
|
33506
|
+
return arrowTableToDataItems(results);
|
|
33507
|
+
}
|
|
33508
|
+
close() {
|
|
33509
|
+
this.db?.terminate();
|
|
33510
|
+
}
|
|
33511
|
+
}
|
|
33512
|
+
let engine = null;
|
|
33513
|
+
async function getEngine() {
|
|
33514
|
+
if (!engine) {
|
|
33515
|
+
engine = new Engine();
|
|
33516
|
+
}
|
|
33517
|
+
return engine.isReady();
|
|
33059
33518
|
}
|
|
33060
33519
|
|
|
33061
33520
|
class BaseReader {
|
|
@@ -33068,10 +33527,18 @@ class BaseReader {
|
|
|
33068
33527
|
this.sort = null;
|
|
33069
33528
|
this.startIndex = null;
|
|
33070
33529
|
this.count = null;
|
|
33530
|
+
this.loadPromise_ = Promise.resolve();
|
|
33531
|
+
this.cacheEnabled = false;
|
|
33532
|
+
}
|
|
33533
|
+
enableCache(enabled) {
|
|
33534
|
+
this.cacheEnabled = enabled;
|
|
33071
33535
|
}
|
|
33072
33536
|
load() {
|
|
33073
33537
|
throw new Error('not implemented');
|
|
33074
33538
|
}
|
|
33539
|
+
get isLoaded() {
|
|
33540
|
+
return this.loadPromise_;
|
|
33541
|
+
}
|
|
33075
33542
|
get properties() {
|
|
33076
33543
|
throw new Error('not implemented');
|
|
33077
33544
|
}
|
|
@@ -33120,178 +33587,237 @@ class BaseReader {
|
|
|
33120
33587
|
}
|
|
33121
33588
|
}
|
|
33122
33589
|
|
|
33123
|
-
|
|
33124
|
-
|
|
33125
|
-
|
|
33126
|
-
|
|
33127
|
-
|
|
33128
|
-
|
|
33129
|
-
|
|
33130
|
-
|
|
33131
|
-
|
|
33132
|
-
|
|
33133
|
-
|
|
33134
|
-
|
|
33135
|
-
|
|
33590
|
+
/**
|
|
33591
|
+
* This reader handles file formats supported natively by DuckDB
|
|
33592
|
+
*/
|
|
33593
|
+
class BaseFileReader extends BaseReader {
|
|
33594
|
+
// a table id should not exceed 63 chars
|
|
33595
|
+
generateDatasetId() {
|
|
33596
|
+
// generate a hash out of the url
|
|
33597
|
+
let hash = 0;
|
|
33598
|
+
for (const char of this.url) {
|
|
33599
|
+
hash = (hash << 5) - hash + char.charCodeAt(0);
|
|
33600
|
+
hash = hash >>> 0; // make it unsigned
|
|
33601
|
+
}
|
|
33602
|
+
return `datafetcher_${hash.toString(16)}`;
|
|
33603
|
+
}
|
|
33604
|
+
async getLoadQuery() {
|
|
33136
33605
|
throw new Error('not implemented');
|
|
33137
33606
|
}
|
|
33138
|
-
load() {
|
|
33139
|
-
this.
|
|
33607
|
+
async load() {
|
|
33608
|
+
this.datasetId = this.generateDatasetId();
|
|
33609
|
+
this.loadPromise_ = getEngine()
|
|
33610
|
+
.then((engine) => {
|
|
33611
|
+
this.engine = engine;
|
|
33612
|
+
return this.getLoadQuery();
|
|
33613
|
+
})
|
|
33614
|
+
.then((loadQuery) => this.engine.loadFile(this.datasetId, loadQuery, !this.cacheEnabled))
|
|
33615
|
+
.then((datasetInfo) => {
|
|
33616
|
+
this.properties_ = datasetInfo.properties;
|
|
33617
|
+
this.geometryColumn = datasetInfo.geometryColumn;
|
|
33618
|
+
this.rowsCount = datasetInfo.rowsCount;
|
|
33619
|
+
// returns void for the loaded promise
|
|
33620
|
+
});
|
|
33140
33621
|
}
|
|
33141
33622
|
get properties() {
|
|
33142
|
-
return this.
|
|
33623
|
+
return this.isLoaded.then(() => this.properties_);
|
|
33143
33624
|
}
|
|
33144
33625
|
get info() {
|
|
33145
|
-
return this.
|
|
33146
|
-
itemsCount:
|
|
33626
|
+
return this.isLoaded.then(() => ({
|
|
33627
|
+
itemsCount: this.rowsCount,
|
|
33628
|
+
hasGeometry: !!this.geometryColumn,
|
|
33147
33629
|
}));
|
|
33148
33630
|
}
|
|
33149
33631
|
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;
|
|
33632
|
+
await this.isLoaded;
|
|
33633
|
+
// if only certain fields are selected, omit the geometry
|
|
33634
|
+
const geometryColumn = this.selected === null ? this.geometryColumn : null;
|
|
33635
|
+
const query = generateSqlQuery(this.datasetId, this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations, geometryColumn);
|
|
33636
|
+
return this.engine.queryItems(query);
|
|
33178
33637
|
}
|
|
33179
|
-
catch (e) {
|
|
33180
|
-
throw new Error('CSV parsing failed: the delimiter could not be guessed');
|
|
33181
|
-
}
|
|
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
33638
|
}
|
|
33639
|
+
|
|
33196
33640
|
class CsvReader extends BaseFileReader {
|
|
33197
|
-
|
|
33198
|
-
|
|
33641
|
+
async getLoadQuery() {
|
|
33642
|
+
// first we get a list of columns hich have a detected type of VARCHAR
|
|
33643
|
+
// 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
|
|
33644
|
+
return `
|
|
33645
|
+
SET VARIABLE strColumns = (SELECT list(name) FROM (SELECT unnest(Columns, recursive := true) FROM sniff_csv("${this.url}")) WHERE type = 'VARCHAR');
|
|
33646
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM read_csv("${this.url}",
|
|
33647
|
+
force_not_null = getvariable('strColumns'),
|
|
33648
|
+
auto_type_candidates = ['NULL', 'BOOLEAN', 'INTEGER', 'DOUBLE', 'DATE', 'VARCHAR']
|
|
33649
|
+
);`;
|
|
33199
33650
|
}
|
|
33200
33651
|
}
|
|
33201
33652
|
|
|
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
33653
|
class JsonReader extends BaseFileReader {
|
|
33214
|
-
|
|
33215
|
-
return
|
|
33654
|
+
async getLoadQuery() {
|
|
33655
|
+
return `
|
|
33656
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM read_json("${this.url}",
|
|
33657
|
+
maximum_object_size = 536870912 -- 500MB
|
|
33658
|
+
);`;
|
|
33216
33659
|
}
|
|
33217
33660
|
}
|
|
33218
33661
|
|
|
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
33662
|
class GeojsonReader extends BaseFileReader {
|
|
33233
|
-
|
|
33234
|
-
return
|
|
33663
|
+
async getLoadQuery() {
|
|
33664
|
+
return `
|
|
33665
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM st_read("${this.url}",
|
|
33666
|
+
allowed_drivers = ['GeoJSON']
|
|
33667
|
+
);`;
|
|
33235
33668
|
}
|
|
33236
33669
|
}
|
|
33237
33670
|
|
|
33671
|
+
async function inferDatasetType(url, typeHint) {
|
|
33672
|
+
const fileExtensionMatches = new URL(url, typeof window !== 'undefined' ? window.location.toString() : undefined).pathname.match(/\.(.+)$/);
|
|
33673
|
+
const fileExtension = fileExtensionMatches && fileExtensionMatches.length
|
|
33674
|
+
? fileExtensionMatches[1].toLowerCase()
|
|
33675
|
+
: null;
|
|
33676
|
+
// 1. type hint
|
|
33677
|
+
if (typeHint)
|
|
33678
|
+
return Promise.resolve(typeHint);
|
|
33679
|
+
// 2. content-type header
|
|
33680
|
+
const headers = await fetchHeaders(url);
|
|
33681
|
+
if ('supportedType' in headers)
|
|
33682
|
+
return headers.supportedType;
|
|
33683
|
+
// 3. file extension from url
|
|
33684
|
+
else if (SupportedTypes.indexOf(fileExtension) > -1)
|
|
33685
|
+
return fileExtension;
|
|
33686
|
+
// no type inferred or hinted
|
|
33687
|
+
if ('mimeType' in headers)
|
|
33688
|
+
throw FetchError.unsupportedType(headers.mimeType);
|
|
33689
|
+
else
|
|
33690
|
+
throw FetchError.unknownType();
|
|
33691
|
+
}
|
|
33692
|
+
function fetchHeaders(url) {
|
|
33693
|
+
return sharedFetch(url, 'HEAD')
|
|
33694
|
+
.catch((error) => {
|
|
33695
|
+
throw FetchError.corsOrNetwork(error.message);
|
|
33696
|
+
})
|
|
33697
|
+
.then((response) => {
|
|
33698
|
+
if (!response.ok) {
|
|
33699
|
+
throw FetchError.http(response.status);
|
|
33700
|
+
}
|
|
33701
|
+
return parseHeaders(response.headers);
|
|
33702
|
+
});
|
|
33703
|
+
}
|
|
33704
|
+
function fetchDataAsText(url, cacheActive) {
|
|
33705
|
+
const fetchFactory = () => sharedFetch(url)
|
|
33706
|
+
.catch((error) => {
|
|
33707
|
+
throw FetchError.corsOrNetwork(error.message);
|
|
33708
|
+
})
|
|
33709
|
+
.then(async (response) => {
|
|
33710
|
+
if (!response.ok) {
|
|
33711
|
+
const clonedResponse = response.clone();
|
|
33712
|
+
throw FetchError.http(response.status, await clonedResponse.text());
|
|
33713
|
+
}
|
|
33714
|
+
const clonedResponse = response.clone();
|
|
33715
|
+
return clonedResponse.text();
|
|
33716
|
+
});
|
|
33717
|
+
return cacheActive ? useCache(fetchFactory, url, 'asText') : fetchFactory();
|
|
33718
|
+
}
|
|
33719
|
+
function fetchDataAsArrayBuffer(url, cacheActive) {
|
|
33720
|
+
const fetchFactory = () => sharedFetch(url)
|
|
33721
|
+
.catch((error) => {
|
|
33722
|
+
throw FetchError.corsOrNetwork(error.message);
|
|
33723
|
+
})
|
|
33724
|
+
.then(async (response) => {
|
|
33725
|
+
if (!response.ok) {
|
|
33726
|
+
throw FetchError.http(response.status, await response.text());
|
|
33727
|
+
}
|
|
33728
|
+
// convert to a numeric array so that we can store the response in cache
|
|
33729
|
+
return Array.from(new Uint8Array(await response.arrayBuffer()));
|
|
33730
|
+
});
|
|
33731
|
+
return (cacheActive ? useCache(fetchFactory, url, 'asArrayBuffer') : fetchFactory()).then((array) => {
|
|
33732
|
+
return new Uint8Array(array).buffer;
|
|
33733
|
+
});
|
|
33734
|
+
}
|
|
33238
33735
|
/**
|
|
33239
|
-
* This
|
|
33240
|
-
*
|
|
33241
|
-
* @param
|
|
33736
|
+
* This creates a Proxy that allows reading and writing to the data item properties
|
|
33737
|
+
* as if it was a simple array of JSON objects
|
|
33738
|
+
* @param items
|
|
33242
33739
|
*/
|
|
33243
|
-
function
|
|
33244
|
-
return
|
|
33245
|
-
|
|
33246
|
-
|
|
33247
|
-
|
|
33248
|
-
|
|
33249
|
-
|
|
33250
|
-
|
|
33251
|
-
|
|
33740
|
+
function getJsonDataItemsProxy(items) {
|
|
33741
|
+
return new Proxy(items, {
|
|
33742
|
+
get(target, p) {
|
|
33743
|
+
if (typeof p === 'string' &&
|
|
33744
|
+
!Number.isNaN(parseInt(p)) &&
|
|
33745
|
+
target[p]?.properties) {
|
|
33746
|
+
return target[p].properties;
|
|
33747
|
+
}
|
|
33748
|
+
return target[p];
|
|
33749
|
+
},
|
|
33750
|
+
set() {
|
|
33751
|
+
throw new Error('This object is read-only');
|
|
33752
|
+
},
|
|
33252
33753
|
});
|
|
33253
33754
|
}
|
|
33755
|
+
|
|
33254
33756
|
class ExcelReader extends BaseFileReader {
|
|
33255
|
-
|
|
33256
|
-
|
|
33757
|
+
async getLoadQuery() {
|
|
33758
|
+
// we download the file as an array buffer first, in order to be able to check if it's an XLS file
|
|
33759
|
+
let buffer = await fetchDataAsArrayBuffer(this.url, this.cacheEnabled);
|
|
33760
|
+
const bufferHandle = `B${this.datasetId}`;
|
|
33761
|
+
// checking against the magic number at the beginning of XLS files, see https://en.wikipedia.org/wiki/List_of_file_signatures
|
|
33762
|
+
const magicNumber = new Uint8Array(buffer, 0, 8); // first 8 bytes
|
|
33763
|
+
const isXls = Array.from(magicNumber)
|
|
33764
|
+
.map((n) => n.toString(16).toUpperCase())
|
|
33765
|
+
.join(' ') === 'D0 CF 11 E0 A1 B1 1A E1';
|
|
33766
|
+
// uh oh, this is an XLS file (not supported by duckdb); convert it to CSV using the xlsx package
|
|
33767
|
+
if (isXls) {
|
|
33768
|
+
buffer = await import('xlsx').then(({ read, utils }) => {
|
|
33769
|
+
const workbook = read(buffer);
|
|
33770
|
+
const json = utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]]);
|
|
33771
|
+
return new TextEncoder().encode(JSON.stringify(json)).buffer;
|
|
33772
|
+
});
|
|
33773
|
+
}
|
|
33774
|
+
const duckDbFn = isXls ? 'read_json' : 'read_xlsx';
|
|
33775
|
+
await this.engine.registerData(bufferHandle, new Uint8Array(buffer));
|
|
33776
|
+
return `
|
|
33777
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM ${duckDbFn}("${bufferHandle}", ignore_errors = true);`;
|
|
33778
|
+
}
|
|
33779
|
+
}
|
|
33780
|
+
|
|
33781
|
+
class GmlReader extends BaseFileReader {
|
|
33782
|
+
async getLoadQuery() {
|
|
33783
|
+
return `
|
|
33784
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM st_read("${this.url}",
|
|
33785
|
+
allowed_drivers = ['GML']
|
|
33786
|
+
);`;
|
|
33257
33787
|
}
|
|
33258
33788
|
}
|
|
33259
33789
|
|
|
33260
|
-
|
|
33261
|
-
|
|
33262
|
-
const
|
|
33790
|
+
const formatGeojson = new GeoJSON$1();
|
|
33791
|
+
function parseGeojson(text) {
|
|
33792
|
+
const parsed = JSON.parse(text);
|
|
33793
|
+
const features = parsed.type === 'FeatureCollection' ? parsed.features : parsed;
|
|
33794
|
+
if (!Array.isArray(features)) {
|
|
33795
|
+
throw new Error('Could not parse GeoJSON, expected a features collection or an array of features at root level');
|
|
33796
|
+
}
|
|
33797
|
+
return features;
|
|
33798
|
+
}
|
|
33799
|
+
function parseGml(text, featureType, version) {
|
|
33800
|
+
const parts = featureType.split(':');
|
|
33801
|
+
const regex = new RegExp(`xmlns:${parts[0]}=["']([^'"]*)["']`);
|
|
33263
33802
|
const match = regex.exec(text);
|
|
33264
33803
|
if (match && match.length >= 2) {
|
|
33265
|
-
const
|
|
33804
|
+
const wfs = new WFS({
|
|
33266
33805
|
featureNS: match[1],
|
|
33267
|
-
featureType:
|
|
33806
|
+
featureType: parts[1],
|
|
33268
33807
|
version: version,
|
|
33269
33808
|
});
|
|
33270
33809
|
let features;
|
|
33271
33810
|
try {
|
|
33272
|
-
features =
|
|
33811
|
+
features = wfs.readFeatures(text);
|
|
33273
33812
|
}
|
|
33274
33813
|
catch (e) {
|
|
33275
|
-
throw Error(
|
|
33814
|
+
throw Error(`Couldn't parse WFS with GML features: ${e.message}`);
|
|
33276
33815
|
}
|
|
33277
|
-
const geojsonItem =
|
|
33278
|
-
return
|
|
33816
|
+
const geojsonItem = formatGeojson.writeFeaturesObject(features);
|
|
33817
|
+
return geojsonItem.features;
|
|
33279
33818
|
}
|
|
33280
33819
|
throw Error("Couldn't retrieve namespace url");
|
|
33281
33820
|
}
|
|
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
33821
|
async function getWfsEndpoint(wfsUrl) {
|
|
33296
33822
|
try {
|
|
33297
33823
|
return await new WfsEndpoint(wfsUrl).isReady();
|
|
@@ -33319,21 +33845,65 @@ async function getWfsEndpoint(wfsUrl) {
|
|
|
33319
33845
|
}
|
|
33320
33846
|
}
|
|
33321
33847
|
}
|
|
33322
|
-
class WfsReader extends
|
|
33323
|
-
constructor(url,
|
|
33324
|
-
super(url
|
|
33325
|
-
this.endpoint =
|
|
33326
|
-
this.
|
|
33327
|
-
|
|
33848
|
+
class WfsReader extends BaseReader {
|
|
33849
|
+
constructor(url, featureTypeName) {
|
|
33850
|
+
super(url);
|
|
33851
|
+
this.endpoint = getWfsEndpoint(url);
|
|
33852
|
+
this.featureType = this.endpoint
|
|
33853
|
+
.then((endpoint) => {
|
|
33854
|
+
const featureTypes = endpoint.getFeatureTypes();
|
|
33855
|
+
return endpoint.getFeatureTypeFull(featureTypes.length === 1 && !featureTypeName
|
|
33856
|
+
? featureTypes[0].name
|
|
33857
|
+
: featureTypeName);
|
|
33858
|
+
})
|
|
33859
|
+
.then((featureType) => {
|
|
33860
|
+
if (!featureType) {
|
|
33861
|
+
throw new Error('wfs.featuretype.notfound');
|
|
33862
|
+
}
|
|
33863
|
+
return featureType;
|
|
33864
|
+
});
|
|
33865
|
+
}
|
|
33866
|
+
get backupReader() {
|
|
33867
|
+
if (this.backupReader_) {
|
|
33868
|
+
return this.backupReader_;
|
|
33869
|
+
}
|
|
33870
|
+
this.backupReader_ = Promise.all([this.endpoint, this.featureType]).then(([endpoint, featureType]) => {
|
|
33871
|
+
let reader;
|
|
33872
|
+
if (endpoint.supportsJson(featureType.name)) {
|
|
33873
|
+
reader = new GeojsonReader(endpoint.getFeatureUrl(featureType.name, {
|
|
33874
|
+
asJson: true,
|
|
33875
|
+
outputCrs: 'EPSG:4326',
|
|
33876
|
+
}));
|
|
33877
|
+
}
|
|
33878
|
+
else {
|
|
33879
|
+
if (featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')) &&
|
|
33880
|
+
(featureType.defaultCrs === 'EPSG:4326' ||
|
|
33881
|
+
featureType.otherCrs?.includes('EPSG:4326'))) {
|
|
33882
|
+
reader = new GmlReader(endpoint.getFeatureUrl(featureType.name, {
|
|
33883
|
+
outputFormat: featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')),
|
|
33884
|
+
outputCrs: 'EPSG:4326',
|
|
33885
|
+
}));
|
|
33886
|
+
}
|
|
33887
|
+
}
|
|
33888
|
+
reader.enableCache(this.cacheEnabled);
|
|
33889
|
+
reader.load();
|
|
33890
|
+
return reader;
|
|
33891
|
+
});
|
|
33892
|
+
return this.backupReader_;
|
|
33328
33893
|
}
|
|
33329
33894
|
get properties() {
|
|
33330
|
-
return this.
|
|
33331
|
-
.getFeatureTypeFull(this.featureTypeName)
|
|
33332
|
-
.then((featureType) => Object.keys(featureType.properties).map((prop) => {
|
|
33895
|
+
return this.featureType.then((featureType) => Object.keys(featureType.properties).map((prop) => {
|
|
33333
33896
|
const originalType = featureType.properties[prop];
|
|
33334
|
-
|
|
33335
|
-
|
|
33336
|
-
|
|
33897
|
+
let type;
|
|
33898
|
+
if (originalType === 'float' || originalType === 'integer') {
|
|
33899
|
+
type = 'number';
|
|
33900
|
+
}
|
|
33901
|
+
else if (originalType === 'boolean') {
|
|
33902
|
+
type = 'string'; // we don't handle booleans yet in the data fetcher
|
|
33903
|
+
}
|
|
33904
|
+
else {
|
|
33905
|
+
type = originalType;
|
|
33906
|
+
}
|
|
33337
33907
|
return {
|
|
33338
33908
|
name: prop,
|
|
33339
33909
|
label: prop,
|
|
@@ -33342,83 +33912,63 @@ class WfsReader extends BaseCacheReader {
|
|
|
33342
33912
|
}));
|
|
33343
33913
|
}
|
|
33344
33914
|
get info() {
|
|
33345
|
-
return this.
|
|
33915
|
+
return this.featureType.then((result) => ({
|
|
33346
33916
|
itemsCount: result.objectCount,
|
|
33917
|
+
hasGeometry: !!result.geometryName,
|
|
33347
33918
|
}));
|
|
33348
33919
|
}
|
|
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
|
-
}
|
|
33920
|
+
load() {
|
|
33921
|
+
// Nothing to load for Wfs
|
|
33378
33922
|
}
|
|
33379
|
-
async
|
|
33380
|
-
|
|
33381
|
-
|
|
33923
|
+
async read() {
|
|
33924
|
+
const endpoint = await this.endpoint;
|
|
33925
|
+
const featureType = await this.featureType;
|
|
33926
|
+
// if we can't use the WFS protocol we fall back to the backup reader
|
|
33927
|
+
if (this.aggregations || this.groupedBy || !endpoint.supportsStartIndex()) {
|
|
33928
|
+
const backupReader = await this.backupReader;
|
|
33929
|
+
backupReader.selectAll();
|
|
33930
|
+
if (this.selected) {
|
|
33931
|
+
backupReader.select(...this.selected);
|
|
33932
|
+
}
|
|
33933
|
+
if (this.aggregations) {
|
|
33934
|
+
backupReader.aggregate(...this.aggregations);
|
|
33935
|
+
}
|
|
33936
|
+
if (this.groupedBy) {
|
|
33937
|
+
backupReader.groupBy(...this.groupedBy);
|
|
33938
|
+
}
|
|
33939
|
+
if (this.sort) {
|
|
33940
|
+
backupReader.orderBy(...this.sort);
|
|
33941
|
+
}
|
|
33942
|
+
if (this.startIndex !== null && this.count !== null) {
|
|
33943
|
+
backupReader.limit(this.startIndex, this.count);
|
|
33944
|
+
}
|
|
33945
|
+
return backupReader.read();
|
|
33382
33946
|
}
|
|
33383
|
-
const asJson =
|
|
33947
|
+
const asJson = endpoint.supportsJson(featureType.name);
|
|
33384
33948
|
const attributes = this.selected ?? undefined;
|
|
33385
|
-
let
|
|
33949
|
+
let sortBy = null;
|
|
33950
|
+
if (this.sort) {
|
|
33951
|
+
const mapSort = (s) => [s[0] === 'desc' ? 'D' : 'A', s[1]];
|
|
33952
|
+
sortBy = Array.isArray(this.sort[0])
|
|
33953
|
+
? this.sort.map(mapSort)
|
|
33954
|
+
: mapSort(this.sort);
|
|
33955
|
+
}
|
|
33956
|
+
const url = endpoint.getFeatureUrl(featureType.name, {
|
|
33386
33957
|
...(this.startIndex !== null && { startIndex: this.startIndex }),
|
|
33387
33958
|
...(this.count !== null && { maxFeatures: this.count }),
|
|
33388
33959
|
asJson,
|
|
33389
33960
|
outputCrs: 'EPSG:4326',
|
|
33390
33961
|
attributes,
|
|
33391
|
-
|
|
33962
|
+
sortBy,
|
|
33392
33963
|
});
|
|
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
|
|
33964
|
+
return fetchDataAsText(url, this.cacheEnabled).then((text) => asJson
|
|
33402
33965
|
? 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;
|
|
33966
|
+
: parseGml(text, featureType.name, endpoint.getVersion()));
|
|
33417
33967
|
}
|
|
33418
33968
|
}
|
|
33419
33969
|
|
|
33420
|
-
async function openDataset(url,
|
|
33421
|
-
const fileType = await inferDatasetType(url, typeHint);
|
|
33970
|
+
async function openDataset(url, options) {
|
|
33971
|
+
const fileType = await inferDatasetType(url, options?.typeHint);
|
|
33422
33972
|
let reader;
|
|
33423
33973
|
try {
|
|
33424
33974
|
switch (fileType) {
|
|
@@ -33435,18 +33985,18 @@ async function openDataset(url, typeHint, options, cacheActive) {
|
|
|
33435
33985
|
reader = new ExcelReader(url);
|
|
33436
33986
|
break;
|
|
33437
33987
|
case 'gml':
|
|
33438
|
-
reader = new GmlReader(url
|
|
33988
|
+
reader = new GmlReader(url);
|
|
33439
33989
|
break;
|
|
33440
33990
|
case 'wfs':
|
|
33441
|
-
reader =
|
|
33991
|
+
reader = new WfsReader(url, options?.wfsFeatureType);
|
|
33442
33992
|
break;
|
|
33443
33993
|
}
|
|
33444
|
-
reader.
|
|
33994
|
+
reader.enableCache(options?.enableCache ?? true);
|
|
33445
33995
|
reader.load();
|
|
33446
33996
|
return reader;
|
|
33447
33997
|
}
|
|
33448
33998
|
catch (e) {
|
|
33449
|
-
//WfsReader may already raise a FetchError
|
|
33999
|
+
// WfsReader may already raise a FetchError
|
|
33450
34000
|
if (e instanceof FetchError)
|
|
33451
34001
|
throw e;
|
|
33452
34002
|
else
|
|
@@ -33457,13 +34007,13 @@ async function openDataset(url, typeHint, options, cacheActive) {
|
|
|
33457
34007
|
* This fetches the full dataset at the given URL and parses it according to its mime type.
|
|
33458
34008
|
* All items in the dataset are converted to GeoJSON features, even if they do not bear any spatial geometry.
|
|
33459
34009
|
* File type can be either inferred (from the HTTP headers or the URL), or hinted using the 2nd argument
|
|
33460
|
-
* File type is determined
|
|
34010
|
+
* File type is determined like so:
|
|
33461
34011
|
* 1. if a type hint is given, use it
|
|
33462
34012
|
* 2. otherwise, look for a Content-Type header in the response with a supported mime type
|
|
33463
34013
|
* 3. if no valid mime type was found, look for an explicit file extension in the url (.csv, .geojson etc.)
|
|
33464
34014
|
*/
|
|
33465
|
-
async function readDataset(url,
|
|
33466
|
-
const reader = await openDataset(url,
|
|
34015
|
+
async function readDataset(url, options) {
|
|
34016
|
+
const reader = await openDataset(url, options);
|
|
33467
34017
|
try {
|
|
33468
34018
|
return await reader.read();
|
|
33469
34019
|
}
|
|
@@ -33694,9 +34244,11 @@ class DataService {
|
|
|
33694
34244
|
getDataset(link, cacheActive) {
|
|
33695
34245
|
if (link.type === 'service' && link.accessServiceProtocol === 'wfs') {
|
|
33696
34246
|
const wfsUrlEndpoint = this.proxy.getProxiedUrl(link.url.toString());
|
|
33697
|
-
return from(openDataset(wfsUrlEndpoint,
|
|
34247
|
+
return from(openDataset(wfsUrlEndpoint, {
|
|
34248
|
+
typeHint: 'wfs',
|
|
33698
34249
|
wfsFeatureType: link.name,
|
|
33699
|
-
|
|
34250
|
+
enableCache: cacheActive,
|
|
34251
|
+
}));
|
|
33700
34252
|
}
|
|
33701
34253
|
else if (link.type === 'download') {
|
|
33702
34254
|
const linkProxifiedUrl = this.proxy.getProxiedUrl(link.url.toString());
|
|
@@ -33704,12 +34256,15 @@ class DataService {
|
|
|
33704
34256
|
const supportedType = SupportedTypes.indexOf(format) > -1
|
|
33705
34257
|
? format
|
|
33706
34258
|
: undefined;
|
|
33707
|
-
return from(openDataset(linkProxifiedUrl,
|
|
34259
|
+
return from(openDataset(linkProxifiedUrl, {
|
|
34260
|
+
typeHint: supportedType,
|
|
34261
|
+
enableCache: cacheActive,
|
|
34262
|
+
})).pipe();
|
|
33708
34263
|
}
|
|
33709
34264
|
else if (link.type === 'service' &&
|
|
33710
34265
|
link.accessServiceProtocol === 'esriRest') {
|
|
33711
34266
|
const url = this.getDownloadUrlFromEsriRest(link.url.toString(), 'geojson');
|
|
33712
|
-
return from(openDataset(url, 'geojson',
|
|
34267
|
+
return from(openDataset(url, { typeHint: 'geojson', enableCache: cacheActive })).pipe();
|
|
33713
34268
|
}
|
|
33714
34269
|
else if (link.type === 'service' &&
|
|
33715
34270
|
link.accessServiceProtocol === 'ogcFeatures') {
|
|
@@ -33723,7 +34278,10 @@ class DataService {
|
|
|
33723
34278
|
}
|
|
33724
34279
|
const urlWithoutLimit = new URL(geojsonUrl);
|
|
33725
34280
|
urlWithoutLimit.searchParams.delete('limit');
|
|
33726
|
-
return openDataset(urlWithoutLimit.toString(),
|
|
34281
|
+
return openDataset(urlWithoutLimit.toString(), {
|
|
34282
|
+
typeHint: 'geojson',
|
|
34283
|
+
enableCache: cacheActive,
|
|
34284
|
+
});
|
|
33727
34285
|
}));
|
|
33728
34286
|
}
|
|
33729
34287
|
return throwError(() => 'protocol not supported');
|
|
@@ -33975,20 +34533,31 @@ class DataTableComponent {
|
|
|
33975
34533
|
this.eltRef = inject(ElementRef);
|
|
33976
34534
|
this.cdr = inject(ChangeDetectorRef);
|
|
33977
34535
|
this.translateService = inject(TranslateService);
|
|
33978
|
-
this.
|
|
34536
|
+
this.columnsFromFeatureCatalog = null;
|
|
34537
|
+
this.columnsFromDataset = [];
|
|
33979
34538
|
this.selected = new EventEmitter();
|
|
33980
|
-
this.properties$ = new BehaviorSubject(null);
|
|
33981
34539
|
this.loading$ = new BehaviorSubject(false);
|
|
33982
34540
|
this.error = null;
|
|
33983
34541
|
}
|
|
33984
34542
|
set featureAttributes(value) {
|
|
33985
|
-
this.
|
|
33986
|
-
|
|
34543
|
+
this.columnsFromFeatureCatalog = value.map((attrs) => ({
|
|
34544
|
+
name: attrs.value,
|
|
34545
|
+
label: attrs.label,
|
|
34546
|
+
}));
|
|
33987
34547
|
}
|
|
33988
34548
|
set dataset(value) {
|
|
33989
34549
|
this.dataset_ = value;
|
|
33990
34550
|
this.dataset_.load();
|
|
33991
|
-
this.dataset_.info.then((info) =>
|
|
34551
|
+
this.dataset_.info.then((info) => {
|
|
34552
|
+
this.count = info.itemsCount;
|
|
34553
|
+
this.cdr.detectChanges();
|
|
34554
|
+
});
|
|
34555
|
+
}
|
|
34556
|
+
get columns() {
|
|
34557
|
+
return this.columnsFromFeatureCatalog ?? this.columnsFromDataset;
|
|
34558
|
+
}
|
|
34559
|
+
get columnNames() {
|
|
34560
|
+
return this.columns.map((c) => c.name);
|
|
33992
34561
|
}
|
|
33993
34562
|
ngOnInit() {
|
|
33994
34563
|
this.dataSource = new DataTableDataSource();
|
|
@@ -34023,11 +34592,12 @@ class DataTableComponent {
|
|
|
34023
34592
|
}
|
|
34024
34593
|
async readData() {
|
|
34025
34594
|
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
34595
|
try {
|
|
34596
|
+
// wait for properties to be read
|
|
34597
|
+
if (!this.columnsFromFeatureCatalog) {
|
|
34598
|
+
this.columnsFromDataset = await this.dataset_.properties;
|
|
34599
|
+
}
|
|
34600
|
+
this.dataset_.select(...this.columnNames);
|
|
34031
34601
|
await this.dataSource.showData(this.dataset_.read());
|
|
34032
34602
|
this.error = null;
|
|
34033
34603
|
}
|
|
@@ -34057,7 +34627,7 @@ class DataTableComponent {
|
|
|
34057
34627
|
}
|
|
34058
34628
|
}
|
|
34059
34629
|
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
|
|
34630
|
+
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
34631
|
}
|
|
34062
34632
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DataTableComponent, decorators: [{
|
|
34063
34633
|
type: Component,
|
|
@@ -34069,10 +34639,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
34069
34639
|
CommonModule,
|
|
34070
34640
|
LoadingMaskComponent,
|
|
34071
34641
|
PopupAlertComponent,
|
|
34072
|
-
LetDirective,
|
|
34073
34642
|
TranslatePipe,
|
|
34074
34643
|
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
|
|
34644
|
+
], 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
34645
|
}], propDecorators: { featureAttributes: [{
|
|
34077
34646
|
type: Input
|
|
34078
34647
|
}], dataset: [{
|
|
@@ -34175,7 +34744,7 @@ class ChartViewComponent {
|
|
|
34175
34744
|
}), shareReplay$1(1));
|
|
34176
34745
|
this.properties$ = combineLatest([this.dataset$, this.featureCatalog$]).pipe(switchMap$1(([dataset, catalog]) => this.setProperties(dataset, catalog)), shareReplay$1(1));
|
|
34177
34746
|
this.yChoices$ = this.properties$.pipe(map$1((properties) => properties
|
|
34178
|
-
.filter((prop) => prop.type === 'number'
|
|
34747
|
+
.filter((prop) => prop.type === 'number')
|
|
34179
34748
|
.map((prop) => ({ value: prop.name, label: prop.label || prop.name }))), tap$1((choices) => {
|
|
34180
34749
|
if (!choices.find((choice) => choice.value === this.yProperty$.value)) {
|
|
34181
34750
|
const newProp = choices[0]?.value || '';
|
|
@@ -34202,6 +34771,7 @@ class ChartViewComponent {
|
|
|
34202
34771
|
this.aggregation$,
|
|
34203
34772
|
]).pipe(filter$1(([_, x, y]) => !!x || !!y), switchMap$1(([dataset, xProp, yProp, aggregation]) => {
|
|
34204
34773
|
const fieldAgg = aggregation === 'count' ? ['count'] : [aggregation, yProp];
|
|
34774
|
+
this.loading = true;
|
|
34205
34775
|
return dataset
|
|
34206
34776
|
.groupBy(['distinct', xProp])
|
|
34207
34777
|
.aggregate(fieldAgg)
|
|
@@ -34443,6 +35013,7 @@ class GeoTableViewComponent {
|
|
|
34443
35013
|
}));
|
|
34444
35014
|
}
|
|
34445
35015
|
async initMapContext() {
|
|
35016
|
+
this.dataset.load();
|
|
34446
35017
|
this.dataset.selectAll();
|
|
34447
35018
|
return {
|
|
34448
35019
|
layers: [
|
|
@@ -34616,8 +35187,7 @@ class MdViewFacade {
|
|
|
34616
35187
|
}));
|
|
34617
35188
|
this.error$ = this.store.pipe(select(getMetadataError));
|
|
34618
35189
|
this.related$ = this.store.pipe(select(getRelated));
|
|
34619
|
-
this.
|
|
34620
|
-
this.sourceOf$ = this.store.pipe(select(getSourceOf));
|
|
35190
|
+
this.linkedRecords$ = this.store.pipe(select(getLinkedRecords));
|
|
34621
35191
|
this.chartConfig$ = this.store.pipe(select(getChartConfig));
|
|
34622
35192
|
this.allLinks$ = this.metadata$.pipe(map$1((record) => 'onlineResources' in record ? record.onlineResources : []), shareReplay$1(1));
|
|
34623
35193
|
this.resourceDoi$ = this.metadata$.pipe(map$1((record) => {
|
|
@@ -34736,12 +35306,9 @@ class MdViewEffects {
|
|
|
34736
35306
|
this.loadRelatedRecords$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getSimilarRecords(full)), map$1((related) => {
|
|
34737
35307
|
return setRelated({ related });
|
|
34738
35308
|
}), 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 })))));
|
|
35309
|
+
this.loadLinkedRecords$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getLinkedRecords(full)), map$1((linkedRecords) => {
|
|
35310
|
+
return setLinkedRecords({ linkedRecords });
|
|
35311
|
+
}), catchError(() => of(setLinkedRecords({ linkedRecords: null })))));
|
|
34745
35312
|
/*
|
|
34746
35313
|
UserFeedback effects
|
|
34747
35314
|
*/
|
|
@@ -40020,7 +40587,7 @@ function flattenQueryParams(params) {
|
|
|
40020
40587
|
const start = flattened[key].start;
|
|
40021
40588
|
const end = flattened[key].end;
|
|
40022
40589
|
flattened[key] = [
|
|
40023
|
-
`${start ? formatDate(start) : ''}..${formatDate(end)
|
|
40590
|
+
`${start ? formatDate(start) : ''}..${end ? formatDate(end) : ''}`,
|
|
40024
40591
|
];
|
|
40025
40592
|
}
|
|
40026
40593
|
}
|
|
@@ -40377,5 +40944,5 @@ const CHART_TYPE_VALUES = [
|
|
|
40377
40944
|
* Generated bundle index. Do not edit.
|
|
40378
40945
|
*/
|
|
40379
40946
|
|
|
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 };
|
|
40947
|
+
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
40948
|
//# sourceMappingURL=geonetwork-ui.mjs.map
|