qwc2 2026.1.13 → 2026.1.14

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 (38) hide show
  1. package/components/AttributeTableWidget.js +126 -56
  2. package/components/IdentifyViewer.js +1 -1
  3. package/components/style/AttributeTableWidget.css +4 -5
  4. package/components/widgets/TextInput.js +13 -6
  5. package/components/widgets/style/TextInput.css +1 -1
  6. package/icons/epsilon.svg +37 -0
  7. package/package.json +1 -1
  8. package/plugins/Bookmark.js +148 -92
  9. package/plugins/Editing.js +7 -0
  10. package/plugins/Identify.js +6 -2
  11. package/plugins/Settings.js +2 -2
  12. package/plugins/style/Bookmark.css +30 -10
  13. package/static/translations/bg-BG.json +2 -2
  14. package/static/translations/ca-ES.json +2 -2
  15. package/static/translations/cs-CZ.json +2 -2
  16. package/static/translations/de-CH.json +2 -2
  17. package/static/translations/de-DE.json +2 -2
  18. package/static/translations/en-US.json +2 -2
  19. package/static/translations/es-ES.json +2 -2
  20. package/static/translations/fi-FI.json +2 -2
  21. package/static/translations/fr-FR.json +2 -2
  22. package/static/translations/hu-HU.json +2 -2
  23. package/static/translations/it-IT.json +2 -2
  24. package/static/translations/ja-JP.json +2 -2
  25. package/static/translations/nl-NL.json +2 -2
  26. package/static/translations/no-NO.json +2 -2
  27. package/static/translations/pl-PL.json +2 -2
  28. package/static/translations/pt-BR.json +2 -2
  29. package/static/translations/pt-PT.json +2 -2
  30. package/static/translations/ro-RO.json +2 -2
  31. package/static/translations/ru-RU.json +2 -2
  32. package/static/translations/sv-SE.json +2 -2
  33. package/static/translations/tr-TR.json +2 -2
  34. package/static/translations/tsconfig.json +2 -2
  35. package/static/translations/uk-UA.json +2 -2
  36. package/utils/IdentifyUtils.js +39 -19
  37. package/utils/MiscUtils.js +3 -0
  38. package/utils/PermaLinkUtils.js +98 -76
@@ -55,7 +55,7 @@ import { FeatureCache, KeyValCache, parseExpression, getFeatureTemplate } from '
55
55
  import LayerUtils from '../utils/LayerUtils';
56
56
  import LocaleUtils from '../utils/LocaleUtils';
57
57
  import MapUtils from '../utils/MapUtils';
58
- import MiscUtils from '../utils/MiscUtils';
58
+ import MiscUtils, { ToggleSet } from '../utils/MiscUtils';
59
59
  import VectorLayerUtils from '../utils/VectorLayerUtils';
60
60
  import './style/AttributeTableWidget.css';
61
61
  var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
@@ -239,6 +239,7 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
239
239
  })) === null || _this$props$layers$fi2 === void 0 || (_this$props$layers$fi2 = _this$props$layers$fi2.translations) === null || _this$props$layers$fi2 === void 0 || (_this$props$layers$fi2 = _this$props$layers$fi2.layers) === null || _this$props$layers$fi2 === void 0 || (_this$props$layers$fi2 = _this$props$layers$fi2[layerName]) === null || _this$props$layers$fi2 === void 0 ? void 0 : _this$props$layers$fi2.fields) !== null && _this$props$layers$fi !== void 0 ? _this$props$layers$fi : {};
240
240
  }
241
241
  newState.selectedLayer = selectedLayer;
242
+ newState.selectedFeatures = new ToggleSet();
242
243
  var options = {
243
244
  bbox: newState.limitToExtent ? _this.props.mapBbox.bounds : null,
244
245
  filter: (_this$props$filter$fi = _this.props.filter.filterParams) === null || _this$props$filter$fi === void 0 ? void 0 : _this$props$filter$fi[selectedLayer],
@@ -255,36 +256,41 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
255
256
  features: _this.filteredSortedFeatures(newState.allFeatures, newState)
256
257
  });
