qwc2 2026.7.8 → 2026.7.18

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 (40) hide show
  1. package/components/EditUploadField.js +6 -2
  2. package/components/ExportSelection.js +33 -3
  3. package/components/MapSelection.js +2 -11
  4. package/components/SearchBox.js +10 -10
  5. package/components/share/ShareSocials.js +6 -14
  6. package/components/widgets/LayerCatalogWidget.js +14 -7
  7. package/components/widgets/MenuButton.js +8 -5
  8. package/extra/plugins/PlotInfoTool.js +1 -1
  9. package/package.json +4 -3
  10. package/plugins/BackgroundSwitcher.js +7 -3
  11. package/plugins/Identify.js +200 -135
  12. package/plugins/LayerCatalog.js +7 -3
  13. package/plugins/ThemeBrowser.js +2 -2
  14. package/plugins/ZoomButtons.js +3 -3
  15. package/plugins/map3d/MapExport3D.js +23 -10
  16. package/plugins/style/Identify.css +13 -0
  17. package/static/translations/bg-BG.json +3 -2
  18. package/static/translations/ca-ES.json +4 -3
  19. package/static/translations/cs-CZ.json +3 -2
  20. package/static/translations/de-CH.json +3 -2
  21. package/static/translations/de-DE.json +3 -2
  22. package/static/translations/en-US.json +4 -3
  23. package/static/translations/es-ES.json +4 -3
  24. package/static/translations/fi-FI.json +3 -2
  25. package/static/translations/fr-FR.json +4 -3
  26. package/static/translations/hu-HU.json +3 -2
  27. package/static/translations/it-IT.json +3 -2
  28. package/static/translations/ja-JP.json +3 -2
  29. package/static/translations/nl-NL.json +3 -2
  30. package/static/translations/no-NO.json +3 -2
  31. package/static/translations/pl-PL.json +3 -2
  32. package/static/translations/pt-BR.json +3 -2
  33. package/static/translations/pt-PT.json +3 -2
  34. package/static/translations/ro-RO.json +436 -435
  35. package/static/translations/ru-RU.json +3 -2
  36. package/static/translations/sv-SE.json +3 -2
  37. package/static/translations/tr-TR.json +3 -2
  38. package/static/translations/tsconfig.json +3 -2
  39. package/static/translations/uk-UA.json +3 -2
  40. package/utils/MiscUtils.js +15 -11
@@ -40,7 +40,8 @@ var EditUploadField = /*#__PURE__*/function (_React$Component) {
40
40
  _defineProperty(_this, "state", {
41
41
  camera: false,
42
42
  imageData: null,
43
- imageFilename: null
43
+ imageFilename: null,
44
+ fileSelected: false
44
45
  });
45
46
  _defineProperty(_this, "fileSelected", function (ev) {
46
47
  if (ev.target.files[0].type.startsWith("image/")) {
@@ -57,6 +58,9 @@ var EditUploadField = /*#__PURE__*/function (_React$Component) {
57
58
  _this.props.updateField(_this.props.fieldId, ev.target.files[0].name);
58
59
  };
59
60
  } else {
61
+ _this.setState({
62
+ fileSelected: true
63
+ });
60
64
  _this.props.updateFile(_this.props.fieldId, ev.target.files[0]);
61
65
  _this.props.updateField(_this.props.fieldId, ev.target.files[0].name);
62
66
  }
@@ -277,7 +281,7 @@ var EditUploadField = /*#__PURE__*/function (_React$Component) {
277
281
  onClick: this.props.disabled ? null : this.clearImage
278
282
  }));
279
283
  }
