qwc2 2026.6.10 → 2026.6.17

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.
Files changed (54) hide show
  1. package/actions/bookmark.js +40 -0
  2. package/actions/theme.js +92 -89
  3. package/components/BookmarkPanel.js +312 -0
  4. package/components/EditUploadField.js +17 -12
  5. package/components/ResizeableWindow.js +22 -0
  6. package/components/StandardApp.js +11 -2
  7. package/components/map/OlMap.js +3 -2
  8. package/components/map3d/Map3D.js +15 -2
  9. package/components/widgets/GroupSelect.js +94 -0
  10. package/icons/drag.svg +73 -0
  11. package/package.json +1 -1
  12. package/plugins/API.js +17 -2
  13. package/plugins/Bookmark.js +52 -266
  14. package/plugins/BottomBar.js +59 -5
  15. package/plugins/Identify.js +8 -1
  16. package/plugins/MapExport.js +6 -3
  17. package/plugins/Portal.js +2 -2
  18. package/plugins/Print.js +1 -1
  19. package/plugins/Settings.js +28 -49
  20. package/plugins/ThemeBrowser.css +132 -0
  21. package/plugins/ThemeBrowser.js +513 -0
  22. package/plugins/View3D.js +14 -0
  23. package/plugins/VisibilityPreset.js +137 -0
  24. package/reducers/bookmark.js +39 -0
  25. package/reducers/layers.js +6 -1
  26. package/scripts/updateTranslations.js +6 -2
  27. package/static/translations/bg-BG.json +15 -1
  28. package/static/translations/ca-ES.json +15 -1
  29. package/static/translations/cs-CZ.json +15 -1
  30. package/static/translations/de-CH.json +16 -2
  31. package/static/translations/de-DE.json +16 -2
  32. package/static/translations/en-US.json +16 -2
  33. package/static/translations/es-ES.json +15 -1
  34. package/static/translations/fi-FI.json +15 -1
  35. package/static/translations/fr-FR.json +15 -1
  36. package/static/translations/hu-HU.json +15 -1
  37. package/static/translations/it-IT.json +16 -2
  38. package/static/translations/ja-JP.json +15 -1
  39. package/static/translations/nl-NL.json +15 -1
  40. package/static/translations/no-NO.json +15 -1
  41. package/static/translations/pl-PL.json +15 -1
  42. package/static/translations/pt-BR.json +15 -1
  43. package/static/translations/pt-PT.json +15 -1
  44. package/static/translations/ro-RO.json +15 -1
  45. package/static/translations/ru-RU.json +15 -1
  46. package/static/translations/sv-SE.json +15 -1
  47. package/static/translations/tr-TR.json +15 -1
  48. package/static/translations/tsconfig.json +14 -1
  49. package/static/translations/uk-UA.json +15 -1
  50. package/utils/LocaleUtils.js +12 -2
  51. package/utils/PermaLinkUtils.js +92 -38
  52. package/utils/ServiceLayerUtils.js +1 -1
  53. package/utils/VectorLayerUtils.js +2 -1
  54. /package/{plugins/style/Bookmark.css → components/style/BookmarkPanel.css} +0 -0
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Copyright 2025 Stadtwerke München GmbH
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import bookmarkReducer from '../reducers/bookmark';
10
+ import ReducerIndex from '../reducers/index';
11
+ import { getBookmarks, getVisibilityPresets } from '../utils/PermaLinkUtils';
12
+ ReducerIndex.register("bookmark", bookmarkReducer);
13
+ export var SET_BOOKMARKS = 'SET_BOOKMARKS';
14
+ export var SET_VISIBILITY_PRESETS = 'SET_VISIBILITY_PRESETS';
15
+ export function setBookmarks(bookmarks) {
16
+ return {
17
+ type: SET_BOOKMARKS,
18
+ bookmarks: bookmarks
19
+ };
20
+ }
21
+ export function setVisibilityPresets(visibilityPresets) {
22
+ return {
23
+ type: SET_VISIBILITY_PRESETS,
24
+ visibilityPresets: visibilityPresets
25
+ };
26
+ }
27
+ export function refreshBookmarks() {
28
+ return function (dispatch) {
29
+ getBookmarks(function (bookmarks) {
30
+ return dispatch(setBookmarks(bookmarks));
31
+ });
32
+ };
33
+ }
34
+ export function refreshVisibilityPresets() {
35
+ return function (dispatch) {
36
+ getVisibilityPresets(function (presets) {
37
+ return dispatch(setVisibilityPresets(presets));
38
+ });
39
+ };
40
+ }
package/actions/theme.js CHANGED
@@ -33,7 +33,7 @@ import MapUtils from '../utils/MapUtils';
33
33
  import { UrlParams } from '../utils/PermaLinkUtils';
34
34
  import ServiceLayerUtils from '../utils/ServiceLayerUtils';