257
258
  } else {
259
+ Object.assign(newState, {
260
+ allFeatures: [],
261
+ features: []
262
+ });
258
263
  if (clientSideFilterSort) {
259
264
  /* eslint-disable-next-line no-alert */
260
- if (!forceReload && !confirm(LocaleUtils.tr("attribtable.fulldatasetload"))) {
265
+ if (!_this.filterWarningShown && !forceReload && !confirm(LocaleUtils.tr("attribtable.fulldatasetload"))) {
261
266
  return {};
262
267
  }
268
+ _this.filterWarningShown = true;
263
269
  } else {
264
270
  var _this$props$filter$fi2;
265
271
  if ((_this$props$filter$fi2 = _this.props.filter.filterParams) !== null && _this$props$filter$fi2 !== void 0 && _this$props$filter$fi2[selectedLayer] && newState.filterVal) {
266
272
  var _this$props$filter$fi3;
267
- options.filter = [(_this$props$filter$fi3 = _this.props.filter.filterParams) === null || _this$props$filter$fi3 === void 0 ? void 0 : _this$props$filter$fi3[selectedLayer], 'and', [newState.filterField, newState.filterOp.replace(), newState.filterVal]];
273
+ options.filter = [(_this$props$filter$fi3 = _this.props.filter.filterParams) === null || _this$props$filter$fi3 === void 0 ? void 0 : _this$props$filter$fi3[selectedLayer], 'and', [newState.filterField, newState.filterOp, newState.filterVal]];
268
274
  } else if (newState.filterVal) {
269
- options.filter = [[newState.filterField, newState.filterOp.replace(), newState.filterVal]];
275
+ options.filter = [[newState.filterField, newState.filterOp, newState.filterVal]];
276
+ } else {
277
+ // NOTE: Query full feature set when filtering so that filtered geometries can be highlighted
278
+ options.offset = newState.currentPage * newState.pageSize;
279
+ options.limit = newState.pageSize;
270
280
  }
271
- options.offset = newState.currentPage * newState.pageSize;
272
- options.limit = newState.pageSize;
273
281
  options.sortby = newState.sortField ? (newState.sortField.dir < 0 ? "-" : "") + newState.sortField.field : null;
274
282
  }
275
283
  newState.loading = true;
276
284
  _this.props.iface.getFeatures(editConfig, _this.props.mapCrs, function (result) {
277
285
  if (result) {
278
- var _result$numberMatched;
286
+ var _result$numberMatched, _options$offset;
279
287
  var featuresSlice = result.features || [];
280
- var featureCount = (_result$numberMatched = result.numberMatched) !== null && _result$numberMatched !== void 0 ? _result$numberMatched : featuresSlice.length;
281
- var features = new Array(featureCount);
282
- features.splice.apply(features, [options.offset, featuresSlice.length].concat(_toConsumableArray(featuresSlice)));
288
+ var features = new Array((_result$numberMatched = result.numberMatched) !== null && _result$numberMatched !== void 0 ? _result$numberMatched : featuresSlice.length);
289
+ features.splice.apply(features, [(_options$offset = options.offset) !== null && _options$offset !== void 0 ? _options$offset : 0, featuresSlice.length].concat(_toConsumableArray(featuresSlice)));
283
290
  _this.setState({
284
291
  loading: false,
285
292
  allFeatures: options.limit === undefined || result.numberMatched === undefined ? features : null,
286
293
  features: clientSideFilterSort ? _this.filteredSortedFeatures(features, newState) : features,
287
- featureCount: featureCount,
288
294
  loadedLayer: newState.selectedLayer,
289
295
  clientSideData: result.numberMatched === undefined
290
296
  });
@@ -292,9 +298,7 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
292
298
  // eslint-disable-next-line
293
299
  alert(LocaleUtils.tr("attribtable.loadfailed"));
294
300
  _this.setState({
295
- loading: false,
296
- features: [],
297
- featureCount: 0
301
+ loading: false
298
302
  });
299
303
  }
300
304
  }, options);
@@ -368,6 +372,25 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
368
372
  return filteredFeatures;
369
373
  }
370
374
  });
