geonetwork-ui 2.11.0-dev.e7b8ec331 → 2.11.0-dev.fb3fea50c
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 +1273 -698
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +223 -79
- 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 +77 -34
- 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 +12 -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 +18 -8
- package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +9 -1
- package/src/libs/feature/search/src/lib/utils/service/fields.ts +43 -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 +17 -2
- package/translations/en.json +16 -1
- package/translations/es.json +15 -0
- package/translations/fr.json +17 -2
- package/translations/it.json +15 -0
- package/translations/nl.json +15 -0
- package/translations/pt.json +15 -0
- package/translations/sk.json +15 -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.fb3fea50c";
|
|
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",
|
|
@@ -20631,8 +20697,8 @@ class ElasticsearchService {
|
|
|
20631
20697
|
if (!payload.runtime_mappings)
|
|
20632
20698
|
payload.runtime_mappings = {};
|
|
20633
20699
|
payload.runtime_mappings[fieldName] = {
|
|
20634
|
-
type:
|
|
20635
|
-
script: this.runtimeFields[fieldName],
|
|
20700
|
+
type: this.runtimeFields[fieldName].type,
|
|
20701
|
+
script: this.runtimeFields[fieldName].script,
|
|
20636
20702
|
};
|
|
20637
20703
|
};
|
|
20638
20704
|
const lookForField = (node) => {
|
|
@@ -20652,6 +20718,12 @@ class ElasticsearchService {
|
|
|
20652
20718
|
(node[runtimeField] === 'asc' || node[runtimeField] === 'desc')) {
|
|
20653
20719
|
addMapping(runtimeField);
|
|
20654
20720
|
}
|
|
20721
|
+
if (runtimeField in node &&
|
|
20722
|
+
typeof node[runtimeField] === 'object' &&
|
|
20723
|
+
node[runtimeField] !== null &&
|
|
20724
|
+
('gte' in node[runtimeField] || 'lte' in node[runtimeField])) {
|
|
20725
|
+
addMapping(runtimeField);
|
|
20726
|
+
}
|
|
20655
20727
|
if ('query' in node &&
|
|
20656
20728
|
typeof node.query === 'string' &&
|
|
20657
20729
|
node.query.indexOf(runtimeField + ':') > -1) {
|
|
@@ -20668,8 +20740,8 @@ class ElasticsearchService {
|
|
|
20668
20740
|
lookForField(payload.query);
|
|
20669
20741
|
return payload;
|
|
20670
20742
|
}
|
|
20671
|
-
registerRuntimeField(fieldName, expression) {
|
|
20672
|
-
this.runtimeFields[fieldName] = expression;
|
|
20743
|
+
registerRuntimeField(fieldName, expression, type = 'keyword') {
|
|
20744
|
+
this.runtimeFields[fieldName] = { script: expression, type };
|
|
20673
20745
|
}
|
|
20674
20746
|
getMetadataByIdsPayload(uuids) {
|
|
20675
20747
|
return {
|
|
@@ -20678,6 +20750,7 @@ class ElasticsearchService {
|
|
|
20678
20750
|
values: uuids,
|
|
20679
20751
|
},
|
|
20680
20752
|
},
|
|
20753
|
+
size: uuids.length,
|
|
20681
20754
|
};
|
|
20682
20755
|
}
|
|
20683
20756
|
getRelatedRecordPayload(record, size = 6, _source = [...ES_SOURCE_SUMMARY, 'createDate']) {
|
|
@@ -20785,7 +20858,13 @@ class ElasticsearchService {
|
|
|
20785
20858
|
isCurrentSearchLang() {
|
|
20786
20859
|
return this.metadataLang === 'current';
|
|
20787
20860
|
}
|
|
20788
|
-
|
|
20861
|
+
findSpatialFilterExtent(filters) {
|
|
20862
|
+
if (typeof filters === 'string') {
|
|
20863
|
+
return undefined;
|
|
20864
|
+
}
|
|
20865
|
+
return Object.values(filters).find(isBoundingBox);
|
|
20866
|
+
}
|
|
20867
|
+
filtersToQuery(filters, spatialFilterExtent = this.findSpatialFilterExtent(filters)) {
|
|
20789
20868
|
const addQuote = (key) => (/^\/.+\/$/.test(key) ? key : `"${key}"`);
|
|
20790
20869
|
const makeQuery = (filter) => {
|
|
20791
20870
|
if (typeof filter === 'string') {
|
|
@@ -20804,7 +20883,9 @@ class ElasticsearchService {
|
|
|
20804
20883
|
? filters
|
|
20805
20884
|
: Object.keys(filters)
|
|
20806
20885
|
.filter((fieldname) => fieldname !== 'gn-ui-crossFieldFilter')
|
|
20886
|
+
.filter((fieldname) => !isBoundingBox(filters[fieldname]))
|
|
20807
20887
|
.filter((fieldname) => !isDateRange(filters[fieldname]))
|
|
20888
|
+
.filter((fieldname) => !Array.isArray(filters[fieldname]))
|
|
20808
20889
|
.filter((fieldname) => filters[fieldname] &&
|
|
20809
20890
|
JSON.stringify(filters[fieldname]) !== '{}')
|
|
20810
20891
|
.map((fieldname) => `${fieldname}:(${makeQuery(filters[fieldname])})`)
|
|
@@ -20812,33 +20893,36 @@ class ElasticsearchService {
|
|
|
20812
20893
|
if (filters['gn-ui-crossFieldFilter']) {
|
|
20813
20894
|
queryString = `${queryString} AND (${filters['gn-ui-crossFieldFilter']})`;
|
|
20814
20895
|
}
|
|
20815
|
-
const
|
|
20816
|
-
.filter(([, value]) => isDateRange(value))
|
|
20817
|
-
.map(([searchField, dateRange]) => {
|
|
20818
|
-
return {
|
|
20819
|
-
searchField,
|
|
20820
|
-
dateRange,
|
|
20821
|
-
};
|
|
20822
|
-
})[0];
|
|
20896
|
+
const queryRanges = Object.entries(filters).filter(([, value]) => isDateRange(value));
|
|
20823
20897
|
const queryParts = [
|
|
20824
20898
|
queryString && {
|
|
20825
20899
|
query_string: {
|
|
20826
20900
|
query: queryString,
|
|
20827
20901
|
},
|
|
20828
20902
|
},
|
|
20829
|
-
|
|
20830
|
-
queryRange.dateRange && {
|
|
20903
|
+
...queryRanges.map(([searchField, dateRange]) => ({
|
|
20831
20904
|
range: {
|
|
20832
|
-
[
|
|
20833
|
-
...(
|
|
20834
|
-
|
|
20835
|
-
}),
|
|
20836
|
-
...(queryRange.dateRange.end && {
|
|
20837
|
-
lte: formatDate(queryRange.dateRange.end),
|
|
20838
|
-
}),
|
|
20905
|
+
[searchField]: {
|
|
20906
|
+
...(dateRange.start && { gte: formatDate(dateRange.start) }),
|
|
20907
|
+
...(dateRange.end && { lte: formatDate(dateRange.end) }),
|
|
20839
20908
|
format: 'yyyy-MM-dd',
|
|
20840
20909
|
},
|
|
20841
20910
|
},
|
|
20911
|
+
})),
|
|
20912
|
+
spatialFilterExtent && {
|
|
20913
|
+
geo_shape: {
|
|
20914
|
+
geom: {
|
|
20915
|
+
shape: {
|
|
20916
|
+
type: 'envelope',
|
|
20917
|
+
// spatialFilterExtent is [minX, minY, maxX, maxY]; envelope coordinates are [top-left, bottom-right]
|
|
20918
|
+
coordinates: [
|
|
20919
|
+
[spatialFilterExtent[0], spatialFilterExtent[3]],
|
|
20920
|
+
[spatialFilterExtent[2], spatialFilterExtent[1]],
|
|
20921
|
+
],
|
|
20922
|
+
},
|
|
20923
|
+
relation: 'intersects',
|
|
20924
|
+
},
|
|
20925
|
+
},
|
|
20842
20926
|
},
|
|
20843
20927
|
].filter(Boolean);
|
|
20844
20928
|
return queryParts.length > 0 ? queryParts : undefined;
|
|
@@ -20866,7 +20950,9 @@ class ElasticsearchService {
|
|
|
20866
20950
|
},
|
|
20867
20951
|
});
|
|
20868
20952
|
}
|
|
20869
|
-
|
|
20953
|
+
// a spatial extent filter takes precedence over the preference geometry for boosting
|
|
20954
|
+
const spatialFilterExtent = this.findSpatialFilterExtent(fieldSearchFilters);
|
|
20955
|
+
const queryFilters = this.filtersToQuery(fieldSearchFilters, spatialFilterExtent);
|
|
20870
20956
|
if (queryFilters) {
|
|
20871
20957
|
filter.push(...queryFilters);
|
|
20872
20958
|
}
|
|
@@ -20877,7 +20963,10 @@ class ElasticsearchService {
|
|
|
20877
20963
|
},
|
|
20878
20964
|
});
|
|
20879
20965
|
}
|
|
20880
|
-
|
|
20966
|
+
const boostGeometry = spatialFilterExtent
|
|
20967
|
+
? bboxToPolygon(spatialFilterExtent)
|
|
20968
|
+
: geometry;
|
|
20969
|
+
if (boostGeometry) {
|
|
20881
20970
|
// boosts applied using the filter geometry:
|
|
20882
20971
|
// * records completely within the geometry receive a boost of 5
|
|
20883
20972
|
// * records intersecting the geometry receive a boost of 2
|
|
@@ -20886,7 +20975,7 @@ class ElasticsearchService {
|
|
|
20886
20975
|
should.push({
|
|
20887
20976
|
geo_shape: {
|
|
20888
20977
|
geom: {
|
|
20889
|
-
shape:
|
|
20978
|
+
shape: boostGeometry,
|
|
20890
20979
|
relation: 'within',
|
|
20891
20980
|
},
|
|
20892
20981
|
boost: 5.0,
|
|
@@ -20894,7 +20983,7 @@ class ElasticsearchService {
|
|
|
20894
20983
|
}, {
|
|
20895
20984
|
geo_shape: {
|
|
20896
20985
|
geom: {
|
|
20897
|
-
shape:
|
|
20986
|
+
shape: boostGeometry,
|
|
20898
20987
|
relation: 'intersects',
|
|
20899
20988
|
},
|
|
20900
20989
|
boost: 2.0,
|
|
@@ -20903,7 +20992,7 @@ class ElasticsearchService {
|
|
|
20903
20992
|
// this will boost the results variably depending on their distance from the given geometry
|
|
20904
20993
|
// note: this takes into account the `location` field of a record; this is generally the center of all spatial extents
|
|
20905
20994
|
// combined, and thus the actual size/coverage of the record spatial extent isn't relevant here
|
|
20906
|
-
const bbox = getGeometryBoundingBox(
|
|
20995
|
+
const bbox = getGeometryBoundingBox(boostGeometry);
|
|
20907
20996
|
const center = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2];
|
|
20908
20997
|
const northToCenter = new LineString([
|
|
20909
20998
|
[center[0], bbox[3]],
|
|
@@ -21229,7 +21318,7 @@ class Gn4Repository {
|
|
|
21229
21318
|
}
|
|
21230
21319
|
getRecord(uniqueIdentifier) {
|
|
21231
21320
|
return this.gn4SearchApi
|
|
21232
|
-
.search('bucket', ['fcats', 'hassources'], JSON.stringify(this.gn4SearchHelper.getMetadataByIdsPayload([uniqueIdentifier])))
|
|
21321
|
+
.search('bucket', ['fcats', 'hassources', 'siblings', 'associated'], JSON.stringify(this.gn4SearchHelper.getMetadataByIdsPayload([uniqueIdentifier])))
|
|
21233
21322
|
.pipe(map$1((results) => results.hits.hits[0]), switchMap((record) => record ? this.gn4Mapper.readRecord(record) : of(null)));
|
|
21234
21323
|
}
|
|
21235
21324
|
getMultipleRecords(uniqueIdentifiers) {
|
|
@@ -21283,19 +21372,28 @@ class Gn4Repository {
|
|
|
21283
21372
|
.search('bucket', null, JSON.stringify(this.gn4SearchHelper.getRelatedRecordPayload(similarTo, 3)))
|
|
21284
21373
|
.pipe(switchMap((results) => this.gn4Mapper.readRecords(results.hits.hits)));
|
|
21285
21374
|
}
|
|
21286
|
-
|
|
21287
|
-
const
|
|
21288
|
-
|
|
21289
|
-
|
|
21290
|
-
|
|
21291
|
-
|
|
21292
|
-
|
|
21293
|
-
|
|
21294
|
-
|
|
21295
|
-
|
|
21296
|
-
|
|
21375
|
+
getLinkedRecords(record) {
|
|
21376
|
+
const siblings = (record.extras?.['siblings'] ?? []);
|
|
21377
|
+
const relations = [
|
|
21378
|
+
['source', (record.extras?.['sourcesIdentifiers'] ?? [])],
|
|
21379
|
+
['sourceOf', (record.extras?.['sourceOfIdentifiers'] ?? [])],
|
|
21380
|
+
['sibling', siblings.map(({ uniqueIdentifier }) => uniqueIdentifier)],
|
|
21381
|
+
[
|
|
21382
|
+
'associated',
|
|
21383
|
+
(record.extras?.['associatedIdentifiers'] ?? []),
|
|
21384
|
+
],
|
|
21385
|
+
];
|
|
21386
|
+
const requested = relations.filter(([, identifiers]) => identifiers.length > 0);
|
|
21387
|
+
if (requested.length === 0) {
|
|
21388
|
+
return of([]);
|
|
21297
21389
|
}
|
|
21298
|
-
return
|
|
21390
|
+
return forkJoin(requested.map(([relation, identifiers]) => this.getMultipleRecords(identifiers).pipe(map$1((records) => (records ?? []).map((record) => ({
|
|
21391
|
+
record,
|
|
21392
|
+
relation,
|
|
21393
|
+
associationType: relation === 'sibling'
|
|
21394
|
+
? siblings.find(({ uniqueIdentifier }) => uniqueIdentifier === record.uniqueIdentifier)?.associationType
|
|
21395
|
+
: undefined,
|
|
21396
|
+
}))), catchError(() => of([]))))).pipe(map$1((groups) => groups.flat()));
|
|
21299
21397
|
}
|
|
21300
21398
|
aggregate(params) {
|
|
21301
21399
|
// if aggregations are empty, return an empty object right away
|
|
@@ -22556,9 +22654,10 @@ const VECTOR_STYLE_DEFAULT = new InjectionToken('vectorStyleDefault', {
|
|
|
22556
22654
|
});
|
|
22557
22655
|
|
|
22558
22656
|
const DEFAULT_BASEMAP_LAYER = {
|
|
22559
|
-
type: '
|
|
22560
|
-
|
|
22561
|
-
|
|
22657
|
+
type: 'maplibre-style',
|
|
22658
|
+
styleUrl: `https://basemaps.cartocdn.com/gl/positron-gl-style/style.json`,
|
|
22659
|
+
clickable: false,
|
|
22660
|
+
hoverable: false,
|
|
22562
22661
|
};
|
|
22563
22662
|
const DEFAULT_VIEW = {
|
|
22564
22663
|
center: [0, 15],
|
|
@@ -23273,6 +23372,7 @@ function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
23273
23372
|
'search_preset',
|
|
23274
23373
|
'advanced_filters',
|
|
23275
23374
|
'limit',
|
|
23375
|
+
'spatial_extent_max_file_size',
|
|
23276
23376
|
], warnings, errors);
|
|
23277
23377
|
const parsedSearchParams = parseMultiConfigSection(parsed, 'search_preset', ['name'], ['sort', 'filters'], warnings, errors);
|
|
23278
23378
|
searchConfig =
|
|
@@ -23290,6 +23390,7 @@ function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
23290
23390
|
})),
|
|
23291
23391
|
ADVANCED_FILTERS: parsedSearchSection.advanced_filters,
|
|
23292
23392
|
LIMIT: parsedSearchSection.limit,
|
|
23393
|
+
SPATIAL_EXTENT_MAX_FILE_SIZE: parsedSearchSection.spatial_extent_max_file_size,
|
|
23293
23394
|
};
|
|
23294
23395
|
const parsedMetadataQualitySection = parseConfigSection(parsed, 'metadata-quality', [], ['enabled'], warnings, errors);
|
|
23295
23396
|
metadataQualityConfig =
|
|
@@ -24547,12 +24648,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24547
24648
|
type: Input
|
|
24548
24649
|
}] } });
|
|
24549
24650
|
|
|
24651
|
+
function provideLocalizedDateAdapter() {
|
|
24652
|
+
return [
|
|
24653
|
+
{ provide: MAT_DATE_LOCALE, useValue: enUS },
|
|
24654
|
+
{ provide: MAT_DATE_FORMATS, useValue: MAT_DATE_FNS_FORMATS },
|
|
24655
|
+
{
|
|
24656
|
+
provide: DateAdapter,
|
|
24657
|
+
useFactory: () => {
|
|
24658
|
+
const dateService = inject(DateService);
|
|
24659
|
+
const adapter = new DateFnsAdapter();
|
|
24660
|
+
dateService
|
|
24661
|
+
.getDateFnsLocale()
|
|
24662
|
+
.then((locale) => adapter.setLocale(locale));
|
|
24663
|
+
return adapter;
|
|
24664
|
+
},
|
|
24665
|
+
},
|
|
24666
|
+
];
|
|
24667
|
+
}
|
|
24668
|
+
|
|
24550
24669
|
class DatePickerComponent {
|
|
24551
24670
|
constructor() {
|
|
24552
|
-
this.dateAdapter = inject(DateAdapter);
|
|
24553
|
-
this.translate = inject(TranslateService);
|
|
24554
24671
|
this.dateChange = new EventEmitter();
|
|
24555
|
-
this.dateAdapter.setLocale(this.translate.getCurrentLang());
|
|
24556
24672
|
}
|
|
24557
24673
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
24558
24674
|
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 +24676,19 @@ class DatePickerComponent {
|
|
|
24560
24676
|
provideNgIconsConfig({
|
|
24561
24677
|
size: '1.5rem',
|
|
24562
24678
|
}),
|
|
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 }); }
|
|
24679
|
+
provideLocalizedDateAdapter(),
|
|
24680
|
+
], 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
24681
|
}
|
|
24569
24682
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatePickerComponent, decorators: [{
|
|
24570
24683
|
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: [
|
|
24684
|
+
args: [{ selector: 'gn-ui-date-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [MatDatepickerModule, ButtonComponent, NgIconComponent], providers: [
|
|
24577
24685
|
provideIcons({ iconoirCalendar }),
|
|
24578
24686
|
provideNgIconsConfig({
|
|
24579
24687
|
size: '1.5rem',
|
|
24580
24688
|
}),
|
|
24581
|
-
|
|
24582
|
-
provide: MAT_DATE_LOCALE,
|
|
24583
|
-
useFactory: (locale) => locale,
|
|
24584
|
-
},
|
|
24689
|
+
provideLocalizedDateAdapter(),
|
|
24585
24690
|
], 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
|
-
}],
|
|
24691
|
+
}], propDecorators: { date: [{
|
|
24587
24692
|
type: Input
|
|
24588
24693
|
}], dateChange: [{
|
|
24589
24694
|
type: Output
|
|
@@ -24591,58 +24696,159 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24591
24696
|
|
|
24592
24697
|
class DateRangeDropdownComponent {
|
|
24593
24698
|
constructor() {
|
|
24594
|
-
this.
|
|
24699
|
+
this.scrollStrategies = inject(ScrollStrategyOptions);
|
|
24700
|
+
this.dateAdapter = inject(DateAdapter);
|
|
24701
|
+
this.dateFormats = inject(MAT_DATE_FORMATS);
|
|
24595
24702
|
this.cdr = inject(ChangeDetectorRef);
|
|
24596
|
-
this.
|
|
24597
|
-
this.
|
|
24598
|
-
this.
|
|
24703
|
+
this.dateRange = {};
|
|
24704
|
+
this.dateRangeChange = new EventEmitter();
|
|
24705
|
+
this.overlayPositions = [
|
|
24706
|
+
{
|
|
24707
|
+
originX: 'start',
|
|
24708
|
+
originY: 'bottom',
|
|
24709
|
+
overlayX: 'start',
|
|
24710
|
+
overlayY: 'top',
|
|
24711
|
+
offsetY: 8,
|
|
24712
|
+
},
|
|
24713
|
+
{
|
|
24714
|
+
originX: 'start',
|
|
24715
|
+
originY: 'top',
|
|
24716
|
+
overlayX: 'start',
|
|
24717
|
+
overlayY: 'bottom',
|
|
24718
|
+
offsetY: -8,
|
|
24719
|
+
},
|
|
24720
|
+
];
|
|
24721
|
+
this.scrollStrategy = this.scrollStrategies.reposition();
|
|
24722
|
+
this.overlayOpen = false;
|
|
24723
|
+
this.expandedBound = 'start';
|
|
24724
|
+
this.invalidBounds = { start: false, end: false };
|
|
24725
|
+
// redraw when the lazily loaded UI locale reaches the adapter
|
|
24726
|
+
this.dateAdapter.localeChanges
|
|
24727
|
+
.pipe(takeUntilDestroyed())
|
|
24728
|
+
.subscribe(() => this.cdr.markForCheck());
|
|
24599
24729
|
}
|
|
24600
|
-
|
|
24601
|
-
this.
|
|
24730
|
+
get startDate() {
|
|
24731
|
+
return this.dateRange.start;
|
|
24602
24732
|
}
|
|
24603
|
-
|
|
24604
|
-
|
|
24605
|
-
|
|
24606
|
-
|
|
24607
|
-
|
|
24608
|
-
|
|
24609
|
-
|
|
24610
|
-
|
|
24733
|
+
get endDate() {
|
|
24734
|
+
return this.dateRange.end;
|
|
24735
|
+
}
|
|
24736
|
+
get selectedDatesCount() {
|
|
24737
|
+
return (this.startDate ? 1 : 0) + (this.endDate ? 1 : 0);
|
|
24738
|
+
}
|
|
24739
|
+
openOverlay() {
|
|
24740
|
+
this.expandedBound = 'start';
|
|
24741
|
+
this.overlayOpen = true;
|
|
24742
|
+
}
|
|
24743
|
+
closeOverlay() {
|
|
24744
|
+
this.overlayOpen = false;
|
|
24745
|
+
}
|
|
24746
|
+
toggleBound(bound) {
|
|
24747
|
+
this.expandedBound = this.expandedBound === bound ? null : bound;
|
|
24748
|
+
}
|
|
24749
|
+
formatDate(date) {
|
|
24750
|
+
return this.dateAdapter.format(date, this.dateFormats.display.dateInput);
|
|
24751
|
+
}
|
|
24752
|
+
onDateInput(bound, event) {
|
|
24753
|
+
const typedText = event.target.value.trim();
|
|
24754
|
+
const date = typedText
|
|
24755
|
+
? this.dateAdapter.parse(typedText, this.dateFormats.parse.dateInput)
|
|
24756
|
+
: null;
|
|
24757
|
+
if (!this.isAcceptable(bound, typedText, date)) {
|
|
24758
|
+
// keep the text as typed and flag it; the filter stays on its last value
|
|
24759
|
+
this.invalidBounds = { ...this.invalidBounds, [bound]: true };
|
|
24760
|
+
return;
|
|
24761
|
+
}
|
|
24762
|
+
if (bound === 'start')
|
|
24763
|
+
this.setStartDate(date);
|
|
24764
|
+
else
|
|
24765
|
+
this.setEndDate(date);
|
|
24766
|
+
}
|
|
24767
|
+
normalizeDateInput(bound, event) {
|
|
24768
|
+
if (this.invalidBounds[bound])
|
|
24769
|
+
return;
|
|
24770
|
+
const date = bound === 'start' ? this.startDate : this.endDate;
|
|
24771
|
+
const input = event.target;
|
|
24772
|
+
input.value = date ? this.formatDate(date) : '';
|
|
24773
|
+
}
|
|
24774
|
+
isAcceptable(bound, typedText, date) {
|
|
24775
|
+
if (!typedText)
|
|
24776
|
+
return true;
|
|
24777
|
+
if (!date || !this.dateAdapter.isValid(date))
|
|
24778
|
+
return false;
|
|
24779
|
+
return bound === 'start'
|
|
24780
|
+
? !this.endDate || date <= this.endDate
|
|
24781
|
+
: !this.startDate || date >= this.startDate;
|
|
24782
|
+
}
|
|
24783
|
+
setStartDate(date) {
|
|
24784
|
+
this.invalidBounds = { ...this.invalidBounds, start: false };
|
|
24785
|
+
this.applyDateRange({ ...this.dateRange, start: date });
|
|
24786
|
+
this.expandedBound = 'end';
|
|
24787
|
+
}
|
|
24788
|
+
setEndDate(date) {
|
|
24789
|
+
this.invalidBounds = { ...this.invalidBounds, end: false };
|
|
24790
|
+
this.applyDateRange({ ...this.dateRange, end: date });
|
|
24791
|
+
this.expandedBound = null;
|
|
24792
|
+
}
|
|
24793
|
+
clearDates(event) {
|
|
24794
|
+
this.invalidBounds = { start: false, end: false };
|
|
24795
|
+
this.expandedBound = 'start';
|
|
24796
|
+
this.applyDateRange({});
|
|
24797
|
+
propagateToDocumentOnly(event);
|
|
24798
|
+
}
|
|
24799
|
+
applyDateRange(dateRange) {
|
|
24800
|
+
this.dateRange = {
|
|
24801
|
+
...(dateRange.start && { start: dateRange.start }),
|
|
24802
|
+
...(dateRange.end && { end: dateRange.end }),
|
|
24803
|
+
};
|
|
24804
|
+
this.dateRangeChange.emit(this.dateRange);
|
|
24611
24805
|
}
|
|
24612
24806
|
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: "
|
|
24807
|
+
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
24808
|
provideIcons({
|
|
24615
|
-
|
|
24809
|
+
iconoirCalendar,
|
|
24810
|
+
matClose,
|
|
24616
24811
|
matExpandLess,
|
|
24812
|
+
matExpandMore,
|
|
24813
|
+
}),
|
|
24814
|
+
provideNgIconsConfig({
|
|
24815
|
+
size: '1.5rem',
|
|
24617
24816
|
}),
|
|
24618
|
-
|
|
24817
|
+
provideLocalizedDateAdapter(),
|
|
24818
|
+
], 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
24819
|
}
|
|
24620
24820
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangeDropdownComponent, decorators: [{
|
|
24621
24821
|
type: Component,
|
|
24622
24822
|
args: [{ selector: 'gn-ui-date-range-dropdown', standalone: true, imports: [
|
|
24623
|
-
|
|
24624
|
-
MatNativeDateModule,
|
|
24625
|
-
MatDatepickerModule,
|
|
24823
|
+
AutofocusDirective,
|
|
24626
24824
|
ButtonComponent,
|
|
24825
|
+
MatDatepickerModule,
|
|
24826
|
+
NgIcon,
|
|
24827
|
+
NgTemplateOutlet,
|
|
24828
|
+
OverlayModule,
|
|
24829
|
+
TranslateDirective,
|
|
24830
|
+
TranslatePipe,
|
|
24627
24831
|
], providers: [
|
|
24628
24832
|
provideIcons({
|
|
24629
|
-
|
|
24833
|
+
iconoirCalendar,
|
|
24834
|
+
matClose,
|
|
24630
24835
|
matExpandLess,
|
|
24836
|
+
matExpandMore,
|
|
24631
24837
|
}),
|
|
24632
|
-
|
|
24633
|
-
|
|
24634
|
-
|
|
24635
|
-
|
|
24838
|
+
provideNgIconsConfig({
|
|
24839
|
+
size: '1.5rem',
|
|
24840
|
+
}),
|
|
24841
|
+
provideLocalizedDateAdapter(),
|
|
24842
|
+
], 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"] }]
|
|
24843
|
+
}], ctorParameters: () => [], propDecorators: { title: [{
|
|
24636
24844
|
type: Input
|
|
24637
|
-
}],
|
|
24845
|
+
}], dateRange: [{
|
|
24638
24846
|
type: Input
|
|
24639
|
-
}],
|
|
24640
|
-
type: Output
|
|
24641
|
-
}], endDateChange: [{
|
|
24847
|
+
}], dateRangeChange: [{
|
|
24642
24848
|
type: Output
|
|
24643
|
-
}],
|
|
24849
|
+
}], overlayOrigin: [{
|
|
24644
24850
|
type: ViewChild,
|
|
24645
|
-
args: ['
|
|
24851
|
+
args: ['overlayOrigin']
|
|
24646
24852
|
}] } });
|
|
24647
24853
|
|
|
24648
24854
|
class DateRangePickerComponent {
|
|
@@ -24656,20 +24862,17 @@ class DateRangePickerComponent {
|
|
|
24656
24862
|
provideNgIconsConfig({
|
|
24657
24863
|
size: '1.5rem',
|
|
24658
24864
|
}),
|
|
24659
|
-
|
|
24865
|
+
provideLocalizedDateAdapter(),
|
|
24866
|
+
], 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
24867
|
}
|
|
24661
24868
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangePickerComponent, decorators: [{
|
|
24662
24869
|
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: [
|
|
24870
|
+
args: [{ selector: 'gn-ui-date-range-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [MatDatepickerModule, ButtonComponent, NgIconComponent], providers: [
|
|
24669
24871
|
provideIcons({ iconoirCalendar }),
|
|
24670
24872
|
provideNgIconsConfig({
|
|
24671
24873
|
size: '1.5rem',
|
|
24672
24874
|
}),
|
|
24875
|
+
provideLocalizedDateAdapter(),
|
|
24673
24876
|
], 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
24877
|
}], propDecorators: { startDate: [{
|
|
24675
24878
|
type: Input
|
|
@@ -24687,28 +24890,68 @@ class DragAndDropFileInputComponent {
|
|
|
24687
24890
|
constructor() {
|
|
24688
24891
|
this.placeholder = placeholder;
|
|
24689
24892
|
this.accept = '*';
|
|
24893
|
+
this.maxFileSizeMb = null;
|
|
24894
|
+
this.icon = null;
|
|
24895
|
+
this.dropzoneBackgroundColor = null;
|
|
24896
|
+
this.textClass = '';
|
|
24897
|
+
this.extraClass = '';
|
|
24898
|
+
this.showFileName = true;
|
|
24690
24899
|
this.fileChange = new EventEmitter();
|
|
24900
|
+
this.errorChange = new EventEmitter();
|
|
24691
24901
|
this.selectedFile = null;
|
|
24692
24902
|
}
|
|
24693
24903
|
get fileName() {
|
|
24694
24904
|
return this.selectedFile && this.selectedFile.name;
|
|
24695
24905
|
}
|
|
24906
|
+
get maxFileSizeBytes() {
|
|
24907
|
+
return typeof this.maxFileSizeMb === 'number'
|
|
24908
|
+
? megabytesToBytes(this.maxFileSizeMb)
|
|
24909
|
+
: null;
|
|
24910
|
+
}
|
|
24696
24911
|
selectFile(event) {
|
|
24912
|
+
if (event.rejectedFiles?.length) {
|
|
24913
|
+
const reason = event.rejectedFiles[0].reason;
|
|
24914
|
+
this.errorChange.emit(reason === 'size' ? 'file-too-large' : 'invalid-extension');
|
|
24915
|
+
return;
|
|
24916
|
+
}
|
|
24697
24917
|
this.selectedFile = event.addedFiles[0];
|
|
24698
24918
|
this.fileChange.emit(this.selectedFile);
|
|
24699
24919
|
}
|
|
24920
|
+
openFileSelector() {
|
|
24921
|
+
this.dropzone.showFileSelector();
|
|
24922
|
+
}
|
|
24923
|
+
clear() {
|
|
24924
|
+
this.selectedFile = null;
|
|
24925
|
+
}
|
|
24700
24926
|
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
|
|
24927
|
+
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
24928
|
}
|
|
24703
24929
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DragAndDropFileInputComponent, decorators: [{
|
|
24704
24930
|
type: Component,
|
|
24705
|
-
args: [{ selector: 'gn-ui-drag-and-drop-file-input', standalone: true, imports: [NgxDropzoneModule], template: "<div
|
|
24931
|
+
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
24932
|
}], propDecorators: { placeholder: [{
|
|
24707
24933
|
type: Input
|
|
24708
24934
|
}], accept: [{
|
|
24709
24935
|
type: Input
|
|
24936
|
+
}], maxFileSizeMb: [{
|
|
24937
|
+
type: Input
|
|
24938
|
+
}], icon: [{
|
|
24939
|
+
type: Input
|
|
24940
|
+
}], dropzoneBackgroundColor: [{
|
|
24941
|
+
type: Input
|
|
24942
|
+
}], textClass: [{
|
|
24943
|
+
type: Input
|
|
24944
|
+
}], extraClass: [{
|
|
24945
|
+
type: Input
|
|
24946
|
+
}], showFileName: [{
|
|
24947
|
+
type: Input
|
|
24710
24948
|
}], fileChange: [{
|
|
24711
24949
|
type: Output
|
|
24950
|
+
}], errorChange: [{
|
|
24951
|
+
type: Output
|
|
24952
|
+
}], dropzone: [{
|
|
24953
|
+
type: ViewChild,
|
|
24954
|
+
args: [NgxDropzoneComponent]
|
|
24712
24955
|
}] } });
|
|
24713
24956
|
|
|
24714
24957
|
class DropdownMultiselectComponent {
|
|
@@ -24863,7 +25106,7 @@ class DropdownMultiselectComponent {
|
|
|
24863
25106
|
matExpandMore,
|
|
24864
25107
|
matExpandLess,
|
|
24865
25108
|
}),
|
|
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-
|
|
25109
|
+
], 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
25110
|
}
|
|
24868
25111
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DropdownMultiselectComponent, decorators: [{
|
|
24869
25112
|
type: Component,
|
|
@@ -24873,7 +25116,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24873
25116
|
matExpandMore,
|
|
24874
25117
|
matExpandLess,
|
|
24875
25118
|
}),
|
|
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-
|
|
25119
|
+
], 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
25120
|
}], propDecorators: { title: [{
|
|
24878
25121
|
type: Input
|
|
24879
25122
|
}], choices: [{
|
|
@@ -25045,7 +25288,7 @@ class DropdownSelectorComponent {
|
|
|
25045
25288
|
provideNgIconsConfig({
|
|
25046
25289
|
size: '1.5em',
|
|
25047
25290
|
}),
|
|
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$
|
|
25291
|
+
], 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
25292
|
}
|
|
25050
25293
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DropdownSelectorComponent, decorators: [{
|
|
25051
25294
|
type: Component,
|
|
@@ -25785,6 +26028,175 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
25785
26028
|
type: Output
|
|
25786
26029
|
}] } });
|
|
25787
26030
|
|
|
26031
|
+
marker('search.filters.spatialExtent.import');
|
|
26032
|
+
marker('search.filters.spatialExtent.helpText');
|
|
26033
|
+
marker('search.filters.spatialExtent.error.title');
|
|
26034
|
+
const LABEL_FROM_FILE = marker('search.filters.spatialExtent.bboxFromFile');
|
|
26035
|
+
const LABEL_FROM_FILE_DELETE = marker('search.filters.spatialExtent.bboxFromFileDelete');
|
|
26036
|
+
const LABEL_INITIAL = marker('search.filters.spatialExtent.bboxInitial');
|
|
26037
|
+
const LABEL_INITIAL_DELETE = marker('search.filters.spatialExtent.bboxInitialDelete');
|
|
26038
|
+
class SpatialExtentDropdownComponent {
|
|
26039
|
+
constructor() {
|
|
26040
|
+
this.cd = inject(ChangeDetectorRef);
|
|
26041
|
+
this.scrollStrategies = inject(ScrollStrategyOptions);
|
|
26042
|
+
this.maxFileSizeMb = null;
|
|
26043
|
+
this.bboxChange = new EventEmitter();
|
|
26044
|
+
this.errorChange = new EventEmitter();
|
|
26045
|
+
this.bbox = null;
|
|
26046
|
+
this.fileName = '';
|
|
26047
|
+
this.overlayPositions = [
|
|
26048
|
+
{
|
|
26049
|
+
originX: 'start',
|
|
26050
|
+
originY: 'bottom',
|
|
26051
|
+
overlayX: 'start',
|
|
26052
|
+
overlayY: 'top',
|
|
26053
|
+
offsetY: 8,
|
|
26054
|
+
},
|
|
26055
|
+
{
|
|
26056
|
+
originX: 'start',
|
|
26057
|
+
originY: 'top',
|
|
26058
|
+
overlayX: 'start',
|
|
26059
|
+
overlayY: 'bottom',
|
|
26060
|
+
offsetY: -8,
|
|
26061
|
+
},
|
|
26062
|
+
];
|
|
26063
|
+
this.scrollStrategy = this.scrollStrategies.reposition();
|
|
26064
|
+
this.overlayOpen = false;
|
|
26065
|
+
this.overlayMinWidth = 'none';
|
|
26066
|
+
this.errorKey = null;
|
|
26067
|
+
}
|
|
26068
|
+
set initialBbox(value) {
|
|
26069
|
+
if (!this.bbox && value) {
|
|
26070
|
+
this.bbox = value;
|
|
26071
|
+
}
|
|
26072
|
+
}
|
|
26073
|
+
get hasSelection() {
|
|
26074
|
+
return !!this.bbox;
|
|
26075
|
+
}
|
|
26076
|
+
get selectionLabelKey() {
|
|
26077
|
+
return this.fileName ? LABEL_FROM_FILE : LABEL_INITIAL;
|
|
26078
|
+
}
|
|
26079
|
+
get selectionDeleteLabelKey() {
|
|
26080
|
+
return this.fileName ? LABEL_FROM_FILE_DELETE : LABEL_INITIAL_DELETE;
|
|
26081
|
+
}
|
|
26082
|
+
get selectionLabelParams() {
|
|
26083
|
+
return { fileName: this.fileName };
|
|
26084
|
+
}
|
|
26085
|
+
openOverlay() {
|
|
26086
|
+
this.overlayMinWidth =
|
|
26087
|
+
this.overlayOrigin.elementRef.nativeElement.getBoundingClientRect()
|
|
26088
|
+
.width + 'px';
|
|
26089
|
+
this.overlayOpen = true;
|
|
26090
|
+
}
|
|
26091
|
+
closeOverlay() {
|
|
26092
|
+
this.overlayOpen = false;
|
|
26093
|
+
}
|
|
26094
|
+
toggleOverlay() {
|
|
26095
|
+
if (this.overlayOpen) {
|
|
26096
|
+
this.closeOverlay();
|
|
26097
|
+
}
|
|
26098
|
+
else {
|
|
26099
|
+
this.openOverlay();
|
|
26100
|
+
}
|
|
26101
|
+
}
|
|
26102
|
+
async handleFileSelected(file) {
|
|
26103
|
+
this.errorKey = null;
|
|
26104
|
+
let content;
|
|
26105
|
+
try {
|
|
26106
|
+
content = await readFileAsText(file);
|
|
26107
|
+
const parsed = JSON.parse(content);
|
|
26108
|
+
const geometry = getGeometryFromGeoJSON(parsed);
|
|
26109
|
+
if (!geometry) {
|
|
26110
|
+
this.setError(marker('search.filters.spatialExtent.error.noGeometry'));
|
|
26111
|
+
return;
|
|
26112
|
+
}
|
|
26113
|
+
const bbox = getGeometryBoundingBox(geometry);
|
|
26114
|
+
this.bbox = bbox;
|
|
26115
|
+
this.fileName = file.name;
|
|
26116
|
+
this.bboxChange.emit(bbox);
|
|
26117
|
+
this.cd.markForCheck();
|
|
26118
|
+
}
|
|
26119
|
+
catch {
|
|
26120
|
+
this.setError(marker('search.filters.spatialExtent.error.invalidFormat'));
|
|
26121
|
+
return;
|
|
26122
|
+
}
|
|
26123
|
+
}
|
|
26124
|
+
handleFileError(error) {
|
|
26125
|
+
if (error === 'file-too-large') {
|
|
26126
|
+
this.setError(marker('search.filters.spatialExtent.error.fileTooLarge'), {
|
|
26127
|
+
maxSize: this.maxFileSizeMb,
|
|
26128
|
+
});
|
|
26129
|
+
}
|
|
26130
|
+
else {
|
|
26131
|
+
this.setError(marker('search.filters.spatialExtent.error.invalidFormat'));
|
|
26132
|
+
}
|
|
26133
|
+
}
|
|
26134
|
+
setError(errorKey, params) {
|
|
26135
|
+
this.errorKey = errorKey;
|
|
26136
|
+
this.errorChange.emit({ key: errorKey, params });
|
|
26137
|
+
this.cd.markForCheck();
|
|
26138
|
+
}
|
|
26139
|
+
removeSelection(event) {
|
|
26140
|
+
this.bbox = null;
|
|
26141
|
+
this.fileName = '';
|
|
26142
|
+
this.errorKey = null;
|
|
26143
|
+
this.fileInput?.clear();
|
|
26144
|
+
this.bboxChange.emit(null);
|
|
26145
|
+
propagateToDocumentOnly(event);
|
|
26146
|
+
}
|
|
26147
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
26148
|
+
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: [
|
|
26149
|
+
provideIcons({
|
|
26150
|
+
iconoirCheckCircle,
|
|
26151
|
+
iconoirImport,
|
|
26152
|
+
iconoirSquareDashed,
|
|
26153
|
+
iconoirTrash,
|
|
26154
|
+
matClose,
|
|
26155
|
+
matExpandLess,
|
|
26156
|
+
matExpandMore,
|
|
26157
|
+
}),
|
|
26158
|
+
], 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 }); }
|
|
26159
|
+
}
|
|
26160
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, decorators: [{
|
|
26161
|
+
type: Component,
|
|
26162
|
+
args: [{ selector: 'gn-ui-spatial-extent-dropdown', standalone: true, imports: [
|
|
26163
|
+
ButtonComponent,
|
|
26164
|
+
NgIcon,
|
|
26165
|
+
OverlayModule,
|
|
26166
|
+
TranslatePipe,
|
|
26167
|
+
DragAndDropFileInputComponent,
|
|
26168
|
+
], providers: [
|
|
26169
|
+
provideIcons({
|
|
26170
|
+
iconoirCheckCircle,
|
|
26171
|
+
iconoirImport,
|
|
26172
|
+
iconoirSquareDashed,
|
|
26173
|
+
iconoirTrash,
|
|
26174
|
+
matClose,
|
|
26175
|
+
matExpandLess,
|
|
26176
|
+
matExpandMore,
|
|
26177
|
+
}),
|
|
26178
|
+
], 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" }]
|
|
26179
|
+
}], propDecorators: { title: [{
|
|
26180
|
+
type: Input
|
|
26181
|
+
}], maxFileSizeMb: [{
|
|
26182
|
+
type: Input
|
|
26183
|
+
}], initialBbox: [{
|
|
26184
|
+
type: Input
|
|
26185
|
+
}], bboxChange: [{
|
|
26186
|
+
type: Output
|
|
26187
|
+
}], errorChange: [{
|
|
26188
|
+
type: Output
|
|
26189
|
+
}], overlayOrigin: [{
|
|
26190
|
+
type: ViewChild,
|
|
26191
|
+
args: ['overlayOrigin']
|
|
26192
|
+
}], overlay: [{
|
|
26193
|
+
type: ViewChild,
|
|
26194
|
+
args: [CdkConnectedOverlay]
|
|
26195
|
+
}], fileInput: [{
|
|
26196
|
+
type: ViewChild,
|
|
26197
|
+
args: [DragAndDropFileInputComponent]
|
|
26198
|
+
}] } });
|
|
26199
|
+
|
|
25788
26200
|
class CellPopinComponent {
|
|
25789
26201
|
constructor() {
|
|
25790
26202
|
this.scrollDispatcher = inject(ScrollDispatcher);
|
|
@@ -25861,7 +26273,7 @@ class CellPopinComponent {
|
|
|
25861
26273
|
this.isOpen = false;
|
|
25862
26274
|
}
|
|
25863
26275
|
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$
|
|
26276
|
+
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
26277
|
}
|
|
25866
26278
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CellPopinComponent, decorators: [{
|
|
25867
26279
|
type: Component,
|
|
@@ -27951,6 +28363,34 @@ class DateRangeSearchField extends SimpleSearchField {
|
|
|
27951
28363
|
return 'dateRange';
|
|
27952
28364
|
}
|
|
27953
28365
|
}
|
|
28366
|
+
class ResourceCreationRevisionDateSearchField extends DateRangeSearchField {
|
|
28367
|
+
constructor(injector, order = 'desc') {
|
|
28368
|
+
super('resourceCreationRevisionDate', injector, order);
|
|
28369
|
+
this.esService.registerRuntimeField('resourceCreationRevisionDate', `if (doc.containsKey('creationDateForResource') && doc['creationDateForResource'].size() > 0) {
|
|
28370
|
+
for (def date : doc['creationDateForResource']) { emit(date.millis); }
|
|
28371
|
+
}
|
|
28372
|
+
if (doc.containsKey('revisionDateForResource') && doc['revisionDateForResource'].size() > 0) {
|
|
28373
|
+
for (def date : doc['revisionDateForResource']) { emit(date.millis); }
|
|
28374
|
+
}`, 'date');
|
|
28375
|
+
}
|
|
28376
|
+
}
|
|
28377
|
+
class BoundingBoxSearchField extends SimpleSearchField {
|
|
28378
|
+
getAvailableValues() {
|
|
28379
|
+
return of([]);
|
|
28380
|
+
}
|
|
28381
|
+
getFiltersForValues(values) {
|
|
28382
|
+
return of({
|
|
28383
|
+
[this.esFieldName]: values.map(Number),
|
|
28384
|
+
});
|
|
28385
|
+
}
|
|
28386
|
+
getValuesForFilter(filters) {
|
|
28387
|
+
const filter = filters[this.esFieldName];
|
|
28388
|
+
return of(isBoundingBox(filter) ? filter : []);
|
|
28389
|
+
}
|
|
28390
|
+
getType() {
|
|
28391
|
+
return 'spatialExtent';
|
|
28392
|
+
}
|
|
28393
|
+
}
|
|
27954
28394
|
marker('search.filters.availableServices.view');
|
|
27955
28395
|
marker('search.filters.availableServices.download');
|
|
27956
28396
|
class AvailableServicesField extends SimpleSearchField {
|
|
@@ -28095,6 +28535,8 @@ marker('search.filters.producerOrg');
|
|
|
28095
28535
|
marker('search.filters.publisherOrg');
|
|
28096
28536
|
marker('search.filters.user');
|
|
28097
28537
|
marker('search.filters.changeDate');
|
|
28538
|
+
marker('search.filters.resourceCreationRevisionDate');
|
|
28539
|
+
marker('search.filters.spatialExtent');
|
|
28098
28540
|
class FieldsService {
|
|
28099
28541
|
constructor() {
|
|
28100
28542
|
this.injector = inject(Injector);
|
|
@@ -28117,7 +28559,9 @@ class FieldsService {
|
|
|
28117
28559
|
publisherOrg: new MultilingualSearchField('distributorOrgForResourceObject', this.injector, 'asc', 'key'),
|
|
28118
28560
|
user: new UserSearchField(this.injector),
|
|
28119
28561
|
changeDate: new DateRangeSearchField('changeDate', this.injector, 'desc'),
|
|
28562
|
+
resourceCreationRevisionDate: new ResourceCreationRevisionDateSearchField(this.injector, 'desc'),
|
|
28120
28563
|
availableServices: new AvailableServicesField(this.injector),
|
|
28564
|
+
spatialExtent: new BoundingBoxSearchField('spatialExtent', this.injector),
|
|
28121
28565
|
};
|
|
28122
28566
|
}
|
|
28123
28567
|
get supportedFields() {
|
|
@@ -29380,7 +29824,7 @@ class ContactPillComponent {
|
|
|
29380
29824
|
this.overlayOpen = false;
|
|
29381
29825
|
}
|
|
29382
29826
|
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$
|
|
29827
|
+
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
29828
|
}
|
|
29385
29829
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ContactPillComponent, decorators: [{
|
|
29386
29830
|
type: Component,
|
|
@@ -31302,19 +31746,118 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31302
31746
|
type: Output
|
|
31303
31747
|
}] } });
|
|
31304
31748
|
|
|
31749
|
+
class NotificationsService {
|
|
31750
|
+
constructor() {
|
|
31751
|
+
this.notifications$ = new BehaviorSubject([]);
|
|
31752
|
+
}
|
|
31753
|
+
showNotification(content, timeoutMs, error) {
|
|
31754
|
+
error && console.error(error);
|
|
31755
|
+
const id = Math.floor(Math.random() * 1000000);
|
|
31756
|
+
this.notifications$.next([...this.notifications$.value, { ...content, id }]);
|
|
31757
|
+
if (typeof timeoutMs !== 'undefined') {
|
|
31758
|
+
setTimeout(() => {
|
|
31759
|
+
this.removeNotificationById(id);
|
|
31760
|
+
}, timeoutMs);
|
|
31761
|
+
}
|
|
31762
|
+
return id;
|
|
31763
|
+
}
|
|
31764
|
+
removeNotificationById(id) {
|
|
31765
|
+
this.notifications$.next(this.notifications$.value.filter((n) => n.id !== id));
|
|
31766
|
+
}
|
|
31767
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
31768
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, providedIn: 'root' }); }
|
|
31769
|
+
}
|
|
31770
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, decorators: [{
|
|
31771
|
+
type: Injectable,
|
|
31772
|
+
args: [{
|
|
31773
|
+
providedIn: 'root',
|
|
31774
|
+
}]
|
|
31775
|
+
}] });
|
|
31776
|
+
|
|
31777
|
+
class NotificationsContainerComponent {
|
|
31778
|
+
constructor() {
|
|
31779
|
+
this.notificationsService = inject(NotificationsService);
|
|
31780
|
+
}
|
|
31781
|
+
trackById(index, notification) {
|
|
31782
|
+
return notification.id;
|
|
31783
|
+
}
|
|
31784
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31785
|
+
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: [
|
|
31786
|
+
trigger('enterExit', [
|
|
31787
|
+
transition(':enter', [
|
|
31788
|
+
animate('150ms', keyframes([
|
|
31789
|
+
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31790
|
+
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31791
|
+
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31792
|
+
])),
|
|
31793
|
+
]),
|
|
31794
|
+
transition(':leave', [
|
|
31795
|
+
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31796
|
+
]),
|
|
31797
|
+
]),
|
|
31798
|
+
], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31799
|
+
}
|
|
31800
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, decorators: [{
|
|
31801
|
+
type: Component,
|
|
31802
|
+
args: [{ selector: 'gn-ui-notifications-container', standalone: true, imports: [CommonModule, NotificationComponent], changeDetection: ChangeDetectionStrategy.OnPush, animations: [
|
|
31803
|
+
trigger('enterExit', [
|
|
31804
|
+
transition(':enter', [
|
|
31805
|
+
animate('150ms', keyframes([
|
|
31806
|
+
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31807
|
+
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31808
|
+
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31809
|
+
])),
|
|
31810
|
+
]),
|
|
31811
|
+
transition(':leave', [
|
|
31812
|
+
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31813
|
+
]),
|
|
31814
|
+
]),
|
|
31815
|
+
], 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" }]
|
|
31816
|
+
}] });
|
|
31817
|
+
|
|
31305
31818
|
class FilterDropdownComponent {
|
|
31306
31819
|
constructor() {
|
|
31307
31820
|
this.searchFacade = inject(SearchFacade);
|
|
31308
31821
|
this.searchService = inject(SearchService);
|
|
31309
31822
|
this.fieldsService = inject(FieldsService);
|
|
31823
|
+
this.notificationsService = inject(NotificationsService);
|
|
31824
|
+
this.translateService = inject(TranslateService);
|
|
31825
|
+
this.spatialExtentMaxFileSize = getOptionalSearchConfig()?.SPATIAL_EXTENT_MAX_FILE_SIZE;
|
|
31310
31826
|
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((
|
|
31827
|
+
this.selectedDateRange$ = this.selected$.pipe(map$1((selected) => (Array.isArray(selected) ? {} : selected)));
|
|
31828
|
+
this.selectedBoundingBox$ = this.selected$.pipe(map$1((selected) => Array.isArray(selected) && selected.length > 0
|
|
31829
|
+
? selected
|
|
31830
|
+
: null));
|
|
31831
|
+
this.spatialExtentErrorNotificationId = null;
|
|
31312
31832
|
}
|
|
31313
31833
|
onSelectedValues(values) {
|
|
31314
31834
|
this.fieldsService
|
|
31315
31835
|
.buildFiltersFromFieldValues({ [this.fieldName]: values })
|
|
31316
31836
|
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
31317
31837
|
}
|
|
31838
|
+
onBboxChange(bbox) {
|
|
31839
|
+
this.fieldsService
|
|
31840
|
+
.buildFiltersFromFieldValues({
|
|
31841
|
+
[this.fieldName]: bbox,
|
|
31842
|
+
})
|
|
31843
|
+
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
31844
|
+
this.clearSpatialExtentErrorNotification();
|
|
31845
|
+
}
|
|
31846
|
+
onSpatialExtentError(error) {
|
|
31847
|
+
this.clearSpatialExtentErrorNotification();
|
|
31848
|
+
this.spatialExtentErrorNotificationId =
|
|
31849
|
+
this.notificationsService.showNotification({
|
|
31850
|
+
type: 'error',
|
|
31851
|
+
title: this.translateService.instant('search.filters.spatialExtent.error.title'),
|
|
31852
|
+
text: this.translateService.instant(error.key, error.params),
|
|
31853
|
+
});
|
|
31854
|
+
}
|
|
31855
|
+
clearSpatialExtentErrorNotification() {
|
|
31856
|
+
if (this.spatialExtentErrorNotificationId === null)
|
|
31857
|
+
return;
|
|
31858
|
+
this.notificationsService.removeNotificationById(this.spatialExtentErrorNotificationId);
|
|
31859
|
+
this.spatialExtentErrorNotificationId = null;
|
|
31860
|
+
}
|
|
31318
31861
|
ngOnInit() {
|
|
31319
31862
|
this.fieldType = this.fieldsService.getFieldType(this.fieldName);
|
|
31320
31863
|
this.choices$ = this.fieldsService.getAvailableValues(this.fieldName).pipe(startWith$1([]), map$1((values) => values.map((v) => ({
|
|
@@ -31322,27 +31865,15 @@ class FilterDropdownComponent {
|
|
|
31322
31865
|
value: v.value.toString(), // converting to string for the dropdown
|
|
31323
31866
|
}))), catchError(() => of([])));
|
|
31324
31867
|
}
|
|
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
|
-
}
|
|
31868
|
+
onDateRangeChange(dateRange) {
|
|
31869
|
+
this.fieldsService
|
|
31870
|
+
.buildFiltersFromFieldValues({
|
|
31871
|
+
[this.fieldName]: dateRange,
|
|
31872
|
+
})
|
|
31873
|
+
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
31343
31874
|
}
|
|
31344
31875
|
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 [
|
|
31876
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FilterDropdownComponent, isStandalone: true, selector: "gn-ui-filter-dropdown", inputs: { fieldName: "fieldName", title: "title" }, ngImport: i0, template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [dateRange]=\"(selectedDateRange$ | async) ?? {}\"\n (dateRangeChange)=\"onDateRangeChange($event)\"\n [attr.data-cy-field]=\"fieldName\"\n ></gn-ui-date-range-dropdown>\n} @else if (fieldType === 'spatialExtent') {\n <gn-ui-spatial-extent-dropdown\n [title]=\"title\"\n [initialBbox]=\"(selectedBoundingBox$ | async) ?? null\"\n [maxFileSizeMb]=\"spatialExtentMaxFileSize\"\n (bboxChange)=\"onBboxChange($event)\"\n (errorChange)=\"onSpatialExtentError($event)\"\n [attr.data-cy-field]=\"fieldName\"\n ></gn-ui-spatial-extent-dropdown>\n} @else {\n <gn-ui-dropdown-multiselect\n class=\"w-full\"\n [title]=\"title\"\n [maxRows]=\"6\"\n [choices]=\"(choices$ | async) || []\"\n [selected]=\"selected$ | async\"\n [allowSearch]=\"true\"\n (selectValues)=\"onSelectedValues($event)\"\n [attr.data-cy-field]=\"fieldName\"\n >\n </gn-ui-dropdown-multiselect>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DateRangeDropdownComponent, selector: "gn-ui-date-range-dropdown", inputs: ["title", "dateRange"], outputs: ["dateRangeChange"] }, { kind: "component", type: DropdownMultiselectComponent, selector: "gn-ui-dropdown-multiselect", inputs: ["title", "choices", "selected", "allowSearch", "maxRows", "searchInputValue"], outputs: ["selectValues"] }, { kind: "component", type: SpatialExtentDropdownComponent, selector: "gn-ui-spatial-extent-dropdown", inputs: ["title", "maxFileSizeMb", "initialBbox"], outputs: ["bboxChange", "errorChange"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31346
31877
|
}
|
|
31347
31878
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, decorators: [{
|
|
31348
31879
|
type: Component,
|
|
@@ -31350,7 +31881,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31350
31881
|
CommonModule,
|
|
31351
31882
|
DateRangeDropdownComponent,
|
|
31352
31883
|
DropdownMultiselectComponent,
|
|
31353
|
-
|
|
31884
|
+
SpatialExtentDropdownComponent,
|
|
31885
|
+
], template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [dateRange]=\"(selectedDateRange$ | async) ?? {}\"\n (dateRangeChange)=\"onDateRangeChange($event)\"\n [attr.data-cy-field]=\"fieldName\"\n ></gn-ui-date-range-dropdown>\n} @else if (fieldType === 'spatialExtent') {\n <gn-ui-spatial-extent-dropdown\n [title]=\"title\"\n [initialBbox]=\"(selectedBoundingBox$ | async) ?? null\"\n [maxFileSizeMb]=\"spatialExtentMaxFileSize\"\n (bboxChange)=\"onBboxChange($event)\"\n (errorChange)=\"onSpatialExtentError($event)\"\n [attr.data-cy-field]=\"fieldName\"\n ></gn-ui-spatial-extent-dropdown>\n} @else {\n <gn-ui-dropdown-multiselect\n class=\"w-full\"\n [title]=\"title\"\n [maxRows]=\"6\"\n [choices]=\"(choices$ | async) || []\"\n [selected]=\"selected$ | async\"\n [allowSearch]=\"true\"\n (selectValues)=\"onSelectedValues($event)\"\n [attr.data-cy-field]=\"fieldName\"\n >\n </gn-ui-dropdown-multiselect>\n}\n" }]
|
|
31354
31886
|
}], propDecorators: { fieldName: [{
|
|
31355
31887
|
type: Input
|
|
31356
31888
|
}], title: [{
|
|
@@ -31886,74 +32418,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31886
32418
|
args: ['gnUiSearchStateContainer']
|
|
31887
32419
|
}] } });
|
|
31888
32420
|
|
|
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
32421
|
class ResultsTableContainerComponent {
|
|
31958
32422
|
constructor() {
|
|
31959
32423
|
this.searchFacade = inject(SearchFacade);
|
|
@@ -32038,12 +32502,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
32038
32502
|
|
|
32039
32503
|
marker('search.filters.summaryLabel.user');
|
|
32040
32504
|
marker('search.filters.summaryLabel.changeDate');
|
|
32505
|
+
marker('search.filters.summaryLabel.resourceCreationRevisionDate');
|
|
32506
|
+
const OPEN_BOUND = '…';
|
|
32041
32507
|
class SearchFiltersSummaryItemComponent {
|
|
32042
32508
|
constructor() {
|
|
32043
32509
|
this.searchFacade = inject(SearchFacade);
|
|
32044
32510
|
this.searchService = inject(SearchService);
|
|
32045
32511
|
this.fieldsService = inject(FieldsService);
|
|
32046
|
-
this.
|
|
32512
|
+
this.dateService = inject(DateService);
|
|
32047
32513
|
this.translate = inject(TranslateService);
|
|
32048
32514
|
this.fieldValues$ = this.searchFacade.searchFilters$.pipe(switchMap((filters) => this.fieldsService.readFieldValuesFromFilters(filters)), map$2((fieldValues) => Array.isArray(fieldValues[this.fieldName])
|
|
32049
32515
|
? fieldValues[this.fieldName]
|
|
@@ -32070,9 +32536,10 @@ class SearchFiltersSummaryItemComponent {
|
|
|
32070
32536
|
getReadableValues(fieldValues) {
|
|
32071
32537
|
return fieldValues.map((value) => {
|
|
32072
32538
|
if (this.fieldType === 'dateRange') {
|
|
32539
|
+
const { start, end } = value;
|
|
32073
32540
|
return {
|
|
32074
32541
|
value,
|
|
32075
|
-
label: `${this.
|
|
32542
|
+
label: `${this.formatBound(start)} - ${this.formatBound(end)}`,
|
|
32076
32543
|
};
|
|
32077
32544
|
}
|
|
32078
32545
|
else if (this.fieldName === 'user') {
|
|
@@ -32083,6 +32550,15 @@ class SearchFiltersSummaryItemComponent {
|
|
|
32083
32550
|
}
|
|
32084
32551
|
});
|
|
32085
32552
|
}
|
|
32553
|
+
formatBound(date) {
|
|
32554
|
+
return date
|
|
32555
|
+
? this.dateService.formatDate(date, {
|
|
32556
|
+
day: '2-digit',
|
|
32557
|
+
month: '2-digit',
|
|
32558
|
+
year: 'numeric',
|
|
32559
|
+
})
|
|
32560
|
+
: OPEN_BOUND;
|
|
32561
|
+
}
|
|
32086
32562
|
async removeFilterValue(fieldValue) {
|
|
32087
32563
|
const currentFieldValues = await firstValueFrom(this.fieldValues$);
|
|
32088
32564
|
const updatedFieldValues = currentFieldValues
|
|
@@ -32095,11 +32571,11 @@ class SearchFiltersSummaryItemComponent {
|
|
|
32095
32571
|
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
32096
32572
|
}
|
|
32097
32573
|
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" },
|
|
32574
|
+
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
32575
|
}
|
|
32100
32576
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SearchFiltersSummaryItemComponent, decorators: [{
|
|
32101
32577
|
type: Component,
|
|
32102
|
-
args: [{ selector: 'gn-ui-search-filters-summary-item', standalone: true, imports: [CommonModule, BadgeComponent],
|
|
32578
|
+
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
32579
|
}], propDecorators: { fieldName: [{
|
|
32104
32580
|
type: Input
|
|
32105
32581
|
}] } });
|
|
@@ -32343,11 +32819,11 @@ class AddLayerFromFileComponent {
|
|
|
32343
32819
|
}, 5000);
|
|
32344
32820
|
}
|
|
32345
32821
|
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" }] }); }
|
|
32822
|
+
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
32823
|
}
|
|
32348
32824
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AddLayerFromFileComponent, decorators: [{
|
|
32349
32825
|
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" }]
|
|
32826
|
+
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
32827
|
}] });
|
|
32352
32828
|
|
|
32353
32829
|
class LayersPanelComponent {
|
|
@@ -32512,8 +32988,7 @@ const closeMetadata = createAction('[Metadata view] close');
|
|
|
32512
32988
|
Related actions
|
|
32513
32989
|
*/
|
|
32514
32990
|
const setRelated = createAction('[Metadata view] Set related records', props());
|
|
32515
|
-
const
|
|
32516
|
-
const setSourceOf = createAction('[Metadata view] Set has sources', props());
|
|
32991
|
+
const setLinkedRecords = createAction('[Metadata view] Set associated records', props());
|
|
32517
32992
|
/*
|
|
32518
32993
|
ChartConfig actions
|
|
32519
32994
|
*/
|
|
@@ -32545,9 +33020,8 @@ var mdview_actions = /*#__PURE__*/Object.freeze({
|
|
|
32545
33020
|
loadUserFeedbacksSuccess: loadUserFeedbacksSuccess,
|
|
32546
33021
|
setChartConfig: setChartConfig,
|
|
32547
33022
|
setIncompleteMetadata: setIncompleteMetadata,
|
|
32548
|
-
|
|
32549
|
-
|
|
32550
|
-
setSources: setSources
|
|
33023
|
+
setLinkedRecords: setLinkedRecords,
|
|
33024
|
+
setRelated: setRelated
|
|
32551
33025
|
});
|
|
32552
33026
|
|
|
32553
33027
|
const METADATA_VIEW_FEATURE_STATE_KEY = 'metadataView';
|
|
@@ -32590,12 +33064,9 @@ on(loadFullMetadata, (state) => ({
|
|
|
32590
33064
|
on(setRelated, (state, { related }) => ({
|
|
32591
33065
|
...state,
|
|
32592
33066
|
related,
|
|
32593
|
-
})), on(
|
|
32594
|
-
...state,
|
|
32595
|
-
sources,
|
|
32596
|
-
})), on(setSourceOf, (state, { sourceOf }) => ({
|
|
33067
|
+
})), on(setLinkedRecords, (state, { linkedRecords }) => ({
|
|
32597
33068
|
...state,
|
|
32598
|
-
|
|
33069
|
+
linkedRecords,
|
|
32599
33070
|
})),
|
|
32600
33071
|
/*
|
|
32601
33072
|
ChartConfig reducers
|
|
@@ -32657,8 +33128,7 @@ const getMetadataError = createSelector(getMdViewState, (state) => state.error);
|
|
|
32657
33128
|
Related selectors
|
|
32658
33129
|
*/
|
|
32659
33130
|
const getRelated = createSelector(getMdViewState, (state) => state.related);
|
|
32660
|
-
const
|
|
32661
|
-
const getSourceOf = createSelector(getMdViewState, (state) => state.sourceOf);
|
|
33131
|
+
const getLinkedRecords = createSelector(getMdViewState, (state) => state.linkedRecords);
|
|
32662
33132
|
/*
|
|
32663
33133
|
Metadata selectors
|
|
32664
33134
|
*/
|
|
@@ -32759,212 +33229,10 @@ function parseHeaders(httpHeaders) {
|
|
|
32759
33229
|
return result;
|
|
32760
33230
|
}
|
|
32761
33231
|
|
|
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
|
-
});
|
|
32825
|
-
}
|
|
32826
|
-
function tryParseDate(input) {
|
|
32827
|
-
if (typeof input !== 'string')
|
|
32828
|
-
return null;
|
|
32829
|
-
function tryIso(value) {
|
|
32830
|
-
const parsed = parseISO(value);
|
|
32831
|
-
return isNaN(parsed.getDate()) ? null : parsed;
|
|
32832
|
-
}
|
|
32833
|
-
function tryFormat(value, format) {
|
|
32834
|
-
const parsed = parse$4(value, format, new Date());
|
|
32835
|
-
return isNaN(parsed.getDate()) ? null : parsed;
|
|
32836
|
-
}
|
|
32837
|
-
return (tryIso(input) ||
|
|
32838
|
-
tryFormat(input, 'dd/MM/yyyy') ||
|
|
32839
|
-
tryFormat(input, 'dd.MM.yyyy') ||
|
|
32840
|
-
tryFormat(input, 'MM/dd/yyyy') ||
|
|
32841
|
-
null);
|
|
32842
|
-
}
|
|
32843
|
-
function tryParseNumber(input) {
|
|
32844
|
-
if (isNaN(input))
|
|
32845
|
-
return null;
|
|
32846
|
-
const parsed = parseFloat(input);
|
|
32847
|
-
return isNaN(parsed) ? null : parsed;
|
|
32848
|
-
}
|
|
32849
|
-
function jsonToGeojsonFeature(object) {
|
|
32850
|
-
const { id, properties } = Object.keys(object)
|
|
32851
|
-
.map((property) => (property ? property : 'unknown')) //prevent empty strings
|
|
32852
|
-
.reduce((prev, curr) => curr.toLowerCase().endsWith('id')
|
|
32853
|
-
? {
|
|
32854
|
-
...prev,
|
|
32855
|
-
id: object[curr],
|
|
32856
|
-
}
|
|
32857
|
-
: {
|
|
32858
|
-
...prev,
|
|
32859
|
-
properties: { ...prev.properties, [curr]: object[curr] },
|
|
32860
|
-
}, { id: undefined, properties: {} });
|
|
32861
|
-
return {
|
|
32862
|
-
type: 'Feature',
|
|
32863
|
-
geometry: null,
|
|
32864
|
-
properties,
|
|
32865
|
-
...(id !== undefined && { id }),
|
|
32866
|
-
};
|
|
32867
|
-
}
|
|
32868
|
-
function mutateProperties(items, mutators) {
|
|
32869
|
-
const mutatorKeys = Object.keys(mutators);
|
|
32870
|
-
for (let i = 0, ii = items.length; i < ii; i++) {
|
|
32871
|
-
const item = items[i];
|
|
32872
|
-
for (const mutatorField of mutatorKeys) {
|
|
32873
|
-
if (!(mutatorField in item.properties))
|
|
32874
|
-
continue;
|
|
32875
|
-
item.properties[mutatorField] = mutators[mutatorField](item.properties[mutatorField]);
|
|
32876
|
-
}
|
|
32877
|
-
}
|
|
32878
|
-
return items;
|
|
32879
|
-
}
|
|
32880
|
-
const SAMPLE_SIZE = 20;
|
|
32881
|
-
/**
|
|
32882
|
-
* This will infer field types from a list of data items and cast the values accordingly
|
|
32883
|
-
* @param items
|
|
32884
|
-
* @param inferTypes
|
|
32885
|
-
*/
|
|
32886
|
-
function processItemProperties(items, inferTypes = false) {
|
|
32887
|
-
const foundFields = {};
|
|
32888
|
-
for (let i = 0, ii = Math.min(SAMPLE_SIZE, items.length); i < ii; i++) {
|
|
32889
|
-
const item = items[i];
|
|
32890
|
-
const fields = Object.keys(item.properties);
|
|
32891
|
-
for (const field of fields) {
|
|
32892
|
-
if (!(field in foundFields)) {
|
|
32893
|
-
foundFields[field] = {
|
|
32894
|
-
label: field,
|
|
32895
|
-
name: field,
|
|
32896
|
-
type: null,
|
|
32897
|
-
};
|
|
32898
|
-
}
|
|
32899
|
-
const value = item.properties[field];
|
|
32900
|
-
const info = foundFields[field];
|
|
32901
|
-
if (value === undefined || value === '' || value === null)
|
|
32902
|
-
continue;
|
|
32903
|
-
if (!inferTypes) {
|
|
32904
|
-
if (info.type === null && typeof value === 'number') {
|
|
32905
|
-
info.type = 'number';
|
|
32906
|
-
}
|
|
32907
|
-
else if (info.type === 'number' && typeof value !== 'number') {
|
|
32908
|
-
info.type = 'string';
|
|
32909
|
-
}
|
|
32910
|
-
continue;
|
|
32911
|
-
}
|
|
32912
|
-
const parsedNumber = tryParseNumber(value);
|
|
32913
|
-
if (info.type === null && parsedNumber !== null) {
|
|
32914
|
-
info.type = 'number';
|
|
32915
|
-
continue;
|
|
32916
|
-
}
|
|
32917
|
-
else if (info.type === 'number' && parsedNumber === null) {
|
|
32918
|
-
info.type = 'string';
|
|
32919
|
-
continue;
|
|
32920
|
-
}
|
|
32921
|
-
const parsedDate = tryParseDate(value);
|
|
32922
|
-
if (info.type === null && parsedDate !== null) {
|
|
32923
|
-
info.type = 'date';
|
|
32924
|
-
}
|
|
32925
|
-
else if (info.type === 'date' && parsedDate === null) {
|
|
32926
|
-
info.type = 'string';
|
|
32927
|
-
}
|
|
32928
|
-
}
|
|
32929
|
-
}
|
|
32930
|
-
const properties = [];
|
|
32931
|
-
const mutators = {};
|
|
32932
|
-
for (const field in foundFields) {
|
|
32933
|
-
const info = foundFields[field];
|
|
32934
|
-
if (info.type === 'number') {
|
|
32935
|
-
mutators[field] = tryParseNumber;
|
|
32936
|
-
}
|
|
32937
|
-
else if (info.type === 'date') {
|
|
32938
|
-
mutators[field] = tryParseDate;
|
|
32939
|
-
}
|
|
32940
|
-
properties.push({ ...info, type: info.type || 'string' });
|
|
32941
|
-
}
|
|
32942
|
-
if (inferTypes) {
|
|
32943
|
-
mutateProperties(items, mutators);
|
|
32944
|
-
}
|
|
32945
|
-
return { items, properties };
|
|
32946
|
-
}
|
|
32947
|
-
/**
|
|
32948
|
-
* This creates a Proxy that allows reading and writing to the data item properties
|
|
32949
|
-
* as if it was a simple array of JSON objects
|
|
32950
|
-
* @param items
|
|
32951
|
-
*/
|
|
32952
|
-
function getJsonDataItemsProxy(items) {
|
|
32953
|
-
return new Proxy(items, {
|
|
32954
|
-
get(target, p) {
|
|
32955
|
-
if (typeof p === 'string' &&
|
|
32956
|
-
!Number.isNaN(parseInt(p)) &&
|
|
32957
|
-
target[p]?.properties) {
|
|
32958
|
-
return target[p].properties;
|
|
32959
|
-
}
|
|
32960
|
-
return target[p];
|
|
32961
|
-
},
|
|
32962
|
-
set() {
|
|
32963
|
-
throw new Error('This object is read-only');
|
|
32964
|
-
},
|
|
32965
|
-
});
|
|
33232
|
+
const GEOMETRY_COLUMN_ALIAS = '__geometry__';
|
|
33233
|
+
function fieldToSql(name) {
|
|
33234
|
+
return `"${name.replace(/"/g, '""')}"`; // escape double quotes in field names
|
|
32966
33235
|
}
|
|
32967
|
-
|
|
32968
33236
|
function filterToSql(filter) {
|
|
32969
33237
|
const operator = filter[0];
|
|
32970
33238
|
const args = filter.slice(1);
|
|
@@ -32979,10 +33247,10 @@ function filterToSql(filter) {
|
|
|
32979
33247
|
case '=':
|
|
32980
33248
|
case '!=':
|
|
32981
33249
|
case 'like':
|
|
32982
|
-
return
|
|
33250
|
+
return `${fieldToSql(args[0])} ${operator.toUpperCase()} ${valueToSql(args[1])}`;
|
|
32983
33251
|
case 'in': {
|
|
32984
33252
|
const values = args.slice(1);
|
|
32985
|
-
return
|
|
33253
|
+
return `${fieldToSql(args[0])} IN (${values.map(valueToSql).join(', ')})`;
|
|
32986
33254
|
}
|
|
32987
33255
|
case 'and':
|
|
32988
33256
|
case 'or': {
|
|
@@ -33001,17 +33269,18 @@ function aggregationToSql(aggregation) {
|
|
|
33001
33269
|
const field = aggregation[1];
|
|
33002
33270
|
switch (operation) {
|
|
33003
33271
|
case 'average':
|
|
33004
|
-
return `AVG(
|
|
33272
|
+
return `CAST(AVG(${fieldToSql(field)}) AS DOUBLE) as ${fieldToSql(`average(${field})`)}`;
|
|
33005
33273
|
case 'sum':
|
|
33006
33274
|
case 'max':
|
|
33007
33275
|
case 'min':
|
|
33008
|
-
return
|
|
33276
|
+
return `CAST(${operation.toUpperCase()}(${fieldToSql(field)}) AS DOUBLE) as ${fieldToSql(`${operation}(${field})`)}`;
|
|
33009
33277
|
case 'count':
|
|
33010
|
-
return 'COUNT(*) as
|
|
33278
|
+
return 'CAST(COUNT(*) AS INTEGER) as "count()"'; // we don't need Bigint precision here
|
|
33011
33279
|
}
|
|
33012
33280
|
}
|
|
33013
33281
|
/**
|
|
33014
33282
|
* Leave arguments at null if not used
|
|
33283
|
+
* @param tableName
|
|
33015
33284
|
* @param selected
|
|
33016
33285
|
* @param filter
|
|
33017
33286
|
* @param sort
|
|
@@ -33020,22 +33289,25 @@ function aggregationToSql(aggregation) {
|
|
|
33020
33289
|
* @param groupBy
|
|
33021
33290
|
* @param aggregations
|
|
33022
33291
|
*/
|
|
33023
|
-
function generateSqlQuery(selected = null, filter = null, sort = null, startIndex = null, count = null, groupBy = null, aggregations = null) {
|
|
33292
|
+
function generateSqlQuery(tableName, selected = null, filter = null, sort = null, startIndex = null, count = null, groupBy = null, aggregations = null, geometryColumn = null) {
|
|
33024
33293
|
let sqlSelect = 'SELECT *';
|
|
33025
|
-
const sqlFrom =
|
|
33294
|
+
const sqlFrom = ` FROM ${tableName}`;
|
|
33026
33295
|
let sqlOrderBy = '';
|
|
33027
33296
|
let sqlWhere = '';
|
|
33028
33297
|
let sqlLimit = '';
|
|
33029
33298
|
let sqlGroupBy = '';
|
|
33030
33299
|
if (selected !== null) {
|
|
33031
|
-
sqlSelect = `SELECT ${selected.map(
|
|
33300
|
+
sqlSelect = `SELECT ${selected.map(fieldToSql).join(', ')}`;
|
|
33301
|
+
}
|
|
33302
|
+
if (geometryColumn !== null) {
|
|
33303
|
+
sqlSelect += `, ST_AsGeoJSON(${fieldToSql(geometryColumn)}) as ${GEOMETRY_COLUMN_ALIAS}`;
|
|
33032
33304
|
}
|
|
33033
33305
|
if (filter !== null) {
|
|
33034
33306
|
sqlWhere = ` WHERE ${filterToSql(filter)}`;
|
|
33035
33307
|
}
|
|
33036
33308
|
if (sort?.length) {
|
|
33037
33309
|
sqlOrderBy = ` ORDER BY ${sort
|
|
33038
|
-
.map((sort) =>
|
|
33310
|
+
.map((sort) => `${fieldToSql(sort[1])} ${sort[0].toUpperCase()}`)
|
|
33039
33311
|
.join(', ')}`;
|
|
33040
33312
|
}
|
|
33041
33313
|
if (startIndex !== null && count !== null) {
|
|
@@ -33045,17 +33317,212 @@ function generateSqlQuery(selected = null, filter = null, sort = null, startInde
|
|
|
33045
33317
|
sqlSelect = `SELECT ${aggregations.map(aggregationToSql).join(', ')}`;
|
|
33046
33318
|
const groupedByDistinct = groupBy.filter((group) => group[0] === 'distinct');
|
|
33047
33319
|
const sqlGroupByFields = groupedByDistinct
|
|
33048
|
-
.map((group) =>
|
|
33320
|
+
.map((group) => fieldToSql(group[1]))
|
|
33049
33321
|
.join(', ');
|
|
33050
33322
|
const sqlGroupBySelect = groupedByDistinct
|
|
33051
|
-
.map((group) =>
|
|
33323
|
+
.map((group) => `${fieldToSql(group[1])} as ${fieldToSql(`distinct(${group[1]})`)}`)
|
|
33052
33324
|
.join(', ');
|
|
33053
33325
|
if (sqlGroupByFields && sqlGroupBySelect) {
|
|
33054
33326
|
sqlGroupBy = ` GROUP BY ${sqlGroupByFields}`;
|
|
33055
33327
|
sqlSelect += `, ${sqlGroupBySelect}`;
|
|
33056
33328
|
}
|
|
33057
33329
|
}
|
|
33058
|
-
return sqlSelect + sqlFrom + sqlGroupBy +
|
|
33330
|
+
return sqlSelect + sqlFrom + sqlGroupBy + sqlWhere + sqlOrderBy + sqlLimit;
|
|
33331
|
+
}
|
|
33332
|
+
|
|
33333
|
+
function arrowTableToDataItems(table) {
|
|
33334
|
+
const fields = table.schema.fields;
|
|
33335
|
+
return table.toArray().map((row) => {
|
|
33336
|
+
const rowJson = row.toJSON();
|
|
33337
|
+
const feature = {
|
|
33338
|
+
type: 'Feature',
|
|
33339
|
+
geometry: null,
|
|
33340
|
+
properties: {},
|
|
33341
|
+
};
|
|
33342
|
+
const keys = Object.keys(rowJson);
|
|
33343
|
+
for (let i = 0; i < keys.length; i++) {
|
|
33344
|
+
const key = keys[i];
|
|
33345
|
+
const dataType = fields[i].type;
|
|
33346
|
+
let value = rowJson[key];
|
|
33347
|
+
// this might happen if we get an array inside a field
|
|
33348
|
+
if (value instanceof Vector) {
|
|
33349
|
+
value = Array.from(value);
|
|
33350
|
+
}
|
|
33351
|
+
// cast bigints to ints
|
|
33352
|
+
if (typeof value === 'bigint') {
|
|
33353
|
+
value = Number(value);
|
|
33354
|
+
}
|
|
33355
|
+
// rename columns with empty name
|
|
33356
|
+
if (!key) {
|
|
33357
|
+
feature.properties['unknown'] = value;
|
|
33358
|
+
continue;
|
|
33359
|
+
}
|
|
33360
|
+
// assign properties that look like an id to the geojson `id` field
|
|
33361
|
+
if (/^(object|feature)?_?id$/.test(key.toLowerCase()) &&
|
|
33362
|
+
(typeof value == 'string' || typeof value === 'number')) {
|
|
33363
|
+
feature.id = value;
|
|
33364
|
+
}
|
|
33365
|
+
// if a date is in timestamp (number) format, convert it to native
|
|
33366
|
+
if (typeof value === 'number' &&
|
|
33367
|
+
(DataType.isTimestamp(dataType) || DataType.isDate(dataType))) {
|
|
33368
|
+
value = new Date(value);
|
|
33369
|
+
}
|
|
33370
|
+
// if a binary field (most likely a geometry column): skip
|
|
33371
|
+
if (DataType.isBinary(dataType)) {
|
|
33372
|
+
continue;
|
|
33373
|
+
}
|
|
33374
|
+
// geometry column
|
|
33375
|
+
if (key === GEOMETRY_COLUMN_ALIAS && DataType.isUtf8(dataType)) {
|
|
33376
|
+
feature.geometry = JSON.parse(value);
|
|
33377
|
+
continue;
|
|
33378
|
+
}
|
|
33379
|
+
feature.properties[key] = value;
|
|
33380
|
+
}
|
|
33381
|
+
return feature;
|
|
33382
|
+
});
|
|
33383
|
+
}
|
|
33384
|
+
|
|
33385
|
+
// init code taken from https://github.com/duckdb/duckdb-wasm/blob/main/packages/duckdb-wasm/README.md
|
|
33386
|
+
const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();
|
|
33387
|
+
// from https://duckdb.org/docs/current/sql/data_types/overview
|
|
33388
|
+
const typesMapping = {
|
|
33389
|
+
INTEGER: 'number',
|
|
33390
|
+
SMALLINT: 'number',
|
|
33391
|
+
TINYINT: 'number',
|
|
33392
|
+
BIGINT: 'number',
|
|
33393
|
+
HUGEINT: 'number',
|
|
33394
|
+
UINTEGER: 'number',
|
|
33395
|
+
USMALLINT: 'number',
|
|
33396
|
+
UTINYINT: 'number',
|
|
33397
|
+
UBIGINT: 'number',
|
|
33398
|
+
UHUGEINT: 'number',
|
|
33399
|
+
DOUBLE: 'number',
|
|
33400
|
+
BIGNUM: 'number',
|
|
33401
|
+
DECIMAL: 'number',
|
|
33402
|
+
NUMERIC: 'number',
|
|
33403
|
+
FLOAT: 'number',
|
|
33404
|
+
REAL: 'number',
|
|
33405
|
+
'TIMESTAMP WITH TIME ZONE': 'date',
|
|
33406
|
+
TIMESTAMP: 'date',
|
|
33407
|
+
DATE: 'date',
|
|
33408
|
+
CHAR: 'string',
|
|
33409
|
+
VARCHAR: 'string',
|
|
33410
|
+
TEXT: 'string',
|
|
33411
|
+
UUID: 'string',
|
|
33412
|
+
BLOB: 'string',
|
|
33413
|
+
BIT: 'string',
|
|
33414
|
+
INTERVAL: 'string',
|
|
33415
|
+
LIST: 'string',
|
|
33416
|
+
BOOLEAN: 'boolean',
|
|
33417
|
+
};
|
|
33418
|
+
class Engine {
|
|
33419
|
+
async makeInit() {
|
|
33420
|
+
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);
|
|
33421
|
+
let worker_url = bundle.mainWorker;
|
|
33422
|
+
// this is necessary to let browsers execute WASM code coming from a cross-origin host
|
|
33423
|
+
if (worker_url.startsWith('https://')) {
|
|
33424
|
+
worker_url = URL.createObjectURL(new Blob([`importScripts("${worker_url}");`], {
|
|
33425
|
+
type: 'text/javascript',
|
|
33426
|
+
}));
|
|
33427
|
+
}
|
|
33428
|
+
const worker = new Worker(worker_url);
|
|
33429
|
+
const logger = new duckdb.ConsoleLogger(duckdb.LogLevel.WARNING);
|
|
33430
|
+
this.db = new duckdb.AsyncDuckDB(logger, worker);
|
|
33431
|
+
await this.db.instantiate(bundle.mainModule, bundle.pthreadWorker);
|
|
33432
|
+
// setup duckdb options
|
|
33433
|
+
const conn = await this.db.connect();
|
|
33434
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
33435
|
+
await conn.query(`INSTALL spatial; LOAD spatial;`);
|
|
33436
|
+
await conn.query(`SET TimeZone = '${timezone}';`);
|
|
33437
|
+
await conn.query(`SET threads = 1;`);
|
|
33438
|
+
// await conn.query(`SET memory_limit = '2GB';`) // used for experimenting when we encounter memory issues
|
|
33439
|
+
await conn.query(`SET preserve_insertion_order = false;`); // this reduces memory usage when loading a dataset
|
|
33440
|
+
conn.close();
|
|
33441
|
+
return this;
|
|
33442
|
+
}
|
|
33443
|
+
isReady() {
|
|
33444
|
+
if (!this.init_) {
|
|
33445
|
+
this.init_ = this.makeInit();
|
|
33446
|
+
}
|
|
33447
|
+
return this.init_;
|
|
33448
|
+
}
|
|
33449
|
+
/**
|
|
33450
|
+
* Returns information about a dataset once it's loaded:
|
|
33451
|
+
* - a list of properties description
|
|
33452
|
+
* - the name of the dataset geometry column (null if no geometry present)
|
|
33453
|
+
* @param datasetId name of the table under which the dataset will be stored
|
|
33454
|
+
* @param loadQuery duckdb-specific query for creating a table out of the data
|
|
33455
|
+
* @param forceReload if true, any existing data will be dropped and redownloaded
|
|
33456
|
+
*/
|
|
33457
|
+
async loadFile(datasetId, loadQuery, forceReload = false) {
|
|
33458
|
+
const conn = await this.db.connect();
|
|
33459
|
+
let results;
|
|
33460
|
+
// either we want to recreate the table, or we keep it if it already exists
|
|
33461
|
+
const safeLoadQuery = forceReload
|
|
33462
|
+
? `DROP TABLE IF EXISTS ${datasetId};
|
|
33463
|
+
${loadQuery}`
|
|
33464
|
+
: loadQuery.replace(/CREATE TABLE(?! IF NOT EXISTS)/gi, 'CREATE TABLE IF NOT EXISTS');
|
|
33465
|
+
// create the table
|
|
33466
|
+
try {
|
|
33467
|
+
results = await conn.query(safeLoadQuery);
|
|
33468
|
+
}
|
|
33469
|
+
catch (e) {
|
|
33470
|
+
throw new FetchError('parse', `DuckDB encountered an error when loading the data: ${e.message}`);
|
|
33471
|
+
}
|
|
33472
|
+
// read rows count
|
|
33473
|
+
results = await conn.query(`SELECT count(*) FROM ${datasetId}`);
|
|
33474
|
+
const { 'count_star()': recordsCount } = results.toArray()[0].toJSON();
|
|
33475
|
+
// read columns
|
|
33476
|
+
results = await conn.query(`SELECT * FROM information_schema.columns WHERE table_name = '${datasetId}';`);
|
|
33477
|
+
let geometryColumn = null;
|
|
33478
|
+
const properties = results
|
|
33479
|
+
.toArray()
|
|
33480
|
+
.map((row) => {
|
|
33481
|
+
const rowObj = row.toJSON();
|
|
33482
|
+
if (rowObj['data_type'] === 'GEOMETRY') {
|
|
33483
|
+
// the geometry is not part of the properties
|
|
33484
|
+
// note: right now we only keep one geometry column name, but if there are multiple they will
|
|
33485
|
+
// all get discarded
|
|
33486
|
+
geometryColumn = rowObj['column_name'];
|
|
33487
|
+
return null;
|
|
33488
|
+
}
|
|
33489
|
+
return {
|
|
33490
|
+
name: rowObj['column_name'],
|
|
33491
|
+
label: rowObj['column_name'],
|
|
33492
|
+
type: typesMapping[rowObj['data_type']] ?? 'other',
|
|
33493
|
+
};
|
|
33494
|
+
})
|
|
33495
|
+
.filter((prop) => prop !== null);
|
|
33496
|
+
conn.close();
|
|
33497
|
+
return {
|
|
33498
|
+
properties,
|
|
33499
|
+
geometryColumn,
|
|
33500
|
+
rowsCount: Number(recordsCount),
|
|
33501
|
+
};
|
|
33502
|
+
}
|
|
33503
|
+
// register a Uint8 buffer using a handle in the duckdb instance
|
|
33504
|
+
async registerData(name, buffer) {
|
|
33505
|
+
return this.db.registerFileBuffer(name, buffer);
|
|
33506
|
+
}
|
|
33507
|
+
/**
|
|
33508
|
+
* @param query duckdb-specific query for fetching items
|
|
33509
|
+
*/
|
|
33510
|
+
async queryItems(query) {
|
|
33511
|
+
const conn = await this.db.connect();
|
|
33512
|
+
const results = await conn.query(query);
|
|
33513
|
+
conn.close();
|
|
33514
|
+
return arrowTableToDataItems(results);
|
|
33515
|
+
}
|
|
33516
|
+
close() {
|
|
33517
|
+
this.db?.terminate();
|
|
33518
|
+
}
|
|
33519
|
+
}
|
|
33520
|
+
let engine = null;
|
|
33521
|
+
async function getEngine() {
|
|
33522
|
+
if (!engine) {
|
|
33523
|
+
engine = new Engine();
|
|
33524
|
+
}
|
|
33525
|
+
return engine.isReady();
|
|
33059
33526
|
}
|
|
33060
33527
|
|
|
33061
33528
|
class BaseReader {
|
|
@@ -33068,10 +33535,18 @@ class BaseReader {
|
|
|
33068
33535
|
this.sort = null;
|
|
33069
33536
|
this.startIndex = null;
|
|
33070
33537
|
this.count = null;
|
|
33538
|
+
this.loadPromise_ = Promise.resolve();
|
|
33539
|
+
this.cacheEnabled = false;
|
|
33540
|
+
}
|
|
33541
|
+
enableCache(enabled) {
|
|
33542
|
+
this.cacheEnabled = enabled;
|
|
33071
33543
|
}
|
|
33072
33544
|
load() {
|
|
33073
33545
|
throw new Error('not implemented');
|
|
33074
33546
|
}
|
|
33547
|
+
get isLoaded() {
|
|
33548
|
+
return this.loadPromise_;
|
|
33549
|
+
}
|
|
33075
33550
|
get properties() {
|
|
33076
33551
|
throw new Error('not implemented');
|
|
33077
33552
|
}
|
|
@@ -33120,178 +33595,237 @@ class BaseReader {
|
|
|
33120
33595
|
}
|
|
33121
33596
|
}
|
|
33122
33597
|
|
|
33123
|
-
|
|
33124
|
-
|
|
33125
|
-
|
|
33126
|
-
|
|
33127
|
-
|
|
33128
|
-
|
|
33129
|
-
|
|
33130
|
-
|
|
33131
|
-
|
|
33132
|
-
|
|
33133
|
-
|
|
33134
|
-
|
|
33135
|
-
|
|
33598
|
+
/**
|
|
33599
|
+
* This reader handles file formats supported natively by DuckDB
|
|
33600
|
+
*/
|
|
33601
|
+
class BaseFileReader extends BaseReader {
|
|
33602
|
+
// a table id should not exceed 63 chars
|
|
33603
|
+
generateDatasetId() {
|
|
33604
|
+
// generate a hash out of the url
|
|
33605
|
+
let hash = 0;
|
|
33606
|
+
for (const char of this.url) {
|
|
33607
|
+
hash = (hash << 5) - hash + char.charCodeAt(0);
|
|
33608
|
+
hash = hash >>> 0; // make it unsigned
|
|
33609
|
+
}
|
|
33610
|
+
return `datafetcher_${hash.toString(16)}`;
|
|
33611
|
+
}
|
|
33612
|
+
async getLoadQuery() {
|
|
33136
33613
|
throw new Error('not implemented');
|
|
33137
33614
|
}
|
|
33138
|
-
load() {
|
|
33139
|
-
this.
|
|
33615
|
+
async load() {
|
|
33616
|
+
this.datasetId = this.generateDatasetId();
|
|
33617
|
+
this.loadPromise_ = getEngine()
|
|
33618
|
+
.then((engine) => {
|
|
33619
|
+
this.engine = engine;
|
|
33620
|
+
return this.getLoadQuery();
|
|
33621
|
+
})
|
|
33622
|
+
.then((loadQuery) => this.engine.loadFile(this.datasetId, loadQuery, !this.cacheEnabled))
|
|
33623
|
+
.then((datasetInfo) => {
|
|
33624
|
+
this.properties_ = datasetInfo.properties;
|
|
33625
|
+
this.geometryColumn = datasetInfo.geometryColumn;
|
|
33626
|
+
this.rowsCount = datasetInfo.rowsCount;
|
|
33627
|
+
// returns void for the loaded promise
|
|
33628
|
+
});
|
|
33140
33629
|
}
|
|
33141
33630
|
get properties() {
|
|
33142
|
-
return this.
|
|
33631
|
+
return this.isLoaded.then(() => this.properties_);
|
|
33143
33632
|
}
|
|
33144
33633
|
get info() {
|
|
33145
|
-
return this.
|
|
33146
|
-
itemsCount:
|
|
33634
|
+
return this.isLoaded.then(() => ({
|
|
33635
|
+
itemsCount: this.rowsCount,
|
|
33636
|
+
hasGeometry: !!this.geometryColumn,
|
|
33147
33637
|
}));
|
|
33148
33638
|
}
|
|
33149
33639
|
async read() {
|
|
33150
|
-
|
|
33151
|
-
//
|
|
33152
|
-
|
|
33153
|
-
|
|
33154
|
-
|
|
33155
|
-
this.sort == null &&
|
|
33156
|
-
this.filter == null &&
|
|
33157
|
-
this.startIndex == null &&
|
|
33158
|
-
this.count == null) {
|
|
33159
|
-
return items;
|
|
33160
|
-
}
|
|
33161
|
-
const jsonItems = getJsonDataItemsProxy(items);
|
|
33162
|
-
const query = generateSqlQuery(this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations);
|
|
33163
|
-
const result = await import('alasql').then((module) => module.default(query, [jsonItems]));
|
|
33164
|
-
return result.map(jsonToGeojsonFeature);
|
|
33165
|
-
}
|
|
33166
|
-
}
|
|
33167
|
-
|
|
33168
|
-
function parseCsv(text) {
|
|
33169
|
-
// first parse the header to guess the delimiter
|
|
33170
|
-
// note that we do that to not rely on Papaparse logic for guessing delimiter
|
|
33171
|
-
let delimiter;
|
|
33172
|
-
try {
|
|
33173
|
-
const header = text.split('\n')[0];
|
|
33174
|
-
const result = Papa.parse(header, {
|
|
33175
|
-
header: false,
|
|
33176
|
-
});
|
|
33177
|
-
delimiter = result.meta.delimiter;
|
|
33178
|
-
}
|
|
33179
|
-
catch (e) {
|
|
33180
|
-
throw new Error('CSV parsing failed: the delimiter could not be guessed');
|
|
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'));
|
|
33640
|
+
await this.isLoaded;
|
|
33641
|
+
// if only certain fields are selected, omit the geometry
|
|
33642
|
+
const geometryColumn = this.selected === null ? this.geometryColumn : null;
|
|
33643
|
+
const query = generateSqlQuery(this.datasetId, this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations, geometryColumn);
|
|
33644
|
+
return this.engine.queryItems(query);
|
|
33192
33645
|
}
|
|
33193
|
-
const items = parsed.data.map(jsonToGeojsonFeature);
|
|
33194
|
-
return processItemProperties(items, true);
|
|
33195
33646
|
}
|
|
33647
|
+
|
|
33196
33648
|
class CsvReader extends BaseFileReader {
|
|
33197
|
-
|
|
33198
|
-
|
|
33649
|
+
async getLoadQuery() {
|
|
33650
|
+
// first we get a list of columns hich have a detected type of VARCHAR
|
|
33651
|
+
// 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
|
|
33652
|
+
return `
|
|
33653
|
+
SET VARIABLE strColumns = (SELECT list(name) FROM (SELECT unnest(Columns, recursive := true) FROM sniff_csv("${this.url}")) WHERE type = 'VARCHAR');
|
|
33654
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM read_csv("${this.url}",
|
|
33655
|
+
force_not_null = getvariable('strColumns'),
|
|
33656
|
+
auto_type_candidates = ['NULL', 'BOOLEAN', 'INTEGER', 'DOUBLE', 'DATE', 'VARCHAR']
|
|
33657
|
+
);`;
|
|
33199
33658
|
}
|
|
33200
33659
|
}
|
|
33201
33660
|
|
|
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
33661
|
class JsonReader extends BaseFileReader {
|
|
33214
|
-
|
|
33215
|
-
return
|
|
33662
|
+
async getLoadQuery() {
|
|
33663
|
+
return `
|
|
33664
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM read_json("${this.url}",
|
|
33665
|
+
maximum_object_size = 536870912 -- 500MB
|
|
33666
|
+
);`;
|
|
33216
33667
|
}
|
|
33217
33668
|
}
|
|
33218
33669
|
|
|
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
33670
|
class GeojsonReader extends BaseFileReader {
|
|
33233
|
-
|
|
33234
|
-
return
|
|
33671
|
+
async getLoadQuery() {
|
|
33672
|
+
return `
|
|
33673
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM st_read("${this.url}",
|
|
33674
|
+
allowed_drivers = ['GeoJSON']
|
|
33675
|
+
);`;
|
|
33235
33676
|
}
|
|
33236
33677
|
}
|
|
33237
33678
|
|
|
33679
|
+
async function inferDatasetType(url, typeHint) {
|
|
33680
|
+
const fileExtensionMatches = new URL(url, typeof window !== 'undefined' ? window.location.toString() : undefined).pathname.match(/\.(.+)$/);
|
|
33681
|
+
const fileExtension = fileExtensionMatches && fileExtensionMatches.length
|
|
33682
|
+
? fileExtensionMatches[1].toLowerCase()
|
|
33683
|
+
: null;
|
|
33684
|
+
// 1. type hint
|
|
33685
|
+
if (typeHint)
|
|
33686
|
+
return Promise.resolve(typeHint);
|
|
33687
|
+
// 2. content-type header
|
|
33688
|
+
const headers = await fetchHeaders(url);
|
|
33689
|
+
if ('supportedType' in headers)
|
|
33690
|
+
return headers.supportedType;
|
|
33691
|
+
// 3. file extension from url
|
|
33692
|
+
else if (SupportedTypes.indexOf(fileExtension) > -1)
|
|
33693
|
+
return fileExtension;
|
|
33694
|
+
// no type inferred or hinted
|
|
33695
|
+
if ('mimeType' in headers)
|
|
33696
|
+
throw FetchError.unsupportedType(headers.mimeType);
|
|
33697
|
+
else
|
|
33698
|
+
throw FetchError.unknownType();
|
|
33699
|
+
}
|
|
33700
|
+
function fetchHeaders(url) {
|
|
33701
|
+
return sharedFetch(url, 'HEAD')
|
|
33702
|
+
.catch((error) => {
|
|
33703
|
+
throw FetchError.corsOrNetwork(error.message);
|
|
33704
|
+
})
|
|
33705
|
+
.then((response) => {
|
|
33706
|
+
if (!response.ok) {
|
|
33707
|
+
throw FetchError.http(response.status);
|
|
33708
|
+
}
|
|
33709
|
+
return parseHeaders(response.headers);
|
|
33710
|
+
});
|
|
33711
|
+
}
|
|
33712
|
+
function fetchDataAsText(url, cacheActive) {
|
|
33713
|
+
const fetchFactory = () => sharedFetch(url)
|
|
33714
|
+
.catch((error) => {
|
|
33715
|
+
throw FetchError.corsOrNetwork(error.message);
|
|
33716
|
+
})
|
|
33717
|
+
.then(async (response) => {
|
|
33718
|
+
if (!response.ok) {
|
|
33719
|
+
const clonedResponse = response.clone();
|
|
33720
|
+
throw FetchError.http(response.status, await clonedResponse.text());
|
|
33721
|
+
}
|
|
33722
|
+
const clonedResponse = response.clone();
|
|
33723
|
+
return clonedResponse.text();
|
|
33724
|
+
});
|
|
33725
|
+
return cacheActive ? useCache(fetchFactory, url, 'asText') : fetchFactory();
|
|
33726
|
+
}
|
|
33727
|
+
function fetchDataAsArrayBuffer(url, cacheActive) {
|
|
33728
|
+
const fetchFactory = () => sharedFetch(url)
|
|
33729
|
+
.catch((error) => {
|
|
33730
|
+
throw FetchError.corsOrNetwork(error.message);
|
|
33731
|
+
})
|
|
33732
|
+
.then(async (response) => {
|
|
33733
|
+
if (!response.ok) {
|
|
33734
|
+
throw FetchError.http(response.status, await response.text());
|
|
33735
|
+
}
|
|
33736
|
+
// convert to a numeric array so that we can store the response in cache
|
|
33737
|
+
return Array.from(new Uint8Array(await response.arrayBuffer()));
|
|
33738
|
+
});
|
|
33739
|
+
return (cacheActive ? useCache(fetchFactory, url, 'asArrayBuffer') : fetchFactory()).then((array) => {
|
|
33740
|
+
return new Uint8Array(array).buffer;
|
|
33741
|
+
});
|
|
33742
|
+
}
|
|
33238
33743
|
/**
|
|
33239
|
-
* This
|
|
33240
|
-
*
|
|
33241
|
-
* @param
|
|
33744
|
+
* This creates a Proxy that allows reading and writing to the data item properties
|
|
33745
|
+
* as if it was a simple array of JSON objects
|
|
33746
|
+
* @param items
|
|
33242
33747
|
*/
|
|
33243
|
-
function
|
|
33244
|
-
return
|
|
33245
|
-
|
|
33246
|
-
|
|
33247
|
-
|
|
33248
|
-
|
|
33249
|
-
|
|
33250
|
-
|
|
33251
|
-
|
|
33748
|
+
function getJsonDataItemsProxy(items) {
|
|
33749
|
+
return new Proxy(items, {
|
|
33750
|
+
get(target, p) {
|
|
33751
|
+
if (typeof p === 'string' &&
|
|
33752
|
+
!Number.isNaN(parseInt(p)) &&
|
|
33753
|
+
target[p]?.properties) {
|
|
33754
|
+
return target[p].properties;
|
|
33755
|
+
}
|
|
33756
|
+
return target[p];
|
|
33757
|
+
},
|
|
33758
|
+
set() {
|
|
33759
|
+
throw new Error('This object is read-only');
|
|
33760
|
+
},
|
|
33252
33761
|
});
|
|
33253
33762
|
}
|
|
33763
|
+
|
|
33254
33764
|
class ExcelReader extends BaseFileReader {
|
|
33255
|
-
|
|
33256
|
-
|
|
33765
|
+
async getLoadQuery() {
|
|
33766
|
+
// we download the file as an array buffer first, in order to be able to check if it's an XLS file
|
|
33767
|
+
let buffer = await fetchDataAsArrayBuffer(this.url, this.cacheEnabled);
|
|
33768
|
+
const bufferHandle = `B${this.datasetId}`;
|
|
33769
|
+
// checking against the magic number at the beginning of XLS files, see https://en.wikipedia.org/wiki/List_of_file_signatures
|
|
33770
|
+
const magicNumber = new Uint8Array(buffer, 0, 8); // first 8 bytes
|
|
33771
|
+
const isXls = Array.from(magicNumber)
|
|
33772
|
+
.map((n) => n.toString(16).toUpperCase())
|
|
33773
|
+
.join(' ') === 'D0 CF 11 E0 A1 B1 1A E1';
|
|
33774
|
+
// uh oh, this is an XLS file (not supported by duckdb); convert it to CSV using the xlsx package
|
|
33775
|
+
if (isXls) {
|
|
33776
|
+
buffer = await import('xlsx').then(({ read, utils }) => {
|
|
33777
|
+
const workbook = read(buffer);
|
|
33778
|
+
const json = utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]]);
|
|
33779
|
+
return new TextEncoder().encode(JSON.stringify(json)).buffer;
|
|
33780
|
+
});
|
|
33781
|
+
}
|
|
33782
|
+
const duckDbFn = isXls ? 'read_json' : 'read_xlsx';
|
|
33783
|
+
await this.engine.registerData(bufferHandle, new Uint8Array(buffer));
|
|
33784
|
+
return `
|
|
33785
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM ${duckDbFn}("${bufferHandle}", ignore_errors = true);`;
|
|
33786
|
+
}
|
|
33787
|
+
}
|
|
33788
|
+
|
|
33789
|
+
class GmlReader extends BaseFileReader {
|
|
33790
|
+
async getLoadQuery() {
|
|
33791
|
+
return `
|
|
33792
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM st_read("${this.url}",
|
|
33793
|
+
allowed_drivers = ['GML']
|
|
33794
|
+
);`;
|
|
33257
33795
|
}
|
|
33258
33796
|
}
|
|
33259
33797
|
|
|
33260
|
-
|
|
33261
|
-
|
|
33262
|
-
const
|
|
33798
|
+
const formatGeojson = new GeoJSON$1();
|
|
33799
|
+
function parseGeojson(text) {
|
|
33800
|
+
const parsed = JSON.parse(text);
|
|
33801
|
+
const features = parsed.type === 'FeatureCollection' ? parsed.features : parsed;
|
|
33802
|
+
if (!Array.isArray(features)) {
|
|
33803
|
+
throw new Error('Could not parse GeoJSON, expected a features collection or an array of features at root level');
|
|
33804
|
+
}
|
|
33805
|
+
return features;
|
|
33806
|
+
}
|
|
33807
|
+
function parseGml(text, featureType, version) {
|
|
33808
|
+
const parts = featureType.split(':');
|
|
33809
|
+
const regex = new RegExp(`xmlns:${parts[0]}=["']([^'"]*)["']`);
|
|
33263
33810
|
const match = regex.exec(text);
|
|
33264
33811
|
if (match && match.length >= 2) {
|
|
33265
|
-
const
|
|
33812
|
+
const wfs = new WFS({
|
|
33266
33813
|
featureNS: match[1],
|
|
33267
|
-
featureType:
|
|
33814
|
+
featureType: parts[1],
|
|
33268
33815
|
version: version,
|
|
33269
33816
|
});
|
|
33270
33817
|
let features;
|
|
33271
33818
|
try {
|
|
33272
|
-
features =
|
|
33819
|
+
features = wfs.readFeatures(text);
|
|
33273
33820
|
}
|
|
33274
33821
|
catch (e) {
|
|
33275
|
-
throw Error(
|
|
33822
|
+
throw Error(`Couldn't parse WFS with GML features: ${e.message}`);
|
|
33276
33823
|
}
|
|
33277
|
-
const geojsonItem =
|
|
33278
|
-
return
|
|
33824
|
+
const geojsonItem = formatGeojson.writeFeaturesObject(features);
|
|
33825
|
+
return geojsonItem.features;
|
|
33279
33826
|
}
|
|
33280
33827
|
throw Error("Couldn't retrieve namespace url");
|
|
33281
33828
|
}
|
|
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
33829
|
async function getWfsEndpoint(wfsUrl) {
|
|
33296
33830
|
try {
|
|
33297
33831
|
return await new WfsEndpoint(wfsUrl).isReady();
|
|
@@ -33319,21 +33853,65 @@ async function getWfsEndpoint(wfsUrl) {
|
|
|
33319
33853
|
}
|
|
33320
33854
|
}
|
|
33321
33855
|
}
|
|
33322
|
-
class WfsReader extends
|
|
33323
|
-
constructor(url,
|
|
33324
|
-
super(url
|
|
33325
|
-
this.endpoint =
|
|
33326
|
-
this.
|
|
33327
|
-
|
|
33856
|
+
class WfsReader extends BaseReader {
|
|
33857
|
+
constructor(url, featureTypeName) {
|
|
33858
|
+
super(url);
|
|
33859
|
+
this.endpoint = getWfsEndpoint(url);
|
|
33860
|
+
this.featureType = this.endpoint
|
|
33861
|
+
.then((endpoint) => {
|
|
33862
|
+
const featureTypes = endpoint.getFeatureTypes();
|
|
33863
|
+
return endpoint.getFeatureTypeFull(featureTypes.length === 1 && !featureTypeName
|
|
33864
|
+
? featureTypes[0].name
|
|
33865
|
+
: featureTypeName);
|
|
33866
|
+
})
|
|
33867
|
+
.then((featureType) => {
|
|
33868
|
+
if (!featureType) {
|
|
33869
|
+
throw new Error('wfs.featuretype.notfound');
|
|
33870
|
+
}
|
|
33871
|
+
return featureType;
|
|
33872
|
+
});
|
|
33873
|
+
}
|
|
33874
|
+
get backupReader() {
|
|
33875
|
+
if (this.backupReader_) {
|
|
33876
|
+
return this.backupReader_;
|
|
33877
|
+
}
|
|
33878
|
+
this.backupReader_ = Promise.all([this.endpoint, this.featureType]).then(([endpoint, featureType]) => {
|
|
33879
|
+
let reader;
|
|
33880
|
+
if (endpoint.supportsJson(featureType.name)) {
|
|
33881
|
+
reader = new GeojsonReader(endpoint.getFeatureUrl(featureType.name, {
|
|
33882
|
+
asJson: true,
|
|
33883
|
+
outputCrs: 'EPSG:4326',
|
|
33884
|
+
}));
|
|
33885
|
+
}
|
|
33886
|
+
else {
|
|
33887
|
+
if (featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')) &&
|
|
33888
|
+
(featureType.defaultCrs === 'EPSG:4326' ||
|
|
33889
|
+
featureType.otherCrs?.includes('EPSG:4326'))) {
|
|
33890
|
+
reader = new GmlReader(endpoint.getFeatureUrl(featureType.name, {
|
|
33891
|
+
outputFormat: featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')),
|
|
33892
|
+
outputCrs: 'EPSG:4326',
|
|
33893
|
+
}));
|
|
33894
|
+
}
|
|
33895
|
+
}
|
|
33896
|
+
reader.enableCache(this.cacheEnabled);
|
|
33897
|
+
reader.load();
|
|
33898
|
+
return reader;
|
|
33899
|
+
});
|
|
33900
|
+
return this.backupReader_;
|
|
33328
33901
|
}
|
|
33329
33902
|
get properties() {
|
|
33330
|
-
return this.
|
|
33331
|
-
.getFeatureTypeFull(this.featureTypeName)
|
|
33332
|
-
.then((featureType) => Object.keys(featureType.properties).map((prop) => {
|
|
33903
|
+
return this.featureType.then((featureType) => Object.keys(featureType.properties).map((prop) => {
|
|
33333
33904
|
const originalType = featureType.properties[prop];
|
|
33334
|
-
|
|
33335
|
-
|
|
33336
|
-
|
|
33905
|
+
let type;
|
|
33906
|
+
if (originalType === 'float' || originalType === 'integer') {
|
|
33907
|
+
type = 'number';
|
|
33908
|
+
}
|
|
33909
|
+
else if (originalType === 'boolean') {
|
|
33910
|
+
type = 'string'; // we don't handle booleans yet in the data fetcher
|
|
33911
|
+
}
|
|
33912
|
+
else {
|
|
33913
|
+
type = originalType;
|
|
33914
|
+
}
|
|
33337
33915
|
return {
|
|
33338
33916
|
name: prop,
|
|
33339
33917
|
label: prop,
|
|
@@ -33342,83 +33920,63 @@ class WfsReader extends BaseCacheReader {
|
|
|
33342
33920
|
}));
|
|
33343
33921
|
}
|
|
33344
33922
|
get info() {
|
|
33345
|
-
return this.
|
|
33923
|
+
return this.featureType.then((result) => ({
|
|
33346
33924
|
itemsCount: result.objectCount,
|
|
33925
|
+
hasGeometry: !!result.geometryName,
|
|
33347
33926
|
}));
|
|
33348
33927
|
}
|
|
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
|
-
}
|
|
33928
|
+
load() {
|
|
33929
|
+
// Nothing to load for Wfs
|
|
33378
33930
|
}
|
|
33379
|
-
async
|
|
33380
|
-
|
|
33381
|
-
|
|
33931
|
+
async read() {
|
|
33932
|
+
const endpoint = await this.endpoint;
|
|
33933
|
+
const featureType = await this.featureType;
|
|
33934
|
+
// if we can't use the WFS protocol we fall back to the backup reader
|
|
33935
|
+
if (this.aggregations || this.groupedBy || !endpoint.supportsStartIndex()) {
|
|
33936
|
+
const backupReader = await this.backupReader;
|
|
33937
|
+
backupReader.selectAll();
|
|
33938
|
+
if (this.selected) {
|
|
33939
|
+
backupReader.select(...this.selected);
|
|
33940
|
+
}
|
|
33941
|
+
if (this.aggregations) {
|
|
33942
|
+
backupReader.aggregate(...this.aggregations);
|
|
33943
|
+
}
|
|
33944
|
+
if (this.groupedBy) {
|
|
33945
|
+
backupReader.groupBy(...this.groupedBy);
|
|
33946
|
+
}
|
|
33947
|
+
if (this.sort) {
|
|
33948
|
+
backupReader.orderBy(...this.sort);
|
|
33949
|
+
}
|
|
33950
|
+
if (this.startIndex !== null && this.count !== null) {
|
|
33951
|
+
backupReader.limit(this.startIndex, this.count);
|
|
33952
|
+
}
|
|
33953
|
+
return backupReader.read();
|
|
33382
33954
|
}
|
|
33383
|
-
const asJson =
|
|
33955
|
+
const asJson = endpoint.supportsJson(featureType.name);
|
|
33384
33956
|
const attributes = this.selected ?? undefined;
|
|
33385
|
-
let
|
|
33957
|
+
let sortBy = null;
|
|
33958
|
+
if (this.sort) {
|
|
33959
|
+
const mapSort = (s) => [s[0] === 'desc' ? 'D' : 'A', s[1]];
|
|
33960
|
+
sortBy = Array.isArray(this.sort[0])
|
|
33961
|
+
? this.sort.map(mapSort)
|
|
33962
|
+
: mapSort(this.sort);
|
|
33963
|
+
}
|
|
33964
|
+
const url = endpoint.getFeatureUrl(featureType.name, {
|
|
33386
33965
|
...(this.startIndex !== null && { startIndex: this.startIndex }),
|
|
33387
33966
|
...(this.count !== null && { maxFeatures: this.count }),
|
|
33388
33967
|
asJson,
|
|
33389
33968
|
outputCrs: 'EPSG:4326',
|
|
33390
33969
|
attributes,
|
|
33391
|
-
|
|
33970
|
+
sortBy,
|
|
33392
33971
|
});
|
|
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
|
|
33972
|
+
return fetchDataAsText(url, this.cacheEnabled).then((text) => asJson
|
|
33402
33973
|
? 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;
|
|
33974
|
+
: parseGml(text, featureType.name, endpoint.getVersion()));
|
|
33417
33975
|
}
|
|
33418
33976
|
}
|
|
33419
33977
|
|
|
33420
|
-
async function openDataset(url,
|
|
33421
|
-
const fileType = await inferDatasetType(url, typeHint);
|
|
33978
|
+
async function openDataset(url, options) {
|
|
33979
|
+
const fileType = await inferDatasetType(url, options?.typeHint);
|
|
33422
33980
|
let reader;
|
|
33423
33981
|
try {
|
|
33424
33982
|
switch (fileType) {
|
|
@@ -33435,18 +33993,18 @@ async function openDataset(url, typeHint, options, cacheActive) {
|
|
|
33435
33993
|
reader = new ExcelReader(url);
|
|
33436
33994
|
break;
|
|
33437
33995
|
case 'gml':
|
|
33438
|
-
reader = new GmlReader(url
|
|
33996
|
+
reader = new GmlReader(url);
|
|
33439
33997
|
break;
|
|
33440
33998
|
case 'wfs':
|
|
33441
|
-
reader =
|
|
33999
|
+
reader = new WfsReader(url, options?.wfsFeatureType);
|
|
33442
34000
|
break;
|
|
33443
34001
|
}
|
|
33444
|
-
reader.
|
|
34002
|
+
reader.enableCache(options?.enableCache ?? true);
|
|
33445
34003
|
reader.load();
|
|
33446
34004
|
return reader;
|
|
33447
34005
|
}
|
|
33448
34006
|
catch (e) {
|
|
33449
|
-
//WfsReader may already raise a FetchError
|
|
34007
|
+
// WfsReader may already raise a FetchError
|
|
33450
34008
|
if (e instanceof FetchError)
|
|
33451
34009
|
throw e;
|
|
33452
34010
|
else
|
|
@@ -33457,13 +34015,13 @@ async function openDataset(url, typeHint, options, cacheActive) {
|
|
|
33457
34015
|
* This fetches the full dataset at the given URL and parses it according to its mime type.
|
|
33458
34016
|
* All items in the dataset are converted to GeoJSON features, even if they do not bear any spatial geometry.
|
|
33459
34017
|
* File type can be either inferred (from the HTTP headers or the URL), or hinted using the 2nd argument
|
|
33460
|
-
* File type is determined
|
|
34018
|
+
* File type is determined like so:
|
|
33461
34019
|
* 1. if a type hint is given, use it
|
|
33462
34020
|
* 2. otherwise, look for a Content-Type header in the response with a supported mime type
|
|
33463
34021
|
* 3. if no valid mime type was found, look for an explicit file extension in the url (.csv, .geojson etc.)
|
|
33464
34022
|
*/
|
|
33465
|
-
async function readDataset(url,
|
|
33466
|
-
const reader = await openDataset(url,
|
|
34023
|
+
async function readDataset(url, options) {
|
|
34024
|
+
const reader = await openDataset(url, options);
|
|
33467
34025
|
try {
|
|
33468
34026
|
return await reader.read();
|
|
33469
34027
|
}
|
|
@@ -33694,9 +34252,11 @@ class DataService {
|
|
|
33694
34252
|
getDataset(link, cacheActive) {
|
|
33695
34253
|
if (link.type === 'service' && link.accessServiceProtocol === 'wfs') {
|
|
33696
34254
|
const wfsUrlEndpoint = this.proxy.getProxiedUrl(link.url.toString());
|
|
33697
|
-
return from(openDataset(wfsUrlEndpoint,
|
|
34255
|
+
return from(openDataset(wfsUrlEndpoint, {
|
|
34256
|
+
typeHint: 'wfs',
|
|
33698
34257
|
wfsFeatureType: link.name,
|
|
33699
|
-
|
|
34258
|
+
enableCache: cacheActive,
|
|
34259
|
+
}));
|
|
33700
34260
|
}
|
|
33701
34261
|
else if (link.type === 'download') {
|
|
33702
34262
|
const linkProxifiedUrl = this.proxy.getProxiedUrl(link.url.toString());
|
|
@@ -33704,12 +34264,15 @@ class DataService {
|
|
|
33704
34264
|
const supportedType = SupportedTypes.indexOf(format) > -1
|
|
33705
34265
|
? format
|
|
33706
34266
|
: undefined;
|
|
33707
|
-
return from(openDataset(linkProxifiedUrl,
|
|
34267
|
+
return from(openDataset(linkProxifiedUrl, {
|
|
34268
|
+
typeHint: supportedType,
|
|
34269
|
+
enableCache: cacheActive,
|
|
34270
|
+
})).pipe();
|
|
33708
34271
|
}
|
|
33709
34272
|
else if (link.type === 'service' &&
|
|
33710
34273
|
link.accessServiceProtocol === 'esriRest') {
|
|
33711
34274
|
const url = this.getDownloadUrlFromEsriRest(link.url.toString(), 'geojson');
|
|
33712
|
-
return from(openDataset(url, 'geojson',
|
|
34275
|
+
return from(openDataset(url, { typeHint: 'geojson', enableCache: cacheActive })).pipe();
|
|
33713
34276
|
}
|
|
33714
34277
|
else if (link.type === 'service' &&
|
|
33715
34278
|
link.accessServiceProtocol === 'ogcFeatures') {
|
|
@@ -33723,7 +34286,10 @@ class DataService {
|
|
|
33723
34286
|
}
|
|
33724
34287
|
const urlWithoutLimit = new URL(geojsonUrl);
|
|
33725
34288
|
urlWithoutLimit.searchParams.delete('limit');
|
|
33726
|
-
return openDataset(urlWithoutLimit.toString(),
|
|
34289
|
+
return openDataset(urlWithoutLimit.toString(), {
|
|
34290
|
+
typeHint: 'geojson',
|
|
34291
|
+
enableCache: cacheActive,
|
|
34292
|
+
});
|
|
33727
34293
|
}));
|
|
33728
34294
|
}
|
|
33729
34295
|
return throwError(() => 'protocol not supported');
|
|
@@ -33975,20 +34541,31 @@ class DataTableComponent {
|
|
|
33975
34541
|
this.eltRef = inject(ElementRef);
|
|
33976
34542
|
this.cdr = inject(ChangeDetectorRef);
|
|
33977
34543
|
this.translateService = inject(TranslateService);
|
|
33978
|
-
this.
|
|
34544
|
+
this.columnsFromFeatureCatalog = null;
|
|
34545
|
+
this.columnsFromDataset = [];
|
|
33979
34546
|
this.selected = new EventEmitter();
|
|
33980
|
-
this.properties$ = new BehaviorSubject(null);
|
|
33981
34547
|
this.loading$ = new BehaviorSubject(false);
|
|
33982
34548
|
this.error = null;
|
|
33983
34549
|
}
|
|
33984
34550
|
set featureAttributes(value) {
|
|
33985
|
-
this.
|
|
33986
|
-
|
|
34551
|
+
this.columnsFromFeatureCatalog = value.map((attrs) => ({
|
|
34552
|
+
name: attrs.value,
|
|
34553
|
+
label: attrs.label,
|
|
34554
|
+
}));
|
|
33987
34555
|
}
|
|
33988
34556
|
set dataset(value) {
|
|
33989
34557
|
this.dataset_ = value;
|
|
33990
34558
|
this.dataset_.load();
|
|
33991
|
-
this.dataset_.info.then((info) =>
|
|
34559
|
+
this.dataset_.info.then((info) => {
|
|
34560
|
+
this.count = info.itemsCount;
|
|
34561
|
+
this.cdr.detectChanges();
|
|
34562
|
+
});
|
|
34563
|
+
}
|
|
34564
|
+
get columns() {
|
|
34565
|
+
return this.columnsFromFeatureCatalog ?? this.columnsFromDataset;
|
|
34566
|
+
}
|
|
34567
|
+
get columnNames() {
|
|
34568
|
+
return this.columns.map((c) => c.name);
|
|
33992
34569
|
}
|
|
33993
34570
|
ngOnInit() {
|
|
33994
34571
|
this.dataSource = new DataTableDataSource();
|
|
@@ -34023,11 +34600,12 @@ class DataTableComponent {
|
|
|
34023
34600
|
}
|
|
34024
34601
|
async readData() {
|
|
34025
34602
|
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
34603
|
try {
|
|
34604
|
+
// wait for properties to be read
|
|
34605
|
+
if (!this.columnsFromFeatureCatalog) {
|
|
34606
|
+
this.columnsFromDataset = await this.dataset_.properties;
|
|
34607
|
+
}
|
|
34608
|
+
this.dataset_.select(...this.columnNames);
|
|
34031
34609
|
await this.dataSource.showData(this.dataset_.read());
|
|
34032
34610
|
this.error = null;
|
|
34033
34611
|
}
|
|
@@ -34057,7 +34635,7 @@ class DataTableComponent {
|
|
|
34057
34635
|
}
|
|
34058
34636
|
}
|
|
34059
34637
|
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
|
|
34638
|
+
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
34639
|
}
|
|
34062
34640
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DataTableComponent, decorators: [{
|
|
34063
34641
|
type: Component,
|
|
@@ -34069,10 +34647,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
34069
34647
|
CommonModule,
|
|
34070
34648
|
LoadingMaskComponent,
|
|
34071
34649
|
PopupAlertComponent,
|
|
34072
|
-
LetDirective,
|
|
34073
34650
|
TranslatePipe,
|
|
34074
34651
|
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
|
|
34652
|
+
], 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
34653
|
}], propDecorators: { featureAttributes: [{
|
|
34077
34654
|
type: Input
|
|
34078
34655
|
}], dataset: [{
|
|
@@ -34175,7 +34752,7 @@ class ChartViewComponent {
|
|
|
34175
34752
|
}), shareReplay$1(1));
|
|
34176
34753
|
this.properties$ = combineLatest([this.dataset$, this.featureCatalog$]).pipe(switchMap$1(([dataset, catalog]) => this.setProperties(dataset, catalog)), shareReplay$1(1));
|
|
34177
34754
|
this.yChoices$ = this.properties$.pipe(map$1((properties) => properties
|
|
34178
|
-
.filter((prop) => prop.type === 'number'
|
|
34755
|
+
.filter((prop) => prop.type === 'number')
|
|
34179
34756
|
.map((prop) => ({ value: prop.name, label: prop.label || prop.name }))), tap$1((choices) => {
|
|
34180
34757
|
if (!choices.find((choice) => choice.value === this.yProperty$.value)) {
|
|
34181
34758
|
const newProp = choices[0]?.value || '';
|
|
@@ -34202,6 +34779,7 @@ class ChartViewComponent {
|
|
|
34202
34779
|
this.aggregation$,
|
|
34203
34780
|
]).pipe(filter$1(([_, x, y]) => !!x || !!y), switchMap$1(([dataset, xProp, yProp, aggregation]) => {
|
|
34204
34781
|
const fieldAgg = aggregation === 'count' ? ['count'] : [aggregation, yProp];
|
|
34782
|
+
this.loading = true;
|
|
34205
34783
|
return dataset
|
|
34206
34784
|
.groupBy(['distinct', xProp])
|
|
34207
34785
|
.aggregate(fieldAgg)
|
|
@@ -34443,6 +35021,7 @@ class GeoTableViewComponent {
|
|
|
34443
35021
|
}));
|
|
34444
35022
|
}
|
|
34445
35023
|
async initMapContext() {
|
|
35024
|
+
this.dataset.load();
|
|
34446
35025
|
this.dataset.selectAll();
|
|
34447
35026
|
return {
|
|
34448
35027
|
layers: [
|
|
@@ -34616,8 +35195,7 @@ class MdViewFacade {
|
|
|
34616
35195
|
}));
|
|
34617
35196
|
this.error$ = this.store.pipe(select(getMetadataError));
|
|
34618
35197
|
this.related$ = this.store.pipe(select(getRelated));
|
|
34619
|
-
this.
|
|
34620
|
-
this.sourceOf$ = this.store.pipe(select(getSourceOf));
|
|
35198
|
+
this.linkedRecords$ = this.store.pipe(select(getLinkedRecords));
|
|
34621
35199
|
this.chartConfig$ = this.store.pipe(select(getChartConfig));
|
|
34622
35200
|
this.allLinks$ = this.metadata$.pipe(map$1((record) => 'onlineResources' in record ? record.onlineResources : []), shareReplay$1(1));
|
|
34623
35201
|
this.resourceDoi$ = this.metadata$.pipe(map$1((record) => {
|
|
@@ -34736,12 +35314,9 @@ class MdViewEffects {
|
|
|
34736
35314
|
this.loadRelatedRecords$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getSimilarRecords(full)), map$1((related) => {
|
|
34737
35315
|
return setRelated({ related });
|
|
34738
35316
|
}), 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 })))));
|
|
35317
|
+
this.loadLinkedRecords$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getLinkedRecords(full)), map$1((linkedRecords) => {
|
|
35318
|
+
return setLinkedRecords({ linkedRecords });
|
|
35319
|
+
}), catchError(() => of(setLinkedRecords({ linkedRecords: null })))));
|
|
34745
35320
|
/*
|
|
34746
35321
|
UserFeedback effects
|
|
34747
35322
|
*/
|
|
@@ -40020,7 +40595,7 @@ function flattenQueryParams(params) {
|
|
|
40020
40595
|
const start = flattened[key].start;
|
|
40021
40596
|
const end = flattened[key].end;
|
|
40022
40597
|
flattened[key] = [
|
|
40023
|
-
`${start ? formatDate(start) : ''}..${formatDate(end)
|
|
40598
|
+
`${start ? formatDate(start) : ''}..${end ? formatDate(end) : ''}`,
|
|
40024
40599
|
];
|
|
40025
40600
|
}
|
|
40026
40601
|
}
|
|
@@ -40377,5 +40952,5 @@ const CHART_TYPE_VALUES = [
|
|
|
40377
40952
|
* Generated bundle index. Do not edit.
|
|
40378
40953
|
*/
|
|
40379
40954
|
|
|
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 };
|
|
40955
|
+
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, ResourceCreationRevisionDateSearchField, ResourceTypeLegacyField, ResultsHitsContainerComponent, ResultsHitsNumberComponent, ResultsHitsSearchKindComponent, ResultsLayoutComponent, ResultsLayoutConfigItem, ResultsListComponent, ResultsListContainerComponent, ResultsListItemComponent, ResultsTableComponent, ResultsTableContainerComponent, ReusePresentationForms, RoleLabels, RoleValues, RouterEffects, RouterFacade, RouterService, SEARCH_FEATURE_KEY, SETTINGS_URL, SET_CONFIG_AGGREGATIONS, SET_CONFIG_FILTERS, SET_CONFIG_REQUEST_FIELDS, SET_ERROR, SET_FAVORITES_ONLY, SET_FILTERS, SET_INCLUDE_ON_AGGREGATION, SET_PAGE_SIZE, SET_RESULTS_AGGREGATIONS, SET_RESULTS_HITS, SET_RESULTS_LAYOUT, SET_SEARCH, SET_SORT_BY, SET_SPATIAL_FILTER_ENABLED, SPATIAL_SCOPES, SearchEffects, SearchFacade, SearchFeatureCatalogComponent, SearchFiltersSummaryComponent, SearchFiltersSummaryItemComponent, SearchInputComponent, SearchRouterContainerDirective, SearchService, SearchStateContainerDirective, SelectionService, ServiceCapabilitiesComponent, SetConfigAggregations, SetConfigFilters, SetConfigRequestFields, SetError, SetFavoritesOnly, SetFilters, SetIncludeOnAggregation, SetPageSize, SetResultsAggregations, SetResultsHits, SetResultsLayout, SetSearch, SetSortBy, SetSpatialFilterEnabled, SimpleSearchField, SiteTitleComponent, SortByComponent, SortByEnum, SortableListComponent, SourceLabelComponent, SourcesService, SpatialExtentComponent, SpatialExtentDropdownComponent, SpinningLoaderComponent, StacItemsResultGridComponent, StacViewComponent, StarToggleComponent, StickyHeaderComponent, SupportedTypes, SwitchToggleComponent, THUMBNAIL_PLACEHOLDER, TRANSLATE_DEBUG_CONFIG, TRANSLATE_DEFAULT_CONFIG, TRANSLATE_WITH_OVERRIDES_CONFIG, TableViewComponent, TextAreaComponent, TextInputComponent, ThemeService, ThumbnailComponent, TranslatedSearchField, TruncatedTextComponent, UPDATE_CONFIG_AGGREGATIONS, UPDATE_FILTERS, UPDATE_REQUEST_AGGREGATION_TERM, UpdateConfigAggregations, UpdateFilters, UrlInputComponent, UserFeedbackItemComponent, UserPreviewComponent, UserSearchField, VECTOR_STYLE_DEFAULT, ViewportIntersectorComponent, WEB_COMPONENT_EMBEDDER_URL, XmlParseError, _reset, allChildrenElement, appConfigWithTranslationFixture, appendChildTree, appendChildren, assertValidXml, associationTypeValues, bboxToPolygon, blockModelFixture, bytesToMegabytes, canEditRecord, checkFileFormat, clearSelectedFeatures, createChild, createDocument, createElement, createFuzzyFilter, createNestedChild, createNestedElement, createSpatialExtentLayer, currentPage, defaultMapStyleFixture, defaultMapStyleHlFixture, downgradeImage, downsizeImage, draftSaveSuccess, dragPanCondition, dropEmptyTranslations, editorReducer, emptyBlockModelFixture, findChildElement, findChildOrCreate, findChildrenElement, findConverterForDocument, findNestedChildOrCreate, findNestedElement, findNestedElements, findParent, firstChildElement, formatDate, formatUserInfo, getAddressLines, getAllKeysValidator, getArrayItem, getAsArray, getAsUrl, getBadgeColor, getCustomTranslations, getError, getFavoritesOnly, getFileFormat, getFileFormatFromServiceOutput, getFirstValue, getFormatPriority, getGeometryBoundingBox, getGeometryFromGeoJSON, getGlobalConfig, getIndividualDisplayName, getIsMobile, getJsonDataItemsProxy, getKeywordHierarchyPath, getLayers, getLinkId, getLinkLabel, getLinkPriority, getMapContext, getMapContextLayerFromConfig, getMapState, getMetadataQualityConfig, getMimeTypeForFormat, getNamespace, getOptionalEditorConfig, getOptionalMapConfig, getOptionalSearchConfig, getPageSize, getQualityValidators, getResourceType, getReusePresentationForm, getReuseType, getRootElement, getSearchConfigAggregations, getSearchFilters, getSearchResults, getSearchResultsAggregations, getSearchResultsHits, getSearchResultsLayout, getSearchResultsLoading, getSearchSortBy, getSearchState, getSearchStateSearch, getSelectedFeatures, getSpatialFilterEnabled, getTemporalRangeUnion, getThemeConfig, handleScrollOnNavigation, hasRecordChangedSinceDraft, hasRecordChangedSinceDraftSuccess, initSearch, initialEditorState, initialMapState, initialState, isBoundingBox, isConfigLoaded, isDateRange, isFileExtensionValid, isFormatInQueryParam, isPublished, itemModelFixture, kindToCodeListValue, loadAppConfig, malformedConfigFixture, mapConfigFixture, mapContact, mapKeywords, mapLogo, mapOrganization, mapReducer, markRecordAsChanged, matchesNoApplicableConstraint, matchesNoKnownConstraint, megabytesToBytes, mimeTypeToFormat, minimalAppConfigFixture, missingMandatoryConfigFixture, mouseWheelZoomCondition, noDuplicateFileName, okAppConfigFixture, openDataset, openRecord, organizationsServiceFactory, parse, parseXmlString, placeholder, prioritizePageScroll, propagateToDocumentOnly, provideGn4, provideI18n, provideLocalizedDateAdapter, provideRepositoryUrl, readAttribute, readDataset, readDatasetHeaders, readFileAsText, readText, reducer$2 as reducer, reducerSearch, removeAllChildren, removeChildren, removeChildrenByName, removeSearchParams, removeWhitespace, renameElements, saveRecord, saveRecordFailure, saveRecordSuccess, selectCanEditRecord, selectCurrentPage, selectEditorConfig, selectEditorState, selectFallback, selectFallbackFields, selectField, selectHasRecordChanged, selectIsPublished, selectRecord, selectRecordChangedSinceSave, selectRecordSaveError, selectRecordSaving, selectRecordSections, selectRecordSource, selectTranslatedField, selectTranslatedValue, setContext, setCurrentPage, setEditorConfiguration, setFieldVisibility, setFocusedField, setSelectedFeatures, setTextContent, sortByFromString, sortByToString, sortByToStrings, spatialExtentToGeometry, spatialExtentsToFeatureCollection, stripHtml, stripNamespace, toDate, toIndividual, toLang2, toLang3, totalPages, undoRecordDraft, unrecognizedKeysConfigFixture, updateFrequencyCodeValues, updateLanguages, updateRecordField, updateRecordLanguages, wmsLayerFlatten, writeAttribute, wrongLanguageCodeConfigFixture, xmlToString };
|
|
40381
40956
|
//# sourceMappingURL=geonetwork-ui.mjs.map
|