@elixpo/lixsketch 5.5.9 → 5.5.10
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/dist/react/{CopyPaste-4CKJRHJ3.js → CopyPaste-SA72NDFN.js} +2 -2
- package/dist/react/{EventDispatcher-AD2BPPPS.js → EventDispatcher-BZQRHROG.js} +2 -2
- package/dist/react/{SceneSerializer-4U4B24JM.js → SceneSerializer-ZPHV6IUP.js} +2 -2
- package/dist/react/{SketchEngine-XWWTA2MT.js → SketchEngine-BCRJ62CV.js} +6 -6
- package/dist/react/TextShape-U6ZZKB35.js +8 -0
- package/dist/react/{chunk-QXFBG7OM.js → chunk-42O6W7DF.js} +18 -1
- package/dist/react/chunk-42O6W7DF.js.map +7 -0
- package/dist/react/{chunk-YVCENC7J.js → chunk-5QIT2WBE.js} +19 -116
- package/dist/react/chunk-5QIT2WBE.js.map +7 -0
- package/dist/react/index.js +3 -3
- package/dist/react/index.js.map +1 -1
- package/dist/react/{textTool-KY4AYUTC.js → textTool-HY5NMM7H.js} +2 -2
- package/package.json +1 -1
- package/src/react/components/sidebars/TextSidebar.jsx +1 -1
- package/src/shapes/TextShape.js +19 -0
- package/src/tools/textTool.js +23 -157
- package/dist/react/TextShape-54ZJ66E3.js +0 -8
- package/dist/react/chunk-QXFBG7OM.js.map +0 -7
- package/dist/react/chunk-YVCENC7J.js.map +0 -7
- /package/dist/react/{CopyPaste-4CKJRHJ3.js.map → CopyPaste-SA72NDFN.js.map} +0 -0
- /package/dist/react/{EventDispatcher-AD2BPPPS.js.map → EventDispatcher-BZQRHROG.js.map} +0 -0
- /package/dist/react/{SceneSerializer-4U4B24JM.js.map → SceneSerializer-ZPHV6IUP.js.map} +0 -0
- /package/dist/react/{SketchEngine-XWWTA2MT.js.map → SketchEngine-BCRJ62CV.js.map} +0 -0
- /package/dist/react/{TextShape-54ZJ66E3.js.map → TextShape-U6ZZKB35.js.map} +0 -0
- /package/dist/react/{textTool-KY4AYUTC.js.map → textTool-HY5NMM7H.js.map} +0 -0
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
handleTextMouseMove,
|
|
7
7
|
handleTextMouseUp,
|
|
8
8
|
updateCodeToggleForShape
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-5QIT2WBE.js";
|
|
10
10
|
import "./chunk-LHRTXNVE.js";
|
|
11
11
|
import "./chunk-BZE4CEBG.js";
|
|
12
12
|
import "./chunk-L5OARZJ7.js";
|
|
@@ -18,4 +18,4 @@ export {
|
|
|
18
18
|
handleTextMouseUp,
|
|
19
19
|
updateCodeToggleForShape
|
|
20
20
|
};
|
|
21
|
-
//# sourceMappingURL=textTool-
|
|
21
|
+
//# sourceMappingURL=textTool-HY5NMM7H.js.map
|
package/package.json
CHANGED
|
@@ -27,7 +27,7 @@ export default function TextSidebar() {
|
|
|
27
27
|
const activeTool = useSketchStore((s) => s.activeTool)
|
|
28
28
|
const selectedShapeSidebar = useSketchStore((s) => s.selectedShapeSidebar)
|
|
29
29
|
const [textColor, setTextColor] = useState('#fff')
|
|
30
|
-
const [fontSize, setFontSize] = useState('
|
|
30
|
+
const [fontSize, setFontSize] = useState('S')
|
|
31
31
|
const [font, setFont] = useState('lixFont')
|
|
32
32
|
const [codeMode, setCodeMode] = useState(false)
|
|
33
33
|
const [language, setLanguage] = useState('javascript')
|
package/src/shapes/TextShape.js
CHANGED
|
@@ -25,9 +25,28 @@ function updateSelectionFeedback() {}
|
|
|
25
25
|
function deselectElement() { selectedElement = null; }
|
|
26
26
|
function selectElement(el) { selectedElement = el; }
|
|
27
27
|
|
|
28
|
+
function removeLegacySoftWrap(groupElement) {
|
|
29
|
+
const textElement = groupElement.querySelector('text');
|
|
30
|
+
if (!textElement || !textElement.hasAttribute('data-wrap-width')) return;
|
|
31
|
+
const source = textElement.getAttribute('data-wrap-source');
|
|
32
|
+
if (source === null) return;
|
|
33
|
+
|
|
34
|
+
while (textElement.firstChild) textElement.removeChild(textElement.firstChild);
|
|
35
|
+
const x = textElement.getAttribute('x') || 0;
|
|
36
|
+
source.split('\n').forEach((line, index) => {
|
|
37
|
+
const tspan = textElement.ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'tspan');
|
|
38
|
+
tspan.setAttribute('x', x);
|
|
39
|
+
tspan.setAttribute('dy', index === 0 ? '0' : '1.2em');
|
|
40
|
+
tspan.textContent = line || ' ';
|
|
41
|
+
textElement.appendChild(tspan);
|
|
42
|
+
});
|
|
43
|
+
textElement.removeAttribute('data-wrap-width');
|
|
44
|
+
}
|
|
45
|
+
|
|
28
46
|
class TextShape {
|
|
29
47
|
constructor(groupElement) {
|
|
30
48
|
this.group = groupElement;
|
|
49
|
+
removeLegacySoftWrap(groupElement);
|
|
31
50
|
this.shapeName = 'text';
|
|
32
51
|
this.shapeID = groupElement.getAttribute('id') || `text-${String(Date.now()).slice(0, 8)}-${Math.floor(Math.random() * 10000)}`;
|
|
33
52
|
|
package/src/tools/textTool.js
CHANGED
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
getSelectedCodeBlock
|
|
28
28
|
} from './codeTool.js';
|
|
29
29
|
|
|
30
|
-
let textSize = "
|
|
30
|
+
let textSize = "20px";
|
|
31
31
|
let textFont = "lixFont";
|
|
32
32
|
let textColor = null;
|
|
33
33
|
let textAlign = "left";
|
|
@@ -196,10 +196,8 @@ function makeTextEditable(textElement, groupElement) {
|
|
|
196
196
|
input.setAttribute("aria-label", "Edit canvas text");
|
|
197
197
|
input.setAttribute("data-editing", "true");
|
|
198
198
|
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
// original line breaks instead of the auto-wrap output. Falls back
|
|
202
|
-
// to tspan-concatenation for legacy text shapes.
|
|
199
|
+
// Prefer the persisted source so re-opening the editor preserves only
|
|
200
|
+
// the line breaks the user explicitly inserted with Shift+Enter.
|
|
203
201
|
let textContent = '';
|
|
204
202
|
const storedSource = textElement.getAttribute('data-wrap-source');
|
|
205
203
|
if (storedSource !== null && storedSource !== '') {
|
|
@@ -224,12 +222,10 @@ function makeTextEditable(textElement, groupElement) {
|
|
|
224
222
|
input.style.boxSizing = "border-box";
|
|
225
223
|
input.style.overflow = "hidden";
|
|
226
224
|
input.style.resize = "none";
|
|
227
|
-
input.style.whiteSpace = "pre
|
|
225
|
+
input.style.whiteSpace = "pre";
|
|
228
226
|
input.style.minHeight = "1.2em";
|
|
229
227
|
input.style.zIndex = "10000";
|
|
230
228
|
|
|
231
|
-
const svgRect = svg.getBoundingClientRect();
|
|
232
|
-
|
|
233
229
|
// Use the group element's own screenCTM which includes group transform + SVG viewBox transform
|
|
234
230
|
const textBBox = textElement.getBBox();
|
|
235
231
|
let pt = svg.createSVGPoint();
|
|
@@ -243,17 +239,15 @@ function makeTextEditable(textElement, groupElement) {
|
|
|
243
239
|
input.style.top = `${screenPt.y}px`;
|
|
244
240
|
|
|
245
241
|
const svgZoomFactor = svg.getScreenCTM() ? svg.getScreenCTM().a : 1;
|
|
246
|
-
const screenWidth = textBBox.width * svgZoomFactor;
|
|
247
|
-
|
|
248
242
|
input.style.width = "auto";
|
|
249
243
|
input.style.height = "auto";
|
|
250
244
|
|
|
251
|
-
const currentFontSize = textElement.getAttribute("font-size") || "
|
|
245
|
+
const currentFontSize = textElement.getAttribute("font-size") || "20px";
|
|
252
246
|
const currentFontFamily = textElement.getAttribute("font-family") || "lixFont";
|
|
253
247
|
const currentFill = textElement.getAttribute("fill") || "#fff";
|
|
254
248
|
const currentAnchor = textElement.getAttribute("text-anchor") || "start";
|
|
255
249
|
// Scale font-size by zoom so the textarea matches what the user sees on canvas
|
|
256
|
-
const rawSize = parseFloat(currentFontSize) ||
|
|
250
|
+
const rawSize = parseFloat(currentFontSize) || 20;
|
|
257
251
|
const scaledFontSize = `${rawSize * svgZoomFactor}px`;
|
|
258
252
|
|
|
259
253
|
input.style.minWidth = "150px";
|
|
@@ -261,53 +255,31 @@ function makeTextEditable(textElement, groupElement) {
|
|
|
261
255
|
input.style.width = "auto";
|
|
262
256
|
input.style.height = "auto";
|
|
263
257
|
input.style.overflow = "visible";
|
|
264
|
-
input.style.whiteSpace = "pre
|
|
265
|
-
input.style.wordBreak = "
|
|
258
|
+
input.style.whiteSpace = "pre";
|
|
259
|
+
input.style.wordBreak = "normal";
|
|
260
|
+
input.style.overflowWrap = "normal";
|
|
266
261
|
input.style.fontSize = scaledFontSize;
|
|
267
262
|
input.style.fontFamily = currentFontFamily;
|
|
268
263
|
input.style.color = currentFill;
|
|
269
264
|
input.style.lineHeight = "1.2em";
|
|
270
265
|
input.style.textAlign = currentAnchor === "middle" ? "center" : currentAnchor === "end" ? "right" : "left";
|
|
271
266
|
input.style.backgroundColor = "transparent";
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
// rendered canvas or exported scene.
|
|
275
|
-
const _isDark = typeof document !== 'undefined'
|
|
276
|
-
&& document.body
|
|
277
|
-
&& document.body.classList.contains('theme-dark');
|
|
278
|
-
const editingBorder = _isDark ? "rgba(139,136,232,0.95)" : "rgba(91,87,209,0.92)";
|
|
279
|
-
input.style.border = `2px solid ${editingBorder}`;
|
|
280
|
-
input.style.backgroundColor = _isDark
|
|
281
|
-
? "rgba(91,87,209,0.08)"
|
|
282
|
-
: "rgba(91,87,209,0.05)";
|
|
283
|
-
input.style.boxShadow = `0 0 0 2px ${_isDark ? "rgba(139,136,232,0.18)" : "rgba(91,87,209,0.14)"}`;
|
|
284
|
-
input.style.borderRadius = "4px";
|
|
267
|
+
input.style.border = "none";
|
|
268
|
+
input.style.boxShadow = "none";
|
|
285
269
|
input.style.outline = "none";
|
|
286
270
|
document.body.appendChild(input);
|
|
287
271
|
|
|
288
272
|
const adjustHeight = () => {
|
|
289
273
|
input.style.height = 'auto';
|
|
290
274
|
input.style.height = input.scrollHeight + 'px';
|
|
291
|
-
|
|
292
|
-
if (input.scrollHeight > maxHeight) {
|
|
293
|
-
input.style.height = maxHeight + 'px';
|
|
294
|
-
input.style.overflowY = 'auto';
|
|
295
|
-
} else {
|
|
296
|
-
input.style.overflowY = 'hidden';
|
|
297
|
-
}
|
|
275
|
+
input.style.overflowY = 'hidden';
|
|
298
276
|
};
|
|
299
277
|
|
|
300
278
|
const adjustWidth = () => {
|
|
301
279
|
input.style.width = 'auto';
|
|
302
|
-
const maxWidth = svgRect.width - (screenPt.x);
|
|
303
280
|
const contentWidth = Math.max(input.scrollWidth, 150);
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
input.style.overflowX = 'auto';
|
|
307
|
-
} else {
|
|
308
|
-
input.style.width = contentWidth + 'px';
|
|
309
|
-
input.style.overflowX = 'hidden';
|
|
310
|
-
}
|
|
281
|
+
input.style.width = contentWidth + 'px';
|
|
282
|
+
input.style.overflowX = 'hidden';
|
|
311
283
|
};
|
|
312
284
|
adjustHeight();
|
|
313
285
|
adjustWidth();
|
|
@@ -349,58 +321,12 @@ function makeTextEditable(textElement, groupElement) {
|
|
|
349
321
|
groupElement.style.display = "none";
|
|
350
322
|
}
|
|
351
323
|
|
|
352
|
-
/**
|
|
353
|
-
|
|
354
|
-
*
|
|
355
|
-
* `paint(textElement, source, wrapWidth)` renders the user-typed `source`
|
|
356
|
-
* string into tspans, soft-wrapping any line that exceeds `wrapWidth`
|
|
357
|
-
* pixels (in SVG units). `wrapWidth = null | 0` falls back to the old
|
|
358
|
-
* "explicit newlines only" behaviour so existing text shapes that have
|
|
359
|
-
* never been width-resized look identical.
|
|
360
|
-
*
|
|
361
|
-
* The raw `source` is persisted on the element as `data-wrap-source`
|
|
362
|
-
* so subsequent edits (textarea re-open) and serialisation can rebuild
|
|
363
|
-
* the original content even after auto-wrap inserted line breaks.
|
|
364
|
-
*/
|
|
365
|
-
function measureSegment(text, refTextElement) {
|
|
366
|
-
if (!text) return 0;
|
|
367
|
-
const NS = 'http://www.w3.org/2000/svg';
|
|
368
|
-
const probe = document.createElementNS(NS, 'tspan');
|
|
369
|
-
probe.textContent = text;
|
|
370
|
-
refTextElement.appendChild(probe);
|
|
371
|
-
let width = 0;
|
|
372
|
-
try { width = probe.getComputedTextLength(); } catch {}
|
|
373
|
-
refTextElement.removeChild(probe);
|
|
374
|
-
return width;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
function paintTextContent(textElement, source, wrapWidth) {
|
|
324
|
+
/** Render only explicit editor line breaks; text never soft-wraps. */
|
|
325
|
+
function paintTextContent(textElement, source) {
|
|
378
326
|
while (textElement.firstChild) textElement.removeChild(textElement.firstChild);
|
|
379
327
|
const x = textElement.getAttribute('x') || 0;
|
|
380
328
|
const NS = 'http://www.w3.org/2000/svg';
|
|
381
|
-
const
|
|
382
|
-
const finalLines = [];
|
|
383
|
-
|
|
384
|
-
for (const paragraph of paragraphs) {
|
|
385
|
-
const para = paragraph.replace(/ /g, ' ');
|
|
386
|
-
if (!wrapWidth || wrapWidth <= 0) {
|
|
387
|
-
finalLines.push(para || ' ');
|
|
388
|
-
continue;
|
|
389
|
-
}
|
|
390
|
-
// Word-wrap the paragraph against wrapWidth.
|
|
391
|
-
const words = para.split(/(\s+)/); // keep separators so spacing survives
|
|
392
|
-
let cur = '';
|
|
393
|
-
for (const token of words) {
|
|
394
|
-
const candidate = cur + token;
|
|
395
|
-
if (measureSegment(candidate, textElement) > wrapWidth && cur.trim().length > 0) {
|
|
396
|
-
finalLines.push(cur);
|
|
397
|
-
cur = token.replace(/^\s+/, ''); // drop the leading break-space
|
|
398
|
-
} else {
|
|
399
|
-
cur = candidate;
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
finalLines.push(cur.length ? cur : ' ');
|
|
403
|
-
}
|
|
329
|
+
const finalLines = (source || '').split('\n').map((line) => line.replace(/ /g, ' '));
|
|
404
330
|
|
|
405
331
|
finalLines.forEach((line, i) => {
|
|
406
332
|
const tspan = document.createElementNS(NS, 'tspan');
|
|
@@ -411,21 +337,6 @@ function paintTextContent(textElement, source, wrapWidth) {
|
|
|
411
337
|
});
|
|
412
338
|
}
|
|
413
339
|
|
|
414
|
-
function getWrapSource(textElement) {
|
|
415
|
-
const stored = textElement.getAttribute('data-wrap-source');
|
|
416
|
-
if (stored !== null && stored !== '') return stored;
|
|
417
|
-
// Fallback: rebuild from existing tspans (legacy text shapes without
|
|
418
|
-
// the attribute). Join each tspan as a paragraph.
|
|
419
|
-
const tspans = textElement.querySelectorAll('tspan');
|
|
420
|
-
if (tspans.length === 0) return textElement.textContent || '';
|
|
421
|
-
return Array.from(tspans).map((t) => (t.textContent || '').replace(/ /g, ' ')).join('\n');
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
function getWrapWidth(textElement) {
|
|
425
|
-
const v = parseFloat(textElement.getAttribute('data-wrap-width') || '');
|
|
426
|
-
return Number.isFinite(v) && v > 0 ? v : 0;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
340
|
function renderText(input, textElement, deleteIfEmpty = false) {
|
|
430
341
|
if (!input || !document.body.contains(input)) {
|
|
431
342
|
return;
|
|
@@ -483,13 +394,12 @@ function renderText(input, textElement, deleteIfEmpty = false) {
|
|
|
483
394
|
removeSelectionFeedback();
|
|
484
395
|
}
|
|
485
396
|
} else {
|
|
486
|
-
// Persist
|
|
487
|
-
//
|
|
488
|
-
// ones. Then call the shared paint helper \u2014 it falls back to the
|
|
489
|
-
// legacy "explicit newlines only" path when no wrap-width is set.
|
|
397
|
+
// Persist and render only the user's explicit Shift+Enter line
|
|
398
|
+
// breaks. Long lines remain long on the infinite canvas.
|
|
490
399
|
const cleanSource = text.replace(/\u00A0/g, ' ');
|
|
491
400
|
textElement.setAttribute('data-wrap-source', cleanSource);
|
|
492
|
-
|
|
401
|
+
textElement.removeAttribute('data-wrap-width');
|
|
402
|
+
paintTextContent(textElement, cleanSource);
|
|
493
403
|
|
|
494
404
|
gElement.style.display = 'block';
|
|
495
405
|
|
|
@@ -551,18 +461,13 @@ function createSelectionFeedback(groupElement) {
|
|
|
551
461
|
selectionBox.setAttribute("pointer-events", "none");
|
|
552
462
|
groupElement.appendChild(selectionBox);
|
|
553
463
|
|
|
554
|
-
//
|
|
555
|
-
//
|
|
556
|
-
// element; the paint helper re-flows tspans so the text wraps into
|
|
557
|
-
// multiple lines. Corner anchors keep their font-size scaling
|
|
558
|
-
// behaviour.
|
|
464
|
+
// Corner anchors scale font size. Width handles are intentionally
|
|
465
|
+
// omitted because text width is content-driven and never soft-wraps.
|
|
559
466
|
const handlesData = [
|
|
560
467
|
{ name: 'nw', x: selX, y: selY, cursor: 'nwse-resize' },
|
|
561
468
|
{ name: 'ne', x: selX + selWidth, y: selY, cursor: 'nesw-resize' },
|
|
562
469
|
{ name: 'sw', x: selX, y: selY + selHeight, cursor: 'nesw-resize' },
|
|
563
470
|
{ name: 'se', x: selX + selWidth, y: selY + selHeight, cursor: 'nwse-resize' },
|
|
564
|
-
{ name: 'e', x: selX + selWidth, y: selY + selHeight / 2, cursor: 'ew-resize' },
|
|
565
|
-
{ name: 'w', x: selX, y: selY + selHeight / 2, cursor: 'ew-resize' },
|
|
566
471
|
];
|
|
567
472
|
|
|
568
473
|
resizeHandles = {};
|
|
@@ -668,10 +573,6 @@ function updateSelectionFeedback() {
|
|
|
668
573
|
{ name: 'ne', x: selX + selWidth, y: selY },
|
|
669
574
|
{ name: 'sw', x: selX, y: selY + selHeight },
|
|
670
575
|
{ name: 'se', x: selX + selWidth, y: selY + selHeight },
|
|
671
|
-
// Issue #48 bug #5: keep E/W midpoint handles aligned with the
|
|
672
|
-
// resize so they follow the box as it scales.
|
|
673
|
-
{ name: 'e', x: selX + selWidth, y: selY + selHeight / 2 },
|
|
674
|
-
{ name: 'w', x: selX, y: selY + selHeight / 2 },
|
|
675
576
|
];
|
|
676
577
|
|
|
677
578
|
handlesData.forEach(handle => {
|
|
@@ -963,7 +864,6 @@ const handleMouseMove = (event) => {
|
|
|
963
864
|
const startHeight = startBBox.height;
|
|
964
865
|
|
|
965
866
|
let anchorX, anchorY;
|
|
966
|
-
const isEdgeWidth = currentResizeHandle === 'e' || currentResizeHandle === 'w';
|
|
967
867
|
|
|
968
868
|
switch (currentResizeHandle) {
|
|
969
869
|
case 'nw':
|
|
@@ -982,34 +882,8 @@ const handleMouseMove = (event) => {
|
|
|
982
882
|
anchorX = startX;
|
|
983
883
|
anchorY = startY;
|
|
984
884
|
break;
|
|
985
|
-
case 'e':
|
|
986
|
-
anchorX = startX;
|
|
987
|
-
anchorY = startY + startHeight / 2;
|
|
988
|
-
break;
|
|
989
|
-
case 'w':
|
|
990
|
-
anchorX = startX + startWidth;
|
|
991
|
-
anchorY = startY + startHeight / 2;
|
|
992
|
-
break;
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
// Issue #48 phase D follow-up: E/W now drives TRUE word-wrap.
|
|
996
|
-
// The drag distance becomes the wrap-width target; we re-paint
|
|
997
|
-
// the text with the new width and short-circuit the font-size
|
|
998
|
-
// scaling path that the corners use.
|
|
999
|
-
if (isEdgeWidth) {
|
|
1000
|
-
const newWrap = Math.abs(currentPoint.x - anchorX);
|
|
1001
|
-
const minWrap = startFontSize * 2; // at least ~one short word wide
|
|
1002
|
-
const clampedWrap = Math.max(minWrap, newWrap);
|
|
1003
|
-
textElement.setAttribute('data-wrap-width', String(clampedWrap));
|
|
1004
|
-
const source = getWrapSource(textElement);
|
|
1005
|
-
paintTextContent(textElement, source, clampedWrap);
|
|
1006
|
-
if (typeof updateSelectionFeedback === 'function') {
|
|
1007
|
-
setTimeout(updateSelectionFeedback, 0);
|
|
1008
|
-
}
|
|
1009
|
-
return;
|
|
1010
885
|
}
|
|
1011
886
|
|
|
1012
|
-
const newWidth = Math.abs(currentPoint.x - anchorX);
|
|
1013
887
|
const newHeight = Math.abs(currentPoint.y - anchorY);
|
|
1014
888
|
const chosenScale = newHeight / startHeight;
|
|
1015
889
|
|
|
@@ -1044,14 +918,6 @@ const handleMouseMove = (event) => {
|
|
|
1044
918
|
newAnchorX = currentBBox.x;
|
|
1045
919
|
newAnchorY = currentBBox.y;
|
|
1046
920
|
break;
|
|
1047
|
-
case 'e':
|
|
1048
|
-
newAnchorX = currentBBox.x;
|
|
1049
|
-
newAnchorY = currentBBox.y + currentBBox.height / 2;
|
|
1050
|
-
break;
|
|
1051
|
-
case 'w':
|
|
1052
|
-
newAnchorX = currentBBox.x + currentBBox.width;
|
|
1053
|
-
newAnchorY = currentBBox.y + currentBBox.height / 2;
|
|
1054
|
-
break;
|
|
1055
921
|
}
|
|
1056
922
|
|
|
1057
923
|
const deltaX = anchorX - newAnchorX;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/shapes/TextShape.js"],
|
|
4
|
-
"sourcesContent": ["/* eslint-disable */\n// TextShape class - extracted from writeText.js\n// Depends on globals: svg, shapes, currentShape\n\nfunction extractRotationFromTransform(el) {\n const t = el.getAttribute(\"transform\") || \"\";\n const m = t.match(/rotate\\(([^,)]+)/);\n return m ? parseFloat(m[1]) : 0;\n}\nfunction updateAttachedArrows(wrapper) {\n if (!wrapper || typeof shapes === \"undefined\") return;\n shapes.forEach(s => {\n if (s && s.shapeName === \"arrow\" && typeof s.updateAttachments === \"function\") {\n if ((s.attachedToStart && s.attachedToStart.shape === wrapper) ||\n (s.attachedToEnd && s.attachedToEnd.shape === wrapper)) {\n s.updateAttachments();\n }\n }\n });\n}\nlet isDragging = false;\nlet hoveredFrameText = null;\nlet selectedElement = null;\nfunction updateSelectionFeedback() {}\nfunction deselectElement() { selectedElement = null; }\nfunction selectElement(el) { selectedElement = el; }\n\nclass TextShape {\n constructor(groupElement) {\n this.group = groupElement;\n this.shapeName = 'text';\n this.shapeID = groupElement.getAttribute('id') || `text-${String(Date.now()).slice(0, 8)}-${Math.floor(Math.random() * 10000)}`;\n \n // Frame attachment properties\n this.parentFrame = null;\n \n // Update group attributes \u2014 ensure data-type is set for textTool interaction\n this.group.setAttribute('type', 'text');\n this.group.setAttribute('data-type', 'text-group');\n this.group.shapeName = 'text';\n this.group.shapeID = this.shapeID;\n }\n \n // Position and dimension properties for frame compatibility\n get x() {\n const transform = this.group.transform.baseVal.consolidate();\n return transform ? transform.matrix.e : parseFloat(this.group.getAttribute('data-x')) || 0;\n }\n \n set x(value) {\n const transform = this.group.transform.baseVal.consolidate();\n const currentY = transform ? transform.matrix.f : parseFloat(this.group.getAttribute('data-y')) || 0;\n const rotation = extractRotationFromTransform(this.group) || 0;\n const textElement = this.group.querySelector('text');\n if (textElement) {\n const bbox = textElement.getBBox();\n const centerX = bbox.x + bbox.width / 2;\n const centerY = bbox.y + bbox.height / 2;\n this.group.setAttribute('transform', `translate(${value}, ${currentY}) rotate(${rotation}, ${centerX}, ${centerY})`);\n } else {\n this.group.setAttribute('transform', `translate(${value}, ${currentY})`);\n }\n this.group.setAttribute('data-x', value);\n }\n \n get y() {\n const transform = this.group.transform.baseVal.consolidate();\n return transform ? transform.matrix.f : parseFloat(this.group.getAttribute('data-y')) || 0;\n }\n \n set y(value) {\n const transform = this.group.transform.baseVal.consolidate();\n const currentX = transform ? transform.matrix.e : parseFloat(this.group.getAttribute('data-x')) || 0;\n const rotation = extractRotationFromTransform(this.group) || 0;\n const textElement = this.group.querySelector('text');\n if (textElement) {\n const bbox = textElement.getBBox();\n const centerX = bbox.x + bbox.width / 2;\n const centerY = bbox.y + bbox.height / 2;\n this.group.setAttribute('transform', `translate(${currentX}, ${value}) rotate(${rotation}, ${centerX}, ${centerY})`);\n } else {\n this.group.setAttribute('transform', `translate(${currentX}, ${value})`);\n }\n this.group.setAttribute('data-y', value);\n }\n \n get width() {\n const textElement = this.group.querySelector('text');\n if (textElement) {\n return textElement.getBBox().width;\n }\n return 0;\n }\n \n set width(value) {\n // Text width is determined by content and font size, not directly settable\n // This is here for frame compatibility but doesn't change the text\n }\n \n get height() {\n const textElement = this.group.querySelector('text');\n if (textElement) {\n return textElement.getBBox().height;\n }\n return 0;\n }\n \n set height(value) {\n // Text height is determined by content and font size, not directly settable\n // This is here for frame compatibility but doesn't change the text\n }\n \n get rotation() {\n return extractRotationFromTransform(this.group) || 0;\n }\n \n set rotation(value) {\n const currentTransform = this.group.transform.baseVal.consolidate();\n const currentX = currentTransform ? currentTransform.matrix.e : 0;\n const currentY = currentTransform ? currentTransform.matrix.f : 0;\n const textElement = this.group.querySelector('text');\n if (textElement) {\n const bbox = textElement.getBBox();\n const centerX = bbox.x + bbox.width / 2;\n const centerY = bbox.y + bbox.height / 2;\n this.group.setAttribute('transform', `translate(${currentX}, ${currentY}) rotate(${value}, ${centerX}, ${centerY})`);\n }\n }\n\n move(dx, dy) {\n const currentTransform = this.group.transform.baseVal.consolidate();\n const currentX = currentTransform ? currentTransform.matrix.e : 0;\n const currentY = currentTransform ? currentTransform.matrix.f : 0;\n\n this.x = currentX + dx;\n this.y = currentY + dy;\n\n // Only update frame containment if we're actively dragging the shape itself\n // and not being moved by a parent frame\n if (isDragging && !this.isBeingMovedByFrame) {\n this.updateFrameContainment();\n }\n\n this.updateAttachedArrows();\n }\n\n updateAttachedArrows() {\n updateAttachedArrows(this);\n }\n\n updateFrameContainment() {\n // Don't update if we're being moved by a frame\n if (this.isBeingMovedByFrame) return;\n \n let targetFrame = null;\n \n // Find which frame this shape is over\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n shapes.forEach(shape => {\n if (shape.shapeName === 'frame' && shape.isShapeInFrame(this)) {\n targetFrame = shape;\n }\n });\n }\n \n // If we have a parent frame and we're being dragged, temporarily remove clipping\n if (this.parentFrame && isDragging) {\n this.parentFrame.temporarilyRemoveFromFrame(this);\n }\n \n // Update frame highlighting\n if (hoveredFrameText && hoveredFrameText !== targetFrame) {\n hoveredFrameText.removeHighlight();\n }\n \n if (targetFrame && targetFrame !== hoveredFrameText) {\n targetFrame.highlightFrame();\n }\n \n hoveredFrameText = targetFrame;\n }\n\n contains(x, y) {\n const textElement = this.group.querySelector('text');\n if (!textElement || typeof textElement.getBBox !== 'function') return false;\n\n let bbox;\n try { bbox = textElement.getBBox(); } catch { return false; }\n const padding = 8; // Selection padding\n\n const CTM = this.group.getCTM();\n if (!CTM) return false;\n \n const inverseCTM = CTM.inverse();\n const svgPoint = svg.createSVGPoint();\n svgPoint.x = x;\n svgPoint.y = y;\n const transformedPoint = svgPoint.matrixTransform(inverseCTM);\n \n return transformedPoint.x >= bbox.x - padding && \n transformedPoint.x <= bbox.x + bbox.width + padding &&\n transformedPoint.y >= bbox.y - padding && \n transformedPoint.y <= bbox.y + bbox.height + padding;\n }\n\n // Add draw method for consistency with other shapes\n draw() {\n // Text doesn't need redrawing like other shapes, but we need this method for consistency\n if (selectedElement === this.group) {\n updateSelectionFeedback();\n }\n }\n\n // Add methods for frame compatibility\n removeSelection() {\n if (selectedElement === this.group) {\n deselectElement();\n }\n }\n\n selectShape() {\n // Use the real textTool selectElement (with selection feedback) if available\n if (typeof window !== 'undefined' && window.__selectTextElement) {\n window.__selectTextElement(this.group);\n } else {\n selectElement(this.group);\n }\n }\n}\n\nexport { TextShape };\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|