280
- } else if (fileValue) {
284
+ } else if (fileValue && !this.state.fileSelected) {
281
285
  return /*#__PURE__*/React.createElement("span", {
282
286
  className: "edit-upload-field edit-upload-field-imagelink" + (this.props.disabled ? " edit-upload-field-disabled" : "")
283
287
  }, /*#__PURE__*/React.createElement("a", {
@@ -128,10 +128,37 @@ var ExportSelection = /*#__PURE__*/function (_React$Component) {
128
128
  }
129
129
  if (this.props.frameRatio !== prevProps.frameRatio) {
130
130
  this.setState(function (state) {
131
- var newheight = _this2.props.frameRatio ? Math.round(state.width * _this2.props.frameRatio) : state.height;
131
+ var containerWidth = _this2.exportContainer.offsetWidth;
132
+ var containerHeight = _this2.exportContainer.offsetHeight;
133
+ var maxWidth = 0.75 * containerWidth;
134
+ var maxHeight = 0.75 * containerHeight;
135
+ var newwidth;
136
+ var newheight;
137
+ if (_this2.props.frameRatio) {
138
+ if (_this2.props.frameRatio >= 1) {
139
+ newwidth = maxWidth;
140
+ newheight = Math.round(newwidth * _this2.props.frameRatio);
141
+ if (newheight > maxHeight) {
142
+ newheight = maxHeight;
143
+ newwidth = Math.round(newheight / _this2.props.frameRatio);
144
+ }
145
+ } else {
146
+ newheight = maxHeight;
147
+ newwidth = Math.round(newheight / _this2.props.frameRatio);
148
+ if (newwidth > maxWidth) {
149
+ newwidth = maxWidth;
150
+ newheight = Math.round(newwidth * _this2.props.frameRatio);
151
+ }
152
+ }
153
+ } else {
154
+ newwidth = state.width;
155
+ newheight = state.height;
156
+ }
132
157
  return {
133
158
  frameRatio: _this2.props.frameRatio,
134
- y: state.y + 0.5 * (state.height - newheight),
159
+ x: 0.5 * (containerWidth - newwidth),
160
+ y: 0.5 * (containerHeight - newheight),
161
+ width: newwidth,
135
162
  height: newheight
136
163
  };
137
164
  }, function () {
@@ -150,7 +177,10 @@ var ExportSelection = /*#__PURE__*/function (_React$Component) {
150
177
  height: this.state.height + 'px'
151
178
  };
152
179
  return /*#__PURE__*/React.createElement("div", {
153
- className: "export-selection-container"
180
+ className: "export-selection-container",
181
+ ref: function ref(el) {
182
+ _this3.exportContainer = el;
183
+ }
154
184
  }, /*#__PURE__*/React.createElement("div", {
155
185
  className: "export-selection",
156
186
  onContextMenu: MiscUtils.killEvent,
@@ -124,7 +124,6 @@ var MapSelection = /*#__PURE__*/function (_React$Component) {
124
124
  if (!geometry) {
125
125
  return;
126
126
  }
127
- var coords = _this.props.geomType === 'Circle' ? null : geometry.getCoordinates();
128
127
  if (_this.props.geomType === "Circle") {
129
128
  // Also store poligonized circle
130
129
  var center = geometry.getCenter();
@@ -141,19 +140,11 @@ var MapSelection = /*#__PURE__*/function (_React$Component) {
141
140
  radius: radius
142
141
  }
143
142
  });
144
- } else if (_this.props.geomType === "DragBox" || _this.props.geomType === "Box") {
145
- var boxcoords = [[Math.min(coords[0][0][0], coords[0][2][0]), Math.min(coords[0][0][1], coords[0][2][1]), Math.max(coords[0][0][0], coords[0][2][0]), Math.max(coords[0][0][1], coords[0][2][1])]];
146
- _this.setState({
147
- geometry: {
148
- type: "Polygon",
149
- coordinates: boxcoords
150
- }
151
- });
152
143
  } else {
153
144
  _this.setState({
154
145
  geometry: {
155
- type: _this.props.geomType,
156
- coordinates: coords
146
+ type: geometry.getType(),
147
+ coordinates: geometry.getCoordinates()
157
148
  }
158
149
  });
159
150
  }
@@ -422,21 +422,21 @@ var SearchBox = /*#__PURE__*/function (_React$Component) {
422
422
  }) : null);
423
423
  });
424
424
  _defineProperty(_this, "renderLayerResult", function (provider, group, result) {
425
- var _result$label3, _result$layer;
425
+ var _ConfigUtils$getPlugi, _result$label3, _result$layer;
426
426
  var parent = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
427
427
  var level = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
428
428
  var key = provider + ":" + group.id + ":" + result.id;
429
429
  var addThemes = ConfigUtils.getConfigProp("allowAddingOtherThemes", _this.props.theme);
430
430
  var icon = null;
431
+ var toggleLayerGroup = function toggleLayerGroup(ev) {
432
+ MiscUtils.killEvent(ev);
433
+ _this.setState(function (state) {
434
+ return {
435
+ expandedLayerGroup: state.expandedLayerGroup === key ? null : key
436
+ };
437
+ });
438
+ };
431
439
  if (result.sublayers) {
432
- var toggleLayerGroup = function toggleLayerGroup(ev) {
433
- MiscUtils.killEvent(ev);
434
- _this.setState(function (state) {
435
- return {
436
- expandedLayerGroup: state.expandedLayerGroup === key ? null : key
437
- };
438
- });
439
- };
440
440
  icon = /*#__PURE__*/React.createElement(Icon, {
441
441
  icon: _this.state.expandedLayerGroup === key ? "minus" : "plus",
442
442
  onClick: function onClick(ev) {
@@ -456,7 +456,7 @@ var SearchBox = /*#__PURE__*/function (_React$Component) {
456
456
  return [/*#__PURE__*/React.createElement("div", {
457
457
  className: "searchbox-result",
458
458
  key: key,
459
- onClick: function onClick() {
459
+ onClick: (_ConfigUtils$getPlugi = ConfigUtils.getPluginConfig("LayerCatalog")) !== null && _ConfigUtils$getPlugi !== void 0 && (_ConfigUtils$getPlugi = _ConfigUtils$getPlugi.cfg) !== null && _ConfigUtils$getPlugi !== void 0 && _ConfigUtils$getPlugi.toggleGroupOnClick && result.sublayers ? toggleLayerGroup : function () {
460
460
  return selectResult(provider, group, result);
461
461
  },
462
462
  style: {
@@ -1,5 +1,4 @@
1
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 _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
3
2
  function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
4
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); } }
5
4
  function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
@@ -23,7 +22,7 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
23
22
  */
24
23
 
25
24
  import React from 'react';
26
- import { FacebookShareButton, LinkedinShareButton, TwitterShareButton, WhatsappShareButton, EmailShareButton, FacebookShareCount, FacebookIcon, TwitterIcon, LinkedinIcon, WhatsappIcon, EmailIcon } from 'react-share';
25
+ import { FacebookShareButton, LinkedinShareButton, TwitterShareButton, WhatsappShareButton, EmailShareButton, FacebookIcon, TwitterIcon, LinkedinIcon, WhatsappIcon, EmailIcon } from 'react-share';
27
26
  import PropTypes from 'prop-types';
28
27
  import LocaleUtils from '../../utils/LocaleUtils';
29
28
  import './style/ShareSocials.css';
@@ -52,7 +51,7 @@ var ShareSocials = /*#__PURE__*/function (_React$Component) {
52
51
  }, /*#__PURE__*/React.createElement(EmailIcon, {
53
52
  round: true,
54
53
  size: 32
55
- })), /*#__PURE__*/React.createElement("div", null, "\xA0")),
54
+ }))),
56
55
  facebook: /*#__PURE__*/React.createElement("div", {
57
56
  className: "social-box",
58
57
  key: "facebook"
@@ -62,11 +61,7 @@ var ShareSocials = /*#__PURE__*/function (_React$Component) {
62
61
  }, /*#__PURE__*/React.createElement(FacebookIcon, {
63
62
  round: true,
64
63
  size: 32
65
- })), /*#__PURE__*/React.createElement(FacebookShareCount, _extends({
66
- url: url
67
- }, countProps), function (count) {
68
- return count;
69
- })),
64
+ }))),
70
65
  twitter: /*#__PURE__*/React.createElement("div", {
71
66
  className: "social-box",
72
67
  key: "twitter"
@@ -76,7 +71,7 @@ var ShareSocials = /*#__PURE__*/function (_React$Component) {
76
71
  }, /*#__PURE__*/React.createElement(TwitterIcon, {
77
72
  round: true,
78
73
  size: 32
79
- })), /*#__PURE__*/React.createElement("div", null, "\xA0")),
74
+ }))),
80
75
  linkedin: /*#__PURE__*/React.createElement("div", {
81
76
  className: "social-box",
82
77
  key: "linkedin"
@@ -86,7 +81,7 @@ var ShareSocials = /*#__PURE__*/function (_React$Component) {
86
81
  }, /*#__PURE__*/React.createElement(LinkedinIcon, {
87
82
  round: true,
88
83
  size: 32
89
- })), /*#__PURE__*/React.createElement("div", null, "\xA0")),
84
+ }))),
90
85
  whatsapp: /*#__PURE__*/React.createElement("div", {
91
86
  className: "social-box",
92
87
  key: "whatsapp"
@@ -96,7 +91,7 @@ var ShareSocials = /*#__PURE__*/function (_React$Component) {
96
91
  }, /*#__PURE__*/React.createElement(WhatsappIcon, {
97
92
  round: true,
98
93
  size: 32
99
- })), /*#__PURE__*/React.createElement("div", null, "\xA0"))
94
+ })))
100
95
  };
