qwc2 2026.5.11 → 2026.5.26

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 (57) hide show
  1. package/actions/bookmark.js +40 -0
  2. package/actions/theme.js +9 -18
  3. package/components/AttributeForm.js +8 -0
  4. package/components/BookmarkPanel.js +307 -0
  5. package/components/MapButton.js +0 -12
  6. package/components/PluginsContainer.js +24 -20
  7. package/components/QtDesignerForm.js +2 -1
  8. package/components/ResizeableWindow.js +4 -2
  9. package/components/StandardApp.js +8 -1
  10. package/components/widgets/DateTimeInput.js +57 -16
  11. package/components/widgets/GroupSelect.js +94 -0
  12. package/package.json +1 -1
  13. package/plugins/BackgroundSwitcher.js +157 -71
  14. package/plugins/Bookmark.js +57 -266
  15. package/plugins/BottomBar.js +49 -5
  16. package/plugins/Editing.js +5 -24
  17. package/plugins/Portal.js +14 -3
  18. package/plugins/Print.js +6 -5
  19. package/plugins/Redlining.js +1 -1
  20. package/plugins/Settings.js +27 -48
  21. package/plugins/VisibilityPreset.js +127 -0
  22. package/plugins/map/RedliningSupport.js +5 -3
  23. package/plugins/map3d/BackgroundSwitcher3D.js +20 -3
  24. package/plugins/style/BackgroundSwitcher.css +41 -10
  25. package/plugins/style/Portal.css +7 -0
  26. package/reducers/bookmark.js +39 -0
  27. package/reducers/theme.js +2 -9
  28. package/static/translations/bg-BG.json +16 -1
  29. package/static/translations/ca-ES.json +16 -1
  30. package/static/translations/cs-CZ.json +16 -1
  31. package/static/translations/de-CH.json +16 -1
  32. package/static/translations/de-DE.json +16 -1
  33. package/static/translations/en-US.json +16 -1
  34. package/static/translations/es-ES.json +16 -1
  35. package/static/translations/fi-FI.json +16 -1
  36. package/static/translations/fr-FR.json +16 -1
  37. package/static/translations/hu-HU.json +16 -1
  38. package/static/translations/it-IT.json +16 -1
  39. package/static/translations/ja-JP.json +16 -1
  40. package/static/translations/nl-NL.json +16 -1
  41. package/static/translations/no-NO.json +16 -1
  42. package/static/translations/pl-PL.json +16 -1
  43. package/static/translations/pt-BR.json +16 -1
  44. package/static/translations/pt-PT.json +16 -1
  45. package/static/translations/ro-RO.json +16 -1
  46. package/static/translations/ru-RU.json +16 -1
  47. package/static/translations/sv-SE.json +16 -1
  48. package/static/translations/tr-TR.json +16 -1
  49. package/static/translations/tsconfig.json +14 -1
  50. package/static/translations/uk-UA.json +16 -1
  51. package/utils/CoordinatesUtils.js +4 -4
  52. package/utils/IdentifyUtils.js +1 -1
  53. package/utils/LayerUtils.js +24 -13
  54. package/utils/PermaLinkUtils.js +31 -22
  55. package/utils/ThemeUtils.js +5 -2
  56. package/utils/VectorLayerUtils.js +21 -22
  57. /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,13 +33,12 @@ 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';
40
40
  export var SET_THEME_LAYERS_LIST = 'SET_THEME_LAYERS_LIST';
41
41
  export var SET_CURRENT_THEME = 'SET_CURRENT_THEME';
