@geogirafe/lib-geoportal 1.0.2217896135 → 1.0.2221886306
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/components/search/component.d.ts +7 -7
- package/components/search/component.js +59 -100
- package/components/search-mobile/component.js +1 -1
- package/models/main.d.ts +1 -2
- package/models/main.js +0 -1
- package/models/searchresult.d.ts +3 -26
- package/models/searchresult.js +1 -6
- package/package.json +1 -1
- package/templates/public/about.json +1 -1
- package/tools/auth/abstractconnectmanager.js +1 -0
- package/tools/auth/authmanager.js +4 -0
- package/tools/auth/gmfmanager.js +0 -1
- package/tools/auth/openidconnectmanager.js +3 -1
- package/tools/configuration/girafeconfig.d.ts +1 -0
- package/tools/configuration/girafeconfig.js +3 -1
- package/tools/state/state.d.ts +1 -0
- package/tools/state/state.js +2 -1
- package/tools/themes/themesmanager.js +2 -1
|
@@ -1,6 +1,5 @@
|
|
|
1
|
+
import Feature from 'ol/Feature';
|
|
1
2
|
import GirafeHTMLElement from '../../base/GirafeHTMLElement';
|
|
2
|
-
import type { AllSearchResults } from '../../models/searchresult';
|
|
3
|
-
import SearchResult from '../../models/searchresult';
|
|
4
3
|
declare class SearchComponent extends GirafeHTMLElement {
|
|
5
4
|
template: () => import("uhtml").Hole;
|
|
6
5
|
searchIcon: string;
|
|
@@ -10,9 +9,10 @@ declare class SearchComponent extends GirafeHTMLElement {
|
|
|
10
9
|
private previewLayers;
|
|
11
10
|
private previewGeoLayer;
|
|
12
11
|
private maxExtent?;
|
|
12
|
+
private readonly geoJsonFormatter;
|
|
13
13
|
private ignoreBlur;
|
|
14
|
-
groupedResults: Record<string,
|
|
15
|
-
protected allResults:
|
|
14
|
+
groupedResults: Record<string, Feature[]>;
|
|
15
|
+
protected allResults: Feature[];
|
|
16
16
|
protected forceHide: boolean;
|
|
17
17
|
private readonly searchTermPlaceholder;
|
|
18
18
|
private readonly searchLangPlaceholder;
|
|
@@ -39,7 +39,7 @@ declare class SearchComponent extends GirafeHTMLElement {
|
|
|
39
39
|
protected connectedCallback(): void;
|
|
40
40
|
protected clearSearch(purge?: boolean): void;
|
|
41
41
|
doSearch(e: Event): Promise<void>;
|
|
42
|
-
protected fetchSearch(term: string): Promise<
|
|
42
|
+
protected fetchSearch(term: string): Promise<Feature[]>;
|
|
43
43
|
/**
|
|
44
44
|
* Debounce the fetch call to API to prevent sending request at every stroke.
|
|
45
45
|
* @param e
|
|
@@ -52,14 +52,14 @@ declare class SearchComponent extends GirafeHTMLElement {
|
|
|
52
52
|
private displayCoordinates;
|
|
53
53
|
private displayResults;
|
|
54
54
|
getIcon(searchGroup: string): any;
|
|
55
|
-
onMouseOver(result:
|
|
55
|
+
onMouseOver(result: Feature): void;
|
|
56
56
|
onMouseLeave(): void;
|
|
57
57
|
private focusResultFromIndex;
|
|
58
58
|
private focusResult;
|
|
59
59
|
private preview;
|
|
60
60
|
private addFeatureToPreview;
|
|
61
61
|
clearPreview(): void;
|
|
62
|
-
onSelect(
|
|
62
|
+
onSelect(feature: Feature): void;
|
|
63
63
|
private addResultToTreeView;
|
|
64
64
|
private zoomTo;
|
|
65
65
|
onMouseMove(): void;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { html as uHtml } from 'uhtml';
|
|
2
2
|
import Collection from 'ol/Collection';
|
|
3
3
|
import Feature from 'ol/Feature';
|
|
4
|
+
import GeoJSON from 'ol/format/GeoJSON';
|
|
4
5
|
import VectorSource from 'ol/source/Vector';
|
|
5
6
|
import VectorLayer from 'ol/layer/Vector';
|
|
6
|
-
import {
|
|
7
|
+
import { Point } from 'ol/geom';
|
|
7
8
|
import { Style, Icon, Stroke, Fill } from 'ol/style';
|
|
8
9
|
import { buffer, getWidth, getHeight, getCenter, containsExtent } from 'ol/extent';
|
|
9
10
|
import GirafeColorPicker from '../../tools/utils/girafecolorpicker';
|
|
@@ -21,7 +22,7 @@ class SearchComponent extends GirafeHTMLElement {
|
|
|
21
22
|
</style><style>
|
|
22
23
|
.hidden{display:none}#searchbox{background-color:var(--bkg-color);border:1px solid #cfd6dd;border-radius:4px;flex-direction:row;width:100%;height:2.25rem;display:flex}#search{-webkit-appearance:none;width:auto;height:2.25rem;color:var(--text-color);background:0 0;border:0;outline:none;-webkit-flex:auto;-ms-flex:auto;flex:auto;margin:0 0 0 .5rem;padding-left:.8rem;font-size:1rem}.search-icon,.close-icon,.select-color-icon{width:14px;color:var(--text-color);padding:0 1rem}.close-button,.select-color-button{cursor:pointer;background-color:#0000;border:none;padding:0}#results{z-index:1000;background-color:var(--bkg-color);scrollbar-width:thin;width:36rem;max-height:31rem;margin-top:.1rem;margin-bottom:.5rem;position:absolute;top:3.5rem;overflow-x:hidden}.result,.title{width:34rem;padding:.3rem 1rem;line-height:1.3rem;display:inline-block}.title{color:var(--text-color);padding-top:1.5rem;font-size:1.3rem;font-weight:600;line-height:2.5rem}.result{cursor:pointer;text-align:left;width:100%;color:var(--text-color);background:0 0;border:none;border-top:1px solid #ccc}.title img{width:14px;margin-right:1rem}.title span{text-transform:uppercase}.result.selected{color:var(--text-color);background-color:#aaa!important}#no-result{background:var(--warning-color);opacity:.75;visibility:hidden;pointer-events:none;border-radius:6px;width:fit-content;margin:auto;padding:.5rem 0;font-size:1rem;position:absolute;top:3.5rem;left:0;right:0}#no-result>span{padding:1.3rem}@keyframes fadeOut{0%{opacity:1}80%{opacity:1}to{opacity:0}}.shown-then-fadeout{animation:2s ease-in-out forwards fadeOut;visibility:visible!important}
|
|
23
24
|
</style>
|
|
24
|
-
<link rel="stylesheet" href="lib/vanilla-picker/vanilla-picker.csp.css"><link rel="stylesheet" href="styles/girafecolorpicker.css"><div id="searchbox"><input tip="search_tip" id="search" class="gg-input" length="20" maxlength="1000" autocomplete="off" autocorrect="off" i18n="Search..." placeholder="Search..." oninput="${(e) => this.doSearchDebounced(e)}" onfocusin="${() => this.onFocusIn()}" onfocusout="${() => this.onFocusOut()}" onkeydown="${(e) => this.onKeyDown(e)}"> <img class="${this.allResults.length > 0 ? 'hidden' : 'search-icon'}" alt="search-icon" src="${this.searchIcon}"> <button class="${this.allResults.length > 0 ? 'close-button' : 'hidden'}" onclick="${() => this.clearSearch(true)}"><img class="close-icon" alt="close-icon" src="icons/close.svg"></button> <button tip="change_search_result_color" id="colorPickerBtn" class="${this.paintSearchResults ? 'select-color-button' : 'hidden'}"><img class="select-color-icon" alt="select-color-icon" src="${this.paintbrushIcon}"></button><div id="results" class="${Object.keys(this.groupedResults).length === 0 || this.forceHide ? 'hidden' : ''}" onmousemove="${() => this.onMouseMove()}">${Object.keys(this.groupedResults).map(group => uHtml `<div class="title"><img alt="result icon" src="${this.getIcon(group)}"> <span i18n="${group}"></span></div>${this.groupedResults[group].map(result => uHtml ` <button class="${result.selected ? 'result selected' : 'result'}" onmousedown="${() => this.onMouseDown()}" onclick="${() => this.onSelect(result)}" onmouseover="${() => this.onMouseOver(result)}" onmouseleave="${() => this.onMouseLeave()}">${result.
|
|
25
|
+
<link rel="stylesheet" href="lib/vanilla-picker/vanilla-picker.csp.css"><link rel="stylesheet" href="styles/girafecolorpicker.css"><div id="searchbox"><input tip="search_tip" id="search" class="gg-input" length="20" maxlength="1000" autocomplete="off" autocorrect="off" i18n="Search..." placeholder="Search..." oninput="${(e) => this.doSearchDebounced(e)}" onfocusin="${() => this.onFocusIn()}" onfocusout="${() => this.onFocusOut()}" onkeydown="${(e) => this.onKeyDown(e)}"> <img class="${this.allResults.length > 0 ? 'hidden' : 'search-icon'}" alt="search-icon" src="${this.searchIcon}"> <button class="${this.allResults.length > 0 ? 'close-button' : 'hidden'}" onclick="${() => this.clearSearch(true)}"><img class="close-icon" alt="close-icon" src="icons/close.svg"></button> <button tip="change_search_result_color" id="colorPickerBtn" class="${this.paintSearchResults ? 'select-color-button' : 'hidden'}"><img class="select-color-icon" alt="select-color-icon" src="${this.paintbrushIcon}"></button><div id="results" class="${Object.keys(this.groupedResults).length === 0 || this.forceHide ? 'hidden' : ''}" onmousemove="${() => this.onMouseMove()}">${Object.keys(this.groupedResults).map(group => uHtml `<div class="title"><img alt="result icon" src="${this.getIcon(group)}"> <span i18n="${group}"></span></div>${this.groupedResults[group].map(result => uHtml ` <button class="${result.get('selected') ? 'result selected' : 'result'}" onmousedown="${() => this.onMouseDown()}" onclick="${() => this.onSelect(result)}" onmouseover="${() => this.onMouseOver(result)}" onmouseleave="${() => this.onMouseLeave()}">${result.get('label')}</button> `)} `)}</div><div id="no-result" class="${this.showNoResultWarning ? 'shown-then-fadeout' : ''}"><span i18n="${'No match found'}"></span></div></div>`;
|
|
25
26
|
};
|
|
26
27
|
searchIcon = SearchIcon;
|
|
27
28
|
paintbrushIcon = PaintbrushIcon;
|
|
@@ -32,6 +33,7 @@ class SearchComponent extends GirafeHTMLElement {
|
|
|
32
33
|
previewLayers = [];
|
|
33
34
|
previewGeoLayer = null;
|
|
34
35
|
maxExtent;
|
|
36
|
+
geoJsonFormatter = new GeoJSON();
|
|
35
37
|
ignoreBlur = false;
|
|
36
38
|
groupedResults = {};
|
|
37
39
|
allResults = [];
|
|
@@ -56,9 +58,9 @@ class SearchComponent extends GirafeHTMLElement {
|
|
|
56
58
|
async initialSearch() {
|
|
57
59
|
const searchTerm = this.context.permalinkManager.getSearchTerm();
|
|
58
60
|
const results = await this.fetchSearch(searchTerm);
|
|
59
|
-
if (results.
|
|
61
|
+
if (results.length > 0) {
|
|
60
62
|
// Apply the first search result in the list
|
|
61
|
-
const firstResult = results
|
|
63
|
+
const firstResult = results[0];
|
|
62
64
|
this.preview(firstResult);
|
|
63
65
|
this.onSelect(firstResult);
|
|
64
66
|
}
|
|
@@ -149,13 +151,13 @@ class SearchComponent extends GirafeHTMLElement {
|
|
|
149
151
|
}
|
|
150
152
|
if (term.length > 0) {
|
|
151
153
|
try {
|
|
152
|
-
const
|
|
154
|
+
const features = await this.fetchSearch(term);
|
|
153
155
|
// If the search term is at least two charecter but yieds no result, a warning
|
|
154
156
|
// box is displayed for 2 seconds and then fades out (CSS)
|
|
155
|
-
if (
|
|
157
|
+
if (features.length === 0 && term.length >= 2) {
|
|
156
158
|
this.showNoResultWarning = true;
|
|
157
159
|
}
|
|
158
|
-
this.displayResults(
|
|
160
|
+
this.displayResults(features);
|
|
159
161
|
}
|
|
160
162
|
catch (error) {
|
|
161
163
|
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
@@ -172,7 +174,11 @@ class SearchComponent extends GirafeHTMLElement {
|
|
|
172
174
|
.replace(this.searchLangPlaceholder, this.state.language);
|
|
173
175
|
const response = await fetch(url, { signal: this.abortController.signal });
|
|
174
176
|
const data = await response.json();
|
|
175
|
-
|
|
177
|
+
const features = this.geoJsonFormatter.readFeatures(data, {
|
|
178
|
+
dataProjection: this.context.configManager.getDefaultConfigValue('search.resultsSrid'),
|
|
179
|
+
featureProjection: this.map.getView().getProjection()
|
|
180
|
+
});
|
|
181
|
+
return features;
|
|
176
182
|
}
|
|
177
183
|
/**
|
|
178
184
|
* Debounce the fetch call to API to prevent sending request at every stroke.
|
|
@@ -206,40 +212,32 @@ class SearchComponent extends GirafeHTMLElement {
|
|
|
206
212
|
if (!east_coord || !north_coord) {
|
|
207
213
|
return;
|
|
208
214
|
}
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
label: `${coord1} ${coord2}`,
|
|
217
|
-
layer_name: 'recenter_map'
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
this.allResults = [result];
|
|
221
|
-
this.groupedResults.recenter_map = [result];
|
|
215
|
+
const feature = new Feature({
|
|
216
|
+
geometry: new Point([east_coord, north_coord]),
|
|
217
|
+
label: `${coord1} ${coord2}`,
|
|
218
|
+
layer_name: 'recenter_map'
|
|
219
|
+
});
|
|
220
|
+
this.allResults = [feature];
|
|
221
|
+
this.groupedResults.recenter_map = [feature];
|
|
222
222
|
super.render();
|
|
223
223
|
super.girafeTranslate();
|
|
224
224
|
}
|
|
225
|
-
displayResults(
|
|
225
|
+
displayResults(features) {
|
|
226
226
|
// First, group the results
|
|
227
|
-
for (const result of
|
|
227
|
+
for (const result of features) {
|
|
228
228
|
// results.features.forEach((result) => {
|
|
229
229
|
let type = 'Unknown layer type';
|
|
230
|
-
if (result.
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
type = 'add_layer';
|
|
242
|
-
}
|
|
230
|
+
if (result.get('layer_name')) {
|
|
231
|
+
type = result.get('layer_name');
|
|
232
|
+
}
|
|
233
|
+
else if (result.get('actions')[0].action.startsWith('add_theme')) {
|
|
234
|
+
type = 'add_theme';
|
|
235
|
+
}
|
|
236
|
+
else if (result.get('actions')[0].action.startsWith('add_group')) {
|
|
237
|
+
type = 'add_group';
|
|
238
|
+
}
|
|
239
|
+
else if (result.get('actions')[0].action.startsWith('add_layer')) {
|
|
240
|
+
type = 'add_layer';
|
|
243
241
|
}
|
|
244
242
|
let resultList;
|
|
245
243
|
if (type in this.groupedResults) {
|
|
@@ -284,12 +282,12 @@ class SearchComponent extends GirafeHTMLElement {
|
|
|
284
282
|
// Clear old selection and preview
|
|
285
283
|
this.clearPreview();
|
|
286
284
|
if (this.focusedResult) {
|
|
287
|
-
this.focusedResult.selected
|
|
285
|
+
this.focusedResult.set('selected', false);
|
|
288
286
|
}
|
|
289
287
|
// Set new selected object, and activate preview
|
|
290
288
|
this.focusedResultIndex = this.allResults.findIndex((r) => r === result);
|
|
291
289
|
this.focusedResult = this.allResults[this.focusedResultIndex];
|
|
292
|
-
this.focusedResult.selected
|
|
290
|
+
this.focusedResult.set('selected', true);
|
|
293
291
|
this.render();
|
|
294
292
|
this.preview(result);
|
|
295
293
|
// Scroll to selected div
|
|
@@ -297,14 +295,12 @@ class SearchComponent extends GirafeHTMLElement {
|
|
|
297
295
|
resultHtmlElement.scrollIntoView({ block: 'nearest' });
|
|
298
296
|
}
|
|
299
297
|
preview(result) {
|
|
300
|
-
if (result.
|
|
298
|
+
if (result.getGeometry() && this.context.configManager.Config.search.objectPreview) {
|
|
301
299
|
// Result with geometry
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
this.updatePreviewLayerStyle();
|
|
305
|
-
}
|
|
300
|
+
this.addFeatureToPreview(result);
|
|
301
|
+
this.updatePreviewLayerStyle();
|
|
306
302
|
}
|
|
307
|
-
const firstAction = result.
|
|
303
|
+
const firstAction = result.get('actions')?.[0];
|
|
308
304
|
if (firstAction?.action.startsWith('add_layer') && this.context.configManager.Config.search.layerPreview) {
|
|
309
305
|
const layer = this.context.themesHelper.findLayerByName(firstAction.data);
|
|
310
306
|
if (layer) {
|
|
@@ -317,47 +313,8 @@ class SearchComponent extends GirafeHTMLElement {
|
|
|
317
313
|
}
|
|
318
314
|
}
|
|
319
315
|
}
|
|
320
|
-
addFeatureToPreview(
|
|
321
|
-
|
|
322
|
-
case 'Point': {
|
|
323
|
-
const feature = new Feature(new Point(geometry.coordinates));
|
|
324
|
-
this.previewFeaturesCollection.push(feature);
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
case 'MultiPoint': {
|
|
328
|
-
const feature = new Feature(new MultiPoint(geometry.coordinates));
|
|
329
|
-
this.previewFeaturesCollection.push(feature);
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
case 'MultiLineString': {
|
|
333
|
-
const feature = new Feature(new MultiLineString(geometry.coordinates));
|
|
334
|
-
this.previewFeaturesCollection.push(feature);
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
337
|
-
case 'LineString': {
|
|
338
|
-
const feature = new Feature(new LineString(geometry.coordinates));
|
|
339
|
-
this.previewFeaturesCollection.push(feature);
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
case 'Polygon': {
|
|
343
|
-
const feature = new Feature(new Polygon(geometry.coordinates));
|
|
344
|
-
this.previewFeaturesCollection.push(feature);
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
case 'MultiPolygon': {
|
|
348
|
-
const feature = new Feature(new MultiPolygon(geometry.coordinates));
|
|
349
|
-
this.previewFeaturesCollection.push(feature);
|
|
350
|
-
return;
|
|
351
|
-
}
|
|
352
|
-
case 'GeometryCollection': {
|
|
353
|
-
for (const geom of geometry.geometries) {
|
|
354
|
-
this.addFeatureToPreview(geom);
|
|
355
|
-
}
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
default:
|
|
359
|
-
throw new Error('Geometry type of search result is not being supported.');
|
|
360
|
-
}
|
|
316
|
+
addFeatureToPreview(feature) {
|
|
317
|
+
this.previewFeaturesCollection.push(feature);
|
|
361
318
|
}
|
|
362
319
|
clearPreview() {
|
|
363
320
|
// Clear preview search result
|
|
@@ -366,46 +323,48 @@ class SearchComponent extends GirafeHTMLElement {
|
|
|
366
323
|
this.context.themesHelper.removeLayersFromLayerTree(this.previewLayers);
|
|
367
324
|
this.previewLayers = [];
|
|
368
325
|
}
|
|
369
|
-
onSelect(
|
|
370
|
-
this.selectedResult =
|
|
326
|
+
onSelect(feature) {
|
|
327
|
+
this.selectedResult = feature;
|
|
371
328
|
this.ignoreBlur = false;
|
|
372
329
|
this.forceHide = true;
|
|
373
330
|
this.previewLayers = [];
|
|
374
331
|
super.render();
|
|
375
|
-
|
|
332
|
+
const geom = feature.getGeometry();
|
|
333
|
+
if (geom) {
|
|
376
334
|
// Result with geometry
|
|
377
|
-
this.zoomTo(
|
|
335
|
+
this.zoomTo(geom.getExtent());
|
|
378
336
|
}
|
|
379
337
|
else {
|
|
380
|
-
this.addResultToTreeView(
|
|
338
|
+
this.addResultToTreeView(feature);
|
|
381
339
|
}
|
|
382
340
|
this.onFocusOut();
|
|
383
341
|
// Update searchbox with result
|
|
384
|
-
if (this.searchInput &&
|
|
385
|
-
this.searchInput.value =
|
|
342
|
+
if (this.searchInput && feature.get('label')) {
|
|
343
|
+
this.searchInput.value = feature.get('label');
|
|
386
344
|
}
|
|
387
345
|
}
|
|
388
|
-
addResultToTreeView(
|
|
346
|
+
addResultToTreeView(feature) {
|
|
389
347
|
let clonedTheme;
|
|
390
|
-
|
|
348
|
+
const actions = feature.get('actions');
|
|
349
|
+
if (!actions || actions.length === 0) {
|
|
391
350
|
// Nothing to add
|
|
392
351
|
return;
|
|
393
352
|
}
|
|
394
353
|
let activate = false;
|
|
395
|
-
if (
|
|
396
|
-
const theme = this.context.themesHelper.findThemeByName(
|
|
354
|
+
if (actions[0].action.startsWith('add_theme')) {
|
|
355
|
+
const theme = this.context.themesHelper.findThemeByName(actions[0].data);
|
|
397
356
|
if (theme) {
|
|
398
357
|
clonedTheme = theme.clone();
|
|
399
358
|
}
|
|
400
359
|
}
|
|
401
|
-
else if (
|
|
402
|
-
const group = this.context.themesHelper.findGroupByName(
|
|
360
|
+
else if (actions[0].action.startsWith('add_group')) {
|
|
361
|
+
const group = this.context.themesHelper.findGroupByName(actions[0].data);
|
|
403
362
|
if (group) {
|
|
404
363
|
clonedTheme = this.context.themesHelper.getMinimalClonedThemeForLayer(group);
|
|
405
364
|
}
|
|
406
365
|
}
|
|
407
|
-
else if (
|
|
408
|
-
const layer = this.context.themesHelper.findLayerByName(
|
|
366
|
+
else if (actions[0].action.startsWith('add_layer')) {
|
|
367
|
+
const layer = this.context.themesHelper.findLayerByName(actions[0].data);
|
|
409
368
|
if (layer) {
|
|
410
369
|
clonedTheme = this.context.themesHelper.getMinimalClonedThemeForLayer(layer);
|
|
411
370
|
activate = true;
|
|
@@ -7,7 +7,7 @@ class MobileSearchComponent extends SearchComponent {
|
|
|
7
7
|
</style><style>
|
|
8
8
|
.hidden{display:none}#searchbox{background-color:var(--bkg-color);z-index:101;border:none;border-radius:4px;flex-direction:row;width:100%;display:flex;position:absolute;box-shadow:0 0 10px 1px #0003}#search{z-index:2;-webkit-appearance:none;width:100%;color:var(--text-color);background:0 0;border:0;outline:none;-webkit-flex:auto;-ms-flex:auto;flex:auto;margin:0 0 0 .5rem;padding-left:.8rem;font-size:1.8em;line-height:1.8em}#search::placeholder{color:inherit;opacity:.4}#search:focus::placeholder{opacity:0;color:#0000;transition:opacity .3s}.search-icon,.close-icon,.select-color-icon{width:17px;color:var(--text-color);opacity:.7;padding:0 1rem}.close-button,.select-color-button{cursor:pointer;background-color:#0000;border:none;padding:0}#results{z-index:100;background-color:var(--bkg-color);scrollbar-width:thin;padding-top:7vh;position:fixed;inset:0;overflow-x:hidden}.result,.title{width:34rem;padding:.3rem 1rem;line-height:1.3rem;display:inline-block}.title{color:var(--text-color);padding-top:1.5rem;font-size:1.3rem;font-weight:600;line-height:2.5rem}.result{cursor:pointer;text-align:left;width:100%;color:var(--text-color);background:0 0;border:none;border-top:1px solid #ccc}.title img{width:14px;margin-right:1rem}.title span{text-transform:uppercase}.result.selected{color:var(--text-color);background-color:#aaa!important}#no-result{background:var(--warning-color);opacity:.75;visibility:hidden;pointer-events:none;border-radius:6px;width:fit-content;margin:auto;padding:.5rem 0;font-size:1rem;position:absolute;top:3rem;left:0;right:0}#no-result>span{padding:1.3rem}@keyframes fadeOut{0%{opacity:1}80%{opacity:1}to{opacity:0}}.shown-then-fadeout{animation:2s ease-in-out forwards fadeOut;visibility:visible!important}
|
|
9
9
|
</style>
|
|
10
|
-
<link rel="stylesheet" href="lib/vanilla-picker/vanilla-picker.csp.css"><div id="searchbox"><input id="search" class="gg-input" length="20" maxlength="1000" autocomplete="off" autocorrect="off" i18n="Search" placeholder="Search..." oninput="${(e) => this.doSearchDebounced(e)}" onkeydown="${(e) => this.onKeyDown(e)}"> <img class="${this.allResults.length > 0 ? 'hidden' : 'search-icon'}" alt="search-icon" src="${this.searchIcon}"> <button class="${this.allResults.length > 0 ? 'close-button' : 'hidden'}" onclick="${() => this.clearSearch(true)}"><img class="close-icon" alt="close-icon" src="icons/close.svg"></button></div><div id="results" class="${Object.keys(this.groupedResults).length === 0 || this.forceHide ? 'hidden' : ''}">${Object.keys(this.groupedResults).map(group => uHtml `<div class="title"><img alt="result icon" src="${this.getIcon(group)}"> <span i18n="${group}"></span></div>${this.groupedResults[group].map(result => uHtml ` <button class="${result.selected ? 'result selected' : 'result'}" onmousedown="${() => this.onMouseDown()}" onclick="${() => this.onSelect(result)}">${result.
|
|
10
|
+
<link rel="stylesheet" href="lib/vanilla-picker/vanilla-picker.csp.css"><div id="searchbox"><input id="search" class="gg-input" length="20" maxlength="1000" autocomplete="off" autocorrect="off" i18n="Search" placeholder="Search..." oninput="${(e) => this.doSearchDebounced(e)}" onkeydown="${(e) => this.onKeyDown(e)}"> <img class="${this.allResults.length > 0 ? 'hidden' : 'search-icon'}" alt="search-icon" src="${this.searchIcon}"> <button class="${this.allResults.length > 0 ? 'close-button' : 'hidden'}" onclick="${() => this.clearSearch(true)}"><img class="close-icon" alt="close-icon" src="icons/close.svg"></button></div><div id="results" class="${Object.keys(this.groupedResults).length === 0 || this.forceHide ? 'hidden' : ''}">${Object.keys(this.groupedResults).map(group => uHtml `<div class="title"><img alt="result icon" src="${this.getIcon(group)}"> <span i18n="${group}"></span></div>${this.groupedResults[group].map(result => uHtml ` <button class="${result.get('selected') ? 'result selected' : 'result'}" onmousedown="${() => this.onMouseDown()}" onclick="${() => this.onSelect(result)}">${result.get('label')}</button> `)} `)}</div><div id="no-result" class="${this.showNoResultWarning ? 'shown-then-fadeout' : ''}"><span i18n="${'No match found'}"></span></div>`;
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
13
|
export default MobileSearchComponent;
|
package/models/main.d.ts
CHANGED
|
@@ -31,8 +31,7 @@ export { default as ThemeLayer } from './layers/themelayer';
|
|
|
31
31
|
export { default as ThemeLayerExternal } from './layers/themelayerexternal';
|
|
32
32
|
export type { TimeAwareLayer } from './layers/timeawarelayer';
|
|
33
33
|
export { isTimeAwareLayer } from './layers/timeawarelayer';
|
|
34
|
-
export type {
|
|
35
|
-
export { default as SearchResult } from './searchresult';
|
|
34
|
+
export type { SearchResultsActions } from './searchresult';
|
|
36
35
|
export type { SelectionTool, SelectionMode } from './selection';
|
|
37
36
|
export { default as SelectionParam } from './selectionparam';
|
|
38
37
|
export { default as ServerOgc } from './serverogc';
|
package/models/main.js
CHANGED
|
@@ -21,7 +21,6 @@ export { isSnappableLayer } from './layers/snappablelayer';
|
|
|
21
21
|
export { default as ThemeLayer } from './layers/themelayer';
|
|
22
22
|
export { default as ThemeLayerExternal } from './layers/themelayerexternal';
|
|
23
23
|
export { isTimeAwareLayer } from './layers/timeawarelayer';
|
|
24
|
-
export { default as SearchResult } from './searchresult';
|
|
25
24
|
export { default as SelectionParam } from './selectionparam';
|
|
26
25
|
export { default as ServerOgc } from './serverogc';
|
|
27
26
|
export { default as ServerOgcApi, OgcApiDefaultEncoding } from './serverogcapi';
|
package/models/searchresult.d.ts
CHANGED
|
@@ -1,27 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
type: 'Point' | 'MultiPoint' | 'LineString' | 'MultiLineString' | 'Polygon' | 'MultiPolygon';
|
|
5
|
-
coordinates: Coordinate | Coordinate[] | Coordinate[][] | Coordinate[][][];
|
|
1
|
+
export type SearchResultsActions = {
|
|
2
|
+
action: string;
|
|
3
|
+
data: string;
|
|
6
4
|
};
|
|
7
|
-
export type GeometryCollectionResult = {
|
|
8
|
-
type: 'GeometryCollection';
|
|
9
|
-
geometries: GeometryCollectionResult[] | GeometryResult[];
|
|
10
|
-
};
|
|
11
|
-
export type AllSearchResults = {
|
|
12
|
-
type: string;
|
|
13
|
-
features: SearchResult[];
|
|
14
|
-
};
|
|
15
|
-
export default class SearchResult {
|
|
16
|
-
bbox?: Extent;
|
|
17
|
-
geometry?: GeometryResult | GeometryCollectionResult;
|
|
18
|
-
properties?: {
|
|
19
|
-
label: string;
|
|
20
|
-
layer_name: string;
|
|
21
|
-
actions: {
|
|
22
|
-
action: string;
|
|
23
|
-
data: string;
|
|
24
|
-
}[];
|
|
25
|
-
};
|
|
26
|
-
selected: boolean;
|
|
27
|
-
}
|
package/models/searchresult.js
CHANGED
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.0.
|
|
1
|
+
{"version":"1.0.2221886306", "build":"2221886306", "date":"18/12/2025"}
|
|
@@ -20,6 +20,7 @@ export default class AbstractConnectManager {
|
|
|
20
20
|
this.state.oauth.status = 'loginFailed';
|
|
21
21
|
}
|
|
22
22
|
loggedOut() {
|
|
23
|
+
this.context.sessionManager.saveStateToSession();
|
|
23
24
|
this.state.oauth.status = 'loggedOut';
|
|
24
25
|
this.state.oauth.tokens = undefined;
|
|
25
26
|
this.state.oauth.userInfo = undefined;
|
|
@@ -81,8 +81,12 @@ export default class AuthManager extends GirafeSingleton {
|
|
|
81
81
|
await this.gmfManager.loginWithToken();
|
|
82
82
|
}
|
|
83
83
|
// And then we get the UserInfos
|
|
84
|
+
const previousUsername = this.state.oauth.userInfo?.username;
|
|
84
85
|
await this.gmfManager.getUserInfo();
|
|
85
86
|
if (this.state.oauth.userInfo?.username) {
|
|
87
|
+
// TODO REG : At the moment we just test if the username has changed,
|
|
88
|
+
// but actually we should test if some of the rights have changed
|
|
89
|
+
this.state.oauth.somethingChanged = this.state.oauth.userInfo.username !== previousUsername;
|
|
86
90
|
this.state.oauth.status = 'loggedIn';
|
|
87
91
|
}
|
|
88
92
|
else {
|
package/tools/auth/gmfmanager.js
CHANGED
|
@@ -178,7 +178,9 @@ export default class OpenIdConnectManager extends AbstractConnectManager {
|
|
|
178
178
|
this.state.oauth.tokens = tokens;
|
|
179
179
|
// Prepare refresh token
|
|
180
180
|
if (this.state.oauth.tokens.expires_in) {
|
|
181
|
-
|
|
181
|
+
// Refresh the token 1 minutes before its expiration,
|
|
182
|
+
// to be sure that it will still be valid when using it.
|
|
183
|
+
const expiresInMs = (this.state.oauth.tokens.expires_in - 60) * 1000;
|
|
182
184
|
setTimeout(() => this.refreshToken(), expiresInMs);
|
|
183
185
|
}
|
|
184
186
|
this.state.oauth.status = 'issuer.loggedIn';
|
|
@@ -73,7 +73,8 @@ class GirafeConfig {
|
|
|
73
73
|
// We just display a warning in the console
|
|
74
74
|
console.warn(e);
|
|
75
75
|
this.search = {
|
|
76
|
-
url: ''
|
|
76
|
+
url: '',
|
|
77
|
+
resultsSrid: config.map.srid
|
|
77
78
|
};
|
|
78
79
|
}
|
|
79
80
|
try {
|
|
@@ -192,6 +193,7 @@ class GirafeConfig {
|
|
|
192
193
|
}
|
|
193
194
|
return {
|
|
194
195
|
url: config.search.url,
|
|
196
|
+
resultsSrid: config.search.resultsSrid ?? config.map.srid,
|
|
195
197
|
objectPreview: config.search.objectPreview ?? false,
|
|
196
198
|
layerPreview: config.search.layerPreview ?? false,
|
|
197
199
|
minResolution: config.search.minResolution ?? 0.5,
|
package/tools/state/state.d.ts
CHANGED
package/tools/state/state.js
CHANGED
|
@@ -99,7 +99,8 @@ export default class State {
|
|
|
99
99
|
isOffline = false;
|
|
100
100
|
oauth = {
|
|
101
101
|
status: 'not-initialized',
|
|
102
|
-
audience: []
|
|
102
|
+
audience: [],
|
|
103
|
+
somethingChanged: true
|
|
103
104
|
};
|
|
104
105
|
// The State object is defined as <not extensible> by the StateManager.
|
|
105
106
|
// This property can be used by third-parts components or extensions
|
|
@@ -30,7 +30,8 @@ class ThemesManager extends GirafeSingleton {
|
|
|
30
30
|
// This is for the next change in the oAuth status.
|
|
31
31
|
this.context.stateManager.subscribe('oauth.status', async () => {
|
|
32
32
|
if (this.state.themes.isLoaded &&
|
|
33
|
-
(this.state.oauth.
|
|
33
|
+
((this.state.oauth.somethingChanged && this.state.oauth.status === 'loggedIn') ||
|
|
34
|
+
this.state.oauth.status === 'loggedOut')) {
|
|
34
35
|
await this.initialize();
|
|
35
36
|
}
|
|
36
37
|
});
|