35
35
  import ThemeUtils from '../utils/ThemeUtils';
36
- import { LayerRole, addLayer, removeLayer, removeAllLayers, replacePlaceholderLayer, setSwipe } from './layers';
36
+ import { LayerRole, addLayer, removeLayer, removeAllLayers, replacePlaceholderLayer, setSwipe, setThemeLayersVisibilityPreset } from './layers';
37
37
  import { configureMap } from './map';
38
38
  import { showNotification, NotificationType } from './windows';
39
39
  export var THEMES_LOADED = 'THEMES_LOADED';
@@ -51,57 +51,81 @@ export function setThemeLayersList(theme) {
51
51
  themelist: theme
52
52
  };
53
53
  }
54
- export function finishThemeSetup(dispatch, theme, themes, layerConfigs, insertPos, permalinkLayers, externalLayerRestorer, visibleBgLayer, initialTheme, initialTask) {
55
- var _theme$config$section, _theme$config, _theme$config2;
56
- // Create layer
57
- var themeLayer = ThemeUtils.createThemeLayer(theme, themes);
58
- var layers = [themeLayer];
59
-
60
- // Restore theme layer configuration, create placeholders for missing layers
54
+ export function finishThemeSetup(dispatch, theme, themes, layerConfigs, preserve, prevLayers, prevTheme, permalinkLayers, externalLayerRestorer, visibleBgLayer, initialTask, initialVisibilityPreset) {
55
+ var _bgLayers$find, _theme$config$section, _theme$config, _theme$config2;
56
+ var layers = [];
61
57
  var externalLayers = {};
62
- if (!isEmpty(permalinkLayers) && ConfigUtils.getConfigProp("storeAllLayersInPermalink")) {
63
- layers = permalinkLayers;
64
- } else {
65
- if (layerConfigs) {
66
- if (ConfigUtils.getConfigProp("allowReorderingLayers", theme) !== true) {
67
- layers = LayerUtils.restoreLayerParams(themeLayer, layerConfigs, permalinkLayers, externalLayers);
68
- } else {
69
- layers = LayerUtils.restoreOrderedLayerParams(themeLayer, layerConfigs, permalinkLayers, externalLayers);
70
- }
58
+
59
+ // Get current background layer if it needs to be preserved
60
+ if (preserve && visibleBgLayer === null && ConfigUtils.getConfigProp("preserveBackgroundOnThemeSwitch", theme) === true) {
61
+ var _prevLayers$find$name, _prevLayers$find;
62
+ visibleBgLayer = (_prevLayers$find$name = (_prevLayers$find = prevLayers.find(function (layer) {
63
+ return layer.role === LayerRole.BACKGROUND && layer.visibility === true;
64
+ })) === null || _prevLayers$find === void 0 ? void 0 : _prevLayers$find.name) !== null && _prevLayers$find$name !== void 0 ? _prevLayers$find$name : null;
65
+ }
66
+
67
+ // Remove old layers
68
+ var preserveUserLayers = preserve && ConfigUtils.getConfigProp("preserveNonThemeLayersOnThemeSwitch", theme) === true;
69
+ var insPos = 0;
70
+ var bgLayerKept = false;
71
+ prevLayers.forEach(function (layer) {
72
+ if (layer.role === LayerRole.USERLAYER && preserveUserLayers) {
73
+ ++insPos;
74
+ } else if (layer.role === LayerRole.BACKGROUND && layer.name === visibleBgLayer) {
75
+ bgLayerKept = true;
76
+ } else {
77
+ dispatch(removeLayer(layer.id));
71
78
  }
72
- if (isEmpty(layers)) {
73
- layers = [_objectSpread(_objectSpread({}, themeLayer), {}, {
74
- sublayers: []
75
- })];
79
+ });
80
+ if (theme.url) {
81
+ // Create layer
82
+ var themeLayer = ThemeUtils.createThemeLayer(theme, themes);
83
+ layers.push(themeLayer);
84
+ // Restore theme layer configuration, create placeholders for missing layers
85
+ if (!isEmpty(permalinkLayers) && ConfigUtils.getConfigProp("storeAllLayersInPermalink")) {
86
+ layers = permalinkLayers;
87
+ } else {
88
+ if (layerConfigs) {
89
+ if (ConfigUtils.getConfigProp("allowReorderingLayers", theme) !== true) {
90
+ layers = LayerUtils.restoreLayerParams(themeLayer, layerConfigs, permalinkLayers, externalLayers);
91
+ } else {
92
+ layers = LayerUtils.restoreOrderedLayerParams(themeLayer, layerConfigs, permalinkLayers, externalLayers);
93
+ }
94
+ }
95
+ if (isEmpty(layers)) {
96
+ layers = [_objectSpread(_objectSpread({}, themeLayer), {}, {
97
+ sublayers: []
98
+ })];
99
+ }
76
100
  }
77
101
  }
78
102
 
79
103
  // Add background layers for theme
80
- var haveVisibleBg = false;
81
104
  var bgLayers = ThemeUtils.createThemeBackgroundLayers(theme.backgroundLayers || [], themes, visibleBgLayer, externalLayers);
82
- if (initialTheme && visibleBgLayer) {
83
- var _bgLayers$find;
84
- var visibleLayer = (_bgLayers$find = bgLayers.find(function (entry) {
85
- return entry.visibility;
86
- })) === null || _bgLayers$find === void 0 ? void 0 : _bgLayers$find.name;
87
- if (visibleLayer !== visibleBgLayer) {
88
- dispatch(showNotification("missingbglayer", LocaleUtils.tr("app.missingbg", visibleBgLayer), NotificationType.WARN, true));
89
- }
105
+ var actuallyVisibleBgLayer = (_bgLayers$find = bgLayers.find(function (entry) {
106
+ return entry.visibility;
107
+ })) === null || _bgLayers$find === void 0 ? void 0 : _bgLayers$find.name;
108
+ if (!prevTheme && visibleBgLayer && actuallyVisibleBgLayer !== visibleBgLayer) {
109
+ dispatch(showNotification("missingbglayer", LocaleUtils.tr("app.missingbg", visibleBgLayer), NotificationType.WARN, true));
90
110
  }
91
111
  var _iterator = _createForOfIteratorHelper(bgLayers.reverse()),
92
112
  _step;
93
113
  try {
94
114
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
95
115
  var bgLayer = _step.value;
96
- haveVisibleBg |= bgLayer.visibility;
97
- dispatch(addLayer(bgLayer));
116
+ // If previous visible BG layer kept, insert other BG layers around that layer
117
+ if (bgLayer.name === visibleBgLayer && bgLayerKept) {
118
+ bgLayerKept = false;
119
+ } else {
120
+ dispatch(addLayer(bgLayer, bgLayerKept ? 1 : 0));
121
+ }
98
122
  }
99
123
  } catch (err) {
100
124
  _iterator.e(err);
101
125
  } finally {
102
126
  _iterator.f();
103
127
  }
104
- if (!haveVisibleBg) {
128
+ if (!actuallyVisibleBgLayer) {
105
129
  UrlParams.updateParams({
106
130
  bl: ""
107
131
  });
@@ -111,7 +135,7 @@ export function finishThemeSetup(dispatch, theme, themes, layerConfigs, insertPo
111
135
  try {
112
136
  for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
113
137
  var layer = _step2.value;
114
- dispatch(addLayer(layer, insertPos));
138
+ dispatch(addLayer(layer, insPos));
115
139
  }
116
140
 
117
141
  // Restore external layers
@@ -134,8 +158,10 @@ export function finishThemeSetup(dispatch, theme, themes, layerConfigs, insertPo
134
158
  // Don't expose sublayers
135
159
  if (layer) {
136
160
  layer.sublayers = null;
161
+ dispatch(replacePlaceholderLayer(id, layer));
162
+ } else {
163
+ dispatch(removeLayer(id));
137
164
  }
138
- dispatch(replacePlaceholderLayer(id, layer));
139
165
  });
140
166
  }
141
167
  }
@@ -143,8 +169,11 @@ export function finishThemeSetup(dispatch, theme, themes, layerConfigs, insertPo
143
169
  type: SET_CURRENT_THEME,
144
170
  theme: theme
145
171
  });
172
+ if (initialVisibilityPreset) {
173
+ dispatch(setThemeLayersVisibilityPreset(initialVisibilityPreset));
174
+ }
146
175
  var section = ConfigUtils.isMobile() ? "mobile" : "desktop";
147
- var task = initialTask || ((_theme$config$section = theme === null || theme === void 0 || (_theme$config = theme.config) === null || _theme$config === void 0 || (_theme$config = _theme$config[section]) === null || _theme$config === void 0 ? void 0 : _theme$config.startupTask) !== null && _theme$config$section !== void 0 ? _theme$config$section : theme === null || theme === void 0 || (_theme$config2 = theme.config) === null || _theme$config2 === void 0 ? void 0 : _theme$config2.startupTask) || (initialTheme ? ConfigUtils.getConfigProp("startupTask") : null);
176
+ var task = initialTask || ((_theme$config$section = theme === null || theme === void 0 || (_theme$config = theme.config) === null || _theme$config === void 0 || (_theme$config = _theme$config[section]) === null || _theme$config === void 0 ? void 0 : _theme$config.startupTask) !== null && _theme$config$section !== void 0 ? _theme$config$section : theme === null || theme === void 0 || (_theme$config2 = theme.config) === null || _theme$config2 === void 0 ? void 0 : _theme$config2.startupTask) || (!prevTheme ? ConfigUtils.getConfigProp("startupTask") : null);
148
177
  if (task) {
149
178
  var mapClickAction = ConfigUtils.getPluginConfig(task.key).mapClickAction;
150
179
  dispatch(setCurrentTask(task.key, task.mode, mapClickAction, task.data));
@@ -159,68 +188,37 @@ export function setCurrentTheme(theme, themes) {
159
188
  var themeLayerRestorer = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : null;
160
189
  var externalLayerRestorer = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : null;
161
190
  var initialTask = arguments.length > 9 && arguments[9] !== undefined ? arguments[9] : null;
191
+ var initialVisibilityPreset = arguments.length > 10 && arguments[10] !== undefined ? arguments[10] : null;
162
192
  return function (dispatch, getState) {
163
- var _getState$layers, _ref, _theme$mapTips;
164
- var curLayers = ((_getState$layers = getState().layers) === null || _getState$layers === void 0 ? void 0 : _getState$layers.flat) || [];
193
+ var _getState$layers, _themes$defaultMapCrs, _themes$defaultMapExt, _themes$defaultMapCrs2, _theme$bbox, _theme$initialBbox, _ref, _theme$version, _ref2, _theme$scales, _theme$printScales, _theme$printResolutio, _theme$printGrid, _theme$searchProvider, _ref3, _theme$backgroundLaye, _theme$mapTips;
194
+ dispatch(setSwipe(null));
165
195
  var mapCrs = theme.mapCrs || themes.defaultMapCrs || "EPSG:3857";
166
196
  if (!(mapCrs in CoordinatesUtils.getAvailableCRS())) {
167
197
  dispatch(showNotification("missingprojection", LocaleUtils.tr("app.missingprojection", theme.title, mapCrs), NotificationType.WARN, true));
168
- return;
169
- }
170
- var initialTheme = !getState().theme.current;
171
-
172
- // Get current background layer if it needs to be preserved
173
- if (preserve && visibleBgLayer === null && ConfigUtils.getConfigProp("preserveBackgroundOnThemeSwitch", theme) === true) {
174
- var curBgLayer = curLayers.find(function (layer) {
175
- return layer.role === LayerRole.BACKGROUND && layer.visibility === true;
176
- });
177
- visibleBgLayer = curBgLayer ? curBgLayer.name : null;
178
- }
179
-
180
- // Remove old layers
181
- var insertPos = 0;
182
- if (preserve && ConfigUtils.getConfigProp("preserveNonThemeLayersOnThemeSwitch", theme) === true) {
183
- // Compute insertion position of new theme layers by counting how many non-theme layers remain
184
- insertPos = curLayers.filter(function (layer) {
185
- return layer.role === LayerRole.USERLAYER;
186
- }).length;
187
- var removeLayers = curLayers.filter(function (layer) {
188
- return layer.role !== LayerRole.USERLAYER;
189
- }).map(function (layer) {
190
- return layer.id;
191
- });
192
- var _iterator3 = _createForOfIteratorHelper(removeLayers),
193
- _step3;
194
- try {
195
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
196
- var layerId = _step3.value;
197
- dispatch(removeLayer(layerId));
198
- }
199
- } catch (err) {
200
- _iterator3.e(err);
201
- } finally {
202
- _iterator3.f();
203
- }
204
- } else {
205
198
  dispatch(removeAllLayers());
206
- }
207
- dispatch(setSwipe(null));
208
- if (!theme) {
209
199
  return;
210
200
  }
201
+ var prevLayers = ((_getState$layers = getState().layers) === null || _getState$layers === void 0 ? void 0 : _getState$layers.flat) || [];
202
+ var prevTheme = getState().theme.current;
203
+ var defaultExtent = {
204
+ crs: (_themes$defaultMapCrs = themes.defaultMapCrs) !== null && _themes$defaultMapCrs !== void 0 ? _themes$defaultMapCrs : "EPSG:3857",
205
+ bounds: (_themes$defaultMapExt = themes.defaultMapExtent) !== null && _themes$defaultMapExt !== void 0 ? _themes$defaultMapExt : CoordinatesUtils.reproject([-20037508.34, -20048966.10, 20037508.34, 20048966.10], "EPSG:3857", (_themes$defaultMapCrs2 = themes.defaultMapCrs) !== null && _themes$defaultMapCrs2 !== void 0 ? _themes$defaultMapCrs2 : "EPSG:3857")
206
+ };
211
207
 
212
208
  // Inherit defaults if necessary
213
209
  theme = _objectSpread(_objectSpread({}, theme), {}, {
214
210
  mapCrs: mapCrs,
215
- version: theme.version || themes.defaultWMSVersion || "1.3.0",
216
- scales: theme.scales || themes.defaultScales || MapUtils.getGoogleMercatorScales(0, 21),
217
- printScales: theme.printScales || themes.defaultPrintScales || undefined,
218
- printResolutions: theme.printResolutions || themes.defaultPrintResolutions || undefined,
219
- printGrid: theme.printGrid || themes.defaultPrintGrid || undefined,
220
- searchProviders: theme.searchProviders || themes.defaultSearchProviders || undefined,
221
- backgroundLayers: theme.backgroundLayers || themes.defaultBackgroundLayers || [],
222
- mapTips: (_ref = (_theme$mapTips = theme.mapTips) !== null && _theme$mapTips !== void 0 ? _theme$mapTips : themes.defaultMapTips) !== null && _ref !== void 0 ? _ref : undefined,
223
- defaultDisplayCrs: theme.defaultDisplayCrs || themes.defaultDisplayCrs || undefined
211
+ bbox: (_theme$bbox = theme.bbox) !== null && _theme$bbox !== void 0 ? _theme$bbox : defaultExtent,
212
+ initialBbox: (_theme$initialBbox = theme.initialBbox) !== null && _theme$initialBbox !== void 0 ? _theme$initialBbox : defaultExtent,
213
+ version: (_ref = (_theme$version = theme.version) !== null && _theme$version !== void 0 ? _theme$version : themes.defaultWMSVersion) !== null && _ref !== void 0 ? _ref : "1.3.0",
214
+ scales: (_ref2 = (_theme$scales = theme.scales) !== null && _theme$scales !== void 0 ? _theme$scales : themes.defaultScales) !== null && _ref2 !== void 0 ? _ref2 : MapUtils.getGoogleMercatorScales(0, 21),
215
+ printScales: (_theme$printScales = theme.printScales) !== null && _theme$printScales !== void 0 ? _theme$printScales : themes.defaultPrintScales,
216
+ printResolutions: (_theme$printResolutio = theme.printResolutions) !== null && _theme$printResolutio !== void 0 ? _theme$printResolutio : themes.defaultPrintResolutions,
217
+ printGrid: (_theme$printGrid = theme.printGrid) !== null && _theme$printGrid !== void 0 ? _theme$printGrid : themes.defaultPrintGrid,
218
+ searchProviders: (_theme$searchProvider = theme.searchProviders) !== null && _theme$searchProvider !== void 0 ? _theme$searchProvider : themes.defaultSearchProviders,
219
+ backgroundLayers: (_ref3 = (_theme$backgroundLaye = theme.backgroundLayers) !== null && _theme$backgroundLaye !== void 0 ? _theme$backgroundLaye : themes.defaultBackgroundLayers) !== null && _ref3 !== void 0 ? _ref3 : [],
220
+ mapTips: (_theme$mapTips = theme.mapTips) !== null && _theme$mapTips !== void 0 ? _theme$mapTips : themes.defaultMapTips,
221
+ defaultDisplayCrs: theme.defaultDisplayCrs || themes.defaultDisplayCrs
224
222
  });
225
223
 
226
224
  // Preserve extent if desired and possible
@@ -307,13 +305,18 @@ export function setCurrentTheme(theme, themes) {
307
305
  dispatch(showNotification("missinglayers", LocaleUtils.tr("app.missinglayers", diff.join(", ")), NotificationType.WARN, true));
308
306
  }
309
307
  }
310
- finishThemeSetup(dispatch, newTheme, themes, layerConfigs, insertPos, permalinkLayers, externalLayerRestorer, visibleBgLayer, initialTheme, initialTask);
308
+ finishThemeSetup(dispatch, newTheme, themes, layerConfigs, preserve, prevLayers, prevTheme, permalinkLayers, externalLayerRestorer, visibleBgLayer, initialTask, initialVisibilityPreset);
311
309
  });
312
310
  } else {
313
311
  if (!isEmpty(missingThemeLayers)) {
314
312
  dispatch(showNotification("missinglayers", LocaleUtils.tr("app.missinglayers", Object.keys(missingThemeLayers).join(", ")), NotificationType.WARN, true));
315
313
  }
316
- finishThemeSetup(dispatch, theme, themes, layerConfigs, insertPos, permalinkLayers, externalLayerRestorer, visibleBgLayer, initialTheme, initialTask);
314
+ finishThemeSetup(dispatch, theme, themes, layerConfigs, preserve, prevLayers, prevTheme, permalinkLayers, externalLayerRestorer, visibleBgLayer, initialTask, initialVisibilityPreset);
317
315
  }
318
316
  };
317
+ }
318
+ export function setBlankTheme(themes) {
319
+ return setCurrentTheme({
320
+ id: ""
321
+ }, themes);
319
322
  }