42
- export var SWITCHING_THEME = 'SWITCHING_THEME';
43
42
  export function themesLoaded(themes) {
44
43
  return {
45
44
  type: THEMES_LOADED,
@@ -52,7 +51,7 @@ export function setThemeLayersList(theme) {
52
51
  themelist: theme
53
52
  };
54
53
  }
55
- export function finishThemeSetup(dispatch, theme, themes, layerConfigs, insertPos, permalinkLayers, externalLayerRestorer, visibleBgLayer, initialTheme, initialTask) {
54
+ export function finishThemeSetup(dispatch, theme, themes, layerConfigs, insertPos, permalinkLayers, externalLayerRestorer, visibleBgLayer, initialTheme, initialTask, initialVisibilityPreset) {
56
55
  var _theme$config$section, _theme$config, _theme$config2;
57
56
  // Create layer
58
57
  var themeLayer = ThemeUtils.createThemeLayer(theme, themes);
@@ -89,7 +88,7 @@ export function finishThemeSetup(dispatch, theme, themes, layerConfigs, insertPo
89
88
  dispatch(showNotification("missingbglayer", LocaleUtils.tr("app.missingbg", visibleBgLayer), NotificationType.WARN, true));
90
89
  }
91
90
  }
92
- var _iterator = _createForOfIteratorHelper(bgLayers),
91
+ var _iterator = _createForOfIteratorHelper(bgLayers.reverse()),
93
92
  _step;
94
93
  try {
95
94
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
@@ -144,10 +143,9 @@ export function finishThemeSetup(dispatch, theme, themes, layerConfigs, insertPo
144
143
  type: SET_CURRENT_THEME,
145
144
  theme: theme
146
145
  });
147
- dispatch({
148
- type: SWITCHING_THEME,
149
- switching: false
150
- });
146
+ if (initialVisibilityPreset) {
147
+ dispatch(setThemeLayersVisibilityPreset(initialVisibilityPreset));
148
+ }
151
149
  var section = ConfigUtils.isMobile() ? "mobile" : "desktop";
152
150
  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);
153
151
  if (task) {
@@ -164,6 +162,7 @@ export function setCurrentTheme(theme, themes) {
164
162
  var themeLayerRestorer = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : null;
165
163
  var externalLayerRestorer = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : null;
166
164
  var initialTask = arguments.length > 9 && arguments[9] !== undefined ? arguments[9] : null;
165
+ var initialVisibilityPreset = arguments.length > 10 && arguments[10] !== undefined ? arguments[10] : null;
167
166
  return function (dispatch, getState) {
168
167
  var _getState$layers, _ref, _theme$mapTips;
169
168
  var curLayers = ((_getState$layers = getState().layers) === null || _getState$layers === void 0 ? void 0 : _getState$layers.flat) || [];
@@ -173,10 +172,6 @@ export function setCurrentTheme(theme, themes) {
173
172
  return;
174
173
  }
175
174
  var initialTheme = !getState().theme.current;
176
- dispatch({
177
- type: SWITCHING_THEME,
178
- switching: true
179
- });
180
175
 
181
176
  // Get current background layer if it needs to be preserved
182
177
  if (preserve && visibleBgLayer === null && ConfigUtils.getConfigProp("preserveBackgroundOnThemeSwitch", theme) === true) {
@@ -215,10 +210,6 @@ export function setCurrentTheme(theme, themes) {
215
210
  }
216
211
  dispatch(setSwipe(null));
217
212
  if (!theme) {
218
- dispatch({
219
- type: SWITCHING_THEME,
220
- switching: false
221
- });
222
213
  return;
223
214
  }
224
215
 
@@ -320,13 +311,13 @@ export function setCurrentTheme(theme, themes) {
320
311
  dispatch(showNotification("missinglayers", LocaleUtils.tr("app.missinglayers", diff.join(", ")), NotificationType.WARN, true));
321
312
  }
322
313
  }
323
- finishThemeSetup(dispatch, newTheme, themes, layerConfigs, insertPos, permalinkLayers, externalLayerRestorer, visibleBgLayer, initialTheme, initialTask);
314
+ finishThemeSetup(dispatch, newTheme, themes, layerConfigs, insertPos, permalinkLayers, externalLayerRestorer, visibleBgLayer, initialTheme, initialTask, initialVisibilityPreset);
324
315
  });
325
316
  } else {
326
317
  if (!isEmpty(missingThemeLayers)) {
327
318
  dispatch(showNotification("missinglayers", LocaleUtils.tr("app.missinglayers", Object.keys(missingThemeLayers).join(", ")), NotificationType.WARN, true));
328
319
  }
329
- finishThemeSetup(dispatch, theme, themes, layerConfigs, insertPos, permalinkLayers, externalLayerRestorer, visibleBgLayer, initialTheme, initialTask);
320
+ finishThemeSetup(dispatch, theme, themes, layerConfigs, insertPos, permalinkLayers, externalLayerRestorer, visibleBgLayer, initialTheme, initialTask, initialVisibilityPreset);
330
321
  }
331
322
  };
332
323
  }
@@ -597,6 +597,10 @@ var AttributeForm = /*#__PURE__*/function (_React$Component) {
597
597
  // Convert text NULL to null
598
598
  value = null;
599
599
  }
