@svgedit/svgcanvas 7.4.1 → 7.4.2

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.
package/core/undo.js CHANGED
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import * as draw from './draw.js'
8
8
  import * as hstry from './history.js'
9
+ import { BBOX_AFFECTING_ATTRS } from './history.js'
9
10
  import {
10
11
  getRotationAngle, getBBox as utilsGetBBox, setHref, getStrokedBBoxDefaultVisible
11
12
  } from './utilities.js'
@@ -204,7 +205,10 @@ export const changeSelectedAttributeNoUndoMethod = (attr, newValue, elems) => {
204
205
  } else if (attr === '#href') {
205
206
  setHref(elem, newValue)
206
207
  } else if (newValue) {
207
- elem.setAttribute(attr, isNaN(parseFloat(newValue)) ? newValue : parseFloat(newValue))
208
+ // Number(), unlike parseFloat(), rejects a value with a trailing unit suffix
209
+ // (e.g. "10pt", "2px") instead of silently truncating it to a bare number.
210
+ const asNumber = Number(newValue)
211
+ elem.setAttribute(attr, Number.isNaN(asNumber) ? newValue : asNumber)
208
212
  } else if (typeof newValue === 'number') {
209
213
  elem.setAttribute(attr, newValue)
210
214
  } else {
@@ -239,26 +243,39 @@ export const changeSelectedAttributeNoUndoMethod = (attr, newValue, elems) => {
239
243
  svgCanvas.selectorManager.requestSelector(elem).resize()
240
244
  }, 0)
241
245
  }
242
- // if this element was rotated, and we changed the position of this element
243
- // we need to update the rotational transform attribute
246
+ // Only recalculate rotation center for attributes that change element geometry.
247
+ // Non-geometric attributes (stroke-width, fill, opacity, etc.) don't affect
248
+ // the bbox center, so the rotation is already correct and must not be touched.
249
+ // BBOX_AFFECTING_ATTRS is imported from history.js to keep the list in one place.
244
250
  const angle = getRotationAngle(elem)
245
- if (angle !== 0 && attr !== 'transform') {
251
+ if (angle !== 0 && attr !== 'transform' && BBOX_AFFECTING_ATTRS.has(attr)) {
246
252
  const tlist = getTransformList(elem)
247
253
  let n = tlist.numberOfItems
248
254
  while (n--) {
249
255
  const xform = tlist.getItem(n)
250
256
  if (xform.type === 4) {
251
- // remove old rotate
257
+ // Compute bbox BEFORE removing the rotation so we can bail out
258
+ // safely if getBBox returns nothing (avoids losing the rotation).
259
+ const box = utilsGetBBox(elem)
260
+ if (!box) break
261
+
252
262
  tlist.removeItem(n)
253
263
 
254
- const box = utilsGetBBox(elem)
264
+ // Transform bbox center through only the transforms that come
265
+ // AFTER the rotation in the list (not the pre-rotation transforms).
266
+ // After removeItem(n), what was at n+1 is now at n.
267
+ let centerMatrix
268
+ if (n < tlist.numberOfItems) {
269
+ centerMatrix = transformListToTransform(tlist, n, tlist.numberOfItems - 1).matrix
270
+ } else {
271
+ centerMatrix = svgCanvas.getSvgRoot().createSVGMatrix() // identity
272
+ }
255
273
  const center = transformPoint(
256
- box.x + box.width / 2, box.y + box.height / 2, transformListToTransform(tlist).matrix
274
+ box.x + box.width / 2, box.y + box.height / 2, centerMatrix
257
275
  )
258
- const cx = center.x
259
- const cy = center.y
276
+
260
277
  const newrot = svgCanvas.getSvgRoot().createSVGTransform()
261
- newrot.setRotate(angle, cx, cy)
278
+ newrot.setRotate(angle, center.x, center.y)
262
279
  tlist.insertItemBefore(newrot, n)
263
280
  break
264
281
  }