@@ -0,0 +1,312 @@
1
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
+ function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
3
+ function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
4
+ function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
5
+ function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
6
+ function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); }
7
+ function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; }
8
+ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
9
+ function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); }
10
+ function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); }
11
+ function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); }
12
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
13
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
14
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
15
+ /**
16
+ * Copyright 2021 Oslandia SAS <infos+qwc2@oslandia.com>
17
+ * All rights reserved.
18
+ *
19
+ * This source code is licensed under the BSD-style license found in the
20
+ * LICENSE file in the root directory of this source tree.
21
+ */
22
+
23
+ import React from 'react';
24
+ import classnames from 'classnames';
25
+ import isEmpty from 'lodash.isempty';
26
+ import PropTypes from 'prop-types';
27
+ import Icon from '../components/Icon';
28
+ import InputContainer from '../components/widgets/InputContainer';
29
+ import TextInput from '../components/widgets/TextInput';
30
+ import ConfigUtils from '../utils/ConfigUtils';
31
+ import LocaleUtils from '../utils/LocaleUtils';
32
+ import MiscUtils from '../utils/MiscUtils';
33
+ import './style/BookmarkPanel.css';
34
+
35
+ /**
36
+ * Allows managing user bookmarks.
37
+ * or user visibility presets if `visibilityPresetsMode` is true.
38
+ *
39
+ * Bookmarks are only allowed for authenticated users.
40
+ *
41
+ * Requires `permalinkServiceUrl` to point to a `qwc-permalink-service`.
42
+ */
43
+ var BookmarkPanel = /*#__PURE__*/function (_React$Component) {
44
+ function BookmarkPanel() {
45
+ var _this;
46
+ _classCallCheck(this, BookmarkPanel);
47
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
48
+ args[_key] = arguments[_key];
49
+ }
50
+ _this = _callSuper(this, BookmarkPanel, [].concat(args));
51
+ _defineProperty(_this, "state", {
52
+ renameBookmark: null,
53
+ currentBookmark: null,
54
+ busy: false
55
+ });
56
+ _defineProperty(_this, "bookmarkClicked", function (ev, bookmark) {
57
+ if (_this.state.renameBookmark) {
58
+ // pass
59
+ } else if (_this.props.openOnClick) {
60
+ _this.props.onOpen(bookmark.key, ev.button === 1);
61
+ } else if (_this.state.currentBookmark === bookmark.key) {
62
+ _this.setState({
63
+ currentBookmark: null,
64
+ description: ""
65
+ });
66
+ } else {
67
+ _this.setState({
68
+ currentBookmark: bookmark.key,
69
+ description: bookmark.description
70
+ });
71
+ }
72
+ });
73
+ _defineProperty(_this, "bookmarkDoubleClicked", function (ev, bookmark) {
74
+ if (!_this.state.renameBookmark) {
75
+ _this.props.onOpen(bookmark.key, false);
76
+ }
77
+ });
78
+ _defineProperty(_this, "updateBookmarkName", function (text) {
79
+ _this.setState({
80
+ busy: true
81
+ });
82
+ _this.props.onRename(_this.state.renameBookmark, text, function (success) {
83
+ if (!success) {
84
+ var _this$props$translati;
85
+ /* eslint-disable-next-line */
86
+ alert((_this$props$translati = _this.props.translations) === null || _this$props$translati === void 0 ? void 0 : _this$props$translati.savefailed);
87
+ }
88
+ _this.props.onRefresh();
89
+ });
90
+ });
91
+ _defineProperty(_this, "addBookmark", function () {
92
+ var _this$props$translati2;
93
+ _this.setState({
94
+ busy: true
95
+ });
96
+ _this.props.onAdd((_this$props$translati2 = _this.props.translations) === null || _this$props$translati2 === void 0 ? void 0 : _this$props$translati2.newbookmark, function (success, key) {
97
+ if (!success) {
98
+ var _this$props$translati3;
99
+ /* eslint-disable-next-line */
100
+ alert((_this$props$translati3 = _this.props.translations) === null || _this$props$translati3 === void 0 ? void 0 : _this$props$translati3.addfailed);
101
+ }
102
+ _this.props.onRefresh();
103
+ });
104
+ _this.setState({
105
+ description: "",
106
+ currentBookmark: null
107
+ });
108
+ });
109
+ _defineProperty(_this, "updateBookmark", function (key) {
110
+ _this.setState({
111
+ busy: true
112
+ });
113
+ var description = _this.props.bookmarks.find(function (bk) {
114
+ return bk.key === key;
115
+ }).description;
116
+ _this.props.onUpdate(key, description, function (success) {
117
+ if (!success) {
118
+ var _this$props$translati4;
119
+ /* eslint-disable-next-line */
120
+ alert((_this$props$translati4 = _this.props.translations) === null || _this$props$translati4 === void 0 ? void 0 : _this$props$translati4.savefailed);
121
+ }
122
+ _this.props.onRefresh();
123
+ });
124
+ });
125
+ _defineProperty(_this, "removeBookmark", function (key) {
126
+ _this.setState({
127
+ busy: true
128
+ });
129
+ _this.props.onRemove(key, function (success) {
130
+ if (!success) {
131
+ var _this$props$translati5;
132
+ /* eslint-disable-next-line */
133
+ alert((_this$props$translati5 = _this.props.translations) === null || _this$props$translati5 === void 0 ? void 0 : _this$props$translati5.removefailed);
134
+ }
135
+ _this.props.onRefresh();
136
+ });
137
+ });
138
+ return _this;
139
+ }
140
+ _inherits(BookmarkPanel, _React$Component);
141
+ return _createClass(BookmarkPanel, [{
142
+ key: "componentDidUpdate",
143
+ value: function componentDidUpdate(prevProps) {
144
+ // Check exact identity of bookmarks array to reset busy state in all cases
145
+ if (prevProps.bookmarks !== this.props.bookmarks) {
146
+ this.setState({
147
+ renameBookmark: null,
148
+ busy: false
149
+ });
150
+ // Select a recently added bookmark
151
+ var addedBookmark = this.props.bookmarks.find(function (bookmark) {
152
+ return !prevProps.bookmarks.some(function (prevBookmark) {
153
+ return prevBookmark.key === bookmark.key;
154
+ });
155
+ });
156
+ if (addedBookmark) {
157
+ this.setState({
158
+ renameBookmark: addedBookmark.key,
159
+ currentBookmark: null
160
+ });
161
+ }
162
+ }
163
+ }
164
+ }, {
165
+ key: "render",
166
+ value: function render() {
167
+ var _this$props$translati6,
168
+ _this$props$translati7,
169
+ _this$props$translati8,
170
+ _this2 = this,
171
+ _this$props$translati9,
172
+ _this$props$translati0,
173
+ _this$props$translati1,
174
+ _this$props$translati10,
175
+ _this$props$translati11;
176
+ var username = ConfigUtils.getConfigProp("username");
177
+ var currentBookmark = this.state.currentBookmark;
178
+ var buttonsDisabled = !currentBookmark || this.state.busy;
179
+ return !username ? /*#__PURE__*/React.createElement("div", {
180
+ className: "bookmark-body",
181
+ role: "body"
182
+ }, " ", (_this$props$translati6 = this.props.translations) === null || _this$props$translati6 === void 0 ? void 0 : _this$props$translati6.notloggedin) : /*#__PURE__*/React.createElement("div", {
183
+ className: "bookmark-body",
184
+ role: "body"
185
+ }, /*#__PURE__*/React.createElement("h4", {
186
+ className: "bookmark-header"
187
+ }, /*#__PURE__*/React.createElement("span", null, (_this$props$translati7 = this.props.translations) === null || _this$props$translati7 === void 0 ? void 0 : _this$props$translati7.manage), /*#__PURE__*/React.createElement("button", {
188
+ className: "button",
189
+ onClick: this.addBookmark,
190
+ title: (_this$props$translati8 = this.props.translations) === null || _this$props$translati8 === void 0 ? void 0 : _this$props$translati8.add
191
+ }, /*#__PURE__*/React.createElement(Icon, {
192
+ icon: "plus"
193
+ }))), !this.props.openOnClick ? /*#__PURE__*/React.createElement("div", {
194
+ className: "bookmark-actions controlgroup"
195
+ }, /*#__PURE__*/React.createElement("button", {
196
+ className: "button",
197
+ disabled: buttonsDisabled,
198
+ onClick: function onClick() {
199
+ return _this2.props.onOpen(currentBookmark, false);
200
+ },
201
+ title: (_this$props$translati9 = this.props.translations) === null || _this$props$translati9 === void 0 ? void 0 : _this$props$translati9.open
202
+ }, /*#__PURE__*/React.createElement(Icon, {
203
+ icon: "folder-open"
204
+ })), this.props.showOpenTab ? /*#__PURE__*/React.createElement("button", {
205
+ className: "button",
206
+ disabled: buttonsDisabled,
207
+ onClick: function onClick() {
208
+ return _this2.props.onOpen(currentBookmark, true);
209
+ },
210
+ title: (_this$props$translati0 = this.props.translations) === null || _this$props$translati0 === void 0 ? void 0 : _this$props$translati0.openTab
211
+ }, /*#__PURE__*/React.createElement(Icon, {
212
+ icon: "open_link"
213
+ })) : null, this.props.showZoomToExtent ? /*#__PURE__*/React.createElement("button", {
214
+ className: "button",
215
+ disabled: buttonsDisabled,
216
+ onClick: function onClick() {
217
+ return _this2.props.onZoomToExtent(currentBookmark);
218
+ },
219
+ title: (_this$props$translati1 = this.props.translations) === null || _this$props$translati1 === void 0 ? void 0 : _this$props$translati1.zoomToExtent
220
+ }, /*#__PURE__*/React.createElement(Icon, {
221
+ icon: "zoom"
222
+ })) : null, /*#__PURE__*/React.createElement("button", {
223
+ className: "button",
224
+ disabled: buttonsDisabled,
225
+ onClick: function onClick() {
226
+ return _this2.updateBookmark(currentBookmark);
227
+ },
228
+ title: (_this$props$translati10 = this.props.translations) === null || _this$props$translati10 === void 0 ? void 0 : _this$props$translati10.update
229
+ }, /*#__PURE__*/React.createElement(Icon, {
230
+ icon: "save"
231
+ }))) : null, /*#__PURE__*/React.createElement("div", {
232
+ className: "bookmark-list"
233
+ }, this.props.bookmarks.map(function (bookmark) {
234
+ var _this2$props$translat;
235
+ var itemclasses = classnames({
236
+ "bookmark-list-item": true,
237
+ "bookmark-list-item-active": currentBookmark === bookmark.key
238
+ });
239
+ return /*#__PURE__*/React.createElement("div", {
240
+ className: itemclasses,
241
+ key: bookmark.key,
242
+ onAuxClick: function onAuxClick(ev) {
243
+ return _this2.bookmarkClicked(ev, bookmark);
244
+ },
245
+ onClick: function onClick(ev) {
246
+ return _this2.bookmarkClicked(ev, bookmark);
247
+ },
248
+ onDoubleClick: function onDoubleClick(ev) {
249
+ return _this2.bookmarkDoubleClicked(ev, bookmark);
250
+ },
251
+ title: ((_this2$props$translat = _this2.props.translations) === null || _this2$props$translat === void 0 ? void 0 : _this2$props$translat.lastUpdate) + ": " + bookmark.date
252
+ }, _this2.state.renameBookmark === bookmark.key ? /*#__PURE__*/React.createElement(InputContainer, null, /*#__PURE__*/React.createElement(TextInput, {
253
+ focusOnRef: true,
254
+ onChange: _this2.updateBookmarkName,
255
+ onNoChange: function onNoChange() {
256
+ return _this2.setState({
257
+ renameBookmark: null
258
+ });
259
+ },
260
+ role: "input",
261
+ showClear: false,
262
+ value: bookmark.description
263
+ }), /*#__PURE__*/React.createElement(Icon, {
264
+ icon: "ok",
265
+ onClick: MiscUtils.killEvent,
266
+ role: "suffix"
267
+ })) : /*#__PURE__*/React.createElement("span", null, bookmark.description), _this2.state.renameBookmark !== bookmark.key ? /*#__PURE__*/React.createElement(Icon, {
268
+ icon: "draw",
269
+ onClick: function onClick(ev) {
270
+ _this2.setState({
271
+ renameBookmark: bookmark.key,
272
+ currentBookmark: null
273
+ });
274
+ MiscUtils.killEvent(ev);
275
+ },
276
+ title: LocaleUtils.tr("common.rename")
277
+ }) : null, _this2.state.renameBookmark !== bookmark.key ? /*#__PURE__*/React.createElement(Icon, {
278
+ disabled: _this2.state.busy,
279
+ icon: "trash",
280
+ onClick: function onClick(ev) {
281
+ _this2.removeBookmark(bookmark.key);
282
+ MiscUtils.killEvent(ev);
283
+ },
284
+ title: LocaleUtils.tr("common.delete")
285
+ }) : null);
286
+ }), isEmpty(this.props.bookmarks) ? /*#__PURE__*/React.createElement("div", {
287
+ className: "bookmark-list-item-empty"
288
+ }, (_this$props$translati11 = this.props.translations) === null || _this$props$translati11 === void 0 ? void 0 : _this$props$translati11.nobookmarks) : null));
289
+ }
290
+ }]);
291
+ }(React.Component);
292
+ _defineProperty(BookmarkPanel, "availableIn3D", true);
293
+ _defineProperty(BookmarkPanel, "propTypes", {
294
+ bookmarks: PropTypes.array,
295
+ onAdd: PropTypes.func,
296
+ onOpen: PropTypes.func,
297
+ onRefresh: PropTypes.func,
298
+ onRemove: PropTypes.func,
299
+ onRename: PropTypes.func,
300
+ onUpdate: PropTypes.func,
301
+ onZoomToExtent: PropTypes.func,
302
+ /** Whether to directly open the bookmark on click / middle click, instead of showing dedicated open buttons. */
303
+ openOnClick: PropTypes.bool,
304
+ showOpenTab: PropTypes.bool,
305
+ showZoomToExtent: PropTypes.bool,
306
+ translations: PropTypes.objectOf(PropTypes.string)
307
+ });
308
+ _defineProperty(BookmarkPanel, "defaultProps", {
309
+ showOpenTab: true,
310
+ showZoomToExtent: false
311
+ });
312
+ export default BookmarkPanel;