600
+ if (nrelFieldConfig.type === 'number' && value !== null) {
601
+ // Ensure numeric values are numbers and not strings
602
+ value = Number(value);
603
+ }
600
604
 
601
605
  // relationValues for table must exist as rows are either pre-existing or were added
602
606
  if (!(field in relationValues[datasetname].features[index].properties)) {
@@ -639,6 +643,10 @@ var AttributeForm = /*#__PURE__*/function (_React$Component) {
639
643
  // Convert text NULL to null
640
644
  value = null;
641
645
  }
646
+ if (fieldConfig.type === 'number' && value !== null) {
647
+ // Ensure numeric values are numbers and not strings
648
+ value = Number(value);
649
+ }
642
650
  if (!(name in feature.properties)) {
643
651
  feature.defaultedProperties = [].concat(_toConsumableArray(feature.defaultedProperties || []), [name]);
644
652
  }
@@ -0,0 +1,307 @@
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, "updateBookmarkName", function (text) {
74
+ _this.setState({
75
+ busy: true
76
+ });
77
+ _this.props.onRename(_this.state.renameBookmark, text, function (success) {
78
+ if (!success) {
79
+ var _this$props$translati;
80
+ /* eslint-disable-next-line */
81
+ alert((_this$props$translati = _this.props.translations) === null || _this$props$translati === void 0 ? void 0 : _this$props$translati.savefailed);
82
+ }
83
+ _this.props.onRefresh();
84
+ });
85
+ });
86
+ _defineProperty(_this, "addBookmark", function () {
87
+ var _this$props$translati2;
88
+ _this.setState({
89
+ busy: true
90
+ });
91
+ _this.props.onAdd((_this$props$translati2 = _this.props.translations) === null || _this$props$translati2 === void 0 ? void 0 : _this$props$translati2.newbookmark, function (success, key) {
92
+ if (!success) {
93
+ var _this$props$translati3;
94
+ /* eslint-disable-next-line */
95
+ alert((_this$props$translati3 = _this.props.translations) === null || _this$props$translati3 === void 0 ? void 0 : _this$props$translati3.addfailed);
96
+ }
97
+ _this.props.onRefresh();
98
+ });
99
+ _this.setState({
100
+ description: "",
101
+ currentBookmark: null
102
+ });
103
+ });
104
+ _defineProperty(_this, "updateBookmark", function (key) {
105
+ _this.setState({
106
+ busy: true
107
+ });
108
+ var description = _this.props.bookmarks.find(function (bk) {
109
+ return bk.key === key;
110
+ }).description;
111
+ _this.props.onUpdate(key, description, function (success) {
112
+ if (!success) {
113
+ var _this$props$translati4;
114
+ /* eslint-disable-next-line */
115
+ alert((_this$props$translati4 = _this.props.translations) === null || _this$props$translati4 === void 0 ? void 0 : _this$props$translati4.savefailed);
116
+ }
117
+ _this.props.onRefresh();
118
+ });
119
+ });
120
+ _defineProperty(_this, "removeBookmark", function (key) {
121
+ _this.setState({
122
+ busy: true
123
+ });
124
+ _this.props.onRemove(key, function (success) {
125
+ if (!success) {
126
+ var _this$props$translati5;
127
+ /* eslint-disable-next-line */
128
+ alert((_this$props$translati5 = _this.props.translations) === null || _this$props$translati5 === void 0 ? void 0 : _this$props$translati5.removefailed);
129
+ }
130
+ _this.props.onRefresh();
131
+ });
132
+ });
133
+ return _this;
134
+ }
135
+ _inherits(BookmarkPanel, _React$Component);
136
+ return _createClass(BookmarkPanel, [{
137
+ key: "componentDidUpdate",
138
+ value: function componentDidUpdate(prevProps) {
139
+ // Check exact identity of bookmarks array to reset busy state in all cases
140
+ if (prevProps.bookmarks !== this.props.bookmarks) {
141
+ this.setState({
142
+ renameBookmark: null,
143
+ busy: false
144
+ });
145
+ // Select a recently added bookmark
146
+ var addedBookmark = this.props.bookmarks.find(function (bookmark) {
147
+ return !prevProps.bookmarks.some(function (prevBookmark) {
148
+ return prevBookmark.key === bookmark.key;
149
+ });
150
+ });
151
+ if (addedBookmark) {
152
+ this.setState({
153
+ renameBookmark: addedBookmark.key,
154
+ currentBookmark: null
155
+ });
156
+ }
157
+ }
158
+ }
159
+ }, {
160
+ key: "render",
161
+ value: function render() {
162
+ var _this$props$translati6,
163
+ _this$props$translati7,
164
+ _this$props$translati8,
165
+ _this2 = this,
166
+ _this$props$translati9,
167
+ _this$props$translati0,
168
+ _this$props$translati1,
169
+ _this$props$translati10,
170
+ _this$props$translati11;
171
+ var username = ConfigUtils.getConfigProp("username");
172
+ var currentBookmark = this.state.currentBookmark;
173
+ var buttonsDisabled = !currentBookmark || this.state.busy;
174
+ return !username ? /*#__PURE__*/React.createElement("div", {
175
+ className: "bookmark-body",
176
+ role: "body"
177
+ }, " ", (_this$props$translati6 = this.props.translations) === null || _this$props$translati6 === void 0 ? void 0 : _this$props$translati6.notloggedin) : /*#__PURE__*/React.createElement("div", {
178
+ className: "bookmark-body",
179
+ role: "body"
180
+ }, /*#__PURE__*/React.createElement("h4", {
181
+ className: "bookmark-header"
182
+ }, /*#__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", {
183
+ className: "button",
184
+ onClick: this.addBookmark,
185
+ title: (_this$props$translati8 = this.props.translations) === null || _this$props$translati8 === void 0 ? void 0 : _this$props$translati8.add
186
+ }, /*#__PURE__*/React.createElement(Icon, {
187
+ icon: "plus"
188
+ }))), !this.props.openOnClick ? /*#__PURE__*/React.createElement("div", {
189
+ className: "bookmark-actions controlgroup"
190
+ }, /*#__PURE__*/React.createElement("button", {
191
+ className: "button",
192
+ disabled: buttonsDisabled,
193
+ onClick: function onClick() {
194
+ return _this2.props.onOpen(currentBookmark, false);
195
+ },
196
+ title: (_this$props$translati9 = this.props.translations) === null || _this$props$translati9 === void 0 ? void 0 : _this$props$translati9.open
197
+ }, /*#__PURE__*/React.createElement(Icon, {
198
+ icon: "folder-open"
199
+ })), this.props.showOpenTab ? /*#__PURE__*/React.createElement("button", {
200
+ className: "button",
201
+ disabled: buttonsDisabled,
202
+ onClick: function onClick() {
203
+ return _this2.props.onOpen(currentBookmark, true);
204
+ },
205
+ title: (_this$props$translati0 = this.props.translations) === null || _this$props$translati0 === void 0 ? void 0 : _this$props$translati0.openTab
206
+ }, /*#__PURE__*/React.createElement(Icon, {
207
+ icon: "open_link"
208
+ })) : null, this.props.showZoomToExtent ? /*#__PURE__*/React.createElement("button", {
209
+ className: "button",
210
+ disabled: buttonsDisabled,
211
+ onClick: function onClick() {
212
+ return _this2.props.onZoomToExtent(currentBookmark);
213
+ },
214
+ title: (_this$props$translati1 = this.props.translations) === null || _this$props$translati1 === void 0 ? void 0 : _this$props$translati1.zoomToExtent
215
+ }, /*#__PURE__*/React.createElement(Icon, {
216
+ icon: "zoom"
217
+ })) : null, /*#__PURE__*/React.createElement("button", {
218
+ className: "button",
219
+ disabled: buttonsDisabled,
220
+ onClick: function onClick() {
221
+ return _this2.updateBookmark(currentBookmark);
222
+ },
223
+ title: (_this$props$translati10 = this.props.translations) === null || _this$props$translati10 === void 0 ? void 0 : _this$props$translati10.update
224
+ }, /*#__PURE__*/React.createElement(Icon, {
225
+ icon: "save"
226
+ }))) : null, /*#__PURE__*/React.createElement("div", {
227
+ className: "bookmark-list"
228
+ }, this.props.bookmarks.map(function (bookmark) {
229
+ var _this2$props$translat;
230
+ var itemclasses = classnames({
231
+ "bookmark-list-item": true,
232
+ "bookmark-list-item-active": currentBookmark === bookmark.key
233
+ });
234
+ return /*#__PURE__*/React.createElement("div", {
235
+ className: itemclasses,
236
+ key: bookmark.key,
237
+ onAuxClick: function onAuxClick(ev) {
238
+ return _this2.bookmarkClicked(ev, bookmark);
239
+ },
240
+ onClick: function onClick(ev) {
241
+ return _this2.bookmarkClicked(ev, bookmark);
242
+ },
243
+ onDoubleClick: function onDoubleClick() {
244
+ return _this2.props.onOpen(bookmark.key, false);
245
+ },
246
+ title: ((_this2$props$translat = _this2.props.translations) === null || _this2$props$translat === void 0 ? void 0 : _this2$props$translat.lastUpdate) + ": " + bookmark.date
247
+ }, _this2.state.renameBookmark === bookmark.key ? /*#__PURE__*/React.createElement(InputContainer, null, /*#__PURE__*/React.createElement(TextInput, {
248
+ focusOnRef: true,
249
+ onChange: _this2.updateBookmarkName,
250
+ onNoChange: function onNoChange() {
251
+ return _this2.setState({
252
+ renameBookmark: null
253
+ });
254
+ },
255
+ role: "input",
256
+ showClear: false,
257
+ value: bookmark.description
258
+ }), /*#__PURE__*/React.createElement(Icon, {
259
+ icon: "ok",
260
+ onClick: MiscUtils.killEvent,
261
+ role: "suffix"
262
+ })) : /*#__PURE__*/React.createElement("span", null, bookmark.description), _this2.state.renameBookmark !== bookmark.key ? /*#__PURE__*/React.createElement(Icon, {
263
+ icon: "draw",
264
+ onClick: function onClick(ev) {
265
+ _this2.setState({
266
+ renameBookmark: bookmark.key,
267
+ currentBookmark: null
268
+ });
269
+ MiscUtils.killEvent(ev);
270
+ },
271
+ title: LocaleUtils.tr("common.rename")
272
+ }) : null, _this2.state.renameBookmark !== bookmark.key ? /*#__PURE__*/React.createElement(Icon, {
273
+ disabled: _this2.state.busy,
274
+ icon: "trash",
275
+ onClick: function onClick(ev) {
276
+ _this2.removeBookmark(bookmark.key);
277
+ MiscUtils.killEvent(ev);
278
+ },
279
+ title: LocaleUtils.tr("common.delete")
280
+ }) : null);
281
+ }), isEmpty(this.props.bookmarks) ? /*#__PURE__*/React.createElement("div", {
282
+ className: "bookmark-list-item-empty"
283
+ }, (_this$props$translati11 = this.props.translations) === null || _this$props$translati11 === void 0 ? void 0 : _this$props$translati11.nobookmarks) : null));
284
+ }
285
+ }]);
286
+ }(React.Component);
287
+ _defineProperty(BookmarkPanel, "availableIn3D", true);
288
+ _defineProperty(BookmarkPanel, "propTypes", {
289
+ bookmarks: PropTypes.array,
290
+ onAdd: PropTypes.func,
291
+ onOpen: PropTypes.func,
292
+ onRefresh: PropTypes.func,
293
+ onRemove: PropTypes.func,
294
+ onRename: PropTypes.func,
295
+ onUpdate: PropTypes.func,
296
+ onZoomToExtent: PropTypes.func,
297
+ /** Whether to directly open the bookmark on click / middle click, instead of showing dedicated open buttons. */
298
+ openOnClick: PropTypes.bool,
299
+ showOpenTab: PropTypes.bool,
300
+ showZoomToExtent: PropTypes.bool,
301
+ translations: PropTypes.objectOf(PropTypes.string)
302
+ });
303
+ _defineProperty(BookmarkPanel, "defaultProps", {
304
+ showOpenTab: true,
305
+ showZoomToExtent: false
306
+ });
307
+ export default BookmarkPanel;
@@ -35,18 +35,6 @@ var MapButton = /*#__PURE__*/function (_React$Component) {
35
35
  }