101
96
  var socialBoxes = [];
102
97
  if (this.props.showSocials === true) {
@@ -125,7 +120,4 @@ _defineProperty(ShareSocials, "propTypes", {
125
120
  shareUrl: PropTypes.string,
126
121
  showSocials: PropTypes.oneOfType([PropTypes.bool, PropTypes.array])
127
122
  });
128
- _defineProperty(ShareSocials, "defaultProps", {
129
- shareTitle: 'GeoSolutions'
130
- });
131
123
  export { ShareSocials as default };
@@ -55,7 +55,11 @@ var LayerCatalogWidget = /*#__PURE__*/function (_React$PureComponent) {
55
55
  _this.checkAddServiceLayer(entry, true);
56
56
  }
57
57
  } else {
58
- _this.checkAddServiceLayer(entry, false);
58
+ if (_this.props.toggleGroupOnClick && !isEmpty(entry.sublayers)) {
59
+ _this.toggleLayerListEntry(path);
60
+ } else {
61
+ _this.checkAddServiceLayer(entry, false);
62
+ }
59
63
  }
60
64
  } else {
61
65
  _this.toggleLayerListEntry(path);
@@ -162,7 +166,7 @@ var LayerCatalogWidget = /*#__PURE__*/function (_React$PureComponent) {
162
166
  var _collectSublayers = function collectSublayers(entry) {
163
167
  var _entry$resource2;
164
168
  var entryParams = LayerUtils.splitLayerUrlParam((_entry$resource2 = entry.resource) !== null && _entry$resource2 !== void 0 ? _entry$resource2 : "");
165
- if (stripQuery(entryParams.url) === groupUrl) {
169
+ if (isEmpty(entry.sublayers) && stripQuery(entryParams.url) === groupUrl) {
166
170
  sublayers[entryParams.name] = entryParams;
167
171
  }
168
172
  (entry.sublayers || []).forEach(_collectSublayers);
@@ -214,10 +218,12 @@ var LayerCatalogWidget = /*#__PURE__*/function (_React$PureComponent) {
214
218
  } else if (sublayerSubset) {
215
219
  var _filterSublayers = function filterSublayers(sublayers) {
216
220
  return sublayers.map(function (sublayer) {
217
- if (sublayer.sublayers) {
218
- return _filterSublayers(sublayer.sublayers);
219
- } else if (sublayer.name in sublayerSubset) {
220
- return _objectSpread(_objectSpread({}, sublayer), sublayerSubset);
221
+ if (sublayer.name in sublayerSubset) {
222
+ return _objectSpread(_objectSpread({}, sublayer), sublayerSubset[sublayer.name]);
223
+ } else if (sublayer.sublayers) {
224
+ return _objectSpread(_objectSpread({}, sublayer), {}, {
225
+ sublayers: _filterSublayers(sublayer.sublayers)
226
+ });
221
227
  } else {
222
228
  return null;
223
229
  }
@@ -380,7 +386,8 @@ _defineProperty(LayerCatalogWidget, "propTypes", {
380
386
  pendingRequests: PropTypes.number,
381
387
  removeLayer: PropTypes.func,
382
388
  replacePlaceholderLayer: PropTypes.func,
383
- showNotification: PropTypes.func
389
+ showNotification: PropTypes.func,
390
+ toggleGroupOnClick: PropTypes.bool
384
391
  });
385
392
  export default connect(function (state) {
386
393
  return {
@@ -40,7 +40,8 @@ var MenuButton = /*#__PURE__*/function (_React$Component) {
40
40
  _defineProperty(_this, "onButtonClicked", function (ev) {
41
41
  ev.stopPropagation();
42
42
  if (_this.state.selected) {
43
- _this.props.onActivate(_this.state.selected);
43
+ var _this$props$onActivat, _this$props;
44
+ (_this$props$onActivat = (_this$props = _this.props).onActivate) === null || _this$props$onActivat === void 0 || _this$props$onActivat.call(_this$props, _this.state.selected);
44
45
  _this.setState({
45
46
  popup: false
46
47
  });
@@ -48,14 +49,16 @@ var MenuButton = /*#__PURE__*/function (_React$Component) {
48
49
  _this.onMenuClicked();
49
50
  }
50
51
  });
51
- _defineProperty(_this, "onChildClicked", function (child) {
52
+ _defineProperty(_this, "onChildClicked", function (ev, child) {
52
53
  if (!child.props.disabled) {
54
+ var _this$props$onActivat2, _this$props2, _child$props$onClick, _child$props;
53
55
  if (_this.state.selected) {
54
56
  _this.setState({
55
57
  selected: child.props.value
56
58
  });
57
59
  }
58
- _this.props.onActivate(child.props.value);
60
+ (_this$props$onActivat2 = (_this$props2 = _this.props).onActivate) === null || _this$props$onActivat2 === void 0 || _this$props$onActivat2.call(_this$props2, child.props.value);
61
+ (_child$props$onClick = (_child$props = child.props).onClick) === null || _child$props$onClick === void 0 || _child$props$onClick.call(_child$props, ev);
59
62
  }
60
63
  });
61
64
  _this.el = null;
@@ -140,8 +143,8 @@ var MenuButton = /*#__PURE__*/function (_React$Component) {
140
143
  return /*#__PURE__*/React.cloneElement(child, {
141
144
  className: classNames,
142
145
  disabled: child.props.disabled,
143
- onClick: function onClick() {
144
- return _this2.onChildClicked(child);
146
+ onClick: function onClick(ev) {
147
+ return _this2.onChildClicked(ev, child);
145
148
  }
146
149
  });
147
150
  })) : null);
@@ -225,7 +225,7 @@ var PlotInfoTool = /*#__PURE__*/function (_React$Component) {
225
225
  _defineProperty(_this, "renderError", function () {
226
226
  return /*#__PURE__*/React.createElement("div", {
227
227
  className: "plot-info-dialog-query-failed"
228
- }, _this.state.expandedInfoData.failed === true ? LocaleUtils.tr("plotinfotool.failed") : LocaleUtils.tr(_this.state.expandedInfoData.failed));
228
+ }, _this.state.expandedInfoData.failed === true ? LocaleUtils.tr("common.dataloadfailed") : LocaleUtils.tr(_this.state.expandedInfoData.failed));
229
229
  });
230
230
  _defineProperty(_this, "renderInfoData", function () {
231
231
  if (_this.props.customInfoComponents[_this.state.expandedInfo]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qwc2",
3
- "version": "2026.07.08",
3
+ "version": "2026.07.18",
4
4
  "description": "QGIS Web Client",
5
5
  "author": "Sourcepole AG",
6
6
  "license": "BSD-2-Clause",
@@ -117,7 +117,7 @@
117
117
  "@types/three": "^0.180.0",
118
118
  "babel-loader": "^10.0.0",
119
119
  "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
120
- "copy-webpack-plugin": "^13.0.1",
120
+ "copy-webpack-plugin": "^14.0.0",
121
121
  "css-loader": "^7.1.3",
122
122
  "eslint": "^9.39.2",
123
123
  "eslint-plugin-perfectionist": "^5.5.0",
@@ -130,7 +130,7 @@
130
130
  "webpack": "^5.105.0",
131
131
  "webpack-bundle-analyzer": "^5.2.0",
132
132
  "webpack-cli": "^6.0.1",
133
- "webpack-dev-server": "^5.2.3"
133
+ "webpack-dev-server": "^6.0.0"
134
134
  },
135
135
  "bin": {
136
136
  "qwc_build_iconfont": "./scripts/makeIconkit.js",
@@ -138,6 +138,7 @@
138
138
  "qwc_update_translations": "./scripts/updateTranslations.js"
139
139
  },
140
140
  "scripts": {
141
+ "preinstall": "node -e \"const ua=process.env.npm_config_user_agent||''; if(ua.startsWith('yarn/')){console.error('This project uses npm. Run npm install instead.'); process.exit(1)}\"",
141
142
  "prod": "npm run tsupdate && npm run themesconfig && npm run iconfont && webpack --mode production --progress",
142
143
  "start": "npm run tsupdate && npm run themesconfig && npm run iconfont && webpack serve --mode development --progress --host 0.0.0.0 --port 8081",
143
144
  "iconfont": "node scripts/makeIconkit.js",
@@ -63,7 +63,11 @@ export var BackgroundSwitcher = /*#__PURE__*/function (_React$Component) {
63
63
  }, backgroundLayers.filter(function (entry) {
64
64
  return !(entry !== null && entry !== void 0 && entry.omitFromSelect);
65
65
  }).map(function (entry) {
66
- return Array.isArray(entry) ? _this.renderGroupItem(entry, visibleBgLayer) : _this.renderLayerItem(entry, visibleBgLayer);
66
+ if (Array.isArray(entry)) {
67
+ return entry.length > 1 ? _this.renderGroupItem(entry, visibleBgLayer) : _this.renderLayerItem(entry[0], visibleBgLayer);
68
+ } else {
69
+ return _this.renderLayerItem(entry, visibleBgLayer);
70
+ }
67
71
  }));
68
72
  });
69
73
  _defineProperty(_this, "itemTitle", function (item) {
@@ -401,8 +405,8 @@ var selector = function selector(state) {
401
405
  var backgroundLayers = Object.values(state.layers.flat.filter(function (layer) {
402
406
  return layer.role === LayerRole.BACKGROUND;
403
407
  }).reduce(function (res, l) {
404
- return _objectSpread(_objectSpread({}, res), {}, _defineProperty({}, "_" + (l.group || l.name), l.group ? [].concat(_toConsumableArray(res["_" + l.group] || []), [l]) : l));
405
- }, {}));
408
+ return _objectSpread(_objectSpread({}, res), {}, _defineProperty({}, "_" + (l.group || l.name), l.group ? [l].concat(_toConsumableArray(res["_" + l.group] || [])) : l));
409
+ }, {})).reverse();
406
410
  return {
407
411
  backgroundLayers: backgroundLayers
408
412
  };