375
+ _defineProperty(_this, "currentPageFeatures", function (state) {
376
+ var indexOffset = state.currentPage * state.pageSize;
377
+ return state.features.slice(indexOffset, indexOffset + state.pageSize);
378
+ });
379
+ _defineProperty(_this, "updateFilter", function (stateField, val) {
380
+ var newState = {
381
+ filterField: _this.state.filterField,
382
+ filterOp: _this.state.filterOp,
383
+ filterVal: _this.state.filterVal
384
+ };
385
+ newState[stateField] = val;
386
+ // Reset page if a reload is triggered (either filter changed with a set filter value, or filter value cleared)
387
+ if (newState.filterVal || _this.state.filterVal && !newState.filterVal) {
388
+ newState.currentPage = 0;
389
+ _this.reload(_this.state.selectedLayer, false, newState);
390
+ } else {
391
+ _this.setState(_defineProperty({}, stateField, val));
392
+ }
393
+ });
371
394
  _defineProperty(_this, "sortBy", function (field) {
372
395
  var newState = {
373
396
  sortField: _this.state.sortField
@@ -413,8 +436,8 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
413
436
  });
414
437
  _defineProperty(_this, "deleteSelectedFeatured", function () {
415
438
  _this.setState(function (state) {
416
- var features = state.features.filter(function (feature) {
417
- return state.selectedFeatures[feature.id] === true;
439
+ var features = _this.currentPageFeatures(state).filter(function (feature) {
440
+ return state.selectedFeatures.has(feature.id);
418
441
  });
419
442
  features.forEach(function (feature) {
420
443
  _this.props.iface.deleteFeature(state.curEditConfig, feature.id, function (success) {
@@ -569,6 +592,10 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
569
592
  var features = [];
570
593
  if (_this.state.highlightedFeature) {
571
594
  features.push(_this.state.highlightedFeature);
595
+ } else if (!_this.state.selectedFeatures.isEmpty()) {
596
+ features = _this.currentPageFeatures(_this.state).filter(function (feature) {
597
+ return _this.state.selectedFeatures.has(feature.id);
598
+ });
572
599
  } else if (_this.state.filterVal) {
573
600
  features = _this.state.features;
574
601
  }
@@ -586,8 +613,8 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
586
613
  _defineProperty(_this, "zoomToSelection", function () {
587
614
  var collection = {
588
615
  type: "FeatureCollection",
589
- features: _this.state.features.filter(function (feature) {
590
- return _this.state.selectedFeatures[feature.id] === true && feature.geometry;
616
+ features: _this.currentPageFeatures(_this.state).filter(function (feature) {
617
+ return _this.state.selectedFeatures.has(feature.id);
591
618
  })
592
619
  };
593
620
  if (!isEmpty(collection.features)) {
@@ -606,7 +633,7 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
606
633
  alert(LocaleUtils.tr("attribtable.nogeomnoform"));
607
634
  return;
608
635
  }
609
- var feature = _this.state.features.find(function (f) {
636
+ var feature = _this.currentPageFeatures(_this.state).find(function (f) {
610
637
  return _this.state.selectedFeatures[f.id] === true;
611
638
  });
612
639
  _this.props.setCurrentTask("Editing", null, null, {
@@ -651,28 +678,73 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
651
678
  }
652
679
  });
653
680
  _defineProperty(_this, "csvExport", function () {
654
- var fields = _this.props.showDisplayFieldOnly ? _this.state.curEditConfig.fields.filter(function (field) {
655
- return field.name === _this.state.curEditConfig.displayField;
656
- }) : _this.state.curEditConfig.fields.filter(function (field) {
657
- return field.id !== 'id';
658
- });
659
- var data = "";
660
- data += "id," + fields.map(function (field) {
661
- return "\"".concat(field.name.replaceAll('"', '""'), "\"");
662
- }).join(",") + "\n";
663
- _this.state.features.forEach(function (feature) {
664
- data += feature.id + "," + fields.map(function (field) {
665
- var value = feature.properties[field.id];
666
- if (value === null || value === undefined) {
667
- return "null";
681
+ var formatCsv = function formatCsv(features) {
682
+ var _this$state$curEditCo4;
683
+ var primaryKey = (_this$state$curEditCo4 = _this.state.curEditConfig.primaryKey) !== null && _this$state$curEditCo4 !== void 0 ? _this$state$curEditCo4 : "id";
684
+ var fields = _this.props.showDisplayFieldOnly ? _this.state.curEditConfig.fields.filter(function (field) {
685
+ return field.name === _this.state.curEditConfig.displayField;
686
+ }) : _this.state.curEditConfig.fields.filter(function (field) {
687
+ return field.id !== primaryKey;
688
+ });
689
+ var data = "";
690
+ data += primaryKey + "," + fields.map(function (field) {
691
+ return "\"".concat(field.name.replaceAll('"', '""'), "\"");
692
+ }).join(",") + "\n";
693
+ features.forEach(function (feature) {
694
+ data += feature.id + "," + fields.map(function (field) {
695
+ var value = feature.properties[field.id];
696
+ if (value === null || value === undefined) {
697
+ return "null";
698
+ } else {
699
+ return "\"".concat(String(feature.properties[field.id]).replaceAll('"', '""'), "\"");
700
+ }
701
+ }).join(",") + "\n";
702
+ });
703
+ FileSaver.saveAs(new Blob([data], {
704
+ type: "text/plain;charset=utf-8"
705
+ }), _this.state.loadedLayer.split("#").slice(-1)[0] + ".csv");
706
+ };
707
+ if (_this.state.clientSideData) {
708
+ formatCsv(_this.state.allFeatures);
709
+ } else {
710
+ var _this$props$filter$fi4, _state$curEditConfig, _fieldMap$state$filte, _fieldMap$state$sortF, _state$sortField;
711
+ var state = _this.state;
712
+ var options = {
713
+ bbox: state.limitToExtent ? _this.props.mapBbox.bounds : null,
714
+ filter: (_this$props$filter$fi4 = _this.props.filter.filterParams) === null || _this$props$filter$fi4 === void 0 ? void 0 : _this$props$filter$fi4[state.loadedLayer],
715
+ filterGeom: _this.props.filter.filterGeom,
716
+ fields: _this.props.showDisplayFieldOnly ? [state.curEditConfig.displayField, "geometry"] : null
717
+ };
718
+ // If sort or filter field is virtual, query full feature set and sort/filter client side
719
+ var fieldMap = (((_state$curEditConfig = state.curEditConfig) === null || _state$curEditConfig === void 0 ? void 0 : _state$curEditConfig.fields) || []).reduce(function (res, field) {
720
+ return _objectSpread(_objectSpread({}, res), {}, _defineProperty({}, field.id, field));
721
+ }, {});
722
+ var clientSideFilterSort = state.clientSideData || state.filterVal && ((_fieldMap$state$filte = fieldMap[state.filterField]) === null || _fieldMap$state$filte === void 0 ? void 0 : _fieldMap$state$filte.expression) || ((_fieldMap$state$sortF = fieldMap[(_state$sortField = state.sortField) === null || _state$sortField === void 0 ? void 0 : _state$sortField.field]) === null || _fieldMap$state$sortF === void 0 ? void 0 : _fieldMap$state$sortF.expression);
723
+ if (!clientSideFilterSort) {
724
+ var _this$props$filter$fi5;
725
+ if ((_this$props$filter$fi5 = _this.props.filter.filterParams) !== null && _this$props$filter$fi5 !== void 0 && _this$props$filter$fi5[state.loadedLayer] && state.filterVal) {
726
+ var _this$props$filter$fi6;
727
+ options.filter = [(_this$props$filter$fi6 = _this.props.filter.filterParams) === null || _this$props$filter$fi6 === void 0 ? void 0 : _this$props$filter$fi6[_this.state.loadedLayer], 'and', [state.filterField, state.filterOp, state.filterVal]];
728
+ } else if (state.filterVal) {
729
+ options.filter = [[state.filterField, state.filterOp, state.filterVal]];
730
+ }
731
+ options.sortby = state.sortField ? (state.sortField.dir < 0 ? "-" : "") + state.sortField.field : null;
732
+ }
733
+ _this.setState({
734
+ loading: true
735
+ });
736
+ _this.props.iface.getFeatures(_this.state.curEditConfig, _this.props.mapCrs, function (result) {
737
+ if (result) {
738
+ formatCsv(clientSideFilterSort ? _this.filteredSortedFeatures(result.features, _this.state) : result.features);
668
739
  } else {
669
- return "\"".concat(String(feature.properties[field.id]).replaceAll('"', '""'), "\"");
740
+ // eslint-disable-next-line
741
+ alert(LocaleUtils.tr("attribtable.loadfailed"));
670
742
  }
671
- }).join(",") + "\n";
672
- });
673
- FileSaver.saveAs(new Blob([data], {
674
- type: "text/plain;charset=utf-8"
675
- }), _this.state.loadedLayer + ".csv");
743
+ _this.setState({
744
+ loading: false
745
+ });
746
+ }, options);
747
+ }
676
748
  });
677
749
  _defineProperty(_this, "translateFieldName", function (fieldName) {
678
750
  var _this$state$fieldTran, _this$state$fieldTran2;
@@ -683,6 +755,7 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
683
755
  _this.table = null;
684
756
  _this.attribTableContents = null;
685
757
  _this.state.limitToExtent = props.limitToExtent;
758
+ _this.filterWarningShown = false;
686
759
  return _this;
687
760
  }
688
761
  _inherits(AttributeTableWidget, _React$Component);
@@ -712,7 +785,7 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
712
785
  });
713
786
  }
714
787
  // Highlight feature
715
- if (this.state.highlightedFeature !== prevState.highlightedFeature || this.state.features !== prevState.features) {
788
+ if (this.state.highlightedFeature !== prevState.highlightedFeature || this.state.features !== prevState.features || this.state.selectedFeatures !== prevState.selectedFeatures) {
716
789
  this.highlightFeatures();
717
790
  }
718
791
  }
@@ -732,6 +805,7 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
732
805
  var readOnly = this.props.readOnly || editPermissions.updatable === false;
733
806
  var loading = this.state.loading;
734
807
  var editing = this.state.changedFeatureIdx !== null || this.state.newFeature;
808
+ var selectionEmpty = this.state.selectedFeatures.isEmpty();
735
809
  var loadOverlay = null;
736
810
  if (loading) {
737
811
  loadOverlay = /*#__PURE__*/React.createElement("div", {
@@ -785,7 +859,10 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
785
859
  title: _this2.translateFieldName(field.name)
786
860
  }, /*#__PURE__*/React.createElement("span", null, _this2.renderColumnResizeHandle(idx + 1, 'l'), /*#__PURE__*/React.createElement("span", {
787
861
  className: "attribtable-table-headername"
788
- }, _this2.translateFieldName(field.name)), _this2.renderSortIndicator(field.id), idx < fields.length - 1 ? _this2.renderColumnResizeHandle(idx + 2, 'r') : null));
862
+ }, _this2.translateFieldName(field.name), field.expression ? /*#__PURE__*/React.createElement(Icon, {
863
+ icon: "epsilon",
864
+ title: LocaleUtils.tr("attribtable.calculatedfield")
865
+ }) : null), _this2.renderSortIndicator(field.id), idx < fields.length - 1 ? _this2.renderColumnResizeHandle(idx + 2, 'r') : null));
789
866
  }))), /*#__PURE__*/React.createElement("tbody", null, features.map(function (feature, sliceidx) {
790
867
  var featureidx = indexOffset + sliceidx;
791
868
  var disabled = readOnly || editing && _this2.state.changedFeatureIdx !== featureidx;
@@ -809,11 +886,11 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
809
886
  });
810
887
  }
811
888
  }, /*#__PURE__*/React.createElement("td", null, /*#__PURE__*/React.createElement("span", null, sliceidx > 0 ? _this2.renderRowResizeHandle(sliceidx, 't') : null, /*#__PURE__*/React.createElement("input", {
812
- checked: _this2.state.selectedFeatures[feature.id] === true,
813
- onChange: function onChange(ev) {
889
+ checked: _this2.state.selectedFeatures.has(feature.id),
890
+ onChange: function onChange() {
814
891
  return _this2.setState(function (state) {
815
892
  return {
816
- selectedFeatures: _objectSpread(_objectSpread({}, state.selectedFeatures), {}, _defineProperty({}, feature.id, ev.target.checked))
893
+ selectedFeatures: state.selectedFeatures.toggle(feature.id)
817
894
  };
818
895
  });
819
896
  },
@@ -831,7 +908,7 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
831
908
  key: field.id
832
909
  }, _this2.renderField(_this2.state.newFeature, curEditConfig, mapPrefix, field, _this2.updateNewFeatureField, false));
833
910
  })) : null));
834
- var npages = Math.ceil(this.state.featureCount / this.state.pageSize);
911
+ var npages = Math.ceil(this.state.features.length / this.state.pageSize);
835
912
  var pages = [this.state.currentPage];
836
913
  var extraright = Math.max(0, 2 - this.state.currentPage);
837
914
  var extraleft = Math.max(0, this.state.currentPage - (npages - 3));
@@ -954,9 +1031,7 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
954
1031
  var showEditButton = !this.props.readOnly && ConfigUtils.havePlugin("Editing") && this.props.showEditFormButton;
955
1032
  var deleteButton = showDelButton ? /*#__PURE__*/React.createElement("button", {
956
1033
  className: "button",
957
- disabled: layerChanged || editing || !Object.values(this.state.selectedFeatures).find(function (entry) {
958
- return entry === true;
959
- }),
1034
+ disabled: layerChanged || editing || selectionEmpty,
960
1035
  onClick: function onClick() {
961
1036
  return _this2.setState({
962
1037
  confirmDelete: true
@@ -1037,18 +1112,14 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
1037
1112
  icon: "plus"
1038
1113
  })) : null, /*#__PURE__*/React.createElement("button", {
1039
1114
  className: "button",
1040
- disabled: layerChanged || !Object.values(this.state.selectedFeatures).find(function (entry) {
1041
- return entry === true;
1042
- }),
1115
+ disabled: layerChanged || selectionEmpty,
1043
1116
  onClick: this.zoomToSelection,
1044
1117
  title: LocaleUtils.tr("attribtable.zoomtoselection")
1045
1118
  }, /*#__PURE__*/React.createElement(Icon, {
1046
1119
  icon: "search"
1047
1120
  })), showEditButton ? /*#__PURE__*/React.createElement("button", {
1048
1121
  className: "button",
1049
- disabled: layerChanged || editing || Object.values(this.state.selectedFeatures).filter(function (entry) {
1050
- return entry === true;
1051
- }).length !== 1,
1122
+ disabled: layerChanged || editing || this.state.selectedFeatures.length !== 1,
1052
1123
  onClick: this.switchToFormEditMode,
1053
1124
  title: LocaleUtils.tr("attribtable.formeditmode")
1054
1125
  }, /*#__PURE__*/React.createElement(Icon, {
@@ -1145,9 +1216,8 @@ _defineProperty(AttributeTableWidget, "defaultState", {
1145
1216
  curEditConfig: null,
1146
1217
  fieldTranslations: null,
1147
1218
  features: [],
1148
- featureCount: 0,
1149
1219
  allFeatures: null,
1150
- selectedFeatures: {},
1220
+ selectedFeatures: new ToggleSet(),
1151
1221
  highlightedFeature: null,
1152
1222
  changedFeatureIdx: null,
1153
1223
  originalFeatureProps: null,
@@ -746,7 +746,7 @@ var IdentifyViewer = /*#__PURE__*/function (_React$Component) {
746
746
  return _this.toggleSelectedResult(key);
747
747
  },
748
748
  title: LocaleUtils.tr("identify.selectforcompare")
749
- }) : null, /*#__PURE__*/React.createElement("span", null, (_this.props.showLayerTitles ? feature.layertitle + ": " : "") + feature.displayname), zoomToFeatureButton, /*#__PURE__*/React.createElement(Icon, {
749
+ }) : null, /*#__PURE__*/React.createElement("span", null, [_this.props.showLayerTitles ? feature.layertitle : "", feature.displayname].filter(Boolean).join(": ")), zoomToFeatureButton, /*#__PURE__*/React.createElement(Icon, {
750
750
  icon: "info-sign",
751
751
  onClick: function onClick() {
752
752
  return _this.showLayerInfo(layerid);
@@ -77,16 +77,15 @@ table.attribtable-table th > span > span.icon {
77
77
  position: absolute;
78
78
  right: 1px;
79
79
  background-color: var(--button-bg-color);
80
+ flex: 0 0 auto;
81
+ margin-left: 0.5em;
80
82
  }
81
83
 
82
84
  span.attribtable-table-headername {
83
85
  flex: 1 1 auto;
84
86
  text-align: left;
85
- }
86
-
87
- table.attribtable-table th > span > span.icon {
88
- flex: 0 0 auto;
89
- margin-left: 0.5em;
87
+ display: flex;
88
+ align-items: center;
90
89
  }
91
90
 
92
91
  table.attribtable-table tr:nth-child(even) {
@@ -52,6 +52,9 @@ var TextInput = /*#__PURE__*/function (_React$Component) {
52
52
  if (el) {
53
53
  _this.input = el;
54
54
  _this.setDefaultValue(_this.state.value, _this.state.valueRev, -1);
55
+ if (_this.props.focusOnRef) {
56
+ el.focus();
57
+ }
55
58
  }
56
59
  });
57
60
  _defineProperty(_this, "setInputContents", function () {
@@ -93,9 +96,7 @@ var TextInput = /*#__PURE__*/function (_React$Component) {
93
96
  _this.setState({
94
97
  focus: false
95
98
  });
96
- if (!_this.skipNextCommitOnBlur) {
97
- _this.commit();
98
- }
99
+ _this.commit();
99
100
  });
100
101
  _defineProperty(_this, "onFocus", function (ev) {
101
102
  _this.setState({
@@ -166,9 +167,10 @@ var TextInput = /*#__PURE__*/function (_React$Component) {
166
167
  curValue: _this.props.value || "",
167
168
  changed: false
168
169
  };
170
+ }, function () {
171
+ return ev.target.blur();
169
172
  });
170
- _this.skipNextCommitOnBlur = true;
171
- ev.target.blur();
173
+ MiscUtils.killEvent(ev);
172
174
  }
173
175
  });
174
176
  _defineProperty(_this, "commit", function () {
@@ -186,6 +188,9 @@ var TextInput = /*#__PURE__*/function (_React$Component) {
186
188
  });
187
189
  _this.props.onChange(_this.state.value);
188
190
  });
191
+ } else {
192
+ var _this$props$onNoChang, _this$props;
193
+ (_this$props$onNoChang = (_this$props = _this.props).onNoChange) === null || _this$props$onNoChang === void 0 || _this$props$onNoChang.call(_this$props);
189
194
  }
190
195
  });
191
196
  _defineProperty(_this, "storeInitialHeight", function (el) {
@@ -212,7 +217,6 @@ var TextInput = /*#__PURE__*/function (_React$Component) {
212
217
  once: true
213
218
  });
214
219
  });
215
- _this.skipNextCommitOnBlur = false;
216
220
  _this.focusEnterClick = false;
217
221
  _this.initialHeight = null;
218
222
  _this.input = null;
@@ -249,6 +253,7 @@ var TextInput = /*#__PURE__*/function (_React$Component) {
249
253
  }
250
254
  return /*#__PURE__*/React.createElement("div", {
251
255
  className: wrapperClassName + " " + (this.props.className || ""),
256
+ onClick: MiscUtils.killEvent,
252
257
  ref: this.storeInitialHeight
253
258
  }, this.props.name ? /*#__PURE__*/React.createElement("textarea", {
254
259
  className: "text-input-form-el",
@@ -309,9 +314,11 @@ _defineProperty(TextInput, "propTypes", {
309
314
  className: PropTypes.string,
310
315
  clearValue: PropTypes.string,
311
316
  disabled: PropTypes.bool,
317
+ focusOnRef: PropTypes.bool,
312
318
  multiline: PropTypes.bool,
313
319
  name: PropTypes.string,
314
320
  onChange: PropTypes.func,
321
+ onNoChange: PropTypes.func,
315
322
  placeholder: PropTypes.string,
316
323
  readOnly: PropTypes.bool,
317
324
  required: PropTypes.bool,
@@ -26,7 +26,7 @@ pre.text-input {
26
26
  font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
27
27
  font-size: 14px;
28
28
  margin: 0;
29
- padding: 0.25em;
29
+ padding: calc(1em - 8px) 0.25em 0.25em 0.25em;
30
30
  overflow: auto;
31
31
  cursor: text;
32
32
  text-align: left;
@@ -0,0 +1,37 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ width="24"
4
+ height="24"
5
+ version="1.1"
6
+ id="svg1"
7
+ sodipodi:docname="epsilon.svg"
8
+ inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
9
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
10
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11
+ xmlns="http://www.w3.org/2000/svg"
12
+ xmlns:svg="http://www.w3.org/2000/svg">
13
+ <defs
14
+ id="defs1" />
15
+ <sodipodi:namedview
16
+ id="namedview1"
17
+ pagecolor="#ffffff"
18
+ bordercolor="#666666"
19
+ borderopacity="1.0"
20
+ inkscape:showpageshadow="2"
21
+ inkscape:pageopacity="0.0"
22
+ inkscape:pagecheckerboard="0"
23
+ inkscape:deskcolor="#d1d1d1"
24
+ inkscape:zoom="12.346084"
25
+ inkscape:cx="22.719754"
26
+ inkscape:cy="26.769621"
27
+ inkscape:window-width="1920"
28
+ inkscape:window-height="1052"
29
+ inkscape:window-x="0"
30
+ inkscape:window-y="0"
31
+ inkscape:window-maximized="1"
32
+ inkscape:current-layer="svg1" />
33
+ <path
34
+ d="m 9.5441108,11.490077 q -3.651853,-1.297627 -3.651853,-3.9298875 0,-2.0761999 1.9279291,-3.3181907 Q 9.7666152,3 12.639896,3 q 2.613753,0 4.152293,0.8712646 1.538638,0.8527247 1.538638,2.0576927 0,0.6302695 -0.482269,1.1122354 -0.48172,0.4634177 -1.112235,0.4634177 -1.038099,0 -1.705522,-1.4458896 -0.926861,-2.0020645 -2.78063,-2.0020645 -1.464405,0 -2.4098541,0.96394 -0.945441,0.9639319 -0.945441,2.6878967 0,3.392326 3.5035831,3.392326 0.370677,0 0.852725,-0.07412 0.834233,-0.111134 1.297618,-0.111134 1.130743,0 1.130743,0.648802 0,0.722961 -1.149349,0.722961 -0.407692,0 -1.22349,-0.12978 -0.611763,-0.111133 -0.945433,-0.111133 -3.7074823,0 -3.7074823,3.781609 0,1.83518 0.9825046,2.966029 0.9825047,1.112227 2.7435177,1.112227 2.205955,0 2.928908,-2.280092 0.370675,-1.204975 0.815611,-1.668402 0.463475,-0.463417 1.223484,-0.463417 0.630261,0 1.130742,0.463417 0.5193,0.444887 0.5193,1.149357 0,1.68691 -1.890809,2.799137 Q 15.216241,21 12.528254,21 9.5808389,21 7.2822329,19.591125 5.0021422,18.182249 5.0021422,15.828016 q 0,-2.947407 4.5416822,-4.337775 z"
35
+ id="path1"
36
+ style="stroke-width:0.100048" />
37
+ </svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qwc2",
3
- "version": "2026.01.13",
3
+ "version": "2026.01.14",
4
4
  "description": "QGIS Web Client",
5
5
  "author": "Sourcepole AG",
6
6
  "license": "BSD-2-Clause",