36
36
  _inherits(MapButton, _React$Component);
37
37
  return _createClass(MapButton, [{
38
- key: "componentDidMount",
39
- value: function componentDidMount() {
40
- this.componentDidUpdate({});
41
- }
42
- }, {
43
- key: "componentDidUpdate",
44
- value: function componentDidUpdate(prevProps) {
45
- if (this.context && this.props.position !== prevProps.position) {
46
- this.context.recomputeSpacers();
47
- }
48
- }
49
- }, {
50
38
  key: "render",
51
39
  value: function render() {
52
40
  if (!this.context) {
@@ -171,6 +171,7 @@ var PluginsContainer = /*#__PURE__*/function (_React$Component) {
171
171
  // Reorder child nodes for natural tab focus order
172
172
  var children = _toConsumableArray(el.children);
173
173
  var slots = {};
174
+ var usedSlots = new Set();
174
175
  children.forEach(function (child) {
175
176
  if (child.dataset.subslot === undefined) {
176
177
  var _slots$child$dataset$;
@@ -178,12 +179,35 @@ var PluginsContainer = /*#__PURE__*/function (_React$Component) {
178
179
  slots[child.dataset.slot] = child.dataset.subslot = subslot;
179
180
  child.style.order = 100 * parseInt(child.style.order, 10) + subslot;
180
181
  }
182
+ if (child.dataset.slot !== undefined) {
183
+ usedSlots.add(child.dataset.slot);
184
+ }
181
185
  });
182
186
  children.sort(function (a, b) {
183
187
  return b.style.order - a.style.order;
184
188
  }).forEach(function (node) {
185
189
  return el.appendChild(node);
186
190
  });
191
+ var slotString = _toConsumableArray(usedSlots).sort().join(":");
192
+ if (slotString !== el.dataset.slotString) {
193
+ // Recompute spacers
194
+ children.forEach(function (child) {
195
+ if (child.dataset.spacer) {
196
+ el.removeChild(child);
197
+ }
198
+ });
199
+ var maxSlot = Math.max.apply(Math, _toConsumableArray(usedSlots));
200
+ for (var i = 0; i < maxSlot; ++i) {
201
+ if (!usedSlots.has(String(i))) {
202
+ var child = document.createElement("div");
203
+ child.className = "map-buttons-spacer";
204
+ child.dataset.spacer = 1;
205
+ child.style.order = 100 * i;
206
+ el.appendChild(child);
207
+ }
208
+ }
209
+ el.dataset.slotString = slotString;
210
+ }
187
211
  }
188
212
  };
189
213
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
@@ -202,26 +226,6 @@ var PluginsContainer = /*#__PURE__*/function (_React$Component) {
202
226
  childList: true
203
227
  });
204
228
  resizeObserver.observe(el);
205
- el.recomputeSpacers = function () {
206
- var slots = new Set();
207
- Array.from(el.childNodes).forEach(function (child) {
208
- if (child.dataset.spacer) {
209
- el.removeChild(child);
210
- } else {
211
- slots.add(child.dataset.slot);
212
- }
213
- });
214
- var maxSlot = Math.max.apply(Math, _toConsumableArray(slots));
215
- for (var i = 0; i < maxSlot; ++i) {
216
- if (!slots.has(String(i))) {
217
- var child = document.createElement("div");
218
- child.className = "map-buttons-spacer";
219
- child.dataset.spacer = 1;
220
- child.style.order = 100 * i;
221
- el.appendChild(child);
222
- }
223
- }
224
- };
225
229
  }
226
230
  });
227
231
  return _this;
@@ -612,7 +612,8 @@ var QtDesignerForm = /*#__PURE__*/function (_React$Component) {
612
612
  readOnly: inputConstraints.readOnly,
613
613
  required: inputConstraints.required,
614
614
  style: fontStyle,
615
- value: value
615
+ value: value,
616
+ valueHasTz: field.data_type === "timestamp with time zone"
616
617
  });
