geonetwork-ui 2.11.0-dev.a83f0f51a → 2.11.0-dev.aed5b2e5b
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 +1248 -747
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +195 -76
- package/index.d.ts.map +1 -1
- package/package.json +11 -11
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +43 -13
- package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +1 -0
- 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/repository/records-repository.interface.ts +2 -2
- package/src/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts +3 -2
- package/src/libs/feature/dataviz/src/lib/geo-table-view/geo-table-view.component.ts +2 -1
- package/src/libs/feature/dataviz/src/lib/service/data.service.ts +22 -22
- package/src/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.html +1 -0
- package/src/libs/feature/notifications/src/lib/notifications.service.ts +6 -4
- package/src/libs/feature/record/src/lib/state/mdview.actions.ts +4 -8
- package/src/libs/feature/record/src/lib/state/mdview.effects.ts +7 -16
- package/src/libs/feature/record/src/lib/state/mdview.facade.ts +1 -3
- package/src/libs/feature/record/src/lib/state/mdview.reducer.ts +4 -9
- package/src/libs/feature/record/src/lib/state/mdview.selectors.ts +2 -7
- package/src/libs/feature/router/src/lib/default/state/query-params.utils.ts +1 -1
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +9 -4
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +46 -19
- package/src/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts +17 -8
- package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +3 -1
- package/src/libs/feature/search/src/lib/utils/service/fields.ts +16 -1
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.html +6 -7
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.ts +31 -19
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.fixtures.ts +2 -2
- package/src/libs/ui/inputs/src/index.ts +2 -0
- package/src/libs/ui/inputs/src/lib/date-adapter.providers.ts +30 -0
- package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +3 -24
- package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.css +37 -3
- package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.html +135 -16
- package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.ts +166 -29
- package/src/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.ts +3 -7
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.css +1 -0
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.html +20 -7
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.ts +46 -5
- package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.css +0 -0
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html +117 -0
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts +191 -0
- package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +4 -5
- package/src/libs/util/app-config/src/lib/app-config.ts +3 -0
- package/src/libs/util/app-config/src/lib/model.ts +1 -0
- package/src/libs/util/data-fetcher/src/lib/data-fetcher.ts +19 -22
- package/src/libs/util/data-fetcher/src/lib/engine/duckdb.ts +185 -0
- package/src/libs/util/data-fetcher/src/lib/engine/results.ts +63 -0
- package/src/libs/util/data-fetcher/src/lib/{sql-utils.ts → engine/sql-utils.ts} +28 -13
- package/src/libs/util/data-fetcher/src/lib/model.ts +2 -1
- package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +53 -38
- package/src/libs/util/data-fetcher/src/lib/readers/base.ts +11 -0
- package/src/libs/util/data-fetcher/src/lib/readers/csv.ts +9 -47
- package/src/libs/util/data-fetcher/src/lib/readers/excel.ts +27 -27
- package/src/libs/util/data-fetcher/src/lib/readers/geojson.ts +5 -24
- package/src/libs/util/data-fetcher/src/lib/readers/gml.ts +5 -49
- package/src/libs/util/data-fetcher/src/lib/readers/json.ts +5 -23
- package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +184 -128
- package/src/libs/util/data-fetcher/src/lib/utils.ts +0 -143
- package/src/libs/util/shared/src/index.ts +1 -0
- package/src/libs/util/shared/src/lib/autofocus.directive.ts +26 -0
- package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
- package/src/libs/util/shared/src/lib/utils/file.ts +15 -0
- package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
- package/tailwind.base.css +5 -0
- package/translations/de.json +13 -2
- package/translations/en.json +12 -1
- package/translations/es.json +11 -0
- package/translations/fr.json +13 -2
- package/translations/it.json +11 -0
- package/translations/nl.json +11 -0
- package/translations/pt.json +11 -0
- package/translations/sk.json +11 -0
- package/src/libs/util/data-fetcher/src/lib/readers/base-cache.ts +0 -14
|
@@ -6,9 +6,9 @@ import GeoJSON from 'ol/format/GeoJSON.js';
|
|
|
6
6
|
import { parse as parse$1 } from 'ol/xml.js';
|
|
7
7
|
import { format } from 'date-fns/format';
|
|
8
8
|
import { Namespace, Literal, lit, parse as parse$2, sym, BlankNode, graph } from 'rdflib';
|
|
9
|
-
import { lastValueFrom, fromEvent, startWith, map as map$2, shareReplay, filter, pairwise, of, switchMap, Subject, combineLatest, from, exhaustMap, throwError,
|
|
9
|
+
import { lastValueFrom, fromEvent, startWith, map as map$2, shareReplay, filter, pairwise, of, switchMap, Subject, forkJoin, combineLatest, from, exhaustMap, throwError, takeLast, firstValueFrom, merge, BehaviorSubject, timer, ReplaySubject, Subscription, first, distinctUntilChanged as distinctUntilChanged$1, animationFrameScheduler, debounceTime as debounceTime$1, Observable, buffer, tap as tap$2, combineLatestWith, take as take$1, catchError as catchError$1, takeUntil, EMPTY, mergeMap as mergeMap$1, withLatestFrom as withLatestFrom$1 } from 'rxjs';
|
|
10
10
|
import * as i0 from '@angular/core';
|
|
11
|
-
import { InjectionToken, inject, Injectable, NgModule, Injector, provideAppInitializer, makeEnvironmentProviders, ElementRef,
|
|
11
|
+
import { InjectionToken, inject, Injectable, NgModule, Injector, provideAppInitializer, makeEnvironmentProviders, ElementRef, afterNextRender, booleanAttribute, Input, Directive, HostListener, Renderer2, DestroyRef, EventEmitter, Output, ViewChild, ChangeDetectionStrategy, Component, ViewEncapsulation, ChangeDetectorRef, HostBinding, ViewContainerRef, TemplateRef, ViewChildren, ContentChild, input, output, signal, viewChild, computed, ContentChildren, NgZone, ComponentFactoryResolver, viewChildren } from '@angular/core';
|
|
12
12
|
import { HttpClient, HttpHeaders, HttpParams, HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi, HttpEventType } from '@angular/common/http';
|
|
13
13
|
import * as i4 from '@ngx-translate/core';
|
|
14
14
|
import { TranslateLoader, TranslateCompiler, TranslateDefaultParser, TranslateParser, TranslateService, provideTranslateService, TranslateDirective, TranslatePipe, TranslateModule } from '@ngx-translate/core';
|
|
@@ -18,7 +18,7 @@ import { map as map$1, shareReplay as shareReplay$1, catchError, tap as tap$1, f
|
|
|
18
18
|
import { lt, valid, coerce, satisfies, ltr } from 'semver';
|
|
19
19
|
import chroma from 'chroma-js';
|
|
20
20
|
import * as i1$1 from '@angular/common';
|
|
21
|
-
import { Location, CommonModule,
|
|
21
|
+
import { Location, CommonModule, NgTemplateOutlet, NgClass, AsyncPipe } from '@angular/common';
|
|
22
22
|
import { formatDistance } from 'date-fns/formatDistance';
|
|
23
23
|
import { Scroll, NavigationEnd, Router, RouteReuseStrategy } from '@angular/router';
|
|
24
24
|
import { WmtsEndpoint, WmsEndpoint, WfsEndpoint, OgcApiEndpoint, sharedFetch, useCache, StacEndpoint, TmsEndpoint } from '@camptocamp/ogc-client';
|
|
@@ -41,10 +41,10 @@ import * as TOML from '@ltd/j-toml';
|
|
|
41
41
|
import { Style, Stroke, Fill, Circle } from 'ol/style.js';
|
|
42
42
|
import CircleStyle from 'ol/style/Circle.js';
|
|
43
43
|
import EmblaCarousel from 'embla-carousel';
|
|
44
|
-
import { iconoirNavArrowLeft, iconoirNavArrowRight, iconoirLongArrowDownLeft, iconoirSearch, iconoirCalendar, iconoirArrowUp, iconoirLink, iconoirFramePlusIn, iconoirCloudUpload, iconoirReduce, iconoirNavArrowUp, iconoirNavArrowDown, iconoirExpand, iconoirSettings, iconoirDownload, iconoirPlus, iconoirBin, iconoirMediaImageXmark, iconoirMediaImage, iconoirAppWindow, iconoirCode, iconoirDatabase, iconoirAppleWallet, iconoirBank, iconoirList, iconoirTranslate, iconoirLock, iconoirUser, iconoirArrowLeft, iconoirLightBulbOn,
|
|
44
|
+
import { iconoirNavArrowLeft, iconoirNavArrowRight, iconoirLongArrowDownLeft, iconoirSearch, iconoirCalendar, iconoirArrowUp, iconoirLink, iconoirFramePlusIn, iconoirCloudUpload, iconoirTrash, iconoirSquareDashed, iconoirImport, iconoirCheckCircle, iconoirReduce, iconoirNavArrowUp, iconoirNavArrowDown, iconoirExpand, iconoirSettings, iconoirDownload, iconoirPlus, iconoirBin, iconoirMediaImageXmark, iconoirMediaImage, iconoirAppWindow, iconoirCode, iconoirDatabase, iconoirAppleWallet, iconoirBank, iconoirList, iconoirTranslate, iconoirLock, iconoirUser, iconoirArrowLeft, iconoirLightBulbOn, iconoirBadgeCheck, iconoirSystemShut, iconoirCircle, iconoirAttachment, iconoirRefresh } from '@ng-icons/iconoir';
|
|
45
45
|
import { MatButtonModule } from '@angular/material/button';
|
|
46
|
-
import * as i1$
|
|
47
|
-
import {
|
|
46
|
+
import * as i1$7 from '@angular/cdk/overlay';
|
|
47
|
+
import { ScrollStrategyOptions, OverlayModule, CdkConnectedOverlay, ScrollDispatcher, Overlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
|
|
48
48
|
import * as i1$3 from '@angular/forms';
|
|
49
49
|
import { UntypedFormControl, ReactiveFormsModule, FormsModule } from '@angular/forms';
|
|
50
50
|
import * as i1$2 from '@angular/material/autocomplete';
|
|
@@ -52,16 +52,18 @@ import { MatAutocompleteModule, MatAutocompleteTrigger, MatAutocomplete } from '
|
|
|
52
52
|
import tippy from 'tippy.js';
|
|
53
53
|
import * as i2 from '@angular/material/progress-spinner';
|
|
54
54
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
55
|
-
import { matClose, matContentCopy,
|
|
55
|
+
import { matClose, matContentCopy, matExpandMore, matExpandLess, matSearch, matStarBorder, matStar, matRemove, matAdd, matChevronRight, matChevronLeft, matArrowForwardIos, matArrowBackIos, matMoodBad, matQuestionMark, matFace, matZoomOutMap, matOpenInNew, matPersonOutline, matMailOutline, matWarningAmber, matCheck, matCheckCircleOutline, matWarning, matMoreVert } from '@ng-icons/material-icons/baseline';
|
|
56
56
|
import * as i1$4 from '@angular/material/checkbox';
|
|
57
57
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
58
58
|
import * as i1$5 from '@angular/material/tooltip';
|
|
59
59
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
60
|
-
import {
|
|
60
|
+
import { MAT_DATE_LOCALE, MAT_DATE_FORMATS, DateAdapter } from '@angular/material/core';
|
|
61
|
+
import { MAT_DATE_FNS_FORMATS, DateFnsAdapter } from '@angular/material-date-fns-adapter';
|
|
62
|
+
import { enUS } from 'date-fns/locale/en-US';
|
|
61
63
|
import * as i1$6 from '@angular/material/datepicker';
|
|
62
64
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
|
63
|
-
import * as i1$
|
|
64
|
-
import { NgxDropzoneModule } from 'ngx-dropzone';
|
|
65
|
+
import * as i1$8 from 'ngx-dropzone';
|
|
66
|
+
import { NgxDropzoneModule, NgxDropzoneComponent } from 'ngx-dropzone';
|
|
65
67
|
import * as i1$9 from '@angular/material/button-toggle';
|
|
66
68
|
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
|
67
69
|
import { moveItemInArray, CdkDropList, CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop';
|
|
@@ -79,10 +81,10 @@ import { ScrollingModule, CdkScrollable } from '@angular/cdk/scrolling';
|
|
|
79
81
|
import Duration from 'duration-relativetimeformat';
|
|
80
82
|
import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
|
|
81
83
|
import { trigger, transition, animate, keyframes, style } from '@angular/animations';
|
|
82
|
-
import * as
|
|
83
|
-
import {
|
|
84
|
-
import
|
|
85
|
-
import
|
|
84
|
+
import * as duckdb from '@duckdb/duckdb-wasm';
|
|
85
|
+
import { Vector, DataType } from 'apache-arrow';
|
|
86
|
+
import WFS from 'ol/format/WFS';
|
|
87
|
+
import GeoJSON$1 from 'ol/format/GeoJSON';
|
|
86
88
|
import { Chart, BarController, BarElement, CategoryScale, LinearScale, LineController, LineElement, PointElement, PieController, ArcElement, ScatterController, Tooltip, Colors, Legend } from 'chart.js';
|
|
87
89
|
import * as i2$4 from '@angular/material/sort';
|
|
88
90
|
import { MatSortModule, MatSort } from '@angular/material/sort';
|
|
@@ -90,7 +92,6 @@ import * as i1$b from '@angular/material/table';
|
|
|
90
92
|
import { MatTableModule } from '@angular/material/table';
|
|
91
93
|
import * as i3 from '@angular/material/paginator';
|
|
92
94
|
import { MatPaginatorIntl, MatPaginatorModule, MatPaginator } from '@angular/material/paginator';
|
|
93
|
-
import { LetDirective } from '@ngrx/component';
|
|
94
95
|
import { Meta } from '@angular/platform-browser';
|
|
95
96
|
import { tablerFolderOpen } from '@ng-icons/tabler-icons';
|
|
96
97
|
import * as i3$1 from '@angular/material/radio';
|
|
@@ -18907,14 +18908,29 @@ class Gn4FieldMapper {
|
|
|
18907
18908
|
featureTypes: selectField(source, 'featureTypes'),
|
|
18908
18909
|
}, output),
|
|
18909
18910
|
related: (output, source) => {
|
|
18910
|
-
const
|
|
18911
|
+
const related = selectField(source, 'related');
|
|
18912
|
+
const fcatSource = selectField(getFirstValue(selectField(related, 'fcats')) ?? {}, '_source');
|
|
18911
18913
|
const featureCatalogIdentifier = selectField(fcatSource, 'uuid');
|
|
18912
|
-
const sourceOfLinks = getAsArray(selectField(
|
|
18914
|
+
const sourceOfLinks = getAsArray(selectField(related, 'hassources'));
|
|
18913
18915
|
const sourceOfIdentifiers = sourceOfLinks
|
|
18914
18916
|
.filter((link) => link['origin'] === 'catalog')
|
|
18915
18917
|
.map((link) => {
|
|
18916
18918
|
return selectField(selectField(link, '_source'), 'uuid');
|
|
18917
18919
|
});
|
|
18920
|
+
const siblingLinks = getAsArray(selectField(related, 'siblings'));
|
|
18921
|
+
const siblings = siblingLinks
|
|
18922
|
+
.filter((link) => link['origin'] === 'catalog')
|
|
18923
|
+
.map((link) => ({
|
|
18924
|
+
uniqueIdentifier: selectField(selectField(link, '_source'), 'uuid'),
|
|
18925
|
+
associationType: getAssociationTypeFromCode(selectField(selectField(link, 'properties'), 'associationType')),
|
|
18926
|
+
}));
|
|
18927
|
+
const associatedLinks = getAsArray(selectField(related, 'associated'));
|
|
18928
|
+
const associatedIdentifiers = associatedLinks
|
|
18929
|
+
.filter((link) => link['origin'] === 'catalog')
|
|
18930
|
+
.map((link) => {
|
|
18931
|
+
return selectField(selectField(link, '_source'), 'uuid');
|
|
18932
|
+
})
|
|
18933
|
+
.filter((uuid) => !siblings.some((sibling) => sibling.uniqueIdentifier === uuid));
|
|
18918
18934
|
const extraValues = {};
|
|
18919
18935
|
if (featureCatalogIdentifier) {
|
|
18920
18936
|
extraValues.featureCatalogIdentifier = featureCatalogIdentifier;
|
|
@@ -18922,6 +18938,12 @@ class Gn4FieldMapper {
|
|
|
18922
18938
|
if (sourceOfIdentifiers && sourceOfIdentifiers.length > 0) {
|
|
18923
18939
|
extraValues.sourceOfIdentifiers = sourceOfIdentifiers;
|
|
18924
18940
|
}
|
|
18941
|
+
if (associatedIdentifiers && associatedIdentifiers.length > 0) {
|
|
18942
|
+
extraValues.associatedIdentifiers = associatedIdentifiers;
|
|
18943
|
+
}
|
|
18944
|
+
if (siblings && siblings.length > 0) {
|
|
18945
|
+
extraValues.siblings = siblings;
|
|
18946
|
+
}
|
|
18925
18947
|
return Object.keys(extraValues).length > 0
|
|
18926
18948
|
? this.addExtra(extraValues, output)
|
|
18927
18949
|
: output;
|
|
@@ -19447,10 +19469,10 @@ class DateService {
|
|
|
19447
19469
|
const dateObj = this.getDateObject(date);
|
|
19448
19470
|
return { locale, dateObj };
|
|
19449
19471
|
}
|
|
19450
|
-
async
|
|
19472
|
+
async getDateFnsLocale() {
|
|
19451
19473
|
const lang = this.translateService.getCurrentLang() || DEFAULT_LANGUAGE;
|
|
19452
19474
|
const locales = await this.dateLocales;
|
|
19453
|
-
return locales[lang];
|
|
19475
|
+
return locales[lang] ?? locales[DEFAULT_LANGUAGE];
|
|
19454
19476
|
}
|
|
19455
19477
|
formatDate(date, options) {
|
|
19456
19478
|
const { locale, dateObj } = this.getLocaleAndDate(date);
|
|
@@ -19463,7 +19485,7 @@ class DateService {
|
|
|
19463
19485
|
async formatRelativeDateTime(date) {
|
|
19464
19486
|
const dateObj = this.getDateObject(date);
|
|
19465
19487
|
const now = new Date();
|
|
19466
|
-
const locale = await this.
|
|
19488
|
+
const locale = await this.getDateFnsLocale();
|
|
19467
19489
|
return formatDistance(dateObj, now, {
|
|
19468
19490
|
addSuffix: true,
|
|
19469
19491
|
locale: locale,
|
|
@@ -19498,6 +19520,18 @@ function propagateToDocumentOnly(event) {
|
|
|
19498
19520
|
}, 0);
|
|
19499
19521
|
}
|
|
19500
19522
|
|
|
19523
|
+
function isFileExtensionValid(fileName, acceptedExtensions) {
|
|
19524
|
+
return acceptedExtensions.some((ext) => fileName.toLowerCase().endsWith(ext));
|
|
19525
|
+
}
|
|
19526
|
+
function readFileAsText(file) {
|
|
19527
|
+
return new Promise((resolve, reject) => {
|
|
19528
|
+
const reader = new FileReader();
|
|
19529
|
+
reader.onload = () => resolve(reader.result);
|
|
19530
|
+
reader.onerror = () => reject(reader.error);
|
|
19531
|
+
reader.readAsText(file);
|
|
19532
|
+
});
|
|
19533
|
+
}
|
|
19534
|
+
|
|
19501
19535
|
function formatUserInfo(userInfo, displayCount = false) {
|
|
19502
19536
|
const infos = (typeof userInfo === 'string' ? userInfo : '').split('|');
|
|
19503
19537
|
const count = displayCount ? ` ${infos[3].split(' ')[1]}` : '';
|
|
@@ -20342,6 +20376,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
20342
20376
|
}]
|
|
20343
20377
|
}] });
|
|
20344
20378
|
|
|
20379
|
+
class AutofocusDirective {
|
|
20380
|
+
constructor() {
|
|
20381
|
+
this.el = inject(ElementRef);
|
|
20382
|
+
this.injector = inject(Injector);
|
|
20383
|
+
}
|
|
20384
|
+
set autofocus(active) {
|
|
20385
|
+
if (!active)
|
|
20386
|
+
return;
|
|
20387
|
+
afterNextRender(() => this.el.nativeElement.focus(), {
|
|
20388
|
+
injector: this.injector,
|
|
20389
|
+
});
|
|
20390
|
+
}
|
|
20391
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AutofocusDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
20392
|
+
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 }); }
|
|
20393
|
+
}
|
|
20394
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AutofocusDirective, decorators: [{
|
|
20395
|
+
type: Directive,
|
|
20396
|
+
args: [{
|
|
20397
|
+
selector: '[gnUiAutofocus]',
|
|
20398
|
+
standalone: true,
|
|
20399
|
+
}]
|
|
20400
|
+
}], propDecorators: { autofocus: [{
|
|
20401
|
+
type: Input,
|
|
20402
|
+
args: [{ alias: 'gnUiAutofocus', transform: booleanAttribute }]
|
|
20403
|
+
}] } });
|
|
20404
|
+
|
|
20345
20405
|
class ImageFallbackDirective {
|
|
20346
20406
|
constructor() {
|
|
20347
20407
|
this.el = inject(ElementRef);
|
|
@@ -20369,7 +20429,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
20369
20429
|
}] } });
|
|
20370
20430
|
|
|
20371
20431
|
var name = "geonetwork-ui";
|
|
20372
|
-
var version = "2.11.0-dev.
|
|
20432
|
+
var version = "2.11.0-dev.aed5b2e5b";
|
|
20373
20433
|
var engines = {
|
|
20374
20434
|
node: ">=24"
|
|
20375
20435
|
};
|
|
@@ -20399,20 +20459,21 @@ var peerDependencies = {
|
|
|
20399
20459
|
"@ngrx/store": "19.x || 20.x || 21.x",
|
|
20400
20460
|
"@ngrx/store-devtools": "19.x || 20.x || 21.x",
|
|
20401
20461
|
"@ngrx/operators": "19.x || 20.x || 21.x",
|
|
20402
|
-
"@ngx-translate/core": "
|
|
20403
|
-
"@ngx-translate/http-loader": "
|
|
20404
|
-
"flag-icons": "
|
|
20462
|
+
"@ngx-translate/core": "17.x",
|
|
20463
|
+
"@ngx-translate/http-loader": "17.x",
|
|
20464
|
+
"flag-icons": "~7.5.0",
|
|
20405
20465
|
rxjs: "7.x",
|
|
20406
20466
|
"zone.js": "*",
|
|
20407
20467
|
tailwindcss: "3.x"
|
|
20408
20468
|
};
|
|
20409
20469
|
var dependencies = {
|
|
20410
20470
|
"@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/
|
|
20471
|
+
"@camptocamp/ogc-client": "1.3.1-dev.200c02a",
|
|
20472
|
+
"@duckdb/duckdb-wasm": "~1.32.0",
|
|
20473
|
+
"@geospatial-sdk/core": "0.0.5-dev.78",
|
|
20474
|
+
"@geospatial-sdk/geocoding": "0.0.5-dev.78",
|
|
20475
|
+
"@geospatial-sdk/legend": "0.0.5-dev.78",
|
|
20476
|
+
"@geospatial-sdk/openlayers": "0.0.5-dev.78",
|
|
20416
20477
|
"@ltd/j-toml": "~1.38.0",
|
|
20417
20478
|
"@messageformat/core": "~3.4.0",
|
|
20418
20479
|
"@ng-icons/core": "~32.5.0",
|
|
@@ -20420,7 +20481,7 @@ var dependencies = {
|
|
|
20420
20481
|
"@ng-icons/material-icons": "~32.5.0",
|
|
20421
20482
|
"@ng-icons/tabler-icons": "~32.4.0",
|
|
20422
20483
|
"@rgrove/parse-xml": "4.2.0",
|
|
20423
|
-
|
|
20484
|
+
"apache-arrow": "~17.0.0",
|
|
20424
20485
|
"chart.js": "~4.5.1",
|
|
20425
20486
|
"chroma-js": "~3.2.0",
|
|
20426
20487
|
"date-fns": "4.1.0",
|
|
@@ -20433,7 +20494,6 @@ var dependencies = {
|
|
|
20433
20494
|
"ngx-dropzone": "~3.1.0",
|
|
20434
20495
|
"ngx-translate-messageformat-compiler": "~7.2.0",
|
|
20435
20496
|
ol: "~10.8.0",
|
|
20436
|
-
papaparse: "~5.5.3",
|
|
20437
20497
|
proj4: "~2.20.4",
|
|
20438
20498
|
rdflib: "~2.3.5",
|
|
20439
20499
|
semver: "~7.7.4",
|
|
@@ -20678,6 +20738,7 @@ class ElasticsearchService {
|
|
|
20678
20738
|
values: uuids,
|
|
20679
20739
|
},
|
|
20680
20740
|
},
|
|
20741
|
+
size: uuids.length,
|
|
20681
20742
|
};
|
|
20682
20743
|
}
|
|
20683
20744
|
getRelatedRecordPayload(record, size = 6, _source = [...ES_SOURCE_SUMMARY, 'createDate']) {
|
|
@@ -21229,7 +21290,7 @@ class Gn4Repository {
|
|
|
21229
21290
|
}
|
|
21230
21291
|
getRecord(uniqueIdentifier) {
|
|
21231
21292
|
return this.gn4SearchApi
|
|
21232
|
-
.search('bucket', ['fcats', 'hassources'], JSON.stringify(this.gn4SearchHelper.getMetadataByIdsPayload([uniqueIdentifier])))
|
|
21293
|
+
.search('bucket', ['fcats', 'hassources', 'siblings', 'associated'], JSON.stringify(this.gn4SearchHelper.getMetadataByIdsPayload([uniqueIdentifier])))
|
|
21233
21294
|
.pipe(map$1((results) => results.hits.hits[0]), switchMap((record) => record ? this.gn4Mapper.readRecord(record) : of(null)));
|
|
21234
21295
|
}
|
|
21235
21296
|
getMultipleRecords(uniqueIdentifiers) {
|
|
@@ -21283,19 +21344,28 @@ class Gn4Repository {
|
|
|
21283
21344
|
.search('bucket', null, JSON.stringify(this.gn4SearchHelper.getRelatedRecordPayload(similarTo, 3)))
|
|
21284
21345
|
.pipe(switchMap((results) => this.gn4Mapper.readRecords(results.hits.hits)));
|
|
21285
21346
|
}
|
|
21286
|
-
|
|
21287
|
-
const
|
|
21288
|
-
|
|
21289
|
-
|
|
21290
|
-
|
|
21291
|
-
|
|
21292
|
-
|
|
21293
|
-
|
|
21294
|
-
|
|
21295
|
-
|
|
21296
|
-
|
|
21347
|
+
getLinkedRecords(record) {
|
|
21348
|
+
const siblings = (record.extras?.['siblings'] ?? []);
|
|
21349
|
+
const relations = [
|
|
21350
|
+
['source', (record.extras?.['sourcesIdentifiers'] ?? [])],
|
|
21351
|
+
['sourceOf', (record.extras?.['sourceOfIdentifiers'] ?? [])],
|
|
21352
|
+
['sibling', siblings.map(({ uniqueIdentifier }) => uniqueIdentifier)],
|
|
21353
|
+
[
|
|
21354
|
+
'associated',
|
|
21355
|
+
(record.extras?.['associatedIdentifiers'] ?? []),
|
|
21356
|
+
],
|
|
21357
|
+
];
|
|
21358
|
+
const requested = relations.filter(([, identifiers]) => identifiers.length > 0);
|
|
21359
|
+
if (requested.length === 0) {
|
|
21360
|
+
return of([]);
|
|
21297
21361
|
}
|
|
21298
|
-
return
|
|
21362
|
+
return forkJoin(requested.map(([relation, identifiers]) => this.getMultipleRecords(identifiers).pipe(map$1((records) => (records ?? []).map((record) => ({
|
|
21363
|
+
record,
|
|
21364
|
+
relation,
|
|
21365
|
+
associationType: relation === 'sibling'
|
|
21366
|
+
? siblings.find(({ uniqueIdentifier }) => uniqueIdentifier === record.uniqueIdentifier)?.associationType
|
|
21367
|
+
: undefined,
|
|
21368
|
+
}))), catchError(() => of([]))))).pipe(map$1((groups) => groups.flat()));
|
|
21299
21369
|
}
|
|
21300
21370
|
aggregate(params) {
|
|
21301
21371
|
// if aggregations are empty, return an empty object right away
|
|
@@ -22556,9 +22626,8 @@ const VECTOR_STYLE_DEFAULT = new InjectionToken('vectorStyleDefault', {
|
|
|
22556
22626
|
});
|
|
22557
22627
|
|
|
22558
22628
|
const DEFAULT_BASEMAP_LAYER = {
|
|
22559
|
-
type: '
|
|
22560
|
-
|
|
22561
|
-
attributions: `<span>© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, © <a href="https://carto.com/">Carto</a></span>`,
|
|
22629
|
+
type: 'maplibre-style',
|
|
22630
|
+
styleUrl: `https://basemaps.cartocdn.com/gl/positron-gl-style/style.json`,
|
|
22562
22631
|
};
|
|
22563
22632
|
const DEFAULT_VIEW = {
|
|
22564
22633
|
center: [0, 15],
|
|
@@ -23273,6 +23342,7 @@ function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
23273
23342
|
'search_preset',
|
|
23274
23343
|
'advanced_filters',
|
|
23275
23344
|
'limit',
|
|
23345
|
+
'spatial_extent_max_file_size',
|
|
23276
23346
|
], warnings, errors);
|
|
23277
23347
|
const parsedSearchParams = parseMultiConfigSection(parsed, 'search_preset', ['name'], ['sort', 'filters'], warnings, errors);
|
|
23278
23348
|
searchConfig =
|
|
@@ -23290,6 +23360,7 @@ function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
|
|
|
23290
23360
|
})),
|
|
23291
23361
|
ADVANCED_FILTERS: parsedSearchSection.advanced_filters,
|
|
23292
23362
|
LIMIT: parsedSearchSection.limit,
|
|
23363
|
+
SPATIAL_EXTENT_MAX_FILE_SIZE: parsedSearchSection.spatial_extent_max_file_size,
|
|
23293
23364
|
};
|
|
23294
23365
|
const parsedMetadataQualitySection = parseConfigSection(parsed, 'metadata-quality', [], ['enabled'], warnings, errors);
|
|
23295
23366
|
metadataQualityConfig =
|
|
@@ -24547,12 +24618,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24547
24618
|
type: Input
|
|
24548
24619
|
}] } });
|
|
24549
24620
|
|
|
24621
|
+
function provideLocalizedDateAdapter() {
|
|
24622
|
+
return [
|
|
24623
|
+
{ provide: MAT_DATE_LOCALE, useValue: enUS },
|
|
24624
|
+
{ provide: MAT_DATE_FORMATS, useValue: MAT_DATE_FNS_FORMATS },
|
|
24625
|
+
{
|
|
24626
|
+
provide: DateAdapter,
|
|
24627
|
+
useFactory: () => {
|
|
24628
|
+
const dateService = inject(DateService);
|
|
24629
|
+
const adapter = new DateFnsAdapter();
|
|
24630
|
+
dateService
|
|
24631
|
+
.getDateFnsLocale()
|
|
24632
|
+
.then((locale) => adapter.setLocale(locale));
|
|
24633
|
+
return adapter;
|
|
24634
|
+
},
|
|
24635
|
+
},
|
|
24636
|
+
];
|
|
24637
|
+
}
|
|
24638
|
+
|
|
24550
24639
|
class DatePickerComponent {
|
|
24551
24640
|
constructor() {
|
|
24552
|
-
this.dateAdapter = inject(DateAdapter);
|
|
24553
|
-
this.translate = inject(TranslateService);
|
|
24554
24641
|
this.dateChange = new EventEmitter();
|
|
24555
|
-
this.dateAdapter.setLocale(this.translate.getCurrentLang());
|
|
24556
24642
|
}
|
|
24557
24643
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
24558
24644
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: DatePickerComponent, isStandalone: true, selector: "gn-ui-date-picker", inputs: { date: "date" }, outputs: { dateChange: "dateChange" }, providers: [
|
|
@@ -24560,30 +24646,19 @@ class DatePickerComponent {
|
|
|
24560
24646
|
provideNgIconsConfig({
|
|
24561
24647
|
size: '1.5rem',
|
|
24562
24648
|
}),
|
|
24563
|
-
|
|
24564
|
-
|
|
24565
|
-
useFactory: (locale) => locale,
|
|
24566
|
-
},
|
|
24567
|
-
], ngImport: i0, template: "<span class=\"w-full inline-block relative\">\n <input\n class=\"gn-ui-text-input pr-[var(--text-padding)]\"\n [matDatepicker]=\"picker\"\n [value]=\"date\"\n (dateChange)=\"dateChange.emit($event.value)\"\n />\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n data-cy=\"date-picker-button\"\n extraClass=\"h-full\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-datepicker #picker></mat-datepicker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"], dependencies: [{ kind: "ngmodule", type: MatNativeDateModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i1$6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i1$6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
24649
|
+
provideLocalizedDateAdapter(),
|
|
24650
|
+
], ngImport: i0, template: "<span class=\"w-full inline-block relative\">\n <input\n class=\"gn-ui-text-input pr-[var(--text-padding)]\"\n [matDatepicker]=\"picker\"\n [value]=\"date\"\n (dateChange)=\"dateChange.emit($event.value)\"\n />\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n data-cy=\"date-picker-button\"\n extraClass=\"h-full\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-datepicker #picker></mat-datepicker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"], dependencies: [{ kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i1$6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i1$6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
24568
24651
|
}
|
|
24569
24652
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DatePickerComponent, decorators: [{
|
|
24570
24653
|
type: Component,
|
|
24571
|
-
args: [{ selector: 'gn-ui-date-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
24572
|
-
MatNativeDateModule,
|
|
24573
|
-
MatDatepickerModule,
|
|
24574
|
-
ButtonComponent,
|
|
24575
|
-
NgIconComponent,
|
|
24576
|
-
], providers: [
|
|
24654
|
+
args: [{ selector: 'gn-ui-date-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [MatDatepickerModule, ButtonComponent, NgIconComponent], providers: [
|
|
24577
24655
|
provideIcons({ iconoirCalendar }),
|
|
24578
24656
|
provideNgIconsConfig({
|
|
24579
24657
|
size: '1.5rem',
|
|
24580
24658
|
}),
|
|
24581
|
-
|
|
24582
|
-
provide: MAT_DATE_LOCALE,
|
|
24583
|
-
useFactory: (locale) => locale,
|
|
24584
|
-
},
|
|
24659
|
+
provideLocalizedDateAdapter(),
|
|
24585
24660
|
], template: "<span class=\"w-full inline-block relative\">\n <input\n class=\"gn-ui-text-input pr-[var(--text-padding)]\"\n [matDatepicker]=\"picker\"\n [value]=\"date\"\n (dateChange)=\"dateChange.emit($event.value)\"\n />\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n data-cy=\"date-picker-button\"\n extraClass=\"h-full\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-datepicker #picker></mat-datepicker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"] }]
|
|
24586
|
-
}],
|
|
24661
|
+
}], propDecorators: { date: [{
|
|
24587
24662
|
type: Input
|
|
24588
24663
|
}], dateChange: [{
|
|
24589
24664
|
type: Output
|
|
@@ -24591,58 +24666,159 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24591
24666
|
|
|
24592
24667
|
class DateRangeDropdownComponent {
|
|
24593
24668
|
constructor() {
|
|
24594
|
-
this.
|
|
24669
|
+
this.scrollStrategies = inject(ScrollStrategyOptions);
|
|
24670
|
+
this.dateAdapter = inject(DateAdapter);
|
|
24671
|
+
this.dateFormats = inject(MAT_DATE_FORMATS);
|
|
24595
24672
|
this.cdr = inject(ChangeDetectorRef);
|
|
24596
|
-
this.
|
|
24597
|
-
this.
|
|
24598
|
-
this.
|
|
24673
|
+
this.dateRange = {};
|
|
24674
|
+
this.dateRangeChange = new EventEmitter();
|
|
24675
|
+
this.overlayPositions = [
|
|
24676
|
+
{
|
|
24677
|
+
originX: 'start',
|
|
24678
|
+
originY: 'bottom',
|
|
24679
|
+
overlayX: 'start',
|
|
24680
|
+
overlayY: 'top',
|
|
24681
|
+
offsetY: 8,
|
|
24682
|
+
},
|
|
24683
|
+
{
|
|
24684
|
+
originX: 'start',
|
|
24685
|
+
originY: 'top',
|
|
24686
|
+
overlayX: 'start',
|
|
24687
|
+
overlayY: 'bottom',
|
|
24688
|
+
offsetY: -8,
|
|
24689
|
+
},
|
|
24690
|
+
];
|
|
24691
|
+
this.scrollStrategy = this.scrollStrategies.reposition();
|
|
24692
|
+
this.overlayOpen = false;
|
|
24693
|
+
this.expandedBound = 'start';
|
|
24694
|
+
this.invalidBounds = { start: false, end: false };
|
|
24695
|
+
// redraw when the lazily loaded UI locale reaches the adapter
|
|
24696
|
+
this.dateAdapter.localeChanges
|
|
24697
|
+
.pipe(takeUntilDestroyed())
|
|
24698
|
+
.subscribe(() => this.cdr.markForCheck());
|
|
24599
24699
|
}
|
|
24600
|
-
|
|
24601
|
-
this.
|
|
24700
|
+
get startDate() {
|
|
24701
|
+
return this.dateRange.start;
|
|
24602
24702
|
}
|
|
24603
|
-
|
|
24604
|
-
|
|
24605
|
-
|
|
24606
|
-
|
|
24607
|
-
|
|
24608
|
-
|
|
24609
|
-
|
|
24610
|
-
|
|
24703
|
+
get endDate() {
|
|
24704
|
+
return this.dateRange.end;
|
|
24705
|
+
}
|
|
24706
|
+
get selectedDatesCount() {
|
|
24707
|
+
return (this.startDate ? 1 : 0) + (this.endDate ? 1 : 0);
|
|
24708
|
+
}
|
|
24709
|
+
openOverlay() {
|
|
24710
|
+
this.expandedBound = 'start';
|
|
24711
|
+
this.overlayOpen = true;
|
|
24712
|
+
}
|
|
24713
|
+
closeOverlay() {
|
|
24714
|
+
this.overlayOpen = false;
|
|
24715
|
+
}
|
|
24716
|
+
toggleBound(bound) {
|
|
24717
|
+
this.expandedBound = this.expandedBound === bound ? null : bound;
|
|
24718
|
+
}
|
|
24719
|
+
formatDate(date) {
|
|
24720
|
+
return this.dateAdapter.format(date, this.dateFormats.display.dateInput);
|
|
24721
|
+
}
|
|
24722
|
+
onDateInput(bound, event) {
|
|
24723
|
+
const typedText = event.target.value.trim();
|
|
24724
|
+
const date = typedText
|
|
24725
|
+
? this.dateAdapter.parse(typedText, this.dateFormats.parse.dateInput)
|
|
24726
|
+
: null;
|
|
24727
|
+
if (!this.isAcceptable(bound, typedText, date)) {
|
|
24728
|
+
// keep the text as typed and flag it; the filter stays on its last value
|
|
24729
|
+
this.invalidBounds = { ...this.invalidBounds, [bound]: true };
|
|
24730
|
+
return;
|
|
24731
|
+
}
|
|
24732
|
+
if (bound === 'start')
|
|
24733
|
+
this.setStartDate(date);
|
|
24734
|
+
else
|
|
24735
|
+
this.setEndDate(date);
|
|
24736
|
+
}
|
|
24737
|
+
normalizeDateInput(bound, event) {
|
|
24738
|
+
if (this.invalidBounds[bound])
|
|
24739
|
+
return;
|
|
24740
|
+
const date = bound === 'start' ? this.startDate : this.endDate;
|
|
24741
|
+
const input = event.target;
|
|
24742
|
+
input.value = date ? this.formatDate(date) : '';
|
|
24743
|
+
}
|
|
24744
|
+
isAcceptable(bound, typedText, date) {
|
|
24745
|
+
if (!typedText)
|
|
24746
|
+
return true;
|
|
24747
|
+
if (!date || !this.dateAdapter.isValid(date))
|
|
24748
|
+
return false;
|
|
24749
|
+
return bound === 'start'
|
|
24750
|
+
? !this.endDate || date <= this.endDate
|
|
24751
|
+
: !this.startDate || date >= this.startDate;
|
|
24752
|
+
}
|
|
24753
|
+
setStartDate(date) {
|
|
24754
|
+
this.invalidBounds = { ...this.invalidBounds, start: false };
|
|
24755
|
+
this.applyDateRange({ ...this.dateRange, start: date });
|
|
24756
|
+
this.expandedBound = 'end';
|
|
24757
|
+
}
|
|
24758
|
+
setEndDate(date) {
|
|
24759
|
+
this.invalidBounds = { ...this.invalidBounds, end: false };
|
|
24760
|
+
this.applyDateRange({ ...this.dateRange, end: date });
|
|
24761
|
+
this.expandedBound = null;
|
|
24762
|
+
}
|
|
24763
|
+
clearDates(event) {
|
|
24764
|
+
this.invalidBounds = { start: false, end: false };
|
|
24765
|
+
this.expandedBound = 'start';
|
|
24766
|
+
this.applyDateRange({});
|
|
24767
|
+
propagateToDocumentOnly(event);
|
|
24768
|
+
}
|
|
24769
|
+
applyDateRange(dateRange) {
|
|
24770
|
+
this.dateRange = {
|
|
24771
|
+
...(dateRange.start && { start: dateRange.start }),
|
|
24772
|
+
...(dateRange.end && { end: dateRange.end }),
|
|
24773
|
+
};
|
|
24774
|
+
this.dateRangeChange.emit(this.dateRange);
|
|
24611
24775
|
}
|
|
24612
24776
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangeDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
24613
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
24777
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DateRangeDropdownComponent, isStandalone: true, selector: "gn-ui-date-range-dropdown", inputs: { title: "title", dateRange: "dateRange" }, outputs: { dateRangeChange: "dateRangeChange" }, providers: [
|
|
24614
24778
|
provideIcons({
|
|
24615
|
-
|
|
24779
|
+
iconoirCalendar,
|
|
24780
|
+
matClose,
|
|
24616
24781
|
matExpandLess,
|
|
24782
|
+
matExpandMore,
|
|
24617
24783
|
}),
|
|
24618
|
-
|
|
24784
|
+
provideNgIconsConfig({
|
|
24785
|
+
size: '1.5rem',
|
|
24786
|
+
}),
|
|
24787
|
+
provideLocalizedDateAdapter(),
|
|
24788
|
+
], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }], ngImport: i0, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n (buttonClick)=\"openOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (selectedDatesCount) {\n <div\n class=\"gn-ui-multiselect-counter shrink-0 rounded-full font-bold text-[12px] w-5 h-5 flex items-center justify-center mr-1 selected-count\"\n >\n {{ selectedDatesCount }}\n </div>\n }\n </div>\n <button\n class=\"h-6 w-6 flex items-center justify-center\"\n data-test=\"dropdown-clear\"\n >\n @if (selectedDatesCount && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"clearDates($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n data-test=\"dropdown-toggle\"\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"w-[296px] flex flex-col gap-2 bg-white border border-gray-400 rounded px-4 py-3 shadow-lg text-main text-[16px] leading-[24px]\"\n data-test=\"date-range-panel\"\n >\n <div class=\"flex flex-col justify-center border-b border-gray-300 pb-1\">\n <ng-container\n [ngTemplateOutlet]=\"boundRow\"\n [ngTemplateOutletContext]=\"{\n bound: 'start',\n label: 'daterange.filter.from',\n date: startDate,\n }\"\n ></ng-container>\n @if (expandedBound === 'start') {\n <div class=\"calendar\" animate.enter=\"expand\" animate.leave=\"collapse\">\n <mat-calendar\n data-test=\"start-date-calendar\"\n [selected]=\"startDate\"\n [startAt]=\"startDate ?? endDate\"\n [maxDate]=\"endDate\"\n (selectedChange)=\"setStartDate($event)\"\n ></mat-calendar>\n </div>\n }\n </div>\n <div class=\"flex flex-col justify-center\">\n <ng-container\n [ngTemplateOutlet]=\"boundRow\"\n [ngTemplateOutletContext]=\"{\n bound: 'end',\n label: 'daterange.filter.to',\n date: endDate,\n }\"\n ></ng-container>\n @if (expandedBound === 'end') {\n <div class=\"calendar\" animate.enter=\"expand\" animate.leave=\"collapse\">\n <mat-calendar\n data-test=\"end-date-calendar\"\n [selected]=\"endDate\"\n [startAt]=\"endDate ?? startDate\"\n [minDate]=\"startDate\"\n (selectedChange)=\"setEndDate($event)\"\n ></mat-calendar>\n </div>\n }\n </div>\n </div>\n</ng-template>\n\n<ng-template #boundRow let-bound=\"bound\" let-label=\"label\" let-date=\"date\">\n <div class=\"flex items-center gap-1 w-full\">\n <span class=\"w-[80px] shrink-0\" [translate]=\"label\"></span>\n <div\n class=\"flex-1 min-w-0 flex items-center gap-1 border rounded-lg px-2 py-1 transition-colors\"\n [class]=\"\n invalidBounds[bound]\n ? 'border-red-400 text-red-800 focus-within:border-red-800 hover:border-red-800'\n : 'border-gray-400 focus-within:border-main hover:border-main'\n \"\n >\n <ng-icon\n name=\"iconoirCalendar\"\n class=\"shrink-0\"\n aria-hidden=\"true\"\n ></ng-icon>\n <input\n type=\"text\"\n inputmode=\"numeric\"\n autocomplete=\"off\"\n [gnUiAutofocus]=\"bound === expandedBound\"\n class=\"min-w-0 grow bg-transparent focus:outline-none\"\n [value]=\"date ? formatDate(date) : ''\"\n [placeholder]=\"'daterange.filter.datePlaceholder' | translate\"\n [attr.aria-label]=\"label | translate\"\n [attr.aria-invalid]=\"invalidBounds[bound] || null\"\n [attr.data-test]=\"bound + '-date-input'\"\n (change)=\"onDateInput(bound, $event)\"\n (blur)=\"normalizeDateInput(bound, $event)\"\n (click)=\"expandedBound !== bound && toggleBound(bound)\"\n />\n </div>\n <div class=\"shrink-0 flex items-center justify-end\">\n <gn-ui-button\n type=\"light\"\n [style.--gn-ui-button-padding]=\"0\"\n [style.--gn-ui-button-width]=\"'24px'\"\n [style.--gn-ui-button-height]=\"'24px'\"\n [attr.aria-expanded]=\"expandedBound === bound\"\n [attr.data-test]=\"bound + '-date-toggle'\"\n (buttonClick)=\"toggleBound(bound)\"\n >\n <ng-icon\n [name]=\"expandedBound === bound ? 'matExpandLess' : 'matExpandMore'\"\n ></ng-icon>\n </gn-ui-button>\n </div>\n </div>\n</ng-template>\n", styles: ["mat-calendar{width:100%}.calendar{display:grid;grid-template-rows:1fr}.calendar>mat-calendar{min-height:0;overflow:hidden}.calendar.expand{animation:open .2s ease-out}.calendar.collapse{animation:open .2s ease-in reverse}@keyframes open{0%{grid-template-rows:0fr;opacity:0}to{grid-template-rows:1fr;opacity:1}}@media (prefers-reduced-motion: reduce){.calendar.expand,.calendar.collapse{animation-duration:1ms}}\n"], dependencies: [{ kind: "directive", type: AutofocusDirective, selector: "[gnUiAutofocus]", inputs: ["gnUiAutofocus"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i1$6.MatCalendar, selector: "mat-calendar", inputs: ["headerComponent", "startAt", "startView", "selected", "minDate", "maxDate", "dateFilter", "dateClass", "comparisonStart", "comparisonEnd", "startDateAccessibleName", "endDateAccessibleName"], outputs: ["selectedChange", "yearSelected", "monthSelected", "viewChanged", "_userSelection", "_userDragDrop"], exportAs: ["matCalendar"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
24619
24789
|
}
|
|
24620
24790
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangeDropdownComponent, decorators: [{
|
|
24621
24791
|
type: Component,
|
|
24622
24792
|
args: [{ selector: 'gn-ui-date-range-dropdown', standalone: true, imports: [
|
|
24623
|
-
|
|
24624
|
-
MatNativeDateModule,
|
|
24625
|
-
MatDatepickerModule,
|
|
24793
|
+
AutofocusDirective,
|
|
24626
24794
|
ButtonComponent,
|
|
24795
|
+
MatDatepickerModule,
|
|
24796
|
+
NgIcon,
|
|
24797
|
+
NgTemplateOutlet,
|
|
24798
|
+
OverlayModule,
|
|
24799
|
+
TranslateDirective,
|
|
24800
|
+
TranslatePipe,
|
|
24627
24801
|
], providers: [
|
|
24628
24802
|
provideIcons({
|
|
24629
|
-
|
|
24803
|
+
iconoirCalendar,
|
|
24804
|
+
matClose,
|
|
24630
24805
|
matExpandLess,
|
|
24806
|
+
matExpandMore,
|
|
24631
24807
|
}),
|
|
24632
|
-
|
|
24633
|
-
|
|
24634
|
-
|
|
24635
|
-
|
|
24808
|
+
provideNgIconsConfig({
|
|
24809
|
+
size: '1.5rem',
|
|
24810
|
+
}),
|
|
24811
|
+
provideLocalizedDateAdapter(),
|
|
24812
|
+
], 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"] }]
|
|
24813
|
+
}], ctorParameters: () => [], propDecorators: { title: [{
|
|
24636
24814
|
type: Input
|
|
24637
|
-
}],
|
|
24815
|
+
}], dateRange: [{
|
|
24638
24816
|
type: Input
|
|
24639
|
-
}],
|
|
24817
|
+
}], dateRangeChange: [{
|
|
24640
24818
|
type: Output
|
|
24641
|
-
}],
|
|
24642
|
-
type: Output
|
|
24643
|
-
}], picker: [{
|
|
24819
|
+
}], overlayOrigin: [{
|
|
24644
24820
|
type: ViewChild,
|
|
24645
|
-
args: ['
|
|
24821
|
+
args: ['overlayOrigin']
|
|
24646
24822
|
}] } });
|
|
24647
24823
|
|
|
24648
24824
|
class DateRangePickerComponent {
|
|
@@ -24656,20 +24832,17 @@ class DateRangePickerComponent {
|
|
|
24656
24832
|
provideNgIconsConfig({
|
|
24657
24833
|
size: '1.5rem',
|
|
24658
24834
|
}),
|
|
24659
|
-
|
|
24835
|
+
provideLocalizedDateAdapter(),
|
|
24836
|
+
], ngImport: i0, template: "<span class=\"w-full inline-block relative\">\n <mat-date-range-input [rangePicker]=\"picker\" class=\"gn-ui-text-input\">\n <input\n matStartDate\n placeholder=\"Start date\"\n [value]=\"startDate\"\n (dateInput)=\"startDateChange.emit($event.value)\"\n />\n <input\n matEndDate\n placeholder=\"End date\"\n [value]=\"endDate\"\n (dateInput)=\"endDateChange.emit($event.value)\"\n />\n </mat-date-range-input>\n\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n extraClass=\"h-full\"\n data-cy=\"date-picker-button\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-date-range-picker #picker></mat-date-range-picker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"], dependencies: [{ kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i1$6.MatDateRangeInput, selector: "mat-date-range-input", inputs: ["rangePicker", "required", "dateFilter", "min", "max", "disabled", "separator", "comparisonStart", "comparisonEnd"], exportAs: ["matDateRangeInput"] }, { kind: "directive", type: i1$6.MatStartDate, selector: "input[matStartDate]", outputs: ["dateChange", "dateInput"] }, { kind: "directive", type: i1$6.MatEndDate, selector: "input[matEndDate]", outputs: ["dateChange", "dateInput"] }, { kind: "component", type: i1$6.MatDateRangePicker, selector: "mat-date-range-picker", exportAs: ["matDateRangePicker"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
24660
24837
|
}
|
|
24661
24838
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DateRangePickerComponent, decorators: [{
|
|
24662
24839
|
type: Component,
|
|
24663
|
-
args: [{ selector: 'gn-ui-date-range-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
24664
|
-
MatNativeDateModule,
|
|
24665
|
-
MatDatepickerModule,
|
|
24666
|
-
ButtonComponent,
|
|
24667
|
-
NgIconComponent,
|
|
24668
|
-
], providers: [
|
|
24840
|
+
args: [{ selector: 'gn-ui-date-range-picker', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [MatDatepickerModule, ButtonComponent, NgIconComponent], providers: [
|
|
24669
24841
|
provideIcons({ iconoirCalendar }),
|
|
24670
24842
|
provideNgIconsConfig({
|
|
24671
24843
|
size: '1.5rem',
|
|
24672
24844
|
}),
|
|
24845
|
+
provideLocalizedDateAdapter(),
|
|
24673
24846
|
], template: "<span class=\"w-full inline-block relative\">\n <mat-date-range-input [rangePicker]=\"picker\" class=\"gn-ui-text-input\">\n <input\n matStartDate\n placeholder=\"Start date\"\n [value]=\"startDate\"\n (dateInput)=\"startDateChange.emit($event.value)\"\n />\n <input\n matEndDate\n placeholder=\"End date\"\n [value]=\"endDate\"\n (dateInput)=\"endDateChange.emit($event.value)\"\n />\n </mat-date-range-input>\n\n <gn-ui-button\n type=\"light\"\n class=\"absolute inset-y-[var(--side-padding)] right-[var(--side-padding)] z-10\"\n (buttonClick)=\"picker.open()\"\n extraClass=\"h-full\"\n data-cy=\"date-picker-button\"\n >\n <ng-icon name=\"iconoirCalendar\" class=\"text-primary\"></ng-icon>\n </gn-ui-button>\n</span>\n<mat-date-range-picker #picker></mat-date-range-picker>\n", styles: [":host{--gn-ui-button-rounded: 8px;--gn-ui-button-width: 32px;--gn-ui-button-padding: 0;--side-padding: calc(var(--gn-ui-text-input-padding, .6em) - 6px);--text-padding: calc(var(--side-padding) + 40px)}\n"] }]
|
|
24674
24847
|
}], propDecorators: { startDate: [{
|
|
24675
24848
|
type: Input
|
|
@@ -24687,28 +24860,68 @@ class DragAndDropFileInputComponent {
|
|
|
24687
24860
|
constructor() {
|
|
24688
24861
|
this.placeholder = placeholder;
|
|
24689
24862
|
this.accept = '*';
|
|
24863
|
+
this.maxFileSizeMb = null;
|
|
24864
|
+
this.icon = null;
|
|
24865
|
+
this.dropzoneBackgroundColor = null;
|
|
24866
|
+
this.textClass = '';
|
|
24867
|
+
this.extraClass = '';
|
|
24868
|
+
this.showFileName = true;
|
|
24690
24869
|
this.fileChange = new EventEmitter();
|
|
24870
|
+
this.errorChange = new EventEmitter();
|
|
24691
24871
|
this.selectedFile = null;
|
|
24692
24872
|
}
|
|
24693
24873
|
get fileName() {
|
|
24694
24874
|
return this.selectedFile && this.selectedFile.name;
|
|
24695
24875
|
}
|
|
24876
|
+
get maxFileSizeBytes() {
|
|
24877
|
+
return typeof this.maxFileSizeMb === 'number'
|
|
24878
|
+
? megabytesToBytes(this.maxFileSizeMb)
|
|
24879
|
+
: null;
|
|
24880
|
+
}
|
|
24696
24881
|
selectFile(event) {
|
|
24882
|
+
if (event.rejectedFiles?.length) {
|
|
24883
|
+
const reason = event.rejectedFiles[0].reason;
|
|
24884
|
+
this.errorChange.emit(reason === 'size' ? 'file-too-large' : 'invalid-extension');
|
|
24885
|
+
return;
|
|
24886
|
+
}
|
|
24697
24887
|
this.selectedFile = event.addedFiles[0];
|
|
24698
24888
|
this.fileChange.emit(this.selectedFile);
|
|
24699
24889
|
}
|
|
24890
|
+
openFileSelector() {
|
|
24891
|
+
this.dropzone.showFileSelector();
|
|
24892
|
+
}
|
|
24893
|
+
clear() {
|
|
24894
|
+
this.selectedFile = null;
|
|
24895
|
+
}
|
|
24700
24896
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DragAndDropFileInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
24701
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DragAndDropFileInputComponent, isStandalone: true, selector: "gn-ui-drag-and-drop-file-input", inputs: { placeholder: "placeholder", accept: "accept" }, outputs: { fileChange: "fileChange" }, ngImport: i0, template: "<div
|
|
24897
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DragAndDropFileInputComponent, isStandalone: true, selector: "gn-ui-drag-and-drop-file-input", inputs: { placeholder: "placeholder", accept: "accept", maxFileSizeMb: "maxFileSizeMb", icon: "icon", dropzoneBackgroundColor: "dropzoneBackgroundColor", textClass: "textClass", extraClass: "extraClass", showFileName: "showFileName" }, outputs: { fileChange: "fileChange", errorChange: "errorChange" }, viewQueries: [{ propertyName: "dropzone", first: true, predicate: NgxDropzoneComponent, descendants: true }], ngImport: i0, template: "<div\n class=\"flex h-full items-center\"\n [class]=\"extraClass\"\n (click)=\"openFileSelector()\"\n>\n <ngx-dropzone\n class=\"flex-1 overflow-hidden\"\n [multiple]=\"false\"\n [disableClick]=\"true\"\n [accept]=\"accept\"\n [maxFileSize]=\"maxFileSizeBytes\"\n [style.background-color]=\"dropzoneBackgroundColor\"\n (change)=\"selectFile($event)\"\n >\n @if (!fileName || !showFileName) {\n <div [class]=\"textClass\">\n {{ placeholder }}\n </div>\n }\n\n @if (fileName && showFileName) {\n <div [class]=\"textClass\">\n {{ fileName }}\n </div>\n }\n </ngx-dropzone>\n\n @if (icon) {\n <ng-icon [name]=\"icon\" class=\"shrink-0 text-gray-950\"></ng-icon>\n }\n</div>\n", styles: ["ngx-dropzone{height:auto;border:none;overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: NgxDropzoneModule }, { kind: "component", type: i1$8.NgxDropzoneComponent, selector: "ngx-dropzone, [ngx-dropzone]", inputs: ["accept", "disabled", "multiple", "maxFileSize", "expandable", "disableClick", "processDirectoryDrop", "id", "aria-label", "aria-labelledby", "aria-describedby"], outputs: ["change"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }] }); }
|
|
24702
24898
|
}
|
|
24703
24899
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DragAndDropFileInputComponent, decorators: [{
|
|
24704
24900
|
type: Component,
|
|
24705
|
-
args: [{ selector: 'gn-ui-drag-and-drop-file-input', standalone: true, imports: [NgxDropzoneModule], template: "<div
|
|
24901
|
+
args: [{ selector: 'gn-ui-drag-and-drop-file-input', standalone: true, imports: [NgxDropzoneModule, NgIcon], template: "<div\n class=\"flex h-full items-center\"\n [class]=\"extraClass\"\n (click)=\"openFileSelector()\"\n>\n <ngx-dropzone\n class=\"flex-1 overflow-hidden\"\n [multiple]=\"false\"\n [disableClick]=\"true\"\n [accept]=\"accept\"\n [maxFileSize]=\"maxFileSizeBytes\"\n [style.background-color]=\"dropzoneBackgroundColor\"\n (change)=\"selectFile($event)\"\n >\n @if (!fileName || !showFileName) {\n <div [class]=\"textClass\">\n {{ placeholder }}\n </div>\n }\n\n @if (fileName && showFileName) {\n <div [class]=\"textClass\">\n {{ fileName }}\n </div>\n }\n </ngx-dropzone>\n\n @if (icon) {\n <ng-icon [name]=\"icon\" class=\"shrink-0 text-gray-950\"></ng-icon>\n }\n</div>\n", styles: ["ngx-dropzone{height:auto;border:none;overflow:hidden}\n"] }]
|
|
24706
24902
|
}], propDecorators: { placeholder: [{
|
|
24707
24903
|
type: Input
|
|
24708
24904
|
}], accept: [{
|
|
24709
24905
|
type: Input
|
|
24906
|
+
}], maxFileSizeMb: [{
|
|
24907
|
+
type: Input
|
|
24908
|
+
}], icon: [{
|
|
24909
|
+
type: Input
|
|
24910
|
+
}], dropzoneBackgroundColor: [{
|
|
24911
|
+
type: Input
|
|
24912
|
+
}], textClass: [{
|
|
24913
|
+
type: Input
|
|
24914
|
+
}], extraClass: [{
|
|
24915
|
+
type: Input
|
|
24916
|
+
}], showFileName: [{
|
|
24917
|
+
type: Input
|
|
24710
24918
|
}], fileChange: [{
|
|
24711
24919
|
type: Output
|
|
24920
|
+
}], errorChange: [{
|
|
24921
|
+
type: Output
|
|
24922
|
+
}], dropzone: [{
|
|
24923
|
+
type: ViewChild,
|
|
24924
|
+
args: [NgxDropzoneComponent]
|
|
24712
24925
|
}] } });
|
|
24713
24926
|
|
|
24714
24927
|
class DropdownMultiselectComponent {
|
|
@@ -24863,7 +25076,7 @@ class DropdownMultiselectComponent {
|
|
|
24863
25076
|
matExpandMore,
|
|
24864
25077
|
matExpandLess,
|
|
24865
25078
|
}),
|
|
24866
|
-
], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "overlayContainer", first: true, predicate: ["overlayContainer"], descendants: true, read: ElementRef }, { propertyName: "searchFieldInput", first: true, predicate: ["searchFieldInput"], descendants: true }, { propertyName: "checkboxes", predicate: ["checkBox"], descendants: true, read: ElementRef }], ngImport: i0, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelectedChoices) {\n <div\n class=\"gn-ui-multiselect-counter shrink-0 rounded-full font-bold text-[12px] w-5 h-5 flex items-center justify-center mr-1 selected-count\"\n >\n {{ selected.length }}\n </div>\n }\n </div>\n <button class=\"h-6 w-6\" data-
|
|
25079
|
+
], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "overlayContainer", first: true, predicate: ["overlayContainer"], descendants: true, read: ElementRef }, { propertyName: "searchFieldInput", first: true, predicate: ["searchFieldInput"], descendants: true }, { propertyName: "checkboxes", predicate: ["checkBox"], descendants: true, read: ElementRef }], ngImport: i0, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelectedChoices) {\n <div\n class=\"gn-ui-multiselect-counter shrink-0 rounded-full font-bold text-[12px] w-5 h-5 flex items-center justify-center mr-1 selected-count\"\n >\n {{ selected.length }}\n </div>\n }\n </div>\n <button class=\"h-6 w-6\" data-test=\"dropdown-clear\">\n @if (hasSelectedChoices && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"clearSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container\"\n [style.max-height]=\"overlayMaxHeight\"\n [style.min-width]=\"overlayMinWidth\"\n role=\"listbox\"\n tabindex=\"-1\"\n [attr.id]=\"id\"\n [attr.aria-multiselectable]=\"true\"\n [attr.aria-label]=\"title\"\n (keydown)=\"handleOverlayKeydown($event)\"\n #overlayContainer\n >\n <div\n class=\"border border-gray-300 rounded mb-2 mx-2 min-h-[44px] flex flex-row gap-[2px] flex-wrap p-2 focus-within:rounded focus-within:border-2 focus-within:border-primary\"\n >\n @for (selected of selectedChoices; track selected) {\n <button\n type=\"button\"\n [title]=\"selected.label\"\n class=\"max-w-full bg-main text-white rounded pr-[7px] flex gap-1 items-center opacity-70 hover:opacity-100 focus:opacity-100 transition-opacity mb-1\"\n (click)=\"select(selected, false)\"\n >\n <div class=\"text-sm truncate leading-[26px] px-2\">\n {{ selected.label }}\n </div>\n <div\n class=\"flex items-center justify-center rounded-full bg-white text-main h-[13px] w-[13px] pt-px -mt-px shrink-0\"\n >\n <ng-icon\n name=\"matClose\"\n class=\"!h-[12px] !w-[11px] text-[12px]\"\n ></ng-icon>\n </div>\n </button>\n }\n\n @if (allowSearch) {\n <div class=\"w-[50%] relative grow shrink\">\n <input\n #searchFieldInput\n class=\"w-full px-2 truncate text-[14px] h-full overlaySearchInput focus:outline-none\"\n [(ngModel)]=\"searchInputValue\"\n [placeholder]=\"'multiselect.filter.placeholder' | translate\"\n />\n @if (!!searchInputValue) {\n <button\n class=\"absolute top-1/2 -translate-y-1/2 right-0 px-[7px] leading-tight clear-search-input mr-2\"\n (click)=\"clearSearchInputValue($event)\"\n >\n <ng-icon class=\"!h-[10px] !w-[12px] text-[12px]\" name=\"matClose\">\n </ng-icon>\n </button>\n }\n </div>\n }\n </div>\n\n @for (choice of filteredChoicesByText; track choice) {\n <label\n [title]=\"choice.label\"\n class=\"flex px-5 py-1 w-full text-gray-900 cursor-pointer hover:text-primary-darkest hover:bg-gray-50 focus-within:text-primary-darkest focus-within:bg-gray-50 transition-colors\"\n >\n <input\n class=\"w-[18px] h-[18px] align-text-top shrink-0\"\n type=\"checkbox\"\n #checkBox\n [checked]=\"isSelected(choice)\"\n (change)=\"select(choice, checkBox.checked)\"\n />\n <span class=\"ml-[8px] text-[14px] truncate\">\n {{ choice.label }}\n </span>\n </label>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
24867
25080
|
}
|
|
24868
25081
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DropdownMultiselectComponent, decorators: [{
|
|
24869
25082
|
type: Component,
|
|
@@ -24873,7 +25086,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
24873
25086
|
matExpandMore,
|
|
24874
25087
|
matExpandLess,
|
|
24875
25088
|
}),
|
|
24876
|
-
], standalone: true, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelectedChoices) {\n <div\n class=\"gn-ui-multiselect-counter shrink-0 rounded-full font-bold text-[12px] w-5 h-5 flex items-center justify-center mr-1 selected-count\"\n >\n {{ selected.length }}\n </div>\n }\n </div>\n <button class=\"h-6 w-6\" data-
|
|
25089
|
+
], standalone: true, template: "<gn-ui-button\n type=\"outline\"\n extraClass=\"bg-background w-full !p-[8px] !pl-[16px]\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n (keydown)=\"handleTriggerKeydown($event)\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <div class=\"grow flex items-center mr-2 gap-2 overflow-hidden\">\n <div class=\"text-left font-medium truncate py-1\">\n {{ title }}\n </div>\n @if (hasSelectedChoices) {\n <div\n class=\"gn-ui-multiselect-counter shrink-0 rounded-full font-bold text-[12px] w-5 h-5 flex items-center justify-center mr-1 selected-count\"\n >\n {{ selected.length }}\n </div>\n }\n </div>\n <button class=\"h-6 w-6\" data-test=\"dropdown-clear\">\n @if (hasSelectedChoices && !overlayOpen) {\n <ng-icon\n class=\"shrink-0 opacity-40 mr-1.5 hover:opacity-80 transition-colors clear-btn\"\n (click)=\"clearSelection($event)\"\n name=\"matClose\"\n ></ng-icon>\n }\n </button>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container\"\n [style.max-height]=\"overlayMaxHeight\"\n [style.min-width]=\"overlayMinWidth\"\n role=\"listbox\"\n tabindex=\"-1\"\n [attr.id]=\"id\"\n [attr.aria-multiselectable]=\"true\"\n [attr.aria-label]=\"title\"\n (keydown)=\"handleOverlayKeydown($event)\"\n #overlayContainer\n >\n <div\n class=\"border border-gray-300 rounded mb-2 mx-2 min-h-[44px] flex flex-row gap-[2px] flex-wrap p-2 focus-within:rounded focus-within:border-2 focus-within:border-primary\"\n >\n @for (selected of selectedChoices; track selected) {\n <button\n type=\"button\"\n [title]=\"selected.label\"\n class=\"max-w-full bg-main text-white rounded pr-[7px] flex gap-1 items-center opacity-70 hover:opacity-100 focus:opacity-100 transition-opacity mb-1\"\n (click)=\"select(selected, false)\"\n >\n <div class=\"text-sm truncate leading-[26px] px-2\">\n {{ selected.label }}\n </div>\n <div\n class=\"flex items-center justify-center rounded-full bg-white text-main h-[13px] w-[13px] pt-px -mt-px shrink-0\"\n >\n <ng-icon\n name=\"matClose\"\n class=\"!h-[12px] !w-[11px] text-[12px]\"\n ></ng-icon>\n </div>\n </button>\n }\n\n @if (allowSearch) {\n <div class=\"w-[50%] relative grow shrink\">\n <input\n #searchFieldInput\n class=\"w-full px-2 truncate text-[14px] h-full overlaySearchInput focus:outline-none\"\n [(ngModel)]=\"searchInputValue\"\n [placeholder]=\"'multiselect.filter.placeholder' | translate\"\n />\n @if (!!searchInputValue) {\n <button\n class=\"absolute top-1/2 -translate-y-1/2 right-0 px-[7px] leading-tight clear-search-input mr-2\"\n (click)=\"clearSearchInputValue($event)\"\n >\n <ng-icon class=\"!h-[10px] !w-[12px] text-[12px]\" name=\"matClose\">\n </ng-icon>\n </button>\n }\n </div>\n }\n </div>\n\n @for (choice of filteredChoicesByText; track choice) {\n <label\n [title]=\"choice.label\"\n class=\"flex px-5 py-1 w-full text-gray-900 cursor-pointer hover:text-primary-darkest hover:bg-gray-50 focus-within:text-primary-darkest focus-within:bg-gray-50 transition-colors\"\n >\n <input\n class=\"w-[18px] h-[18px] align-text-top shrink-0\"\n type=\"checkbox\"\n #checkBox\n [checked]=\"isSelected(choice)\"\n (change)=\"select(choice, checkBox.checked)\"\n />\n <span class=\"ml-[8px] text-[14px] truncate\">\n {{ choice.label }}\n </span>\n </label>\n }\n </div>\n</ng-template>\n" }]
|
|
24877
25090
|
}], propDecorators: { title: [{
|
|
24878
25091
|
type: Input
|
|
24879
25092
|
}], choices: [{
|
|
@@ -25045,7 +25258,7 @@ class DropdownSelectorComponent {
|
|
|
25045
25258
|
provideNgIconsConfig({
|
|
25046
25259
|
size: '1.5em',
|
|
25047
25260
|
}),
|
|
25048
|
-
], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "choiceInputs", predicate: ["choiceInputs"], descendants: true, read: ElementRef }], ngImport: i0, template: "<div class=\"flex flex-col sm:flex-row sm:items-center relative w-full\">\n @if (showTitle) {\n <span\n class=\"tracking-wide text-sm mb-2 sm:mb-0 sm:mr-2 whitespace-nowrap\"\n [attr.for]=\"id\"\n >\n {{ title }}\n </span>\n }\n <gn-ui-button\n type=\"outline\"\n class=\"grow min-w-0\"\n [style.--gn-ui-button-padding]=\"'8px 8px 8px 16px'\"\n extraClass=\"bg-background flex flex-row w-full {{ extraBtnClass }}\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n (keydown)=\"handleTriggerKeydown($event)\"\n [disabled]=\"disabled\"\n >\n <div class=\"grow truncate py-1 mr-2 text-left\">\n {{ getChoiceLabel() | translate }}\n </div>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n </gn-ui-button>\n</div>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container\"\n [style.max-height]=\"overlayMaxHeight\"\n [style.min-width]=\"overlayWidth\"\n role=\"listbox\"\n tabindex=\"-1\"\n [attr.id]=\"id\"\n [attr.aria-multiselectable]=\"true\"\n [attr.aria-label]=\"title\"\n (keydown)=\"handleOverlayKeydown($event)\"\n >\n @for (choice of choices; track choice) {\n <button\n #choiceInputs\n type=\"button\"\n [title]=\"choice.label | translate\"\n class=\"flex px-5 py-1 w-full text-start cursor-pointer transition-colors\"\n [ngClass]=\"\n isSelected(choice)\n ? 'text-white bg-primary hover:text-white hover:bg-primary-darker focus:text-white focus:bg-primary-darker'\n : 'text-gray-900 hover:text-primary-darkest hover:bg-gray-50 focus:text-primary-darkest focus:bg-gray-50'\n \"\n (click)=\"onSelectValue(choice)\"\n (keydown)=\"selectIfEnter($event, choice)\"\n [attr.data-cy-value]=\"choice.value.toString()\"\n [attr.data-cy-active]=\"isSelected(choice) ? 'true' : undefined\"\n >\n <span class=\"text-[14px]\">\n {{ choice.label | translate }}\n </span>\n </button>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$
|
|
25261
|
+
], viewQueries: [{ propertyName: "overlayOrigin", first: true, predicate: ["overlayOrigin"], descendants: true }, { propertyName: "overlay", first: true, predicate: CdkConnectedOverlay, descendants: true }, { propertyName: "choiceInputs", predicate: ["choiceInputs"], descendants: true, read: ElementRef }], ngImport: i0, template: "<div class=\"flex flex-col sm:flex-row sm:items-center relative w-full\">\n @if (showTitle) {\n <span\n class=\"tracking-wide text-sm mb-2 sm:mb-0 sm:mr-2 whitespace-nowrap\"\n [attr.for]=\"id\"\n >\n {{ title }}\n </span>\n }\n <gn-ui-button\n type=\"outline\"\n class=\"grow min-w-0\"\n [style.--gn-ui-button-padding]=\"'8px 8px 8px 16px'\"\n extraClass=\"bg-background flex flex-row w-full {{ extraBtnClass }}\"\n [title]=\"title\"\n [attr.aria-owns]=\"id\"\n (buttonClick)=\"openOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n (keydown)=\"handleTriggerKeydown($event)\"\n [disabled]=\"disabled\"\n >\n <div class=\"grow truncate py-1 mr-2 text-left\">\n {{ getChoiceLabel() | translate }}\n </div>\n <ng-icon\n [name]=\"overlayOpen ? 'matExpandLess' : 'matExpandMore'\"\n class=\"shrink-0 opacity-40\"\n >\n </ng-icon>\n </gn-ui-button>\n</div>\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div\n class=\"bg-white border border-gray-300 rounded shadow-lg py-2 w-full overflow-x-hidden overflow-y-auto overlay-container\"\n [style.max-height]=\"overlayMaxHeight\"\n [style.min-width]=\"overlayWidth\"\n role=\"listbox\"\n tabindex=\"-1\"\n [attr.id]=\"id\"\n [attr.aria-multiselectable]=\"true\"\n [attr.aria-label]=\"title\"\n (keydown)=\"handleOverlayKeydown($event)\"\n >\n @for (choice of choices; track choice) {\n <button\n #choiceInputs\n type=\"button\"\n [title]=\"choice.label | translate\"\n class=\"flex px-5 py-1 w-full text-start cursor-pointer transition-colors\"\n [ngClass]=\"\n isSelected(choice)\n ? 'text-white bg-primary hover:text-white hover:bg-primary-darker focus:text-white focus:bg-primary-darker'\n : 'text-gray-900 hover:text-primary-darkest hover:bg-gray-50 focus:text-primary-darkest focus:bg-gray-50'\n \"\n (click)=\"onSelectValue(choice)\"\n (keydown)=\"selectIfEnter($event, choice)\"\n [attr.data-cy-value]=\"choice.value.toString()\"\n [attr.data-cy-active]=\"isSelected(choice) ? 'true' : undefined\"\n >\n <span class=\"text-[14px]\">\n {{ choice.label | translate }}\n </span>\n </button>\n }\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
25049
25262
|
}
|
|
25050
25263
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DropdownSelectorComponent, decorators: [{
|
|
25051
25264
|
type: Component,
|
|
@@ -25785,6 +25998,157 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
25785
25998
|
type: Output
|
|
25786
25999
|
}] } });
|
|
25787
26000
|
|
|
26001
|
+
marker('search.filters.spatialExtent.import');
|
|
26002
|
+
marker('search.filters.spatialExtent.helpText');
|
|
26003
|
+
marker('search.filters.spatialExtent.error.title');
|
|
26004
|
+
marker('search.filters.spatialExtent.bboxPrefix');
|
|
26005
|
+
marker('search.filters.spatialExtent.bboxDelete');
|
|
26006
|
+
class SpatialExtentDropdownComponent {
|
|
26007
|
+
constructor() {
|
|
26008
|
+
this.cd = inject(ChangeDetectorRef);
|
|
26009
|
+
this.scrollStrategies = inject(ScrollStrategyOptions);
|
|
26010
|
+
this.maxFileSizeMb = null;
|
|
26011
|
+
this.bboxChange = new EventEmitter();
|
|
26012
|
+
this.errorChange = new EventEmitter();
|
|
26013
|
+
this.bbox = null;
|
|
26014
|
+
this.fileName = '';
|
|
26015
|
+
this.overlayPositions = [
|
|
26016
|
+
{
|
|
26017
|
+
originX: 'start',
|
|
26018
|
+
originY: 'bottom',
|
|
26019
|
+
overlayX: 'start',
|
|
26020
|
+
overlayY: 'top',
|
|
26021
|
+
offsetY: 8,
|
|
26022
|
+
},
|
|
26023
|
+
{
|
|
26024
|
+
originX: 'start',
|
|
26025
|
+
originY: 'top',
|
|
26026
|
+
overlayX: 'start',
|
|
26027
|
+
overlayY: 'bottom',
|
|
26028
|
+
offsetY: -8,
|
|
26029
|
+
},
|
|
26030
|
+
];
|
|
26031
|
+
this.scrollStrategy = this.scrollStrategies.reposition();
|
|
26032
|
+
this.overlayOpen = false;
|
|
26033
|
+
this.overlayMinWidth = 'none';
|
|
26034
|
+
this.errorKey = null;
|
|
26035
|
+
}
|
|
26036
|
+
get hasSelection() {
|
|
26037
|
+
return !!this.bbox;
|
|
26038
|
+
}
|
|
26039
|
+
openOverlay() {
|
|
26040
|
+
this.overlayMinWidth =
|
|
26041
|
+
this.overlayOrigin.elementRef.nativeElement.getBoundingClientRect()
|
|
26042
|
+
.width + 'px';
|
|
26043
|
+
this.overlayOpen = true;
|
|
26044
|
+
}
|
|
26045
|
+
closeOverlay() {
|
|
26046
|
+
this.overlayOpen = false;
|
|
26047
|
+
}
|
|
26048
|
+
toggleOverlay() {
|
|
26049
|
+
if (this.overlayOpen) {
|
|
26050
|
+
this.closeOverlay();
|
|
26051
|
+
}
|
|
26052
|
+
else {
|
|
26053
|
+
this.openOverlay();
|
|
26054
|
+
}
|
|
26055
|
+
}
|
|
26056
|
+
async handleFileSelected(file) {
|
|
26057
|
+
this.errorKey = null;
|
|
26058
|
+
let content;
|
|
26059
|
+
try {
|
|
26060
|
+
content = await readFileAsText(file);
|
|
26061
|
+
const parsed = JSON.parse(content);
|
|
26062
|
+
const geometry = getGeometryFromGeoJSON(parsed);
|
|
26063
|
+
if (!geometry) {
|
|
26064
|
+
this.setError(marker('search.filters.spatialExtent.error.noGeometry'));
|
|
26065
|
+
return;
|
|
26066
|
+
}
|
|
26067
|
+
const bbox = getGeometryBoundingBox(geometry);
|
|
26068
|
+
this.bbox = bbox;
|
|
26069
|
+
this.fileName = file.name;
|
|
26070
|
+
this.bboxChange.emit(bbox);
|
|
26071
|
+
this.cd.markForCheck();
|
|
26072
|
+
}
|
|
26073
|
+
catch {
|
|
26074
|
+
this.setError(marker('search.filters.spatialExtent.error.invalidFormat'));
|
|
26075
|
+
return;
|
|
26076
|
+
}
|
|
26077
|
+
}
|
|
26078
|
+
handleFileError(error) {
|
|
26079
|
+
if (error === 'file-too-large') {
|
|
26080
|
+
this.setError(marker('search.filters.spatialExtent.error.fileTooLarge'), {
|
|
26081
|
+
maxSize: this.maxFileSizeMb,
|
|
26082
|
+
});
|
|
26083
|
+
}
|
|
26084
|
+
else {
|
|
26085
|
+
this.setError(marker('search.filters.spatialExtent.error.invalidFormat'));
|
|
26086
|
+
}
|
|
26087
|
+
}
|
|
26088
|
+
setError(errorKey, params) {
|
|
26089
|
+
this.errorKey = errorKey;
|
|
26090
|
+
this.errorChange.emit({ key: errorKey, params });
|
|
26091
|
+
this.cd.markForCheck();
|
|
26092
|
+
}
|
|
26093
|
+
removeSelection(event) {
|
|
26094
|
+
this.bbox = null;
|
|
26095
|
+
this.fileName = '';
|
|
26096
|
+
this.errorKey = null;
|
|
26097
|
+
this.fileInput?.clear();
|
|
26098
|
+
this.bboxChange.emit(null);
|
|
26099
|
+
propagateToDocumentOnly(event);
|
|
26100
|
+
}
|
|
26101
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
26102
|
+
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: [
|
|
26103
|
+
provideIcons({
|
|
26104
|
+
iconoirCheckCircle,
|
|
26105
|
+
iconoirImport,
|
|
26106
|
+
iconoirSquareDashed,
|
|
26107
|
+
iconoirTrash,
|
|
26108
|
+
matClose,
|
|
26109
|
+
matExpandLess,
|
|
26110
|
+
matExpandMore,
|
|
26111
|
+
}),
|
|
26112
|
+
], 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 }); }
|
|
26113
|
+
}
|
|
26114
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SpatialExtentDropdownComponent, decorators: [{
|
|
26115
|
+
type: Component,
|
|
26116
|
+
args: [{ selector: 'gn-ui-spatial-extent-dropdown', standalone: true, imports: [
|
|
26117
|
+
ButtonComponent,
|
|
26118
|
+
NgIcon,
|
|
26119
|
+
OverlayModule,
|
|
26120
|
+
TranslatePipe,
|
|
26121
|
+
DragAndDropFileInputComponent,
|
|
26122
|
+
], providers: [
|
|
26123
|
+
provideIcons({
|
|
26124
|
+
iconoirCheckCircle,
|
|
26125
|
+
iconoirImport,
|
|
26126
|
+
iconoirSquareDashed,
|
|
26127
|
+
iconoirTrash,
|
|
26128
|
+
matClose,
|
|
26129
|
+
matExpandLess,
|
|
26130
|
+
matExpandMore,
|
|
26131
|
+
}),
|
|
26132
|
+
], 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" }]
|
|
26133
|
+
}], propDecorators: { title: [{
|
|
26134
|
+
type: Input
|
|
26135
|
+
}], maxFileSizeMb: [{
|
|
26136
|
+
type: Input
|
|
26137
|
+
}], bboxChange: [{
|
|
26138
|
+
type: Output
|
|
26139
|
+
}], errorChange: [{
|
|
26140
|
+
type: Output
|
|
26141
|
+
}], overlayOrigin: [{
|
|
26142
|
+
type: ViewChild,
|
|
26143
|
+
args: ['overlayOrigin']
|
|
26144
|
+
}], overlay: [{
|
|
26145
|
+
type: ViewChild,
|
|
26146
|
+
args: [CdkConnectedOverlay]
|
|
26147
|
+
}], fileInput: [{
|
|
26148
|
+
type: ViewChild,
|
|
26149
|
+
args: [DragAndDropFileInputComponent]
|
|
26150
|
+
}] } });
|
|
26151
|
+
|
|
25788
26152
|
class CellPopinComponent {
|
|
25789
26153
|
constructor() {
|
|
25790
26154
|
this.scrollDispatcher = inject(ScrollDispatcher);
|
|
@@ -25861,7 +26225,7 @@ class CellPopinComponent {
|
|
|
25861
26225
|
this.isOpen = false;
|
|
25862
26226
|
}
|
|
25863
26227
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CellPopinComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
25864
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: CellPopinComponent, isStandalone: true, selector: "gn-ui-cell-popin", inputs: { extraClass: "extraClass", cdkScrollContainer: "cdkScrollContainer", scrollContainer: "scrollContainer", activePopin: "activePopin" }, providers: [provideIcons({ iconoirReduce })], viewQueries: [{ propertyName: "anchorRef", first: true, predicate: ["anchorRef"], descendants: true }], ngImport: i0, template: "<div\n class=\"h-full flex items-center justify-center\"\n cdkOverlayOrigin\n #anchorRef\n #trigger=\"cdkOverlayOrigin\"\n [class]=\"extraClass\"\n data-cy=\"cell-popin\"\n>\n <ng-content select=\"[cellContent]\"></ng-content>\n @if (activePopin) {\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayPush]=\"false\"\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayWidth]=\"'auto'\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n [cdkConnectedOverlayGrowAfterOpen]=\"true\"\n (detach)=\"closeOverlay()\"\n >\n @if (activePopin && isVisible) {\n <div\n class=\"bg-white shadow-lg border border-gray-300 relative\"\n data-cy=\"cell-popin-content\"\n >\n <ng-content select=\"[popinContent]\"></ng-content>\n <gn-ui-button\n data-cy=\"cell-popin-close\"\n [style.--gn-ui-button-background]=\"'bg-transparent'\"\n class=\"absolute top-2 right-2\"\n type=\"light\"\n (buttonClick)=\"closeOverlay()\"\n extraClass=\"w-10 h-8 px-1\"\n >\n <ng-icon name=\"iconoirReduce\" size=\"24\"></ng-icon>\n </gn-ui-button>\n </div>\n }\n </ng-template>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$
|
|
26228
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: CellPopinComponent, isStandalone: true, selector: "gn-ui-cell-popin", inputs: { extraClass: "extraClass", cdkScrollContainer: "cdkScrollContainer", scrollContainer: "scrollContainer", activePopin: "activePopin" }, providers: [provideIcons({ iconoirReduce })], viewQueries: [{ propertyName: "anchorRef", first: true, predicate: ["anchorRef"], descendants: true }], ngImport: i0, template: "<div\n class=\"h-full flex items-center justify-center\"\n cdkOverlayOrigin\n #anchorRef\n #trigger=\"cdkOverlayOrigin\"\n [class]=\"extraClass\"\n data-cy=\"cell-popin\"\n>\n <ng-content select=\"[cellContent]\"></ng-content>\n @if (activePopin) {\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayPush]=\"false\"\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayWidth]=\"'auto'\"\n [cdkConnectedOverlayFlexibleDimensions]=\"true\"\n [cdkConnectedOverlayGrowAfterOpen]=\"true\"\n (detach)=\"closeOverlay()\"\n >\n @if (activePopin && isVisible) {\n <div\n class=\"bg-white shadow-lg border border-gray-300 relative\"\n data-cy=\"cell-popin-content\"\n >\n <ng-content select=\"[popinContent]\"></ng-content>\n <gn-ui-button\n data-cy=\"cell-popin-close\"\n [style.--gn-ui-button-background]=\"'bg-transparent'\"\n class=\"absolute top-2 right-2\"\n type=\"light\"\n (buttonClick)=\"closeOverlay()\"\n extraClass=\"w-10 h-8 px-1\"\n >\n <ng-icon name=\"iconoirReduce\" size=\"24\"></ng-icon>\n </gn-ui-button>\n </div>\n }\n </ng-template>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "component", type: NgIconComponent, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }] }); }
|
|
25865
26229
|
}
|
|
25866
26230
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CellPopinComponent, decorators: [{
|
|
25867
26231
|
type: Component,
|
|
@@ -27951,6 +28315,18 @@ class DateRangeSearchField extends SimpleSearchField {
|
|
|
27951
28315
|
return 'dateRange';
|
|
27952
28316
|
}
|
|
27953
28317
|
}
|
|
28318
|
+
class SpatialExtentSearchField extends SimpleSearchField {
|
|
28319
|
+
constructor(injector) {
|
|
28320
|
+
super('spatialExtent', injector, 'asc');
|
|
28321
|
+
}
|
|
28322
|
+
getAvailableValues() {
|
|
28323
|
+
// TODO: return an array of spatial extents to show which ones are available in the dropdown
|
|
28324
|
+
return of([]);
|
|
28325
|
+
}
|
|
28326
|
+
getType() {
|
|
28327
|
+
return 'spatialExtent';
|
|
28328
|
+
}
|
|
28329
|
+
}
|
|
27954
28330
|
marker('search.filters.availableServices.view');
|
|
27955
28331
|
marker('search.filters.availableServices.download');
|
|
27956
28332
|
class AvailableServicesField extends SimpleSearchField {
|
|
@@ -28095,6 +28471,7 @@ marker('search.filters.producerOrg');
|
|
|
28095
28471
|
marker('search.filters.publisherOrg');
|
|
28096
28472
|
marker('search.filters.user');
|
|
28097
28473
|
marker('search.filters.changeDate');
|
|
28474
|
+
marker('search.filters.spatialExtent');
|
|
28098
28475
|
class FieldsService {
|
|
28099
28476
|
constructor() {
|
|
28100
28477
|
this.injector = inject(Injector);
|
|
@@ -28118,6 +28495,7 @@ class FieldsService {
|
|
|
28118
28495
|
user: new UserSearchField(this.injector),
|
|
28119
28496
|
changeDate: new DateRangeSearchField('changeDate', this.injector, 'desc'),
|
|
28120
28497
|
availableServices: new AvailableServicesField(this.injector),
|
|
28498
|
+
spatialExtent: new SpatialExtentSearchField(this.injector),
|
|
28121
28499
|
};
|
|
28122
28500
|
}
|
|
28123
28501
|
get supportedFields() {
|
|
@@ -29380,7 +29758,7 @@ class ContactPillComponent {
|
|
|
29380
29758
|
this.overlayOpen = false;
|
|
29381
29759
|
}
|
|
29382
29760
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ContactPillComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
29383
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: ContactPillComponent, isStandalone: true, selector: "gn-ui-contact-pill", inputs: { contact: "contact" }, ngImport: i0, template: "<gn-ui-button\n [type]=\"overlayOpen ? 'gray-light' : 'primary-light'\"\n extraClass=\"group w-full min-h-12 gap-3 justify-between py-2 pl-5 pr-4 rounded\"\n data-test=\"contact-pill\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <span\n class=\"font-title font-medium text-base leading-tight truncate\"\n [class]=\"!overlayOpen ? 'text-primary-black group-hover:text-white' : ''\"\n [title]=\"displayName\"\n >{{ displayName }}</span\n >\n <div\n class=\"gn-ui-card-icon items-center justify-center w-10 h-8\"\n [class]=\"\n !overlayOpen ? 'group-hover:border-white group-hover:text-white' : ''\n \"\n >\n @if (overlayOpen) {\n <ng-icon class=\"!w-6 !h-6 !text-[24px]\" name=\"matClose\"></ng-icon>\n } @else {\n <ng-icon class=\"!w-6 !h-6 !text-[24px]\" name=\"matInfoOutline\"></ng-icon>\n }\n </div>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayOffsetX]=\"overlayOffsetX\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div [style.width.px]=\"overlayWidth\">\n <gn-ui-contact-details [contact]=\"contact\"></gn-ui-contact-details>\n </div>\n</ng-template>\n", dependencies: [{ kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$
|
|
29761
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: ContactPillComponent, isStandalone: true, selector: "gn-ui-contact-pill", inputs: { contact: "contact" }, ngImport: i0, template: "<gn-ui-button\n [type]=\"overlayOpen ? 'gray-light' : 'primary-light'\"\n extraClass=\"group w-full min-h-12 gap-3 justify-between py-2 pl-5 pr-4 rounded\"\n data-test=\"contact-pill\"\n (buttonClick)=\"toggleOverlay()\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n>\n <span\n class=\"font-title font-medium text-base leading-tight truncate\"\n [class]=\"!overlayOpen ? 'text-primary-black group-hover:text-white' : ''\"\n [title]=\"displayName\"\n >{{ displayName }}</span\n >\n <div\n class=\"gn-ui-card-icon items-center justify-center w-10 h-8\"\n [class]=\"\n !overlayOpen ? 'group-hover:border-white group-hover:text-white' : ''\n \"\n >\n @if (overlayOpen) {\n <ng-icon class=\"!w-6 !h-6 !text-[24px]\" name=\"matClose\"></ng-icon>\n } @else {\n <ng-icon class=\"!w-6 !h-6 !text-[24px]\" name=\"matInfoOutline\"></ng-icon>\n }\n </div>\n</gn-ui-button>\n\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"overlayOpen\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayOffsetX]=\"overlayOffsetX\"\n (overlayOutsideClick)=\"closeOverlay()\"\n (detach)=\"closeOverlay()\"\n>\n <div [style.width.px]=\"overlayWidth\">\n <gn-ui-contact-details [contact]=\"contact\"></gn-ui-contact-details>\n </div>\n</ng-template>\n", dependencies: [{ kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: ButtonComponent, selector: "gn-ui-button", inputs: ["type", "disabled", "extraClass"], outputs: ["buttonClick"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$7.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$7.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: ContactDetailsComponent, selector: "gn-ui-contact-details", inputs: ["contact"] }], viewProviders: [provideIcons({ matClose, matInfoOutline })], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
29384
29762
|
}
|
|
29385
29763
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ContactPillComponent, decorators: [{
|
|
29386
29764
|
type: Component,
|
|
@@ -31302,19 +31680,111 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31302
31680
|
type: Output
|
|
31303
31681
|
}] } });
|
|
31304
31682
|
|
|
31683
|
+
class NotificationsService {
|
|
31684
|
+
constructor() {
|
|
31685
|
+
this.notifications$ = new BehaviorSubject([]);
|
|
31686
|
+
}
|
|
31687
|
+
showNotification(content, timeoutMs, error) {
|
|
31688
|
+
error && console.error(error);
|
|
31689
|
+
const id = Math.floor(Math.random() * 1000000);
|
|
31690
|
+
this.notifications$.next([...this.notifications$.value, { ...content, id }]);
|
|
31691
|
+
if (typeof timeoutMs !== 'undefined') {
|
|
31692
|
+
setTimeout(() => {
|
|
31693
|
+
this.removeNotificationById(id);
|
|
31694
|
+
}, timeoutMs);
|
|
31695
|
+
}
|
|
31696
|
+
return id;
|
|
31697
|
+
}
|
|
31698
|
+
removeNotificationById(id) {
|
|
31699
|
+
this.notifications$.next(this.notifications$.value.filter((n) => n.id !== id));
|
|
31700
|
+
}
|
|
31701
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
31702
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, providedIn: 'root' }); }
|
|
31703
|
+
}
|
|
31704
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, decorators: [{
|
|
31705
|
+
type: Injectable,
|
|
31706
|
+
args: [{
|
|
31707
|
+
providedIn: 'root',
|
|
31708
|
+
}]
|
|
31709
|
+
}] });
|
|
31710
|
+
|
|
31711
|
+
class NotificationsContainerComponent {
|
|
31712
|
+
constructor() {
|
|
31713
|
+
this.notificationsService = inject(NotificationsService);
|
|
31714
|
+
}
|
|
31715
|
+
trackById(index, notification) {
|
|
31716
|
+
return notification.id;
|
|
31717
|
+
}
|
|
31718
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31719
|
+
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: [
|
|
31720
|
+
trigger('enterExit', [
|
|
31721
|
+
transition(':enter', [
|
|
31722
|
+
animate('150ms', keyframes([
|
|
31723
|
+
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31724
|
+
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31725
|
+
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31726
|
+
])),
|
|
31727
|
+
]),
|
|
31728
|
+
transition(':leave', [
|
|
31729
|
+
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31730
|
+
]),
|
|
31731
|
+
]),
|
|
31732
|
+
], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31733
|
+
}
|
|
31734
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, decorators: [{
|
|
31735
|
+
type: Component,
|
|
31736
|
+
args: [{ selector: 'gn-ui-notifications-container', standalone: true, imports: [CommonModule, NotificationComponent], changeDetection: ChangeDetectionStrategy.OnPush, animations: [
|
|
31737
|
+
trigger('enterExit', [
|
|
31738
|
+
transition(':enter', [
|
|
31739
|
+
animate('150ms', keyframes([
|
|
31740
|
+
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31741
|
+
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31742
|
+
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31743
|
+
])),
|
|
31744
|
+
]),
|
|
31745
|
+
transition(':leave', [
|
|
31746
|
+
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31747
|
+
]),
|
|
31748
|
+
]),
|
|
31749
|
+
], 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" }]
|
|
31750
|
+
}] });
|
|
31751
|
+
|
|
31305
31752
|
class FilterDropdownComponent {
|
|
31306
31753
|
constructor() {
|
|
31307
31754
|
this.searchFacade = inject(SearchFacade);
|
|
31308
31755
|
this.searchService = inject(SearchService);
|
|
31309
31756
|
this.fieldsService = inject(FieldsService);
|
|
31757
|
+
this.notificationsService = inject(NotificationsService);
|
|
31758
|
+
this.translateService = inject(TranslateService);
|
|
31759
|
+
this.spatialExtentMaxFileSize = getOptionalSearchConfig()?.SPATIAL_EXTENT_MAX_FILE_SIZE;
|
|
31310
31760
|
this.selected$ = this.searchFacade.searchFilters$.pipe(switchMap((filters) => this.fieldsService.readFieldValuesFromFilters(filters)), map$1((fieldValues) => fieldValues[this.fieldName]), filter$1((selected) => !!selected), startWith$1([]), catchError(() => of([])));
|
|
31311
|
-
this.selectedDateRange$ = this.selected$.pipe(map$1((
|
|
31761
|
+
this.selectedDateRange$ = this.selected$.pipe(map$1((selected) => (Array.isArray(selected) ? {} : selected)));
|
|
31762
|
+
this.spatialExtentErrorNotificationId = null;
|
|
31312
31763
|
}
|
|
31313
31764
|
onSelectedValues(values) {
|
|
31314
31765
|
this.fieldsService
|
|
31315
31766
|
.buildFiltersFromFieldValues({ [this.fieldName]: values })
|
|
31316
31767
|
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
31317
31768
|
}
|
|
31769
|
+
onBboxChange(bbox) {
|
|
31770
|
+
console.log(bbox);
|
|
31771
|
+
this.clearSpatialExtentErrorNotification();
|
|
31772
|
+
}
|
|
31773
|
+
onSpatialExtentError(error) {
|
|
31774
|
+
this.clearSpatialExtentErrorNotification();
|
|
31775
|
+
this.spatialExtentErrorNotificationId =
|
|
31776
|
+
this.notificationsService.showNotification({
|
|
31777
|
+
type: 'error',
|
|
31778
|
+
title: this.translateService.instant('search.filters.spatialExtent.error.title'),
|
|
31779
|
+
text: this.translateService.instant(error.key, error.params),
|
|
31780
|
+
});
|
|
31781
|
+
}
|
|
31782
|
+
clearSpatialExtentErrorNotification() {
|
|
31783
|
+
if (this.spatialExtentErrorNotificationId === null)
|
|
31784
|
+
return;
|
|
31785
|
+
this.notificationsService.removeNotificationById(this.spatialExtentErrorNotificationId);
|
|
31786
|
+
this.spatialExtentErrorNotificationId = null;
|
|
31787
|
+
}
|
|
31318
31788
|
ngOnInit() {
|
|
31319
31789
|
this.fieldType = this.fieldsService.getFieldType(this.fieldName);
|
|
31320
31790
|
this.choices$ = this.fieldsService.getAvailableValues(this.fieldName).pipe(startWith$1([]), map$1((values) => values.map((v) => ({
|
|
@@ -31322,27 +31792,15 @@ class FilterDropdownComponent {
|
|
|
31322
31792
|
value: v.value.toString(), // converting to string for the dropdown
|
|
31323
31793
|
}))), catchError(() => of([])));
|
|
31324
31794
|
}
|
|
31325
|
-
|
|
31326
|
-
|
|
31327
|
-
|
|
31328
|
-
|
|
31329
|
-
|
|
31330
|
-
|
|
31331
|
-
if (!end)
|
|
31332
|
-
return;
|
|
31333
|
-
this.dateRange = { ...this.dateRange, end };
|
|
31334
|
-
if (this.dateRange.start && this.dateRange.end) {
|
|
31335
|
-
this.fieldsService
|
|
31336
|
-
.buildFiltersFromFieldValues({
|
|
31337
|
-
[this.fieldName]: this.dateRange,
|
|
31338
|
-
})
|
|
31339
|
-
.subscribe((filters) => {
|
|
31340
|
-
return this.searchService.updateFilters(filters);
|
|
31341
|
-
});
|
|
31342
|
-
}
|
|
31795
|
+
onDateRangeChange(dateRange) {
|
|
31796
|
+
this.fieldsService
|
|
31797
|
+
.buildFiltersFromFieldValues({
|
|
31798
|
+
[this.fieldName]: dateRange,
|
|
31799
|
+
})
|
|
31800
|
+
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
31343
31801
|
}
|
|
31344
31802
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31345
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FilterDropdownComponent, isStandalone: true, selector: "gn-ui-filter-dropdown", inputs: { fieldName: "fieldName", title: "title" }, ngImport: i0, template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [
|
|
31803
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: FilterDropdownComponent, isStandalone: true, selector: "gn-ui-filter-dropdown", inputs: { fieldName: "fieldName", title: "title" }, ngImport: i0, template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [dateRange]=\"(selectedDateRange$ | async) ?? {}\"\n (dateRangeChange)=\"onDateRangeChange($event)\"\n ></gn-ui-date-range-dropdown>\n} @else if (fieldType === 'spatialExtent') {\n <gn-ui-spatial-extent-dropdown\n [title]=\"title\"\n [maxFileSizeMb]=\"spatialExtentMaxFileSize\"\n (bboxChange)=\"onBboxChange($event)\"\n (errorChange)=\"onSpatialExtentError($event)\"\n ></gn-ui-spatial-extent-dropdown>\n} @else {\n <gn-ui-dropdown-multiselect\n class=\"w-full\"\n [title]=\"title\"\n [maxRows]=\"6\"\n [choices]=\"(choices$ | async) || []\"\n [selected]=\"selected$ | async\"\n [allowSearch]=\"true\"\n (selectValues)=\"onSelectedValues($event)\"\n [attr.data-cy-field]=\"fieldName\"\n >\n </gn-ui-dropdown-multiselect>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DateRangeDropdownComponent, selector: "gn-ui-date-range-dropdown", inputs: ["title", "dateRange"], outputs: ["dateRangeChange"] }, { kind: "component", type: DropdownMultiselectComponent, selector: "gn-ui-dropdown-multiselect", inputs: ["title", "choices", "selected", "allowSearch", "maxRows", "searchInputValue"], outputs: ["selectValues"] }, { kind: "component", type: SpatialExtentDropdownComponent, selector: "gn-ui-spatial-extent-dropdown", inputs: ["title", "maxFileSizeMb"], outputs: ["bboxChange", "errorChange"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31346
31804
|
}
|
|
31347
31805
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FilterDropdownComponent, decorators: [{
|
|
31348
31806
|
type: Component,
|
|
@@ -31350,7 +31808,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31350
31808
|
CommonModule,
|
|
31351
31809
|
DateRangeDropdownComponent,
|
|
31352
31810
|
DropdownMultiselectComponent,
|
|
31353
|
-
|
|
31811
|
+
SpatialExtentDropdownComponent,
|
|
31812
|
+
], template: "@if (fieldType === 'dateRange') {\n <gn-ui-date-range-dropdown\n [title]=\"title\"\n [dateRange]=\"(selectedDateRange$ | async) ?? {}\"\n (dateRangeChange)=\"onDateRangeChange($event)\"\n ></gn-ui-date-range-dropdown>\n} @else if (fieldType === 'spatialExtent') {\n <gn-ui-spatial-extent-dropdown\n [title]=\"title\"\n [maxFileSizeMb]=\"spatialExtentMaxFileSize\"\n (bboxChange)=\"onBboxChange($event)\"\n (errorChange)=\"onSpatialExtentError($event)\"\n ></gn-ui-spatial-extent-dropdown>\n} @else {\n <gn-ui-dropdown-multiselect\n class=\"w-full\"\n [title]=\"title\"\n [maxRows]=\"6\"\n [choices]=\"(choices$ | async) || []\"\n [selected]=\"selected$ | async\"\n [allowSearch]=\"true\"\n (selectValues)=\"onSelectedValues($event)\"\n [attr.data-cy-field]=\"fieldName\"\n >\n </gn-ui-dropdown-multiselect>\n}\n" }]
|
|
31354
31813
|
}], propDecorators: { fieldName: [{
|
|
31355
31814
|
type: Input
|
|
31356
31815
|
}], title: [{
|
|
@@ -31886,74 +32345,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
31886
32345
|
args: ['gnUiSearchStateContainer']
|
|
31887
32346
|
}] } });
|
|
31888
32347
|
|
|
31889
|
-
class NotificationsService {
|
|
31890
|
-
constructor() {
|
|
31891
|
-
this.notifications$ = new BehaviorSubject([]);
|
|
31892
|
-
}
|
|
31893
|
-
showNotification(content, timeoutMs, error) {
|
|
31894
|
-
error && console.error(error);
|
|
31895
|
-
const id = Math.floor(Math.random() * 1000000);
|
|
31896
|
-
this.notifications$.next([...this.notifications$.value, { ...content, id }]);
|
|
31897
|
-
if (typeof timeoutMs === 'undefined')
|
|
31898
|
-
return;
|
|
31899
|
-
setTimeout(() => {
|
|
31900
|
-
this.removeNotificationById(id);
|
|
31901
|
-
}, timeoutMs);
|
|
31902
|
-
}
|
|
31903
|
-
removeNotificationById(id) {
|
|
31904
|
-
this.notifications$.next(this.notifications$.value.filter((n) => n.id !== id));
|
|
31905
|
-
}
|
|
31906
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
31907
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, providedIn: 'root' }); }
|
|
31908
|
-
}
|
|
31909
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsService, decorators: [{
|
|
31910
|
-
type: Injectable,
|
|
31911
|
-
args: [{
|
|
31912
|
-
providedIn: 'root',
|
|
31913
|
-
}]
|
|
31914
|
-
}] });
|
|
31915
|
-
|
|
31916
|
-
class NotificationsContainerComponent {
|
|
31917
|
-
constructor() {
|
|
31918
|
-
this.notificationsService = inject(NotificationsService);
|
|
31919
|
-
}
|
|
31920
|
-
trackById(index, notification) {
|
|
31921
|
-
return notification.id;
|
|
31922
|
-
}
|
|
31923
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
31924
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: NotificationsContainerComponent, isStandalone: true, selector: "gn-ui-notifications-container", ngImport: i0, template: "<div class=\"flex flex-col gap-6 p-6 items-start pointer-events-none\">\n @for (\n notification of notificationsService.notifications$ | async;\n track trackById($index, notification)\n ) {\n <gn-ui-notification\n class=\"max-w-full pointer-events-auto\"\n [text]=\"notification.text\"\n [type]=\"notification.type\"\n [title]=\"notification.title\"\n [closeMessage]=\"notification.closeMessage\"\n (notificationClose)=\"\n notificationsService.removeNotificationById(notification.id)\n \"\n [@enterExit]\n ></gn-ui-notification>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: NotificationComponent, selector: "gn-ui-notification", inputs: ["type", "title", "text", "closeMessage"], outputs: ["notificationClose"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }], animations: [
|
|
31925
|
-
trigger('enterExit', [
|
|
31926
|
-
transition(':enter', [
|
|
31927
|
-
animate('150ms', keyframes([
|
|
31928
|
-
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31929
|
-
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31930
|
-
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31931
|
-
])),
|
|
31932
|
-
]),
|
|
31933
|
-
transition(':leave', [
|
|
31934
|
-
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31935
|
-
]),
|
|
31936
|
-
]),
|
|
31937
|
-
], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
31938
|
-
}
|
|
31939
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NotificationsContainerComponent, decorators: [{
|
|
31940
|
-
type: Component,
|
|
31941
|
-
args: [{ selector: 'gn-ui-notifications-container', standalone: true, imports: [CommonModule, NotificationComponent], changeDetection: ChangeDetectionStrategy.OnPush, animations: [
|
|
31942
|
-
trigger('enterExit', [
|
|
31943
|
-
transition(':enter', [
|
|
31944
|
-
animate('150ms', keyframes([
|
|
31945
|
-
style({ transform: 'scale(1)', opacity: 0 }),
|
|
31946
|
-
style({ transform: 'scale(1.03)', opacity: 0.5 }),
|
|
31947
|
-
style({ transform: 'scale(1)', opacity: 1 }),
|
|
31948
|
-
])),
|
|
31949
|
-
]),
|
|
31950
|
-
transition(':leave', [
|
|
31951
|
-
animate('200ms', style({ transform: 'translateX(50px)', opacity: 0 })),
|
|
31952
|
-
]),
|
|
31953
|
-
]),
|
|
31954
|
-
], template: "<div class=\"flex flex-col gap-6 p-6 items-start pointer-events-none\">\n @for (\n notification of notificationsService.notifications$ | async;\n track trackById($index, notification)\n ) {\n <gn-ui-notification\n class=\"max-w-full pointer-events-auto\"\n [text]=\"notification.text\"\n [type]=\"notification.type\"\n [title]=\"notification.title\"\n [closeMessage]=\"notification.closeMessage\"\n (notificationClose)=\"\n notificationsService.removeNotificationById(notification.id)\n \"\n [@enterExit]\n ></gn-ui-notification>\n }\n</div>\n" }]
|
|
31955
|
-
}] });
|
|
31956
|
-
|
|
31957
32348
|
class ResultsTableContainerComponent {
|
|
31958
32349
|
constructor() {
|
|
31959
32350
|
this.searchFacade = inject(SearchFacade);
|
|
@@ -32038,12 +32429,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
32038
32429
|
|
|
32039
32430
|
marker('search.filters.summaryLabel.user');
|
|
32040
32431
|
marker('search.filters.summaryLabel.changeDate');
|
|
32432
|
+
const OPEN_BOUND = '…';
|
|
32041
32433
|
class SearchFiltersSummaryItemComponent {
|
|
32042
32434
|
constructor() {
|
|
32043
32435
|
this.searchFacade = inject(SearchFacade);
|
|
32044
32436
|
this.searchService = inject(SearchService);
|
|
32045
32437
|
this.fieldsService = inject(FieldsService);
|
|
32046
|
-
this.
|
|
32438
|
+
this.dateService = inject(DateService);
|
|
32047
32439
|
this.translate = inject(TranslateService);
|
|
32048
32440
|
this.fieldValues$ = this.searchFacade.searchFilters$.pipe(switchMap((filters) => this.fieldsService.readFieldValuesFromFilters(filters)), map$2((fieldValues) => Array.isArray(fieldValues[this.fieldName])
|
|
32049
32441
|
? fieldValues[this.fieldName]
|
|
@@ -32070,9 +32462,10 @@ class SearchFiltersSummaryItemComponent {
|
|
|
32070
32462
|
getReadableValues(fieldValues) {
|
|
32071
32463
|
return fieldValues.map((value) => {
|
|
32072
32464
|
if (this.fieldType === 'dateRange') {
|
|
32465
|
+
const { start, end } = value;
|
|
32073
32466
|
return {
|
|
32074
32467
|
value,
|
|
32075
|
-
label: `${this.
|
|
32468
|
+
label: `${this.formatBound(start)} - ${this.formatBound(end)}`,
|
|
32076
32469
|
};
|
|
32077
32470
|
}
|
|
32078
32471
|
else if (this.fieldName === 'user') {
|
|
@@ -32083,6 +32476,15 @@ class SearchFiltersSummaryItemComponent {
|
|
|
32083
32476
|
}
|
|
32084
32477
|
});
|
|
32085
32478
|
}
|
|
32479
|
+
formatBound(date) {
|
|
32480
|
+
return date
|
|
32481
|
+
? this.dateService.formatDate(date, {
|
|
32482
|
+
day: '2-digit',
|
|
32483
|
+
month: '2-digit',
|
|
32484
|
+
year: 'numeric',
|
|
32485
|
+
})
|
|
32486
|
+
: OPEN_BOUND;
|
|
32487
|
+
}
|
|
32086
32488
|
async removeFilterValue(fieldValue) {
|
|
32087
32489
|
const currentFieldValues = await firstValueFrom(this.fieldValues$);
|
|
32088
32490
|
const updatedFieldValues = currentFieldValues
|
|
@@ -32095,11 +32497,11 @@ class SearchFiltersSummaryItemComponent {
|
|
|
32095
32497
|
.subscribe((filters) => this.searchService.updateFilters(filters));
|
|
32096
32498
|
}
|
|
32097
32499
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SearchFiltersSummaryItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
32098
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: SearchFiltersSummaryItemComponent, isStandalone: true, selector: "gn-ui-search-filters-summary-item", inputs: { fieldName: "fieldName" },
|
|
32500
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: SearchFiltersSummaryItemComponent, isStandalone: true, selector: "gn-ui-search-filters-summary-item", inputs: { fieldName: "fieldName" }, ngImport: i0, template: "@if ((fieldValues$ | async)?.length > 0) {\n <div class=\"flex flex-row items-center gap-2\">\n <span class=\"text-gray-800\" data-cy=\"filterSummaryLabel\">{{\n translatedLabel\n }}</span>\n @for (fieldValue of fieldValues$ | async; track fieldValue) {\n <gn-ui-badge\n class=\"gn-ui-badge\"\n [style.--gn-ui-badge-rounded]=\"'8px'\"\n [style.--gn-ui-badge-padding]=\"'3px 4px'\"\n [style.--gn-ui-badge-text-color]=\"'black'\"\n [style.--gn-ui-badge-background-color]=\"'#F2F2F2'\"\n [style.--gn-ui-badge-font-weight]=\"'700'\"\n [removable]=\"true\"\n (badgeRemoveClicked)=\"removeFilterValue(fieldValue.value)\"\n >{{ fieldValue.label }}</gn-ui-badge\n >\n }\n </div>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: BadgeComponent, selector: "gn-ui-badge", inputs: ["clickable", "removable"], outputs: ["badgeRemoveClicked", "badgeClicked"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }] }); }
|
|
32099
32501
|
}
|
|
32100
32502
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SearchFiltersSummaryItemComponent, decorators: [{
|
|
32101
32503
|
type: Component,
|
|
32102
|
-
args: [{ selector: 'gn-ui-search-filters-summary-item', standalone: true, imports: [CommonModule, BadgeComponent],
|
|
32504
|
+
args: [{ selector: 'gn-ui-search-filters-summary-item', standalone: true, imports: [CommonModule, BadgeComponent], template: "@if ((fieldValues$ | async)?.length > 0) {\n <div class=\"flex flex-row items-center gap-2\">\n <span class=\"text-gray-800\" data-cy=\"filterSummaryLabel\">{{\n translatedLabel\n }}</span>\n @for (fieldValue of fieldValues$ | async; track fieldValue) {\n <gn-ui-badge\n class=\"gn-ui-badge\"\n [style.--gn-ui-badge-rounded]=\"'8px'\"\n [style.--gn-ui-badge-padding]=\"'3px 4px'\"\n [style.--gn-ui-badge-text-color]=\"'black'\"\n [style.--gn-ui-badge-background-color]=\"'#F2F2F2'\"\n [style.--gn-ui-badge-font-weight]=\"'700'\"\n [removable]=\"true\"\n (badgeRemoveClicked)=\"removeFilterValue(fieldValue.value)\"\n >{{ fieldValue.label }}</gn-ui-badge\n >\n }\n </div>\n}\n" }]
|
|
32103
32505
|
}], propDecorators: { fieldName: [{
|
|
32104
32506
|
type: Input
|
|
32105
32507
|
}] } });
|
|
@@ -32343,11 +32745,11 @@ class AddLayerFromFileComponent {
|
|
|
32343
32745
|
}, 5000);
|
|
32344
32746
|
}
|
|
32345
32747
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AddLayerFromFileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
32346
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: AddLayerFromFileComponent, isStandalone: true, selector: "gn-ui-add-layer-from-file", ngImport: i0, template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n", styles: [""], dependencies: [{ kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: DragAndDropFileInputComponent, selector: "gn-ui-drag-and-drop-file-input", inputs: ["placeholder", "accept"], outputs: ["fileChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
32748
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: AddLayerFromFileComponent, isStandalone: true, selector: "gn-ui-add-layer-from-file", ngImport: i0, template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n textClass=\"text-gray-900 pl-2 py-2\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n", styles: [""], dependencies: [{ kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: DragAndDropFileInputComponent, selector: "gn-ui-drag-and-drop-file-input", inputs: ["placeholder", "accept", "maxFileSizeMb", "icon", "dropzoneBackgroundColor", "textClass", "extraClass", "showFileName"], outputs: ["fileChange", "errorChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
32347
32749
|
}
|
|
32348
32750
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AddLayerFromFileComponent, decorators: [{
|
|
32349
32751
|
type: Component,
|
|
32350
|
-
args: [{ selector: 'gn-ui-add-layer-from-file', standalone: true, imports: [TranslateDirective, TranslatePipe, DragAndDropFileInputComponent], template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n" }]
|
|
32752
|
+
args: [{ selector: 'gn-ui-add-layer-from-file', standalone: true, imports: [TranslateDirective, TranslatePipe, DragAndDropFileInputComponent], template: "<div class=\"flex flex-col gap-2 my-2\">\n <div class=\"flex items-center gap-4\">\n <div class=\"grow rounded-md border-2 border-gray-200\">\n <gn-ui-drag-and-drop-file-input\n (fileChange)=\"handleFileChange($event)\"\n [accept]=\"acceptedMimeType.join(',')\"\n [placeholder]=\"'map.addFromFile.placeholder' | translate\"\n textClass=\"text-gray-900 pl-2 py-2\"\n class=\"placeholder-grey\"\n ></gn-ui-drag-and-drop-file-input>\n </div>\n </div>\n <p class=\"text-sm text-gray-600\" translate>map.help.addFromFile</p>\n</div>\n\n@if (errorMessage) {\n <div class=\"text-red-500 mt-2\">\n {{ errorMessage }}\n </div>\n}\n\n@if (successMessage) {\n <div class=\"text-green-500 mt-2\">\n {{ successMessage }}\n </div>\n}\n" }]
|
|
32351
32753
|
}] });
|
|
32352
32754
|
|
|
32353
32755
|
class LayersPanelComponent {
|
|
@@ -32512,8 +32914,7 @@ const closeMetadata = createAction('[Metadata view] close');
|
|
|
32512
32914
|
Related actions
|
|
32513
32915
|
*/
|
|
32514
32916
|
const setRelated = createAction('[Metadata view] Set related records', props());
|
|
32515
|
-
const
|
|
32516
|
-
const setSourceOf = createAction('[Metadata view] Set has sources', props());
|
|
32917
|
+
const setLinkedRecords = createAction('[Metadata view] Set associated records', props());
|
|
32517
32918
|
/*
|
|
32518
32919
|
ChartConfig actions
|
|
32519
32920
|
*/
|
|
@@ -32545,9 +32946,8 @@ var mdview_actions = /*#__PURE__*/Object.freeze({
|
|
|
32545
32946
|
loadUserFeedbacksSuccess: loadUserFeedbacksSuccess,
|
|
32546
32947
|
setChartConfig: setChartConfig,
|
|
32547
32948
|
setIncompleteMetadata: setIncompleteMetadata,
|
|
32548
|
-
|
|
32549
|
-
|
|
32550
|
-
setSources: setSources
|
|
32949
|
+
setLinkedRecords: setLinkedRecords,
|
|
32950
|
+
setRelated: setRelated
|
|
32551
32951
|
});
|
|
32552
32952
|
|
|
32553
32953
|
const METADATA_VIEW_FEATURE_STATE_KEY = 'metadataView';
|
|
@@ -32590,12 +32990,9 @@ on(loadFullMetadata, (state) => ({
|
|
|
32590
32990
|
on(setRelated, (state, { related }) => ({
|
|
32591
32991
|
...state,
|
|
32592
32992
|
related,
|
|
32593
|
-
})), on(
|
|
32594
|
-
...state,
|
|
32595
|
-
sources,
|
|
32596
|
-
})), on(setSourceOf, (state, { sourceOf }) => ({
|
|
32993
|
+
})), on(setLinkedRecords, (state, { linkedRecords }) => ({
|
|
32597
32994
|
...state,
|
|
32598
|
-
|
|
32995
|
+
linkedRecords,
|
|
32599
32996
|
})),
|
|
32600
32997
|
/*
|
|
32601
32998
|
ChartConfig reducers
|
|
@@ -32657,8 +33054,7 @@ const getMetadataError = createSelector(getMdViewState, (state) => state.error);
|
|
|
32657
33054
|
Related selectors
|
|
32658
33055
|
*/
|
|
32659
33056
|
const getRelated = createSelector(getMdViewState, (state) => state.related);
|
|
32660
|
-
const
|
|
32661
|
-
const getSourceOf = createSelector(getMdViewState, (state) => state.sourceOf);
|
|
33057
|
+
const getLinkedRecords = createSelector(getMdViewState, (state) => state.linkedRecords);
|
|
32662
33058
|
/*
|
|
32663
33059
|
Metadata selectors
|
|
32664
33060
|
*/
|
|
@@ -32759,6 +33155,453 @@ function parseHeaders(httpHeaders) {
|
|
|
32759
33155
|
return result;
|
|
32760
33156
|
}
|
|
32761
33157
|
|
|
33158
|
+
const GEOMETRY_COLUMN_ALIAS = '__geometry__';
|
|
33159
|
+
function fieldToSql(name) {
|
|
33160
|
+
return `"${name.replace(/"/g, '""')}"`; // escape double quotes in field names
|
|
33161
|
+
}
|
|
33162
|
+
function filterToSql(filter) {
|
|
33163
|
+
const operator = filter[0];
|
|
33164
|
+
const args = filter.slice(1);
|
|
33165
|
+
function valueToSql(value) {
|
|
33166
|
+
return typeof value === 'number' ? value : `'${value}'`;
|
|
33167
|
+
}
|
|
33168
|
+
switch (operator) {
|
|
33169
|
+
case '<':
|
|
33170
|
+
case '<=':
|
|
33171
|
+
case '>':
|
|
33172
|
+
case '>=':
|
|
33173
|
+
case '=':
|
|
33174
|
+
case '!=':
|
|
33175
|
+
case 'like':
|
|
33176
|
+
return `${fieldToSql(args[0])} ${operator.toUpperCase()} ${valueToSql(args[1])}`;
|
|
33177
|
+
case 'in': {
|
|
33178
|
+
const values = args.slice(1);
|
|
33179
|
+
return `${fieldToSql(args[0])} IN (${values.map(valueToSql).join(', ')})`;
|
|
33180
|
+
}
|
|
33181
|
+
case 'and':
|
|
33182
|
+
case 'or': {
|
|
33183
|
+
const children = args
|
|
33184
|
+
.map(filterToSql)
|
|
33185
|
+
.join(` ${operator.toUpperCase()} `);
|
|
33186
|
+
return `(${children})`;
|
|
33187
|
+
}
|
|
33188
|
+
case 'not':
|
|
33189
|
+
return `NOT (${filterToSql(args[0])})`;
|
|
33190
|
+
}
|
|
33191
|
+
throw new Error(`Could not generate SQL query, operator not recognized: ${operator}`);
|
|
33192
|
+
}
|
|
33193
|
+
function aggregationToSql(aggregation) {
|
|
33194
|
+
const operation = aggregation[0];
|
|
33195
|
+
const field = aggregation[1];
|
|
33196
|
+
switch (operation) {
|
|
33197
|
+
case 'average':
|
|
33198
|
+
return `CAST(AVG(${fieldToSql(field)}) AS DOUBLE) as ${fieldToSql(`average(${field})`)}`;
|
|
33199
|
+
case 'sum':
|
|
33200
|
+
case 'max':
|
|
33201
|
+
case 'min':
|
|
33202
|
+
return `CAST(${operation.toUpperCase()}(${fieldToSql(field)}) AS DOUBLE) as ${fieldToSql(`${operation}(${field})`)}`;
|
|
33203
|
+
case 'count':
|
|
33204
|
+
return 'CAST(COUNT(*) AS INTEGER) as "count()"'; // we don't need Bigint precision here
|
|
33205
|
+
}
|
|
33206
|
+
}
|
|
33207
|
+
/**
|
|
33208
|
+
* Leave arguments at null if not used
|
|
33209
|
+
* @param tableName
|
|
33210
|
+
* @param selected
|
|
33211
|
+
* @param filter
|
|
33212
|
+
* @param sort
|
|
33213
|
+
* @param startIndex
|
|
33214
|
+
* @param count
|
|
33215
|
+
* @param groupBy
|
|
33216
|
+
* @param aggregations
|
|
33217
|
+
*/
|
|
33218
|
+
function generateSqlQuery(tableName, selected = null, filter = null, sort = null, startIndex = null, count = null, groupBy = null, aggregations = null, geometryColumn = null) {
|
|
33219
|
+
let sqlSelect = 'SELECT *';
|
|
33220
|
+
const sqlFrom = ` FROM ${tableName}`;
|
|
33221
|
+
let sqlOrderBy = '';
|
|
33222
|
+
let sqlWhere = '';
|
|
33223
|
+
let sqlLimit = '';
|
|
33224
|
+
let sqlGroupBy = '';
|
|
33225
|
+
if (selected !== null) {
|
|
33226
|
+
sqlSelect = `SELECT ${selected.map(fieldToSql).join(', ')}`;
|
|
33227
|
+
}
|
|
33228
|
+
if (geometryColumn !== null) {
|
|
33229
|
+
sqlSelect += `, ST_AsGeoJSON(${fieldToSql(geometryColumn)}) as ${GEOMETRY_COLUMN_ALIAS}`;
|
|
33230
|
+
}
|
|
33231
|
+
if (filter !== null) {
|
|
33232
|
+
sqlWhere = ` WHERE ${filterToSql(filter)}`;
|
|
33233
|
+
}
|
|
33234
|
+
if (sort?.length) {
|
|
33235
|
+
sqlOrderBy = ` ORDER BY ${sort
|
|
33236
|
+
.map((sort) => `${fieldToSql(sort[1])} ${sort[0].toUpperCase()}`)
|
|
33237
|
+
.join(', ')}`;
|
|
33238
|
+
}
|
|
33239
|
+
if (startIndex !== null && count !== null) {
|
|
33240
|
+
sqlLimit = ` LIMIT ${count} OFFSET ${startIndex}`;
|
|
33241
|
+
}
|
|
33242
|
+
if (groupBy !== null && aggregations !== null) {
|
|
33243
|
+
sqlSelect = `SELECT ${aggregations.map(aggregationToSql).join(', ')}`;
|
|
33244
|
+
const groupedByDistinct = groupBy.filter((group) => group[0] === 'distinct');
|
|
33245
|
+
const sqlGroupByFields = groupedByDistinct
|
|
33246
|
+
.map((group) => fieldToSql(group[1]))
|
|
33247
|
+
.join(', ');
|
|
33248
|
+
const sqlGroupBySelect = groupedByDistinct
|
|
33249
|
+
.map((group) => `${fieldToSql(group[1])} as ${fieldToSql(`distinct(${group[1]})`)}`)
|
|
33250
|
+
.join(', ');
|
|
33251
|
+
if (sqlGroupByFields && sqlGroupBySelect) {
|
|
33252
|
+
sqlGroupBy = ` GROUP BY ${sqlGroupByFields}`;
|
|
33253
|
+
sqlSelect += `, ${sqlGroupBySelect}`;
|
|
33254
|
+
}
|
|
33255
|
+
}
|
|
33256
|
+
return sqlSelect + sqlFrom + sqlGroupBy + sqlWhere + sqlOrderBy + sqlLimit;
|
|
33257
|
+
}
|
|
33258
|
+
|
|
33259
|
+
function arrowTableToDataItems(table) {
|
|
33260
|
+
const fields = table.schema.fields;
|
|
33261
|
+
return table.toArray().map((row) => {
|
|
33262
|
+
const rowJson = row.toJSON();
|
|
33263
|
+
const feature = {
|
|
33264
|
+
type: 'Feature',
|
|
33265
|
+
geometry: null,
|
|
33266
|
+
properties: {},
|
|
33267
|
+
};
|
|
33268
|
+
const keys = Object.keys(rowJson);
|
|
33269
|
+
for (let i = 0; i < keys.length; i++) {
|
|
33270
|
+
const key = keys[i];
|
|
33271
|
+
const dataType = fields[i].type;
|
|
33272
|
+
let value = rowJson[key];
|
|
33273
|
+
// this might happen if we get an array inside a field
|
|
33274
|
+
if (value instanceof Vector) {
|
|
33275
|
+
value = Array.from(value);
|
|
33276
|
+
}
|
|
33277
|
+
// cast bigints to ints
|
|
33278
|
+
if (typeof value === 'bigint') {
|
|
33279
|
+
value = Number(value);
|
|
33280
|
+
}
|
|
33281
|
+
// rename columns with empty name
|
|
33282
|
+
if (!key) {
|
|
33283
|
+
feature.properties['unknown'] = value;
|
|
33284
|
+
continue;
|
|
33285
|
+
}
|
|
33286
|
+
// assign properties that look like an id to the geojson `id` field
|
|
33287
|
+
if (/^(object|feature)?_?id$/.test(key.toLowerCase()) &&
|
|
33288
|
+
(typeof value == 'string' || typeof value === 'number')) {
|
|
33289
|
+
feature.id = value;
|
|
33290
|
+
}
|
|
33291
|
+
// if a date is in timestamp (number) format, convert it to native
|
|
33292
|
+
if (typeof value === 'number' &&
|
|
33293
|
+
(DataType.isTimestamp(dataType) || DataType.isDate(dataType))) {
|
|
33294
|
+
value = new Date(value);
|
|
33295
|
+
}
|
|
33296
|
+
// if a binary field (most likely a geometry column): skip
|
|
33297
|
+
if (DataType.isBinary(dataType)) {
|
|
33298
|
+
continue;
|
|
33299
|
+
}
|
|
33300
|
+
// geometry column
|
|
33301
|
+
if (key === GEOMETRY_COLUMN_ALIAS && DataType.isUtf8(dataType)) {
|
|
33302
|
+
feature.geometry = JSON.parse(value);
|
|
33303
|
+
continue;
|
|
33304
|
+
}
|
|
33305
|
+
feature.properties[key] = value;
|
|
33306
|
+
}
|
|
33307
|
+
return feature;
|
|
33308
|
+
});
|
|
33309
|
+
}
|
|
33310
|
+
|
|
33311
|
+
// init code taken from https://github.com/duckdb/duckdb-wasm/blob/main/packages/duckdb-wasm/README.md
|
|
33312
|
+
const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();
|
|
33313
|
+
// from https://duckdb.org/docs/current/sql/data_types/overview
|
|
33314
|
+
const typesMapping = {
|
|
33315
|
+
INTEGER: 'number',
|
|
33316
|
+
SMALLINT: 'number',
|
|
33317
|
+
TINYINT: 'number',
|
|
33318
|
+
BIGINT: 'number',
|
|
33319
|
+
HUGEINT: 'number',
|
|
33320
|
+
UINTEGER: 'number',
|
|
33321
|
+
USMALLINT: 'number',
|
|
33322
|
+
UTINYINT: 'number',
|
|
33323
|
+
UBIGINT: 'number',
|
|
33324
|
+
UHUGEINT: 'number',
|
|
33325
|
+
DOUBLE: 'number',
|
|
33326
|
+
BIGNUM: 'number',
|
|
33327
|
+
DECIMAL: 'number',
|
|
33328
|
+
NUMERIC: 'number',
|
|
33329
|
+
FLOAT: 'number',
|
|
33330
|
+
REAL: 'number',
|
|
33331
|
+
'TIMESTAMP WITH TIME ZONE': 'date',
|
|
33332
|
+
TIMESTAMP: 'date',
|
|
33333
|
+
DATE: 'date',
|
|
33334
|
+
CHAR: 'string',
|
|
33335
|
+
VARCHAR: 'string',
|
|
33336
|
+
TEXT: 'string',
|
|
33337
|
+
UUID: 'string',
|
|
33338
|
+
BLOB: 'string',
|
|
33339
|
+
BIT: 'string',
|
|
33340
|
+
INTERVAL: 'string',
|
|
33341
|
+
LIST: 'string',
|
|
33342
|
+
BOOLEAN: 'boolean',
|
|
33343
|
+
};
|
|
33344
|
+
class Engine {
|
|
33345
|
+
async makeInit() {
|
|
33346
|
+
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);
|
|
33347
|
+
let worker_url = bundle.mainWorker;
|
|
33348
|
+
// this is necessary to let browsers execute WASM code coming from a cross-origin host
|
|
33349
|
+
if (worker_url.startsWith('https://')) {
|
|
33350
|
+
worker_url = URL.createObjectURL(new Blob([`importScripts("${worker_url}");`], {
|
|
33351
|
+
type: 'text/javascript',
|
|
33352
|
+
}));
|
|
33353
|
+
}
|
|
33354
|
+
const worker = new Worker(worker_url);
|
|
33355
|
+
const logger = new duckdb.ConsoleLogger(duckdb.LogLevel.WARNING);
|
|
33356
|
+
this.db = new duckdb.AsyncDuckDB(logger, worker);
|
|
33357
|
+
await this.db.instantiate(bundle.mainModule, bundle.pthreadWorker);
|
|
33358
|
+
// setup duckdb options
|
|
33359
|
+
const conn = await this.db.connect();
|
|
33360
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
33361
|
+
await conn.query(`INSTALL spatial; LOAD spatial;`);
|
|
33362
|
+
await conn.query(`SET TimeZone = '${timezone}';`);
|
|
33363
|
+
await conn.query(`SET threads = 1;`);
|
|
33364
|
+
// await conn.query(`SET memory_limit = '2GB';`) // used for experimenting when we encounter memory issues
|
|
33365
|
+
await conn.query(`SET preserve_insertion_order = false;`); // this reduces memory usage when loading a dataset
|
|
33366
|
+
conn.close();
|
|
33367
|
+
return this;
|
|
33368
|
+
}
|
|
33369
|
+
isReady() {
|
|
33370
|
+
if (!this.init_) {
|
|
33371
|
+
this.init_ = this.makeInit();
|
|
33372
|
+
}
|
|
33373
|
+
return this.init_;
|
|
33374
|
+
}
|
|
33375
|
+
/**
|
|
33376
|
+
* Returns information about a dataset once it's loaded:
|
|
33377
|
+
* - a list of properties description
|
|
33378
|
+
* - the name of the dataset geometry column (null if no geometry present)
|
|
33379
|
+
* @param datasetId name of the table under which the dataset will be stored
|
|
33380
|
+
* @param loadQuery duckdb-specific query for creating a table out of the data
|
|
33381
|
+
* @param forceReload if true, any existing data will be dropped and redownloaded
|
|
33382
|
+
*/
|
|
33383
|
+
async loadFile(datasetId, loadQuery, forceReload = false) {
|
|
33384
|
+
const conn = await this.db.connect();
|
|
33385
|
+
let results;
|
|
33386
|
+
// either we want to recreate the table, or we keep it if it already exists
|
|
33387
|
+
const safeLoadQuery = forceReload
|
|
33388
|
+
? `DROP TABLE IF EXISTS ${datasetId};
|
|
33389
|
+
${loadQuery}`
|
|
33390
|
+
: loadQuery.replace(/CREATE TABLE(?! IF NOT EXISTS)/gi, 'CREATE TABLE IF NOT EXISTS');
|
|
33391
|
+
// create the table
|
|
33392
|
+
try {
|
|
33393
|
+
results = await conn.query(safeLoadQuery);
|
|
33394
|
+
}
|
|
33395
|
+
catch (e) {
|
|
33396
|
+
throw new FetchError('parse', `DuckDB encountered an error when loading the data: ${e.message}`);
|
|
33397
|
+
}
|
|
33398
|
+
// read rows count
|
|
33399
|
+
results = await conn.query(`SELECT count(*) FROM ${datasetId}`);
|
|
33400
|
+
const { 'count_star()': recordsCount } = results.toArray()[0].toJSON();
|
|
33401
|
+
// read columns
|
|
33402
|
+
results = await conn.query(`SELECT * FROM information_schema.columns WHERE table_name = '${datasetId}';`);
|
|
33403
|
+
let geometryColumn = null;
|
|
33404
|
+
const properties = results
|
|
33405
|
+
.toArray()
|
|
33406
|
+
.map((row) => {
|
|
33407
|
+
const rowObj = row.toJSON();
|
|
33408
|
+
if (rowObj['data_type'] === 'GEOMETRY') {
|
|
33409
|
+
// the geometry is not part of the properties
|
|
33410
|
+
// note: right now we only keep one geometry column name, but if there are multiple they will
|
|
33411
|
+
// all get discarded
|
|
33412
|
+
geometryColumn = rowObj['column_name'];
|
|
33413
|
+
return null;
|
|
33414
|
+
}
|
|
33415
|
+
return {
|
|
33416
|
+
name: rowObj['column_name'],
|
|
33417
|
+
label: rowObj['column_name'],
|
|
33418
|
+
type: typesMapping[rowObj['data_type']] ?? 'other',
|
|
33419
|
+
};
|
|
33420
|
+
})
|
|
33421
|
+
.filter((prop) => prop !== null);
|
|
33422
|
+
conn.close();
|
|
33423
|
+
return {
|
|
33424
|
+
properties,
|
|
33425
|
+
geometryColumn,
|
|
33426
|
+
rowsCount: Number(recordsCount),
|
|
33427
|
+
};
|
|
33428
|
+
}
|
|
33429
|
+
// register a Uint8 buffer using a handle in the duckdb instance
|
|
33430
|
+
async registerData(name, buffer) {
|
|
33431
|
+
return this.db.registerFileBuffer(name, buffer);
|
|
33432
|
+
}
|
|
33433
|
+
/**
|
|
33434
|
+
* @param query duckdb-specific query for fetching items
|
|
33435
|
+
*/
|
|
33436
|
+
async queryItems(query) {
|
|
33437
|
+
const conn = await this.db.connect();
|
|
33438
|
+
const results = await conn.query(query);
|
|
33439
|
+
conn.close();
|
|
33440
|
+
return arrowTableToDataItems(results);
|
|
33441
|
+
}
|
|
33442
|
+
close() {
|
|
33443
|
+
this.db?.terminate();
|
|
33444
|
+
}
|
|
33445
|
+
}
|
|
33446
|
+
let engine = null;
|
|
33447
|
+
async function getEngine() {
|
|
33448
|
+
if (!engine) {
|
|
33449
|
+
engine = new Engine();
|
|
33450
|
+
}
|
|
33451
|
+
return engine.isReady();
|
|
33452
|
+
}
|
|
33453
|
+
|
|
33454
|
+
class BaseReader {
|
|
33455
|
+
constructor(url) {
|
|
33456
|
+
this.url = url;
|
|
33457
|
+
this.selected = null;
|
|
33458
|
+
this.groupedBy = null;
|
|
33459
|
+
this.aggregations = null;
|
|
33460
|
+
this.filter = null;
|
|
33461
|
+
this.sort = null;
|
|
33462
|
+
this.startIndex = null;
|
|
33463
|
+
this.count = null;
|
|
33464
|
+
this.loadPromise_ = Promise.resolve();
|
|
33465
|
+
this.cacheEnabled = false;
|
|
33466
|
+
}
|
|
33467
|
+
enableCache(enabled) {
|
|
33468
|
+
this.cacheEnabled = enabled;
|
|
33469
|
+
}
|
|
33470
|
+
load() {
|
|
33471
|
+
throw new Error('not implemented');
|
|
33472
|
+
}
|
|
33473
|
+
get isLoaded() {
|
|
33474
|
+
return this.loadPromise_;
|
|
33475
|
+
}
|
|
33476
|
+
get properties() {
|
|
33477
|
+
throw new Error('not implemented');
|
|
33478
|
+
}
|
|
33479
|
+
get info() {
|
|
33480
|
+
throw new Error('not implemented');
|
|
33481
|
+
}
|
|
33482
|
+
read() {
|
|
33483
|
+
throw new Error('not implemented');
|
|
33484
|
+
}
|
|
33485
|
+
selectAll() {
|
|
33486
|
+
this.groupedBy = null;
|
|
33487
|
+
this.aggregations = null;
|
|
33488
|
+
this.selected = null;
|
|
33489
|
+
this.filter = null;
|
|
33490
|
+
this.startIndex = null;
|
|
33491
|
+
this.count = null;
|
|
33492
|
+
return this;
|
|
33493
|
+
}
|
|
33494
|
+
select(...selectedFields) {
|
|
33495
|
+
this.selected = selectedFields;
|
|
33496
|
+
this.aggregations = null; // clear aggregations & groups when selecting fields
|
|
33497
|
+
this.groupedBy = null;
|
|
33498
|
+
return this;
|
|
33499
|
+
}
|
|
33500
|
+
groupBy(...groupBy) {
|
|
33501
|
+
this.groupedBy = groupBy;
|
|
33502
|
+
this.selected = null; // clear normal field selection when aggregating
|
|
33503
|
+
return this;
|
|
33504
|
+
}
|
|
33505
|
+
aggregate(...aggregations) {
|
|
33506
|
+
this.aggregations = aggregations;
|
|
33507
|
+
return this;
|
|
33508
|
+
}
|
|
33509
|
+
where(filter) {
|
|
33510
|
+
this.filter = filter;
|
|
33511
|
+
return this;
|
|
33512
|
+
}
|
|
33513
|
+
orderBy(...fieldSorts) {
|
|
33514
|
+
this.sort = fieldSorts;
|
|
33515
|
+
return this;
|
|
33516
|
+
}
|
|
33517
|
+
limit(startIndex, count) {
|
|
33518
|
+
this.startIndex = startIndex;
|
|
33519
|
+
this.count = count;
|
|
33520
|
+
return this;
|
|
33521
|
+
}
|
|
33522
|
+
}
|
|
33523
|
+
|
|
33524
|
+
/**
|
|
33525
|
+
* This reader handles file formats supported natively by DuckDB
|
|
33526
|
+
*/
|
|
33527
|
+
class BaseFileReader extends BaseReader {
|
|
33528
|
+
// a table id should not exceed 63 chars
|
|
33529
|
+
generateDatasetId() {
|
|
33530
|
+
// generate a hash out of the url
|
|
33531
|
+
let hash = 0;
|
|
33532
|
+
for (const char of this.url) {
|
|
33533
|
+
hash = (hash << 5) - hash + char.charCodeAt(0);
|
|
33534
|
+
hash = hash >>> 0; // make it unsigned
|
|
33535
|
+
}
|
|
33536
|
+
return `datafetcher_${hash.toString(16)}`;
|
|
33537
|
+
}
|
|
33538
|
+
async getLoadQuery() {
|
|
33539
|
+
throw new Error('not implemented');
|
|
33540
|
+
}
|
|
33541
|
+
async load() {
|
|
33542
|
+
this.datasetId = this.generateDatasetId();
|
|
33543
|
+
this.loadPromise_ = getEngine()
|
|
33544
|
+
.then((engine) => {
|
|
33545
|
+
this.engine = engine;
|
|
33546
|
+
return this.getLoadQuery();
|
|
33547
|
+
})
|
|
33548
|
+
.then((loadQuery) => this.engine.loadFile(this.datasetId, loadQuery, !this.cacheEnabled))
|
|
33549
|
+
.then((datasetInfo) => {
|
|
33550
|
+
this.properties_ = datasetInfo.properties;
|
|
33551
|
+
this.geometryColumn = datasetInfo.geometryColumn;
|
|
33552
|
+
this.rowsCount = datasetInfo.rowsCount;
|
|
33553
|
+
// returns void for the loaded promise
|
|
33554
|
+
});
|
|
33555
|
+
}
|
|
33556
|
+
get properties() {
|
|
33557
|
+
return this.isLoaded.then(() => this.properties_);
|
|
33558
|
+
}
|
|
33559
|
+
get info() {
|
|
33560
|
+
return this.isLoaded.then(() => ({
|
|
33561
|
+
itemsCount: this.rowsCount,
|
|
33562
|
+
hasGeometry: !!this.geometryColumn,
|
|
33563
|
+
}));
|
|
33564
|
+
}
|
|
33565
|
+
async read() {
|
|
33566
|
+
await this.isLoaded;
|
|
33567
|
+
// if only certain fields are selected, omit the geometry
|
|
33568
|
+
const geometryColumn = this.selected === null ? this.geometryColumn : null;
|
|
33569
|
+
const query = generateSqlQuery(this.datasetId, this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations, geometryColumn);
|
|
33570
|
+
return this.engine.queryItems(query);
|
|
33571
|
+
}
|
|
33572
|
+
}
|
|
33573
|
+
|
|
33574
|
+
class CsvReader extends BaseFileReader {
|
|
33575
|
+
async getLoadQuery() {
|
|
33576
|
+
// first we get a list of columns hich have a detected type of VARCHAR
|
|
33577
|
+
// 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
|
|
33578
|
+
return `
|
|
33579
|
+
SET VARIABLE strColumns = (SELECT list(name) FROM (SELECT unnest(Columns, recursive := true) FROM sniff_csv("${this.url}")) WHERE type = 'VARCHAR');
|
|
33580
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM read_csv("${this.url}",
|
|
33581
|
+
force_not_null = getvariable('strColumns'),
|
|
33582
|
+
auto_type_candidates = ['NULL', 'BOOLEAN', 'INTEGER', 'DOUBLE', 'DATE', 'VARCHAR']
|
|
33583
|
+
);`;
|
|
33584
|
+
}
|
|
33585
|
+
}
|
|
33586
|
+
|
|
33587
|
+
class JsonReader extends BaseFileReader {
|
|
33588
|
+
async getLoadQuery() {
|
|
33589
|
+
return `
|
|
33590
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM read_json("${this.url}",
|
|
33591
|
+
maximum_object_size = 536870912 -- 500MB
|
|
33592
|
+
);`;
|
|
33593
|
+
}
|
|
33594
|
+
}
|
|
33595
|
+
|
|
33596
|
+
class GeojsonReader extends BaseFileReader {
|
|
33597
|
+
async getLoadQuery() {
|
|
33598
|
+
return `
|
|
33599
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM st_read("${this.url}",
|
|
33600
|
+
allowed_drivers = ['GeoJSON']
|
|
33601
|
+
);`;
|
|
33602
|
+
}
|
|
33603
|
+
}
|
|
33604
|
+
|
|
32762
33605
|
async function inferDatasetType(url, typeHint) {
|
|
32763
33606
|
const fileExtensionMatches = new URL(url, typeof window !== 'undefined' ? window.location.toString() : undefined).pathname.match(/\.(.+)$/);
|
|
32764
33607
|
const fileExtension = fileExtensionMatches && fileExtensionMatches.length
|
|
@@ -32823,127 +33666,6 @@ function fetchDataAsArrayBuffer(url, cacheActive) {
|
|
|
32823
33666
|
return new Uint8Array(array).buffer;
|
|
32824
33667
|
});
|
|
32825
33668
|
}
|
|
32826
|
-
function tryParseDate(input) {
|
|
32827
|
-
if (typeof input !== 'string')
|
|
32828
|
-
return null;
|
|
32829
|
-
function tryIso(value) {
|
|
32830
|
-
const parsed = parseISO(value);
|
|
32831
|
-
return isNaN(parsed.getDate()) ? null : parsed;
|
|
32832
|
-
}
|
|
32833
|
-
function tryFormat(value, format) {
|
|
32834
|
-
const parsed = parse$4(value, format, new Date());
|
|
32835
|
-
return isNaN(parsed.getDate()) ? null : parsed;
|
|
32836
|
-
}
|
|
32837
|
-
return (tryIso(input) ||
|
|
32838
|
-
tryFormat(input, 'dd/MM/yyyy') ||
|
|
32839
|
-
tryFormat(input, 'dd.MM.yyyy') ||
|
|
32840
|
-
tryFormat(input, 'MM/dd/yyyy') ||
|
|
32841
|
-
null);
|
|
32842
|
-
}
|
|
32843
|
-
function tryParseNumber(input) {
|
|
32844
|
-
if (isNaN(input))
|
|
32845
|
-
return null;
|
|
32846
|
-
const parsed = parseFloat(input);
|
|
32847
|
-
return isNaN(parsed) ? null : parsed;
|
|
32848
|
-
}
|
|
32849
|
-
function jsonToGeojsonFeature(object) {
|
|
32850
|
-
const { id, properties } = Object.keys(object)
|
|
32851
|
-
.map((property) => (property ? property : 'unknown')) //prevent empty strings
|
|
32852
|
-
.reduce((prev, curr) => curr.toLowerCase().endsWith('id')
|
|
32853
|
-
? {
|
|
32854
|
-
...prev,
|
|
32855
|
-
id: object[curr],
|
|
32856
|
-
}
|
|
32857
|
-
: {
|
|
32858
|
-
...prev,
|
|
32859
|
-
properties: { ...prev.properties, [curr]: object[curr] },
|
|
32860
|
-
}, { id: undefined, properties: {} });
|
|
32861
|
-
return {
|
|
32862
|
-
type: 'Feature',
|
|
32863
|
-
geometry: null,
|
|
32864
|
-
properties,
|
|
32865
|
-
...(id !== undefined && { id }),
|
|
32866
|
-
};
|
|
32867
|
-
}
|
|
32868
|
-
function mutateProperties(items, mutators) {
|
|
32869
|
-
const mutatorKeys = Object.keys(mutators);
|
|
32870
|
-
for (let i = 0, ii = items.length; i < ii; i++) {
|
|
32871
|
-
const item = items[i];
|
|
32872
|
-
for (const mutatorField of mutatorKeys) {
|
|
32873
|
-
if (!(mutatorField in item.properties))
|
|
32874
|
-
continue;
|
|
32875
|
-
item.properties[mutatorField] = mutators[mutatorField](item.properties[mutatorField]);
|
|
32876
|
-
}
|
|
32877
|
-
}
|
|
32878
|
-
return items;
|
|
32879
|
-
}
|
|
32880
|
-
const SAMPLE_SIZE = 20;
|
|
32881
|
-
/**
|
|
32882
|
-
* This will infer field types from a list of data items and cast the values accordingly
|
|
32883
|
-
* @param items
|
|
32884
|
-
* @param inferTypes
|
|
32885
|
-
*/
|
|
32886
|
-
function processItemProperties(items, inferTypes = false) {
|
|
32887
|
-
const foundFields = {};
|
|
32888
|
-
for (let i = 0, ii = Math.min(SAMPLE_SIZE, items.length); i < ii; i++) {
|
|
32889
|
-
const item = items[i];
|
|
32890
|
-
const fields = Object.keys(item.properties);
|
|
32891
|
-
for (const field of fields) {
|
|
32892
|
-
if (!(field in foundFields)) {
|
|
32893
|
-
foundFields[field] = {
|
|
32894
|
-
label: field,
|
|
32895
|
-
name: field,
|
|
32896
|
-
type: null,
|
|
32897
|
-
};
|
|
32898
|
-
}
|
|
32899
|
-
const value = item.properties[field];
|
|
32900
|
-
const info = foundFields[field];
|
|
32901
|
-
if (value === undefined || value === '' || value === null)
|
|
32902
|
-
continue;
|
|
32903
|
-
if (!inferTypes) {
|
|
32904
|
-
if (info.type === null && typeof value === 'number') {
|
|
32905
|
-
info.type = 'number';
|
|
32906
|
-
}
|
|
32907
|
-
else if (info.type === 'number' && typeof value !== 'number') {
|
|
32908
|
-
info.type = 'string';
|
|
32909
|
-
}
|
|
32910
|
-
continue;
|
|
32911
|
-
}
|
|
32912
|
-
const parsedNumber = tryParseNumber(value);
|
|
32913
|
-
if (info.type === null && parsedNumber !== null) {
|
|
32914
|
-
info.type = 'number';
|
|
32915
|
-
continue;
|
|
32916
|
-
}
|
|
32917
|
-
else if (info.type === 'number' && parsedNumber === null) {
|
|
32918
|
-
info.type = 'string';
|
|
32919
|
-
continue;
|
|
32920
|
-
}
|
|
32921
|
-
const parsedDate = tryParseDate(value);
|
|
32922
|
-
if (info.type === null && parsedDate !== null) {
|
|
32923
|
-
info.type = 'date';
|
|
32924
|
-
}
|
|
32925
|
-
else if (info.type === 'date' && parsedDate === null) {
|
|
32926
|
-
info.type = 'string';
|
|
32927
|
-
}
|
|
32928
|
-
}
|
|
32929
|
-
}
|
|
32930
|
-
const properties = [];
|
|
32931
|
-
const mutators = {};
|
|
32932
|
-
for (const field in foundFields) {
|
|
32933
|
-
const info = foundFields[field];
|
|
32934
|
-
if (info.type === 'number') {
|
|
32935
|
-
mutators[field] = tryParseNumber;
|
|
32936
|
-
}
|
|
32937
|
-
else if (info.type === 'date') {
|
|
32938
|
-
mutators[field] = tryParseDate;
|
|
32939
|
-
}
|
|
32940
|
-
properties.push({ ...info, type: info.type || 'string' });
|
|
32941
|
-
}
|
|
32942
|
-
if (inferTypes) {
|
|
32943
|
-
mutateProperties(items, mutators);
|
|
32944
|
-
}
|
|
32945
|
-
return { items, properties };
|
|
32946
|
-
}
|
|
32947
33669
|
/**
|
|
32948
33670
|
* This creates a Proxy that allows reading and writing to the data item properties
|
|
32949
33671
|
* as if it was a simple array of JSON objects
|
|
@@ -32965,333 +33687,71 @@ function getJsonDataItemsProxy(items) {
|
|
|
32965
33687
|
});
|
|
32966
33688
|
}
|
|
32967
33689
|
|
|
32968
|
-
|
|
32969
|
-
|
|
32970
|
-
|
|
32971
|
-
|
|
32972
|
-
|
|
32973
|
-
|
|
32974
|
-
|
|
32975
|
-
|
|
32976
|
-
|
|
32977
|
-
|
|
32978
|
-
|
|
32979
|
-
|
|
32980
|
-
|
|
32981
|
-
|
|
32982
|
-
|
|
32983
|
-
|
|
32984
|
-
|
|
32985
|
-
return `[${args[0]}] IN (${values.map(valueToSql).join(', ')})`;
|
|
32986
|
-
}
|
|
32987
|
-
case 'and':
|
|
32988
|
-
case 'or': {
|
|
32989
|
-
const children = args
|
|
32990
|
-
.map(filterToSql)
|
|
32991
|
-
.join(` ${operator.toUpperCase()} `);
|
|
32992
|
-
return `(${children})`;
|
|
32993
|
-
}
|
|
32994
|
-
case 'not':
|
|
32995
|
-
return `NOT (${filterToSql(args[0])})`;
|
|
32996
|
-
}
|
|
32997
|
-
throw new Error(`Could not generate SQL query, operator not recognized: ${operator}`);
|
|
32998
|
-
}
|
|
32999
|
-
function aggregationToSql(aggregation) {
|
|
33000
|
-
const operation = aggregation[0];
|
|
33001
|
-
const field = aggregation[1];
|
|
33002
|
-
switch (operation) {
|
|
33003
|
-
case 'average':
|
|
33004
|
-
return `AVG([${field}]) as [average(${field})]`;
|
|
33005
|
-
case 'sum':
|
|
33006
|
-
case 'max':
|
|
33007
|
-
case 'min':
|
|
33008
|
-
return `${operation.toUpperCase()}([${field}]) as [${operation}(${field})]`;
|
|
33009
|
-
case 'count':
|
|
33010
|
-
return 'COUNT(*) as [count()]';
|
|
33011
|
-
}
|
|
33012
|
-
}
|
|
33013
|
-
/**
|
|
33014
|
-
* Leave arguments at null if not used
|
|
33015
|
-
* @param selected
|
|
33016
|
-
* @param filter
|
|
33017
|
-
* @param sort
|
|
33018
|
-
* @param startIndex
|
|
33019
|
-
* @param count
|
|
33020
|
-
* @param groupBy
|
|
33021
|
-
* @param aggregations
|
|
33022
|
-
*/
|
|
33023
|
-
function generateSqlQuery(selected = null, filter = null, sort = null, startIndex = null, count = null, groupBy = null, aggregations = null) {
|
|
33024
|
-
let sqlSelect = 'SELECT *';
|
|
33025
|
-
const sqlFrom = ' FROM ?';
|
|
33026
|
-
let sqlOrderBy = '';
|
|
33027
|
-
let sqlWhere = '';
|
|
33028
|
-
let sqlLimit = '';
|
|
33029
|
-
let sqlGroupBy = '';
|
|
33030
|
-
if (selected !== null) {
|
|
33031
|
-
sqlSelect = `SELECT ${selected.map((name) => `[${name}]`).join(', ')}`;
|
|
33032
|
-
}
|
|
33033
|
-
if (filter !== null) {
|
|
33034
|
-
sqlWhere = ` WHERE ${filterToSql(filter)}`;
|
|
33035
|
-
}
|
|
33036
|
-
if (sort?.length) {
|
|
33037
|
-
sqlOrderBy = ` ORDER BY ${sort
|
|
33038
|
-
.map((sort) => `[${sort[1]}] ${sort[0].toUpperCase()}`)
|
|
33039
|
-
.join(', ')}`;
|
|
33040
|
-
}
|
|
33041
|
-
if (startIndex !== null && count !== null) {
|
|
33042
|
-
sqlLimit = ` LIMIT ${count} OFFSET ${startIndex}`;
|
|
33043
|
-
}
|
|
33044
|
-
if (groupBy !== null && aggregations !== null) {
|
|
33045
|
-
sqlSelect = `SELECT ${aggregations.map(aggregationToSql).join(', ')}`;
|
|
33046
|
-
const groupedByDistinct = groupBy.filter((group) => group[0] === 'distinct');
|
|
33047
|
-
const sqlGroupByFields = groupedByDistinct
|
|
33048
|
-
.map((group) => `[${group[1]}]`)
|
|
33049
|
-
.join(', ');
|
|
33050
|
-
const sqlGroupBySelect = groupedByDistinct
|
|
33051
|
-
.map((group) => `[${group[1]}] as [distinct(${group[1]})]`)
|
|
33052
|
-
.join(', ');
|
|
33053
|
-
if (sqlGroupByFields && sqlGroupBySelect) {
|
|
33054
|
-
sqlGroupBy = ` GROUP BY ${sqlGroupByFields}`;
|
|
33055
|
-
sqlSelect += `, ${sqlGroupBySelect}`;
|
|
33690
|
+
class ExcelReader extends BaseFileReader {
|
|
33691
|
+
async getLoadQuery() {
|
|
33692
|
+
// we download the file as an array buffer first, in order to be able to check if it's an XLS file
|
|
33693
|
+
let buffer = await fetchDataAsArrayBuffer(this.url, this.cacheEnabled);
|
|
33694
|
+
const bufferHandle = `B${this.datasetId}`;
|
|
33695
|
+
// checking against the magic number at the beginning of XLS files, see https://en.wikipedia.org/wiki/List_of_file_signatures
|
|
33696
|
+
const magicNumber = new Uint8Array(buffer, 0, 8); // first 8 bytes
|
|
33697
|
+
const isXls = Array.from(magicNumber)
|
|
33698
|
+
.map((n) => n.toString(16).toUpperCase())
|
|
33699
|
+
.join(' ') === 'D0 CF 11 E0 A1 B1 1A E1';
|
|
33700
|
+
// uh oh, this is an XLS file (not supported by duckdb); convert it to CSV using the xlsx package
|
|
33701
|
+
if (isXls) {
|
|
33702
|
+
buffer = await import('xlsx').then(({ read, utils }) => {
|
|
33703
|
+
const workbook = read(buffer);
|
|
33704
|
+
const json = utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]]);
|
|
33705
|
+
return new TextEncoder().encode(JSON.stringify(json)).buffer;
|
|
33706
|
+
});
|
|
33056
33707
|
}
|
|
33057
|
-
|
|
33058
|
-
|
|
33059
|
-
|
|
33060
|
-
|
|
33061
|
-
class BaseReader {
|
|
33062
|
-
constructor(url) {
|
|
33063
|
-
this.url = url;
|
|
33064
|
-
this.selected = null;
|
|
33065
|
-
this.groupedBy = null;
|
|
33066
|
-
this.aggregations = null;
|
|
33067
|
-
this.filter = null;
|
|
33068
|
-
this.sort = null;
|
|
33069
|
-
this.startIndex = null;
|
|
33070
|
-
this.count = null;
|
|
33071
|
-
}
|
|
33072
|
-
load() {
|
|
33073
|
-
throw new Error('not implemented');
|
|
33074
|
-
}
|
|
33075
|
-
get properties() {
|
|
33076
|
-
throw new Error('not implemented');
|
|
33077
|
-
}
|
|
33078
|
-
get info() {
|
|
33079
|
-
throw new Error('not implemented');
|
|
33080
|
-
}
|
|
33081
|
-
read() {
|
|
33082
|
-
throw new Error('not implemented');
|
|
33083
|
-
}
|
|
33084
|
-
selectAll() {
|
|
33085
|
-
this.groupedBy = null;
|
|
33086
|
-
this.aggregations = null;
|
|
33087
|
-
this.selected = null;
|
|
33088
|
-
this.filter = null;
|
|
33089
|
-
this.startIndex = null;
|
|
33090
|
-
this.count = null;
|
|
33091
|
-
return this;
|
|
33092
|
-
}
|
|
33093
|
-
select(...selectedFields) {
|
|
33094
|
-
this.selected = selectedFields;
|
|
33095
|
-
this.aggregations = null; // clear aggregations & groups when selecting fields
|
|
33096
|
-
this.groupedBy = null;
|
|
33097
|
-
return this;
|
|
33098
|
-
}
|
|
33099
|
-
groupBy(...groupBy) {
|
|
33100
|
-
this.groupedBy = groupBy;
|
|
33101
|
-
this.selected = null; // clear normal field selection when aggregating
|
|
33102
|
-
return this;
|
|
33103
|
-
}
|
|
33104
|
-
aggregate(...aggregations) {
|
|
33105
|
-
this.aggregations = aggregations;
|
|
33106
|
-
return this;
|
|
33107
|
-
}
|
|
33108
|
-
where(filter) {
|
|
33109
|
-
this.filter = filter;
|
|
33110
|
-
return this;
|
|
33111
|
-
}
|
|
33112
|
-
orderBy(...fieldSorts) {
|
|
33113
|
-
this.sort = fieldSorts;
|
|
33114
|
-
return this;
|
|
33115
|
-
}
|
|
33116
|
-
limit(startIndex, count) {
|
|
33117
|
-
this.startIndex = startIndex;
|
|
33118
|
-
this.count = count;
|
|
33119
|
-
return this;
|
|
33120
|
-
}
|
|
33121
|
-
}
|
|
33122
|
-
|
|
33123
|
-
class BaseCacheReader extends BaseReader {
|
|
33124
|
-
constructor(url, cacheActive = true) {
|
|
33125
|
-
super(url);
|
|
33126
|
-
this.url = url;
|
|
33127
|
-
this.cacheActive = cacheActive;
|
|
33128
|
-
}
|
|
33129
|
-
setCacheActive(value) {
|
|
33130
|
-
this.cacheActive = value;
|
|
33131
|
-
}
|
|
33132
|
-
}
|
|
33133
|
-
|
|
33134
|
-
class BaseFileReader extends BaseCacheReader {
|
|
33135
|
-
getData() {
|
|
33136
|
-
throw new Error('not implemented');
|
|
33137
|
-
}
|
|
33138
|
-
load() {
|
|
33139
|
-
this.parseResult_ = this.getData();
|
|
33140
|
-
}
|
|
33141
|
-
get properties() {
|
|
33142
|
-
return this.parseResult_.then((result) => result.properties);
|
|
33143
|
-
}
|
|
33144
|
-
get info() {
|
|
33145
|
-
return this.parseResult_.then((result) => ({
|
|
33146
|
-
itemsCount: result.items.length,
|
|
33147
|
-
}));
|
|
33148
|
-
}
|
|
33149
|
-
async read() {
|
|
33150
|
-
const items = (await this.parseResult_).items;
|
|
33151
|
-
// no query defined: return the full results as is
|
|
33152
|
-
if (this.groupedBy == null &&
|
|
33153
|
-
this.aggregations == null &&
|
|
33154
|
-
this.selected == null &&
|
|
33155
|
-
this.sort == null &&
|
|
33156
|
-
this.filter == null &&
|
|
33157
|
-
this.startIndex == null &&
|
|
33158
|
-
this.count == null) {
|
|
33159
|
-
return items;
|
|
33160
|
-
}
|
|
33161
|
-
const jsonItems = getJsonDataItemsProxy(items);
|
|
33162
|
-
const query = generateSqlQuery(this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations);
|
|
33163
|
-
const result = await import('alasql').then((module) => module.default(query, [jsonItems]));
|
|
33164
|
-
return result.map(jsonToGeojsonFeature);
|
|
33165
|
-
}
|
|
33166
|
-
}
|
|
33167
|
-
|
|
33168
|
-
function parseCsv(text) {
|
|
33169
|
-
// first parse the header to guess the delimiter
|
|
33170
|
-
// note that we do that to not rely on Papaparse logic for guessing delimiter
|
|
33171
|
-
let delimiter;
|
|
33172
|
-
try {
|
|
33173
|
-
const header = text.split('\n')[0];
|
|
33174
|
-
const result = Papa.parse(header, {
|
|
33175
|
-
header: false,
|
|
33176
|
-
});
|
|
33177
|
-
delimiter = result.meta.delimiter;
|
|
33178
|
-
}
|
|
33179
|
-
catch (e) {
|
|
33180
|
-
throw new Error('CSV parsing failed: the delimiter could not be guessed');
|
|
33181
|
-
}
|
|
33182
|
-
const parsed = Papa.parse(text, {
|
|
33183
|
-
header: true,
|
|
33184
|
-
skipEmptyLines: true,
|
|
33185
|
-
delimiter,
|
|
33186
|
-
});
|
|
33187
|
-
if (parsed.errors.length) {
|
|
33188
|
-
throw new Error('CSV parsing failed for the following reasons:\n' +
|
|
33189
|
-
parsed.errors
|
|
33190
|
-
.map((error) => `* ${error.message} at row ${error.row}, column ${error.index}`)
|
|
33191
|
-
.join('\n'));
|
|
33192
|
-
}
|
|
33193
|
-
const items = parsed.data.map(jsonToGeojsonFeature);
|
|
33194
|
-
return processItemProperties(items, true);
|
|
33195
|
-
}
|
|
33196
|
-
class CsvReader extends BaseFileReader {
|
|
33197
|
-
getData() {
|
|
33198
|
-
return fetchDataAsText(this.url, this.cacheActive).then(parseCsv);
|
|
33708
|
+
const duckDbFn = isXls ? 'read_json' : 'read_xlsx';
|
|
33709
|
+
await this.engine.registerData(bufferHandle, new Uint8Array(buffer));
|
|
33710
|
+
return `
|
|
33711
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM ${duckDbFn}("${bufferHandle}", ignore_errors = true);`;
|
|
33199
33712
|
}
|
|
33200
33713
|
}
|
|
33201
33714
|
|
|
33202
|
-
|
|
33203
|
-
|
|
33204
|
-
|
|
33205
|
-
|
|
33206
|
-
|
|
33207
|
-
|
|
33208
|
-
if (!Array.isArray(parsed)) {
|
|
33209
|
-
throw new Error('Could not parse JSON, expected an array at root level');
|
|
33210
|
-
}
|
|
33211
|
-
return processItemProperties(parsed.map(jsonToGeojsonFeature));
|
|
33212
|
-
}
|
|
33213
|
-
class JsonReader extends BaseFileReader {
|
|
33214
|
-
getData() {
|
|
33215
|
-
return fetchDataAsText(this.url, this.cacheActive).then(parseJson);
|
|
33715
|
+
class GmlReader extends BaseFileReader {
|
|
33716
|
+
async getLoadQuery() {
|
|
33717
|
+
return `
|
|
33718
|
+
CREATE TABLE ${this.datasetId} AS SELECT * FROM st_read("${this.url}",
|
|
33719
|
+
allowed_drivers = ['GML']
|
|
33720
|
+
);`;
|
|
33216
33721
|
}
|
|
33217
33722
|
}
|
|
33218
33723
|
|
|
33219
|
-
|
|
33220
|
-
* This parser supports both Geojson Feature collections or arrays
|
|
33221
|
-
* of Features
|
|
33222
|
-
* @param text
|
|
33223
|
-
*/
|
|
33724
|
+
const formatGeojson = new GeoJSON$1();
|
|
33224
33725
|
function parseGeojson(text) {
|
|
33225
33726
|
const parsed = JSON.parse(text);
|
|
33226
33727
|
const features = parsed.type === 'FeatureCollection' ? parsed.features : parsed;
|
|
33227
33728
|
if (!Array.isArray(features)) {
|
|
33228
33729
|
throw new Error('Could not parse GeoJSON, expected a features collection or an array of features at root level');
|
|
33229
33730
|
}
|
|
33230
|
-
return
|
|
33231
|
-
}
|
|
33232
|
-
class GeojsonReader extends BaseFileReader {
|
|
33233
|
-
getData() {
|
|
33234
|
-
return fetchDataAsText(this.url, this.cacheActive).then(parseGeojson);
|
|
33235
|
-
}
|
|
33236
|
-
}
|
|
33237
|
-
|
|
33238
|
-
/**
|
|
33239
|
-
* This will read the first sheet of the excel workbook and expect the first
|
|
33240
|
-
* line to contain the properties names
|
|
33241
|
-
* @param buffer
|
|
33242
|
-
*/
|
|
33243
|
-
function parseExcel(buffer) {
|
|
33244
|
-
return import('xlsx').then(({ read, utils }) => {
|
|
33245
|
-
const workbook = read(buffer);
|
|
33246
|
-
const sheet = workbook.Sheets[workbook.SheetNames[0]];
|
|
33247
|
-
let json = utils.sheet_to_json(sheet);
|
|
33248
|
-
if (!json.length) {
|
|
33249
|
-
json = [];
|
|
33250
|
-
}
|
|
33251
|
-
return processItemProperties(json.map(jsonToGeojsonFeature), true);
|
|
33252
|
-
});
|
|
33253
|
-
}
|
|
33254
|
-
class ExcelReader extends BaseFileReader {
|
|
33255
|
-
getData() {
|
|
33256
|
-
return fetchDataAsArrayBuffer(this.url, this.cacheActive).then(parseExcel);
|
|
33257
|
-
}
|
|
33731
|
+
return features;
|
|
33258
33732
|
}
|
|
33259
|
-
|
|
33260
|
-
|
|
33261
|
-
const
|
|
33262
|
-
const regex = new RegExp(`xmlns:${splittedNamespace[0]}=["']([^'"]*)["']`);
|
|
33733
|
+
function parseGml(text, featureType, version) {
|
|
33734
|
+
const parts = featureType.split(':');
|
|
33735
|
+
const regex = new RegExp(`xmlns:${parts[0]}=["']([^'"]*)["']`);
|
|
33263
33736
|
const match = regex.exec(text);
|
|
33264
33737
|
if (match && match.length >= 2) {
|
|
33265
|
-
const
|
|
33738
|
+
const wfs = new WFS({
|
|
33266
33739
|
featureNS: match[1],
|
|
33267
|
-
featureType:
|
|
33740
|
+
featureType: parts[1],
|
|
33268
33741
|
version: version,
|
|
33269
33742
|
});
|
|
33270
33743
|
let features;
|
|
33271
33744
|
try {
|
|
33272
|
-
features =
|
|
33745
|
+
features = wfs.readFeatures(text);
|
|
33273
33746
|
}
|
|
33274
33747
|
catch (e) {
|
|
33275
|
-
throw Error(
|
|
33748
|
+
throw Error(`Couldn't parse WFS with GML features: ${e.message}`);
|
|
33276
33749
|
}
|
|
33277
|
-
const geojsonItem =
|
|
33278
|
-
return
|
|
33750
|
+
const geojsonItem = formatGeojson.writeFeaturesObject(features);
|
|
33751
|
+
return geojsonItem.features;
|
|
33279
33752
|
}
|
|
33280
33753
|
throw Error("Couldn't retrieve namespace url");
|
|
33281
33754
|
}
|
|
33282
|
-
class GmlReader extends BaseFileReader {
|
|
33283
|
-
constructor(url, namespace, version, cacheActive = true) {
|
|
33284
|
-
super(url);
|
|
33285
|
-
this.url = url;
|
|
33286
|
-
this.namespace = namespace;
|
|
33287
|
-
this.version = version;
|
|
33288
|
-
this.cacheActive = cacheActive;
|
|
33289
|
-
}
|
|
33290
|
-
getData() {
|
|
33291
|
-
return fetchDataAsText(this.url, this.cacheActive).then((text) => parseGml(text, this.namespace, this.version));
|
|
33292
|
-
}
|
|
33293
|
-
}
|
|
33294
|
-
|
|
33295
33755
|
async function getWfsEndpoint(wfsUrl) {
|
|
33296
33756
|
try {
|
|
33297
33757
|
return await new WfsEndpoint(wfsUrl).isReady();
|
|
@@ -33319,21 +33779,65 @@ async function getWfsEndpoint(wfsUrl) {
|
|
|
33319
33779
|
}
|
|
33320
33780
|
}
|
|
33321
33781
|
}
|
|
33322
|
-
class WfsReader extends
|
|
33323
|
-
constructor(url,
|
|
33324
|
-
super(url
|
|
33325
|
-
this.endpoint =
|
|
33326
|
-
this.
|
|
33327
|
-
|
|
33782
|
+
class WfsReader extends BaseReader {
|
|
33783
|
+
constructor(url, featureTypeName) {
|
|
33784
|
+
super(url);
|
|
33785
|
+
this.endpoint = getWfsEndpoint(url);
|
|
33786
|
+
this.featureType = this.endpoint
|
|
33787
|
+
.then((endpoint) => {
|
|
33788
|
+
const featureTypes = endpoint.getFeatureTypes();
|
|
33789
|
+
return endpoint.getFeatureTypeFull(featureTypes.length === 1 && !featureTypeName
|
|
33790
|
+
? featureTypes[0].name
|
|
33791
|
+
: featureTypeName);
|
|
33792
|
+
})
|
|
33793
|
+
.then((featureType) => {
|
|
33794
|
+
if (!featureType) {
|
|
33795
|
+
throw new Error('wfs.featuretype.notfound');
|
|
33796
|
+
}
|
|
33797
|
+
return featureType;
|
|
33798
|
+
});
|
|
33799
|
+
}
|
|
33800
|
+
get backupReader() {
|
|
33801
|
+
if (this.backupReader_) {
|
|
33802
|
+
return this.backupReader_;
|
|
33803
|
+
}
|
|
33804
|
+
this.backupReader_ = Promise.all([this.endpoint, this.featureType]).then(([endpoint, featureType]) => {
|
|
33805
|
+
let reader;
|
|
33806
|
+
if (endpoint.supportsJson(featureType.name)) {
|
|
33807
|
+
reader = new GeojsonReader(endpoint.getFeatureUrl(featureType.name, {
|
|
33808
|
+
asJson: true,
|
|
33809
|
+
outputCrs: 'EPSG:4326',
|
|
33810
|
+
}));
|
|
33811
|
+
}
|
|
33812
|
+
else {
|
|
33813
|
+
if (featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')) &&
|
|
33814
|
+
(featureType.defaultCrs === 'EPSG:4326' ||
|
|
33815
|
+
featureType.otherCrs?.includes('EPSG:4326'))) {
|
|
33816
|
+
reader = new GmlReader(endpoint.getFeatureUrl(featureType.name, {
|
|
33817
|
+
outputFormat: featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')),
|
|
33818
|
+
outputCrs: 'EPSG:4326',
|
|
33819
|
+
}));
|
|
33820
|
+
}
|
|
33821
|
+
}
|
|
33822
|
+
reader.enableCache(this.cacheEnabled);
|
|
33823
|
+
reader.load();
|
|
33824
|
+
return reader;
|
|
33825
|
+
});
|
|
33826
|
+
return this.backupReader_;
|
|
33328
33827
|
}
|
|
33329
33828
|
get properties() {
|
|
33330
|
-
return this.
|
|
33331
|
-
.getFeatureTypeFull(this.featureTypeName)
|
|
33332
|
-
.then((featureType) => Object.keys(featureType.properties).map((prop) => {
|
|
33829
|
+
return this.featureType.then((featureType) => Object.keys(featureType.properties).map((prop) => {
|
|
33333
33830
|
const originalType = featureType.properties[prop];
|
|
33334
|
-
|
|
33335
|
-
|
|
33336
|
-
|
|
33831
|
+
let type;
|
|
33832
|
+
if (originalType === 'float' || originalType === 'integer') {
|
|
33833
|
+
type = 'number';
|
|
33834
|
+
}
|
|
33835
|
+
else if (originalType === 'boolean') {
|
|
33836
|
+
type = 'string'; // we don't handle booleans yet in the data fetcher
|
|
33837
|
+
}
|
|
33838
|
+
else {
|
|
33839
|
+
type = originalType;
|
|
33840
|
+
}
|
|
33337
33841
|
return {
|
|
33338
33842
|
name: prop,
|
|
33339
33843
|
label: prop,
|
|
@@ -33342,83 +33846,63 @@ class WfsReader extends BaseCacheReader {
|
|
|
33342
33846
|
}));
|
|
33343
33847
|
}
|
|
33344
33848
|
get info() {
|
|
33345
|
-
return this.
|
|
33849
|
+
return this.featureType.then((result) => ({
|
|
33346
33850
|
itemsCount: result.objectCount,
|
|
33851
|
+
hasGeometry: !!result.geometryName,
|
|
33347
33852
|
}));
|
|
33348
33853
|
}
|
|
33349
|
-
|
|
33350
|
-
|
|
33351
|
-
const featureTypes = wfsEndpoint.getFeatureTypes();
|
|
33352
|
-
const featureType = wfsEndpoint.getFeatureTypeSummary(featureTypes.length === 1 && !featureTypeName
|
|
33353
|
-
? featureTypes[0].name
|
|
33354
|
-
: featureTypeName);
|
|
33355
|
-
if (!featureType) {
|
|
33356
|
-
throw new Error('wfs.featuretype.notfound');
|
|
33357
|
-
}
|
|
33358
|
-
if (wfsEndpoint.supportsStartIndex()) {
|
|
33359
|
-
return new WfsReader(wfsUrlEndpoint, wfsEndpoint, featureType.name);
|
|
33360
|
-
}
|
|
33361
|
-
else if (wfsEndpoint.supportsJson(featureType.name)) {
|
|
33362
|
-
return new GeojsonReader(wfsEndpoint.getFeatureUrl(featureType.name, {
|
|
33363
|
-
asJson: true,
|
|
33364
|
-
outputCrs: 'EPSG:4326',
|
|
33365
|
-
}));
|
|
33366
|
-
}
|
|
33367
|
-
else {
|
|
33368
|
-
if (featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')) &&
|
|
33369
|
-
(featureType.defaultCrs === 'EPSG:4326' ||
|
|
33370
|
-
featureType.otherCrs?.includes('EPSG:4326'))) {
|
|
33371
|
-
return new GmlReader(wfsEndpoint.getFeatureUrl(featureType.name, {
|
|
33372
|
-
outputFormat: featureType.outputFormats.find((f) => f.toLowerCase().includes('gml')),
|
|
33373
|
-
outputCrs: 'EPSG:4326',
|
|
33374
|
-
}), featureType.name, wfsEndpoint.getVersion());
|
|
33375
|
-
}
|
|
33376
|
-
throw new Error('wfs.geojsongml.notsupported');
|
|
33377
|
-
}
|
|
33854
|
+
load() {
|
|
33855
|
+
// Nothing to load for Wfs
|
|
33378
33856
|
}
|
|
33379
|
-
async
|
|
33380
|
-
|
|
33381
|
-
|
|
33857
|
+
async read() {
|
|
33858
|
+
const endpoint = await this.endpoint;
|
|
33859
|
+
const featureType = await this.featureType;
|
|
33860
|
+
// if we can't use the WFS protocol we fall back to the backup reader
|
|
33861
|
+
if (this.aggregations || this.groupedBy || !endpoint.supportsStartIndex()) {
|
|
33862
|
+
const backupReader = await this.backupReader;
|
|
33863
|
+
backupReader.selectAll();
|
|
33864
|
+
if (this.selected) {
|
|
33865
|
+
backupReader.select(...this.selected);
|
|
33866
|
+
}
|
|
33867
|
+
if (this.aggregations) {
|
|
33868
|
+
backupReader.aggregate(...this.aggregations);
|
|
33869
|
+
}
|
|
33870
|
+
if (this.groupedBy) {
|
|
33871
|
+
backupReader.groupBy(...this.groupedBy);
|
|
33872
|
+
}
|
|
33873
|
+
if (this.sort) {
|
|
33874
|
+
backupReader.orderBy(...this.sort);
|
|
33875
|
+
}
|
|
33876
|
+
if (this.startIndex !== null && this.count !== null) {
|
|
33877
|
+
backupReader.limit(this.startIndex, this.count);
|
|
33878
|
+
}
|
|
33879
|
+
return backupReader.read();
|
|
33382
33880
|
}
|
|
33383
|
-
const asJson =
|
|
33881
|
+
const asJson = endpoint.supportsJson(featureType.name);
|
|
33384
33882
|
const attributes = this.selected ?? undefined;
|
|
33385
|
-
let
|
|
33883
|
+
let sortBy = null;
|
|
33884
|
+
if (this.sort) {
|
|
33885
|
+
const mapSort = (s) => [s[0] === 'desc' ? 'D' : 'A', s[1]];
|
|
33886
|
+
sortBy = Array.isArray(this.sort[0])
|
|
33887
|
+
? this.sort.map(mapSort)
|
|
33888
|
+
: mapSort(this.sort);
|
|
33889
|
+
}
|
|
33890
|
+
const url = endpoint.getFeatureUrl(featureType.name, {
|
|
33386
33891
|
...(this.startIndex !== null && { startIndex: this.startIndex }),
|
|
33387
33892
|
...(this.count !== null && { maxFeatures: this.count }),
|
|
33388
33893
|
asJson,
|
|
33389
33894
|
outputCrs: 'EPSG:4326',
|
|
33390
33895
|
attributes,
|
|
33391
|
-
|
|
33896
|
+
sortBy,
|
|
33392
33897
|
});
|
|
33393
|
-
|
|
33394
|
-
const finalUrl = new URL(url);
|
|
33395
|
-
const sorts = this.sort
|
|
33396
|
-
.map((fieldSort) => `${fieldSort[1]}+${fieldSort[0] === 'asc' ? 'A' : 'D'}`)
|
|
33397
|
-
.join(',');
|
|
33398
|
-
// Direct update on string url to prevent encoding of +A and +D
|
|
33399
|
-
url = `${url}${finalUrl.search ? '&' : ''}SORTBY=${sorts}`;
|
|
33400
|
-
}
|
|
33401
|
-
return fetchDataAsText(url, this.cacheActive).then((text) => asJson
|
|
33898
|
+
return fetchDataAsText(url, this.cacheEnabled).then((text) => asJson
|
|
33402
33899
|
? parseGeojson(text)
|
|
33403
|
-
: parseGml(text,
|
|
33404
|
-
}
|
|
33405
|
-
async getQueryData() {
|
|
33406
|
-
const items = (await this.getData()).items;
|
|
33407
|
-
const jsonItems = getJsonDataItemsProxy(items);
|
|
33408
|
-
const query = generateSqlQuery(this.selected, this.filter, this.sort, this.startIndex, this.count, this.groupedBy, this.aggregations);
|
|
33409
|
-
const result = await import('alasql').then((module) => module.default(query, [jsonItems]));
|
|
33410
|
-
return result.map(jsonToGeojsonFeature);
|
|
33411
|
-
}
|
|
33412
|
-
load() {
|
|
33413
|
-
// Nothing to load for Wfs
|
|
33414
|
-
}
|
|
33415
|
-
async read() {
|
|
33416
|
-
return (await this.getData(this.aggregations, this.groupedBy)).items;
|
|
33900
|
+
: parseGml(text, featureType.name, endpoint.getVersion()));
|
|
33417
33901
|
}
|
|
33418
33902
|
}
|
|
33419
33903
|
|
|
33420
|
-
async function openDataset(url,
|
|
33421
|
-
const fileType = await inferDatasetType(url, typeHint);
|
|
33904
|
+
async function openDataset(url, options) {
|
|
33905
|
+
const fileType = await inferDatasetType(url, options?.typeHint);
|
|
33422
33906
|
let reader;
|
|
33423
33907
|
try {
|
|
33424
33908
|
switch (fileType) {
|
|
@@ -33435,18 +33919,18 @@ async function openDataset(url, typeHint, options, cacheActive) {
|
|
|
33435
33919
|
reader = new ExcelReader(url);
|
|
33436
33920
|
break;
|
|
33437
33921
|
case 'gml':
|
|
33438
|
-
reader = new GmlReader(url
|
|
33922
|
+
reader = new GmlReader(url);
|
|
33439
33923
|
break;
|
|
33440
33924
|
case 'wfs':
|
|
33441
|
-
reader =
|
|
33925
|
+
reader = new WfsReader(url, options?.wfsFeatureType);
|
|
33442
33926
|
break;
|
|
33443
33927
|
}
|
|
33444
|
-
reader.
|
|
33928
|
+
reader.enableCache(options?.enableCache ?? true);
|
|
33445
33929
|
reader.load();
|
|
33446
33930
|
return reader;
|
|
33447
33931
|
}
|
|
33448
33932
|
catch (e) {
|
|
33449
|
-
//WfsReader may already raise a FetchError
|
|
33933
|
+
// WfsReader may already raise a FetchError
|
|
33450
33934
|
if (e instanceof FetchError)
|
|
33451
33935
|
throw e;
|
|
33452
33936
|
else
|
|
@@ -33457,13 +33941,13 @@ async function openDataset(url, typeHint, options, cacheActive) {
|
|
|
33457
33941
|
* This fetches the full dataset at the given URL and parses it according to its mime type.
|
|
33458
33942
|
* All items in the dataset are converted to GeoJSON features, even if they do not bear any spatial geometry.
|
|
33459
33943
|
* File type can be either inferred (from the HTTP headers or the URL), or hinted using the 2nd argument
|
|
33460
|
-
* File type is determined
|
|
33944
|
+
* File type is determined like so:
|
|
33461
33945
|
* 1. if a type hint is given, use it
|
|
33462
33946
|
* 2. otherwise, look for a Content-Type header in the response with a supported mime type
|
|
33463
33947
|
* 3. if no valid mime type was found, look for an explicit file extension in the url (.csv, .geojson etc.)
|
|
33464
33948
|
*/
|
|
33465
|
-
async function readDataset(url,
|
|
33466
|
-
const reader = await openDataset(url,
|
|
33949
|
+
async function readDataset(url, options) {
|
|
33950
|
+
const reader = await openDataset(url, options);
|
|
33467
33951
|
try {
|
|
33468
33952
|
return await reader.read();
|
|
33469
33953
|
}
|
|
@@ -33694,9 +34178,11 @@ class DataService {
|
|
|
33694
34178
|
getDataset(link, cacheActive) {
|
|
33695
34179
|
if (link.type === 'service' && link.accessServiceProtocol === 'wfs') {
|
|
33696
34180
|
const wfsUrlEndpoint = this.proxy.getProxiedUrl(link.url.toString());
|
|
33697
|
-
return from(openDataset(wfsUrlEndpoint,
|
|
34181
|
+
return from(openDataset(wfsUrlEndpoint, {
|
|
34182
|
+
typeHint: 'wfs',
|
|
33698
34183
|
wfsFeatureType: link.name,
|
|
33699
|
-
|
|
34184
|
+
enableCache: cacheActive,
|
|
34185
|
+
}));
|
|
33700
34186
|
}
|
|
33701
34187
|
else if (link.type === 'download') {
|
|
33702
34188
|
const linkProxifiedUrl = this.proxy.getProxiedUrl(link.url.toString());
|
|
@@ -33704,12 +34190,15 @@ class DataService {
|
|
|
33704
34190
|
const supportedType = SupportedTypes.indexOf(format) > -1
|
|
33705
34191
|
? format
|
|
33706
34192
|
: undefined;
|
|
33707
|
-
return from(openDataset(linkProxifiedUrl,
|
|
34193
|
+
return from(openDataset(linkProxifiedUrl, {
|
|
34194
|
+
typeHint: supportedType,
|
|
34195
|
+
enableCache: cacheActive,
|
|
34196
|
+
})).pipe();
|
|
33708
34197
|
}
|
|
33709
34198
|
else if (link.type === 'service' &&
|
|
33710
34199
|
link.accessServiceProtocol === 'esriRest') {
|
|
33711
34200
|
const url = this.getDownloadUrlFromEsriRest(link.url.toString(), 'geojson');
|
|
33712
|
-
return from(openDataset(url, 'geojson',
|
|
34201
|
+
return from(openDataset(url, { typeHint: 'geojson', enableCache: cacheActive })).pipe();
|
|
33713
34202
|
}
|
|
33714
34203
|
else if (link.type === 'service' &&
|
|
33715
34204
|
link.accessServiceProtocol === 'ogcFeatures') {
|
|
@@ -33723,7 +34212,10 @@ class DataService {
|
|
|
33723
34212
|
}
|
|
33724
34213
|
const urlWithoutLimit = new URL(geojsonUrl);
|
|
33725
34214
|
urlWithoutLimit.searchParams.delete('limit');
|
|
33726
|
-
return openDataset(urlWithoutLimit.toString(),
|
|
34215
|
+
return openDataset(urlWithoutLimit.toString(), {
|
|
34216
|
+
typeHint: 'geojson',
|
|
34217
|
+
enableCache: cacheActive,
|
|
34218
|
+
});
|
|
33727
34219
|
}));
|
|
33728
34220
|
}
|
|
33729
34221
|
return throwError(() => 'protocol not supported');
|
|
@@ -33975,20 +34467,31 @@ class DataTableComponent {
|
|
|
33975
34467
|
this.eltRef = inject(ElementRef);
|
|
33976
34468
|
this.cdr = inject(ChangeDetectorRef);
|
|
33977
34469
|
this.translateService = inject(TranslateService);
|
|
33978
|
-
this.
|
|
34470
|
+
this.columnsFromFeatureCatalog = null;
|
|
34471
|
+
this.columnsFromDataset = [];
|
|
33979
34472
|
this.selected = new EventEmitter();
|
|
33980
|
-
this.properties$ = new BehaviorSubject(null);
|
|
33981
34473
|
this.loading$ = new BehaviorSubject(false);
|
|
33982
34474
|
this.error = null;
|
|
33983
34475
|
}
|
|
33984
34476
|
set featureAttributes(value) {
|
|
33985
|
-
this.
|
|
33986
|
-
|
|
34477
|
+
this.columnsFromFeatureCatalog = value.map((attrs) => ({
|
|
34478
|
+
name: attrs.value,
|
|
34479
|
+
label: attrs.label,
|
|
34480
|
+
}));
|
|
33987
34481
|
}
|
|
33988
34482
|
set dataset(value) {
|
|
33989
34483
|
this.dataset_ = value;
|
|
33990
34484
|
this.dataset_.load();
|
|
33991
|
-
this.dataset_.info.then((info) =>
|
|
34485
|
+
this.dataset_.info.then((info) => {
|
|
34486
|
+
this.count = info.itemsCount;
|
|
34487
|
+
this.cdr.detectChanges();
|
|
34488
|
+
});
|
|
34489
|
+
}
|
|
34490
|
+
get columns() {
|
|
34491
|
+
return this.columnsFromFeatureCatalog ?? this.columnsFromDataset;
|
|
34492
|
+
}
|
|
34493
|
+
get columnNames() {
|
|
34494
|
+
return this.columns.map((c) => c.name);
|
|
33992
34495
|
}
|
|
33993
34496
|
ngOnInit() {
|
|
33994
34497
|
this.dataSource = new DataTableDataSource();
|
|
@@ -34023,11 +34526,12 @@ class DataTableComponent {
|
|
|
34023
34526
|
}
|
|
34024
34527
|
async readData() {
|
|
34025
34528
|
this.loading$.next(true);
|
|
34026
|
-
// wait for properties to be read
|
|
34027
|
-
const properties = await firstValueFrom(this.properties$.pipe(filter((p) => !!p)));
|
|
34028
|
-
const propsWithoutGeom = properties.filter((p) => !p.toLowerCase().startsWith('geom'));
|
|
34029
|
-
this.dataset_.select(...propsWithoutGeom);
|
|
34030
34529
|
try {
|
|
34530
|
+
// wait for properties to be read
|
|
34531
|
+
if (!this.columnsFromFeatureCatalog) {
|
|
34532
|
+
this.columnsFromDataset = await this.dataset_.properties;
|
|
34533
|
+
}
|
|
34534
|
+
this.dataset_.select(...this.columnNames);
|
|
34031
34535
|
await this.dataSource.showData(this.dataset_.read());
|
|
34032
34536
|
this.error = null;
|
|
34033
34537
|
}
|
|
@@ -34057,7 +34561,7 @@ class DataTableComponent {
|
|
|
34057
34561
|
}
|
|
34058
34562
|
}
|
|
34059
34563
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DataTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
34060
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DataTableComponent, isStandalone: true, selector: "gn-ui-data-table", inputs: { featureAttributes: "featureAttributes", dataset: "dataset", activeId: "activeId" }, outputs: { selected: "selected" }, providers: [{ provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl }], viewQueries: [{ propertyName: "sort", first: true, predicate: MatSort, descendants: true }, { propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col border border-gray-300 rounded-lg bg-white h-full\">\n <div class=\"flex-1 overflow-y-hidden overflow-x-auto rounded-lg relative\">\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"setSort($event)\"\n [matSortDisableClear]=\"true\"\n
|
|
34564
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: DataTableComponent, isStandalone: true, selector: "gn-ui-data-table", inputs: { featureAttributes: "featureAttributes", dataset: "dataset", activeId: "activeId" }, outputs: { selected: "selected" }, providers: [{ provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl }], viewQueries: [{ propertyName: "sort", first: true, predicate: MatSort, descendants: true }, { propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"flex flex-col border border-gray-300 rounded-lg bg-white h-full\">\n <div class=\"flex-1 overflow-y-hidden overflow-x-auto rounded-lg relative\">\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"setSort($event)\"\n [matSortDisableClear]=\"true\"\n >\n @for (column of columns; track column) {\n <ng-container [matColumnDef]=\"column.name\">\n <th\n mat-header-cell\n *matHeaderCellDef\n mat-sort-header\n class=\"text-sm text-black bg-white\"\n >\n {{ column.label }}\n </th>\n <td\n mat-cell\n *matCellDef=\"let element\"\n class=\"whitespace-nowrap pr-1 truncate\"\n >\n {{ element[column.name] }}\n </td>\n </ng-container>\n }\n\n <tr mat-header-row *matHeaderRowDef=\"columnNames; sticky: true\"></tr>\n <tr\n [id]=\"getRowEltId(row.id)\"\n mat-row\n *matRowDef=\"let row; columns: columnNames\"\n (click)=\"selected.emit(row)\"\n [class.active]=\"\n activeId !== undefined && activeId !== null && row.id === activeId\n \"\n ></tr>\n </table>\n @if (loading$ | async) {\n <gn-ui-loading-mask\n class=\"sticky inset-0\"\n [message]=\"'table.loading.data' | translate\"\n ></gn-ui-loading-mask>\n }\n @if (error) {\n <gn-ui-popup-alert\n type=\"warning\"\n icon=\"matErrorOutlineOutline\"\n class=\"absolute m-2 inset-0 z-[100]\"\n >\n <span translate>{{ error }}</span>\n </gn-ui-popup-alert>\n }\n </div>\n <div class=\"flex justify-between items-center overflow-hidden\">\n <div class=\"text-gray-900 px-4 py-2 text-sm\">\n <span class=\"count font-extrabold text-primary\">{{ count }}</span\n > <span translate>table.object.count</span>.\n </div>\n\n <mat-paginator\n class=\"my-[-16px]\"\n (page)=\"setPagination()\"\n [length]=\"count\"\n [pageSize]=\"10\"\n [showFirstLastButtons]=\"true\"\n [hidePageSize]=\"true\"\n ></mat-paginator>\n </div>\n</div>\n", styles: ["table{width:100%;background:#fff}th.mat-mdc-header-cell,td.mat-mdc-cell,td.mat-mdc-footer-cell{padding-right:20px}tr.mat-mdc-row,tr.mat-mdc-footer-row{height:36px}tr:hover{background:#f5f5f5}tr.mat-mdc-header-row{height:48px}[mat-header-cell]{color:#0000008a;font-size:12px;font-weight:500}tr{cursor:pointer}.active .mat-mdc-cell{color:var(--color-primary)}.mat-mdc-paginator{background:none}\n"], dependencies: [{ kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i1$b.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i1$b.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i1$b.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i1$b.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i1$b.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i1$b.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i1$b.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i1$b.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i1$b.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i1$b.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i2$4.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i2$4.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: LoadingMaskComponent, selector: "gn-ui-loading-mask", inputs: ["message"] }, { kind: "component", type: PopupAlertComponent, selector: "gn-ui-popup-alert", inputs: ["icon", "type", "position"] }, { kind: "directive", type: TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
34061
34565
|
}
|
|
34062
34566
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: DataTableComponent, decorators: [{
|
|
34063
34567
|
type: Component,
|
|
@@ -34069,10 +34573,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
34069
34573
|
CommonModule,
|
|
34070
34574
|
LoadingMaskComponent,
|
|
34071
34575
|
PopupAlertComponent,
|
|
34072
|
-
LetDirective,
|
|
34073
34576
|
TranslatePipe,
|
|
34074
34577
|
TranslateDirective,
|
|
34075
|
-
], providers: [{ provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl }], selector: 'gn-ui-data-table', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex flex-col border border-gray-300 rounded-lg bg-white h-full\">\n <div class=\"flex-1 overflow-y-hidden overflow-x-auto rounded-lg relative\">\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"setSort($event)\"\n [matSortDisableClear]=\"true\"\n
|
|
34578
|
+
], providers: [{ provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl }], selector: 'gn-ui-data-table', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex flex-col border border-gray-300 rounded-lg bg-white h-full\">\n <div class=\"flex-1 overflow-y-hidden overflow-x-auto rounded-lg relative\">\n <table\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"setSort($event)\"\n [matSortDisableClear]=\"true\"\n >\n @for (column of columns; track column) {\n <ng-container [matColumnDef]=\"column.name\">\n <th\n mat-header-cell\n *matHeaderCellDef\n mat-sort-header\n class=\"text-sm text-black bg-white\"\n >\n {{ column.label }}\n </th>\n <td\n mat-cell\n *matCellDef=\"let element\"\n class=\"whitespace-nowrap pr-1 truncate\"\n >\n {{ element[column.name] }}\n </td>\n </ng-container>\n }\n\n <tr mat-header-row *matHeaderRowDef=\"columnNames; sticky: true\"></tr>\n <tr\n [id]=\"getRowEltId(row.id)\"\n mat-row\n *matRowDef=\"let row; columns: columnNames\"\n (click)=\"selected.emit(row)\"\n [class.active]=\"\n activeId !== undefined && activeId !== null && row.id === activeId\n \"\n ></tr>\n </table>\n @if (loading$ | async) {\n <gn-ui-loading-mask\n class=\"sticky inset-0\"\n [message]=\"'table.loading.data' | translate\"\n ></gn-ui-loading-mask>\n }\n @if (error) {\n <gn-ui-popup-alert\n type=\"warning\"\n icon=\"matErrorOutlineOutline\"\n class=\"absolute m-2 inset-0 z-[100]\"\n >\n <span translate>{{ error }}</span>\n </gn-ui-popup-alert>\n }\n </div>\n <div class=\"flex justify-between items-center overflow-hidden\">\n <div class=\"text-gray-900 px-4 py-2 text-sm\">\n <span class=\"count font-extrabold text-primary\">{{ count }}</span\n > <span translate>table.object.count</span>.\n </div>\n\n <mat-paginator\n class=\"my-[-16px]\"\n (page)=\"setPagination()\"\n [length]=\"count\"\n [pageSize]=\"10\"\n [showFirstLastButtons]=\"true\"\n [hidePageSize]=\"true\"\n ></mat-paginator>\n </div>\n</div>\n", styles: ["table{width:100%;background:#fff}th.mat-mdc-header-cell,td.mat-mdc-cell,td.mat-mdc-footer-cell{padding-right:20px}tr.mat-mdc-row,tr.mat-mdc-footer-row{height:36px}tr:hover{background:#f5f5f5}tr.mat-mdc-header-row{height:48px}[mat-header-cell]{color:#0000008a;font-size:12px;font-weight:500}tr{cursor:pointer}.active .mat-mdc-cell{color:var(--color-primary)}.mat-mdc-paginator{background:none}\n"] }]
|
|
34076
34579
|
}], propDecorators: { featureAttributes: [{
|
|
34077
34580
|
type: Input
|
|
34078
34581
|
}], dataset: [{
|
|
@@ -34175,7 +34678,7 @@ class ChartViewComponent {
|
|
|
34175
34678
|
}), shareReplay$1(1));
|
|
34176
34679
|
this.properties$ = combineLatest([this.dataset$, this.featureCatalog$]).pipe(switchMap$1(([dataset, catalog]) => this.setProperties(dataset, catalog)), shareReplay$1(1));
|
|
34177
34680
|
this.yChoices$ = this.properties$.pipe(map$1((properties) => properties
|
|
34178
|
-
.filter((prop) => prop.type === 'number'
|
|
34681
|
+
.filter((prop) => prop.type === 'number')
|
|
34179
34682
|
.map((prop) => ({ value: prop.name, label: prop.label || prop.name }))), tap$1((choices) => {
|
|
34180
34683
|
if (!choices.find((choice) => choice.value === this.yProperty$.value)) {
|
|
34181
34684
|
const newProp = choices[0]?.value || '';
|
|
@@ -34202,6 +34705,7 @@ class ChartViewComponent {
|
|
|
34202
34705
|
this.aggregation$,
|
|
34203
34706
|
]).pipe(filter$1(([_, x, y]) => !!x || !!y), switchMap$1(([dataset, xProp, yProp, aggregation]) => {
|
|
34204
34707
|
const fieldAgg = aggregation === 'count' ? ['count'] : [aggregation, yProp];
|
|
34708
|
+
this.loading = true;
|
|
34205
34709
|
return dataset
|
|
34206
34710
|
.groupBy(['distinct', xProp])
|
|
34207
34711
|
.aggregate(fieldAgg)
|
|
@@ -34443,6 +34947,7 @@ class GeoTableViewComponent {
|
|
|
34443
34947
|
}));
|
|
34444
34948
|
}
|
|
34445
34949
|
async initMapContext() {
|
|
34950
|
+
this.dataset.load();
|
|
34446
34951
|
this.dataset.selectAll();
|
|
34447
34952
|
return {
|
|
34448
34953
|
layers: [
|
|
@@ -34616,8 +35121,7 @@ class MdViewFacade {
|
|
|
34616
35121
|
}));
|
|
34617
35122
|
this.error$ = this.store.pipe(select(getMetadataError));
|
|
34618
35123
|
this.related$ = this.store.pipe(select(getRelated));
|
|
34619
|
-
this.
|
|
34620
|
-
this.sourceOf$ = this.store.pipe(select(getSourceOf));
|
|
35124
|
+
this.linkedRecords$ = this.store.pipe(select(getLinkedRecords));
|
|
34621
35125
|
this.chartConfig$ = this.store.pipe(select(getChartConfig));
|
|
34622
35126
|
this.allLinks$ = this.metadata$.pipe(map$1((record) => 'onlineResources' in record ? record.onlineResources : []), shareReplay$1(1));
|
|
34623
35127
|
this.resourceDoi$ = this.metadata$.pipe(map$1((record) => {
|
|
@@ -34736,12 +35240,9 @@ class MdViewEffects {
|
|
|
34736
35240
|
this.loadRelatedRecords$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getSimilarRecords(full)), map$1((related) => {
|
|
34737
35241
|
return setRelated({ related });
|
|
34738
35242
|
}), catchError(() => of(setRelated({ related: null })))));
|
|
34739
|
-
this.
|
|
34740
|
-
return
|
|
34741
|
-
}), catchError(() => of(
|
|
34742
|
-
this.loadSourceOf$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getSourceOf(full)), map$1((sourceOf) => {
|
|
34743
|
-
return setSourceOf({ sourceOf });
|
|
34744
|
-
}), catchError(() => of(setSourceOf({ sourceOf: null })))));
|
|
35243
|
+
this.loadLinkedRecords$ = createEffect(() => this.actions$.pipe(ofType(loadFullMetadataSuccess), switchMap$1(({ full }) => this.recordsRepository.getLinkedRecords(full)), map$1((linkedRecords) => {
|
|
35244
|
+
return setLinkedRecords({ linkedRecords });
|
|
35245
|
+
}), catchError(() => of(setLinkedRecords({ linkedRecords: null })))));
|
|
34745
35246
|
/*
|
|
34746
35247
|
UserFeedback effects
|
|
34747
35248
|
*/
|
|
@@ -40020,7 +40521,7 @@ function flattenQueryParams(params) {
|
|
|
40020
40521
|
const start = flattened[key].start;
|
|
40021
40522
|
const end = flattened[key].end;
|
|
40022
40523
|
flattened[key] = [
|
|
40023
|
-
`${start ? formatDate(start) : ''}..${formatDate(end)
|
|
40524
|
+
`${start ? formatDate(start) : ''}..${end ? formatDate(end) : ''}`,
|
|
40024
40525
|
];
|
|
40025
40526
|
}
|
|
40026
40527
|
}
|
|
@@ -40377,5 +40878,5 @@ const CHART_TYPE_VALUES = [
|
|
|
40377
40878
|
* Generated bundle index. Do not edit.
|
|
40378
40879
|
*/
|
|
40379
40880
|
|
|
40380
|
-
export { ADD_RESULTS, ADD_SEARCH, AVAILABLE_LICENSES, AbstractAction, AbstractSearchField, ActionMenuComponent, AddLayerFromCatalogComponent, AddLayerRecordPreviewComponent, AddResults, AddSearch, AnchorLinkDirective, ApiCardComponent, ApplicationBannerComponent, AuthService, AutocompleteComponent, AvailableServicesField, AvatarComponent, AvatarServiceInterface, BASEMAP_LAYERS, BadgeComponent, BaseConverter, BaseFileReader, BaseReader, BlockListComponent, ButtonComponent, CHART_TYPE_VALUES, CLEAR_ERROR, CLEAR_RESULTS, CarouselComponent, CatalogTitleComponent, CellPopinComponent, ChartComponent, ChartViewComponent, CheckToggleComponent, CheckboxComponent, ClearError, ClearResults, ColorScaleComponent, ConfirmationDialogComponent, ContactDetailsComponent, ContactDetailsFormComponent, ContactPillComponent, ContentGhostComponent, CopyTextButtonComponent, DEFAULT_CONFIGURATION, DEFAULT_GN4_LOGIN_URL, DEFAULT_GN4_LOGOUT_URL, DEFAULT_GN4_SETTINGS_URL, DEFAULT_LANG, DEFAULT_PAGE_SIZE, DEFAULT_RECORD_CONVERTER, DEFAULT_RESULTS_LAYOUT_CONFIG, DEFAULT_SEARCH_KEY, DEFAULT_SPATIAL_EXTENT_STYLE, DISABLE_AUTH, DISABLE_DRAFT, DO_NOT_USE_DEFAULT_BASEMAP, DataService, DataTableComponent, DataViewComponent, DataViewPermalinkComponent, DataViewShareComponent, DataViewWebComponentComponent, DatePickerComponent, DateRangeDropdownComponent, DateRangeInputsComponent, DateRangePickerComponent, DateRangeSearchField, DateService, DcatApConverter, DefaultRouterModule, DownloadItemComponent, DownloadsListComponent, DragAndDropFileInputComponent, DropdownMultiselectComponent, DropdownSelectorComponent, EDITOR_FEATURE_KEY, ES_QUERY_FIELDS_PRIORITY, ES_RESOURCES_VALUES, ES_SOURCE_SUMMARY, EXTERNAL_VIEWER_OPEN_NEW_TAB, EXTERNAL_VIEWER_URL_TEMPLATE, EditableLabelDirective, EditorFacade, EditorService, ElasticsearchService, ErrorComponent, ErrorType, ExpandablePanelButtonComponent, ExpandablePanelComponent, ExternalLinkCardComponent, ExternalViewerButtonComponent, FIELDS_BRIEF, FIELDS_SUMMARY, FILTER_GEOMETRY, FILTER_SUMMARY_IGNORE_LIST, FORMATS, FacetBlockComponent, FacetItemComponent, FacetListComponent, FacetsContainerComponent, FavoriteStarComponent, FavoritesService, FeatureCatalogListComponent, FeatureDetailComponent, FeatureEditorModule, FeatureMapModule, FeatureRecordModule, FeatureSearchModule, FetchError, FieldFocusDirective, FieldsService, FigureComponent, FigureContainerComponent, FileInputComponent, FileTranslateLoader, FilesDropDirective, FilterDropdownComponent, FormFieldArrayComponent, FormFieldComponent, FormFieldDateComponent, FormFieldFileComponent, FormFieldKeywordsComponent, FormFieldLicenseComponent, FormFieldObjectComponent, FormFieldRichComponent, FormFieldSimpleComponent, FormFieldSpatialExtentComponent, FormFieldTemporalExtentsComponent, FormFieldTopicsComponent, FormFieldWrapperComponent, FullTextSearchField, FuzzySearchComponent, GEONETWORK_UI_TAG_NAME, GEONETWORK_UI_VERSION, GeoDataBadgeComponent, GeoTableViewComponent, GeocodingComponent, GeojsonReader, Gn4Converter, Gn4PlatformMapper, Gn4PlatformService, Gn4Repository, Gn4SettingsService, GnUiHumanizeDateDirective, GpfApiDlComponent, GravatarService, I18nInterceptor, ISO_TOPICS, ImageFallbackDirective, ImageInputComponent, ImageOverlayPreviewComponent, ImportRecordComponent, InlineFilterComponent, InteractiveTableColumnComponent, InteractiveTableComponent, InternalLinkCardComponent, IsSpatialSearchField, Iso191153Converter, Iso19139Converter, KeywordBadgeComponent, KindBadgeComponent, LANGUAGES_LIST, LANGUAGE_NAMES, LANGUAGE_STORAGE_KEY, LANG_2_TO_3_MAPPER, LOGIN_URL, LOGOUT_URL, LONLAT_CRS_CODES, LanguageSwitcherComponent, LayersPanelComponent, LicenseSearchField, LinkClassifierService, LinkUsage, LoadingMaskComponent, LogService, MAP_FEATURE_KEY, MAP_VIEW_CONSTRAINTS, MAX_UPLOAD_SIZE_MB, METADATA_LANGUAGE, MapContainerComponent, MapFacade, MapLegendComponent, MapStateContainerComponent, MapStyleService, MapUtilsService, MapViewComponent, MarkdownEditorComponent, MarkdownParserComponent, MaxLinesComponent, mdview_actions as MdViewActions, MdViewFacade, MetadataCatalogComponent, MetadataContactComponent, MetadataDoiComponent, MetadataInfoComponent, MetadataLinkType, MetadataMapperContext, MetadataQualityComponent, MetadataQualityItemComponent, MetadataQualityPanelComponent, ModalDialogComponent, MultilingualPanelComponent, MultilingualSearchField, MyOrgService, NAMESPACES, NOT_APPLICABLE_CONSTRAINT, NOT_KNOWN_CONSTRAINT, NotificationComponent, NotificationsContainerComponent, NotificationsService, OPEN_DATA_LICENSE, ORGANIZATIONS_STRATEGY, ORGANIZATION_PAGE_URL_TOKEN, ORGANIZATION_URL_TOKEN, OnlineResourceCardComponent, OnlineServiceResourceInputComponent, OrganisationPreviewComponent, OrganisationsComponent, OrganisationsFilterComponent, OrganisationsResultComponent, OrganizationSearchField, OrganizationsFromGroupsService, OrganizationsFromMetadataService, OrganizationsServiceInterface, OwnerSearchField, PAGINATE, PARSE_DELIMITER, PATCH_RESULTS_AGGREGATIONS, PROXY_PATH, Paginate, PaginationButtonsComponent, PaginationComponent, PaginationDotsComponent, PatchResultsAggregations, PlatformServiceInterface, PopoverComponent, PopupAlertComponent, PossibleResourceTypes, PossibleResourceTypesDefinition, PreviousNextButtonsComponent, ProgressBarComponent, ProxyService, QUERY_FIELDS, RECORD_DATASET_URL_TOKEN, RECORD_REUSE_URL_TOKEN, RECORD_SERVICE_URL_TOKEN, REQUEST_MORE_ON_AGGREGATION, REQUEST_MORE_RESULTS, REQUEST_NEW_RESULTS, RESULTS_LAYOUT_CONFIG, REUSE_LIGHT_CONFIGURATION, ROUTER_CONFIG, ROUTER_ROUTE_DATASET, ROUTER_ROUTE_ORGANIZATION, ROUTER_ROUTE_REUSE, ROUTER_ROUTE_SEARCH, ROUTER_ROUTE_SERVICE, ROUTER_STATE_KEY, ROUTE_PARAMS, RecordApiFormComponent, RecordFormComponent, RecordKindField, RecordMetaComponent, RecordMetricComponent, RecordPreviewCardComponent, RecordPreviewComponent, RecordPreviewFeedComponent, RecordPreviewListComponent, RecordPreviewRowComponent, RecordPreviewTextComponent, RecordPreviewTitleComponent, RecordStatusValues, RecordsMetricsComponent, RecordsRepositoryInterface, RecordsService, RequestMoreOnAggregation, RequestMoreResults, RequestNewResults, ResourceTypeLegacyField, ResultsHitsContainerComponent, ResultsHitsNumberComponent, ResultsHitsSearchKindComponent, ResultsLayoutComponent, ResultsLayoutConfigItem, ResultsListComponent, ResultsListContainerComponent, ResultsListItemComponent, ResultsTableComponent, ResultsTableContainerComponent, ReusePresentationForms, RoleLabels, RoleValues, RouterEffects, RouterFacade, RouterService, SEARCH_FEATURE_KEY, SETTINGS_URL, SET_CONFIG_AGGREGATIONS, SET_CONFIG_FILTERS, SET_CONFIG_REQUEST_FIELDS, SET_ERROR, SET_FAVORITES_ONLY, SET_FILTERS, SET_INCLUDE_ON_AGGREGATION, SET_PAGE_SIZE, SET_RESULTS_AGGREGATIONS, SET_RESULTS_HITS, SET_RESULTS_LAYOUT, SET_SEARCH, SET_SORT_BY, SET_SPATIAL_FILTER_ENABLED, SPATIAL_SCOPES, SearchEffects, SearchFacade, SearchFeatureCatalogComponent, SearchFiltersSummaryComponent, SearchFiltersSummaryItemComponent, SearchInputComponent, SearchRouterContainerDirective, SearchService, SearchStateContainerDirective, SelectionService, ServiceCapabilitiesComponent, SetConfigAggregations, SetConfigFilters, SetConfigRequestFields, SetError, SetFavoritesOnly, SetFilters, SetIncludeOnAggregation, SetPageSize, SetResultsAggregations, SetResultsHits, SetResultsLayout, SetSearch, SetSortBy, SetSpatialFilterEnabled, SimpleSearchField, SiteTitleComponent, SortByComponent, SortByEnum, SortableListComponent, SourceLabelComponent, SourcesService, SpatialExtentComponent, SpinningLoaderComponent, StacItemsResultGridComponent, StacViewComponent, StarToggleComponent, StickyHeaderComponent, SupportedTypes, SwitchToggleComponent, THUMBNAIL_PLACEHOLDER, TRANSLATE_DEBUG_CONFIG, TRANSLATE_DEFAULT_CONFIG, TRANSLATE_WITH_OVERRIDES_CONFIG, TableViewComponent, TextAreaComponent, TextInputComponent, ThemeService, ThumbnailComponent, TranslatedSearchField, TruncatedTextComponent, UPDATE_CONFIG_AGGREGATIONS, UPDATE_FILTERS, UPDATE_REQUEST_AGGREGATION_TERM, UpdateConfigAggregations, UpdateFilters, UrlInputComponent, UserFeedbackItemComponent, UserPreviewComponent, UserSearchField, VECTOR_STYLE_DEFAULT, ViewportIntersectorComponent, WEB_COMPONENT_EMBEDDER_URL, XmlParseError, _reset, allChildrenElement, appConfigWithTranslationFixture, appendChildTree, appendChildren, assertValidXml, associationTypeValues, bboxToPolygon, blockModelFixture, bytesToMegabytes, canEditRecord, checkFileFormat, clearSelectedFeatures, createChild, createDocument, createElement, createFuzzyFilter, createNestedChild, createNestedElement, createSpatialExtentLayer, currentPage, defaultMapStyleFixture, defaultMapStyleHlFixture, downgradeImage, downsizeImage, draftSaveSuccess, dragPanCondition, dropEmptyTranslations, editorReducer, emptyBlockModelFixture, findChildElement, findChildOrCreate, findChildrenElement, findConverterForDocument, findNestedChildOrCreate, findNestedElement, findNestedElements, findParent, firstChildElement, formatDate, formatUserInfo, getAddressLines, getAllKeysValidator, getArrayItem, getAsArray, getAsUrl, getBadgeColor, getCustomTranslations, getError, getFavoritesOnly, getFileFormat, getFileFormatFromServiceOutput, getFirstValue, getFormatPriority, getGeometryBoundingBox, getGeometryFromGeoJSON, getGlobalConfig, getIndividualDisplayName, getIsMobile, getJsonDataItemsProxy, getKeywordHierarchyPath, getLayers, getLinkId, getLinkLabel, getLinkPriority, getMapContext, getMapContextLayerFromConfig, getMapState, getMetadataQualityConfig, getMimeTypeForFormat, getNamespace, getOptionalEditorConfig, getOptionalMapConfig, getOptionalSearchConfig, getPageSize, getQualityValidators, getResourceType, getReusePresentationForm, getReuseType, getRootElement, getSearchConfigAggregations, getSearchFilters, getSearchResults, getSearchResultsAggregations, getSearchResultsHits, getSearchResultsLayout, getSearchResultsLoading, getSearchSortBy, getSearchState, getSearchStateSearch, getSelectedFeatures, getSpatialFilterEnabled, getTemporalRangeUnion, getThemeConfig, handleScrollOnNavigation, hasRecordChangedSinceDraft, hasRecordChangedSinceDraftSuccess, initSearch, initialEditorState, initialMapState, initialState, isConfigLoaded, isDateRange, isFormatInQueryParam, isPublished, itemModelFixture, kindToCodeListValue, loadAppConfig, malformedConfigFixture, mapConfigFixture, mapContact, mapKeywords, mapLogo, mapOrganization, mapReducer, markRecordAsChanged, matchesNoApplicableConstraint, matchesNoKnownConstraint, megabytesToBytes, mimeTypeToFormat, minimalAppConfigFixture, missingMandatoryConfigFixture, mouseWheelZoomCondition, noDuplicateFileName, okAppConfigFixture, openDataset, openRecord, organizationsServiceFactory, parse, parseXmlString, placeholder, prioritizePageScroll, propagateToDocumentOnly, provideGn4, provideI18n, provideRepositoryUrl, readAttribute, readDataset, readDatasetHeaders, readText, reducer$2 as reducer, reducerSearch, removeAllChildren, removeChildren, removeChildrenByName, removeSearchParams, removeWhitespace, renameElements, saveRecord, saveRecordFailure, saveRecordSuccess, selectCanEditRecord, selectCurrentPage, selectEditorConfig, selectEditorState, selectFallback, selectFallbackFields, selectField, selectHasRecordChanged, selectIsPublished, selectRecord, selectRecordChangedSinceSave, selectRecordSaveError, selectRecordSaving, selectRecordSections, selectRecordSource, selectTranslatedField, selectTranslatedValue, setContext, setCurrentPage, setEditorConfiguration, setFieldVisibility, setFocusedField, setSelectedFeatures, setTextContent, sortByFromString, sortByToString, sortByToStrings, spatialExtentToGeometry, spatialExtentsToFeatureCollection, stripHtml, stripNamespace, toDate, toIndividual, toLang2, toLang3, totalPages, undoRecordDraft, unrecognizedKeysConfigFixture, updateFrequencyCodeValues, updateLanguages, updateRecordField, updateRecordLanguages, wmsLayerFlatten, writeAttribute, wrongLanguageCodeConfigFixture, xmlToString };
|
|
40881
|
+
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, 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, SpatialExtentSearchField, 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, isFileExtensionValid, isFormatInQueryParam, isPublished, itemModelFixture, kindToCodeListValue, loadAppConfig, malformedConfigFixture, mapConfigFixture, mapContact, mapKeywords, mapLogo, mapOrganization, mapReducer, markRecordAsChanged, matchesNoApplicableConstraint, matchesNoKnownConstraint, megabytesToBytes, mimeTypeToFormat, minimalAppConfigFixture, missingMandatoryConfigFixture, mouseWheelZoomCondition, noDuplicateFileName, okAppConfigFixture, openDataset, openRecord, organizationsServiceFactory, parse, parseXmlString, placeholder, prioritizePageScroll, propagateToDocumentOnly, provideGn4, provideI18n, provideLocalizedDateAdapter, provideRepositoryUrl, readAttribute, readDataset, readDatasetHeaders, readFileAsText, readText, reducer$2 as reducer, reducerSearch, removeAllChildren, removeChildren, removeChildrenByName, removeSearchParams, removeWhitespace, renameElements, saveRecord, saveRecordFailure, saveRecordSuccess, selectCanEditRecord, selectCurrentPage, selectEditorConfig, selectEditorState, selectFallback, selectFallbackFields, selectField, selectHasRecordChanged, selectIsPublished, selectRecord, selectRecordChangedSinceSave, selectRecordSaveError, selectRecordSaving, selectRecordSections, selectRecordSource, selectTranslatedField, selectTranslatedValue, setContext, setCurrentPage, setEditorConfiguration, setFieldVisibility, setFocusedField, setSelectedFeatures, setTextContent, sortByFromString, sortByToString, sortByToStrings, spatialExtentToGeometry, spatialExtentsToFeatureCollection, stripHtml, stripNamespace, toDate, toIndividual, toLang2, toLang3, totalPages, undoRecordDraft, unrecognizedKeysConfigFixture, updateFrequencyCodeValues, updateLanguages, updateRecordField, updateRecordLanguages, wmsLayerFlatten, writeAttribute, wrongLanguageCodeConfigFixture, xmlToString };
|
|
40381
40882
|
//# sourceMappingURL=geonetwork-ui.mjs.map
|