@configuratorware/configurator-admingui 1.37.0 → 1.37.1

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.
@@ -71,19 +71,45 @@ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArra
71
71
 
72
72
  function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
73
73
 
74
- function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
75
-
76
- function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
77
-
78
74
  function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
79
75
 
80
76
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
81
77
 
78
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
79
+
80
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
81
+
82
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
83
+
82
84
  var maskTypeValues = Object.values(_maskTypes.maskTypes);
83
85
  var defaultValue = {
84
86
  maskType: (0, _first["default"])(maskTypeValues),
85
87
  maskData: {}
86
88
  };
89
+ var safeClipElements = ['ellipse', 'circle', 'rect', 'line', 'polygon', 'polyline', 'path'];
90
+
91
+ var getUnsafeElements = function getUnsafeElements(svgString) {
92
+ var matchedElements = svgString.matchAll(/<((?!svg)\w+)/g);
93
+ var unsafeElements = [];
94
+
95
+ var _iterator = _createForOfIteratorHelper(matchedElements),
96
+ _step;
97
+
98
+ try {
99
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
100
+ var match = _step.value;
101
+ var tagName = match[1];
102
+ !safeClipElements.includes(tagName) && !unsafeElements.includes(tagName) && unsafeElements.push(tagName);
103
+ }
104
+ } catch (err) {
105
+ _iterator.e(err);
106
+ } finally {
107
+ _iterator.f();
108
+ }
109
+
110
+ return unsafeElements;
111
+ };
112
+
87
113
  var UploadStatus = (0, _withStyles["default"])({
88
114
  uploadStatusContainer: {
89
115
  textAlign: 'right'
@@ -113,6 +139,16 @@ var styles = {
113
139
  }
114
140
  }
115
141
  };
142
+ var StyledDialog = (0, _withStyles["default"])({
143
+ dialogContentTextRoot: {
144
+ display: 'flex',
145
+ '& canvas': {
146
+ margin: 'auto',
147
+ maxHeight: '100%',
148
+ maxWidth: '100%'
149
+ }
150
+ }
151
+ })(_Dialog["default"]);
116
152
  var MaskEditorField = (0, _withStyles["default"])(styles)((0, _withFileUpload.withFileUpload)(function (_ref2) {
117
153
  var value = _ref2.value,
118
154
  _ref2$ratio = _ref2.ratio,
@@ -144,7 +180,7 @@ var MaskEditorField = (0, _withStyles["default"])(styles)((0, _withFileUpload.wi
144
180
  freeFormFile = _useState6[0],
145
181
  setFreeFormFile = _useState6[1];
146
182
 
147
- var _useState7 = (0, _react.useState)(false),
183
+ var _useState7 = (0, _react.useState)((initialValue === null || initialValue === void 0 ? void 0 : initialValue.maskDataUrl) || false),
148
184
  _useState8 = _slicedToArray(_useState7, 2),
149
185
  dataUrl = _useState8[0],
150
186
  setDataUrl = _useState8[1];
@@ -154,6 +190,11 @@ var MaskEditorField = (0, _withStyles["default"])(styles)((0, _withFileUpload.wi
154
190
  embeddedImageError = _useState10[0],
155
191
  setEmbeddedImageError = _useState10[1];
156
192
 
193
+ var _useState11 = (0, _react.useState)([]),
194
+ _useState12 = _slicedToArray(_useState11, 2),
195
+ unsafeElements = _useState12[0],
196
+ setUnsafeElements = _useState12[1];
197
+
157
198
  var maskType = currentValue.maskType,
158
199
  maskData = currentValue.maskData;
159
200
 
@@ -173,7 +214,7 @@ var MaskEditorField = (0, _withStyles["default"])(styles)((0, _withFileUpload.wi
173
214
 
174
215
  var resetState = function resetState() {
175
216
  setFreeFormFile(false);
176
- setDataUrl(false);
217
+ setDataUrl((initialValue === null || initialValue === void 0 ? void 0 : initialValue.maskDataUrl) || false);
177
218
  setEmbeddedImageError(false);
178
219
  };
179
220
 
@@ -191,15 +232,20 @@ var MaskEditorField = (0, _withStyles["default"])(styles)((0, _withFileUpload.wi
191
232
 
192
233
  if (embeddedImageIndex !== -1) {
193
234
  setEmbeddedImageError(true);
194
- } else {
195
- setDataUrl(reader.result);
196
- setFreeFormFile(file);
197
- clearMaskData();
235
+ return;
198
236
  }
237
+
238
+ setUnsafeElements(getUnsafeElements(svgString));
239
+ setDataUrl(reader.result);
240
+ setFreeFormFile(file);
241
+ clearMaskData();
199
242
  }, false);
200
243
  reader.readAsDataURL(file);
201
244
  };
202
245
 
246
+ (0, _react.useEffect)(function () {
247
+ (initialValue === null || initialValue === void 0 ? void 0 : initialValue.maskDataUrl) && setDataUrl(initialValue.maskDataUrl);
248
+ }, [initialValue]);
203
249
  (0, _react.useEffect)(function () {
204
250
  // update the current value explicitly with the value prop (eg. the changes has been saved)
205
251
  if ((0, _size["default"])(value) > 0) {
@@ -224,9 +270,9 @@ var MaskEditorField = (0, _withStyles["default"])(styles)((0, _withFileUpload.wi
224
270
  return function (maskType) {
225
271
  var _maskTypes$none$maskT;
226
272
 
227
- return showDialog && (_maskTypes$none$maskT = {}, _defineProperty(_maskTypes$none$maskT, _maskTypes.maskTypes.none, false), _defineProperty(_maskTypes$none$maskT, _maskTypes.maskTypes.circle, _circle["default"]), _defineProperty(_maskTypes$none$maskT, _maskTypes.maskTypes.freeForm, initialValue.maskDataUrl || dataUrl || "".concat(freeFormUrl, "?v=").concat(Math.random())), _maskTypes$none$maskT)[maskType];
273
+ return showDialog && (_maskTypes$none$maskT = {}, _defineProperty(_maskTypes$none$maskT, _maskTypes.maskTypes.none, false), _defineProperty(_maskTypes$none$maskT, _maskTypes.maskTypes.circle, _circle["default"]), _defineProperty(_maskTypes$none$maskT, _maskTypes.maskTypes.freeForm, dataUrl || "".concat(freeFormUrl, "?v=").concat(Math.random())), _maskTypes$none$maskT)[maskType];
228
274
  };
229
- }, [freeFormUrl, dataUrl, showDialog, initialValue]);
275
+ }, [freeFormUrl, dataUrl, showDialog]);
230
276
  var getMaskDataRef = (0, _react.useRef)(function () {});
231
277
 
232
278
  var onOkClick = /*#__PURE__*/function () {
@@ -289,7 +335,7 @@ var MaskEditorField = (0, _withStyles["default"])(styles)((0, _withFileUpload.wi
289
335
  color: "primary"
290
336
  }),
291
337
  label: (0, _i18n.t)('maskEditor.checkboxLabel')
292
- })), /*#__PURE__*/_react["default"].createElement(_Dialog["default"], {
338
+ })), /*#__PURE__*/_react["default"].createElement(StyledDialog, {
293
339
  title: /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement("div", {
294
340
  className: classes.inlineFormWrapper
295
341
  }, (0, _i18n.T)('mask editor'), /*#__PURE__*/_react["default"].createElement(_SelectField["default"], {
@@ -314,7 +360,13 @@ var MaskEditorField = (0, _withStyles["default"])(styles)((0, _withFileUpload.wi
314
360
  })), embeddedImageError && /*#__PURE__*/_react["default"].createElement(_Typography["default"], {
315
361
  variant: "body1",
316
362
  color: "error"
317
- }, (0, _i18n.t)('maskEditor.embeddedImageError'))),
363
+ }, (0, _i18n.t)('maskEditor.embeddedImageError')), unsafeElements.length > 0 && /*#__PURE__*/_react["default"].createElement(_Typography["default"], {
364
+ variant: "body1",
365
+ color: "error"
366
+ }, (0, _i18n.T)('maskEditor.unsafeElementsError', {
367
+ unsafeElements: unsafeElements.join(', '),
368
+ safeClipElements: safeClipElements.join(', ')
369
+ }))),
318
370
  open: showDialog,
319
371
  maxWidth: 'xl',
320
372
  fullWidth: false,
@@ -44,7 +44,8 @@ require("../../../../App/i18n").use({
44
44
  uploadAndSave: 'Upload and save',
45
45
  uploadFailed: 'Failed to upload the SVG',
46
46
  uploadInProgress: 'Upload in progress, please wait...',
47
- embeddedImageError: 'The provided file contains an embedded raster graphic. Please use plain / vector only svg files.'
47
+ embeddedImageError: 'The provided file contains an embedded raster graphic. Please use plain / vector only svg files.',
48
+ unsafeElementsError: 'The provided file contains elements that are not safe to be used for clipping. Please consider using only the following safe elements: %{safeClipElements}'
48
49
  },
49
50
  validation: {
50
51
  designProductionMethod: {
@@ -106,7 +107,8 @@ require("../../../../App/i18n").use({
106
107
  uploadAndSave: 'Hochladen und speichern',
107
108
  uploadFailed: 'Fehler beim Hochladen von SVG',
108
109
  uploadInProgress: 'Upload läuft, bitte warten...',
109
- embeddedImageError: 'Die gewählte Datei enthält eingebettete Pixelgrafiken und kann nicht verwendet werden. Bitte wähle eine Datei, die ausschließlich Vektordaten enthält.'
110
+ embeddedImageError: 'Die gewählte Datei enthält eingebettete Pixelgrafiken und kann nicht verwendet werden. Bitte wähle eine Datei, die ausschließlich Vektordaten enthält.',
111
+ unsafeElementsError: 'Die verwendete Datei beinhaltet Elemente, die für das Beschneiden nicht verwendet werden können. Bitte verwenden Sie nur folgende Elemente: %{safeClipElements}'
110
112
  },
111
113
  validation: {
112
114
  designProductionMethod: {
@@ -31,6 +31,12 @@ var getPointFromPointerEvent = function getPointFromPointerEvent(evt) {
31
31
  y -= top;
32
32
  }
33
33
 
34
+ if (relativeTo.width) {
35
+ var scaling = relativeTo.getBoundingClientRect().width / relativeTo.width;
36
+ x *= 1 / scaling;
37
+ y *= 1 / scaling;
38
+ }
39
+
34
40
  return {
35
41
  x: x,
36
42
  y: y
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@configuratorware/configurator-admingui",
3
- "version": "1.37.0",
3
+ "version": "1.37.1",
4
4
  "license": "UNLICENSED",
5
5
  "private": false,
6
6
  "dependencies": {
@@ -29,7 +29,7 @@
29
29
  "react-redux-i18n": "^1.9.3",
30
30
  "react-router": "^3.2.6",
31
31
  "react-sortable-hoc": "^1.11.0",
32
- "redhotmagma-visualization": "1.37.0",
32
+ "redhotmagma-visualization": "1.37.1",
33
33
  "redux": "^4.1.0",
34
34
  "redux-logger": "^3.0.6",
35
35
  "redux-persist": "^5.10.0",
@@ -25,6 +25,20 @@ const defaultValue = {
25
25
  maskData: {},
26
26
  };
27
27
 
28
+ const safeClipElements = ['ellipse', 'circle', 'rect', 'line', 'polygon', 'polyline', 'path'];
29
+
30
+ const getUnsafeElements = svgString => {
31
+ const matchedElements = svgString.matchAll(/<((?!svg)\w+)/g);
32
+ const unsafeElements = [];
33
+ for (const match of matchedElements) {
34
+ const tagName = match[1];
35
+ !safeClipElements.includes(tagName) &&
36
+ !unsafeElements.includes(tagName) &&
37
+ unsafeElements.push(tagName);
38
+ }
39
+ return unsafeElements;
40
+ };
41
+
28
42
  const UploadStatus = withStyles({
29
43
  uploadStatusContainer: {
30
44
  textAlign: 'right',
@@ -58,6 +72,17 @@ const styles = {
58
72
  },
59
73
  };
60
74
 
75
+ const StyledDialog = withStyles({
76
+ dialogContentTextRoot: {
77
+ display: 'flex',
78
+ '& canvas': {
79
+ margin: 'auto',
80
+ maxHeight: '100%',
81
+ maxWidth: '100%',
82
+ },
83
+ },
84
+ })(Dialog);
85
+
61
86
  export const MaskEditorField = withStyles(styles)(
62
87
  withFileUpload(
63
88
  ({
@@ -78,8 +103,9 @@ export const MaskEditorField = withStyles(styles)(
78
103
  const [showDialog, toggleDialog] = useState(false);
79
104
  const [currentValue, setCurrentValue] = useState(initialValue);
80
105
  const [freeFormFile, setFreeFormFile] = useState(false);
81
- const [dataUrl, setDataUrl] = useState(false);
106
+ const [dataUrl, setDataUrl] = useState(initialValue?.maskDataUrl || false);
82
107
  const [embeddedImageError, setEmbeddedImageError] = useState(false);
108
+ const [unsafeElements, setUnsafeElements] = useState([]);
83
109
 
84
110
  const { maskType, maskData } = currentValue;
85
111
 
@@ -88,10 +114,10 @@ export const MaskEditorField = withStyles(styles)(
88
114
  setCurrentValue({ ...currentValue, maskType, maskData: {} });
89
115
  resetState();
90
116
  };
91
-
117
+
92
118
  const resetState = () => {
93
119
  setFreeFormFile(false);
94
- setDataUrl(false);
120
+ setDataUrl(initialValue?.maskDataUrl || false);
95
121
  setEmbeddedImageError(false);
96
122
  };
97
123
 
@@ -108,18 +134,22 @@ export const MaskEditorField = withStyles(styles)(
108
134
  const embeddedImageIndex = svgString.search('<image');
109
135
  if (embeddedImageIndex !== -1) {
110
136
  setEmbeddedImageError(true);
137
+ return;
111
138
  }
112
- else {
113
- setDataUrl(reader.result);
114
- setFreeFormFile(file);
115
- clearMaskData();
116
- }
139
+ setUnsafeElements(getUnsafeElements(svgString));
140
+ setDataUrl(reader.result);
141
+ setFreeFormFile(file);
142
+ clearMaskData();
117
143
  },
118
144
  false
119
145
  );
120
146
  reader.readAsDataURL(file);
121
147
  };
122
148
 
149
+ useEffect(() => {
150
+ initialValue?.maskDataUrl && setDataUrl(initialValue.maskDataUrl);
151
+ }, [initialValue]);
152
+
123
153
  useEffect(() => {
124
154
  // update the current value explicitly with the value prop (eg. the changes has been saved)
125
155
  if (size(value) > 0) {
@@ -148,10 +178,9 @@ export const MaskEditorField = withStyles(styles)(
148
178
  {
149
179
  [maskTypes.none]: false,
150
180
  [maskTypes.circle]: circleMask,
151
- [maskTypes.freeForm]:
152
- initialValue.maskDataUrl || dataUrl || `${freeFormUrl}?v=${Math.random()}`,
181
+ [maskTypes.freeForm]: dataUrl || `${freeFormUrl}?v=${Math.random()}`,
153
182
  }[maskType],
154
- [freeFormUrl, dataUrl, showDialog, initialValue]
183
+ [freeFormUrl, dataUrl, showDialog]
155
184
  );
156
185
 
157
186
  const getMaskDataRef = useRef(() => {});
@@ -194,7 +223,7 @@ export const MaskEditorField = withStyles(styles)(
194
223
  label={t('maskEditor.checkboxLabel')}
195
224
  />
196
225
  </Grid>
197
- <Dialog
226
+ <StyledDialog
198
227
  title={
199
228
  <React.Fragment>
200
229
  <div className={classes.inlineFormWrapper}>
@@ -227,11 +256,19 @@ export const MaskEditorField = withStyles(styles)(
227
256
  />
228
257
  )}
229
258
  </div>
230
- {embeddedImageError &&
259
+ {embeddedImageError && (
231
260
  <Typography variant="body1" color="error">
232
261
  {t('maskEditor.embeddedImageError')}
233
262
  </Typography>
234
- }
263
+ )}
264
+ {unsafeElements.length > 0 && (
265
+ <Typography variant="body1" color="error">
266
+ {T('maskEditor.unsafeElementsError', {
267
+ unsafeElements: unsafeElements.join(', '),
268
+ safeClipElements: safeClipElements.join(', '),
269
+ })}
270
+ </Typography>
271
+ )}
235
272
  </React.Fragment>
236
273
  }
237
274
  open={showDialog}
@@ -261,7 +298,7 @@ export const MaskEditorField = withStyles(styles)(
261
298
  />
262
299
 
263
300
  <UploadStatus uploadStatus={uploadStatus} />
264
- </Dialog>
301
+ </StyledDialog>
265
302
  </React.Fragment>
266
303
  );
267
304
  }
@@ -44,7 +44,10 @@ require('../../../../App/i18n').use(
44
44
  uploadAndSave: 'Upload and save',
45
45
  uploadFailed: 'Failed to upload the SVG',
46
46
  uploadInProgress: 'Upload in progress, please wait...',
47
- embeddedImageError: 'The provided file contains an embedded raster graphic. Please use plain / vector only svg files.'
47
+ embeddedImageError:
48
+ 'The provided file contains an embedded raster graphic. Please use plain / vector only svg files.',
49
+ unsafeElementsError:
50
+ 'The provided file contains elements that are not safe to be used for clipping. Please consider using only the following safe elements: %{safeClipElements}',
48
51
  },
49
52
  validation: {
50
53
  designProductionMethod: {
@@ -109,7 +112,10 @@ require('../../../../App/i18n').use(
109
112
  uploadAndSave: 'Hochladen und speichern',
110
113
  uploadFailed: 'Fehler beim Hochladen von SVG',
111
114
  uploadInProgress: 'Upload läuft, bitte warten...',
112
- embeddedImageError: 'Die gewählte Datei enthält eingebettete Pixelgrafiken und kann nicht verwendet werden. Bitte wähle eine Datei, die ausschließlich Vektordaten enthält.'
115
+ embeddedImageError:
116
+ 'Die gewählte Datei enthält eingebettete Pixelgrafiken und kann nicht verwendet werden. Bitte wähle eine Datei, die ausschließlich Vektordaten enthält.',
117
+ unsafeElementsError:
118
+ 'Die verwendete Datei beinhaltet Elemente, die für das Beschneiden nicht verwendet werden können. Bitte verwenden Sie nur folgende Elemente: %{safeClipElements}',
113
119
  },
114
120
  validation: {
115
121
  designProductionMethod: {
@@ -18,6 +18,11 @@ export const getPointFromPointerEvent = (evt, relativeTo = false) => {
18
18
  x -= left;
19
19
  y -= top;
20
20
  }
21
+ if (relativeTo.width) {
22
+ const scaling = relativeTo.getBoundingClientRect().width / relativeTo.width;
23
+ x *= 1 / scaling;
24
+ y *= 1 / scaling;
25
+ }
21
26
  return { x, y };
22
27
  };
23
28