617
618
  } else if (widget["class"] === "QListWidget") {
618
619
  return /*#__PURE__*/React.createElement(ListInput, {
@@ -54,7 +54,7 @@ var ResizeableWindow = /*#__PURE__*/function (_React$Component) {
54
54
  ev.stopPropagation();
55
55
  });
56
56
  _defineProperty(_this, "renderTitleBar", function () {
57
- if (_this.props.fullscreen) {
57
+ if (_this.props.fullscreen || _this.props.hideTitleBar) {
58
58
  return null;
59
59
  }
60
60
  var maximized = _this.state.geometry.maximized ? true : false;
@@ -118,7 +118,7 @@ var ResizeableWindow = /*#__PURE__*/function (_React$Component) {
118
118
  onClick: entry.callback,
119
119
  title: entry.title
120
120
  });
121
- }), !maximized && dockable ? /*#__PURE__*/React.createElement(Icon, {
121
+ }), !maximized && dockable && !_this.props.hideDockIcon ? /*#__PURE__*/React.createElement(Icon, {
122
122
  className: iconClasses,
123
123
  icon: dockIcon,
124
124
  onClick: _this.toggleDock,
@@ -549,6 +549,8 @@ _defineProperty(ResizeableWindow, "propTypes", {
549
549
  })),
550
550
  fitHeight: PropTypes.bool,
551
551
  fullscreen: PropTypes.bool,
552
+ hideDockIcon: PropTypes.bool,
553
+ hideTitleBar: PropTypes.bool,
552
554
  icon: PropTypes.string,
553
555
  initialHeight: PropTypes.number,
554
556
  initialWidth: PropTypes.number,
@@ -40,6 +40,7 @@ import deepmerge from 'deepmerge';
40
40
  import { register as olProj4Register } from 'ol/proj/proj4';
41
41
  import Proj4js from 'proj4';
42
42
  import PropTypes from 'prop-types';
43
+ import { refreshBookmarks, refreshVisibilityPresets } from '../actions/bookmark';
43
44
  import { localConfigLoaded, setStartupParameters, setColorScheme } from '../actions/localConfig';
44
45
  import { changeLocale } from '../actions/locale';
45
46
  import { setCurrentTask } from '../actions/task';
@@ -163,7 +164,7 @@ var AppContainerComponent = /*#__PURE__*/function (_React$Component) {
163
164
  layerParams.reverse();
164
165
  }
165
166
  var initialTaskParam = params.task ? JSON.parse(decodeURIComponent(params.task)) : null;
166
- _this.props.setCurrentTheme(theme, themes, false, initialExtent, layerParams, (_params$bl = params.bl) !== null && _params$bl !== void 0 ? _params$bl : null, state.layers, _this.props.appConfig.themeLayerRestorer, _this.props.appConfig.externalLayerRestorer, initialTaskParam);
167
+ _this.props.setCurrentTheme(theme, themes, false, initialExtent, layerParams, (_params$bl = params.bl) !== null && _params$bl !== void 0 ? _params$bl : null, state.layers, _this.props.appConfig.themeLayerRestorer, _this.props.appConfig.externalLayerRestorer, initialTaskParam, state.visibilityPreset);
167
168
  } else if (!ConfigUtils.havePlugin("Portal")) {
168
169
  _this.props.showNotification("missingdefaulttheme", LocaleUtils.tr("app.missingdefaulttheme", params.t), NotificationType.WARN, true);
169
170
  }
@@ -204,6 +205,7 @@ var AppContainerComponent = /*#__PURE__*/function (_React$Component) {
204
205
  }(React.Component);
205
206
  _defineProperty(AppContainerComponent, "propTypes", {
206
207
  appConfig: PropTypes.object,
208
+ currentTheme: PropTypes.object,
207
209
  defaultUrlParams: PropTypes.string,
208
210
  haveMapSize: PropTypes.bool,
209
211
  localConfig: PropTypes.object,
@@ -211,6 +213,7 @@ _defineProperty(AppContainerComponent, "propTypes", {
211
213
  setBottombarHeight: PropTypes.func,
212
214
  setCurrentTask: PropTypes.func,
213
215
  setCurrentTheme: PropTypes.func,
216
+ setThemeLayersVisibilityPreset: PropTypes.func,
214
217
  setTopbarHeight: PropTypes.func,
215
218
  showNotification: PropTypes.func,
216
219
  startupConfig: PropTypes.object,
@@ -331,6 +334,10 @@ var StandardApp = /*#__PURE__*/function (_React$Component2) {
331
334
  var colorScheme = initialParams.style || storedColorScheme || ConfigUtils.getConfigProp("defaultColorScheme");
332
335
  StandardApp.store.dispatch(setColorScheme(colorScheme));
333
336
 
337
+ // Load all bookmarks & visiblity presets
338
+ StandardApp.store.dispatch(refreshBookmarks());
339
+ StandardApp.store.dispatch(refreshVisibilityPresets());
340
+
334
341
  // Resolve permalink and restore settings
335
342
  resolvePermaLink(initialParams, function (params, state, success) {
336
343
  StandardApp.store.dispatch(setStartupParameters(params, state));