geonetwork-ui 2.11.0-dev.96d0c6ce2 → 2.11.0-dev.9ba9c4d3d
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 +1243 -687
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +227 -81
- package/index.d.ts.map +1 -1
- package/package.json +12 -11
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +43 -13
- package/src/libs/api/metadata-converter/src/lib/gn4/types/elasticsearch.model.ts +13 -1
- package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +70 -10
- 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/model/search/sort-by.model.ts +1 -5
- package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +2 -2
- package/src/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts +3 -2
- package/src/libs/feature/dataviz/src/lib/geo-table-view/geo-table-view.component.ts +2 -1
- package/src/libs/feature/dataviz/src/lib/service/data.service.ts +22 -22
- package/src/libs/feature/map/src/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/router.service.ts +1 -1
- package/src/libs/feature/router/src/lib/default/state/query-params.utils.ts +1 -1
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +9 -4
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +46 -19
- package/src/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts +17 -8
- package/src/libs/feature/search/src/lib/sort-by/sort-by.component.ts +1 -1
- package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +3 -1
- package/src/libs/feature/search/src/lib/utils/service/fields.ts +27 -2
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.html +6 -7
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.ts +31 -19
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.fixtures.ts +2 -2
- package/src/libs/ui/inputs/src/index.ts +2 -0
- package/src/libs/ui/inputs/src/lib/date-adapter.providers.ts +30 -0
- package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +3 -24
- package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.css +37 -3
- package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.html +135 -16
- package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.ts +166 -29
- package/src/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.ts +3 -7
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.css +1 -0
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.html +20 -7
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.ts +46 -5
- package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.css +0 -0
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html +117 -0
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts +191 -0
- package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +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 +14 -3
- package/translations/en.json +13 -2
- package/translations/es.json +11 -0
- package/translations/fr.json +14 -3
- package/translations/it.json +12 -1
- package/translations/nl.json +11 -0
- package/translations/pt.json +11 -0
- package/translations/sk.json +12 -1
- package/src/libs/util/data-fetcher/src/lib/readers/base-cache.ts +0 -14
|
@@ -6,9 +6,9 @@ import GeoJSON from 'ol/format/GeoJSON.js';
|
|
|
6
6
|
import { parse as parse$1 } from 'ol/xml.js';
|
|
7
7
|
import { format } from 'date-fns/format';
|
|
8
8
|
import { Namespace, Literal, lit, parse as parse$2, sym, BlankNode, graph } from 'rdflib';
|
|
9
|
-
import { lastValueFrom, fromEvent, startWith, map as map$2, shareReplay, filter, pairwise, of, switchMap, Subject, combineLatest, from, exhaustMap, throwError,
|
|
9
|
+
import { lastValueFrom, fromEvent, startWith, map as map$2, shareReplay, filter, pairwise, of, switchMap, Subject, forkJoin, combineLatest, from, exhaustMap, throwError, takeLast, firstValueFrom, merge, BehaviorSubject, timer, ReplaySubject, Subscription, first, distinctUntilChanged as distinctUntilChanged$1, animationFrameScheduler, debounceTime as debounceTime$1, Observable, buffer, tap as tap$2, combineLatestWith, take as take$1, catchError as catchError$1, takeUntil, EMPTY, mergeMap as mergeMap$1, withLatestFrom as withLatestFrom$1 } from 'rxjs';
|
|
10
10
|
import * as i0 from '@angular/core';
|
|
11
|
-
import { InjectionToken, inject, Injectable, NgModule, Injector, provideAppInitializer, makeEnvironmentProviders, ElementRef,
|
|
11
|
+
import { InjectionToken, inject, Injectable, NgModule, Injector, provideAppInitializer, makeEnvironmentProviders, ElementRef, afterNextRender, booleanAttribute, Input, Directive, HostListener, Renderer2, DestroyRef, EventEmitter, Output, ViewChild, ChangeDetectionStrategy, Component, ViewEncapsulation, ChangeDetectorRef, HostBinding, ViewContainerRef, TemplateRef, ViewChildren, ContentChild, input, output, signal, viewChild, computed, ContentChildren, NgZone, ComponentFactoryResolver, viewChildren } from '@angular/core';
|
|
12
12
|
import { HttpClient, HttpHeaders, HttpParams, HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi, HttpEventType } from '@angular/common/http';
|
|
13
13
|
import * as i4 from '@ngx-translate/core';
|
|
14
14
|
import { TranslateLoader, TranslateCompiler, TranslateDefaultParser, TranslateParser, TranslateService, provideTranslateService, TranslateDirective, TranslatePipe, TranslateModule } from '@ngx-translate/core';
|
|
@@ -18,7 +18,7 @@ import { map as map$1, shareReplay as shareReplay$1, catchError, tap as tap$1, f
|
|
|
18
18
|
import { lt, valid, coerce, satisfies, ltr } from 'semver';
|
|
19
19
|
import chroma from 'chroma-js';
|
|
20
20
|
import * as i1$1 from '@angular/common';
|
|
21
|
-
import { Location, CommonModule,
|
|
21
|
+
import { Location, CommonModule, NgTemplateOutlet, NgClass, AsyncPipe } from '@angular/common';
|
|
22
22
|
import { formatDistance } from 'date-fns/formatDistance';
|
|
23
23
|
import { Scroll, NavigationEnd, Router, RouteReuseStrategy } from '@angular/router';
|
|
24
24
|
import { WmtsEndpoint, WmsEndpoint, WfsEndpoint, OgcApiEndpoint, sharedFetch, useCache, StacEndpoint, TmsEndpoint } from '@camptocamp/ogc-client';
|
|
@@ -41,10 +41,10 @@ import * as TOML from '@ltd/j-toml';
|
|
|
41
41
|
import { Style, Stroke, Fill, Circle } from 'ol/style.js';
|
|
42
42
|
import CircleStyle from 'ol/style/Circle.js';
|
|
43
43
|
import EmblaCarousel from 'embla-carousel';
|
|
44
|
-
import { iconoirNavArrowLeft, iconoirNavArrowRight, iconoirLongArrowDownLeft, iconoirSearch, iconoirCalendar, iconoirArrowUp, iconoirLink, iconoirFramePlusIn, iconoirCloudUpload, iconoirReduce, iconoirNavArrowUp, iconoirNavArrowDown, iconoirExpand, iconoirSettings, iconoirDownload, iconoirPlus, iconoirBin, iconoirMediaImageXmark, iconoirMediaImage, iconoirAppWindow, iconoirCode, iconoirDatabase, iconoirAppleWallet, iconoirBank, iconoirList, iconoirTranslate, iconoirLock, iconoirUser, iconoirArrowLeft, iconoirLightBulbOn,
|
|
44
|
+
import { iconoirNavArrowLeft, iconoirNavArrowRight, iconoirLongArrowDownLeft, iconoirSearch, iconoirCalendar, iconoirArrowUp, iconoirLink, iconoirFramePlusIn, iconoirCloudUpload, iconoirTrash, iconoirSquareDashed, iconoirImport, iconoirCheckCircle, iconoirReduce, iconoirNavArrowUp, iconoirNavArrowDown, iconoirExpand, iconoirSettings, iconoirDownload, iconoirPlus, iconoirBin, iconoirMediaImageXmark, iconoirMediaImage, iconoirAppWindow, iconoirCode, iconoirDatabase, iconoirAppleWallet, iconoirBank, iconoirList, iconoirTranslate, iconoirLock, iconoirUser, iconoirArrowLeft, iconoirLightBulbOn, iconoirBadgeCheck, iconoirSystemShut, iconoirCircle, iconoirAttachment, iconoirRefresh } from '@ng-icons/iconoir';
|
|
45
45
|
import { MatButtonModule } from '@angular/material/button';
|
|
46
|
-
import * as i1$
|
|
47
|
-
import {
|
|
46
|
+
import * as i1$7 from '@angular/cdk/overlay';
|
|
47
|
+
import { ScrollStrategyOptions, OverlayModule, CdkConnectedOverlay, ScrollDispatcher, Overlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
|
|
48
48
|
import * as i1$3 from '@angular/forms';
|
|
49
49
|
import { UntypedFormControl, ReactiveFormsModule, FormsModule } from '@angular/forms';
|
|
50
50
|
import * as i1$2 from '@angular/material/autocomplete';
|
|
@@ -52,16 +52,18 @@ import { MatAutocompleteModule, MatAutocompleteTrigger, MatAutocomplete } from '
|
|
|
52
52
|
import tippy from 'tippy.js';
|
|
53
53
|
import * as i2 from '@angular/material/progress-spinner';
|
|
54
54
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
55
|
-
import { matClose, matContentCopy,
|
|
55
|
+
import { matClose, matContentCopy, matExpandMore, matExpandLess, matSearch, matStarBorder, matStar, matRemove, matAdd, matChevronRight, matChevronLeft, matArrowForwardIos, matArrowBackIos, matMoodBad, matQuestionMark, matFace, matZoomOutMap, matOpenInNew, matPersonOutline, matMailOutline, matWarningAmber, matCheck, matCheckCircleOutline, matWarning, matMoreVert } from '@ng-icons/material-icons/baseline';
|
|
56
56
|
import * as i1$4 from '@angular/material/checkbox';
|
|
57
57
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
58
58
|
import * as i1$5 from '@angular/material/tooltip';
|
|
59
59
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
60
|
-
import {
|
|
60
|
+
import { MAT_DATE_LOCALE, MAT_DATE_FORMATS, DateAdapter } from '@angular/material/core';
|
|
61
|
+
import { MAT_DATE_FNS_FORMATS, DateFnsAdapter } from '@angular/material-date-fns-adapter';
|
|
62
|
+
import { enUS } from 'date-fns/locale/en-US';
|
|
61
63
|
import * as i1$6 from '@angular/material/datepicker';
|
|
62
64
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
|
63
|
-
import * as i1$
|
|
64
|
-
import { NgxDropzoneModule } from 'ngx-dropzone';
|
|
65
|
+
import * as i1$8 from 'ngx-dropzone';
|
|
66
|
+
import { NgxDropzoneModule, NgxDropzoneComponent } from 'ngx-dropzone';
|
|
65
67
|
import * as i1$9 from '@angular/material/button-toggle';
|
|
66
68
|
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
|
67
69
|
import { moveItemInArray, CdkDropList, CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop';
|
|
@@ -79,10 +81,10 @@ import { ScrollingModule, CdkScrollable } from '@angular/cdk/scrolling';
|
|
|
79
81
|
import Duration from 'duration-relativetimeformat';
|
|
80
82
|
import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
|
|
81
83
|
import { trigger, transition, animate, keyframes, style } from '@angular/animations';
|
|
82
|
-
import * as
|
|
83
|
-
import {
|
|
84
|
-
import
|
|
85
|
-
import
|
|
84
|
+
import * as duckdb from '@duckdb/duckdb-wasm';
|
|
85
|
+
import { Vector, DataType } from 'apache-arrow';
|
|
86
|
+
import WFS from 'ol/format/WFS';
|
|
87
|
+
import GeoJSON$1 from 'ol/format/GeoJSON';
|
|
86
88
|
import { Chart, BarController, BarElement, CategoryScale, LinearScale, LineController, LineElement, PointElement, PieController, ArcElement, ScatterController, Tooltip, Colors, Legend } from 'chart.js';
|
|
87
89
|
import * as i2$4 from '@angular/material/sort';
|
|
88
90
|
import { MatSortModule, MatSort } from '@angular/material/sort';
|
|
@@ -90,7 +92,6 @@ import * as i1$b from '@angular/material/table';
|
|
|
90
92
|
import { MatTableModule } from '@angular/material/table';
|
|
91
93
|
import * as i3 from '@angular/material/paginator';
|
|
92
94
|
import { MatPaginatorIntl, MatPaginatorModule, MatPaginator } from '@angular/material/paginator';
|
|
93
|
-
import { LetDirective } from '@ngrx/component';
|
|
94
95
|
import { Meta } from '@angular/platform-browser';
|
|
95
96
|
import { tablerFolderOpen } from '@ng-icons/tabler-icons';
|
|
96
97
|
import * as i3$1 from '@angular/material/radio';
|
|
@@ -18907,14 +18908,29 @@ class Gn4FieldMapper {
|
|
|
18907
18908
|
featureTypes: selectField(source, 'featureTypes'),
|
|
18908
18909
|
}, output),
|
|
18909
18910
|
related: (output, source) => {
|
|
18910
|
-
const
|
|
18911
|
+
const related = selectField(source, 'related');
|
|
18912
|
+
const fcatSource = selectField(getFirstValue(selectField(related, 'fcats')) ?? {}, '_source');
|
|
18911
18913
|
const featureCatalogIdentifier = selectField(fcatSource, 'uuid');
|
|
18912
|
-
const sourceOfLinks = getAsArray(selectField(
|
|
18914
|
+
const sourceOfLinks = getAsArray(selectField(related, 'hassources'));
|
|
18913
18915
|
const sourceOfIdentifiers = sourceOfLinks
|
|
18914
18916
|
.filter((link) => link['origin'] === 'catalog')
|
|
18915
18917
|
.map((link) => {
|
|
18916
18918
|
return selectField(selectField(link, '_source'), 'uuid');
|
|
18917
18919
|
});
|
|
18920
|
+
const siblingLinks = getAsArray(selectField(related, 'siblings'));
|
|
18921
|
+
const siblings = siblingLinks
|
|
18922
|
+
.filter((link) => link['origin'] === 'catalog')
|
|
18923
|
+
.map((link) => ({
|
|
18924
|
+
uniqueIdentifier: selectField(selectField(link, '_source'), 'uuid'),
|
|
18925
|
+
associationType: getAssociationTypeFromCode(selectField(selectField(link, 'properties'), 'associationType')),
|
|
18926
|
+
}));
|
|
18927
|
+
const associatedLinks = getAsArray(selectField(related, 'associated'));
|
|
18928
|
+
const associatedIdentifiers = associatedLinks
|
|
18929
|
+
.filter((link) => link['origin'] === 'catalog')
|
|
18930
|
+
.map((link) => {
|
|
18931
|
+
return selectField(selectField(link, '_source'), 'uuid');
|
|
18932
|
+
})
|
|
18933
|
+
.filter((uuid) => !siblings.some((sibling) => sibling.uniqueIdentifier === uuid));
|
|
18918
18934
|
const extraValues = {};
|
|
18919
18935
|
if (featureCatalogIdentifier) {
|
|
18920
18936
|
extraValues.featureCatalogIdentifier = featureCatalogIdentifier;
|
|
@@ -18922,6 +18938,12 @@ class Gn4FieldMapper {
|
|
|
18922
18938
|
if (sourceOfIdentifiers && sourceOfIdentifiers.length > 0) {
|
|
18923
18939
|
extraValues.sourceOfIdentifiers = sourceOfIdentifiers;
|
|
18924
18940
|
}
|
|
18941
|
+
if (associatedIdentifiers && associatedIdentifiers.length > 0) {
|
|
18942
|
+
extraValues.associatedIdentifiers = associatedIdentifiers;
|
|
18943
|
+
}
|
|
18944
|
+
if (siblings && siblings.length > 0) {
|
|
18945
|
+
extraValues.siblings = siblings;
|
|
18946
|
+
}
|
|
18925
18947
|
return Object.keys(extraValues).length > 0
|
|
18926
18948
|
? this.addExtra(extraValues, output)
|
|
18927
18949
|
: output;
|
|
@@ -19447,10 +19469,10 @@ class DateService {
|
|
|
19447
19469
|
const dateObj = this.getDateObject(date);
|
|
19448
19470
|
return { locale, dateObj };
|
|
19449
19471
|
}
|
|
19450
|
-
async
|
|
19472
|
+
async getDateFnsLocale() {
|
|
19451
19473
|
const lang = this.translateService.getCurrentLang() || DEFAULT_LANGUAGE;
|
|
19452
19474
|
const locales = await this.dateLocales;
|
|
19453
|
-
return locales[lang];
|
|
19475
|
+
return locales[lang] ?? locales[DEFAULT_LANGUAGE];
|
|
19454
19476
|
}
|
|
19455
19477
|
formatDate(date, options) {
|
|
19456
19478
|
const { locale, dateObj } = this.getLocaleAndDate(date);
|
|
@@ -19463,7 +19485,7 @@ class DateService {
|
|
|
19463
19485
|
async formatRelativeDateTime(date) {
|
|
19464
19486
|
const dateObj = this.getDateObject(date);
|
|
19465
19487
|
const now = new Date();
|
|
19466
|
-
const locale = await this.
|
|
19488
|
+
const locale = await this.getDateFnsLocale();
|
|
19467
19489
|
return formatDistance(dateObj, now, {
|
|
19468
19490
|
addSuffix: true,
|
|
19469
19491
|
locale: locale,
|
|
@@ -19498,6 +19520,18 @@ function propagateToDocumentOnly(event) {
|
|
|
19498
19520
|
}, 0);
|
|
19499
19521
|
}
|
|
19500
19522
|
|
|
19523
|
+
function isFileExtensionValid(fileName, acceptedExtensions) {
|
|
19524
|
+
return acceptedExtensions.some((ext) => fileName.toLowerCase().endsWith(ext));
|
|
19525
|
+
}
|
|
19526
|
+
function readFileAsText(file) {
|
|
19527
|
+
return new Promise((resolve, reject) => {
|
|
19528
|
+
const reader = new FileReader();
|
|
19529
|
+
reader.onload = () => resolve(reader.result);
|
|
19530
|
+
reader.onerror = () => reject(reader.error);
|
|
19531
|
+
reader.readAsText(file);
|
|
19532
|
+
});
|
|
19533
|
+
}
|
|
19534
|
+
|
|
19501
19535
|
function formatUserInfo(userInfo, displayCount = false) {
|
|
19502
19536
|
const infos = (typeof userInfo === 'string' ? userInfo : '').split('|');
|
|
19503
19537
|
const count = displayCount ? ` ${infos[3].split(' ')[1]}` : '';
|
|
@@ -19557,6 +19591,11 @@ function getGeometryFromGeoJSON(data) {
|
|
|
19557
19591
|
}
|
|
19558
19592
|
return null;
|
|
19559
19593
|
}
|
|
19594
|
+
function isBoundingBox(value) {
|
|
19595
|
+
return (Array.isArray(value) &&
|
|
19596
|
+
value.length === 4 &&
|
|
19597
|
+
value.every((item) => typeof item === 'number'));
|
|
19598
|
+
}
|
|
19560
19599
|
function getGeometryBoundingBox(geometry) {
|
|
19561
19600
|
// use the bounding box if specified in the GeoJSON object
|
|
19562
19601
|
if (geometry.bbox) {
|
|
@@ -20342,6 +20381,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
20342
20381
|
}]
|
|
20343
20382
|
}] });
|
|
20344
20383
|
|
|
20384
|
+
class AutofocusDirective {
|
|
20385
|
+
constructor() {
|
|
20386
|
+
this.el = inject(ElementRef);
|
|
20387
|
+
this.injector = inject(Injector);
|
|
20388
|
+
}
|
|
20389
|
+
set autofocus(active) {
|
|
20390
|
+
if (!active)
|
|
20391
|
+
return;
|
|
20392
|
+
afterNextRender(() => this.el.nativeElement.focus(), {
|
|
20393
|
+
injector: this.injector,
|
|
20394
|
+
});
|
|
20395
|
+
}
|
|
20396
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AutofocusDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
20397
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "20.3.19", type: AutofocusDirective, isStandalone: true, selector: "[gnUiAutofocus]", inputs: { autofocus: ["gnUiAutofocus", "autofocus", booleanAttribute] }, ngImport: i0 }); }
|
|
20398
|
+
}
|
|
20399
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AutofocusDirective, decorators: [{
|
|
20400
|
+
type: Directive,
|
|
20401
|
+
args: [{
|
|
20402
|
+
selector: '[gnUiAutofocus]',
|
|
20403
|
+
standalone: true,
|
|
20404
|
+
}]
|
|
20405
|
+
}], propDecorators: { autofocus: [{
|
|
20406
|
+
type: Input,
|
|
20407
|
+
args: [{ alias: 'gnUiAutofocus', transform: booleanAttribute }]
|
|
20408
|
+
}] } });
|
|
20409
|
+
|
|
20345
20410
|
class ImageFallbackDirective {
|
|
20346
20411
|
constructor() {
|
|
20347
20412
|
this.el = inject(ElementRef);
|
|
@@ -20369,7 +20434,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
20369
20434
|
}] } });
|
|
20370
20435
|
|
|
20371
20436
|
var name = "geonetwork-ui";
|
|
20372
|
-
var version = "2.11.0-dev.
|
|
20437
|
+
var version = "2.11.0-dev.9ba9c4d3d";
|
|
20373
20438
|
var engines = {
|
|
20374
20439
|
node: ">=24"
|
|
20375
20440
|
};
|
|
@@ -20390,6 +20455,7 @@ var peerDependencies = {
|
|
|
20390
20455
|
"@angular/core": "19.x || 20.x || 21.x",
|
|
20391
20456
|
"@angular/forms": "19.x || 20.x || 21.x",
|
|
20392
20457
|
"@angular/material": "19.x || 20.x || 21.x",
|
|
20458
|
+
"@angular/material-date-fns-adapter": "19.x || 20.x",
|
|
20393
20459
|
"@angular/platform-browser": "19.x || 20.x || 21.x",
|
|
20394
20460
|
"@angular/platform-browser-dynamic": "19.x || 20.x || 21.x",
|
|
20395
20461
|
"@angular/router": "19.x || 20.x || 21.x",
|
|
@@ -20399,20 +20465,21 @@ var peerDependencies = {
|
|
|
20399
20465
|
"@ngrx/store": "19.x || 20.x || 21.x",
|
|
20400
20466
|
"@ngrx/store-devtools": "19.x || 20.x || 21.x",
|
|
20401
20467
|
"@ngrx/operators": "19.x || 20.x || 21.x",
|
|
20402
|
-
"@ngx-translate/core": "
|
|
20403
|
-
"@ngx-translate/http-loader": "
|
|
20404
|
-
"flag-icons": "
|
|
20468
|
+
"@ngx-translate/core": "17.x",
|
|
20469
|
+
"@ngx-translate/http-loader": "17.x",
|
|
20470
|
+
"flag-icons": "~7.5.0",
|
|
20405
20471
|
rxjs: "7.x",
|
|
20406
20472
|
"zone.js": "*",
|
|
20407
20473
|
tailwindcss: "3.x"
|
|
20408
20474
|
};
|
|
20409
20475
|
var dependencies = {
|
|
20410
20476
|
"@biesbjerg/ngx-translate-extract-marker": "~1.0.0",
|
|
20411
|
-
"@camptocamp/ogc-client": "1.3.1-dev.
|
|
20412
|
-
"@
|
|
20413
|
-
"@geospatial-sdk/
|
|
20414
|
-
"@geospatial-sdk/
|
|
20415
|
-
"@geospatial-sdk/
|
|
20477
|
+
"@camptocamp/ogc-client": "1.3.1-dev.200c02a",
|
|
20478
|
+
"@duckdb/duckdb-wasm": "~1.32.0",
|
|
20479
|
+
"@geospatial-sdk/core": "0.0.5-dev.78",
|
|
20480
|
+
"@geospatial-sdk/geocoding": "0.0.5-dev.78",
|
|
20481
|
+
"@geospatial-sdk/legend": "0.0.5-dev.78",
|
|
20482
|
+
"@geospatial-sdk/openlayers": "0.0.5-dev.78",
|
|
20416
20483
|
"@ltd/j-toml": "~1.38.0",
|
|
20417
20484
|
"@messageformat/core": "~3.4.0",
|
|
20418
20485
|
"@ng-icons/core": "~32.5.0",
|
|
@@ -20420,7 +20487,7 @@ var dependencies = {
|
|
|
20420
20487
|
"@ng-icons/material-icons": "~32.5.0",
|
|
20421
20488
|
"@ng-icons/tabler-icons": "~32.4.0",
|
|
20422
20489
|
"@rgrove/parse-xml": "4.2.0",
|
|
20423
|
-
|
|
20490
|
+
"apache-arrow": "~17.0.0",
|
|
20424
20491
|
"chart.js": "~4.5.1",
|
|
20425
20492
|
"chroma-js": "~3.2.0",
|
|
20426
20493
|
"date-fns": "4.1.0",
|
|
@@ -20433,7 +20500,6 @@ var dependencies = {
|
|
|
20433
20500
|
"ngx-dropzone": "~3.1.0",
|
|
20434
20501
|
"ngx-translate-messageformat-compiler": "~7.2.0",
|
|
20435
20502
|
ol: "~10.8.0",
|
|
20436
|
-
papaparse: "~5.5.3",
|
|
20437
20503
|
proj4: "~2.20.4",
|
|
20438
20504
|
rdflib: "~2.3.5",
|
|
20439
20505
|
semver: "~7.7.4",
|
|
@@ -20678,6 +20744,7 @@ class ElasticsearchService {
|
|
|
20678
20744
|
values: uuids,
|
|
20679
20745
|
},
|
|
20680
20746
|
},
|
|
20747
|
+
size: uuids.length,
|
|
20681
20748
|
};
|
|
20682
20749
|
}
|
|
20683
20750
|
getRelatedRecordPayload(record, size = 6, _source = [...ES_SOURCE_SUMMARY, 'createDate']) {
|
|
@@ -20728,10 +20795,28 @@ class ElasticsearchService {
|
|
|
20728
20795
|
};
|
|
20729
20796
|
}
|
|
20730
20797
|
buildPayloadSort(sortBy) {
|
|
20731
|
-
if (sortBy ===
|
|
20798
|
+
if (!sortBy || sortBy.length === 0)
|
|
20732
20799
|
return undefined;
|
|
20733
|
-
const fields = Array.isArray(sortBy[0])
|
|
20734
|
-
|
|
20800
|
+
const fields = Array.isArray(sortBy[0])
|
|
20801
|
+
? sortBy
|
|
20802
|
+
: [sortBy];
|
|
20803
|
+
return fields.map((field) => {
|
|
20804
|
+
// Sort by nested array of dates only works with the explicit syntax
|
|
20805
|
+
if (field[1].endsWith('.date')) {
|
|
20806
|
+
const nestedPath = field[1].slice(0, field[1].lastIndexOf('.date'));
|
|
20807
|
+
return {
|
|
20808
|
+
[field[1]]: {
|
|
20809
|
+
order: field[0],
|
|
20810
|
+
mode: field[0] === 'desc' ? 'max' : 'min',
|
|
20811
|
+
missing: '_last',
|
|
20812
|
+
nested: {
|
|
20813
|
+
path: nestedPath,
|
|
20814
|
+
},
|
|
20815
|
+
},
|
|
20816
|
+
};
|
|
20817
|
+
}
|
|
20818
|
+
return { [field[1]]: field[0] };
|
|
20819
|
+
});
|
|
20735
20820
|
}
|
|
20736
20821
|
injectLangInQueryStringFields(queryFieldsPriority) {
|
|
20737
20822
|
const queryLang = this.getQueryLang();
|
|
@@ -20767,7 +20852,13 @@ class ElasticsearchService {
|
|
|
20767
20852
|
isCurrentSearchLang() {
|
|
20768
20853
|
return this.metadataLang === 'current';
|
|
20769
20854
|
}
|
|
20770
|
-
|
|
20855
|
+
findSpatialFilterExtent(filters) {
|
|
20856
|
+
if (typeof filters === 'string') {
|
|
20857
|
+
return undefined;
|
|
20858
|
+
}
|
|
20859
|
+
return Object.values(filters).find(isBoundingBox);
|
|
20860
|
+
}
|
|
20861
|
+
filtersToQuery(filters, spatialFilterExtent = this.findSpatialFilterExtent(filters)) {
|
|
20771
20862
|
const addQuote = (key) => (/^\/.+\/$/.test(key) ? key : `"${key}"`);
|
|
20772
20863
|
const makeQuery = (filter) => {
|
|
20773
20864
|
if (typeof filter === 'string') {
|
|
@@ -20786,7 +20877,9 @@ class ElasticsearchService {
|
|
|
20786
20877
|
? filters
|
|
20787
20878
|
: Object.keys(filters)
|
|
20788
20879
|
.filter((fieldname) => fieldname !== 'gn-ui-crossFieldFilter')
|
|
20880
|
+
.filter((fieldname) => !isBoundingBox(filters[fieldname]))
|
|
20789
20881
|
.filter((fieldname) => !isDateRange(filters[fieldname]))
|
|
20882
|
+
.filter((fieldname) => !Array.isArray(filters[fieldname]))
|
|
20790
20883
|
.filter((fieldname) => filters[fieldname] &&
|
|
20791
20884
|
JSON.stringify(filters[fieldname]) !== '{}')
|
|
20792
20885
|
.map((fieldname) => `${fieldname}:(${makeQuery(filters[fieldname])})`)
|
|
@@ -20822,6 +20915,21 @@ class ElasticsearchService {
|
|
|
20822
20915
|
},
|
|
20823
20916
|
},
|
|
20824
20917
|
},
|
|
20918
|
+
spatialFilterExtent && {
|
|
20919
|
+
geo_shape: {
|
|
20920
|
+
geom: {
|
|
20921
|
+
shape: {
|
|
20922
|
+
type: 'envelope',
|
|
20923
|
+
// spatialFilterExtent is [minX, minY, maxX, maxY]; envelope coordinates are [top-left, bottom-right]
|
|
20924
|
+
coordinates: [
|
|
20925
|
+
[spatialFilterExtent[0], spatialFilterExtent[3]],
|
|
20926
|
+
[spatialFilterExtent[2], spatialFilterExtent[1]],
|
|
20927
|
+
],
|
|
20928
|
+
},
|
|
20929
|
+
relation: 'intersects',
|
|
20930
|
+
},
|
|
20931
|
+
},
|
|
20932
|
+
},
|
|
20825
20933
|
].filter(Boolean);
|
|
20826
20934
|
return queryParts.length > 0 ? queryParts : undefined;
|
|
20827
20935
|
}
|
|
@@ -20848,7 +20956,9 @@ class ElasticsearchService {
|
|
|
20848
20956
|
},
|
|
20849
20957
|
});
|
|
20850
20958
|
}
|
|
20851
|
-
|
|
20959
|
+
// a spatial extent filter takes precedence over the preference geometry for boosting
|
|
20960
|
+
const spatialFilterExtent = this.findSpatialFilterExtent(fieldSearchFilters);
|
|
20961
|
+
const queryFilters = this.filtersToQuery(fieldSearchFilters, spatialFilterExtent);
|
|
20852
20962
|
if (queryFilters) {
|
|
20853
20963
|
filter.push(...queryFilters);
|
|
20854
20964
|
}
|
|
@@ -20859,7 +20969,10 @@ class ElasticsearchService {
|
|
|
20859
20969
|
},
|
|
20860
20970
|
});
|
|
20861
20971
|
}
|
|
20862
|
-
|
|
20972
|
+
const boostGeometry = spatialFilterExtent
|
|
20973
|
+
? bboxToPolygon(spatialFilterExtent)
|
|
20974
|
+
: geometry;
|
|
20975
|
+
if (boostGeometry) {
|
|
20863
20976
|
// boosts applied using the filter geometry:
|
|
20864
20977
|
// * records completely within the geometry receive a boost of 5
|
|
20865
20978
|
// * records intersecting the geometry receive a boost of 2
|
|
@@ -20868,7 +20981,7 @@ class ElasticsearchService {
|
|
|
20868
20981
|
should.push({
|
|
20869
20982
|
geo_shape: {
|
|
20870
20983
|
geom: {
|
|
20871
|
-
shape:
|
|
20984
|
+
shape: boostGeometry,
|
|
20872
20985
|
relation: 'within',
|
|
20873
20986
|
},
|
|
20874
20987
|
boost: 5.0,
|
|
@@ -20876,7 +20989,7 @@ class ElasticsearchService {
|
|
|
20876
20989
|
}, {
|
|
20877
20990
|
geo_shape: {
|
|
20878
20991
|
geom: {
|
|
20879
|
-
shape:
|
|
20992
|
+
shape: boostGeometry,
|
|
20880
20993
|
relation: 'intersects',
|
|
20881
20994
|
},
|
|
20882
20995
|
boost: 2.0,
|
|
@@ -20885,7 +20998,7 @@ class ElasticsearchService {
|
|
|
20885
20998
|
// this will boost the results variably depending on their distance from the given geometry
|
|
20886
20999
|
// note: this takes into account the `location` field of a record; this is generally the center of all spatial extents
|
|
20887
21000
|
// combined, and thus the actual size/coverage of the record spatial extent isn't relevant here
|
|
20888
|
-
const bbox = getGeometryBoundingBox(
|
|
21001
|
+
const bbox = getGeometryBoundingBox(boostGeometry);
|
|
20889
21002
|
const center = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2];
|
|
20890
21003
|
const northToCenter = new LineString([
|
|
20891
21004
|
[center[0], bbox[3]],
|
|
@@ -21211,7 +21324,7 @@ class Gn4Repository {
|
|
|
21211
21324
|
}
|
|
21212
21325
|
getRecord(uniqueIdentifier) {
|
|
21213
21326
|
return this.gn4SearchApi
|
|
21214
|
-
.search('bucket', ['fcats', 'hassources'], JSON.stringify(this.gn4SearchHelper.getMetadataByIdsPayload([uniqueIdentifier])))
|
|
21327
|
+
.search('bucket', ['fcats', 'hassources', 'siblings', 'associated'], JSON.stringify(this.gn4SearchHelper.getMetadataByIdsPayload([uniqueIdentifier])))
|
|
21215
21328
|
.pipe(map$1((results) => results.hits.hits[0]), switchMap((record) => record ? this.gn4Mapper.readRecord(record) : of(null)));
|
|
21216
21329
|
}
|
|
21217
21330
|
getMultipleRecords(uniqueIdentifiers) {
|
|
@@ -21265,19 +21378,28 @@ class Gn4Repository {
|
|
|
21265
21378
|
.search('bucket', null, JSON.stringify(this.gn4SearchHelper.getRelatedRecordPayload(similarTo, 3)))
|
|
21266
21379
|
.pipe(switchMap((results) => this.gn4Mapper.readRecords(results.hits.hits)));
|
|
21267
21380
|
}
|
|
21268
|
-
|
|
21269
|
-
const
|
|
21270
|
-
|
|
21271
|
-
|
|
21272
|
-
|
|
21273
|
-
|
|
21274
|
-
|
|
21275
|
-
|
|
21276
|
-
|
|
21277
|
-
|
|
21278
|
-
|
|
21381
|
+
getLinkedRecords(record) {
|
|
21382
|
+
const siblings = (record.extras?.['siblings'] ?? []);
|
|
21383
|
+
const relations = [
|
|
21384
|
+
['source', (record.extras?.['sourcesIdentifiers'] ?? [])],
|
|
21385
|
+
['sourceOf', (record.extras?.['sourceOfIdentifiers'] ?? [])],
|
|
21386
|
+
['sibling', siblings.map(({ uniqueIdentifier }) => uniqueIdentifier)],
|
|
21387
|
+
[
|
|
21388
|
+
'associated',
|
|
21389
|
+
(record.extras?.['associatedIdentifiers'] ?? []),
|
|
21390
|
+
],
|
|
21391
|
+
];
|
|
21392
|
+
const requested = relations.filter(([, identifiers]) => identifiers.length > 0);
|
|
21393
|
+
if (requested.length === 0) {
|
|
21394
|
+
return of([]);
|
|
21279
21395
|
}
|
|
21280
|
-
return
|
|
21396
|
+
return forkJoin(requested.map(([relation, identifiers]) => this.getMultipleRecords(identifiers).pipe(map$1((records) => (records ?? []).map((record) => ({
|
|
21397
|
+
record,
|
|
21398
|
+
relation,
|
|
21399
|
+
associationType: relation === 'sibling'
|
|
21400
|
+
? siblings.find(({ uniqueIdentifier }) => uniqueIdentifier === record.uniqueIdentifier)?.associationType
|
|
21401
|
+
: undefined,
|
|
21402
|
+
}))), catchError(() => of([]))))).pipe(map$1((groups) => groups.flat()));
|
|
21281
21403
|
}
|
|
21282
21404
|
aggregate(params) {
|
|
21283
21405
|
// if aggregations are empty, return an empty object right away
|
|
@@ -22538,9 +22660,10 @@ const VECTOR_STYLE_DEFAULT = new InjectionToken('vectorStyleDefault', {
|
|
|
22538
22660
|
});
|
|
22539
22661
|
|
|
22540
22662
|
const DEFAULT_BASEMAP_LAYER = {
|
|
22541
|
-
type: '
|
|
22542
|
-
|
|
22543
|
-
|
|
22663
|
+
type: 'maplibre-style',
|
|
22664
|
+
styleUrl: `https://basemaps.cartocdn.com/gl/positron-gl-style/style.json`,
|
|
22665
|
+
clickable: false,
|
|
22666
|
+
hoverable: false,
|
|
22544
22667
|
};
|
|
22545
22668
|
const DEFAULT_VIEW = {
|
|
22546
22669
|
center: [0, 15],
|
|
@@ -23255,6 +23378,7 @@ function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
23255
23378
|
'search_preset',
|
|
23256
23379
|
'advanced_filters',
|
|
23257
23380
|
'limit',
|
|
23381
|
+
'spatial_extent_max_file_size',
|
|
23258
23382
|
], warnings, errors);
|
|
23259
23383
|
const parsedSearchParams = parseMultiConfigSection(parsed, 'search_preset', ['name'], ['sort', 'filters'], warnings, errors);
|
|
23260
23384
|
searchConfig =
|
|
@@ -23272,6 +23396,7 @@ function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
23272
23396
|
})),
|
|
23273
23397
|
ADVANCED_FILTERS: parsedSearchSection.advanced_filters,
|
|
23274
23398
|
LIMIT: parsedSearchSection.limit,
|
|
23399
|
+
SPATIAL_EXTENT_MAX_FILE_SIZE: parsedSearchSection.spatial_extent_max_file_size,
|
|
23275
23400
|
};
|
|
23276
23401
|
const parsedMetadataQualitySection = parseConfigSection(parsed, 'metadata-quality', [], ['enabled'], warnings, errors);
|
|
23277
23402
|
metadataQualityConfig =
|
|
@@ -24529,12 +24654,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24529
24654
|
type: Input
|
|
24530
24655
|
}] } });
|
|
24531
24656
|
|
|
24657
|
+
function provideLocalizedDateAdapter() {
|
|
24658
|
+
return [
|
|
24659
|
+
{ provide: MAT_DATE_LOCALE, useValue: enUS },
|
|
24660
|
+
{ provide: MAT_DATE_FORMATS, useValue: MAT_DATE_FNS_FORMATS },
|
|
24661
|
+
{
|
|
24662
|
+
provide: DateAdapter,
|
|
24663
|
+
useFactory: () => {
|
|
24664
|
+
const dateService = inject(DateService);
|
|
24665
|
+
const adapter = new DateFnsAdapter();
|
|
24666
|
+
dateService
|
|
24667
|
+
.getDateFnsLocale()
|
|
24668
|
+
.then((locale) => adapter.setLocale(locale));
|
|
24669
|
+
return adapter;
|
|
24670
|
+
},
|
|
24671
|
+
},
|
|
24672
|
+
];
|
|
24673
|
+
}
|
|
24674
|
+
|
|
24532
24675
|
class DatePickerComponent {
|
|
24533
24676
|
constructor() {
|
|
24534
|
-
this.dateAdapter = inject(DateAdapter);
|
|
24535
|
-
this.translate = inject(TranslateService);
|
|
24536
24677
|
this.dateChange = new EventEmitter();
|
|
24537
|
-
this.dateAdapter.setLocale(this.translate.getCurrentLang());
|
|
24538
24678
|
}
|
|
24539
24679
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
24540
24680
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: DatePickerComponent, isStandalone: true, selector: "gn-ui-date-picker", inputs: { date: "date" }, outputs: { dateChange: "dateChange" }, providers: [
|
|
@@ -24542,30 +24682,19 @@ class DatePickerComponent {
|
|
|
24542
24682
|
provideNgIconsConfig({
|
|
24543
24683
|
size: '1.5rem',
|
|
24544
24684
|
}),
|
|
24545
|
-
|
|
24546
|
-
|
|
24547
|
-
useFactory: (locale) => locale,
|
|
24548
|
-
},
|
|
24549
|
-
], ngImport: i0, template: "<span class=\"w-full inline-block relative\">\n <input\n class=\"gn-ui-text-input pr-[var(--text-padding)]\"\n [matDatepicker]=\"picker\"\n [value]=\"date\"\n (dateChange)=\"dateChange.emit($event.value)\"\n />\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n data-cy=\"date-picker-button\"\n extraClass=\"h-full\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-datepicker #picker></mat-datepicker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"], dependencies: [{ kind: "ngmodule", type: MatNativeDateModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i1$6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i1$6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
24685
|
+
provideLocalizedDateAdapter(),
|
|
24686
|
+
], ngImport: i0, template: "<span class=\"w-full inline-block relative\">\n <input\n class=\"gn-ui-text-input pr-[var(--text-padding)]\"\n [matDatepicker]=\"picker\"\n [value]=\"date\"\n (dateChange)=\"dateChange.emit($event.value)\"\n />\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n data-cy=\"date-picker-button\"\n extraClass=\"h-full\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-datepicker #picker></mat-datepicker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"], dependencies: [{ kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i1$6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i1$6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
24550
24687
|
}
|
|
24551
24688
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatePickerComponent, decorators: [{
|
|
24552
24689
|
type: Component,
|
|
24553
|
-
args: [{ selector: 'gn-ui-date-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
24554
|
-
MatNativeDateModule,
|
|
24555
|
-
MatDatepickerModule,
|
|
24556
|
-
ButtonComponent,
|
|
24557
|
-
NgIconComponent,
|
|
24558
|
-
], providers: [
|
|
24690
|
+
args: [{ selector: 'gn-ui-date-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [MatDatepickerModule, ButtonComponent, NgIconComponent], providers: [
|
|
24559
24691
|
provideIcons({ iconoirCalendar }),
|
|
24560
24692
|
provideNgIconsConfig({
|
|
24561
24693
|
size: '1.5rem',
|
|
24562
24694
|
}),
|
|
24563
|
-
|
|
24564
|
-
provide: MAT_DATE_LOCALE,
|
|
24565
|
-
useFactory: (locale) => locale,
|
|
24566
|
-
},
|
|
24695
|
+
provideLocalizedDateAdapter(),
|
|
24567
24696
|
], template: "<span class=\"w-full inline-block relative\">\n <input\n class=\"gn-ui-text-input pr-[var(--text-padding)]\"\n [matDatepicker]=\"picker\"\n [value]=\"date\"\n (dateChange)=\"dateChange.emit($event.value)\"\n />\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n data-cy=\"date-picker-button\"\n extraClass=\"h-full\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-datepicker #picker></mat-datepicker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"] }]
|
|
24568
|
-
}],
|
|
24697
|
+
}], propDecorators: { date: [{
|
|
24569
24698
|
type: Input
|
|
24570
24699
|
}], dateChange: [{
|
|
24571
24700
|
type: Output
|
|
@@ -24573,58 +24702,159 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24573
24702
|
|
|
24574
24703
|
class DateRangeDropdownComponent {
|
|
24575
24704
|
constructor() {
|
|
24576
|
-
this.
|
|
24705
|
+
this.scrollStrategies = inject(ScrollStrategyOptions);
|
|
24706
|
+
this.dateAdapter = inject(DateAdapter);
|
|
24707
|
+
this.dateFormats = inject(MAT_DATE_FORMATS);
|
|
24577
24708
|
this.cdr = inject(ChangeDetectorRef);
|
|
24578
|
-
this.
|
|
24579
|
-
this.
|
|
24580
|
-
this.
|
|
24709
|
+
this.dateRange = {};
|
|
24710
|
+
this.dateRangeChange = new EventEmitter();
|
|
24711
|
+
this.overlayPositions = [
|
|
24712
|
+
{
|
|
24713
|
+
originX: 'start',
|
|
24714
|
+
originY: 'bottom',
|
|
24715
|
+
overlayX: 'start',
|
|
24716
|
+
overlayY: 'top',
|
|
24717
|
+
offsetY: 8,
|
|
24718
|
+
},
|
|
24719
|
+
{
|
|
24720
|
+
originX: 'start',
|
|
24721
|
+
originY: 'top',
|
|
24722
|
+
overlayX: 'start',
|
|
24723
|
+
overlayY: 'bottom',
|
|
24724
|
+
offsetY: -8,
|
|
24725
|
+
},
|
|
24726
|
+
];
|
|
24727
|
+
this.scrollStrategy = this.scrollStrategies.reposition();
|
|
24728
|
+
this.overlayOpen = false;
|
|
24729
|
+
this.expandedBound = 'start';
|
|
24730
|
+
this.invalidBounds = { start: false, end: false };
|
|
24731
|
+
// redraw when the lazily loaded UI locale reaches the adapter
|
|
24732
|
+
this.dateAdapter.localeChanges
|
|
24733
|
+
.pipe(takeUntilDestroyed())
|
|
24734
|
+
.subscribe(() => this.cdr.markForCheck());
|
|
24581
24735
|
}
|
|
24582
|
-
|
|
24583
|
-
this.
|
|
24736
|
+
get startDate() {
|
|
24737
|
+
return this.dateRange.start;
|
|
24584
24738
|
}
|
|
24585
|
-
|
|
24586
|
-
|
|
24587
|
-
|
|
24588
|
-
|
|
24589
|
-
|
|
24590
|
-
|
|
24591
|
-
|
|
24592
|
-
|
|
24739
|
+
get endDate() {
|
|
24740
|
+
return this.dateRange.end;
|
|
24741
|
+
}
|
|
24742
|
+
get selectedDatesCount() {
|
|
24743
|
+
return (this.startDate ? 1 : 0) + (this.endDate ? 1 : 0);
|
|
24744
|
+
}
|
|
24745
|
+
openOverlay() {
|
|
24746
|
+
this.expandedBound = 'start';
|
|
24747
|
+
this.overlayOpen = true;
|
|
24748
|
+
}
|
|
24749
|
+
closeOverlay() {
|
|
24750
|
+
this.overlayOpen = false;
|
|
24751
|
+
}
|
|
24752
|
+
toggleBound(bound) {
|
|
24753
|
+
this.expandedBound = this.expandedBound === bound ? null : bound;
|
|
24754
|
+
}
|
|
24755
|
+
formatDate(date) {
|
|
24756
|
+
return this.dateAdapter.format(date, this.dateFormats.display.dateInput);
|
|
24757
|
+
}
|
|
24758
|
+
onDateInput(bound, event) {
|
|
24759
|
+
const typedText = event.target.value.trim();
|
|
24760
|
+
const date = typedText
|
|
24761
|
+
? this.dateAdapter.parse(typedText, this.dateFormats.parse.dateInput)
|
|
24762
|
+
: null;
|
|
24763
|
+
if (!this.isAcceptable(bound, typedText, date)) {
|
|
24764
|
+
// keep the text as typed and flag it; the filter stays on its last value
|
|
24765
|
+
this.invalidBounds = { ...this.invalidBounds, [bound]: true };
|
|
24766
|
+
return;
|
|
24767
|
+
}
|
|
24768
|
+
if (bound === 'start')
|
|
24769
|
+
this.setStartDate(date);
|
|
24770
|
+
else
|
|
24771
|
+
this.setEndDate(date);
|
|
24772
|
+
}
|
|
24773
|
+
normalizeDateInput(bound, event) {
|
|
24774
|
+
if (this.invalidBounds[bound])
|
|
24775
|
+
return;
|
|
24776
|
+
const date = bound === 'start' ? this.startDate : this.endDate;
|
|
24777
|
+
const input = event.target;
|
|
24778
|
+
input.value = date ? this.formatDate(date) : '';
|
|
24779
|
+
}
|
|
24780
|
+
isAcceptable(bound, typedText, date) {
|
|
24781
|
+
if (!typedText)
|
|
24782
|
+
return true;
|
|
24783
|
+
if (!date || !this.dateAdapter.isValid(date))
|
|
24784
|
+
return false;
|
|
24785
|
+
return bound === 'start'
|
|
24786
|
+
? !this.endDate || date <= this.endDate
|
|
24787
|
+
: !this.startDate || date >= this.startDate;
|
|
24788
|
+
}
|
|
24789
|
+
setStartDate(date) {
|
|
24790
|
+
this.invalidBounds = { ...this.invalidBounds, start: false };
|
|
24791
|
+
this.applyDateRange({ ...this.dateRange, start: date });
|
|
24792
|
+
this.expandedBound = 'end';
|
|
24793
|
+
}
|
|
24794
|
+
setEndDate(date) {
|
|
24795
|
+
this.invalidBounds = { ...this.invalidBounds, end: false };
|
|
24796
|
+
this.applyDateRange({ ...this.dateRange, end: date });
|
|
24797
|
+
this.expandedBound = null;
|
|
24798
|
+
}
|
|
24799
|
+
clearDates(event) {
|
|
24800
|
+
this.invalidBounds = { start: false, end: false };
|
|
24801
|
+
this.expandedBound = 'start';
|
|
24802
|
+
this.applyDateRange({});
|
|
24803
|
+
propagateToDocumentOnly(event);
|
|
24804
|
+
}
|
|
24805
|
+
applyDateRange(dateRange) {
|
|
24806
|
+
this.dateRange = {
|
|
24807
|
+
...(dateRange.start && { start: dateRange.start }),
|
|
24808
|
+
...(dateRange.end && { end: dateRange.end }),
|
|
24809
|
+
};
|
|
24810
|
+
this.dateRangeChange.emit(this.dateRange);
|
|
24593
24811
|
}
|
|
24594
24812
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangeDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
24595
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
24813
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DateRangeDropdownComponent, isStandalone: true, selector: "gn-ui-date-range-dropdown", inputs: { title: "title", dateRange: "dateRange" }, outputs: { dateRangeChange: "dateRangeChange" }, providers: [
|
|
24596
24814
|
provideIcons({
|
|
24597
|
-
|
|
24815
|
+
iconoirCalendar,
|
|
24816
|
+
matClose,
|
|
24598
24817
|
matExpandLess,
|
|
24818
|
+
matExpandMore,
|
|
24599
24819
|
}),
|
|
24600
|
-
|
|
24820
|
+
provideNgIconsConfig({
|
|
24821
|
+
size: '1.5rem',
|
|
24822
|
+
}),
|
|
24823
|
+
provideLocalizedDateAdapter(),
|
|
24824
|
+
], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }], ngImport: i0, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"openOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (selectedDatesCount) {\n <div\n class=\"gn-ui-multiselect-counter shrink-0 rounded-full font-bold text-[12px] w-5 h-5 flex items-center justify-center mr-1 selected-count\"\n >\n {{ selectedDatesCount }}\n </div>\n }\n </div>\n <button\n class=\"h-6 w-6 flex items-center justify-center\"\n data-test=\"dropdown-clear\"\n >\n @if (selectedDatesCount && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"clearDates($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n data-test=\"dropdown-toggle\"\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"w-[296px] flex flex-col gap-2 bg-white border border-gray-400 rounded px-4 py-3 shadow-lg text-main text-[16px] leading-[24px]\"\n data-test=\"date-range-panel\"\n >\n <div class=\"flex flex-col justify-center border-b border-gray-300 pb-1\">\n <ng-container\n [ngTemplateOutlet]=\"boundRow\"\n [ngTemplateOutletContext]=\"{\n bound: 'start',\n label: 'daterange.filter.from',\n date: startDate,\n }\"\n ></ng-container>\n @if (expandedBound === 'start') {\n <div class=\"calendar\" animate.enter=\"expand\" animate.leave=\"collapse\">\n <mat-calendar\n data-test=\"start-date-calendar\"\n [selected]=\"startDate\"\n [startAt]=\"startDate ?? endDate\"\n [maxDate]=\"endDate\"\n (selectedChange)=\"setStartDate($event)\"\n ></mat-calendar>\n </div>\n }\n </div>\n <div class=\"flex flex-col justify-center\">\n <ng-container\n [ngTemplateOutlet]=\"boundRow\"\n [ngTemplateOutletContext]=\"{\n bound: 'end',\n label: 'daterange.filter.to',\n date: endDate,\n }\"\n ></ng-container>\n @if (expandedBound === 'end') {\n <div class=\"calendar\" animate.enter=\"expand\" animate.leave=\"collapse\">\n <mat-calendar\n data-test=\"end-date-calendar\"\n [selected]=\"endDate\"\n [startAt]=\"endDate ?? startDate\"\n [minDate]=\"startDate\"\n (selectedChange)=\"setEndDate($event)\"\n ></mat-calendar>\n </div>\n }\n </div>\n </div>\n</ng-template>\n\n<ng-template #boundRow let-bound=\"bound\" let-label=\"label\" let-date=\"date\">\n <div class=\"flex items-center gap-1 w-full\">\n <span class=\"w-[80px] shrink-0\" [translate]=\"label\"></span>\n <div\n class=\"flex-1 min-w-0 flex items-center gap-1 border rounded-lg px-2 py-1 transition-colors\"\n [class]=\"\n invalidBounds[bound]\n ? 'border-red-400 text-red-800 focus-within:border-red-800 hover:border-red-800'\n : 'border-gray-400 focus-within:border-main hover:border-main'\n \"\n >\n <ng-icon\n name=\"iconoirCalendar\"\n class=\"shrink-0\"\n aria-hidden=\"true\"\n ></ng-icon>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n autocomplete=\"off\"\n [gnUiAutofocus]=\"bound === expandedBound\"\n class=\"min-w-0 grow bg-transparent focus:outline-none\"\n [value]=\"date ? formatDate(date) : ''\"\n [placeholder]=\"'daterange.filter.datePlaceholder' | translate\"\n [attr.aria-label]=\"label | translate\"\n [attr.aria-invalid]=\"invalidBounds[bound] || null\"\n [attr.data-test]=\"bound + '-date-input'\"\n (change)=\"onDateInput(bound, $event)\"\n (blur)=\"normalizeDateInput(bound, $event)\"\n (click)=\"expandedBound !== bound && toggleBound(bound)\"\n />\n </div>\n <div class=\"shrink-0 flex items-center justify-end\">\n <gn-ui-button\n type=\"light\"\n [style.--gn-ui-button-padding]=\"0\"\n [style.--gn-ui-button-width]=\"'24px'\"\n [style.--gn-ui-button-height]=\"'24px'\"\n [attr.aria-expanded]=\"expandedBound === bound\"\n [attr.data-test]=\"bound + '-date-toggle'\"\n (buttonClick)=\"toggleBound(bound)\"\n >\n <ng-icon\n [name]=\"expandedBound === bound ? 'matExpandLess' : 'matExpandMore'\"\n ></ng-icon>\n </gn-ui-button>\n </div>\n </div>\n</ng-template>\n", styles: ["mat-calendar{width:100%}.calendar{display:grid;grid-template-rows:1fr}.calendar>mat-calendar{min-height:0;overflow:hidden}.calendar.expand{animation:open .2s ease-out}.calendar.collapse{animation:open .2s ease-in reverse}@keyframes open{0%{grid-template-rows:0fr;opacity:0}to{grid-template-rows:1fr;opacity:1}}@media (prefers-reduced-motion: reduce){.calendar.expand,.calendar.collapse{animation-duration:1ms}}\n"], dependencies: [{ kind: "directive", type: AutofocusDirective, selector: "[gnUiAutofocus]", inputs: ["gnUiAutofocus"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i1$6.MatCalendar, selector: "mat-calendar", inputs: ["headerComponent", "startAt", "startView", "selected", "minDate", "maxDate", "dateFilter", "dateClass", "comparisonStart", "comparisonEnd", "startDateAccessibleName", "endDateAccessibleName"], outputs: ["selectedChange", "yearSelected", "monthSelected", "viewChanged", "_userSelection", "_userDragDrop"], exportAs: ["matCalendar"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
24601
24825
|
}
|
|
24602
24826
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangeDropdownComponent, decorators: [{
|
|
24603
24827
|
type: Component,
|
|
24604
24828
|
args: [{ selector: 'gn-ui-date-range-dropdown', standalone: true, imports: [
|
|
24605
|
-
|
|
24606
|
-
MatNativeDateModule,
|
|
24607
|
-
MatDatepickerModule,
|
|
24829
|
+
AutofocusDirective,
|
|
24608
24830
|
ButtonComponent,
|
|
24831
|
+
MatDatepickerModule,
|
|
24832
|
+
NgIcon,
|
|
24833
|
+
NgTemplateOutlet,
|
|
24834
|
+
OverlayModule,
|
|
24835
|
+
TranslateDirective,
|
|
24836
|
+
TranslatePipe,
|
|
24609
24837
|
], providers: [
|
|
24610
24838
|
provideIcons({
|
|
24611
|
-
|
|
24839
|
+
iconoirCalendar,
|
|
24840
|
+
matClose,
|
|
24612
24841
|
matExpandLess,
|
|
24842
|
+
matExpandMore,
|
|
24613
24843
|
}),
|
|
24614
|
-
|
|
24615
|
-
|
|
24616
|
-
|
|
24617
|
-
|
|
24844
|
+
provideNgIconsConfig({
|
|
24845
|
+
size: '1.5rem',
|
|
24846
|
+
}),
|
|
24847
|
+
provideLocalizedDateAdapter(),
|
|
24848
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"openOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (selectedDatesCount) {\n <div\n class=\"gn-ui-multiselect-counter shrink-0 rounded-full font-bold text-[12px] w-5 h-5 flex items-center justify-center mr-1 selected-count\"\n >\n {{ selectedDatesCount }}\n </div>\n }\n </div>\n <button\n class=\"h-6 w-6 flex items-center justify-center\"\n data-test=\"dropdown-clear\"\n >\n @if (selectedDatesCount && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"clearDates($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n data-test=\"dropdown-toggle\"\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"w-[296px] flex flex-col gap-2 bg-white border border-gray-400 rounded px-4 py-3 shadow-lg text-main text-[16px] leading-[24px]\"\n data-test=\"date-range-panel\"\n >\n <div class=\"flex flex-col justify-center border-b border-gray-300 pb-1\">\n <ng-container\n [ngTemplateOutlet]=\"boundRow\"\n [ngTemplateOutletContext]=\"{\n bound: 'start',\n label: 'daterange.filter.from',\n date: startDate,\n }\"\n ></ng-container>\n @if (expandedBound === 'start') {\n <div class=\"calendar\" animate.enter=\"expand\" animate.leave=\"collapse\">\n <mat-calendar\n data-test=\"start-date-calendar\"\n [selected]=\"startDate\"\n [startAt]=\"startDate ?? endDate\"\n [maxDate]=\"endDate\"\n (selectedChange)=\"setStartDate($event)\"\n ></mat-calendar>\n </div>\n }\n </div>\n <div class=\"flex flex-col justify-center\">\n <ng-container\n [ngTemplateOutlet]=\"boundRow\"\n [ngTemplateOutletContext]=\"{\n bound: 'end',\n label: 'daterange.filter.to',\n date: endDate,\n }\"\n ></ng-container>\n @if (expandedBound === 'end') {\n <div class=\"calendar\" animate.enter=\"expand\" animate.leave=\"collapse\">\n <mat-calendar\n data-test=\"end-date-calendar\"\n [selected]=\"endDate\"\n [startAt]=\"endDate ?? startDate\"\n [minDate]=\"startDate\"\n (selectedChange)=\"setEndDate($event)\"\n ></mat-calendar>\n </div>\n }\n </div>\n </div>\n</ng-template>\n\n<ng-template #boundRow let-bound=\"bound\" let-label=\"label\" let-date=\"date\">\n <div class=\"flex items-center gap-1 w-full\">\n <span class=\"w-[80px] shrink-0\" [translate]=\"label\"></span>\n <div\n class=\"flex-1 min-w-0 flex items-center gap-1 border rounded-lg px-2 py-1 transition-colors\"\n [class]=\"\n invalidBounds[bound]\n ? 'border-red-400 text-red-800 focus-within:border-red-800 hover:border-red-800'\n : 'border-gray-400 focus-within:border-main hover:border-main'\n \"\n >\n <ng-icon\n name=\"iconoirCalendar\"\n class=\"shrink-0\"\n aria-hidden=\"true\"\n ></ng-icon>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n autocomplete=\"off\"\n [gnUiAutofocus]=\"bound === expandedBound\"\n class=\"min-w-0 grow bg-transparent focus:outline-none\"\n [value]=\"date ? formatDate(date) : ''\"\n [placeholder]=\"'daterange.filter.datePlaceholder' | translate\"\n [attr.aria-label]=\"label | translate\"\n [attr.aria-invalid]=\"invalidBounds[bound] || null\"\n [attr.data-test]=\"bound + '-date-input'\"\n (change)=\"onDateInput(bound, $event)\"\n (blur)=\"normalizeDateInput(bound, $event)\"\n (click)=\"expandedBound !== bound && toggleBound(bound)\"\n />\n </div>\n <div class=\"shrink-0 flex items-center justify-end\">\n <gn-ui-button\n type=\"light\"\n [style.--gn-ui-button-padding]=\"0\"\n [style.--gn-ui-button-width]=\"'24px'\"\n [style.--gn-ui-button-height]=\"'24px'\"\n [attr.aria-expanded]=\"expandedBound === bound\"\n [attr.data-test]=\"bound + '-date-toggle'\"\n (buttonClick)=\"toggleBound(bound)\"\n >\n <ng-icon\n [name]=\"expandedBound === bound ? 'matExpandLess' : 'matExpandMore'\"\n ></ng-icon>\n </gn-ui-button>\n </div>\n </div>\n</ng-template>\n", styles: ["mat-calendar{width:100%}.calendar{display:grid;grid-template-rows:1fr}.calendar>mat-calendar{min-height:0;overflow:hidden}.calendar.expand{animation:open .2s ease-out}.calendar.collapse{animation:open .2s ease-in reverse}@keyframes open{0%{grid-template-rows:0fr;opacity:0}to{grid-template-rows:1fr;opacity:1}}@media (prefers-reduced-motion: reduce){.calendar.expand,.calendar.collapse{animation-duration:1ms}}\n"] }]
|
|
24849
|
+
}], ctorParameters: () => [], propDecorators: { title: [{
|
|
24618
24850
|
type: Input
|
|
24619
|
-
}],
|
|
24851
|
+
}], dateRange: [{
|
|
24620
24852
|
type: Input
|
|
24621
|
-
}],
|
|
24853
|
+
}], dateRangeChange: [{
|
|
24622
24854
|
type: Output
|
|
24623
|
-
}],
|
|
24624
|
-
type: Output
|
|
24625
|
-
}], picker: [{
|
|
24855
|
+
}], overlayOrigin: [{
|
|
24626
24856
|
type: ViewChild,
|
|
24627
|
-
args: ['
|
|
24857
|
+
args: ['overlayOrigin']
|
|
24628
24858
|
}] } });
|
|
24629
24859
|
|
|
24630
24860
|
class DateRangePickerComponent {
|
|
@@ -24638,20 +24868,17 @@ class DateRangePickerComponent {
|
|
|
24638
24868
|
provideNgIconsConfig({
|
|
24639
24869
|
size: '1.5rem',
|
|
24640
24870
|
}),
|
|
24641
|
-
|
|
24871
|
+
provideLocalizedDateAdapter(),
|
|
24872
|
+
], ngImport: i0, template: "<span class=\"w-full inline-block relative\">\n <mat-date-range-input [rangePicker]=\"picker\" class=\"gn-ui-text-input\">\n <input\n matStartDate\n placeholder=\"Start date\"\n [value]=\"startDate\"\n (dateInput)=\"startDateChange.emit($event.value)\"\n />\n <input\n matEndDate\n placeholder=\"End date\"\n [value]=\"endDate\"\n (dateInput)=\"endDateChange.emit($event.value)\"\n />\n </mat-date-range-input>\n\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n extraClass=\"h-full\"\n data-cy=\"date-picker-button\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-date-range-picker #picker></mat-date-range-picker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"], dependencies: [{ kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i1$6.MatDateRangeInput, selector: "mat-date-range-input", inputs: ["rangePicker", "required", "dateFilter", "min", "max", "disabled", "separator", "comparisonStart", "comparisonEnd"], exportAs: ["matDateRangeInput"] }, { kind: "directive", type: i1$6.MatStartDate, selector: "input[matStartDate]", outputs: ["dateChange", "dateInput"] }, { kind: "directive", type: i1$6.MatEndDate, selector: "input[matEndDate]", outputs: ["dateChange", "dateInput"] }, { kind: "component", type: i1$6.MatDateRangePicker, selector: "mat-date-range-picker", exportAs: ["matDateRangePicker"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
24642
24873
|
}
|
|
24643
24874
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangePickerComponent, decorators: [{
|
|
24644
24875
|
type: Component,
|
|
24645
|
-
args: [{ selector: 'gn-ui-date-range-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
24646
|
-
MatNativeDateModule,
|
|
24647
|
-
MatDatepickerModule,
|
|
24648
|
-
ButtonComponent,
|
|
24649
|
-
NgIconComponent,
|
|
24650
|
-
], providers: [
|
|
24876
|
+
args: [{ selector: 'gn-ui-date-range-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [MatDatepickerModule, ButtonComponent, NgIconComponent], providers: [
|
|
24651
24877
|
provideIcons({ iconoirCalendar }),
|
|
24652
24878
|
provideNgIconsConfig({
|
|
24653
24879
|
size: '1.5rem',
|
|
24654
24880
|
}),
|
|
24881
|
+
provideLocalizedDateAdapter(),
|
|
24655
24882
|
], template: "<span class=\"w-full inline-block relative\">\n <mat-date-range-input [rangePicker]=\"picker\" class=\"gn-ui-text-input\">\n <input\n matStartDate\n placeholder=\"Start date\"\n [value]=\"startDate\"\n (dateInput)=\"startDateChange.emit($event.value)\"\n />\n <input\n matEndDate\n placeholder=\"End date\"\n [value]=\"endDate\"\n (dateInput)=\"endDateChange.emit($event.value)\"\n />\n </mat-date-range-input>\n\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n extraClass=\"h-full\"\n data-cy=\"date-picker-button\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-date-range-picker #picker></mat-date-range-picker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"] }]
|
|
24656
24883
|
}], propDecorators: { startDate: [{
|
|
24657
24884
|
type: Input
|
|
@@ -24669,28 +24896,68 @@ class DragAndDropFileInputComponent {
|
|
|
24669
24896
|
constructor() {
|
|
24670
24897
|
this.placeholder = placeholder;
|
|
24671
24898
|
this.accept = '*';
|
|
24899
|
+
this.maxFileSizeMb = null;
|
|
24900
|
+
this.icon = null;
|
|
24901
|
+
this.dropzoneBackgroundColor = null;
|
|
24902
|
+
this.textClass = '';
|
|
24903
|
+
this.extraClass = '';
|
|
24904
|
+
this.showFileName = true;
|
|
24672
24905
|
this.fileChange = new EventEmitter();
|
|
24906
|
+
this.errorChange = new EventEmitter();
|
|
24673
24907
|
this.selectedFile = null;
|
|
24674
24908
|
}
|
|
24675
24909
|
get fileName() {
|
|
24676
24910
|
return this.selectedFile && this.selectedFile.name;
|
|
24677
24911
|
}
|
|
24912
|
+
get maxFileSizeBytes() {
|
|
24913
|
+
return typeof this.maxFileSizeMb === 'number'
|
|
24914
|
+
? megabytesToBytes(this.maxFileSizeMb)
|
|
24915
|
+
: null;
|
|
24916
|
+
}
|
|
24678
24917
|
selectFile(event) {
|
|
24918
|
+
if (event.rejectedFiles?.length) {
|
|
24919
|
+
const reason = event.rejectedFiles[0].reason;
|
|
24920
|
+
this.errorChange.emit(reason === 'size' ? 'file-too-large' : 'invalid-extension');
|
|
24921
|
+
return;
|
|
24922
|
+
}
|
|
24679
24923
|
this.selectedFile = event.addedFiles[0];
|
|
24680
24924
|
this.fileChange.emit(this.selectedFile);
|
|
24681
24925
|
}
|
|
24926
|
+
openFileSelector() {
|
|
24927
|
+
this.dropzone.showFileSelector();
|
|
24928
|
+
}
|
|
24929
|
+
clear() {
|
|
24930
|
+
this.selectedFile = null;
|
|
24931
|
+
}
|
|
24682
24932
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DragAndDropFileInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
24683
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DragAndDropFileInputComponent, isStandalone: true, selector: "gn-ui-drag-and-drop-file-input", inputs: { placeholder: "placeholder", accept: "accept" }, outputs: { fileChange: "fileChange" }, ngImport: i0, template: "<div
|
|
24933
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DragAndDropFileInputComponent, isStandalone: true, selector: "gn-ui-drag-and-drop-file-input", inputs: { placeholder: "placeholder", accept: "accept", maxFileSizeMb: "maxFileSizeMb", icon: "icon", dropzoneBackgroundColor: "dropzoneBackgroundColor", textClass: "textClass", extraClass: "extraClass", showFileName: "showFileName" }, outputs: { fileChange: "fileChange", errorChange: "errorChange" }, viewQueries: [{ propertyName: "dropzone", first: true, predicate: NgxDropzoneComponent, descendants: true }], ngImport: i0, template: "<div\n class=\"flex h-full items-center\"\n [class]=\"extraClass\"\n (click)=\"openFileSelector()\"\n>\n <ngx-dropzone\n class=\"flex-1 overflow-hidden\"\n [multiple]=\"false\"\n [disableClick]=\"true\"\n [accept]=\"accept\"\n [maxFileSize]=\"maxFileSizeBytes\"\n [style.background-color]=\"dropzoneBackgroundColor\"\n (change)=\"selectFile($event)\"\n >\n @if (!fileName || !showFileName) {\n <div [class]=\"textClass\">\n {{ placeholder }}\n </div>\n }\n\n @if (fileName && showFileName) {\n <div [class]=\"textClass\">\n {{ fileName }}\n </div>\n }\n </ngx-dropzone>\n\n @if (icon) {\n <ng-icon [name]=\"icon\" class=\"shrink-0 text-gray-950\"></ng-icon>\n }\n</div>\n", styles: ["ngx-dropzone{height:auto;border:none;overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: NgxDropzoneModule }, { kind: "component", type: i1$8.NgxDropzoneComponent, selector: "ngx-dropzone, [ngx-dropzone]", inputs: ["accept", "disabled", "multiple", "maxFileSize", "expandable", "disableClick", "processDirectoryDrop", "id", "aria-label", "aria-labelledby", "aria-describedby"], outputs: ["change"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }] }); }
|
|
24684
24934
|
}
|
|
24685
24935
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DragAndDropFileInputComponent, decorators: [{
|
|
24686
24936
|
type: Component,
|
|
24687
|
-
args: [{ selector: 'gn-ui-drag-and-drop-file-input', standalone: true, imports: [NgxDropzoneModule], template: "<div
|
|
24937
|
+
args: [{ selector: 'gn-ui-drag-and-drop-file-input', standalone: true, imports: [NgxDropzoneModule, NgIcon], template: "<div\n class=\"flex h-full items-center\"\n [class]=\"extraClass\"\n (click)=\"openFileSelector()\"\n>\n <ngx-dropzone\n class=\"flex-1 overflow-hidden\"\n [multiple]=\"false\"\n [disableClick]=\"true\"\n [accept]=\"accept\"\n [maxFileSize]=\"maxFileSizeBytes\"\n [style.background-color]=\"dropzoneBackgroundColor\"\n (change)=\"selectFile($event)\"\n >\n @if (!fileName || !showFileName) {\n <div [class]=\"textClass\">\n {{ placeholder }}\n </div>\n }\n\n @if (fileName && showFileName) {\n <div [class]=\"textClass\">\n {{ fileName }}\n </div>\n }\n </ngx-dropzone>\n\n @if (icon) {\n <ng-icon [name]=\"icon\" class=\"shrink-0 text-gray-950\"></ng-icon>\n }\n</div>\n", styles: ["ngx-dropzone{height:auto;border:none;overflow:hidden}\n"] }]
|
|
24688
24938
|
}], propDecorators: { placeholder: [{
|
|
24689
24939
|
type: Input
|
|
24690
24940
|
}], accept: [{
|
|
24691
24941
|
type: Input
|
|
24942
|
+
}], maxFileSizeMb: [{
|
|
24943
|
+
type: Input
|
|
24944
|
+
}], icon: [{
|
|
24945
|
+
type: Input
|
|
24946
|
+
}], dropzoneBackgroundColor: [{
|
|
24947
|
+
type: Input
|
|
24948
|
+
}], textClass: [{
|
|
24949
|
+
type: Input
|
|
24950
|
+
}], extraClass: [{
|
|
24951
|
+
type: Input
|
|
24952
|
+
}], showFileName: [{
|
|
24953
|
+
type: Input
|
|
24692
24954
|
}], fileChange: [{
|
|
24693
24955
|
type: Output
|
|
24956
|
+
}], errorChange: [{
|
|
24957
|
+
type: Output
|
|
24958
|
+
}], dropzone: [{
|
|
24959
|
+
type: ViewChild,
|
|
24960
|
+
args: [NgxDropzoneComponent]
|
|
24694
24961
|
}] } });
|
|
24695
24962
|
|
|
24696
24963
|
class DropdownMultiselectComponent {
|
|
@@ -24845,7 +25112,7 @@ class DropdownMultiselectComponent {
|
|
|
24845
25112
|
matExpandMore,
|
|
24846
25113
|
matExpandLess,
|
|
24847
25114
|
}),
|
|
24848
|
-
], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "overlayContainer", first: true, predicate: ["overlayContainer"], descendants: true, read: ElementRef }, { propertyName: "searchFieldInput", first: true, predicate: ["searchFieldInput"], descendants: true }, { propertyName: "checkboxes", predicate: ["checkBox"], descendants: true, read: ElementRef }], ngImport: i0, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelectedChoices) {\n <div\n class=\"gn-ui-multiselect-counter shrink-0 rounded-full font-bold text-[12px] w-5 h-5 flex items-center justify-center mr-1 selected-count\"\n >\n {{ selected.length }}\n </div>\n }\n </div>\n <button class=\"h-6 w-6\" data-
|
|
25115
|
+
], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "overlayContainer", first: true, predicate: ["overlayContainer"], descendants: true, read: ElementRef }, { propertyName: "searchFieldInput", first: true, predicate: ["searchFieldInput"], descendants: true }, { propertyName: "checkboxes", predicate: ["checkBox"], descendants: true, read: ElementRef }], ngImport: i0, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelectedChoices) {\n <div\n class=\"gn-ui-multiselect-counter shrink-0 rounded-full font-bold text-[12px] w-5 h-5 flex items-center justify-center mr-1 selected-count\"\n >\n {{ selected.length }}\n </div>\n }\n </div>\n <button class=\"h-6 w-6\" data-test=\"dropdown-clear\">\n @if (hasSelectedChoices && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"clearSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container\"\n [style.max-height]=\"overlayMaxHeight\"\n [style.min-width]=\"overlayMinWidth\"\n role=\"listbox\"\n tabindex=\"-1\"\n [attr.id]=\"id\"\n [attr.aria-multiselectable]=\"true\"\n [attr.aria-label]=\"title\"\n (keydown)=\"handleOverlayKeydown($event)\"\n #overlayContainer\n >\n <div\n class=\"border border-gray-300 rounded mb-2 mx-2 min-h-[44px] flex flex-row gap-[2px] flex-wrap p-2 focus-within:rounded focus-within:border-2 focus-within:border-primary\"\n >\n @for (selected of selectedChoices; track selected) {\n <button\n type=\"button\"\n [title]=\"selected.label\"\n class=\"max-w-full bg-main text-white rounded pr-[7px] flex gap-1 items-center opacity-70 hover:opacity-100 focus:opacity-100 transition-opacity mb-1\"\n (click)=\"select(selected, false)\"\n >\n <div class=\"text-sm truncate leading-[26px] px-2\">\n {{ selected.label }}\n </div>\n <div\n class=\"flex items-center justify-center rounded-full bg-white text-main h-[13px] w-[13px] pt-px -mt-px shrink-0\"\n >\n <ng-icon\n name=\"matClose\"\n class=\"!h-[12px] !w-[11px] text-[12px]\"\n ></ng-icon>\n </div>\n </button>\n }\n\n @if (allowSearch) {\n <div class=\"w-[50%] relative grow shrink\">\n <input\n #searchFieldInput\n class=\"w-full px-2 truncate text-[14px] h-full overlaySearchInput focus:outline-none\"\n [(ngModel)]=\"searchInputValue\"\n [placeholder]=\"'multiselect.filter.placeholder' | translate\"\n />\n @if (!!searchInputValue) {\n <button\n class=\"absolute top-1/2 -translate-y-1/2 right-0 px-[7px] leading-tight clear-search-input mr-2\"\n (click)=\"clearSearchInputValue($event)\"\n >\n <ng-icon class=\"!h-[10px] !w-[12px] text-[12px]\" name=\"matClose\">\n </ng-icon>\n </button>\n }\n </div>\n }\n </div>\n\n @for (choice of filteredChoicesByText; track choice) {\n <label\n [title]=\"choice.label\"\n class=\"flex px-5 py-1 w-full text-gray-900 cursor-pointer hover:text-primary-darkest hover:bg-gray-50 focus-within:text-primary-darkest focus-within:bg-gray-50 transition-colors\"\n >\n <input\n class=\"w-[18px] h-[18px] align-text-top shrink-0\"\n type=\"checkbox\"\n #checkBox\n [checked]=\"isSelected(choice)\"\n (change)=\"select(choice, checkBox.checked)\"\n />\n <span class=\"ml-[8px] text-[14px] truncate\">\n {{ choice.label }}\n </span>\n </label>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
24849
25116
|
}
|
|
24850
25117
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DropdownMultiselectComponent, decorators: [{
|
|
24851
25118
|
type: Component,
|
|
@@ -24855,7 +25122,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24855
25122
|
matExpandMore,
|
|
24856
25123
|
matExpandLess,
|
|
24857
25124
|
}),
|
|
24858
|
-
], standalone: true, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelectedChoices) {\n <div\n class=\"gn-ui-multiselect-counter shrink-0 rounded-full font-bold text-[12px] w-5 h-5 flex items-center justify-center mr-1 selected-count\"\n >\n {{ selected.length }}\n </div>\n }\n </div>\n <button class=\"h-6 w-6\" data-
|
|
25125
|
+
], standalone: true, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelectedChoices) {\n <div\n class=\"gn-ui-multiselect-counter shrink-0 rounded-full font-bold text-[12px] w-5 h-5 flex items-center justify-center mr-1 selected-count\"\n >\n {{ selected.length }}\n </div>\n }\n </div>\n <button class=\"h-6 w-6\" data-test=\"dropdown-clear\">\n @if (hasSelectedChoices && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"clearSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container\"\n [style.max-height]=\"overlayMaxHeight\"\n [style.min-width]=\"overlayMinWidth\"\n role=\"listbox\"\n tabindex=\"-1\"\n [attr.id]=\"id\"\n [attr.aria-multiselectable]=\"true\"\n [attr.aria-label]=\"title\"\n (keydown)=\"handleOverlayKeydown($event)\"\n #overlayContainer\n >\n <div\n class=\"border border-gray-300 rounded mb-2 mx-2 min-h-[44px] flex flex-row gap-[2px] flex-wrap p-2 focus-within:rounded focus-within:border-2 focus-within:border-primary\"\n >\n @for (selected of selectedChoices; track selected) {\n <button\n type=\"button\"\n [title]=\"selected.label\"\n class=\"max-w-full bg-main text-white rounded pr-[7px] flex gap-1 items-center opacity-70 hover:opacity-100 focus:opacity-100 transition-opacity mb-1\"\n (click)=\"select(selected, false)\"\n >\n <div class=\"text-sm truncate leading-[26px] px-2\">\n {{ selected.label }}\n </div>\n <div\n class=\"flex items-center justify-center rounded-full bg-white text-main h-[13px] w-[13px] pt-px -mt-px shrink-0\"\n >\n <ng-icon\n name=\"matClose\"\n class=\"!h-[12px] !w-[11px] text-[12px]\"\n ></ng-icon>\n </div>\n </button>\n }\n\n @if (allowSearch) {\n <div class=\"w-[50%] relative grow shrink\">\n <input\n #searchFieldInput\n class=\"w-full px-2 truncate text-[14px] h-full overlaySearchInput focus:outline-none\"\n [(ngModel)]=\"searchInputValue\"\n [placeholder]=\"'multiselect.filter.placeholder' | translate\"\n />\n @if (!!searchInputValue) {\n <button\n class=\"absolute top-1/2 -translate-y-1/2 right-0 px-[7px] leading-tight clear-search-input mr-2\"\n (click)=\"clearSearchInputValue($event)\"\n >\n <ng-icon class=\"!h-[10px] !w-[12px] text-[12px]\" name=\"matClose\">\n </ng-icon>\n </button>\n }\n </div>\n }\n </div>\n\n @for (choice of filteredChoicesByText; track choice) {\n <label\n [title]=\"choice.label\"\n class=\"flex px-5 py-1 w-full text-gray-900 cursor-pointer hover:text-primary-darkest hover:bg-gray-50 focus-within:text-primary-darkest focus-within:bg-gray-50 transition-colors\"\n >\n <input\n class=\"w-[18px] h-[18px] align-text-top shrink-0\"\n type=\"checkbox\"\n #checkBox\n [checked]=\"isSelected(choice)\"\n (change)=\"select(choice, checkBox.checked)\"\n />\n <span class=\"ml-[8px] text-[14px] truncate\">\n {{ choice.label }}\n </span>\n </label>\n }\n </div>\n</ng-template>\n" }]
|
|
24859
25126
|
}], propDecorators: { title: [{
|
|
24860
25127
|
type: Input
|
|
24861
25128
|
}], choices: [{
|
|
@@ -25027,7 +25294,7 @@ class DropdownSelectorComponent {
|
|
|
25027
25294
|
provideNgIconsConfig({
|
|
25028
25295
|
size: '1.5em',
|
|
25029
25296
|
}),
|
|
25030
|
-
], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "choiceInputs", predicate: ["choiceInputs"], descendants: true, read: ElementRef }], ngImport: i0, template: "<div class=\"flex flex-col sm:flex-row sm:items-center relative w-full\">\n @if (showTitle) {\n <span\n class=\"tracking-wide text-sm mb-2 sm:mb-0 sm:mr-2 whitespace-nowrap\"\n [attr.for]=\"id\"\n >\n {{ title }}\n </span>\n }\n <gn-ui-button\n type=\"outline\"\n class=\"grow min-w-0\"\n [style.--gn-ui-button-padding]=\"'8px 8px 8px 16px'\"\n extraClass=\"bg-background flex flex-row w-full {{ extraBtnClass }}\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n (keydown)=\"handleTriggerKeydown($event)\"\n [disabled]=\"disabled\"\n >\n <div class=\"grow truncate py-1 mr-2 text-left\">\n {{ getChoiceLabel() | translate }}\n </div>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n </gn-ui-button>\n</div>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container\"\n [style.max-height]=\"overlayMaxHeight\"\n [style.min-width]=\"overlayWidth\"\n role=\"listbox\"\n tabindex=\"-1\"\n [attr.id]=\"id\"\n [attr.aria-multiselectable]=\"true\"\n [attr.aria-label]=\"title\"\n (keydown)=\"handleOverlayKeydown($event)\"\n >\n @for (choice of choices; track choice) {\n <button\n #choiceInputs\n type=\"button\"\n [title]=\"choice.label | translate\"\n class=\"flex px-5 py-1 w-full text-start cursor-pointer transition-colors\"\n [ngClass]=\"\n isSelected(choice)\n ? 'text-white bg-primary hover:text-white hover:bg-primary-darker focus:text-white focus:bg-primary-darker'\n : 'text-gray-900 hover:text-primary-darkest hover:bg-gray-50 focus:text-primary-darkest focus:bg-gray-50'\n \"\n (click)=\"onSelectValue(choice)\"\n (keydown)=\"selectIfEnter($event, choice)\"\n [attr.data-cy-value]=\"choice.value.toString()\"\n [attr.data-cy-active]=\"isSelected(choice) ? 'true' : undefined\"\n >\n <span class=\"text-[14px]\">\n {{ choice.label | translate }}\n </span>\n </button>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$
|
|
25297
|
+
], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "choiceInputs", predicate: ["choiceInputs"], descendants: true, read: ElementRef }], ngImport: i0, template: "<div class=\"flex flex-col sm:flex-row sm:items-center relative w-full\">\n @if (showTitle) {\n <span\n class=\"tracking-wide text-sm mb-2 sm:mb-0 sm:mr-2 whitespace-nowrap\"\n [attr.for]=\"id\"\n >\n {{ title }}\n </span>\n }\n <gn-ui-button\n type=\"outline\"\n class=\"grow min-w-0\"\n [style.--gn-ui-button-padding]=\"'8px 8px 8px 16px'\"\n extraClass=\"bg-background flex flex-row w-full {{ extraBtnClass }}\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n (keydown)=\"handleTriggerKeydown($event)\"\n [disabled]=\"disabled\"\n >\n <div class=\"grow truncate py-1 mr-2 text-left\">\n {{ getChoiceLabel() | translate }}\n </div>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n </gn-ui-button>\n</div>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container\"\n [style.max-height]=\"overlayMaxHeight\"\n [style.min-width]=\"overlayWidth\"\n role=\"listbox\"\n tabindex=\"-1\"\n [attr.id]=\"id\"\n [attr.aria-multiselectable]=\"true\"\n [attr.aria-label]=\"title\"\n (keydown)=\"handleOverlayKeydown($event)\"\n >\n @for (choice of choices; track choice) {\n <button\n #choiceInputs\n type=\"button\"\n [title]=\"choice.label | translate\"\n class=\"flex px-5 py-1 w-full text-start cursor-pointer transition-colors\"\n [ngClass]=\"\n isSelected(choice)\n ? 'text-white bg-primary hover:text-white hover:bg-primary-darker focus:text-white focus:bg-primary-darker'\n : 'text-gray-900 hover:text-primary-darkest hover:bg-gray-50 focus:text-primary-darkest focus:bg-gray-50'\n \"\n (click)=\"onSelectValue(choice)\"\n (keydown)=\"selectIfEnter($event, choice)\"\n [attr.data-cy-value]=\"choice.value.toString()\"\n [attr.data-cy-active]=\"isSelected(choice) ? 'true' : undefined\"\n >\n <span class=\"text-[14px]\">\n {{ choice.label | translate }}\n </span>\n </button>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
25031
25298
|
}
|
|
25032
25299
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DropdownSelectorComponent, decorators: [{
|
|
25033
25300
|
type: Component,
|
|
@@ -25767,6 +26034,157 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
25767
26034
|
type: Output
|
|
25768
26035
|
}] } });
|
|
25769
26036
|
|
|
26037
|
+
marker('search.filters.spatialExtent.import');
|
|
26038
|
+
marker('search.filters.spatialExtent.helpText');
|
|
26039
|
+
marker('search.filters.spatialExtent.error.title');
|
|
26040
|
+
marker('search.filters.spatialExtent.bboxPrefix');
|
|
26041
|
+
marker('search.filters.spatialExtent.bboxDelete');
|
|
26042
|
+
class SpatialExtentDropdownComponent {
|
|
26043
|
+
constructor() {
|
|
26044
|
+
this.cd = inject(ChangeDetectorRef);
|
|
26045
|
+
this.scrollStrategies = inject(ScrollStrategyOptions);
|
|
26046
|
+
this.maxFileSizeMb = null;
|
|
26047
|
+
this.bboxChange = new EventEmitter();
|
|
26048
|
+
this.errorChange = new EventEmitter();
|
|
26049
|
+
this.bbox = null;
|
|
26050
|
+
this.fileName = '';
|
|
26051
|
+
this.overlayPositions = [
|
|
26052
|
+
{
|
|
26053
|
+
originX: 'start',
|
|
26054
|
+
originY: 'bottom',
|
|
26055
|
+
overlayX: 'start',
|
|
26056
|
+
overlayY: 'top',
|
|
26057
|
+
offsetY: 8,
|
|
26058
|
+
},
|
|
26059
|
+
{
|
|
26060
|
+
originX: 'start',
|
|
26061
|
+
originY: 'top',
|
|
26062
|
+
overlayX: 'start',
|
|
26063
|
+
overlayY: 'bottom',
|
|
26064
|
+
offsetY: -8,
|
|
26065
|
+
},
|
|
26066
|
+
];
|
|
26067
|
+
this.scrollStrategy = this.scrollStrategies.reposition();
|
|
26068
|
+
this.overlayOpen = false;
|
|
26069
|
+
this.overlayMinWidth = 'none';
|
|
26070
|
+
this.errorKey = null;
|
|
26071
|
+
}
|
|
26072
|
+
get hasSelection() {
|
|
26073
|
+
return !!this.bbox;
|
|
26074
|
+
}
|
|
26075
|
+
openOverlay() {
|
|
26076
|
+
this.overlayMinWidth =
|
|
26077
|
+
this.overlayOrigin.elementRef.nativeElement.getBoundingClientRect()
|
|
26078
|
+
.width + 'px';
|
|
26079
|
+
this.overlayOpen = true;
|
|
26080
|
+
}
|
|
26081
|
+
closeOverlay() {
|
|
26082
|
+
this.overlayOpen = false;
|
|
26083
|
+
}
|
|
26084
|
+
toggleOverlay() {
|
|
26085
|
+
if (this.overlayOpen) {
|
|
26086
|
+
this.closeOverlay();
|
|
26087
|
+
}
|
|
26088
|
+
else {
|
|
26089
|
+
this.openOverlay();
|
|
26090
|
+
}
|
|
26091
|
+
}
|
|
26092
|
+
async handleFileSelected(file) {
|
|
26093
|
+
this.errorKey = null;
|
|
26094
|
+
let content;
|
|
26095
|
+
try {
|
|
26096
|
+
content = await readFileAsText(file);
|
|
26097
|
+
const parsed = JSON.parse(content);
|
|
26098
|
+
const geometry = getGeometryFromGeoJSON(parsed);
|
|
26099
|
+
if (!geometry) {
|
|
26100
|
+
this.setError(marker('search.filters.spatialExtent.error.noGeometry'));
|
|
26101
|
+
return;
|
|
26102
|
+
}
|
|
26103
|
+
const bbox = getGeometryBoundingBox(geometry);
|
|
26104
|
+
this.bbox = bbox;
|
|
26105
|
+
this.fileName = file.name;
|
|
26106
|
+
this.bboxChange.emit(bbox);
|
|
26107
|
+
this.cd.markForCheck();
|
|
26108
|
+
}
|
|
26109
|
+
catch {
|
|
26110
|
+
this.setError(marker('search.filters.spatialExtent.error.invalidFormat'));
|
|
26111
|
+
return;
|
|
26112
|
+
}
|
|
26113
|
+
}
|
|
26114
|
+
handleFileError(error) {
|
|
26115
|
+
if (error === 'file-too-large') {
|
|
26116
|
+
this.setError(marker('search.filters.spatialExtent.error.fileTooLarge'), {
|
|
26117
|
+
maxSize: this.maxFileSizeMb,
|
|
26118
|
+
});
|
|
26119
|
+
}
|
|
26120
|
+
else {
|
|
26121
|
+
this.setError(marker('search.filters.spatialExtent.error.invalidFormat'));
|
|
26122
|
+
}
|
|
26123
|
+
}
|
|
26124
|
+
setError(errorKey, params) {
|
|
26125
|
+
this.errorKey = errorKey;
|
|
26126
|
+
this.errorChange.emit({ key: errorKey, params });
|
|
26127
|
+
this.cd.markForCheck();
|
|
26128
|
+
}
|
|
26129
|
+
removeSelection(event) {
|
|
26130
|
+
this.bbox = null;
|
|
26131
|
+
this.fileName = '';
|
|
26132
|
+
this.errorKey = null;
|
|
26133
|
+
this.fileInput?.clear();
|
|
26134
|
+
this.bboxChange.emit(null);
|
|
26135
|
+
propagateToDocumentOnly(event);
|
|
26136
|
+
}
|
|
26137
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
26138
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: SpatialExtentDropdownComponent, isStandalone: true, selector: "gn-ui-spatial-extent-dropdown", inputs: { title: "title", maxFileSizeMb: "maxFileSizeMb" }, outputs: { bboxChange: "bboxChange", errorChange: "errorChange" }, providers: [
|
|
26139
|
+
provideIcons({
|
|
26140
|
+
iconoirCheckCircle,
|
|
26141
|
+
iconoirImport,
|
|
26142
|
+
iconoirSquareDashed,
|
|
26143
|
+
iconoirTrash,
|
|
26144
|
+
matClose,
|
|
26145
|
+
matExpandLess,
|
|
26146
|
+
matExpandMore,
|
|
26147
|
+
}),
|
|
26148
|
+
], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "fileInput", first: true, predicate: DragAndDropFileInputComponent, descendants: true }], ngImport: i0, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelection) {\n <div\n class=\"bg-primary-lighter text-white shrink-0 rounded-full font-bold text-[12px] w-[22px] h-[22px] flex items-center justify-center\"\n >\n 1\n </div>\n }\n </div>\n <button class=\"h-6 w-6 flex items-center\" data-cy=\"clearSelection\">\n @if (hasSelection && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"removeSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-400 rounded shadow-lg pt-[12px] px-[16px] overlay-container w-[280px] flex flex-col gap-2\"\n [style.min-width]=\"overlayMinWidth\"\n >\n <div class=\"text-xs text-black\">\n {{ 'search.filters.spatialExtent.helpText' | translate }}\n </div>\n\n <div\n [class]=\"\n (hasSelection\n ? 'border-b-[1px] border-gray-400 pb-[20px]'\n : 'pb-[8px]') + ' text-black'\n \"\n >\n <gn-ui-drag-and-drop-file-input\n accept=\".json,.geojson\"\n [placeholder]=\"'search.filters.spatialExtent.import' | translate\"\n [maxFileSizeMb]=\"maxFileSizeMb\"\n [showFileName]=\"false\"\n textClass=\"text-black truncate\"\n dropzoneBackgroundColor=\"transparent\"\n icon=\"iconoirImport\"\n extraClass=\"gn-ui-gray-outline-input w-full !h-[32px] px-[8px] py-[4px] justify-between\"\n (fileChange)=\"handleFileSelected($event)\"\n (errorChange)=\"handleFileError($event)\"\n data-cy=\"importGeojson\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n\n @if (hasSelection) {\n <gn-ui-button\n [type]=\"'primary-light'\"\n [title]=\"\n ('search.filters.spatialExtent.bboxDelete' | translate) +\n ' ' +\n fileName\n \"\n (buttonClick)=\"removeSelection($event)\"\n extraClass=\"group gap-x-2 w-full h-[28px] px-[8px] py-[4px] mb-[12px] bg-primary-lighter text-white\"\n data-test=\"spatial-extent-selected-item\"\n >\n <ng-icon\n name=\"iconoirSquareDashed\"\n class=\"shrink-0\"\n size=\"15px\"\n ></ng-icon>\n <span class=\"group-hover:hidden truncate text-sm m-auto\">\n {{ 'search.filters.spatialExtent.bboxPrefix' | translate }}\n {{ fileName }}\n </span>\n <span class=\"hidden group-hover:block truncate text-sm m-auto\">\n {{ 'search.filters.spatialExtent.bboxDelete' | translate }}\n {{ fileName }}\n </span>\n <ng-icon\n name=\"iconoirCheckCircle\"\n class=\"group-hover:hidden shrink-0\"\n size=\"14px\"\n ></ng-icon>\n <ng-icon\n name=\"iconoirTrash\"\n class=\"!hidden group-hover:!block shrink-0\"\n size=\"15px\"\n ></ng-icon>\n </gn-ui-button>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: DragAndDropFileInputComponent, selector: "gn-ui-drag-and-drop-file-input", inputs: ["placeholder", "accept", "maxFileSizeMb", "icon", "dropzoneBackgroundColor", "textClass", "extraClass", "showFileName"], outputs: ["fileChange", "errorChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
26149
|
+
}
|
|
26150
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, decorators: [{
|
|
26151
|
+
type: Component,
|
|
26152
|
+
args: [{ selector: 'gn-ui-spatial-extent-dropdown', standalone: true, imports: [
|
|
26153
|
+
ButtonComponent,
|
|
26154
|
+
NgIcon,
|
|
26155
|
+
OverlayModule,
|
|
26156
|
+
TranslatePipe,
|
|
26157
|
+
DragAndDropFileInputComponent,
|
|
26158
|
+
], providers: [
|
|
26159
|
+
provideIcons({
|
|
26160
|
+
iconoirCheckCircle,
|
|
26161
|
+
iconoirImport,
|
|
26162
|
+
iconoirSquareDashed,
|
|
26163
|
+
iconoirTrash,
|
|
26164
|
+
matClose,
|
|
26165
|
+
matExpandLess,
|
|
26166
|
+
matExpandMore,
|
|
26167
|
+
}),
|
|
26168
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelection) {\n <div\n class=\"bg-primary-lighter text-white shrink-0 rounded-full font-bold text-[12px] w-[22px] h-[22px] flex items-center justify-center\"\n >\n 1\n </div>\n }\n </div>\n <button class=\"h-6 w-6 flex items-center\" data-cy=\"clearSelection\">\n @if (hasSelection && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"removeSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-400 rounded shadow-lg pt-[12px] px-[16px] overlay-container w-[280px] flex flex-col gap-2\"\n [style.min-width]=\"overlayMinWidth\"\n >\n <div class=\"text-xs text-black\">\n {{ 'search.filters.spatialExtent.helpText' | translate }}\n </div>\n\n <div\n [class]=\"\n (hasSelection\n ? 'border-b-[1px] border-gray-400 pb-[20px]'\n : 'pb-[8px]') + ' text-black'\n \"\n >\n <gn-ui-drag-and-drop-file-input\n accept=\".json,.geojson\"\n [placeholder]=\"'search.filters.spatialExtent.import' | translate\"\n [maxFileSizeMb]=\"maxFileSizeMb\"\n [showFileName]=\"false\"\n textClass=\"text-black truncate\"\n dropzoneBackgroundColor=\"transparent\"\n icon=\"iconoirImport\"\n extraClass=\"gn-ui-gray-outline-input w-full !h-[32px] px-[8px] py-[4px] justify-between\"\n (fileChange)=\"handleFileSelected($event)\"\n (errorChange)=\"handleFileError($event)\"\n data-cy=\"importGeojson\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n\n @if (hasSelection) {\n <gn-ui-button\n [type]=\"'primary-light'\"\n [title]=\"\n ('search.filters.spatialExtent.bboxDelete' | translate) +\n ' ' +\n fileName\n \"\n (buttonClick)=\"removeSelection($event)\"\n extraClass=\"group gap-x-2 w-full h-[28px] px-[8px] py-[4px] mb-[12px] bg-primary-lighter text-white\"\n data-test=\"spatial-extent-selected-item\"\n >\n <ng-icon\n name=\"iconoirSquareDashed\"\n class=\"shrink-0\"\n size=\"15px\"\n ></ng-icon>\n <span class=\"group-hover:hidden truncate text-sm m-auto\">\n {{ 'search.filters.spatialExtent.bboxPrefix' | translate }}\n {{ fileName }}\n </span>\n <span class=\"hidden group-hover:block truncate text-sm m-auto\">\n {{ 'search.filters.spatialExtent.bboxDelete' | translate }}\n {{ fileName }}\n </span>\n <ng-icon\n name=\"iconoirCheckCircle\"\n class=\"group-hover:hidden shrink-0\"\n size=\"14px\"\n ></ng-icon>\n <ng-icon\n name=\"iconoirTrash\"\n class=\"!hidden group-hover:!block shrink-0\"\n size=\"15px\"\n ></ng-icon>\n </gn-ui-button>\n }\n </div>\n</ng-template>\n" }]
|
|
26169
|
+
}], propDecorators: { title: [{
|
|
26170
|
+
type: Input
|
|
26171
|
+
}], maxFileSizeMb: [{
|
|
26172
|
+
type: Input
|
|
26173
|
+
}], bboxChange: [{
|
|
26174
|
+
type: Output
|
|
26175
|
+
}], errorChange: [{
|
|
26176
|
+
type: Output
|
|
26177
|
+
}], overlayOrigin: [{
|
|
26178
|
+
type: ViewChild,
|
|
26179
|
+
args: ['overlayOrigin']
|
|
26180
|
+
}], overlay: [{
|
|
26181
|
+
type: ViewChild,
|
|
26182
|
+
args: [CdkConnectedOverlay]
|
|
26183
|
+
}], fileInput: [{
|
|
26184
|
+
type: ViewChild,
|
|
26185
|
+
args: [DragAndDropFileInputComponent]
|
|
26186
|
+
}] } });
|
|
26187
|
+
|
|
25770
26188
|
class CellPopinComponent {
|
|
25771
26189
|
constructor() {
|
|
25772
26190
|
this.scrollDispatcher = inject(ScrollDispatcher);
|
|
@@ -25843,7 +26261,7 @@ class CellPopinComponent {
|
|
|
25843
26261
|
this.isOpen = false;
|
|
25844
26262
|
}
|
|
25845
26263
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CellPopinComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
25846
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: CellPopinComponent, isStandalone: true, selector: "gn-ui-cell-popin", inputs: { extraClass: "extraClass", cdkScrollContainer: "cdkScrollContainer", scrollContainer: "scrollContainer", activePopin: "activePopin" }, providers: [provideIcons({ iconoirReduce })], viewQueries: [{ propertyName: "anchorRef", first: true, predicate: ["anchorRef"], descendants: true }], ngImport: i0, template: "<div\n class=\"h-full flex items-center justify-center\"\n cdkOverlayOrigin\n #anchorRef\n #trigger=\"cdkOverlayOrigin\"\n [class]=\"extraClass\"\n data-cy=\"cell-popin\"\n>\n <ng-content select=\"[cellContent]\"></ng-content>\n @if (activePopin) {\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayPush]=\"false\"\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayWidth]=\"'auto'\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n [cdkConnectedOverlayGrowAfterOpen]=\"true\"\n (detach)=\"closeOverlay()\"\n >\n @if (activePopin && isVisible) {\n <div\n class=\"bg-white shadow-lg border border-gray-300 relative\"\n data-cy=\"cell-popin-content\"\n >\n <ng-content select=\"[popinContent]\"></ng-content>\n <gn-ui-button\n data-cy=\"cell-popin-close\"\n [style.--gn-ui-button-background]=\"'bg-transparent'\"\n class=\"absolute top-2 right-2\"\n type=\"light\"\n (buttonClick)=\"closeOverlay()\"\n extraClass=\"w-10 h-8 px-1\"\n >\n <ng-icon name=\"iconoirReduce\" size=\"24\"></ng-icon>\n </gn-ui-button>\n </div>\n }\n </ng-template>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$
|
|
26264
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: CellPopinComponent, isStandalone: true, selector: "gn-ui-cell-popin", inputs: { extraClass: "extraClass", cdkScrollContainer: "cdkScrollContainer", scrollContainer: "scrollContainer", activePopin: "activePopin" }, providers: [provideIcons({ iconoirReduce })], viewQueries: [{ propertyName: "anchorRef", first: true, predicate: ["anchorRef"], descendants: true }], ngImport: i0, template: "<div\n class=\"h-full flex items-center justify-center\"\n cdkOverlayOrigin\n #anchorRef\n #trigger=\"cdkOverlayOrigin\"\n [class]=\"extraClass\"\n data-cy=\"cell-popin\"\n>\n <ng-content select=\"[cellContent]\"></ng-content>\n @if (activePopin) {\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayPush]=\"false\"\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayWidth]=\"'auto'\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n [cdkConnectedOverlayGrowAfterOpen]=\"true\"\n (detach)=\"closeOverlay()\"\n >\n @if (activePopin && isVisible) {\n <div\n class=\"bg-white shadow-lg border border-gray-300 relative\"\n data-cy=\"cell-popin-content\"\n >\n <ng-content select=\"[popinContent]\"></ng-content>\n <gn-ui-button\n data-cy=\"cell-popin-close\"\n [style.--gn-ui-button-background]=\"'bg-transparent'\"\n class=\"absolute top-2 right-2\"\n type=\"light\"\n (buttonClick)=\"closeOverlay()\"\n extraClass=\"w-10 h-8 px-1\"\n >\n <ng-icon name=\"iconoirReduce\" size=\"24\"></ng-icon>\n </gn-ui-button>\n </div>\n }\n </ng-template>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }] }); }
|
|
25847
26265
|
}
|
|
25848
26266
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CellPopinComponent, decorators: [{
|
|
25849
26267
|
type: Component,
|
|
@@ -27933,6 +28351,23 @@ class DateRangeSearchField extends SimpleSearchField {
|
|
|
27933
28351
|
return 'dateRange';
|
|
27934
28352
|
}
|
|
27935
28353
|
}
|
|
28354
|
+
class BoundingBoxSearchField extends SimpleSearchField {
|
|
28355
|
+
getAvailableValues() {
|
|
28356
|
+
return of([]);
|
|
28357
|
+
}
|
|
28358
|
+
getFiltersForValues(values) {
|
|
28359
|
+
return of({
|
|
28360
|
+
[this.esFieldName]: values.map(Number),
|
|
28361
|
+
});
|
|
28362
|
+
}
|
|
28363
|
+
getValuesForFilter(filters) {
|
|
28364
|
+
const filter = filters[this.esFieldName];
|
|
28365
|
+
return of(isBoundingBox(filter) ? filter : []);
|
|
28366
|
+
}
|
|
28367
|
+
getType() {
|
|
28368
|
+
return 'spatialExtent';
|
|
28369
|
+
}
|
|
28370
|
+
}
|
|
27936
28371
|
marker('search.filters.availableServices.view');
|
|
27937
28372
|
marker('search.filters.availableServices.download');
|
|
27938
28373
|
class AvailableServicesField extends SimpleSearchField {
|
|
@@ -28077,6 +28512,7 @@ marker('search.filters.producerOrg');
|
|
|
28077
28512
|
marker('search.filters.publisherOrg');
|
|
28078
28513
|
marker('search.filters.user');
|
|
28079
28514
|
marker('search.filters.changeDate');
|
|
28515
|
+
marker('search.filters.spatialExtent');
|
|
28080
28516
|
class FieldsService {
|
|
28081
28517
|
constructor() {
|
|
28082
28518
|
this.injector = inject(Injector);
|
|
@@ -28100,6 +28536,7 @@ class FieldsService {
|
|
|
28100
28536
|
user: new UserSearchField(this.injector),
|
|
28101
28537
|
changeDate: new DateRangeSearchField('changeDate', this.injector, 'desc'),
|
|
28102
28538
|
availableServices: new AvailableServicesField(this.injector),
|
|
28539
|
+
spatialExtent: new BoundingBoxSearchField('spatialExtent', this.injector),
|
|
28103
28540
|
};
|
|
28104
28541
|
}
|
|
28105
28542
|
get supportedFields() {
|
|
@@ -29362,7 +29799,7 @@ class ContactPillComponent {
|
|
|
29362
29799
|
this.overlayOpen = false;
|
|
29363
29800
|
}
|
|
29364
29801
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ContactPillComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
29365
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: ContactPillComponent, isStandalone: true, selector: "gn-ui-contact-pill", inputs: { contact: "contact" }, ngImport: i0, template: "<gn-ui-button\n [type]=\"overlayOpen ? 'gray-light' : 'primary-light'\"\n extraClass=\"group w-full min-h-12 gap-3 justify-between py-2 pl-5 pr-4 rounded\"\n data-test=\"contact-pill\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <span\n class=\"font-title font-medium text-base leading-tight truncate\"\n [class]=\"!overlayOpen ? 'text-primary-black group-hover:text-white' : ''\"\n [title]=\"displayName\"\n >{{ displayName }}</span\n >\n <div\n class=\"gn-ui-card-icon items-center justify-center w-10 h-8\"\n [class]=\"\n !overlayOpen ? 'group-hover:border-white group-hover:text-white' : ''\n \"\n >\n @if (overlayOpen) {\n <ng-icon class=\"!w-6 !h-6 !text-[24px]\" name=\"matClose\"></ng-icon>\n } @else {\n <ng-icon class=\"!w-6 !h-6 !text-[24px]\" name=\"matInfoOutline\"></ng-icon>\n }\n </div>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayOffsetX]=\"overlayOffsetX\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div [style.width.px]=\"overlayWidth\">\n <gn-ui-contact-details [contact]=\"contact\"></gn-ui-contact-details>\n </div>\n</ng-template>\n", dependencies: [{ kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$
|
|
29802
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: ContactPillComponent, isStandalone: true, selector: "gn-ui-contact-pill", inputs: { contact: "contact" }, ngImport: i0, template: "<gn-ui-button\n [type]=\"overlayOpen ? 'gray-light' : 'primary-light'\"\n extraClass=\"group w-full min-h-12 gap-3 justify-between py-2 pl-5 pr-4 rounded\"\n data-test=\"contact-pill\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <span\n class=\"font-title font-medium text-base leading-tight truncate\"\n [class]=\"!overlayOpen ? 'text-primary-black group-hover:text-white' : ''\"\n [title]=\"displayName\"\n >{{ displayName }}</span\n >\n <div\n class=\"gn-ui-card-icon items-center justify-center w-10 h-8\"\n [class]=\"\n !overlayOpen ? 'group-hover:border-white group-hover:text-white' : ''\n \"\n >\n @if (overlayOpen) {\n <ng-icon class=\"!w-6 !h-6 !text-[24px]\" name=\"matClose\"></ng-icon>\n } @else {\n <ng-icon class=\"!w-6 !h-6 !text-[24px]\" name=\"matInfoOutline\"></ng-icon>\n }\n </div>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayOffsetX]=\"overlayOffsetX\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div [style.width.px]=\"overlayWidth\">\n <gn-ui-contact-details [contact]=\"contact\"></gn-ui-contact-details>\n </div>\n</ng-template>\n", dependencies: [{ kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: ContactDetailsComponent, selector: "gn-ui-contact-details", inputs: ["contact"] }], viewProviders: [provideIcons({ matClose, matInfoOutline })], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
29366
29803
|
}
|
|
29367
29804
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ContactPillComponent, decorators: [{
|
|
29368
29805
|
type: Component,
|
|
@@ -31284,19 +31721,111 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31284
31721
|
type: Output
|
|
31285
31722
|
}] } });
|
|
31286
31723
|
|
|
31724
|
+
class NotificationsService {
|
|
31725
|
+
constructor() {
|
|
31726
|
+
this.notifications$ = new BehaviorSubject([]);
|
|
31727
|
+
}
|
|
31728
|
+
showNotification(content, timeoutMs, error) {
|
|
31729
|
+
error && console.error(error);
|
|
31730
|
+
const id = Math.floor(Math.random() * 1000000);
|
|
31731
|
+
this.notifications$.next([...this.notifications$.value, { ...content, id }]);
|
|
31732
|
+
if (typeof timeoutMs !== 'undefined') {
|
|
31733
|
+
setTimeout(() => {
|
|
31734
|
+
this.removeNotificationById(id);
|
|
31735
|
+
}, timeoutMs);
|
|
31736
|
+
}
|
|
31737
|
+
return id;
|
|
31738
|
+
}
|
|
31739
|
+
removeNotificationById(id) {
|
|
31740
|
+
this.notifications$.next(this.notifications$.value.filter((n) => n.id !== id));
|
|
31741
|
+
}
|
|
31742
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
31743
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, providedIn: 'root' }); }
|
|
31744
|
+
}
|
|
31745
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, decorators: [{
|
|
31746
|
+
type: Injectable,
|
|
31747
|
+
args: [{
|
|
31748
|
+
providedIn: 'root',
|
|
31749
|
+
}]
|
|
31750
|
+
}] });
|
|
31751
|
+
|
|
31752
|
+
class NotificationsContainerComponent {
|
|
31753
|
+
constructor() {
|
|
31754
|
+
this.notificationsService = inject(NotificationsService);
|
|
31755
|
+
}
|
|
31756
|
+
trackById(index, notification) {
|
|
31757
|
+
return notification.id;
|
|
31758
|
+
}
|
|
31759
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31760
|
+
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: [
|
|
31761
|
+
trigger('enterExit', [
|
|
31762
|
+
transition(':enter', [
|
|
31763
|
+
animate('150ms', keyframes([
|
|
31764
|
+
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31765
|
+
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31766
|
+
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31767
|
+
])),
|
|
31768
|
+
]),
|
|
31769
|
+
transition(':leave', [
|
|
31770
|
+
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31771
|
+
]),
|
|
31772
|
+
]),
|
|
31773
|
+
], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31774
|
+
}
|
|
31775
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, decorators: [{
|
|
31776
|
+
type: Component,
|
|
31777
|
+
args: [{ selector: 'gn-ui-notifications-container', standalone: true, imports: [CommonModule, NotificationComponent], changeDetection: ChangeDetectionStrategy.OnPush, animations: [
|
|
31778
|
+
trigger('enterExit', [
|
|
31779
|
+
transition(':enter', [
|
|
31780
|
+
animate('150ms', keyframes([
|
|
31781
|
+
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31782
|
+
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31783
|
+
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31784
|
+
])),
|
|
31785
|
+
]),
|
|
31786
|
+
transition(':leave', [
|
|
31787
|
+
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31788
|
+
]),
|
|
31789
|
+
]),
|
|
31790
|
+
], 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" }]
|
|
31791
|
+
}] });
|
|
31792
|
+
|
|
31287
31793
|
class FilterDropdownComponent {
|
|
31288
31794
|
constructor() {
|
|
31289
31795
|
this.searchFacade = inject(SearchFacade);
|
|
31290
31796
|
this.searchService = inject(SearchService);
|
|
31291
31797
|
this.fieldsService = inject(FieldsService);
|
|
31798
|
+
this.notificationsService = inject(NotificationsService);
|
|
31799
|
+
this.translateService = inject(TranslateService);
|
|
31800
|
+
this.spatialExtentMaxFileSize = getOptionalSearchConfig()?.SPATIAL_EXTENT_MAX_FILE_SIZE;
|
|
31292
31801
|
this.selected$ = this.searchFacade.searchFilters$.pipe(switchMap((filters) => this.fieldsService.readFieldValuesFromFilters(filters)), map$1((fieldValues) => fieldValues[this.fieldName]), filter$1((selected) => !!selected), startWith$1([]), catchError(() => of([])));
|
|
31293
|
-
this.selectedDateRange$ = this.selected$.pipe(map$1((
|
|
31802
|
+
this.selectedDateRange$ = this.selected$.pipe(map$1((selected) => (Array.isArray(selected) ? {} : selected)));
|
|
31803
|
+
this.spatialExtentErrorNotificationId = null;
|
|
31294
31804
|
}
|
|
31295
31805
|
onSelectedValues(values) {
|
|
31296
31806
|
this.fieldsService
|
|
31297
31807
|
.buildFiltersFromFieldValues({ [this.fieldName]: values })
|
|
31298
31808
|
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
31299
31809
|
}
|
|
31810
|
+
onBboxChange(bbox) {
|
|
31811
|
+
console.log(bbox);
|
|
31812
|
+
this.clearSpatialExtentErrorNotification();
|
|
31813
|
+
}
|
|
31814
|
+
onSpatialExtentError(error) {
|
|
31815
|
+
this.clearSpatialExtentErrorNotification();
|
|
31816
|
+
this.spatialExtentErrorNotificationId =
|
|
31817
|
+
this.notificationsService.showNotification({
|
|
31818
|
+
type: 'error',
|
|
31819
|
+
title: this.translateService.instant('search.filters.spatialExtent.error.title'),
|
|
31820
|
+
text: this.translateService.instant(error.key, error.params),
|
|
31821
|
+
});
|
|
31822
|
+
}
|
|
31823
|
+
clearSpatialExtentErrorNotification() {
|
|
31824
|
+
if (this.spatialExtentErrorNotificationId === null)
|
|
31825
|
+
return;
|
|
31826
|
+
this.notificationsService.removeNotificationById(this.spatialExtentErrorNotificationId);
|
|
31827
|
+
this.spatialExtentErrorNotificationId = null;
|
|
31828
|
+
}
|
|
31300
31829
|
ngOnInit() {
|
|
31301
31830
|
this.fieldType = this.fieldsService.getFieldType(this.fieldName);
|
|
31302
31831
|
this.choices$ = this.fieldsService.getAvailableValues(this.fieldName).pipe(startWith$1([]), map$1((values) => values.map((v) => ({
|
|
@@ -31304,27 +31833,15 @@ class FilterDropdownComponent {
|
|
|
31304
31833
|
value: v.value.toString(), // converting to string for the dropdown
|
|
31305
31834
|
}))), catchError(() => of([])));
|
|
31306
31835
|
}
|
|
31307
|
-
|
|
31308
|
-
|
|
31309
|
-
|
|
31310
|
-
|
|
31311
|
-
|
|
31312
|
-
|
|
31313
|
-
if (!end)
|
|
31314
|
-
return;
|
|
31315
|
-
this.dateRange = { ...this.dateRange, end };
|
|
31316
|
-
if (this.dateRange.start && this.dateRange.end) {
|
|
31317
|
-
this.fieldsService
|
|
31318
|
-
.buildFiltersFromFieldValues({
|
|
31319
|
-
[this.fieldName]: this.dateRange,
|
|
31320
|
-
})
|
|
31321
|
-
.subscribe((filters) => {
|
|
31322
|
-
return this.searchService.updateFilters(filters);
|
|
31323
|
-
});
|
|
31324
|
-
}
|
|
31836
|
+
onDateRangeChange(dateRange) {
|
|
31837
|
+
this.fieldsService
|
|
31838
|
+
.buildFiltersFromFieldValues({
|
|
31839
|
+
[this.fieldName]: dateRange,
|
|
31840
|
+
})
|
|
31841
|
+
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
31325
31842
|
}
|
|
31326
31843
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31327
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FilterDropdownComponent, isStandalone: true, selector: "gn-ui-filter-dropdown", inputs: { fieldName: "fieldName", title: "title" }, ngImport: i0, template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [
|
|
31844
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FilterDropdownComponent, isStandalone: true, selector: "gn-ui-filter-dropdown", inputs: { fieldName: "fieldName", title: "title" }, ngImport: i0, template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [dateRange]=\"(selectedDateRange$ | async) ?? {}\"\n (dateRangeChange)=\"onDateRangeChange($event)\"\n ></gn-ui-date-range-dropdown>\n} @else if (fieldType === 'spatialExtent') {\n <gn-ui-spatial-extent-dropdown\n [title]=\"title\"\n [maxFileSizeMb]=\"spatialExtentMaxFileSize\"\n (bboxChange)=\"onBboxChange($event)\"\n (errorChange)=\"onSpatialExtentError($event)\"\n ></gn-ui-spatial-extent-dropdown>\n} @else {\n <gn-ui-dropdown-multiselect\n class=\"w-full\"\n [title]=\"title\"\n [maxRows]=\"6\"\n [choices]=\"(choices$ | async) || []\"\n [selected]=\"selected$ | async\"\n [allowSearch]=\"true\"\n (selectValues)=\"onSelectedValues($event)\"\n [attr.data-cy-field]=\"fieldName\"\n >\n </gn-ui-dropdown-multiselect>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DateRangeDropdownComponent, selector: "gn-ui-date-range-dropdown", inputs: ["title", "dateRange"], outputs: ["dateRangeChange"] }, { kind: "component", type: DropdownMultiselectComponent, selector: "gn-ui-dropdown-multiselect", inputs: ["title", "choices", "selected", "allowSearch", "maxRows", "searchInputValue"], outputs: ["selectValues"] }, { kind: "component", type: SpatialExtentDropdownComponent, selector: "gn-ui-spatial-extent-dropdown", inputs: ["title", "maxFileSizeMb"], outputs: ["bboxChange", "errorChange"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31328
31845
|
}
|
|
31329
31846
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, decorators: [{
|
|
31330
31847
|
type: Component,
|
|
@@ -31332,7 +31849,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31332
31849
|
CommonModule,
|
|
31333
31850
|
DateRangeDropdownComponent,
|
|
31334
31851
|
DropdownMultiselectComponent,
|
|
31335
|
-
|
|
31852
|
+
SpatialExtentDropdownComponent,
|
|
31853
|
+
], template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [dateRange]=\"(selectedDateRange$ | async) ?? {}\"\n (dateRangeChange)=\"onDateRangeChange($event)\"\n ></gn-ui-date-range-dropdown>\n} @else if (fieldType === 'spatialExtent') {\n <gn-ui-spatial-extent-dropdown\n [title]=\"title\"\n [maxFileSizeMb]=\"spatialExtentMaxFileSize\"\n (bboxChange)=\"onBboxChange($event)\"\n (errorChange)=\"onSpatialExtentError($event)\"\n ></gn-ui-spatial-extent-dropdown>\n} @else {\n <gn-ui-dropdown-multiselect\n class=\"w-full\"\n [title]=\"title\"\n [maxRows]=\"6\"\n [choices]=\"(choices$ | async) || []\"\n [selected]=\"selected$ | async\"\n [allowSearch]=\"true\"\n (selectValues)=\"onSelectedValues($event)\"\n [attr.data-cy-field]=\"fieldName\"\n >\n </gn-ui-dropdown-multiselect>\n}\n" }]
|
|
31336
31854
|
}], propDecorators: { fieldName: [{
|
|
31337
31855
|
type: Input
|
|
31338
31856
|
}], title: [{
|
|
@@ -31802,11 +32320,7 @@ const SortByEnum = {
|
|
|
31802
32320
|
RELEVANCY: ['desc', '_score'],
|
|
31803
32321
|
QUALITY_SCORE: ['desc', 'qualityScore'],
|
|
31804
32322
|
CHANGE_DATE: ['desc', 'changeDate'],
|
|
31805
|
-
|
|
31806
|
-
['desc', 'revisionDateForResource'],
|
|
31807
|
-
['desc', 'publicationDateForResource'],
|
|
31808
|
-
['desc', 'creationDateForResource'],
|
|
31809
|
-
],
|
|
32323
|
+
RESOURCE_DATE: ['desc', 'resourceDate.date'],
|
|
31810
32324
|
};
|
|
31811
32325
|
|
|
31812
32326
|
class SortByComponent {
|
|
@@ -31820,7 +32334,7 @@ class SortByComponent {
|
|
|
31820
32334
|
},
|
|
31821
32335
|
{
|
|
31822
32336
|
label: marker('results.sortBy.dateStamp'),
|
|
31823
|
-
value: SortByEnum.
|
|
32337
|
+
value: SortByEnum.RESOURCE_DATE,
|
|
31824
32338
|
},
|
|
31825
32339
|
{
|
|
31826
32340
|
label: marker('results.sortBy.popularity'),
|
|
@@ -31872,74 +32386,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31872
32386
|
args: ['gnUiSearchStateContainer']
|
|
31873
32387
|
}] } });
|
|
31874
32388
|
|
|
31875
|
-
class NotificationsService {
|
|
31876
|
-
constructor() {
|
|
31877
|
-
this.notifications$ = new BehaviorSubject([]);
|
|
31878
|
-
}
|
|
31879
|
-
showNotification(content, timeoutMs, error) {
|
|
31880
|
-
error && console.error(error);
|
|
31881
|
-
const id = Math.floor(Math.random() * 1000000);
|
|
31882
|
-
this.notifications$.next([...this.notifications$.value, { ...content, id }]);
|
|
31883
|
-
if (typeof timeoutMs === 'undefined')
|
|
31884
|
-
return;
|
|
31885
|
-
setTimeout(() => {
|
|
31886
|
-
this.removeNotificationById(id);
|
|
31887
|
-
}, timeoutMs);
|
|
31888
|
-
}
|
|
31889
|
-
removeNotificationById(id) {
|
|
31890
|
-
this.notifications$.next(this.notifications$.value.filter((n) => n.id !== id));
|
|
31891
|
-
}
|
|
31892
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
31893
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, providedIn: 'root' }); }
|
|
31894
|
-
}
|
|
31895
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, decorators: [{
|
|
31896
|
-
type: Injectable,
|
|
31897
|
-
args: [{
|
|
31898
|
-
providedIn: 'root',
|
|
31899
|
-
}]
|
|
31900
|
-
}] });
|
|
31901
|
-
|
|
31902
|
-
class NotificationsContainerComponent {
|
|
31903
|
-
constructor() {
|
|
31904
|
-
this.notificationsService = inject(NotificationsService);
|
|
31905
|
-
}
|
|
31906
|
-
trackById(index, notification) {
|
|
31907
|
-
return notification.id;
|
|
31908
|
-
}
|
|
31909
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31910
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: NotificationsContainerComponent, isStandalone: true, selector: "gn-ui-notifications-container", ngImport: i0, template: "<div class=\"flex flex-col gap-6 p-6 items-start pointer-events-none\">\n @for (\n notification of notificationsService.notifications$ | async;\n track trackById($index, notification)\n ) {\n <gn-ui-notification\n class=\"max-w-full pointer-events-auto\"\n [text]=\"notification.text\"\n [type]=\"notification.type\"\n [title]=\"notification.title\"\n [closeMessage]=\"notification.closeMessage\"\n (notificationClose)=\"\n notificationsService.removeNotificationById(notification.id)\n \"\n [@enterExit]\n ></gn-ui-notification>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: NotificationComponent, selector: "gn-ui-notification", inputs: ["type", "title", "text", "closeMessage"], outputs: ["notificationClose"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], animations: [
|
|
31911
|
-
trigger('enterExit', [
|
|
31912
|
-
transition(':enter', [
|
|
31913
|
-
animate('150ms', keyframes([
|
|
31914
|
-
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31915
|
-
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31916
|
-
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31917
|
-
])),
|
|
31918
|
-
]),
|
|
31919
|
-
transition(':leave', [
|
|
31920
|
-
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31921
|
-
]),
|
|
31922
|
-
]),
|
|
31923
|
-
], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31924
|
-
}
|
|
31925
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, decorators: [{
|
|
31926
|
-
type: Component,
|
|
31927
|
-
args: [{ selector: 'gn-ui-notifications-container', standalone: true, imports: [CommonModule, NotificationComponent], changeDetection: ChangeDetectionStrategy.OnPush, animations: [
|
|
31928
|
-
trigger('enterExit', [
|
|
31929
|
-
transition(':enter', [
|
|
31930
|
-
animate('150ms', keyframes([
|
|
31931
|
-
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31932
|
-
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31933
|
-
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31934
|
-
])),
|
|
31935
|
-
]),
|
|
31936
|
-
transition(':leave', [
|
|
31937
|
-
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31938
|
-
]),
|
|
31939
|
-
]),
|
|
31940
|
-
], template: "<div class=\"flex flex-col gap-6 p-6 items-start pointer-events-none\">\n @for (\n notification of notificationsService.notifications$ | async;\n track trackById($index, notification)\n ) {\n <gn-ui-notification\n class=\"max-w-full pointer-events-auto\"\n [text]=\"notification.text\"\n [type]=\"notification.type\"\n [title]=\"notification.title\"\n [closeMessage]=\"notification.closeMessage\"\n (notificationClose)=\"\n notificationsService.removeNotificationById(notification.id)\n \"\n [@enterExit]\n ></gn-ui-notification>\n }\n</div>\n" }]
|
|
31941
|
-
}] });
|
|
31942
|
-
|
|
31943
32389
|
class ResultsTableContainerComponent {
|
|
31944
32390
|
constructor() {
|
|
31945
32391
|
this.searchFacade = inject(SearchFacade);
|
|
@@ -32024,12 +32470,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
32024
32470
|
|
|
32025
32471
|
marker('search.filters.summaryLabel.user');
|
|
32026
32472
|
marker('search.filters.summaryLabel.changeDate');
|
|
32473
|
+
const OPEN_BOUND = '…';
|
|
32027
32474
|
class SearchFiltersSummaryItemComponent {
|
|
32028
32475
|
constructor() {
|
|
32029
32476
|
this.searchFacade = inject(SearchFacade);
|
|
32030
32477
|
this.searchService = inject(SearchService);
|
|
32031
32478
|
this.fieldsService = inject(FieldsService);
|
|
32032
|
-
this.
|
|
32479
|
+
this.dateService = inject(DateService);
|
|
32033
32480
|
this.translate = inject(TranslateService);
|
|
32034
32481
|
this.fieldValues$ = this.searchFacade.searchFilters$.pipe(switchMap((filters) => this.fieldsService.readFieldValuesFromFilters(filters)), map$2((fieldValues) => Array.isArray(fieldValues[this.fieldName])
|
|
32035
32482
|
? fieldValues[this.fieldName]
|
|
@@ -32056,9 +32503,10 @@ class SearchFiltersSummaryItemComponent {
|
|
|
32056
32503
|
getReadableValues(fieldValues) {
|
|
32057
32504
|
return fieldValues.map((value) => {
|
|
32058
32505
|
if (this.fieldType === 'dateRange') {
|
|
32506
|
+
const { start, end } = value;
|
|
32059
32507
|
return {
|
|
32060
32508
|
value,
|
|
32061
|
-
label: `${this.
|
|
32509
|
+
label: `${this.formatBound(start)} - ${this.formatBound(end)}`,
|
|
32062
32510
|
};
|
|
32063
32511
|
}
|
|
32064
32512
|
else if (this.fieldName === 'user') {
|
|
@@ -32069,6 +32517,15 @@ class SearchFiltersSummaryItemComponent {
|
|
|
32069
32517
|
}
|
|
32070
32518
|
});
|
|
32071
32519
|
}
|
|
32520
|
+
formatBound(date) {
|
|
32521
|
+
return date
|
|
32522
|
+
? this.dateService.formatDate(date, {
|
|
32523
|
+
day: '2-digit',
|
|
32524
|
+
month: '2-digit',
|
|
32525
|
+
year: 'numeric',
|
|
32526
|
+
})
|
|
32527
|
+
: OPEN_BOUND;
|
|
32528
|
+
}
|
|
32072
32529
|
async removeFilterValue(fieldValue) {
|
|
32073
32530
|
const currentFieldValues = await firstValueFrom(this.fieldValues$);
|
|
32074
32531
|
const updatedFieldValues = currentFieldValues
|
|
@@ -32081,11 +32538,11 @@ class SearchFiltersSummaryItemComponent {
|
|
|
32081
32538
|
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
32082
32539
|
}
|
|
32083
32540
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SearchFiltersSummaryItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
32084
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: SearchFiltersSummaryItemComponent, isStandalone: true, selector: "gn-ui-search-filters-summary-item", inputs: { fieldName: "fieldName" },
|
|
32541
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: SearchFiltersSummaryItemComponent, isStandalone: true, selector: "gn-ui-search-filters-summary-item", inputs: { fieldName: "fieldName" }, ngImport: i0, template: "@if ((fieldValues$ | async)?.length > 0) {\n <div class=\"flex flex-row items-center gap-2\">\n <span class=\"text-gray-800\" data-cy=\"filterSummaryLabel\">{{\n translatedLabel\n }}</span>\n @for (fieldValue of fieldValues$ | async; track fieldValue) {\n <gn-ui-badge\n class=\"gn-ui-badge\"\n [style.--gn-ui-badge-rounded]=\"'8px'\"\n [style.--gn-ui-badge-padding]=\"'3px 4px'\"\n [style.--gn-ui-badge-text-color]=\"'black'\"\n [style.--gn-ui-badge-background-color]=\"'#F2F2F2'\"\n [style.--gn-ui-badge-font-weight]=\"'700'\"\n [removable]=\"true\"\n (badgeRemoveClicked)=\"removeFilterValue(fieldValue.value)\"\n >{{ fieldValue.label }}</gn-ui-badge\n >\n }\n </div>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: BadgeComponent, selector: "gn-ui-badge", inputs: ["clickable", "removable"], outputs: ["badgeRemoveClicked", "badgeClicked"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }] }); }
|
|
32085
32542
|
}
|
|
32086
32543
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SearchFiltersSummaryItemComponent, decorators: [{
|
|
32087
32544
|
type: Component,
|
|
32088
|
-
args: [{ selector: 'gn-ui-search-filters-summary-item', standalone: true, imports: [CommonModule, BadgeComponent],
|
|
32545
|
+
args: [{ selector: 'gn-ui-search-filters-summary-item', standalone: true, imports: [CommonModule, BadgeComponent], template: "@if ((fieldValues$ | async)?.length > 0) {\n <div class=\"flex flex-row items-center gap-2\">\n <span class=\"text-gray-800\" data-cy=\"filterSummaryLabel\">{{\n translatedLabel\n }}</span>\n @for (fieldValue of fieldValues$ | async; track fieldValue) {\n <gn-ui-badge\n class=\"gn-ui-badge\"\n [style.--gn-ui-badge-rounded]=\"'8px'\"\n [style.--gn-ui-badge-padding]=\"'3px 4px'\"\n [style.--gn-ui-badge-text-color]=\"'black'\"\n [style.--gn-ui-badge-background-color]=\"'#F2F2F2'\"\n [style.--gn-ui-badge-font-weight]=\"'700'\"\n [removable]=\"true\"\n (badgeRemoveClicked)=\"removeFilterValue(fieldValue.value)\"\n >{{ fieldValue.label }}</gn-ui-badge\n >\n }\n </div>\n}\n" }]
|
|
32089
32546
|
}], propDecorators: { fieldName: [{
|
|
32090
32547
|
type: Input
|
|
32091
32548
|
}] } });
|
|
@@ -32329,11 +32786,11 @@ class AddLayerFromFileComponent {
|
|
|
32329
32786
|
}, 5000);
|
|
32330
32787
|
}
|
|
32331
32788
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AddLayerFromFileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
32332
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: AddLayerFromFileComponent, isStandalone: true, selector: "gn-ui-add-layer-from-file", ngImport: i0, template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n", styles: [""], dependencies: [{ kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: DragAndDropFileInputComponent, selector: "gn-ui-drag-and-drop-file-input", inputs: ["placeholder", "accept"], outputs: ["fileChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
32789
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: AddLayerFromFileComponent, isStandalone: true, selector: "gn-ui-add-layer-from-file", ngImport: i0, template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n textClass=\"text-gray-900 pl-2 py-2\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n", styles: [""], dependencies: [{ kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: DragAndDropFileInputComponent, selector: "gn-ui-drag-and-drop-file-input", inputs: ["placeholder", "accept", "maxFileSizeMb", "icon", "dropzoneBackgroundColor", "textClass", "extraClass", "showFileName"], outputs: ["fileChange", "errorChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
32333
32790
|
}
|
|
32334
32791
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AddLayerFromFileComponent, decorators: [{
|
|
32335
32792
|
type: Component,
|
|
32336
|
-
args: [{ selector: 'gn-ui-add-layer-from-file', standalone: true, imports: [TranslateDirective, TranslatePipe, DragAndDropFileInputComponent], template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n" }]
|
|
32793
|
+
args: [{ selector: 'gn-ui-add-layer-from-file', standalone: true, imports: [TranslateDirective, TranslatePipe, DragAndDropFileInputComponent], template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n textClass=\"text-gray-900 pl-2 py-2\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n" }]
|
|
32337
32794
|
}] });
|
|
32338
32795
|
|
|
32339
32796
|
class LayersPanelComponent {
|
|
@@ -32498,8 +32955,7 @@ const closeMetadata = createAction('[Metadata view] close');
|
|
|
32498
32955
|
Related actions
|
|
32499
32956
|
*/
|
|
32500
32957
|
const setRelated = createAction('[Metadata view] Set related records', props());
|
|
32501
|
-
const
|
|
32502
|
-
const setSourceOf = createAction('[Metadata view] Set has sources', props());
|
|
32958
|
+
const setLinkedRecords = createAction('[Metadata view] Set associated records', props());
|
|
32503
32959
|
/*
|
|
32504
32960
|
ChartConfig actions
|
|
32505
32961
|
*/
|
|
@@ -32531,9 +32987,8 @@ var mdview_actions = /*#__PURE__*/Object.freeze({
|
|
|
32531
32987
|
loadUserFeedbacksSuccess: loadUserFeedbacksSuccess,
|
|
32532
32988
|
setChartConfig: setChartConfig,
|
|
32533
32989
|
setIncompleteMetadata: setIncompleteMetadata,
|
|
32534
|
-
|
|
32535
|
-
|
|
32536
|
-
setSources: setSources
|
|
32990
|
+
setLinkedRecords: setLinkedRecords,
|
|
32991
|
+
setRelated: setRelated
|
|
32537
32992
|
});
|
|
32538
32993
|
|
|
32539
32994
|
const METADATA_VIEW_FEATURE_STATE_KEY = 'metadataView';
|
|
@@ -32576,12 +33031,9 @@ on(loadFullMetadata, (state) => ({
|
|
|
32576
33031
|
on(setRelated, (state, { related }) => ({
|
|
32577
33032
|
...state,
|
|
32578
33033
|
related,
|
|
32579
|
-
})), on(
|
|
33034
|
+
})), on(setLinkedRecords, (state, { linkedRecords }) => ({
|
|
32580
33035
|
...state,
|
|
32581
|
-
|
|
32582
|
-
})), on(setSourceOf, (state, { sourceOf }) => ({
|
|
32583
|
-
...state,
|
|
32584
|
-
sourceOf,
|
|
33036
|
+
linkedRecords,
|
|
32585
33037
|
})),
|
|
32586
33038
|
/*
|
|
32587
33039
|
ChartConfig reducers
|
|
@@ -32643,8 +33095,7 @@ const getMetadataError = createSelector(getMdViewState, (state) => state.error);
|
|
|
32643
33095
|
Related selectors
|
|
32644
33096
|
*/
|
|
32645
33097
|
const getRelated = createSelector(getMdViewState, (state) => state.related);
|
|
32646
|
-
const
|
|
32647
|
-
const getSourceOf = createSelector(getMdViewState, (state) => state.sourceOf);
|
|
33098
|
+
const getLinkedRecords = createSelector(getMdViewState, (state) => state.linkedRecords);
|
|
32648
33099
|
/*
|
|
32649
33100
|
Metadata selectors
|
|
32650
33101
|
*/
|
|
@@ -32745,212 +33196,10 @@ function parseHeaders(httpHeaders) {
|
|
|
32745
33196
|
return result;
|
|
32746
33197
|
}
|
|
32747
33198
|
|
|
32748
|
-
|
|
32749
|
-
|
|
32750
|
-
|
|
32751
|
-
? fileExtensionMatches[1].toLowerCase()
|
|
32752
|
-
: null;
|
|
32753
|
-
// 1. type hint
|
|
32754
|
-
if (typeHint)
|
|
32755
|
-
return Promise.resolve(typeHint);
|
|
32756
|
-
// 2. content-type header
|
|
32757
|
-
const headers = await fetchHeaders(url);
|
|
32758
|
-
if ('supportedType' in headers)
|
|
32759
|
-
return headers.supportedType;
|
|
32760
|
-
// 3. file extension from url
|
|
32761
|
-
else if (SupportedTypes.indexOf(fileExtension) > -1)
|
|
32762
|
-
return fileExtension;
|
|
32763
|
-
// no type inferred or hinted
|
|
32764
|
-
if ('mimeType' in headers)
|
|
32765
|
-
throw FetchError.unsupportedType(headers.mimeType);
|
|
32766
|
-
else
|
|
32767
|
-
throw FetchError.unknownType();
|
|
32768
|
-
}
|
|
32769
|
-
function fetchHeaders(url) {
|
|
32770
|
-
return sharedFetch(url, 'HEAD')
|
|
32771
|
-
.catch((error) => {
|
|
32772
|
-
throw FetchError.corsOrNetwork(error.message);
|
|
32773
|
-
})
|
|
32774
|
-
.then((response) => {
|
|
32775
|
-
if (!response.ok) {
|
|
32776
|
-
throw FetchError.http(response.status);
|
|
32777
|
-
}
|
|
32778
|
-
return parseHeaders(response.headers);
|
|
32779
|
-
});
|
|
32780
|
-
}
|
|
32781
|
-
function fetchDataAsText(url, cacheActive) {
|
|
32782
|
-
const fetchFactory = () => sharedFetch(url)
|
|
32783
|
-
.catch((error) => {
|
|
32784
|
-
throw FetchError.corsOrNetwork(error.message);
|
|
32785
|
-
})
|
|
32786
|
-
.then(async (response) => {
|
|
32787
|
-
if (!response.ok) {
|
|
32788
|
-
const clonedResponse = response.clone();
|
|
32789
|
-
throw FetchError.http(response.status, await clonedResponse.text());
|
|
32790
|
-
}
|
|
32791
|
-
const clonedResponse = response.clone();
|
|
32792
|
-
return clonedResponse.text();
|
|
32793
|
-
});
|
|
32794
|
-
return cacheActive ? useCache(fetchFactory, url, 'asText') : fetchFactory();
|
|
32795
|
-
}
|
|
32796
|
-
function fetchDataAsArrayBuffer(url, cacheActive) {
|
|
32797
|
-
const fetchFactory = () => sharedFetch(url)
|
|
32798
|
-
.catch((error) => {
|
|
32799
|
-
throw FetchError.corsOrNetwork(error.message);
|
|
32800
|
-
})
|
|
32801
|
-
.then(async (response) => {
|
|
32802
|
-
if (!response.ok) {
|
|
32803
|
-
throw FetchError.http(response.status, await response.text());
|
|
32804
|
-
}
|
|
32805
|
-
// convert to a numeric array so that we can store the response in cache
|
|
32806
|
-
return Array.from(new Uint8Array(await response.arrayBuffer()));
|
|
32807
|
-
});
|
|
32808
|
-
return (cacheActive ? useCache(fetchFactory, url, 'asArrayBuffer') : fetchFactory()).then((array) => {
|
|
32809
|
-
return new Uint8Array(array).buffer;
|
|
32810
|
-
});
|
|
32811
|
-
}
|
|
32812
|
-
function tryParseDate(input) {
|
|
32813
|
-
if (typeof input !== 'string')
|
|
32814
|
-
return null;
|
|
32815
|
-
function tryIso(value) {
|
|
32816
|
-
const parsed = parseISO(value);
|
|
32817
|
-
return isNaN(parsed.getDate()) ? null : parsed;
|
|
32818
|
-
}
|
|
32819
|
-
function tryFormat(value, format) {
|
|
32820
|
-
const parsed = parse$4(value, format, new Date());
|
|
32821
|
-
return isNaN(parsed.getDate()) ? null : parsed;
|
|
32822
|
-
}
|
|
32823
|
-
return (tryIso(input) ||
|
|
32824
|
-
tryFormat(input, 'dd/MM/yyyy') ||
|
|
32825
|
-
tryFormat(input, 'dd.MM.yyyy') ||
|
|
32826
|
-
tryFormat(input, 'MM/dd/yyyy') ||
|
|
32827
|
-
null);
|
|
32828
|
-
}
|
|
32829
|
-
function tryParseNumber(input) {
|
|
32830
|
-
if (isNaN(input))
|
|
32831
|
-
return null;
|
|
32832
|
-
const parsed = parseFloat(input);
|
|
32833
|
-
return isNaN(parsed) ? null : parsed;
|
|
32834
|
-
}
|
|
32835
|
-
function jsonToGeojsonFeature(object) {
|
|
32836
|
-
const { id, properties } = Object.keys(object)
|
|
32837
|
-
.map((property) => (property ? property : 'unknown')) //prevent empty strings
|
|
32838
|
-
.reduce((prev, curr) => curr.toLowerCase().endsWith('id')
|
|
32839
|
-
? {
|
|
32840
|
-
...prev,
|
|
32841
|
-
id: object[curr],
|
|
32842
|
-
}
|
|
32843
|
-
: {
|
|
32844
|
-
...prev,
|
|
32845
|
-
properties: { ...prev.properties, [curr]: object[curr] },
|
|
32846
|
-
}, { id: undefined, properties: {} });
|
|
32847
|
-
return {
|
|
32848
|
-
type: 'Feature',
|
|
32849
|
-
geometry: null,
|
|
32850
|
-
properties,
|
|
32851
|
-
...(id !== undefined && { id }),
|
|
32852
|
-
};
|
|
32853
|
-
}
|
|
32854
|
-
function mutateProperties(items, mutators) {
|
|
32855
|
-
const mutatorKeys = Object.keys(mutators);
|
|
32856
|
-
for (let i = 0, ii = items.length; i < ii; i++) {
|
|
32857
|
-
const item = items[i];
|
|
32858
|
-
for (const mutatorField of mutatorKeys) {
|
|
32859
|
-
if (!(mutatorField in item.properties))
|
|
32860
|
-
continue;
|
|
32861
|
-
item.properties[mutatorField] = mutators[mutatorField](item.properties[mutatorField]);
|
|
32862
|
-
}
|
|
32863
|
-
}
|
|
32864
|
-
return items;
|
|
32865
|
-
}
|
|
32866
|
-
const SAMPLE_SIZE = 20;
|
|
32867
|
-
/**
|
|
32868
|
-
* This will infer field types from a list of data items and cast the values accordingly
|
|
32869
|
-
* @param items
|
|
32870
|
-
* @param inferTypes
|
|
32871
|
-
*/
|
|
32872
|
-
function processItemProperties(items, inferTypes = false) {
|
|
32873
|
-
const foundFields = {};
|
|
32874
|
-
for (let i = 0, ii = Math.min(SAMPLE_SIZE, items.length); i < ii; i++) {
|
|
32875
|
-
const item = items[i];
|
|
32876
|
-
const fields = Object.keys(item.properties);
|
|
32877
|
-
for (const field of fields) {
|
|
32878
|
-
if (!(field in foundFields)) {
|
|
32879
|
-
foundFields[field] = {
|
|
32880
|
-
label: field,
|
|
32881
|
-
name: field,
|
|
32882
|
-
type: null,
|
|
32883
|
-
};
|
|
32884
|
-
}
|
|
32885
|
-
const value = item.properties[field];
|
|
32886
|
-
const info = foundFields[field];
|
|
32887
|
-
if (value === undefined || value === '' || value === null)
|
|
32888
|
-
continue;
|
|
32889
|
-
if (!inferTypes) {
|
|
32890
|
-
if (info.type === null && typeof value === 'number') {
|
|
32891
|
-
info.type = 'number';
|
|
32892
|
-
}
|
|
32893
|
-
else if (info.type === 'number' && typeof value !== 'number') {
|
|
32894
|
-
info.type = 'string';
|
|
32895
|
-
}
|
|
32896
|
-
continue;
|
|
32897
|
-
}
|
|
32898
|
-
const parsedNumber = tryParseNumber(value);
|
|
32899
|
-
if (info.type === null && parsedNumber !== null) {
|
|
32900
|
-
info.type = 'number';
|
|
32901
|
-
continue;
|
|
32902
|
-
}
|
|
32903
|
-
else if (info.type === 'number' && parsedNumber === null) {
|
|
32904
|
-
info.type = 'string';
|
|
32905
|
-
continue;
|
|
32906
|
-
}
|
|
32907
|
-
const parsedDate = tryParseDate(value);
|
|
32908
|
-
if (info.type === null && parsedDate !== null) {
|
|
32909
|
-
info.type = 'date';
|
|
32910
|
-
}
|
|
32911
|
-
else if (info.type === 'date' && parsedDate === null) {
|
|
32912
|
-
info.type = 'string';
|
|
32913
|
-
}
|
|
32914
|
-
}
|
|
32915
|
-
}
|
|
32916
|
-
const properties = [];
|
|
32917
|
-
const mutators = {};
|
|
32918
|
-
for (const field in foundFields) {
|
|
32919
|
-
const info = foundFields[field];
|
|
32920
|
-
if (info.type === 'number') {
|
|
32921
|
-
mutators[field] = tryParseNumber;
|
|
32922
|
-
}
|
|
32923
|
-
else if (info.type === 'date') {
|
|
32924
|
-
mutators[field] = tryParseDate;
|
|
32925
|
-
}
|
|
32926
|
-
properties.push({ ...info, type: info.type || 'string' });
|
|
32927
|
-
}
|
|
32928
|
-
if (inferTypes) {
|
|
32929
|
-
mutateProperties(items, mutators);
|
|
32930
|
-
}
|
|
32931
|
-
return { items, properties };
|
|
33199
|
+
const GEOMETRY_COLUMN_ALIAS = '__geometry__';
|
|
33200
|
+
function fieldToSql(name) {
|
|
33201
|
+
return `"${name.replace(/"/g, '""')}"`; // escape double quotes in field names
|
|
32932
33202
|
}
|
|
32933
|
-
/**
|
|
32934
|
-
* This creates a Proxy that allows reading and writing to the data item properties
|
|
32935
|
-
* as if it was a simple array of JSON objects
|
|
32936
|
-
* @param items
|
|
32937
|
-
*/
|
|
32938
|
-
function getJsonDataItemsProxy(items) {
|
|
32939
|
-
return new Proxy(items, {
|
|
32940
|
-
get(target, p) {
|
|
32941
|
-
if (typeof p === 'string' &&
|
|
32942
|
-
!Number.isNaN(parseInt(p)) &&
|
|
32943
|
-
target[p]?.properties) {
|
|
32944
|
-
return target[p].properties;
|
|
32945
|
-
}
|
|
32946
|
-
return target[p];
|
|
32947
|
-
},
|
|
32948
|
-
set() {
|
|
32949
|
-
throw new Error('This object is read-only');
|
|
32950
|
-
},
|
|
32951
|
-
});
|
|
32952
|
-
}
|
|
32953
|
-
|
|
32954
33203
|
function filterToSql(filter) {
|
|
32955
33204
|
const operator = filter[0];
|
|
32956
33205
|
const args = filter.slice(1);
|
|
@@ -32965,10 +33214,10 @@ function filterToSql(filter) {
|
|
|
32965
33214
|
case '=':
|
|
32966
33215
|
case '!=':
|
|
32967
33216
|
case 'like':
|
|
32968
|
-
return
|
|
33217
|
+
return `${fieldToSql(args[0])} ${operator.toUpperCase()} ${valueToSql(args[1])}`;
|
|
32969
33218
|
case 'in': {
|
|
32970
33219
|
const values = args.slice(1);
|
|
32971
|
-
return
|
|
33220
|
+
return `${fieldToSql(args[0])} IN (${values.map(valueToSql).join(', ')})`;
|
|
32972
33221
|
}
|
|
32973
33222
|
case 'and':
|
|
32974
33223
|
case 'or': {
|
|
@@ -32987,17 +33236,18 @@ function aggregationToSql(aggregation) {
|
|
|
32987
33236
|
const field = aggregation[1];
|
|
32988
33237
|
switch (operation) {
|
|
32989
33238
|
case 'average':
|
|
32990
|
-
return `AVG(
|
|
33239
|
+
return `CAST(AVG(${fieldToSql(field)}) AS DOUBLE) as ${fieldToSql(`average(${field})`)}`;
|
|
32991
33240
|
case 'sum':
|
|
32992
33241
|
case 'max':
|
|
32993
33242
|
case 'min':
|
|
32994
|
-
return
|
|
33243
|
+
return `CAST(${operation.toUpperCase()}(${fieldToSql(field)}) AS DOUBLE) as ${fieldToSql(`${operation}(${field})`)}`;
|
|
32995
33244
|
case 'count':
|
|
32996
|
-
return 'COUNT(*) as
|
|
33245
|
+
return 'CAST(COUNT(*) AS INTEGER) as "count()"'; // we don't need Bigint precision here
|
|
32997
33246
|
}
|
|
32998
33247
|
}
|
|
32999
33248
|
/**
|
|
33000
33249
|
* Leave arguments at null if not used
|
|
33250
|
+
* @param tableName
|
|
33001
33251
|
* @param selected
|
|
33002
33252
|
* @param filter
|
|
33003
33253
|
* @param sort
|
|
@@ -33006,22 +33256,25 @@ function aggregationToSql(aggregation) {
|
|
|
33006
33256
|
* @param groupBy
|
|
33007
33257
|
* @param aggregations
|
|
33008
33258
|
*/
|
|
33009
|
-
function generateSqlQuery(selected = null, filter = null, sort = null, startIndex = null, count = null, groupBy = null, aggregations = null) {
|
|
33259
|
+
function generateSqlQuery(tableName, selected = null, filter = null, sort = null, startIndex = null, count = null, groupBy = null, aggregations = null, geometryColumn = null) {
|
|
33010
33260
|
let sqlSelect = 'SELECT *';
|
|
33011
|
-
const sqlFrom =
|
|
33261
|
+
const sqlFrom = ` FROM ${tableName}`;
|
|
33012
33262
|
let sqlOrderBy = '';
|
|
33013
33263
|
let sqlWhere = '';
|
|
33014
33264
|
let sqlLimit = '';
|
|
33015
33265
|
let sqlGroupBy = '';
|
|
33016
33266
|
if (selected !== null) {
|
|
33017
|
-
sqlSelect = `SELECT ${selected.map(
|
|
33267
|
+
sqlSelect = `SELECT ${selected.map(fieldToSql).join(', ')}`;
|
|
33268
|
+
}
|
|
33269
|
+
if (geometryColumn !== null) {
|
|
33270
|
+
sqlSelect += `, ST_AsGeoJSON(${fieldToSql(geometryColumn)}) as ${GEOMETRY_COLUMN_ALIAS}`;
|
|
33018
33271
|
}
|
|
33019
33272
|
if (filter !== null) {
|
|
33020
33273
|
sqlWhere = ` WHERE ${filterToSql(filter)}`;
|
|
33021
33274
|
}
|
|
33022
33275
|
if (sort?.length) {
|
|
33023
33276
|
sqlOrderBy = ` ORDER BY ${sort
|
|
33024
|
-
.map((sort) =>
|
|
33277
|
+
.map((sort) => `${fieldToSql(sort[1])} ${sort[0].toUpperCase()}`)
|
|
33025
33278
|
.join(', ')}`;
|
|
33026
33279
|
}
|
|
33027
33280
|
if (startIndex !== null && count !== null) {
|
|
@@ -33031,17 +33284,212 @@ function generateSqlQuery(selected = null, filter = null, sort = null, startInde
|
|
|
33031
33284
|
sqlSelect = `SELECT ${aggregations.map(aggregationToSql).join(', ')}`;
|
|
33032
33285
|
const groupedByDistinct = groupBy.filter((group) => group[0] === 'distinct');
|
|
33033
33286
|
const sqlGroupByFields = groupedByDistinct
|
|
33034
|
-
.map((group) =>
|
|
33287
|
+
.map((group) => fieldToSql(group[1]))
|
|
33035
33288
|
.join(', ');
|
|
33036
33289
|
const sqlGroupBySelect = groupedByDistinct
|
|
33037
|
-
.map((group) =>
|
|
33290
|
+
.map((group) => `${fieldToSql(group[1])} as ${fieldToSql(`distinct(${group[1]})`)}`)
|
|
33038
33291
|
.join(', ');
|
|
33039
33292
|
if (sqlGroupByFields && sqlGroupBySelect) {
|
|
33040
33293
|
sqlGroupBy = ` GROUP BY ${sqlGroupByFields}`;
|
|
33041
33294
|
sqlSelect += `, ${sqlGroupBySelect}`;
|
|
33042
33295
|
}
|
|
33043
33296
|
}
|
|
33044
|
-
return sqlSelect + sqlFrom + sqlGroupBy +
|
|
33297
|
+
return sqlSelect + sqlFrom + sqlGroupBy + sqlWhere + sqlOrderBy + sqlLimit;
|
|
33298
|
+
}
|
|
33299
|
+
|
|
33300
|
+
function arrowTableToDataItems(table) {
|
|
33301
|
+
const fields = table.schema.fields;
|
|
33302
|
+
return table.toArray().map((row) => {
|
|
33303
|
+
const rowJson = row.toJSON();
|
|
33304
|
+
const feature = {
|
|
33305
|
+
type: 'Feature',
|
|
33306
|
+
geometry: null,
|
|
33307
|
+
properties: {},
|
|
33308
|
+
};
|
|
33309
|
+
const keys = Object.keys(rowJson);
|
|
33310
|
+
for (let i = 0; i < keys.length; i++) {
|
|
33311
|
+
const key = keys[i];
|
|
33312
|
+
const dataType = fields[i].type;
|
|
33313
|
+
let value = rowJson[key];
|
|
33314
|
+
// this might happen if we get an array inside a field
|
|
33315
|
+
if (value instanceof Vector) {
|
|
33316
|
+
value = Array.from(value);
|
|
33317
|
+
}
|
|
33318
|
+
// cast bigints to ints
|
|
33319
|
+
if (typeof value === 'bigint') {
|
|
33320
|
+
value = Number(value);
|
|
33321
|
+
}
|
|
33322
|
+
// rename columns with empty name
|
|
33323
|
+
if (!key) {
|
|
33324
|
+
feature.properties['unknown'] = value;
|
|
33325
|
+
continue;
|
|
33326
|
+
}
|
|
33327
|
+
// assign properties that look like an id to the geojson `id` field
|
|
33328
|
+
if (/^(object|feature)?_?id$/.test(key.toLowerCase()) &&
|
|
33329
|
+
(typeof value == 'string' || typeof value === 'number')) {
|
|
33330
|
+
feature.id = value;
|
|
33331
|
+
}
|
|
33332
|
+
// if a date is in timestamp (number) format, convert it to native
|
|
33333
|
+
if (typeof value === 'number' &&
|
|
33334
|
+
(DataType.isTimestamp(dataType) || DataType.isDate(dataType))) {
|
|
33335
|
+
value = new Date(value);
|
|
33336
|
+
}
|
|
33337
|
+
// if a binary field (most likely a geometry column): skip
|
|
33338
|
+
if (DataType.isBinary(dataType)) {
|
|
33339
|
+
continue;
|
|
33340
|
+
}
|
|
33341
|
+
// geometry column
|
|
33342
|
+
if (key === GEOMETRY_COLUMN_ALIAS && DataType.isUtf8(dataType)) {
|
|
33343
|
+
feature.geometry = JSON.parse(value);
|
|
33344
|
+
continue;
|
|
33345
|
+
}
|
|
33346
|
+
feature.properties[key] = value;
|
|
33347
|
+
}
|
|
33348
|
+
return feature;
|
|
33349
|
+
});
|
|
33350
|
+
}
|
|
33351
|
+
|
|
33352
|
+
// init code taken from https://github.com/duckdb/duckdb-wasm/blob/main/packages/duckdb-wasm/README.md
|
|
33353
|
+
const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();
|
|
33354
|
+
// from https://duckdb.org/docs/current/sql/data_types/overview
|
|
33355
|
+
const typesMapping = {
|
|
33356
|
+
INTEGER: 'number',
|
|
33357
|
+
SMALLINT: 'number',
|
|
33358
|
+
TINYINT: 'number',
|
|
33359
|
+
BIGINT: 'number',
|
|
33360
|
+
HUGEINT: 'number',
|
|
33361
|
+
UINTEGER: 'number',
|
|
33362
|
+
USMALLINT: 'number',
|
|
33363
|
+
UTINYINT: 'number',
|
|
33364
|
+
UBIGINT: 'number',
|
|
33365
|
+
UHUGEINT: 'number',
|
|
33366
|
+
DOUBLE: 'number',
|
|
33367
|
+
BIGNUM: 'number',
|
|
33368
|
+
DECIMAL: 'number',
|
|
33369
|
+
NUMERIC: 'number',
|
|
33370
|
+
FLOAT: 'number',
|
|
33371
|
+
REAL: 'number',
|
|
33372
|
+
'TIMESTAMP WITH TIME ZONE': 'date',
|
|
33373
|
+
TIMESTAMP: 'date',
|
|
33374
|
+
DATE: 'date',
|
|
33375
|
+
CHAR: 'string',
|
|
33376
|
+
VARCHAR: 'string',
|
|
33377
|
+
TEXT: 'string',
|
|
33378
|
+
UUID: 'string',
|
|
33379
|
+
BLOB: 'string',
|
|
33380
|
+
BIT: 'string',
|
|
33381
|
+
INTERVAL: 'string',
|
|
33382
|
+
LIST: 'string',
|
|
33383
|
+
BOOLEAN: 'boolean',
|
|
33384
|
+
};
|
|
33385
|
+
class Engine {
|
|
33386
|
+
async makeInit() {
|
|
33387
|
+
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);
|
|
33388
|
+
let worker_url = bundle.mainWorker;
|
|
33389
|
+
// this is necessary to let browsers execute WASM code coming from a cross-origin host
|
|
33390
|
+
if (worker_url.startsWith('https://')) {
|
|
33391
|
+
worker_url = URL.createObjectURL(new Blob([`importScripts("${worker_url}");`], {
|
|
33392
|
+
type: 'text/javascript',
|
|
33393
|
+
}));
|
|
33394
|
+
}
|
|
33395
|
+
const worker = new Worker(worker_url);
|
|
33396
|
+
const logger = new duckdb.ConsoleLogger(duckdb.LogLevel.WARNING);
|
|
33397
|
+
this.db = new duckdb.AsyncDuckDB(logger, worker);
|
|
33398
|
+
await this.db.instantiate(bundle.mainModule, bundle.pthreadWorker);
|
|
33399
|
+
// setup duckdb options
|
|
33400
|
+
const conn = await this.db.connect();
|
|
33401
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
33402
|
+
await conn.query(`INSTALL spatial; LOAD spatial;`);
|
|
33403
|
+
await conn.query(`SET TimeZone = '${timezone}';`);
|
|
33404
|
+
await conn.query(`SET threads = 1;`);
|
|
33405
|
+
// await conn.query(`SET memory_limit = '2GB';`) // used for experimenting when we encounter memory issues
|
|
33406
|
+
await conn.query(`SET preserve_insertion_order = false;`); // this reduces memory usage when loading a dataset
|
|
33407
|
+
conn.close();
|
|
33408
|
+
return this;
|
|
33409
|
+
}
|
|
33410
|
+
isReady() {
|
|
33411
|
+
if (!this.init_) {
|
|
33412
|
+
this.init_ = this.makeInit();
|
|
33413
|
+
}
|
|
33414
|
+
return this.init_;
|
|
33415
|
+
}
|
|
33416
|
+
/**
|
|
33417
|
+
* Returns information about a dataset once it's loaded:
|
|
33418
|
+
* - a list of properties description
|
|
33419
|
+
* - the name of the dataset geometry column (null if no geometry present)
|
|
33420
|
+
* @param datasetId name of the table under which the dataset will be stored
|
|
33421
|
+
* @param loadQuery duckdb-specific query for creating a table out of the data
|
|
33422
|
+
* @param forceReload if true, any existing data will be dropped and redownloaded
|
|
33423
|
+
*/
|
|
33424
|
+
async loadFile(datasetId, loadQuery, forceReload = false) {
|
|
33425
|
+
const conn = await this.db.connect();
|
|
33426
|
+
let results;
|
|
33427
|
+
// either we want to recreate the table, or we keep it if it already exists
|
|
33428
|
+
const safeLoadQuery = forceReload
|
|
33429
|
+
? `DROP TABLE IF EXISTS ${datasetId};
|
|
33430
|
+
${loadQuery}`
|
|
33431
|
+
: loadQuery.replace(/CREATE TABLE(?! IF NOT EXISTS)/gi, 'CREATE TABLE IF NOT EXISTS');
|
|
33432
|
+
// create the table
|
|
33433
|
+
try {
|
|
33434
|
+
results = await conn.query(safeLoadQuery);
|
|
33435
|
+
}
|
|
33436
|
+
catch (e) {
|
|
33437
|
+
throw new FetchError('parse', `DuckDB encountered an error when loading the data: ${e.message}`);
|
|
33438
|
+
}
|
|
33439
|
+
// read rows count
|
|
33440
|
+
results = await conn.query(`SELECT count(*) FROM ${datasetId}`);
|
|
33441
|
+
const { 'count_star()': recordsCount } = results.toArray()[0].toJSON();
|
|
33442
|
+
// read columns
|
|
33443
|
+
results = await conn.query(`SELECT * FROM information_schema.columns WHERE table_name = '${datasetId}';`);
|
|
33444
|
+
let geometryColumn = null;
|
|
33445
|
+
const properties = results
|
|
33446
|
+
.toArray()
|
|
33447
|
+
.map((row) => {
|
|
33448
|
+
const rowObj = row.toJSON();
|
|
33449
|
+
if (rowObj['data_type'] === 'GEOMETRY') {
|
|
33450
|
+
// the geometry is not part of the properties
|
|
33451
|
+
// note: right now we only keep one geometry column name, but if there are multiple they will
|
|
33452
|
+
// all get discarded
|
|
33453
|
+
geometryColumn = rowObj['column_name'];
|
|
33454
|
+
return null;
|
|
33455
|
+
}
|
|
33456
|
+
return {
|
|
33457
|
+
name: rowObj['column_name'],
|
|
33458
|
+
label: rowObj['column_name'],
|
|
33459
|
+
type: typesMapping[rowObj['data_type']] ?? 'other',
|
|
33460
|
+
};
|
|
33461
|
+
})
|
|
33462
|
+
.filter((prop) => prop !== null);
|
|
33463
|
+
conn.close();
|
|
33464
|
+
return {
|
|
33465
|
+
properties,
|
|
33466
|
+
geometryColumn,
|
|
33467
|
+
rowsCount: Number(recordsCount),
|
|
33468
|
+
};
|
|
33469
|
+
}
|
|
33470
|
+
// register a Uint8 buffer using a handle in the duckdb instance
|
|
33471
|
+
async registerData(name, buffer) {
|
|
33472
|
+
return this.db.registerFileBuffer(name, buffer);
|
|
33473
|
+
}
|
|
33474
|
+
/**
|
|
33475
|
+
* @param query duckdb-specific query for fetching items
|
|
33476
|
+
*/
|
|
33477
|
+
async queryItems(query) {
|
|
33478
|
+
const conn = await this.db.connect();
|
|
33479
|
+
const results = await conn.query(query);
|
|
33480
|
+
conn.close();
|
|
33481
|
+
return arrowTableToDataItems(results);
|
|
33482
|
+
}
|
|
33483
|
+
close() {
|
|
33484
|
+
this.db?.terminate();
|
|
33485
|
+
}
|
|
33486
|
+
}
|
|
33487
|
+
let engine = null;
|
|
33488
|
+
async function getEngine() {
|
|
33489
|
+
if (!engine) {
|
|
33490
|
+
engine = new Engine();
|
|
33491
|
+
}
|
|
33492
|
+
return engine.isReady();
|
|
33045
33493
|
}
|
|
33046
33494
|
|
|
33047
33495
|
class BaseReader {
|
|
@@ -33054,10 +33502,18 @@ class BaseReader {
|
|
|
33054
33502
|
this.sort = null;
|
|
33055
33503
|
this.startIndex = null;
|
|
33056
33504
|
this.count = null;
|
|
33505
|
+
this.loadPromise_ = Promise.resolve();
|
|
33506
|
+
this.cacheEnabled = false;
|
|
33507
|
+
}
|
|
33508
|
+
enableCache(enabled) {
|
|
33509
|
+
this.cacheEnabled = enabled;
|
|
33057
33510
|
}
|
|
33058
33511
|
load() {
|
|
33059
33512
|
throw new Error('not implemented');
|
|
33060
33513
|
}
|
|
33514
|
+
get isLoaded() {
|
|
33515
|
+
return this.loadPromise_;
|
|
33516
|
+
}
|
|
33061
33517
|
get properties() {
|
|
33062
33518
|
throw new Error('not implemented');
|
|
33063
33519
|
}
|
|
@@ -33106,178 +33562,237 @@ class BaseReader {
|
|
|
33106
33562
|
}
|
|
33107
33563
|
}
|
|
33108
33564
|
|
|
33109
|
-
|
|
33110
|
-
|
|
33111
|
-
|
|
33112
|
-
|
|
33113
|
-
|
|
33114
|
-
|
|
33115
|
-
|
|
33116
|
-
|
|
33117
|
-
|
|
33118
|
-
|
|
33119
|
-
|
|
33120
|
-
|
|
33121
|
-
|
|
33565
|
+
/**
|
|
33566
|
+
* This reader handles file formats supported natively by DuckDB
|
|
33567
|
+
*/
|
|
33568
|
+
class BaseFileReader extends BaseReader {
|
|
33569
|
+
// a table id should not exceed 63 chars
|
|
33570
|
+
generateDatasetId() {
|
|
33571
|
+
// generate a hash out of the url
|
|
33572
|
+
let hash = 0;
|
|
33573
|
+
for (const char of this.url) {
|
|
33574
|
+
hash = (hash << 5) - hash + char.charCodeAt(0);
|
|
33575
|
+
hash = hash >>> 0; // make it unsigned
|
|
33576
|
+
}
|
|
33577
|
+
return `datafetcher_${hash.toString(16)}`;
|
|
33578
|
+
}
|
|
33579
|
+
async getLoadQuery() {
|
|
33122
33580
|
throw new Error('not implemented');
|
|
33123
33581
|
}
|
|
33124
|
-
load() {
|
|
33125
|
-
this.
|
|
33582
|
+
async load() {
|
|
33583
|
+
this.datasetId = this.generateDatasetId();
|
|
33584
|
+
this.loadPromise_ = getEngine()
|
|
33585
|
+
.then((engine) => {
|
|
33586
|
+
this.engine = engine;
|
|
33587
|
+
return this.getLoadQuery();
|
|
33588
|
+
})
|
|
33589
|
+
.then((loadQuery) => this.engine.loadFile(this.datasetId, loadQuery, !this.cacheEnabled))
|
|
33590
|
+
.then((datasetInfo) => {
|
|
33591
|
+
this.properties_ = datasetInfo.properties;
|
|
33592
|
+
this.geometryColumn = datasetInfo.geometryColumn;
|
|
33593
|
+
this.rowsCount = datasetInfo.rowsCount;
|
|
33594
|
+
// returns void for the loaded promise
|
|
33595
|
+
});
|
|
33126
33596
|
}
|
|
33127
33597
|
get properties() {
|
|
33128
|
-
return this.
|
|
33598
|
+
return this.isLoaded.then(() => this.properties_);
|
|
33129
33599
|
}
|
|
33130
33600
|
get info() {
|
|
33131
|
-
return this.
|
|
33132
|
-
itemsCount:
|
|
33601
|
+
return this.isLoaded.then(() => ({
|
|
33602
|
+
itemsCount: this.rowsCount,
|
|
33603
|
+
hasGeometry: !!this.geometryColumn,
|
|
33133
33604
|
}));
|
|
33134
33605
|
}
|
|
33135
33606
|
async read() {
|
|
33136
|
-
|
|
33137
|
-
//
|
|
33138
|
-
|
|
33139
|
-
|
|
33140
|
-
|
|
33141
|
-
this.sort == null &&
|
|
33142
|
-
this.filter == null &&
|
|
33143
|
-
this.startIndex == null &&
|
|
33144
|
-
this.count == null) {
|
|
33145
|
-
return items;
|
|
33146
|
-
}
|
|
33147
|
-
const jsonItems = getJsonDataItemsProxy(items);
|
|
33148
|
-
const query = generateSqlQuery(this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations);
|
|
33149
|
-
const result = await import('alasql').then((module) => module.default(query, [jsonItems]));
|
|
33150
|
-
return result.map(jsonToGeojsonFeature);
|
|
33151
|
-
}
|
|
33152
|
-
}
|
|
33153
|
-
|
|
33154
|
-
function parseCsv(text) {
|
|
33155
|
-
// first parse the header to guess the delimiter
|
|
33156
|
-
// note that we do that to not rely on Papaparse logic for guessing delimiter
|
|
33157
|
-
let delimiter;
|
|
33158
|
-
try {
|
|
33159
|
-
const header = text.split('\n')[0];
|
|
33160
|
-
const result = Papa.parse(header, {
|
|
33161
|
-
header: false,
|
|
33162
|
-
});
|
|
33163
|
-
delimiter = result.meta.delimiter;
|
|
33607
|
+
await this.isLoaded;
|
|
33608
|
+
// if only certain fields are selected, omit the geometry
|
|
33609
|
+
const geometryColumn = this.selected === null ? this.geometryColumn : null;
|
|
33610
|
+
const query = generateSqlQuery(this.datasetId, this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations, geometryColumn);
|
|
33611
|
+
return this.engine.queryItems(query);
|
|
33164
33612
|
}
|
|
33165
|
-
catch (e) {
|
|
33166
|
-
throw new Error('CSV parsing failed: the delimiter could not be guessed');
|
|
33167
|
-
}
|
|
33168
|
-
const parsed = Papa.parse(text, {
|
|
33169
|
-
header: true,
|
|
33170
|
-
skipEmptyLines: true,
|
|
33171
|
-
delimiter,
|
|
33172
|
-
});
|
|
33173
|
-
if (parsed.errors.length) {
|
|
33174
|
-
throw new Error('CSV parsing failed for the following reasons:\n' +
|
|
33175
|
-
parsed.errors
|
|
33176
|
-
.map((error) => `* ${error.message} at row ${error.row}, column ${error.index}`)
|
|
33177
|
-
.join('\n'));
|
|
33178
|
-
}
|
|
33179
|
-
const items = parsed.data.map(jsonToGeojsonFeature);
|
|
33180
|
-
return processItemProperties(items, true);
|
|
33181
33613
|
}
|
|
33614
|
+
|
|
33182
33615
|
class CsvReader extends BaseFileReader {
|
|
33183
|
-
|
|
33184
|
-
|
|
33616
|
+
async getLoadQuery() {
|
|
33617
|
+
// first we get a list of columns hich have a detected type of VARCHAR
|
|
33618
|
+
// 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
|
|
33619
|
+
return `
|
|
33620
|
+
SET VARIABLE strColumns = (SELECT list(name) FROM (SELECT unnest(Columns, recursive := true) FROM sniff_csv("${this.url}")) WHERE type = 'VARCHAR');
|
|
33621
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM read_csv("${this.url}",
|
|
33622
|
+
force_not_null = getvariable('strColumns'),
|
|
33623
|
+
auto_type_candidates = ['NULL', 'BOOLEAN', 'INTEGER', 'DOUBLE', 'DATE', 'VARCHAR']
|
|
33624
|
+
);`;
|
|
33185
33625
|
}
|
|
33186
33626
|
}
|
|
33187
33627
|
|
|
33188
|
-
/**
|
|
33189
|
-
* This parser only supports arrays of simple flat objects with properties
|
|
33190
|
-
* @param text
|
|
33191
|
-
*/
|
|
33192
|
-
function parseJson(text) {
|
|
33193
|
-
const parsed = JSON.parse(text);
|
|
33194
|
-
if (!Array.isArray(parsed)) {
|
|
33195
|
-
throw new Error('Could not parse JSON, expected an array at root level');
|
|
33196
|
-
}
|
|
33197
|
-
return processItemProperties(parsed.map(jsonToGeojsonFeature));
|
|
33198
|
-
}
|
|
33199
33628
|
class JsonReader extends BaseFileReader {
|
|
33200
|
-
|
|
33201
|
-
return
|
|
33629
|
+
async getLoadQuery() {
|
|
33630
|
+
return `
|
|
33631
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM read_json("${this.url}",
|
|
33632
|
+
maximum_object_size = 536870912 -- 500MB
|
|
33633
|
+
);`;
|
|
33202
33634
|
}
|
|
33203
33635
|
}
|
|
33204
33636
|
|
|
33205
|
-
/**
|
|
33206
|
-
* This parser supports both Geojson Feature collections or arrays
|
|
33207
|
-
* of Features
|
|
33208
|
-
* @param text
|
|
33209
|
-
*/
|
|
33210
|
-
function parseGeojson(text) {
|
|
33211
|
-
const parsed = JSON.parse(text);
|
|
33212
|
-
const features = parsed.type === 'FeatureCollection' ? parsed.features : parsed;
|
|
33213
|
-
if (!Array.isArray(features)) {
|
|
33214
|
-
throw new Error('Could not parse GeoJSON, expected a features collection or an array of features at root level');
|
|
33215
|
-
}
|
|
33216
|
-
return processItemProperties(features);
|
|
33217
|
-
}
|
|
33218
33637
|
class GeojsonReader extends BaseFileReader {
|
|
33219
|
-
|
|
33220
|
-
return
|
|
33638
|
+
async getLoadQuery() {
|
|
33639
|
+
return `
|
|
33640
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM st_read("${this.url}",
|
|
33641
|
+
allowed_drivers = ['GeoJSON']
|
|
33642
|
+
);`;
|
|
33221
33643
|
}
|
|
33222
33644
|
}
|
|
33223
33645
|
|
|
33646
|
+
async function inferDatasetType(url, typeHint) {
|
|
33647
|
+
const fileExtensionMatches = new URL(url, typeof window !== 'undefined' ? window.location.toString() : undefined).pathname.match(/\.(.+)$/);
|
|
33648
|
+
const fileExtension = fileExtensionMatches && fileExtensionMatches.length
|
|
33649
|
+
? fileExtensionMatches[1].toLowerCase()
|
|
33650
|
+
: null;
|
|
33651
|
+
// 1. type hint
|
|
33652
|
+
if (typeHint)
|
|
33653
|
+
return Promise.resolve(typeHint);
|
|
33654
|
+
// 2. content-type header
|
|
33655
|
+
const headers = await fetchHeaders(url);
|
|
33656
|
+
if ('supportedType' in headers)
|
|
33657
|
+
return headers.supportedType;
|
|
33658
|
+
// 3. file extension from url
|
|
33659
|
+
else if (SupportedTypes.indexOf(fileExtension) > -1)
|
|
33660
|
+
return fileExtension;
|
|
33661
|
+
// no type inferred or hinted
|
|
33662
|
+
if ('mimeType' in headers)
|
|
33663
|
+
throw FetchError.unsupportedType(headers.mimeType);
|
|
33664
|
+
else
|
|
33665
|
+
throw FetchError.unknownType();
|
|
33666
|
+
}
|
|
33667
|
+
function fetchHeaders(url) {
|
|
33668
|
+
return sharedFetch(url, 'HEAD')
|
|
33669
|
+
.catch((error) => {
|
|
33670
|
+
throw FetchError.corsOrNetwork(error.message);
|
|
33671
|
+
})
|
|
33672
|
+
.then((response) => {
|
|
33673
|
+
if (!response.ok) {
|
|
33674
|
+
throw FetchError.http(response.status);
|
|
33675
|
+
}
|
|
33676
|
+
return parseHeaders(response.headers);
|
|
33677
|
+
});
|
|
33678
|
+
}
|
|
33679
|
+
function fetchDataAsText(url, cacheActive) {
|
|
33680
|
+
const fetchFactory = () => sharedFetch(url)
|
|
33681
|
+
.catch((error) => {
|
|
33682
|
+
throw FetchError.corsOrNetwork(error.message);
|
|
33683
|
+
})
|
|
33684
|
+
.then(async (response) => {
|
|
33685
|
+
if (!response.ok) {
|
|
33686
|
+
const clonedResponse = response.clone();
|
|
33687
|
+
throw FetchError.http(response.status, await clonedResponse.text());
|
|
33688
|
+
}
|
|
33689
|
+
const clonedResponse = response.clone();
|
|
33690
|
+
return clonedResponse.text();
|
|
33691
|
+
});
|
|
33692
|
+
return cacheActive ? useCache(fetchFactory, url, 'asText') : fetchFactory();
|
|
33693
|
+
}
|
|
33694
|
+
function fetchDataAsArrayBuffer(url, cacheActive) {
|
|
33695
|
+
const fetchFactory = () => sharedFetch(url)
|
|
33696
|
+
.catch((error) => {
|
|
33697
|
+
throw FetchError.corsOrNetwork(error.message);
|
|
33698
|
+
})
|
|
33699
|
+
.then(async (response) => {
|
|
33700
|
+
if (!response.ok) {
|
|
33701
|
+
throw FetchError.http(response.status, await response.text());
|
|
33702
|
+
}
|
|
33703
|
+
// convert to a numeric array so that we can store the response in cache
|
|
33704
|
+
return Array.from(new Uint8Array(await response.arrayBuffer()));
|
|
33705
|
+
});
|
|
33706
|
+
return (cacheActive ? useCache(fetchFactory, url, 'asArrayBuffer') : fetchFactory()).then((array) => {
|
|
33707
|
+
return new Uint8Array(array).buffer;
|
|
33708
|
+
});
|
|
33709
|
+
}
|
|
33224
33710
|
/**
|
|
33225
|
-
* This
|
|
33226
|
-
*
|
|
33227
|
-
* @param
|
|
33711
|
+
* This creates a Proxy that allows reading and writing to the data item properties
|
|
33712
|
+
* as if it was a simple array of JSON objects
|
|
33713
|
+
* @param items
|
|
33228
33714
|
*/
|
|
33229
|
-
function
|
|
33230
|
-
return
|
|
33231
|
-
|
|
33232
|
-
|
|
33233
|
-
|
|
33234
|
-
|
|
33235
|
-
|
|
33236
|
-
|
|
33237
|
-
|
|
33715
|
+
function getJsonDataItemsProxy(items) {
|
|
33716
|
+
return new Proxy(items, {
|
|
33717
|
+
get(target, p) {
|
|
33718
|
+
if (typeof p === 'string' &&
|
|
33719
|
+
!Number.isNaN(parseInt(p)) &&
|
|
33720
|
+
target[p]?.properties) {
|
|
33721
|
+
return target[p].properties;
|
|
33722
|
+
}
|
|
33723
|
+
return target[p];
|
|
33724
|
+
},
|
|
33725
|
+
set() {
|
|
33726
|
+
throw new Error('This object is read-only');
|
|
33727
|
+
},
|
|
33238
33728
|
});
|
|
33239
33729
|
}
|
|
33730
|
+
|
|
33240
33731
|
class ExcelReader extends BaseFileReader {
|
|
33241
|
-
|
|
33242
|
-
|
|
33732
|
+
async getLoadQuery() {
|
|
33733
|
+
// we download the file as an array buffer first, in order to be able to check if it's an XLS file
|
|
33734
|
+
let buffer = await fetchDataAsArrayBuffer(this.url, this.cacheEnabled);
|
|
33735
|
+
const bufferHandle = `B${this.datasetId}`;
|
|
33736
|
+
// checking against the magic number at the beginning of XLS files, see https://en.wikipedia.org/wiki/List_of_file_signatures
|
|
33737
|
+
const magicNumber = new Uint8Array(buffer, 0, 8); // first 8 bytes
|
|
33738
|
+
const isXls = Array.from(magicNumber)
|
|
33739
|
+
.map((n) => n.toString(16).toUpperCase())
|
|
33740
|
+
.join(' ') === 'D0 CF 11 E0 A1 B1 1A E1';
|
|
33741
|
+
// uh oh, this is an XLS file (not supported by duckdb); convert it to CSV using the xlsx package
|
|
33742
|
+
if (isXls) {
|
|
33743
|
+
buffer = await import('xlsx').then(({ read, utils }) => {
|
|
33744
|
+
const workbook = read(buffer);
|
|
33745
|
+
const json = utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]]);
|
|
33746
|
+
return new TextEncoder().encode(JSON.stringify(json)).buffer;
|
|
33747
|
+
});
|
|
33748
|
+
}
|
|
33749
|
+
const duckDbFn = isXls ? 'read_json' : 'read_xlsx';
|
|
33750
|
+
await this.engine.registerData(bufferHandle, new Uint8Array(buffer));
|
|
33751
|
+
return `
|
|
33752
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM ${duckDbFn}("${bufferHandle}", ignore_errors = true);`;
|
|
33753
|
+
}
|
|
33754
|
+
}
|
|
33755
|
+
|
|
33756
|
+
class GmlReader extends BaseFileReader {
|
|
33757
|
+
async getLoadQuery() {
|
|
33758
|
+
return `
|
|
33759
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM st_read("${this.url}",
|
|
33760
|
+
allowed_drivers = ['GML']
|
|
33761
|
+
);`;
|
|
33243
33762
|
}
|
|
33244
33763
|
}
|
|
33245
33764
|
|
|
33246
|
-
|
|
33247
|
-
|
|
33248
|
-
const
|
|
33765
|
+
const formatGeojson = new GeoJSON$1();
|
|
33766
|
+
function parseGeojson(text) {
|
|
33767
|
+
const parsed = JSON.parse(text);
|
|
33768
|
+
const features = parsed.type === 'FeatureCollection' ? parsed.features : parsed;
|
|
33769
|
+
if (!Array.isArray(features)) {
|
|
33770
|
+
throw new Error('Could not parse GeoJSON, expected a features collection or an array of features at root level');
|
|
33771
|
+
}
|
|
33772
|
+
return features;
|
|
33773
|
+
}
|
|
33774
|
+
function parseGml(text, featureType, version) {
|
|
33775
|
+
const parts = featureType.split(':');
|
|
33776
|
+
const regex = new RegExp(`xmlns:${parts[0]}=["']([^'"]*)["']`);
|
|
33249
33777
|
const match = regex.exec(text);
|
|
33250
33778
|
if (match && match.length >= 2) {
|
|
33251
|
-
const
|
|
33779
|
+
const wfs = new WFS({
|
|
33252
33780
|
featureNS: match[1],
|
|
33253
|
-
featureType:
|
|
33781
|
+
featureType: parts[1],
|
|
33254
33782
|
version: version,
|
|
33255
33783
|
});
|
|
33256
33784
|
let features;
|
|
33257
33785
|
try {
|
|
33258
|
-
features =
|
|
33786
|
+
features = wfs.readFeatures(text);
|
|
33259
33787
|
}
|
|
33260
33788
|
catch (e) {
|
|
33261
|
-
throw Error(
|
|
33789
|
+
throw Error(`Couldn't parse WFS with GML features: ${e.message}`);
|
|
33262
33790
|
}
|
|
33263
|
-
const geojsonItem =
|
|
33264
|
-
return
|
|
33791
|
+
const geojsonItem = formatGeojson.writeFeaturesObject(features);
|
|
33792
|
+
return geojsonItem.features;
|
|
33265
33793
|
}
|
|
33266
33794
|
throw Error("Couldn't retrieve namespace url");
|
|
33267
33795
|
}
|
|
33268
|
-
class GmlReader extends BaseFileReader {
|
|
33269
|
-
constructor(url, namespace, version, cacheActive = true) {
|
|
33270
|
-
super(url);
|
|
33271
|
-
this.url = url;
|
|
33272
|
-
this.namespace = namespace;
|
|
33273
|
-
this.version = version;
|
|
33274
|
-
this.cacheActive = cacheActive;
|
|
33275
|
-
}
|
|
33276
|
-
getData() {
|
|
33277
|
-
return fetchDataAsText(this.url, this.cacheActive).then((text) => parseGml(text, this.namespace, this.version));
|
|
33278
|
-
}
|
|
33279
|
-
}
|
|
33280
|
-
|
|
33281
33796
|
async function getWfsEndpoint(wfsUrl) {
|
|
33282
33797
|
try {
|
|
33283
33798
|
return await new WfsEndpoint(wfsUrl).isReady();
|
|
@@ -33305,21 +33820,65 @@ async function getWfsEndpoint(wfsUrl) {
|
|
|
33305
33820
|
}
|
|
33306
33821
|
}
|
|
33307
33822
|
}
|
|
33308
|
-
class WfsReader extends
|
|
33309
|
-
constructor(url,
|
|
33310
|
-
super(url
|
|
33311
|
-
this.endpoint =
|
|
33312
|
-
this.
|
|
33313
|
-
|
|
33823
|
+
class WfsReader extends BaseReader {
|
|
33824
|
+
constructor(url, featureTypeName) {
|
|
33825
|
+
super(url);
|
|
33826
|
+
this.endpoint = getWfsEndpoint(url);
|
|
33827
|
+
this.featureType = this.endpoint
|
|
33828
|
+
.then((endpoint) => {
|
|
33829
|
+
const featureTypes = endpoint.getFeatureTypes();
|
|
33830
|
+
return endpoint.getFeatureTypeFull(featureTypes.length === 1 && !featureTypeName
|
|
33831
|
+
? featureTypes[0].name
|
|
33832
|
+
: featureTypeName);
|
|
33833
|
+
})
|
|
33834
|
+
.then((featureType) => {
|
|
33835
|
+
if (!featureType) {
|
|
33836
|
+
throw new Error('wfs.featuretype.notfound');
|
|
33837
|
+
}
|
|
33838
|
+
return featureType;
|
|
33839
|
+
});
|
|
33840
|
+
}
|
|
33841
|
+
get backupReader() {
|
|
33842
|
+
if (this.backupReader_) {
|
|
33843
|
+
return this.backupReader_;
|
|
33844
|
+
}
|
|
33845
|
+
this.backupReader_ = Promise.all([this.endpoint, this.featureType]).then(([endpoint, featureType]) => {
|
|
33846
|
+
let reader;
|
|
33847
|
+
if (endpoint.supportsJson(featureType.name)) {
|
|
33848
|
+
reader = new GeojsonReader(endpoint.getFeatureUrl(featureType.name, {
|
|
33849
|
+
asJson: true,
|
|
33850
|
+
outputCrs: 'EPSG:4326',
|
|
33851
|
+
}));
|
|
33852
|
+
}
|
|
33853
|
+
else {
|
|
33854
|
+
if (featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')) &&
|
|
33855
|
+
(featureType.defaultCrs === 'EPSG:4326' ||
|
|
33856
|
+
featureType.otherCrs?.includes('EPSG:4326'))) {
|
|
33857
|
+
reader = new GmlReader(endpoint.getFeatureUrl(featureType.name, {
|
|
33858
|
+
outputFormat: featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')),
|
|
33859
|
+
outputCrs: 'EPSG:4326',
|
|
33860
|
+
}));
|
|
33861
|
+
}
|
|
33862
|
+
}
|
|
33863
|
+
reader.enableCache(this.cacheEnabled);
|
|
33864
|
+
reader.load();
|
|
33865
|
+
return reader;
|
|
33866
|
+
});
|
|
33867
|
+
return this.backupReader_;
|
|
33314
33868
|
}
|
|
33315
33869
|
get properties() {
|
|
33316
|
-
return this.
|
|
33317
|
-
.getFeatureTypeFull(this.featureTypeName)
|
|
33318
|
-
.then((featureType) => Object.keys(featureType.properties).map((prop) => {
|
|
33870
|
+
return this.featureType.then((featureType) => Object.keys(featureType.properties).map((prop) => {
|
|
33319
33871
|
const originalType = featureType.properties[prop];
|
|
33320
|
-
|
|
33321
|
-
|
|
33322
|
-
|
|
33872
|
+
let type;
|
|
33873
|
+
if (originalType === 'float' || originalType === 'integer') {
|
|
33874
|
+
type = 'number';
|
|
33875
|
+
}
|
|
33876
|
+
else if (originalType === 'boolean') {
|
|
33877
|
+
type = 'string'; // we don't handle booleans yet in the data fetcher
|
|
33878
|
+
}
|
|
33879
|
+
else {
|
|
33880
|
+
type = originalType;
|
|
33881
|
+
}
|
|
33323
33882
|
return {
|
|
33324
33883
|
name: prop,
|
|
33325
33884
|
label: prop,
|
|
@@ -33328,83 +33887,63 @@ class WfsReader extends BaseCacheReader {
|
|
|
33328
33887
|
}));
|
|
33329
33888
|
}
|
|
33330
33889
|
get info() {
|
|
33331
|
-
return this.
|
|
33890
|
+
return this.featureType.then((result) => ({
|
|
33332
33891
|
itemsCount: result.objectCount,
|
|
33892
|
+
hasGeometry: !!result.geometryName,
|
|
33333
33893
|
}));
|
|
33334
33894
|
}
|
|
33335
|
-
|
|
33336
|
-
|
|
33337
|
-
const featureTypes = wfsEndpoint.getFeatureTypes();
|
|
33338
|
-
const featureType = wfsEndpoint.getFeatureTypeSummary(featureTypes.length === 1 && !featureTypeName
|
|
33339
|
-
? featureTypes[0].name
|
|
33340
|
-
: featureTypeName);
|
|
33341
|
-
if (!featureType) {
|
|
33342
|
-
throw new Error('wfs.featuretype.notfound');
|
|
33343
|
-
}
|
|
33344
|
-
if (wfsEndpoint.supportsStartIndex()) {
|
|
33345
|
-
return new WfsReader(wfsUrlEndpoint, wfsEndpoint, featureType.name);
|
|
33346
|
-
}
|
|
33347
|
-
else if (wfsEndpoint.supportsJson(featureType.name)) {
|
|
33348
|
-
return new GeojsonReader(wfsEndpoint.getFeatureUrl(featureType.name, {
|
|
33349
|
-
asJson: true,
|
|
33350
|
-
outputCrs: 'EPSG:4326',
|
|
33351
|
-
}));
|
|
33352
|
-
}
|
|
33353
|
-
else {
|
|
33354
|
-
if (featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')) &&
|
|
33355
|
-
(featureType.defaultCrs === 'EPSG:4326' ||
|
|
33356
|
-
featureType.otherCrs?.includes('EPSG:4326'))) {
|
|
33357
|
-
return new GmlReader(wfsEndpoint.getFeatureUrl(featureType.name, {
|
|
33358
|
-
outputFormat: featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')),
|
|
33359
|
-
outputCrs: 'EPSG:4326',
|
|
33360
|
-
}), featureType.name, wfsEndpoint.getVersion());
|
|
33361
|
-
}
|
|
33362
|
-
throw new Error('wfs.geojsongml.notsupported');
|
|
33363
|
-
}
|
|
33895
|
+
load() {
|
|
33896
|
+
// Nothing to load for Wfs
|
|
33364
33897
|
}
|
|
33365
|
-
async
|
|
33366
|
-
|
|
33367
|
-
|
|
33898
|
+
async read() {
|
|
33899
|
+
const endpoint = await this.endpoint;
|
|
33900
|
+
const featureType = await this.featureType;
|
|
33901
|
+
// if we can't use the WFS protocol we fall back to the backup reader
|
|
33902
|
+
if (this.aggregations || this.groupedBy || !endpoint.supportsStartIndex()) {
|
|
33903
|
+
const backupReader = await this.backupReader;
|
|
33904
|
+
backupReader.selectAll();
|
|
33905
|
+
if (this.selected) {
|
|
33906
|
+
backupReader.select(...this.selected);
|
|
33907
|
+
}
|
|
33908
|
+
if (this.aggregations) {
|
|
33909
|
+
backupReader.aggregate(...this.aggregations);
|
|
33910
|
+
}
|
|
33911
|
+
if (this.groupedBy) {
|
|
33912
|
+
backupReader.groupBy(...this.groupedBy);
|
|
33913
|
+
}
|
|
33914
|
+
if (this.sort) {
|
|
33915
|
+
backupReader.orderBy(...this.sort);
|
|
33916
|
+
}
|
|
33917
|
+
if (this.startIndex !== null && this.count !== null) {
|
|
33918
|
+
backupReader.limit(this.startIndex, this.count);
|
|
33919
|
+
}
|
|
33920
|
+
return backupReader.read();
|
|
33368
33921
|
}
|
|
33369
|
-
const asJson =
|
|
33922
|
+
const asJson = endpoint.supportsJson(featureType.name);
|
|
33370
33923
|
const attributes = this.selected ?? undefined;
|
|
33371
|
-
let
|
|
33924
|
+
let sortBy = null;
|
|
33925
|
+
if (this.sort) {
|
|
33926
|
+
const mapSort = (s) => [s[0] === 'desc' ? 'D' : 'A', s[1]];
|
|
33927
|
+
sortBy = Array.isArray(this.sort[0])
|
|
33928
|
+
? this.sort.map(mapSort)
|
|
33929
|
+
: mapSort(this.sort);
|
|
33930
|
+
}
|
|
33931
|
+
const url = endpoint.getFeatureUrl(featureType.name, {
|
|
33372
33932
|
...(this.startIndex !== null && { startIndex: this.startIndex }),
|
|
33373
33933
|
...(this.count !== null && { maxFeatures: this.count }),
|
|
33374
33934
|
asJson,
|
|
33375
33935
|
outputCrs: 'EPSG:4326',
|
|
33376
33936
|
attributes,
|
|
33377
|
-
|
|
33937
|
+
sortBy,
|
|
33378
33938
|
});
|
|
33379
|
-
|
|
33380
|
-
const finalUrl = new URL(url);
|
|
33381
|
-
const sorts = this.sort
|
|
33382
|
-
.map((fieldSort) => `${fieldSort[1]}+${fieldSort[0] === 'asc' ? 'A' : 'D'}`)
|
|
33383
|
-
.join(',');
|
|
33384
|
-
// Direct update on string url to prevent encoding of +A and +D
|
|
33385
|
-
url = `${url}${finalUrl.search ? '&' : ''}SORTBY=${sorts}`;
|
|
33386
|
-
}
|
|
33387
|
-
return fetchDataAsText(url, this.cacheActive).then((text) => asJson
|
|
33939
|
+
return fetchDataAsText(url, this.cacheEnabled).then((text) => asJson
|
|
33388
33940
|
? parseGeojson(text)
|
|
33389
|
-
: parseGml(text,
|
|
33390
|
-
}
|
|
33391
|
-
async getQueryData() {
|
|
33392
|
-
const items = (await this.getData()).items;
|
|
33393
|
-
const jsonItems = getJsonDataItemsProxy(items);
|
|
33394
|
-
const query = generateSqlQuery(this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations);
|
|
33395
|
-
const result = await import('alasql').then((module) => module.default(query, [jsonItems]));
|
|
33396
|
-
return result.map(jsonToGeojsonFeature);
|
|
33397
|
-
}
|
|
33398
|
-
load() {
|
|
33399
|
-
// Nothing to load for Wfs
|
|
33400
|
-
}
|
|
33401
|
-
async read() {
|
|
33402
|
-
return (await this.getData(this.aggregations, this.groupedBy)).items;
|
|
33941
|
+
: parseGml(text, featureType.name, endpoint.getVersion()));
|
|
33403
33942
|
}
|
|
33404
33943
|
}
|
|
33405
33944
|
|
|
33406
|
-
async function openDataset(url,
|
|
33407
|
-
const fileType = await inferDatasetType(url, typeHint);
|
|
33945
|
+
async function openDataset(url, options) {
|
|
33946
|
+
const fileType = await inferDatasetType(url, options?.typeHint);
|
|
33408
33947
|
let reader;
|
|
33409
33948
|
try {
|
|
33410
33949
|
switch (fileType) {
|
|
@@ -33421,18 +33960,18 @@ async function openDataset(url, typeHint, options, cacheActive) {
|
|
|
33421
33960
|
reader = new ExcelReader(url);
|
|
33422
33961
|
break;
|
|
33423
33962
|
case 'gml':
|
|
33424
|
-
reader = new GmlReader(url
|
|
33963
|
+
reader = new GmlReader(url);
|
|
33425
33964
|
break;
|
|
33426
33965
|
case 'wfs':
|
|
33427
|
-
reader =
|
|
33966
|
+
reader = new WfsReader(url, options?.wfsFeatureType);
|
|
33428
33967
|
break;
|
|
33429
33968
|
}
|
|
33430
|
-
reader.
|
|
33969
|
+
reader.enableCache(options?.enableCache ?? true);
|
|
33431
33970
|
reader.load();
|
|
33432
33971
|
return reader;
|
|
33433
33972
|
}
|
|
33434
33973
|
catch (e) {
|
|
33435
|
-
//WfsReader may already raise a FetchError
|
|
33974
|
+
// WfsReader may already raise a FetchError
|
|
33436
33975
|
if (e instanceof FetchError)
|
|
33437
33976
|
throw e;
|
|
33438
33977
|
else
|
|
@@ -33443,13 +33982,13 @@ async function openDataset(url, typeHint, options, cacheActive) {
|
|
|
33443
33982
|
* This fetches the full dataset at the given URL and parses it according to its mime type.
|
|
33444
33983
|
* All items in the dataset are converted to GeoJSON features, even if they do not bear any spatial geometry.
|
|
33445
33984
|
* File type can be either inferred (from the HTTP headers or the URL), or hinted using the 2nd argument
|
|
33446
|
-
* File type is determined
|
|
33985
|
+
* File type is determined like so:
|
|
33447
33986
|
* 1. if a type hint is given, use it
|
|
33448
33987
|
* 2. otherwise, look for a Content-Type header in the response with a supported mime type
|
|
33449
33988
|
* 3. if no valid mime type was found, look for an explicit file extension in the url (.csv, .geojson etc.)
|
|
33450
33989
|
*/
|
|
33451
|
-
async function readDataset(url,
|
|
33452
|
-
const reader = await openDataset(url,
|
|
33990
|
+
async function readDataset(url, options) {
|
|
33991
|
+
const reader = await openDataset(url, options);
|
|
33453
33992
|
try {
|
|
33454
33993
|
return await reader.read();
|
|
33455
33994
|
}
|
|
@@ -33680,9 +34219,11 @@ class DataService {
|
|
|
33680
34219
|
getDataset(link, cacheActive) {
|
|
33681
34220
|
if (link.type === 'service' && link.accessServiceProtocol === 'wfs') {
|
|
33682
34221
|
const wfsUrlEndpoint = this.proxy.getProxiedUrl(link.url.toString());
|
|
33683
|
-
return from(openDataset(wfsUrlEndpoint,
|
|
34222
|
+
return from(openDataset(wfsUrlEndpoint, {
|
|
34223
|
+
typeHint: 'wfs',
|
|
33684
34224
|
wfsFeatureType: link.name,
|
|
33685
|
-
|
|
34225
|
+
enableCache: cacheActive,
|
|
34226
|
+
}));
|
|
33686
34227
|
}
|
|
33687
34228
|
else if (link.type === 'download') {
|
|
33688
34229
|
const linkProxifiedUrl = this.proxy.getProxiedUrl(link.url.toString());
|
|
@@ -33690,12 +34231,15 @@ class DataService {
|
|
|
33690
34231
|
const supportedType = SupportedTypes.indexOf(format) > -1
|
|
33691
34232
|
? format
|
|
33692
34233
|
: undefined;
|
|
33693
|
-
return from(openDataset(linkProxifiedUrl,
|
|
34234
|
+
return from(openDataset(linkProxifiedUrl, {
|
|
34235
|
+
typeHint: supportedType,
|
|
34236
|
+
enableCache: cacheActive,
|
|
34237
|
+
})).pipe();
|
|
33694
34238
|
}
|
|
33695
34239
|
else if (link.type === 'service' &&
|
|
33696
34240
|
link.accessServiceProtocol === 'esriRest') {
|
|
33697
34241
|
const url = this.getDownloadUrlFromEsriRest(link.url.toString(), 'geojson');
|
|
33698
|
-
return from(openDataset(url, 'geojson',
|
|
34242
|
+
return from(openDataset(url, { typeHint: 'geojson', enableCache: cacheActive })).pipe();
|
|
33699
34243
|
}
|
|
33700
34244
|
else if (link.type === 'service' &&
|
|
33701
34245
|
link.accessServiceProtocol === 'ogcFeatures') {
|
|
@@ -33709,7 +34253,10 @@ class DataService {
|
|
|
33709
34253
|
}
|
|
33710
34254
|
const urlWithoutLimit = new URL(geojsonUrl);
|
|
33711
34255
|
urlWithoutLimit.searchParams.delete('limit');
|
|
33712
|
-
return openDataset(urlWithoutLimit.toString(),
|
|
34256
|
+
return openDataset(urlWithoutLimit.toString(), {
|
|
34257
|
+
typeHint: 'geojson',
|
|
34258
|
+
enableCache: cacheActive,
|
|
34259
|
+
});
|
|
33713
34260
|
}));
|
|
33714
34261
|
}
|
|
33715
34262
|
return throwError(() => 'protocol not supported');
|
|
@@ -33961,20 +34508,31 @@ class DataTableComponent {
|
|
|
33961
34508
|
this.eltRef = inject(ElementRef);
|
|
33962
34509
|
this.cdr = inject(ChangeDetectorRef);
|
|
33963
34510
|
this.translateService = inject(TranslateService);
|
|
33964
|
-
this.
|
|
34511
|
+
this.columnsFromFeatureCatalog = null;
|
|
34512
|
+
this.columnsFromDataset = [];
|
|
33965
34513
|
this.selected = new EventEmitter();
|
|
33966
|
-
this.properties$ = new BehaviorSubject(null);
|
|
33967
34514
|
this.loading$ = new BehaviorSubject(false);
|
|
33968
34515
|
this.error = null;
|
|
33969
34516
|
}
|
|
33970
34517
|
set featureAttributes(value) {
|
|
33971
|
-
this.
|
|
33972
|
-
|
|
34518
|
+
this.columnsFromFeatureCatalog = value.map((attrs) => ({
|
|
34519
|
+
name: attrs.value,
|
|
34520
|
+
label: attrs.label,
|
|
34521
|
+
}));
|
|
33973
34522
|
}
|
|
33974
34523
|
set dataset(value) {
|
|
33975
34524
|
this.dataset_ = value;
|
|
33976
34525
|
this.dataset_.load();
|
|
33977
|
-
this.dataset_.info.then((info) =>
|
|
34526
|
+
this.dataset_.info.then((info) => {
|
|
34527
|
+
this.count = info.itemsCount;
|
|
34528
|
+
this.cdr.detectChanges();
|
|
34529
|
+
});
|
|
34530
|
+
}
|
|
34531
|
+
get columns() {
|
|
34532
|
+
return this.columnsFromFeatureCatalog ?? this.columnsFromDataset;
|
|
34533
|
+
}
|
|
34534
|
+
get columnNames() {
|
|
34535
|
+
return this.columns.map((c) => c.name);
|
|
33978
34536
|
}
|
|
33979
34537
|
ngOnInit() {
|
|
33980
34538
|
this.dataSource = new DataTableDataSource();
|
|
@@ -34009,11 +34567,12 @@ class DataTableComponent {
|
|
|
34009
34567
|
}
|
|
34010
34568
|
async readData() {
|
|
34011
34569
|
this.loading$.next(true);
|
|
34012
|
-
// wait for properties to be read
|
|
34013
|
-
const properties = await firstValueFrom(this.properties$.pipe(filter((p) => !!p)));
|
|
34014
|
-
const propsWithoutGeom = properties.filter((p) => !p.toLowerCase().startsWith('geom'));
|
|
34015
|
-
this.dataset_.select(...propsWithoutGeom);
|
|
34016
34570
|
try {
|
|
34571
|
+
// wait for properties to be read
|
|
34572
|
+
if (!this.columnsFromFeatureCatalog) {
|
|
34573
|
+
this.columnsFromDataset = await this.dataset_.properties;
|
|
34574
|
+
}
|
|
34575
|
+
this.dataset_.select(...this.columnNames);
|
|
34017
34576
|
await this.dataSource.showData(this.dataset_.read());
|
|
34018
34577
|
this.error = null;
|
|
34019
34578
|
}
|
|
@@ -34043,7 +34602,7 @@ class DataTableComponent {
|
|
|
34043
34602
|
}
|
|
34044
34603
|
}
|
|
34045
34604
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DataTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
34046
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DataTableComponent, isStandalone: true, selector: "gn-ui-data-table", inputs: { featureAttributes: "featureAttributes", dataset: "dataset", activeId: "activeId" }, outputs: { selected: "selected" }, providers: [{ provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl }], viewQueries: [{ propertyName: "sort", first: true, predicate: MatSort, descendants: true }, { propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col border border-gray-300 rounded-lg bg-white h-full\">\n <div class=\"flex-1 overflow-y-hidden overflow-x-auto rounded-lg relative\">\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"setSort($event)\"\n [matSortDisableClear]=\"true\"\n
|
|
34605
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DataTableComponent, isStandalone: true, selector: "gn-ui-data-table", inputs: { featureAttributes: "featureAttributes", dataset: "dataset", activeId: "activeId" }, outputs: { selected: "selected" }, providers: [{ provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl }], viewQueries: [{ propertyName: "sort", first: true, predicate: MatSort, descendants: true }, { propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col border border-gray-300 rounded-lg bg-white h-full\">\n <div class=\"flex-1 overflow-y-hidden overflow-x-auto rounded-lg relative\">\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"setSort($event)\"\n [matSortDisableClear]=\"true\"\n >\n @for (column of columns; track column) {\n <ng-container [matColumnDef]=\"column.name\">\n <th\n mat-header-cell\n *matHeaderCellDef\n mat-sort-header\n class=\"text-sm text-black bg-white\"\n >\n {{ column.label }}\n </th>\n <td\n mat-cell\n *matCellDef=\"let element\"\n class=\"whitespace-nowrap pr-1 truncate\"\n >\n {{ element[column.name] }}\n </td>\n </ng-container>\n }\n\n <tr mat-header-row *matHeaderRowDef=\"columnNames; sticky: true\"></tr>\n <tr\n [id]=\"getRowEltId(row.id)\"\n mat-row\n *matRowDef=\"let row; columns: columnNames\"\n (click)=\"selected.emit(row)\"\n [class.active]=\"\n activeId !== undefined && activeId !== null && row.id === activeId\n \"\n ></tr>\n </table>\n @if (loading$ | async) {\n <gn-ui-loading-mask\n class=\"sticky inset-0\"\n [message]=\"'table.loading.data' | translate\"\n ></gn-ui-loading-mask>\n }\n @if (error) {\n <gn-ui-popup-alert\n type=\"warning\"\n icon=\"matErrorOutlineOutline\"\n class=\"absolute m-2 inset-0 z-[100]\"\n >\n <span translate>{{ error }}</span>\n </gn-ui-popup-alert>\n }\n </div>\n <div class=\"flex justify-between items-center overflow-hidden\">\n <div class=\"text-gray-900 px-4 py-2 text-sm\">\n <span class=\"count font-extrabold text-primary\">{{ count }}</span\n > <span translate>table.object.count</span>.\n </div>\n\n <mat-paginator\n class=\"my-[-16px]\"\n (page)=\"setPagination()\"\n [length]=\"count\"\n [pageSize]=\"10\"\n [showFirstLastButtons]=\"true\"\n [hidePageSize]=\"true\"\n ></mat-paginator>\n </div>\n</div>\n", styles: ["table{width:100%;background:#fff}th.mat-mdc-header-cell,td.mat-mdc-cell,td.mat-mdc-footer-cell{padding-right:20px}tr.mat-mdc-row,tr.mat-mdc-footer-row{height:36px}tr:hover{background:#f5f5f5}tr.mat-mdc-header-row{height:48px}[mat-header-cell]{color:#0000008a;font-size:12px;font-weight:500}tr{cursor:pointer}.active .mat-mdc-cell{color:var(--color-primary)}.mat-mdc-paginator{background:none}\n"], dependencies: [{ kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i1$b.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i1$b.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i1$b.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i1$b.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i1$b.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i1$b.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i1$b.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i1$b.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i1$b.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i1$b.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i2$4.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i2$4.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: LoadingMaskComponent, selector: "gn-ui-loading-mask", inputs: ["message"] }, { kind: "component", type: PopupAlertComponent, selector: "gn-ui-popup-alert", inputs: ["icon", "type", "position"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
34047
34606
|
}
|
|
34048
34607
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DataTableComponent, decorators: [{
|
|
34049
34608
|
type: Component,
|
|
@@ -34055,10 +34614,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
34055
34614
|
CommonModule,
|
|
34056
34615
|
LoadingMaskComponent,
|
|
34057
34616
|
PopupAlertComponent,
|
|
34058
|
-
LetDirective,
|
|
34059
34617
|
TranslatePipe,
|
|
34060
34618
|
TranslateDirective,
|
|
34061
|
-
], providers: [{ provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl }], selector: 'gn-ui-data-table', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex flex-col border border-gray-300 rounded-lg bg-white h-full\">\n <div class=\"flex-1 overflow-y-hidden overflow-x-auto rounded-lg relative\">\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"setSort($event)\"\n [matSortDisableClear]=\"true\"\n
|
|
34619
|
+
], providers: [{ provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl }], selector: 'gn-ui-data-table', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex flex-col border border-gray-300 rounded-lg bg-white h-full\">\n <div class=\"flex-1 overflow-y-hidden overflow-x-auto rounded-lg relative\">\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"setSort($event)\"\n [matSortDisableClear]=\"true\"\n >\n @for (column of columns; track column) {\n <ng-container [matColumnDef]=\"column.name\">\n <th\n mat-header-cell\n *matHeaderCellDef\n mat-sort-header\n class=\"text-sm text-black bg-white\"\n >\n {{ column.label }}\n </th>\n <td\n mat-cell\n *matCellDef=\"let element\"\n class=\"whitespace-nowrap pr-1 truncate\"\n >\n {{ element[column.name] }}\n </td>\n </ng-container>\n }\n\n <tr mat-header-row *matHeaderRowDef=\"columnNames; sticky: true\"></tr>\n <tr\n [id]=\"getRowEltId(row.id)\"\n mat-row\n *matRowDef=\"let row; columns: columnNames\"\n (click)=\"selected.emit(row)\"\n [class.active]=\"\n activeId !== undefined && activeId !== null && row.id === activeId\n \"\n ></tr>\n </table>\n @if (loading$ | async) {\n <gn-ui-loading-mask\n class=\"sticky inset-0\"\n [message]=\"'table.loading.data' | translate\"\n ></gn-ui-loading-mask>\n }\n @if (error) {\n <gn-ui-popup-alert\n type=\"warning\"\n icon=\"matErrorOutlineOutline\"\n class=\"absolute m-2 inset-0 z-[100]\"\n >\n <span translate>{{ error }}</span>\n </gn-ui-popup-alert>\n }\n </div>\n <div class=\"flex justify-between items-center overflow-hidden\">\n <div class=\"text-gray-900 px-4 py-2 text-sm\">\n <span class=\"count font-extrabold text-primary\">{{ count }}</span\n > <span translate>table.object.count</span>.\n </div>\n\n <mat-paginator\n class=\"my-[-16px]\"\n (page)=\"setPagination()\"\n [length]=\"count\"\n [pageSize]=\"10\"\n [showFirstLastButtons]=\"true\"\n [hidePageSize]=\"true\"\n ></mat-paginator>\n </div>\n</div>\n", styles: ["table{width:100%;background:#fff}th.mat-mdc-header-cell,td.mat-mdc-cell,td.mat-mdc-footer-cell{padding-right:20px}tr.mat-mdc-row,tr.mat-mdc-footer-row{height:36px}tr:hover{background:#f5f5f5}tr.mat-mdc-header-row{height:48px}[mat-header-cell]{color:#0000008a;font-size:12px;font-weight:500}tr{cursor:pointer}.active .mat-mdc-cell{color:var(--color-primary)}.mat-mdc-paginator{background:none}\n"] }]
|
|
34062
34620
|
}], propDecorators: { featureAttributes: [{
|
|
34063
34621
|
type: Input
|
|
34064
34622
|
}], dataset: [{
|
|
@@ -34161,7 +34719,7 @@ class ChartViewComponent {
|
|
|
34161
34719
|
}), shareReplay$1(1));
|
|
34162
34720
|
this.properties$ = combineLatest([this.dataset$, this.featureCatalog$]).pipe(switchMap$1(([dataset, catalog]) => this.setProperties(dataset, catalog)), shareReplay$1(1));
|
|
34163
34721
|
this.yChoices$ = this.properties$.pipe(map$1((properties) => properties
|
|
34164
|
-
.filter((prop) => prop.type === 'number'
|
|
34722
|
+
.filter((prop) => prop.type === 'number')
|
|
34165
34723
|
.map((prop) => ({ value: prop.name, label: prop.label || prop.name }))), tap$1((choices) => {
|
|
34166
34724
|
if (!choices.find((choice) => choice.value === this.yProperty$.value)) {
|
|
34167
34725
|
const newProp = choices[0]?.value || '';
|
|
@@ -34188,6 +34746,7 @@ class ChartViewComponent {
|
|
|
34188
34746
|
this.aggregation$,
|
|
34189
34747
|
]).pipe(filter$1(([_, x, y]) => !!x || !!y), switchMap$1(([dataset, xProp, yProp, aggregation]) => {
|
|
34190
34748
|
const fieldAgg = aggregation === 'count' ? ['count'] : [aggregation, yProp];
|
|
34749
|
+
this.loading = true;
|
|
34191
34750
|
return dataset
|
|
34192
34751
|
.groupBy(['distinct', xProp])
|
|
34193
34752
|
.aggregate(fieldAgg)
|
|
@@ -34429,6 +34988,7 @@ class GeoTableViewComponent {
|
|
|
34429
34988
|
}));
|
|
34430
34989
|
}
|
|
34431
34990
|
async initMapContext() {
|
|
34991
|
+
this.dataset.load();
|
|
34432
34992
|
this.dataset.selectAll();
|
|
34433
34993
|
return {
|
|
34434
34994
|
layers: [
|
|
@@ -34602,8 +35162,7 @@ class MdViewFacade {
|
|
|
34602
35162
|
}));
|
|
34603
35163
|
this.error$ = this.store.pipe(select(getMetadataError));
|
|
34604
35164
|
this.related$ = this.store.pipe(select(getRelated));
|
|
34605
|
-
this.
|
|
34606
|
-
this.sourceOf$ = this.store.pipe(select(getSourceOf));
|
|
35165
|
+
this.linkedRecords$ = this.store.pipe(select(getLinkedRecords));
|
|
34607
35166
|
this.chartConfig$ = this.store.pipe(select(getChartConfig));
|
|
34608
35167
|
this.allLinks$ = this.metadata$.pipe(map$1((record) => 'onlineResources' in record ? record.onlineResources : []), shareReplay$1(1));
|
|
34609
35168
|
this.resourceDoi$ = this.metadata$.pipe(map$1((record) => {
|
|
@@ -34722,12 +35281,9 @@ class MdViewEffects {
|
|
|
34722
35281
|
this.loadRelatedRecords$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getSimilarRecords(full)), map$1((related) => {
|
|
34723
35282
|
return setRelated({ related });
|
|
34724
35283
|
}), catchError(() => of(setRelated({ related: null })))));
|
|
34725
|
-
this.
|
|
34726
|
-
return
|
|
34727
|
-
}), catchError(() => of(
|
|
34728
|
-
this.loadSourceOf$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getSourceOf(full)), map$1((sourceOf) => {
|
|
34729
|
-
return setSourceOf({ sourceOf });
|
|
34730
|
-
}), catchError(() => of(setSourceOf({ sourceOf: null })))));
|
|
35284
|
+
this.loadLinkedRecords$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getLinkedRecords(full)), map$1((linkedRecords) => {
|
|
35285
|
+
return setLinkedRecords({ linkedRecords });
|
|
35286
|
+
}), catchError(() => of(setLinkedRecords({ linkedRecords: null })))));
|
|
34731
35287
|
/*
|
|
34732
35288
|
UserFeedback effects
|
|
34733
35289
|
*/
|
|
@@ -39946,7 +40502,7 @@ class RouterService {
|
|
|
39946
40502
|
return ROUTER_ROUTE_ORGANIZATION;
|
|
39947
40503
|
}
|
|
39948
40504
|
getDefaultSort() {
|
|
39949
|
-
return SortByEnum.
|
|
40505
|
+
return SortByEnum.RESOURCE_DATE;
|
|
39950
40506
|
}
|
|
39951
40507
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: RouterService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
39952
40508
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: RouterService, providedIn: 'root' }); }
|
|
@@ -40006,7 +40562,7 @@ function flattenQueryParams(params) {
|
|
|
40006
40562
|
const start = flattened[key].start;
|
|
40007
40563
|
const end = flattened[key].end;
|
|
40008
40564
|
flattened[key] = [
|
|
40009
|
-
`${start ? formatDate(start) : ''}..${formatDate(end)
|
|
40565
|
+
`${start ? formatDate(start) : ''}..${end ? formatDate(end) : ''}`,
|
|
40010
40566
|
];
|
|
40011
40567
|
}
|
|
40012
40568
|
}
|
|
@@ -40363,5 +40919,5 @@ const CHART_TYPE_VALUES = [
|
|
|
40363
40919
|
* Generated bundle index. Do not edit.
|
|
40364
40920
|
*/
|
|
40365
40921
|
|
|
40366
|
-
export { ADD_RESULTS, ADD_SEARCH, AVAILABLE_LICENSES, AbstractAction, AbstractSearchField, ActionMenuComponent, AddLayerFromCatalogComponent, AddLayerRecordPreviewComponent, AddResults, AddSearch, AnchorLinkDirective, ApiCardComponent, ApplicationBannerComponent, AuthService, AutocompleteComponent, AvailableServicesField, AvatarComponent, AvatarServiceInterface, BASEMAP_LAYERS, BadgeComponent, BaseConverter, BaseFileReader, BaseReader, BlockListComponent, ButtonComponent, CHART_TYPE_VALUES, CLEAR_ERROR, CLEAR_RESULTS, CarouselComponent, CatalogTitleComponent, CellPopinComponent, ChartComponent, ChartViewComponent, CheckToggleComponent, CheckboxComponent, ClearError, ClearResults, ColorScaleComponent, ConfirmationDialogComponent, ContactDetailsComponent, ContactDetailsFormComponent, ContactPillComponent, ContentGhostComponent, CopyTextButtonComponent, DEFAULT_CONFIGURATION, DEFAULT_GN4_LOGIN_URL, DEFAULT_GN4_LOGOUT_URL, DEFAULT_GN4_SETTINGS_URL, DEFAULT_LANG, DEFAULT_PAGE_SIZE, DEFAULT_RECORD_CONVERTER, DEFAULT_RESULTS_LAYOUT_CONFIG, DEFAULT_SEARCH_KEY, DEFAULT_SPATIAL_EXTENT_STYLE, DISABLE_AUTH, DISABLE_DRAFT, DO_NOT_USE_DEFAULT_BASEMAP, DataService, DataTableComponent, DataViewComponent, DataViewPermalinkComponent, DataViewShareComponent, DataViewWebComponentComponent, DatePickerComponent, DateRangeDropdownComponent, DateRangeInputsComponent, DateRangePickerComponent, DateRangeSearchField, DateService, DcatApConverter, DefaultRouterModule, DownloadItemComponent, DownloadsListComponent, DragAndDropFileInputComponent, DropdownMultiselectComponent, DropdownSelectorComponent, EDITOR_FEATURE_KEY, ES_QUERY_FIELDS_PRIORITY, ES_RESOURCES_VALUES, ES_SOURCE_SUMMARY, EXTERNAL_VIEWER_OPEN_NEW_TAB, EXTERNAL_VIEWER_URL_TEMPLATE, EditableLabelDirective, EditorFacade, EditorService, ElasticsearchService, ErrorComponent, ErrorType, ExpandablePanelButtonComponent, ExpandablePanelComponent, ExternalLinkCardComponent, ExternalViewerButtonComponent, FIELDS_BRIEF, FIELDS_SUMMARY, FILTER_GEOMETRY, FILTER_SUMMARY_IGNORE_LIST, FORMATS, FacetBlockComponent, FacetItemComponent, FacetListComponent, FacetsContainerComponent, FavoriteStarComponent, FavoritesService, FeatureCatalogListComponent, FeatureDetailComponent, FeatureEditorModule, FeatureMapModule, FeatureRecordModule, FeatureSearchModule, FetchError, FieldFocusDirective, FieldsService, FigureComponent, FigureContainerComponent, FileInputComponent, FileTranslateLoader, FilesDropDirective, FilterDropdownComponent, FormFieldArrayComponent, FormFieldComponent, FormFieldDateComponent, FormFieldFileComponent, FormFieldKeywordsComponent, FormFieldLicenseComponent, FormFieldObjectComponent, FormFieldRichComponent, FormFieldSimpleComponent, FormFieldSpatialExtentComponent, FormFieldTemporalExtentsComponent, FormFieldTopicsComponent, FormFieldWrapperComponent, FullTextSearchField, FuzzySearchComponent, GEONETWORK_UI_TAG_NAME, GEONETWORK_UI_VERSION, GeoDataBadgeComponent, GeoTableViewComponent, GeocodingComponent, GeojsonReader, Gn4Converter, Gn4PlatformMapper, Gn4PlatformService, Gn4Repository, Gn4SettingsService, GnUiHumanizeDateDirective, GpfApiDlComponent, GravatarService, I18nInterceptor, ISO_TOPICS, ImageFallbackDirective, ImageInputComponent, ImageOverlayPreviewComponent, ImportRecordComponent, InlineFilterComponent, InteractiveTableColumnComponent, InteractiveTableComponent, InternalLinkCardComponent, IsSpatialSearchField, Iso191153Converter, Iso19139Converter, KeywordBadgeComponent, KindBadgeComponent, LANGUAGES_LIST, LANGUAGE_NAMES, LANGUAGE_STORAGE_KEY, LANG_2_TO_3_MAPPER, LOGIN_URL, LOGOUT_URL, LONLAT_CRS_CODES, LanguageSwitcherComponent, LayersPanelComponent, LicenseSearchField, LinkClassifierService, LinkUsage, LoadingMaskComponent, LogService, MAP_FEATURE_KEY, MAP_VIEW_CONSTRAINTS, MAX_UPLOAD_SIZE_MB, METADATA_LANGUAGE, MapContainerComponent, MapFacade, MapLegendComponent, MapStateContainerComponent, MapStyleService, MapUtilsService, MapViewComponent, MarkdownEditorComponent, MarkdownParserComponent, MaxLinesComponent, mdview_actions as MdViewActions, MdViewFacade, MetadataCatalogComponent, MetadataContactComponent, MetadataDoiComponent, MetadataInfoComponent, MetadataLinkType, MetadataMapperContext, MetadataQualityComponent, MetadataQualityItemComponent, MetadataQualityPanelComponent, ModalDialogComponent, MultilingualPanelComponent, MultilingualSearchField, MyOrgService, NAMESPACES, NOT_APPLICABLE_CONSTRAINT, NOT_KNOWN_CONSTRAINT, NotificationComponent, NotificationsContainerComponent, NotificationsService, OPEN_DATA_LICENSE, ORGANIZATIONS_STRATEGY, ORGANIZATION_PAGE_URL_TOKEN, ORGANIZATION_URL_TOKEN, OnlineResourceCardComponent, OnlineServiceResourceInputComponent, OrganisationPreviewComponent, OrganisationsComponent, OrganisationsFilterComponent, OrganisationsResultComponent, OrganizationSearchField, OrganizationsFromGroupsService, OrganizationsFromMetadataService, OrganizationsServiceInterface, OwnerSearchField, PAGINATE, PARSE_DELIMITER, PATCH_RESULTS_AGGREGATIONS, PROXY_PATH, Paginate, PaginationButtonsComponent, PaginationComponent, PaginationDotsComponent, PatchResultsAggregations, PlatformServiceInterface, PopoverComponent, PopupAlertComponent, PossibleResourceTypes, PossibleResourceTypesDefinition, PreviousNextButtonsComponent, ProgressBarComponent, ProxyService, QUERY_FIELDS, RECORD_DATASET_URL_TOKEN, RECORD_REUSE_URL_TOKEN, RECORD_SERVICE_URL_TOKEN, REQUEST_MORE_ON_AGGREGATION, REQUEST_MORE_RESULTS, REQUEST_NEW_RESULTS, RESULTS_LAYOUT_CONFIG, REUSE_LIGHT_CONFIGURATION, ROUTER_CONFIG, ROUTER_ROUTE_DATASET, ROUTER_ROUTE_ORGANIZATION, ROUTER_ROUTE_REUSE, ROUTER_ROUTE_SEARCH, ROUTER_ROUTE_SERVICE, ROUTER_STATE_KEY, ROUTE_PARAMS, RecordApiFormComponent, RecordFormComponent, RecordKindField, RecordMetaComponent, RecordMetricComponent, RecordPreviewCardComponent, RecordPreviewComponent, RecordPreviewFeedComponent, RecordPreviewListComponent, RecordPreviewRowComponent, RecordPreviewTextComponent, RecordPreviewTitleComponent, RecordStatusValues, RecordsMetricsComponent, RecordsRepositoryInterface, RecordsService, RequestMoreOnAggregation, RequestMoreResults, RequestNewResults, ResourceTypeLegacyField, ResultsHitsContainerComponent, ResultsHitsNumberComponent, ResultsHitsSearchKindComponent, ResultsLayoutComponent, ResultsLayoutConfigItem, ResultsListComponent, ResultsListContainerComponent, ResultsListItemComponent, ResultsTableComponent, ResultsTableContainerComponent, ReusePresentationForms, RoleLabels, RoleValues, RouterEffects, RouterFacade, RouterService, SEARCH_FEATURE_KEY, SETTINGS_URL, SET_CONFIG_AGGREGATIONS, SET_CONFIG_FILTERS, SET_CONFIG_REQUEST_FIELDS, SET_ERROR, SET_FAVORITES_ONLY, SET_FILTERS, SET_INCLUDE_ON_AGGREGATION, SET_PAGE_SIZE, SET_RESULTS_AGGREGATIONS, SET_RESULTS_HITS, SET_RESULTS_LAYOUT, SET_SEARCH, SET_SORT_BY, SET_SPATIAL_FILTER_ENABLED, SPATIAL_SCOPES, SearchEffects, SearchFacade, SearchFeatureCatalogComponent, SearchFiltersSummaryComponent, SearchFiltersSummaryItemComponent, SearchInputComponent, SearchRouterContainerDirective, SearchService, SearchStateContainerDirective, SelectionService, ServiceCapabilitiesComponent, SetConfigAggregations, SetConfigFilters, SetConfigRequestFields, SetError, SetFavoritesOnly, SetFilters, SetIncludeOnAggregation, SetPageSize, SetResultsAggregations, SetResultsHits, SetResultsLayout, SetSearch, SetSortBy, SetSpatialFilterEnabled, SimpleSearchField, SiteTitleComponent, SortByComponent, SortByEnum, SortableListComponent, SourceLabelComponent, SourcesService, SpatialExtentComponent, SpinningLoaderComponent, StacItemsResultGridComponent, StacViewComponent, StarToggleComponent, StickyHeaderComponent, SupportedTypes, SwitchToggleComponent, THUMBNAIL_PLACEHOLDER, TRANSLATE_DEBUG_CONFIG, TRANSLATE_DEFAULT_CONFIG, TRANSLATE_WITH_OVERRIDES_CONFIG, TableViewComponent, TextAreaComponent, TextInputComponent, ThemeService, ThumbnailComponent, TranslatedSearchField, TruncatedTextComponent, UPDATE_CONFIG_AGGREGATIONS, UPDATE_FILTERS, UPDATE_REQUEST_AGGREGATION_TERM, UpdateConfigAggregations, UpdateFilters, UrlInputComponent, UserFeedbackItemComponent, UserPreviewComponent, UserSearchField, VECTOR_STYLE_DEFAULT, ViewportIntersectorComponent, WEB_COMPONENT_EMBEDDER_URL, XmlParseError, _reset, allChildrenElement, appConfigWithTranslationFixture, appendChildTree, appendChildren, assertValidXml, associationTypeValues, bboxToPolygon, blockModelFixture, bytesToMegabytes, canEditRecord, checkFileFormat, clearSelectedFeatures, createChild, createDocument, createElement, createFuzzyFilter, createNestedChild, createNestedElement, createSpatialExtentLayer, currentPage, defaultMapStyleFixture, defaultMapStyleHlFixture, downgradeImage, downsizeImage, draftSaveSuccess, dragPanCondition, dropEmptyTranslations, editorReducer, emptyBlockModelFixture, findChildElement, findChildOrCreate, findChildrenElement, findConverterForDocument, findNestedChildOrCreate, findNestedElement, findNestedElements, findParent, firstChildElement, formatDate, formatUserInfo, getAddressLines, getAllKeysValidator, getArrayItem, getAsArray, getAsUrl, getBadgeColor, getCustomTranslations, getError, getFavoritesOnly, getFileFormat, getFileFormatFromServiceOutput, getFirstValue, getFormatPriority, getGeometryBoundingBox, getGeometryFromGeoJSON, getGlobalConfig, getIndividualDisplayName, getIsMobile, getJsonDataItemsProxy, getKeywordHierarchyPath, getLayers, getLinkId, getLinkLabel, getLinkPriority, getMapContext, getMapContextLayerFromConfig, getMapState, getMetadataQualityConfig, getMimeTypeForFormat, getNamespace, getOptionalEditorConfig, getOptionalMapConfig, getOptionalSearchConfig, getPageSize, getQualityValidators, getResourceType, getReusePresentationForm, getReuseType, getRootElement, getSearchConfigAggregations, getSearchFilters, getSearchResults, getSearchResultsAggregations, getSearchResultsHits, getSearchResultsLayout, getSearchResultsLoading, getSearchSortBy, getSearchState, getSearchStateSearch, getSelectedFeatures, getSpatialFilterEnabled, getTemporalRangeUnion, getThemeConfig, handleScrollOnNavigation, hasRecordChangedSinceDraft, hasRecordChangedSinceDraftSuccess, initSearch, initialEditorState, initialMapState, initialState, isConfigLoaded, isDateRange, isFormatInQueryParam, isPublished, itemModelFixture, kindToCodeListValue, loadAppConfig, malformedConfigFixture, mapConfigFixture, mapContact, mapKeywords, mapLogo, mapOrganization, mapReducer, markRecordAsChanged, matchesNoApplicableConstraint, matchesNoKnownConstraint, megabytesToBytes, mimeTypeToFormat, minimalAppConfigFixture, missingMandatoryConfigFixture, mouseWheelZoomCondition, noDuplicateFileName, okAppConfigFixture, openDataset, openRecord, organizationsServiceFactory, parse, parseXmlString, placeholder, prioritizePageScroll, propagateToDocumentOnly, provideGn4, provideI18n, provideRepositoryUrl, readAttribute, readDataset, readDatasetHeaders, readText, reducer$2 as reducer, reducerSearch, removeAllChildren, removeChildren, removeChildrenByName, removeSearchParams, removeWhitespace, renameElements, saveRecord, saveRecordFailure, saveRecordSuccess, selectCanEditRecord, selectCurrentPage, selectEditorConfig, selectEditorState, selectFallback, selectFallbackFields, selectField, selectHasRecordChanged, selectIsPublished, selectRecord, selectRecordChangedSinceSave, selectRecordSaveError, selectRecordSaving, selectRecordSections, selectRecordSource, selectTranslatedField, selectTranslatedValue, setContext, setCurrentPage, setEditorConfiguration, setFieldVisibility, setFocusedField, setSelectedFeatures, setTextContent, sortByFromString, sortByToString, sortByToStrings, spatialExtentToGeometry, spatialExtentsToFeatureCollection, stripHtml, stripNamespace, toDate, toIndividual, toLang2, toLang3, totalPages, undoRecordDraft, unrecognizedKeysConfigFixture, updateFrequencyCodeValues, updateLanguages, updateRecordField, updateRecordLanguages, wmsLayerFlatten, writeAttribute, wrongLanguageCodeConfigFixture, xmlToString };
|
|
40922
|
+
export { ADD_RESULTS, ADD_SEARCH, AVAILABLE_LICENSES, AbstractAction, AbstractSearchField, ActionMenuComponent, AddLayerFromCatalogComponent, AddLayerRecordPreviewComponent, AddResults, AddSearch, AnchorLinkDirective, ApiCardComponent, ApplicationBannerComponent, AuthService, AutocompleteComponent, AutofocusDirective, AvailableServicesField, AvatarComponent, AvatarServiceInterface, BASEMAP_LAYERS, BadgeComponent, BaseConverter, BaseFileReader, BaseReader, BlockListComponent, BoundingBoxSearchField, ButtonComponent, CHART_TYPE_VALUES, CLEAR_ERROR, CLEAR_RESULTS, CarouselComponent, CatalogTitleComponent, CellPopinComponent, ChartComponent, ChartViewComponent, CheckToggleComponent, CheckboxComponent, ClearError, ClearResults, ColorScaleComponent, ConfirmationDialogComponent, ContactDetailsComponent, ContactDetailsFormComponent, ContactPillComponent, ContentGhostComponent, CopyTextButtonComponent, DEFAULT_BASEMAP_LAYER, DEFAULT_CONFIGURATION, DEFAULT_GN4_LOGIN_URL, DEFAULT_GN4_LOGOUT_URL, DEFAULT_GN4_SETTINGS_URL, DEFAULT_LANG, DEFAULT_PAGE_SIZE, DEFAULT_RECORD_CONVERTER, DEFAULT_RESULTS_LAYOUT_CONFIG, DEFAULT_SEARCH_KEY, DEFAULT_SPATIAL_EXTENT_STYLE, DISABLE_AUTH, DISABLE_DRAFT, DO_NOT_USE_DEFAULT_BASEMAP, DataService, DataTableComponent, DataViewComponent, DataViewPermalinkComponent, DataViewShareComponent, DataViewWebComponentComponent, DatePickerComponent, DateRangeDropdownComponent, DateRangeInputsComponent, DateRangePickerComponent, DateRangeSearchField, DateService, DcatApConverter, DefaultRouterModule, DownloadItemComponent, DownloadsListComponent, DragAndDropFileInputComponent, DropdownMultiselectComponent, DropdownSelectorComponent, EDITOR_FEATURE_KEY, ES_QUERY_FIELDS_PRIORITY, ES_RESOURCES_VALUES, ES_SOURCE_SUMMARY, EXTERNAL_VIEWER_OPEN_NEW_TAB, EXTERNAL_VIEWER_URL_TEMPLATE, EditableLabelDirective, EditorFacade, EditorService, ElasticsearchService, ErrorComponent, ErrorType, ExpandablePanelButtonComponent, ExpandablePanelComponent, ExternalLinkCardComponent, ExternalViewerButtonComponent, FIELDS_BRIEF, FIELDS_SUMMARY, FILTER_GEOMETRY, FILTER_SUMMARY_IGNORE_LIST, FORMATS, FacetBlockComponent, FacetItemComponent, FacetListComponent, FacetsContainerComponent, FavoriteStarComponent, FavoritesService, FeatureCatalogListComponent, FeatureDetailComponent, FeatureEditorModule, FeatureMapModule, FeatureRecordModule, FeatureSearchModule, FetchError, FieldFocusDirective, FieldsService, FigureComponent, FigureContainerComponent, FileInputComponent, FileTranslateLoader, FilesDropDirective, FilterDropdownComponent, FormFieldArrayComponent, FormFieldComponent, FormFieldDateComponent, FormFieldFileComponent, FormFieldKeywordsComponent, FormFieldLicenseComponent, FormFieldObjectComponent, FormFieldRichComponent, FormFieldSimpleComponent, FormFieldSpatialExtentComponent, FormFieldTemporalExtentsComponent, FormFieldTopicsComponent, FormFieldWrapperComponent, FullTextSearchField, FuzzySearchComponent, GEONETWORK_UI_TAG_NAME, GEONETWORK_UI_VERSION, GeoDataBadgeComponent, GeoTableViewComponent, GeocodingComponent, GeojsonReader, Gn4Converter, Gn4PlatformMapper, Gn4PlatformService, Gn4Repository, Gn4SettingsService, GnUiHumanizeDateDirective, GpfApiDlComponent, GravatarService, I18nInterceptor, ISO_TOPICS, ImageFallbackDirective, ImageInputComponent, ImageOverlayPreviewComponent, ImportRecordComponent, InlineFilterComponent, InteractiveTableColumnComponent, InteractiveTableComponent, InternalLinkCardComponent, IsSpatialSearchField, Iso191153Converter, Iso19139Converter, KeywordBadgeComponent, KindBadgeComponent, LANGUAGES_LIST, LANGUAGE_NAMES, LANGUAGE_STORAGE_KEY, LANG_2_TO_3_MAPPER, LOGIN_URL, LOGOUT_URL, LONLAT_CRS_CODES, LanguageSwitcherComponent, LayersPanelComponent, LicenseSearchField, LinkClassifierService, LinkUsage, LoadingMaskComponent, LogService, MAP_FEATURE_KEY, MAP_VIEW_CONSTRAINTS, MAX_UPLOAD_SIZE_MB, METADATA_LANGUAGE, MapContainerComponent, MapFacade, MapLegendComponent, MapStateContainerComponent, MapStyleService, MapUtilsService, MapViewComponent, MarkdownEditorComponent, MarkdownParserComponent, MaxLinesComponent, mdview_actions as MdViewActions, MdViewFacade, MetadataCatalogComponent, MetadataContactComponent, MetadataDoiComponent, MetadataInfoComponent, MetadataLinkType, MetadataMapperContext, MetadataQualityComponent, MetadataQualityItemComponent, MetadataQualityPanelComponent, ModalDialogComponent, MultilingualPanelComponent, MultilingualSearchField, MyOrgService, NAMESPACES, NOT_APPLICABLE_CONSTRAINT, NOT_KNOWN_CONSTRAINT, NotificationComponent, NotificationsContainerComponent, NotificationsService, OPEN_DATA_LICENSE, ORGANIZATIONS_STRATEGY, ORGANIZATION_PAGE_URL_TOKEN, ORGANIZATION_URL_TOKEN, OnlineResourceCardComponent, OnlineServiceResourceInputComponent, OrganisationPreviewComponent, OrganisationsComponent, OrganisationsFilterComponent, OrganisationsResultComponent, OrganizationSearchField, OrganizationsFromGroupsService, OrganizationsFromMetadataService, OrganizationsServiceInterface, OwnerSearchField, PAGINATE, PARSE_DELIMITER, PATCH_RESULTS_AGGREGATIONS, PROXY_PATH, Paginate, PaginationButtonsComponent, PaginationComponent, PaginationDotsComponent, PatchResultsAggregations, PlatformServiceInterface, PopoverComponent, PopupAlertComponent, PossibleResourceTypes, PossibleResourceTypesDefinition, PreviousNextButtonsComponent, ProgressBarComponent, ProxyService, QUERY_FIELDS, RECORD_DATASET_URL_TOKEN, RECORD_REUSE_URL_TOKEN, RECORD_SERVICE_URL_TOKEN, REQUEST_MORE_ON_AGGREGATION, REQUEST_MORE_RESULTS, REQUEST_NEW_RESULTS, RESULTS_LAYOUT_CONFIG, REUSE_LIGHT_CONFIGURATION, ROUTER_CONFIG, ROUTER_ROUTE_DATASET, ROUTER_ROUTE_ORGANIZATION, ROUTER_ROUTE_REUSE, ROUTER_ROUTE_SEARCH, ROUTER_ROUTE_SERVICE, ROUTER_STATE_KEY, ROUTE_PARAMS, RecordApiFormComponent, RecordFormComponent, RecordKindField, RecordMetaComponent, RecordMetricComponent, RecordPreviewCardComponent, RecordPreviewComponent, RecordPreviewFeedComponent, RecordPreviewListComponent, RecordPreviewRowComponent, RecordPreviewTextComponent, RecordPreviewTitleComponent, RecordStatusValues, RecordsMetricsComponent, RecordsRepositoryInterface, RecordsService, RequestMoreOnAggregation, RequestMoreResults, RequestNewResults, ResourceTypeLegacyField, ResultsHitsContainerComponent, ResultsHitsNumberComponent, ResultsHitsSearchKindComponent, ResultsLayoutComponent, ResultsLayoutConfigItem, ResultsListComponent, ResultsListContainerComponent, ResultsListItemComponent, ResultsTableComponent, ResultsTableContainerComponent, ReusePresentationForms, RoleLabels, RoleValues, RouterEffects, RouterFacade, RouterService, SEARCH_FEATURE_KEY, SETTINGS_URL, SET_CONFIG_AGGREGATIONS, SET_CONFIG_FILTERS, SET_CONFIG_REQUEST_FIELDS, SET_ERROR, SET_FAVORITES_ONLY, SET_FILTERS, SET_INCLUDE_ON_AGGREGATION, SET_PAGE_SIZE, SET_RESULTS_AGGREGATIONS, SET_RESULTS_HITS, SET_RESULTS_LAYOUT, SET_SEARCH, SET_SORT_BY, SET_SPATIAL_FILTER_ENABLED, SPATIAL_SCOPES, SearchEffects, SearchFacade, SearchFeatureCatalogComponent, SearchFiltersSummaryComponent, SearchFiltersSummaryItemComponent, SearchInputComponent, SearchRouterContainerDirective, SearchService, SearchStateContainerDirective, SelectionService, ServiceCapabilitiesComponent, SetConfigAggregations, SetConfigFilters, SetConfigRequestFields, SetError, SetFavoritesOnly, SetFilters, SetIncludeOnAggregation, SetPageSize, SetResultsAggregations, SetResultsHits, SetResultsLayout, SetSearch, SetSortBy, SetSpatialFilterEnabled, SimpleSearchField, SiteTitleComponent, SortByComponent, SortByEnum, SortableListComponent, SourceLabelComponent, SourcesService, SpatialExtentComponent, SpatialExtentDropdownComponent, SpinningLoaderComponent, StacItemsResultGridComponent, StacViewComponent, StarToggleComponent, StickyHeaderComponent, SupportedTypes, SwitchToggleComponent, THUMBNAIL_PLACEHOLDER, TRANSLATE_DEBUG_CONFIG, TRANSLATE_DEFAULT_CONFIG, TRANSLATE_WITH_OVERRIDES_CONFIG, TableViewComponent, TextAreaComponent, TextInputComponent, ThemeService, ThumbnailComponent, TranslatedSearchField, TruncatedTextComponent, UPDATE_CONFIG_AGGREGATIONS, UPDATE_FILTERS, UPDATE_REQUEST_AGGREGATION_TERM, UpdateConfigAggregations, UpdateFilters, UrlInputComponent, UserFeedbackItemComponent, UserPreviewComponent, UserSearchField, VECTOR_STYLE_DEFAULT, ViewportIntersectorComponent, WEB_COMPONENT_EMBEDDER_URL, XmlParseError, _reset, allChildrenElement, appConfigWithTranslationFixture, appendChildTree, appendChildren, assertValidXml, associationTypeValues, bboxToPolygon, blockModelFixture, bytesToMegabytes, canEditRecord, checkFileFormat, clearSelectedFeatures, createChild, createDocument, createElement, createFuzzyFilter, createNestedChild, createNestedElement, createSpatialExtentLayer, currentPage, defaultMapStyleFixture, defaultMapStyleHlFixture, downgradeImage, downsizeImage, draftSaveSuccess, dragPanCondition, dropEmptyTranslations, editorReducer, emptyBlockModelFixture, findChildElement, findChildOrCreate, findChildrenElement, findConverterForDocument, findNestedChildOrCreate, findNestedElement, findNestedElements, findParent, firstChildElement, formatDate, formatUserInfo, getAddressLines, getAllKeysValidator, getArrayItem, getAsArray, getAsUrl, getBadgeColor, getCustomTranslations, getError, getFavoritesOnly, getFileFormat, getFileFormatFromServiceOutput, getFirstValue, getFormatPriority, getGeometryBoundingBox, getGeometryFromGeoJSON, getGlobalConfig, getIndividualDisplayName, getIsMobile, getJsonDataItemsProxy, getKeywordHierarchyPath, getLayers, getLinkId, getLinkLabel, getLinkPriority, getMapContext, getMapContextLayerFromConfig, getMapState, getMetadataQualityConfig, getMimeTypeForFormat, getNamespace, getOptionalEditorConfig, getOptionalMapConfig, getOptionalSearchConfig, getPageSize, getQualityValidators, getResourceType, getReusePresentationForm, getReuseType, getRootElement, getSearchConfigAggregations, getSearchFilters, getSearchResults, getSearchResultsAggregations, getSearchResultsHits, getSearchResultsLayout, getSearchResultsLoading, getSearchSortBy, getSearchState, getSearchStateSearch, getSelectedFeatures, getSpatialFilterEnabled, getTemporalRangeUnion, getThemeConfig, handleScrollOnNavigation, hasRecordChangedSinceDraft, hasRecordChangedSinceDraftSuccess, initSearch, initialEditorState, initialMapState, initialState, isBoundingBox, isConfigLoaded, isDateRange, isFileExtensionValid, isFormatInQueryParam, isPublished, itemModelFixture, kindToCodeListValue, loadAppConfig, malformedConfigFixture, mapConfigFixture, mapContact, mapKeywords, mapLogo, mapOrganization, mapReducer, markRecordAsChanged, matchesNoApplicableConstraint, matchesNoKnownConstraint, megabytesToBytes, mimeTypeToFormat, minimalAppConfigFixture, missingMandatoryConfigFixture, mouseWheelZoomCondition, noDuplicateFileName, okAppConfigFixture, openDataset, openRecord, organizationsServiceFactory, parse, parseXmlString, placeholder, prioritizePageScroll, propagateToDocumentOnly, provideGn4, provideI18n, provideLocalizedDateAdapter, provideRepositoryUrl, readAttribute, readDataset, readDatasetHeaders, readFileAsText, readText, reducer$2 as reducer, reducerSearch, removeAllChildren, removeChildren, removeChildrenByName, removeSearchParams, removeWhitespace, renameElements, saveRecord, saveRecordFailure, saveRecordSuccess, selectCanEditRecord, selectCurrentPage, selectEditorConfig, selectEditorState, selectFallback, selectFallbackFields, selectField, selectHasRecordChanged, selectIsPublished, selectRecord, selectRecordChangedSinceSave, selectRecordSaveError, selectRecordSaving, selectRecordSections, selectRecordSource, selectTranslatedField, selectTranslatedValue, setContext, setCurrentPage, setEditorConfiguration, setFieldVisibility, setFocusedField, setSelectedFeatures, setTextContent, sortByFromString, sortByToString, sortByToStrings, spatialExtentToGeometry, spatialExtentsToFeatureCollection, stripHtml, stripNamespace, toDate, toIndividual, toLang2, toLang3, totalPages, undoRecordDraft, unrecognizedKeysConfigFixture, updateFrequencyCodeValues, updateLanguages, updateRecordField, updateRecordLanguages, wmsLayerFlatten, writeAttribute, wrongLanguageCodeConfigFixture, xmlToString };
|
|
40367
40923
|
//# sourceMappingURL=geonetwork-ui.mjs.map
|