@overtone-art/canvas-editor-core 0.6.1 → 0.6.3
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/index.d.mts +64 -4
- package/dist/index.d.ts +64 -4
- package/dist/index.global.js +52 -52
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +279 -73
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +229 -24
- package/dist/index.mjs.map +1 -1
- package/dist/node.d.mts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/{types-LM7pOkkj.d.mts → types-DSu2jbiV.d.mts} +8 -0
- package/dist/{types-LM7pOkkj.d.ts → types-DSu2jbiV.d.ts} +8 -0
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -209,6 +209,19 @@ var LayerManager = class {
|
|
|
209
209
|
this.layers = this.layers.filter((l) => l.id !== group.id);
|
|
210
210
|
this.syncZOrder();
|
|
211
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Move existing layer records under a group, and announce the new tree.
|
|
214
|
+
* Assigning `group.children` directly emits nothing, and the reorder that
|
|
215
|
+
* follows a grouping is a no-op whenever the group already sits where it
|
|
216
|
+
* belongs — leaving `layers:changed` describing a childless group, so a panel
|
|
217
|
+
* built on it renders the group with nothing inside.
|
|
218
|
+
*/
|
|
219
|
+
adoptChildren(group, children) {
|
|
220
|
+
for (const child of children) child.parentId = group.id;
|
|
221
|
+
group.children = children;
|
|
222
|
+
this.emitChanged();
|
|
223
|
+
this.onPropertyChanged?.();
|
|
224
|
+
}
|
|
212
225
|
/**
|
|
213
226
|
* Re-attach child records to an enlivened group. `data[i]` describes
|
|
214
227
|
* `group._objects[i]` — the order fabric serializes and restores them in. A
|
|
@@ -1130,7 +1143,60 @@ var STROKE2 = "#22c55e";
|
|
|
1130
1143
|
import { util } from "fabric";
|
|
1131
1144
|
|
|
1132
1145
|
// src/pattern/tiled-pattern-object.ts
|
|
1133
|
-
import { FabricObject, Point } from "fabric";
|
|
1146
|
+
import { FabricObject, Point as Point2 } from "fabric";
|
|
1147
|
+
|
|
1148
|
+
// src/text-fit.ts
|
|
1149
|
+
import { Point } from "fabric";
|
|
1150
|
+
var TEXT_FIT_SLACK = 0.5;
|
|
1151
|
+
var MEASURE_WIDTH = 1e5;
|
|
1152
|
+
function unwrappedWidth(text) {
|
|
1153
|
+
const authored = text.width;
|
|
1154
|
+
try {
|
|
1155
|
+
text.set({ width: MEASURE_WIDTH });
|
|
1156
|
+
text.initDimensions?.();
|
|
1157
|
+
const measured = text.calcTextWidth?.() ?? authored;
|
|
1158
|
+
return Number.isFinite(measured) && measured > 0 ? measured : authored;
|
|
1159
|
+
} finally {
|
|
1160
|
+
text.set({ width: authored });
|
|
1161
|
+
text.initDimensions?.();
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
function asText(object) {
|
|
1165
|
+
if (!object) return null;
|
|
1166
|
+
const text = object;
|
|
1167
|
+
return typeof text.text === "string" && typeof text.calcTextWidth === "function" ? text : null;
|
|
1168
|
+
}
|
|
1169
|
+
function textInk(text) {
|
|
1170
|
+
const boxWidth = Math.max(0, text.width ?? 0);
|
|
1171
|
+
const measured = text.calcTextWidth?.() ?? boxWidth;
|
|
1172
|
+
const width = Math.max(1, Math.min(boxWidth, Number.isFinite(measured) ? measured : boxWidth));
|
|
1173
|
+
const slack = (boxWidth - width) / 2;
|
|
1174
|
+
const align = text.textAlign ?? "left";
|
|
1175
|
+
if (align.includes("center")) return { width, dx: 0 };
|
|
1176
|
+
const flip = text.flipX ? -1 : 1;
|
|
1177
|
+
return { width, dx: flip * (align.includes("right") ? slack : -slack) };
|
|
1178
|
+
}
|
|
1179
|
+
function fitTextWidth(object) {
|
|
1180
|
+
const text = asText(object);
|
|
1181
|
+
if (!text || text.path) return false;
|
|
1182
|
+
const before = textInk(text);
|
|
1183
|
+
const fitted = unwrappedWidth(text) + TEXT_FIT_SLACK;
|
|
1184
|
+
if (Math.abs(fitted - text.width) < TEXT_FIT_SLACK) return false;
|
|
1185
|
+
const centre = text.getCenterPoint();
|
|
1186
|
+
text.set({ width: fitted });
|
|
1187
|
+
text.initDimensions?.();
|
|
1188
|
+
const after = textInk(text);
|
|
1189
|
+
const shift = (before.dx - after.dx) * (text.scaleX ?? 1);
|
|
1190
|
+
const radians = (text.angle ?? 0) * Math.PI / 180;
|
|
1191
|
+
const moved = new Point(
|
|
1192
|
+
centre.x + shift * Math.cos(radians),
|
|
1193
|
+
centre.y + shift * Math.sin(radians)
|
|
1194
|
+
);
|
|
1195
|
+
text.setPositionByOrigin(moved, "center", "center");
|
|
1196
|
+
text.setCoords();
|
|
1197
|
+
text.dirty = true;
|
|
1198
|
+
return true;
|
|
1199
|
+
}
|
|
1134
1200
|
|
|
1135
1201
|
// src/pattern/tile-geometry.ts
|
|
1136
1202
|
var MAX_TILES_PER_AXIS = 200;
|
|
@@ -1143,13 +1209,13 @@ function computeTilePositions(config, targetW, targetH, baseW, baseH, origin) {
|
|
|
1143
1209
|
const tileH = Math.max(minTile, baseH * (1 + config.verticalSpacing / 100));
|
|
1144
1210
|
const cols = Math.ceil(span / tileW) + 2;
|
|
1145
1211
|
const rows = Math.ceil(span / tileH) + 2;
|
|
1146
|
-
const
|
|
1147
|
-
const
|
|
1212
|
+
const [colFrom, colTo] = axisRange(config.horizontalLimit, Math.ceil(cols / 2));
|
|
1213
|
+
const [rowFrom, rowTo] = axisRange(config.verticalLimit, Math.ceil(rows / 2));
|
|
1148
1214
|
const shiftX = tileW * (clampOffset(config.offsetX) / 100);
|
|
1149
1215
|
const shiftY = tileH * (clampOffset(config.offsetY) / 100);
|
|
1150
1216
|
const placements = [];
|
|
1151
|
-
for (let j =
|
|
1152
|
-
for (let i =
|
|
1217
|
+
for (let j = rowFrom; j <= rowTo; j++) {
|
|
1218
|
+
for (let i = colFrom; i <= colTo; i++) {
|
|
1153
1219
|
let x = i * tileW + shiftX;
|
|
1154
1220
|
let y = j * tileH + shiftY;
|
|
1155
1221
|
if (config.mode === "brick-horizontal" && mod2(j) === 1) {
|
|
@@ -1179,6 +1245,12 @@ function drawTiles(ctx, img, config, targetW, targetH, baseW, baseH, origin, dra
|
|
|
1179
1245
|
}
|
|
1180
1246
|
ctx.restore();
|
|
1181
1247
|
}
|
|
1248
|
+
function axisRange(limit, half) {
|
|
1249
|
+
const count = Number.isFinite(limit) ? Math.floor(limit) : 0;
|
|
1250
|
+
if (!count || count <= 0) return [-half, half];
|
|
1251
|
+
const from = -Math.floor((count - 1) / 2);
|
|
1252
|
+
return [from, from + count - 1];
|
|
1253
|
+
}
|
|
1182
1254
|
function cornerRadius(origin, w, h) {
|
|
1183
1255
|
const dx = Math.max(Math.abs(origin.x), Math.abs(w - origin.x));
|
|
1184
1256
|
const dy = Math.max(Math.abs(origin.y), Math.abs(h - origin.y));
|
|
@@ -1250,7 +1322,7 @@ var TiledPatternObject = class _TiledPatternObject extends FabricObject {
|
|
|
1250
1322
|
*/
|
|
1251
1323
|
syncBox() {
|
|
1252
1324
|
const { tileW, tileH } = this.baseTile();
|
|
1253
|
-
const origin = this.source.getCenterPoint();
|
|
1325
|
+
const origin = this.source.getCenterPoint().add(this.inkOffset());
|
|
1254
1326
|
const anchor = this.anchorFromOrigin(origin, tileW, tileH, this.config.angle);
|
|
1255
1327
|
this.set({
|
|
1256
1328
|
left: anchor.x,
|
|
@@ -1280,7 +1352,8 @@ var TiledPatternObject = class _TiledPatternObject extends FabricObject {
|
|
|
1280
1352
|
const { tileW, tileH } = this.liveTile();
|
|
1281
1353
|
const centre = this.getCenterPoint();
|
|
1282
1354
|
const shift = this.shiftVector(tileW, tileH, this.liveAngle());
|
|
1283
|
-
|
|
1355
|
+
const ink = this.inkOffset();
|
|
1356
|
+
return new Point2(centre.x - shift.x - ink.x, centre.y - shift.y - ink.y);
|
|
1284
1357
|
}
|
|
1285
1358
|
_render(ctx) {
|
|
1286
1359
|
const { width, height } = this.area;
|
|
@@ -1295,8 +1368,7 @@ var TiledPatternObject = class _TiledPatternObject extends FabricObject {
|
|
|
1295
1368
|
ctx.scale(1 / (this.scaleX || 1), 1 / (this.scaleY || 1));
|
|
1296
1369
|
ctx.rotate(-angle * Math.PI / 180);
|
|
1297
1370
|
ctx.translate(-centre.x, -centre.y);
|
|
1298
|
-
const
|
|
1299
|
-
const origin = { x: centre.x - shift.x, y: centre.y - shift.y };
|
|
1371
|
+
const origin = { x: centre.x, y: centre.y };
|
|
1300
1372
|
const config = { ...this.config, angle, offsetX: 0, offsetY: 0 };
|
|
1301
1373
|
drawTiles(
|
|
1302
1374
|
ctx,
|
|
@@ -1325,7 +1397,6 @@ var TiledPatternObject = class _TiledPatternObject extends FabricObject {
|
|
|
1325
1397
|
if (!snapshot) return [];
|
|
1326
1398
|
const centre = this.getCenterPoint();
|
|
1327
1399
|
const angle = this.liveAngle();
|
|
1328
|
-
const shift = this.shiftVector(tileW, tileH, angle);
|
|
1329
1400
|
drawTiles(
|
|
1330
1401
|
ctx,
|
|
1331
1402
|
snapshot,
|
|
@@ -1334,7 +1405,7 @@ var TiledPatternObject = class _TiledPatternObject extends FabricObject {
|
|
|
1334
1405
|
height,
|
|
1335
1406
|
tileW,
|
|
1336
1407
|
tileH,
|
|
1337
|
-
{ x: centre.x
|
|
1408
|
+
{ x: centre.x, y: centre.y },
|
|
1338
1409
|
// The fallback raster is built at 1:1, so a device pixel is a canvas unit.
|
|
1339
1410
|
this.drawSize(tileW, tileH, TILE_BLEED_DEVICE_PX)
|
|
1340
1411
|
);
|
|
@@ -1377,7 +1448,7 @@ var TiledPatternObject = class _TiledPatternObject extends FabricObject {
|
|
|
1377
1448
|
return Promise.resolve(copy);
|
|
1378
1449
|
}
|
|
1379
1450
|
/**
|
|
1380
|
-
* The source's tile footprint:
|
|
1451
|
+
* The source's tile footprint: the art it paints, minus a stroke it reserves
|
|
1381
1452
|
* room for but never paints.
|
|
1382
1453
|
*
|
|
1383
1454
|
* fabric keeps `strokeWidth` inside an object's box whether or not a `stroke`
|
|
@@ -1385,11 +1456,18 @@ var TiledPatternObject = class _TiledPatternObject extends FabricObject {
|
|
|
1385
1456
|
* stroke. Stepping the grid by that box puts a transparent seam between every
|
|
1386
1457
|
* pair of neighbours at zero spacing: the artwork is a pixel narrower than the
|
|
1387
1458
|
* box it is stepped by.
|
|
1459
|
+
*
|
|
1460
|
+
* Text is the same problem an order of magnitude larger — see {@link textInk}.
|
|
1388
1461
|
*/
|
|
1389
1462
|
sourceBox() {
|
|
1463
|
+
const text = asText(this.source);
|
|
1464
|
+
const paints = paintsStroke(this.source);
|
|
1390
1465
|
const rect = this.source.getBoundingRect();
|
|
1391
|
-
if (
|
|
1392
|
-
const
|
|
1466
|
+
if (paints && !text) return { width: rect.width, height: rect.height };
|
|
1467
|
+
const options = {};
|
|
1468
|
+
if (!paints) options.strokeWidth = 0;
|
|
1469
|
+
if (text) options.width = textInk(text).width;
|
|
1470
|
+
const dims = this.source._getTransformedDimensions(options);
|
|
1393
1471
|
const radians = (this.source.angle ?? 0) * Math.PI / 180;
|
|
1394
1472
|
const cos = Math.abs(Math.cos(radians));
|
|
1395
1473
|
const sin = Math.abs(Math.sin(radians));
|
|
@@ -1398,6 +1476,22 @@ var TiledPatternObject = class _TiledPatternObject extends FabricObject {
|
|
|
1398
1476
|
height: dims.x * sin + dims.y * cos
|
|
1399
1477
|
};
|
|
1400
1478
|
}
|
|
1479
|
+
/**
|
|
1480
|
+
* Vector from the source's centre to the centre of the art it paints.
|
|
1481
|
+
*
|
|
1482
|
+
* Zero for everything except text that does not fill its box: the tile has to
|
|
1483
|
+
* sit on the glyphs, or the frame the user drags frames empty space and the
|
|
1484
|
+
* grid registers off the run by half the padding.
|
|
1485
|
+
*/
|
|
1486
|
+
inkOffset() {
|
|
1487
|
+
const text = asText(this.source);
|
|
1488
|
+
if (!text) return new Point2(0, 0);
|
|
1489
|
+
const { dx } = textInk(text);
|
|
1490
|
+
if (!dx) return new Point2(0, 0);
|
|
1491
|
+
const scaled = dx * (this.source.scaleX ?? 1);
|
|
1492
|
+
const radians = (this.source.angle ?? 0) * Math.PI / 180;
|
|
1493
|
+
return new Point2(scaled * Math.cos(radians), scaled * Math.sin(radians));
|
|
1494
|
+
}
|
|
1401
1495
|
/** The source's on-canvas width, before the tile scale. */
|
|
1402
1496
|
baseW() {
|
|
1403
1497
|
return Math.max(1, this.sourceBox().width);
|
|
@@ -1457,11 +1551,11 @@ var TiledPatternObject = class _TiledPatternObject extends FabricObject {
|
|
|
1457
1551
|
const radians = angle * Math.PI / 180;
|
|
1458
1552
|
const cos = Math.cos(radians);
|
|
1459
1553
|
const sin = Math.sin(radians);
|
|
1460
|
-
return new
|
|
1554
|
+
return new Point2(dx * cos - dy * sin, dx * sin + dy * cos);
|
|
1461
1555
|
}
|
|
1462
1556
|
anchorFromOrigin(origin, tileW, tileH, angle) {
|
|
1463
1557
|
const shift = this.shiftVector(tileW, tileH, angle);
|
|
1464
|
-
return new
|
|
1558
|
+
return new Point2(origin.x + shift.x, origin.y + shift.y);
|
|
1465
1559
|
}
|
|
1466
1560
|
/**
|
|
1467
1561
|
* Snapshot the source at (at least) `scale`, reusing the cached one while it
|
|
@@ -1474,7 +1568,15 @@ var TiledPatternObject = class _TiledPatternObject extends FabricObject {
|
|
|
1474
1568
|
const source = this.source;
|
|
1475
1569
|
const opacity = source.opacity;
|
|
1476
1570
|
source.opacity = 1;
|
|
1571
|
+
const text = asText(source);
|
|
1572
|
+
const authoredWidth = text?.width;
|
|
1573
|
+
const fitted = text ? textInk(text).width + TEXT_FIT_SLACK : 0;
|
|
1574
|
+
const hug = text !== null && authoredWidth !== void 0 && fitted < authoredWidth;
|
|
1477
1575
|
try {
|
|
1576
|
+
if (hug && text) {
|
|
1577
|
+
text.set({ width: fitted });
|
|
1578
|
+
text.initDimensions?.();
|
|
1579
|
+
}
|
|
1478
1580
|
const el = source.toCanvasElement({ multiplier: wanted, enableRetinaScaling: false });
|
|
1479
1581
|
if (!el.width || !el.height) return null;
|
|
1480
1582
|
this.snapshotEl = el;
|
|
@@ -1483,6 +1585,10 @@ var TiledPatternObject = class _TiledPatternObject extends FabricObject {
|
|
|
1483
1585
|
return this.snapshotEl;
|
|
1484
1586
|
} finally {
|
|
1485
1587
|
source.opacity = opacity;
|
|
1588
|
+
if (hug && text && authoredWidth !== void 0) {
|
|
1589
|
+
text.set({ width: authoredWidth });
|
|
1590
|
+
text.initDimensions?.();
|
|
1591
|
+
}
|
|
1486
1592
|
source.dirty = false;
|
|
1487
1593
|
}
|
|
1488
1594
|
return this.snapshotEl;
|
|
@@ -1859,17 +1965,55 @@ function buildCurvePathData(config, width, fontSize) {
|
|
|
1859
1965
|
}
|
|
1860
1966
|
|
|
1861
1967
|
// src/text-curve.ts
|
|
1862
|
-
var
|
|
1968
|
+
var MEASURE_WIDTH2 = 1e5;
|
|
1863
1969
|
var PATH_SLACK = 0.06;
|
|
1864
1970
|
var FALLBACK_LINE_HEIGHT = 1.16;
|
|
1865
1971
|
var patches = /* @__PURE__ */ new WeakMap();
|
|
1972
|
+
var dimensionPatches = /* @__PURE__ */ new WeakMap();
|
|
1973
|
+
var authoredCaching = /* @__PURE__ */ new WeakMap();
|
|
1974
|
+
var GLYPH_HALF_HEIGHT = 0.7;
|
|
1975
|
+
function curvedInkSize(text) {
|
|
1976
|
+
const lineCount = text._textLines?.length ?? 0;
|
|
1977
|
+
for (let index = 0; index < lineCount; index += 1) text.getLineWidth?.(index);
|
|
1978
|
+
const lines = text.__charBounds;
|
|
1979
|
+
if (!lines?.length) return null;
|
|
1980
|
+
let halfWidth = 0;
|
|
1981
|
+
let halfHeight = 0;
|
|
1982
|
+
for (const line of lines) {
|
|
1983
|
+
for (const box of line ?? []) {
|
|
1984
|
+
const { renderLeft, renderTop } = box;
|
|
1985
|
+
if (typeof renderLeft !== "number" || typeof renderTop !== "number") continue;
|
|
1986
|
+
const halfGlyphWidth = (box.kernedWidth ?? box.width ?? 0) / 2;
|
|
1987
|
+
const halfGlyphHeight = (box.height ?? text.fontSize) * GLYPH_HALF_HEIGHT;
|
|
1988
|
+
const angle = box.angle ?? 0;
|
|
1989
|
+
const cos = Math.abs(Math.cos(angle));
|
|
1990
|
+
const sin = Math.abs(Math.sin(angle));
|
|
1991
|
+
halfWidth = Math.max(
|
|
1992
|
+
halfWidth,
|
|
1993
|
+
Math.abs(renderLeft) + halfGlyphWidth * cos + halfGlyphHeight * sin
|
|
1994
|
+
);
|
|
1995
|
+
halfHeight = Math.max(
|
|
1996
|
+
halfHeight,
|
|
1997
|
+
Math.abs(renderTop) + halfGlyphWidth * sin + halfGlyphHeight * cos
|
|
1998
|
+
);
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
if (halfWidth === 0 && halfHeight === 0) return null;
|
|
2002
|
+
return { width: halfWidth * 2, height: halfHeight * 2 };
|
|
2003
|
+
}
|
|
2004
|
+
function growBoxToInk(text) {
|
|
2005
|
+
const ink = curvedInkSize(text);
|
|
2006
|
+
if (!ink) return;
|
|
2007
|
+
if (ink.width > (text.width ?? 0)) text.width = ink.width;
|
|
2008
|
+
if (ink.height > (text.height ?? 0)) text.height = ink.height;
|
|
2009
|
+
}
|
|
1866
2010
|
function isCurvable(object) {
|
|
1867
2011
|
return !!object && typeof object.text === "string";
|
|
1868
2012
|
}
|
|
1869
2013
|
function measureText(text) {
|
|
1870
2014
|
const authored = text.width;
|
|
1871
2015
|
try {
|
|
1872
|
-
text.set({ width:
|
|
2016
|
+
text.set({ width: MEASURE_WIDTH2 });
|
|
1873
2017
|
text.initDimensions?.();
|
|
1874
2018
|
return Math.max(1, text.calcTextWidth?.() ?? text.width ?? 1);
|
|
1875
2019
|
} finally {
|
|
@@ -1894,11 +2038,29 @@ var TextCurveManager = class {
|
|
|
1894
2038
|
this.layers = layers;
|
|
1895
2039
|
this.history = history;
|
|
1896
2040
|
this.events = events;
|
|
2041
|
+
this.canvas.on("text:changed", this.onTextChanged);
|
|
1897
2042
|
}
|
|
1898
2043
|
canvas;
|
|
1899
2044
|
layers;
|
|
1900
2045
|
history;
|
|
1901
2046
|
events;
|
|
2047
|
+
/**
|
|
2048
|
+
* Typing on the canvas changes the run the paths were built for. Fabric drops
|
|
2049
|
+
* a glyph whose centre falls past the end of the path, so without rebuilding
|
|
2050
|
+
* here a longer replacement simply vanishes — select-all-and-retype rendered
|
|
2051
|
+
* an empty box — and a shorter one piles up on a path built for the old text.
|
|
2052
|
+
*
|
|
2053
|
+
* No history entry: fabric records the edit when editing exits, and one save
|
|
2054
|
+
* per keystroke would bury every earlier step under the typing.
|
|
2055
|
+
*/
|
|
2056
|
+
onTextChanged = (event) => {
|
|
2057
|
+
const layer = event.target ? this.layers.findByObject(event.target) : void 0;
|
|
2058
|
+
if (!layer?.meta.curve) return;
|
|
2059
|
+
this.refresh(layer.id, false);
|
|
2060
|
+
};
|
|
2061
|
+
dispose() {
|
|
2062
|
+
this.canvas.off("text:changed", this.onTextChanged);
|
|
2063
|
+
}
|
|
1902
2064
|
/** Curve parameters for a layer, or null when it is not curved text. */
|
|
1903
2065
|
get(layerId) {
|
|
1904
2066
|
const layer = this.layers.get(layerId);
|
|
@@ -1983,6 +2145,8 @@ var TextCurveManager = class {
|
|
|
1983
2145
|
* outside the layer's box — where object caching clips it away.
|
|
1984
2146
|
*/
|
|
1985
2147
|
attach(text, curves) {
|
|
2148
|
+
if (!authoredCaching.has(text)) authoredCaching.set(text, text.objectCaching);
|
|
2149
|
+
text.set({ objectCaching: false });
|
|
1986
2150
|
const paths = curves.map(toFabricPath);
|
|
1987
2151
|
const union = paths.length === 1 ? paths[0] : toFabricPath({ data: curves.map((c) => c.data).join(" "), length: 0 });
|
|
1988
2152
|
text.set({
|
|
@@ -1994,6 +2158,35 @@ var TextCurveManager = class {
|
|
|
1994
2158
|
pathStartOffset: 0
|
|
1995
2159
|
});
|
|
1996
2160
|
this.patchLineMeasure(text, paths, union);
|
|
2161
|
+
this.patchDimensions(text);
|
|
2162
|
+
}
|
|
2163
|
+
/**
|
|
2164
|
+
* Re-apply {@link growBoxToInk} after every one of fabric's own dimension
|
|
2165
|
+
* passes. Typing, a font change, a style edit and a state restore all call
|
|
2166
|
+
* `initDimensions`, and each one puts the box back to the path's bounding box
|
|
2167
|
+
* — so growing it once, here, would hold only until the next keystroke.
|
|
2168
|
+
*/
|
|
2169
|
+
patchDimensions(text) {
|
|
2170
|
+
if (dimensionPatches.has(text)) return;
|
|
2171
|
+
const original = text.initDimensions;
|
|
2172
|
+
if (typeof original !== "function") return;
|
|
2173
|
+
const installed = function() {
|
|
2174
|
+
original.call(this);
|
|
2175
|
+
growBoxToInk(this);
|
|
2176
|
+
};
|
|
2177
|
+
const ownedOriginal = Object.prototype.hasOwnProperty.call(text, "initDimensions");
|
|
2178
|
+
text.initDimensions = installed;
|
|
2179
|
+
dimensionPatches.set(text, { original, installed, ownedOriginal });
|
|
2180
|
+
}
|
|
2181
|
+
/** Restore fabric's own dimension pass, if this manager replaced it. */
|
|
2182
|
+
unpatchDimensions(text) {
|
|
2183
|
+
const patch = dimensionPatches.get(text);
|
|
2184
|
+
if (!patch) return;
|
|
2185
|
+
if (text.initDimensions === patch.installed) {
|
|
2186
|
+
if (patch.ownedOriginal) text.initDimensions = patch.original;
|
|
2187
|
+
else delete text.initDimensions;
|
|
2188
|
+
}
|
|
2189
|
+
dimensionPatches.delete(text);
|
|
1997
2190
|
}
|
|
1998
2191
|
/**
|
|
1999
2192
|
* Fabric lays every line of a text object along `this.path`, from one
|
|
@@ -2045,6 +2238,12 @@ var TextCurveManager = class {
|
|
|
2045
2238
|
}
|
|
2046
2239
|
detach(text, authoredWidth) {
|
|
2047
2240
|
this.unpatchLineMeasure(text);
|
|
2241
|
+
this.unpatchDimensions(text);
|
|
2242
|
+
const caching = authoredCaching.get(text);
|
|
2243
|
+
if (caching !== void 0) {
|
|
2244
|
+
text.set({ objectCaching: caching });
|
|
2245
|
+
authoredCaching.delete(text);
|
|
2246
|
+
}
|
|
2048
2247
|
text.set({ path: null, pathStartOffset: 0 });
|
|
2049
2248
|
if (authoredWidth !== void 0 && authoredWidth > 0) text.set({ width: authoredWidth });
|
|
2050
2249
|
}
|
|
@@ -3581,8 +3780,8 @@ var LayerMaskManager = class extends MaskStackStore {
|
|
|
3581
3780
|
onLayersChanged = () => this.pin();
|
|
3582
3781
|
// Selecting a layer means the user has moved on from the mask they had open;
|
|
3583
3782
|
// leaving both selected would show mask controls for an unrelated layer.
|
|
3584
|
-
onLayerSelected = () => {
|
|
3585
|
-
if (this.selected) this.select(null, null);
|
|
3783
|
+
onLayerSelected = ({ layerId }) => {
|
|
3784
|
+
if (layerId && this.selected) this.select(null, null);
|
|
3586
3785
|
};
|
|
3587
3786
|
onObjectModified = (event) => {
|
|
3588
3787
|
const object = event.target;
|
|
@@ -3642,6 +3841,7 @@ var LayerMaskManager = class extends MaskStackStore {
|
|
|
3642
3841
|
}
|
|
3643
3842
|
select(target, maskId) {
|
|
3644
3843
|
this.selected = target && maskId ? { target, maskId } : null;
|
|
3844
|
+
if (this.selected) this.layers.select(null);
|
|
3645
3845
|
this.events.emit("mask:selected", { target, maskId: this.selected ? maskId : null });
|
|
3646
3846
|
}
|
|
3647
3847
|
// ─── Mutations ───────────────────────────────────────
|
|
@@ -4194,8 +4394,7 @@ var CanvasEditor = class {
|
|
|
4194
4394
|
}
|
|
4195
4395
|
const group = new Group7(objects);
|
|
4196
4396
|
const grouped = this.layers.add("group", group, name);
|
|
4197
|
-
|
|
4198
|
-
grouped.children = children;
|
|
4397
|
+
this.layers.adoptChildren(grouped, children);
|
|
4199
4398
|
this.layers.reorder(grouped.id, insertIndex);
|
|
4200
4399
|
this.layers.select(grouped.id);
|
|
4201
4400
|
this.history.save();
|
|
@@ -4804,6 +5003,7 @@ var CanvasEditor = class {
|
|
|
4804
5003
|
this.crop.dispose();
|
|
4805
5004
|
this.history.dispose();
|
|
4806
5005
|
this.patterns.dispose();
|
|
5006
|
+
this.curves.dispose();
|
|
4807
5007
|
this.events.removeAllListeners();
|
|
4808
5008
|
this.canvas.dispose();
|
|
4809
5009
|
}
|
|
@@ -4863,11 +5063,15 @@ var DEFAULT_PATTERN_CONFIG = {
|
|
|
4863
5063
|
horizontalSpacing: 0,
|
|
4864
5064
|
verticalSpacing: 0,
|
|
4865
5065
|
angle: 0,
|
|
4866
|
-
|
|
5066
|
+
// Half a tile: the offset only bites in the brick modes, and a brick course
|
|
5067
|
+
// that is not half-shifted is just a grid with extra steps.
|
|
5068
|
+
horizontalOffset: 50,
|
|
4867
5069
|
offsetX: 0,
|
|
4868
5070
|
offsetY: 0,
|
|
4869
5071
|
rotationStepH: 0,
|
|
4870
|
-
rotationStepV: 0
|
|
5072
|
+
rotationStepV: 0,
|
|
5073
|
+
horizontalLimit: 0,
|
|
5074
|
+
verticalLimit: 0
|
|
4871
5075
|
};
|
|
4872
5076
|
|
|
4873
5077
|
// src/presets.ts
|
|
@@ -4972,6 +5176,7 @@ export {
|
|
|
4972
5176
|
exportPNG,
|
|
4973
5177
|
exportPrintArea,
|
|
4974
5178
|
exportSVG,
|
|
5179
|
+
fitTextWidth,
|
|
4975
5180
|
fitToBox,
|
|
4976
5181
|
generateId,
|
|
4977
5182
|
isCssColor,
|