scanic 1.1.1 → 1.2.0
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/README.md +4 -4
- package/dist/scanic.js +432 -242
- package/dist/scanic.umd.cjs +1 -1
- package/package.json +7 -2
- package/src/scanic.d.ts +68 -7
package/dist/scanic.js
CHANGED
|
@@ -1174,15 +1174,6 @@ function createDefaultCorners(imageWidth, imageHeight, insetRatio = 0.08) {
|
|
|
1174
1174
|
bottomLeft: { x: inset, y: imageHeight - inset }
|
|
1175
1175
|
};
|
|
1176
1176
|
}
|
|
1177
|
-
function drawHandle(ctx, point, radius, color, stroke, lineWidth) {
|
|
1178
|
-
ctx.beginPath();
|
|
1179
|
-
ctx.arc(point.x, point.y, radius, 0, Math.PI * 2);
|
|
1180
|
-
ctx.fillStyle = color;
|
|
1181
|
-
ctx.fill();
|
|
1182
|
-
ctx.lineWidth = lineWidth;
|
|
1183
|
-
ctx.strokeStyle = stroke;
|
|
1184
|
-
ctx.stroke();
|
|
1185
|
-
}
|
|
1186
1177
|
function normalizeImageToCanvas(image) {
|
|
1187
1178
|
if (!image) {
|
|
1188
1179
|
throw new Error("No image provided");
|
|
@@ -1214,30 +1205,205 @@ function normalizeImageToCanvas(image) {
|
|
|
1214
1205
|
}
|
|
1215
1206
|
return { canvas, width, height };
|
|
1216
1207
|
}
|
|
1208
|
+
const STYLE_ID = "scanic-corner-editor-styles";
|
|
1209
|
+
const EDITOR_CSS = `
|
|
1210
|
+
.scanic-corner-editor {
|
|
1211
|
+
--scanic-accent: #6366f1;
|
|
1212
|
+
--scanic-mask: rgba(15, 23, 42, 0.45);
|
|
1213
|
+
--scanic-edge-color: var(--scanic-accent);
|
|
1214
|
+
--scanic-edge-width: 2.5;
|
|
1215
|
+
--scanic-handle-size: 20px;
|
|
1216
|
+
--scanic-handle-hit: 44px;
|
|
1217
|
+
--scanic-handle-color: #ffffff;
|
|
1218
|
+
--scanic-handle-ring: 2px;
|
|
1219
|
+
--scanic-handle-ring-color: var(--scanic-accent);
|
|
1220
|
+
--scanic-handle-shadow: 0 1px 4px rgba(15, 23, 42, 0.45);
|
|
1221
|
+
--scanic-handle-active-shadow: 0 8px 22px rgba(15, 23, 42, 0.5);
|
|
1222
|
+
--scanic-handle-active-scale: 1.28;
|
|
1223
|
+
--scanic-surface: rgba(15, 23, 42, 0.92);
|
|
1224
|
+
--scanic-surface-fg: #e2e8f0;
|
|
1225
|
+
--scanic-surface-radius: 12px;
|
|
1226
|
+
}
|
|
1227
|
+
.scanic-handle {
|
|
1228
|
+
position: absolute;
|
|
1229
|
+
box-sizing: border-box;
|
|
1230
|
+
width: var(--scanic-handle-size);
|
|
1231
|
+
height: var(--scanic-handle-size);
|
|
1232
|
+
margin: 0;
|
|
1233
|
+
padding: 0;
|
|
1234
|
+
border: var(--scanic-handle-ring) solid var(--scanic-handle-ring-color);
|
|
1235
|
+
border-radius: 50%;
|
|
1236
|
+
background: var(--scanic-handle-color);
|
|
1237
|
+
box-shadow: var(--scanic-handle-shadow);
|
|
1238
|
+
transform: translate(-50%, -50%);
|
|
1239
|
+
cursor: grab;
|
|
1240
|
+
touch-action: none;
|
|
1241
|
+
z-index: 2;
|
|
1242
|
+
transition: transform 0.12s ease, box-shadow 0.12s ease,
|
|
1243
|
+
background 0.12s ease, border-color 0.12s ease;
|
|
1244
|
+
}
|
|
1245
|
+
/* Enlarges the pointer/touch target without changing the visual size. */
|
|
1246
|
+
.scanic-handle::after {
|
|
1247
|
+
content: '';
|
|
1248
|
+
position: absolute;
|
|
1249
|
+
left: 50%;
|
|
1250
|
+
top: 50%;
|
|
1251
|
+
width: var(--scanic-handle-hit);
|
|
1252
|
+
height: var(--scanic-handle-hit);
|
|
1253
|
+
transform: translate(-50%, -50%);
|
|
1254
|
+
border-radius: 50%;
|
|
1255
|
+
}
|
|
1256
|
+
/* Soft accent halo that blooms when a handle is grabbed — the "depth" layer. */
|
|
1257
|
+
.scanic-handle::before {
|
|
1258
|
+
content: '';
|
|
1259
|
+
position: absolute;
|
|
1260
|
+
left: 50%;
|
|
1261
|
+
top: 50%;
|
|
1262
|
+
width: 0;
|
|
1263
|
+
height: 0;
|
|
1264
|
+
border-radius: 50%;
|
|
1265
|
+
background: var(--scanic-accent);
|
|
1266
|
+
opacity: 0;
|
|
1267
|
+
transform: translate(-50%, -50%);
|
|
1268
|
+
transition: opacity 0.15s ease, width 0.15s ease, height 0.15s ease;
|
|
1269
|
+
pointer-events: none;
|
|
1270
|
+
}
|
|
1271
|
+
.scanic-handle:hover {
|
|
1272
|
+
transform: translate(-50%, -50%) scale(1.12);
|
|
1273
|
+
}
|
|
1274
|
+
.scanic-handle:focus-visible {
|
|
1275
|
+
outline: none;
|
|
1276
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--scanic-accent) 45%, transparent),
|
|
1277
|
+
var(--scanic-handle-shadow);
|
|
1278
|
+
}
|
|
1279
|
+
/* The currently selected corner — the target of nudges / keyboard. */
|
|
1280
|
+
.scanic-handle.is-selected {
|
|
1281
|
+
border-color: var(--scanic-accent);
|
|
1282
|
+
transform: translate(-50%, -50%) scale(1.12);
|
|
1283
|
+
}
|
|
1284
|
+
.scanic-handle.is-selected::before {
|
|
1285
|
+
opacity: 0.16;
|
|
1286
|
+
width: calc(var(--scanic-handle-size) * 2);
|
|
1287
|
+
height: calc(var(--scanic-handle-size) * 2);
|
|
1288
|
+
}
|
|
1289
|
+
.scanic-handle.is-active {
|
|
1290
|
+
cursor: grabbing;
|
|
1291
|
+
background: var(--scanic-accent);
|
|
1292
|
+
border-color: #ffffff;
|
|
1293
|
+
transform: translate(-50%, -50%) scale(var(--scanic-handle-active-scale));
|
|
1294
|
+
box-shadow: var(--scanic-handle-active-shadow);
|
|
1295
|
+
}
|
|
1296
|
+
.scanic-handle.is-active::before {
|
|
1297
|
+
opacity: 0.22;
|
|
1298
|
+
width: calc(var(--scanic-handle-size) * 2.8);
|
|
1299
|
+
height: calc(var(--scanic-handle-size) * 2.8);
|
|
1300
|
+
}
|
|
1301
|
+
.scanic-toolbar,
|
|
1302
|
+
.scanic-nudges {
|
|
1303
|
+
position: absolute;
|
|
1304
|
+
z-index: 3;
|
|
1305
|
+
display: flex;
|
|
1306
|
+
gap: 4px;
|
|
1307
|
+
padding: 5px;
|
|
1308
|
+
background: var(--scanic-surface);
|
|
1309
|
+
border-radius: var(--scanic-surface-radius);
|
|
1310
|
+
-webkit-backdrop-filter: blur(8px);
|
|
1311
|
+
backdrop-filter: blur(8px);
|
|
1312
|
+
box-shadow: 0 6px 20px rgba(15, 23, 42, 0.35);
|
|
1313
|
+
}
|
|
1314
|
+
.scanic-toolbar {
|
|
1315
|
+
left: 50%;
|
|
1316
|
+
bottom: 12px;
|
|
1317
|
+
transform: translateX(-50%);
|
|
1318
|
+
}
|
|
1319
|
+
.scanic-nudges {
|
|
1320
|
+
top: 10px;
|
|
1321
|
+
right: 10px;
|
|
1322
|
+
display: grid;
|
|
1323
|
+
grid-template-columns: repeat(4, auto);
|
|
1324
|
+
gap: 3px;
|
|
1325
|
+
}
|
|
1326
|
+
.scanic-toolbar button,
|
|
1327
|
+
.scanic-nudges button {
|
|
1328
|
+
display: grid;
|
|
1329
|
+
place-items: center;
|
|
1330
|
+
margin: 0;
|
|
1331
|
+
padding: 0;
|
|
1332
|
+
font-family: inherit;
|
|
1333
|
+
font-size: 15px;
|
|
1334
|
+
font-weight: 600;
|
|
1335
|
+
line-height: 1;
|
|
1336
|
+
color: var(--scanic-surface-fg);
|
|
1337
|
+
background: transparent;
|
|
1338
|
+
border: 0;
|
|
1339
|
+
border-radius: 8px;
|
|
1340
|
+
cursor: pointer;
|
|
1341
|
+
transition: background 0.12s ease, color 0.12s ease, filter 0.12s ease;
|
|
1342
|
+
}
|
|
1343
|
+
.scanic-toolbar button {
|
|
1344
|
+
width: 34px;
|
|
1345
|
+
height: 34px;
|
|
1346
|
+
}
|
|
1347
|
+
.scanic-nudges button {
|
|
1348
|
+
width: 30px;
|
|
1349
|
+
height: 30px;
|
|
1350
|
+
}
|
|
1351
|
+
.scanic-toolbar button svg {
|
|
1352
|
+
width: 18px;
|
|
1353
|
+
height: 18px;
|
|
1354
|
+
display: block;
|
|
1355
|
+
}
|
|
1356
|
+
.scanic-toolbar button:hover,
|
|
1357
|
+
.scanic-nudges button:hover {
|
|
1358
|
+
background: rgba(255, 255, 255, 0.14);
|
|
1359
|
+
}
|
|
1360
|
+
.scanic-toolbar button:focus-visible,
|
|
1361
|
+
.scanic-nudges button:focus-visible {
|
|
1362
|
+
outline: 2px solid var(--scanic-accent);
|
|
1363
|
+
outline-offset: 1px;
|
|
1364
|
+
}
|
|
1365
|
+
.scanic-toolbar .scanic-btn-apply {
|
|
1366
|
+
background: var(--scanic-accent);
|
|
1367
|
+
color: #ffffff;
|
|
1368
|
+
}
|
|
1369
|
+
.scanic-toolbar .scanic-btn-apply:hover {
|
|
1370
|
+
background: var(--scanic-accent);
|
|
1371
|
+
filter: brightness(1.1);
|
|
1372
|
+
}
|
|
1373
|
+
.scanic-toolbar .scanic-btn-expert.is-on {
|
|
1374
|
+
background: color-mix(in srgb, var(--scanic-accent) 32%, transparent);
|
|
1375
|
+
color: #ffffff;
|
|
1376
|
+
}
|
|
1377
|
+
`;
|
|
1378
|
+
const ICONS = {
|
|
1379
|
+
reset: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 4 3 10 9 10"/><path d="M3.5 14a8.5 8.5 0 1 0 2-7.4L3 10"/></svg>',
|
|
1380
|
+
cancel: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>',
|
|
1381
|
+
apply: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>',
|
|
1382
|
+
expert: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></svg>'
|
|
1383
|
+
};
|
|
1384
|
+
function injectStyles(doc) {
|
|
1385
|
+
if (!doc || typeof doc.getElementById !== "function") return;
|
|
1386
|
+
if (doc.getElementById(STYLE_ID)) return;
|
|
1387
|
+
const style = doc.createElement("style");
|
|
1388
|
+
style.id = STYLE_ID;
|
|
1389
|
+
style.textContent = EDITOR_CSS;
|
|
1390
|
+
(doc.head || doc.documentElement).appendChild(style);
|
|
1391
|
+
}
|
|
1217
1392
|
function createCornerEditor(options = {}) {
|
|
1218
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
1393
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
|
|
1219
1394
|
const container = options.container;
|
|
1220
1395
|
if (!container || typeof container.appendChild !== "function") {
|
|
1221
1396
|
throw new Error("createCornerEditor requires a valid container element");
|
|
1222
1397
|
}
|
|
1223
|
-
const
|
|
1224
|
-
const
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
editorCanvas.style.left = "0";
|
|
1228
|
-
editorCanvas.style.display = "block";
|
|
1229
|
-
editorCanvas.style.boxSizing = "border-box";
|
|
1230
|
-
editorCanvas.style.touchAction = "none";
|
|
1231
|
-
editorCanvas.style.userSelect = "none";
|
|
1232
|
-
editorCanvas.style.webkitUserSelect = "none";
|
|
1233
|
-
editorCanvas.style.cursor = "crosshair";
|
|
1234
|
-
editorCanvas.style.outline = "none";
|
|
1235
|
-
const keyboardEnabled = options.keyboard !== false;
|
|
1236
|
-
if (keyboardEnabled) {
|
|
1237
|
-
editorCanvas.tabIndex = 0;
|
|
1238
|
-
editorCanvas.setAttribute("role", "application");
|
|
1239
|
-
editorCanvas.setAttribute("aria-label", "Document corner editor. Use arrow keys to adjust the selected corner.");
|
|
1398
|
+
const doc = container.ownerDocument || (typeof document !== "undefined" ? document : null);
|
|
1399
|
+
const runtimeGlobal = typeof window !== "undefined" ? window : globalThis;
|
|
1400
|
+
if (options.injectStyles !== false) {
|
|
1401
|
+
injectStyles(doc);
|
|
1240
1402
|
}
|
|
1403
|
+
const { canvas: sourceCanvas, width: imageWidth, height: imageHeight } = normalizeImageToCanvas(options.image);
|
|
1404
|
+
const addedRootClass = !container.classList.contains("scanic-corner-editor");
|
|
1405
|
+
container.classList.add("scanic-corner-editor");
|
|
1406
|
+
if ((_a = options.classNames) == null ? void 0 : _a.root) container.classList.add(options.classNames.root);
|
|
1241
1407
|
const restoreContainerStyle = {
|
|
1242
1408
|
position: container.style.position,
|
|
1243
1409
|
minHeight: container.style.minHeight
|
|
@@ -1248,40 +1414,123 @@ function createCornerEditor(options = {}) {
|
|
|
1248
1414
|
container.style.position = "relative";
|
|
1249
1415
|
changedContainerPosition = true;
|
|
1250
1416
|
}
|
|
1417
|
+
applyThemeOption(options.theme);
|
|
1418
|
+
const editorCanvas = doc.createElement("canvas");
|
|
1419
|
+
editorCanvas.style.position = "absolute";
|
|
1420
|
+
editorCanvas.style.top = "0";
|
|
1421
|
+
editorCanvas.style.left = "0";
|
|
1422
|
+
editorCanvas.style.display = "block";
|
|
1423
|
+
editorCanvas.style.touchAction = "none";
|
|
1424
|
+
editorCanvas.style.userSelect = "none";
|
|
1425
|
+
editorCanvas.style.webkitUserSelect = "none";
|
|
1426
|
+
editorCanvas.style.cursor = "default";
|
|
1251
1427
|
container.appendChild(editorCanvas);
|
|
1252
1428
|
const ctx = editorCanvas.getContext("2d");
|
|
1253
1429
|
if (!ctx) {
|
|
1254
1430
|
throw new Error("Failed to create 2D canvas context for corner editor");
|
|
1255
1431
|
}
|
|
1256
1432
|
const magnifier = {
|
|
1257
|
-
enabled: ((
|
|
1258
|
-
size: ((
|
|
1259
|
-
zoom: ((
|
|
1260
|
-
margin: ((
|
|
1261
|
-
borderColor: ((
|
|
1262
|
-
borderWidth: ((
|
|
1263
|
-
crosshairColor: ((
|
|
1264
|
-
crosshairSize: ((
|
|
1433
|
+
enabled: ((_b = options.magnifier) == null ? void 0 : _b.enabled) !== false,
|
|
1434
|
+
size: ((_c = options.magnifier) == null ? void 0 : _c.size) || 120,
|
|
1435
|
+
zoom: ((_d = options.magnifier) == null ? void 0 : _d.zoom) || 2,
|
|
1436
|
+
margin: ((_e = options.magnifier) == null ? void 0 : _e.margin) || 8,
|
|
1437
|
+
borderColor: ((_f = options.magnifier) == null ? void 0 : _f.borderColor) || "#ffffff",
|
|
1438
|
+
borderWidth: ((_g = options.magnifier) == null ? void 0 : _g.borderWidth) || 2,
|
|
1439
|
+
crosshairColor: ((_h = options.magnifier) == null ? void 0 : _h.crosshairColor) || "#ffffff",
|
|
1440
|
+
crosshairSize: ((_i = options.magnifier) == null ? void 0 : _i.crosshairSize) || 18
|
|
1265
1441
|
};
|
|
1266
1442
|
const nudges = {
|
|
1267
|
-
enabled: !!((
|
|
1268
|
-
steps: (((
|
|
1443
|
+
enabled: !!((_j = options.nudges) == null ? void 0 : _j.enabled),
|
|
1444
|
+
steps: (((_k = options.nudges) == null ? void 0 : _k.steps) && options.nudges.steps.length ? options.nudges.steps : [1, 10]).map((v) => Math.max(1, Math.round(v)))
|
|
1445
|
+
};
|
|
1446
|
+
const toolbar = {
|
|
1447
|
+
enabled: ((_l = options.toolbar) == null ? void 0 : _l.enabled) !== false,
|
|
1448
|
+
reset: ((_m = options.toolbar) == null ? void 0 : _m.reset) !== false,
|
|
1449
|
+
cancel: ((_n = options.toolbar) == null ? void 0 : _n.cancel) !== false,
|
|
1450
|
+
apply: ((_o = options.toolbar) == null ? void 0 : _o.apply) !== false,
|
|
1451
|
+
labels: {
|
|
1452
|
+
reset: ((_q = (_p = options.toolbar) == null ? void 0 : _p.labels) == null ? void 0 : _q.reset) || "Reset",
|
|
1453
|
+
cancel: ((_s = (_r = options.toolbar) == null ? void 0 : _r.labels) == null ? void 0 : _s.cancel) || "Cancel",
|
|
1454
|
+
apply: ((_u = (_t = options.toolbar) == null ? void 0 : _t.labels) == null ? void 0 : _u.apply) || "Apply"
|
|
1455
|
+
}
|
|
1269
1456
|
};
|
|
1457
|
+
const keyboardEnabled = options.keyboard !== false;
|
|
1270
1458
|
const defaultCorners = createDefaultCorners(imageWidth, imageHeight);
|
|
1271
1459
|
const requestedCorners = options.corners ? cloneCorners(options.corners) : defaultCorners;
|
|
1272
1460
|
let corners = cornersAreFiniteAndDistinct$1(requestedCorners) && isConvexQuadrilateral$1(requestedCorners) ? requestedCorners : defaultCorners;
|
|
1273
1461
|
const initialCorners = cloneCorners(corners);
|
|
1274
1462
|
let isDestroyed = false;
|
|
1275
1463
|
let activeCornerKey = null;
|
|
1464
|
+
let focusedCornerKey = "topLeft";
|
|
1276
1465
|
let dragPointerId = null;
|
|
1277
1466
|
let lastPointerPosition = null;
|
|
1278
|
-
|
|
1279
|
-
const handleRadius = Math.max(8, Math.min(16, handleHitArea * 0.3));
|
|
1467
|
+
Math.max(24, options.handleHitArea || 44);
|
|
1280
1468
|
const cornerOrder = ["topLeft", "topRight", "bottomRight", "bottomLeft"];
|
|
1469
|
+
const cornerLabels = {
|
|
1470
|
+
topLeft: "Top-left corner",
|
|
1471
|
+
topRight: "Top-right corner",
|
|
1472
|
+
bottomRight: "Bottom-right corner",
|
|
1473
|
+
bottomLeft: "Bottom-left corner"
|
|
1474
|
+
};
|
|
1281
1475
|
let view = { scale: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 };
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
const
|
|
1476
|
+
const resolved = { mask: "rgba(15,23,42,0.45)", edgeColor: "#6366f1", edgeWidth: 2.5 };
|
|
1477
|
+
const handleEls = {};
|
|
1478
|
+
for (const key of cornerOrder) {
|
|
1479
|
+
const el = doc.createElement("button");
|
|
1480
|
+
el.type = "button";
|
|
1481
|
+
el.className = "scanic-handle" + (((_v = options.classNames) == null ? void 0 : _v.handle) ? " " + options.classNames.handle : "");
|
|
1482
|
+
el.dataset.corner = key;
|
|
1483
|
+
el.setAttribute("aria-label", cornerLabels[key]);
|
|
1484
|
+
if (!keyboardEnabled) el.tabIndex = -1;
|
|
1485
|
+
el.addEventListener("pointerdown", (e) => onHandlePointerDown(key, e));
|
|
1486
|
+
el.addEventListener("pointermove", (e) => onHandlePointerMove(key, e));
|
|
1487
|
+
el.addEventListener("pointerup", (e) => onHandlePointerUp(key, e));
|
|
1488
|
+
el.addEventListener("pointercancel", (e) => onHandlePointerUp(key, e));
|
|
1489
|
+
el.addEventListener("focus", () => {
|
|
1490
|
+
focusedCornerKey = key;
|
|
1491
|
+
updateSelected();
|
|
1492
|
+
});
|
|
1493
|
+
if (keyboardEnabled) el.addEventListener("keydown", (e) => onHandleKeyDown(key, e));
|
|
1494
|
+
container.appendChild(el);
|
|
1495
|
+
handleEls[key] = el;
|
|
1496
|
+
}
|
|
1497
|
+
function resolveColor(expr, fallback) {
|
|
1498
|
+
const prev = editorCanvas.style.color;
|
|
1499
|
+
editorCanvas.style.color = `var(${expr}, ${fallback})`;
|
|
1500
|
+
const value = getComputedStyle(editorCanvas).color || fallback;
|
|
1501
|
+
editorCanvas.style.color = prev;
|
|
1502
|
+
return value;
|
|
1503
|
+
}
|
|
1504
|
+
function resolveTheme() {
|
|
1505
|
+
resolved.mask = resolveColor("--scanic-mask", "rgba(15, 23, 42, 0.45)");
|
|
1506
|
+
resolved.edgeColor = resolveColor("--scanic-edge-color", "#6366f1");
|
|
1507
|
+
const widthRaw = getComputedStyle(container).getPropertyValue("--scanic-edge-width");
|
|
1508
|
+
const parsed = parseFloat(widthRaw);
|
|
1509
|
+
resolved.edgeWidth = Number.isFinite(parsed) && parsed > 0 ? parsed : 2.5;
|
|
1510
|
+
}
|
|
1511
|
+
function applyThemeOption(theme) {
|
|
1512
|
+
if (!theme || typeof theme !== "object") return;
|
|
1513
|
+
const pxKeys = /* @__PURE__ */ new Set(["handleSize", "handleHit"]);
|
|
1514
|
+
const map = {
|
|
1515
|
+
accent: "--scanic-accent",
|
|
1516
|
+
mask: "--scanic-mask",
|
|
1517
|
+
edgeColor: "--scanic-edge-color",
|
|
1518
|
+
edgeWidth: "--scanic-edge-width",
|
|
1519
|
+
handleSize: "--scanic-handle-size",
|
|
1520
|
+
handleHit: "--scanic-handle-hit",
|
|
1521
|
+
handleColor: "--scanic-handle-color",
|
|
1522
|
+
handleRingColor: "--scanic-handle-ring-color",
|
|
1523
|
+
surface: "--scanic-surface",
|
|
1524
|
+
surfaceColor: "--scanic-surface-fg",
|
|
1525
|
+
radius: "--scanic-surface-radius"
|
|
1526
|
+
};
|
|
1527
|
+
for (const [key, cssVar] of Object.entries(map)) {
|
|
1528
|
+
const value = theme[key];
|
|
1529
|
+
if (value == null) continue;
|
|
1530
|
+
const cssValue = typeof value === "number" && pxKeys.has(key) ? `${value}px` : String(value);
|
|
1531
|
+
container.style.setProperty(cssVar, cssValue);
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1285
1534
|
function emitChange() {
|
|
1286
1535
|
if (typeof options.onChange === "function") {
|
|
1287
1536
|
options.onChange(cloneCorners(corners));
|
|
@@ -1326,6 +1575,7 @@ function createCornerEditor(options = {}) {
|
|
|
1326
1575
|
width,
|
|
1327
1576
|
height
|
|
1328
1577
|
};
|
|
1578
|
+
resolveTheme();
|
|
1329
1579
|
}
|
|
1330
1580
|
function imageToView(point) {
|
|
1331
1581
|
return {
|
|
@@ -1351,55 +1601,27 @@ function createCornerEditor(options = {}) {
|
|
|
1351
1601
|
y: event.clientY - rect.top
|
|
1352
1602
|
};
|
|
1353
1603
|
}
|
|
1354
|
-
function
|
|
1355
|
-
let hit = null;
|
|
1356
|
-
let bestDistance = Infinity;
|
|
1357
|
-
for (const key of cornerOrder) {
|
|
1358
|
-
const p = imageToView(corners[key]);
|
|
1359
|
-
const d = Math.hypot(canvasX - p.x, canvasY - p.y);
|
|
1360
|
-
if (d <= handleHitArea && d < bestDistance) {
|
|
1361
|
-
bestDistance = d;
|
|
1362
|
-
hit = key;
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
return hit;
|
|
1366
|
-
}
|
|
1367
|
-
function drawPolygonOverlay() {
|
|
1604
|
+
function drawOverlay() {
|
|
1368
1605
|
const points = cornerOrder.map((key) => imageToView(corners[key]));
|
|
1369
1606
|
ctx.save();
|
|
1370
|
-
ctx.fillStyle =
|
|
1607
|
+
ctx.fillStyle = resolved.mask;
|
|
1371
1608
|
ctx.beginPath();
|
|
1372
1609
|
ctx.rect(0, 0, view.width, view.height);
|
|
1373
1610
|
ctx.moveTo(points[0].x, points[0].y);
|
|
1374
|
-
for (let i = 1; i < points.length; i++)
|
|
1375
|
-
ctx.lineTo(points[i].x, points[i].y);
|
|
1376
|
-
}
|
|
1611
|
+
for (let i = 1; i < points.length; i++) ctx.lineTo(points[i].x, points[i].y);
|
|
1377
1612
|
ctx.closePath();
|
|
1378
1613
|
ctx.fill("evenodd");
|
|
1379
1614
|
ctx.restore();
|
|
1380
1615
|
ctx.save();
|
|
1381
|
-
ctx.strokeStyle =
|
|
1382
|
-
ctx.lineWidth =
|
|
1616
|
+
ctx.strokeStyle = resolved.edgeColor;
|
|
1617
|
+
ctx.lineWidth = resolved.edgeWidth;
|
|
1618
|
+
ctx.lineJoin = "round";
|
|
1383
1619
|
ctx.beginPath();
|
|
1384
1620
|
ctx.moveTo(points[0].x, points[0].y);
|
|
1385
|
-
for (let i = 1; i < points.length; i++)
|
|
1386
|
-
ctx.lineTo(points[i].x, points[i].y);
|
|
1387
|
-
}
|
|
1621
|
+
for (let i = 1; i < points.length; i++) ctx.lineTo(points[i].x, points[i].y);
|
|
1388
1622
|
ctx.closePath();
|
|
1389
1623
|
ctx.stroke();
|
|
1390
1624
|
ctx.restore();
|
|
1391
|
-
for (const key of cornerOrder) {
|
|
1392
|
-
const p = imageToView(corners[key]);
|
|
1393
|
-
const isActive = key === activeCornerKey;
|
|
1394
|
-
drawHandle(
|
|
1395
|
-
ctx,
|
|
1396
|
-
p,
|
|
1397
|
-
isActive ? handleRadius + 1 : handleRadius,
|
|
1398
|
-
isActive ? "#f59e0b" : "#ffffff",
|
|
1399
|
-
isActive ? "#7c2d12" : "#0f172a",
|
|
1400
|
-
2
|
|
1401
|
-
);
|
|
1402
|
-
}
|
|
1403
1625
|
}
|
|
1404
1626
|
function drawMagnifier() {
|
|
1405
1627
|
if (!magnifier.enabled || !activeCornerKey || !lastPointerPosition) {
|
|
@@ -1445,6 +1667,19 @@ function createCornerEditor(options = {}) {
|
|
|
1445
1667
|
ctx.stroke();
|
|
1446
1668
|
ctx.restore();
|
|
1447
1669
|
}
|
|
1670
|
+
function positionHandles() {
|
|
1671
|
+
for (const key of cornerOrder) {
|
|
1672
|
+
const p = imageToView(corners[key]);
|
|
1673
|
+
const el = handleEls[key];
|
|
1674
|
+
el.style.left = p.x + "px";
|
|
1675
|
+
el.style.top = p.y + "px";
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
function updateSelected() {
|
|
1679
|
+
for (const key of cornerOrder) {
|
|
1680
|
+
handleEls[key].classList.toggle("is-selected", key === focusedCornerKey);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1448
1683
|
function render() {
|
|
1449
1684
|
if (isDestroyed) return;
|
|
1450
1685
|
ctx.clearRect(0, 0, view.width, view.height);
|
|
@@ -1459,8 +1694,9 @@ function createCornerEditor(options = {}) {
|
|
|
1459
1694
|
imageWidth * view.scale,
|
|
1460
1695
|
imageHeight * view.scale
|
|
1461
1696
|
);
|
|
1462
|
-
|
|
1697
|
+
drawOverlay();
|
|
1463
1698
|
drawMagnifier();
|
|
1699
|
+
positionHandles();
|
|
1464
1700
|
}
|
|
1465
1701
|
const raf = typeof runtimeGlobal.requestAnimationFrame === "function" ? runtimeGlobal.requestAnimationFrame.bind(runtimeGlobal) : null;
|
|
1466
1702
|
const caf = typeof runtimeGlobal.cancelAnimationFrame === "function" ? runtimeGlobal.cancelAnimationFrame.bind(runtimeGlobal) : null;
|
|
@@ -1487,110 +1723,58 @@ function createCornerEditor(options = {}) {
|
|
|
1487
1723
|
return false;
|
|
1488
1724
|
}
|
|
1489
1725
|
corners = candidate;
|
|
1490
|
-
|
|
1726
|
+
focusedCornerKey = nextCornerKey;
|
|
1727
|
+
updateSelected();
|
|
1491
1728
|
emitChange();
|
|
1492
1729
|
scheduleRender();
|
|
1493
1730
|
return true;
|
|
1494
1731
|
}
|
|
1495
|
-
function
|
|
1496
|
-
if (!nudges.enabled) {
|
|
1497
|
-
return;
|
|
1498
|
-
}
|
|
1499
|
-
nudgeControls = document.createElement("div");
|
|
1500
|
-
nudgeControls.style.position = "absolute";
|
|
1501
|
-
nudgeControls.style.right = "8px";
|
|
1502
|
-
nudgeControls.style.bottom = "8px";
|
|
1503
|
-
nudgeControls.style.background = "rgba(15, 23, 42, 0.9)";
|
|
1504
|
-
nudgeControls.style.border = "1px solid rgba(148, 163, 184, 0.5)";
|
|
1505
|
-
nudgeControls.style.borderRadius = "10px";
|
|
1506
|
-
nudgeControls.style.padding = "8px";
|
|
1507
|
-
nudgeControls.style.display = "grid";
|
|
1508
|
-
nudgeControls.style.gridTemplateColumns = "repeat(4, auto)";
|
|
1509
|
-
nudgeControls.style.gap = "6px";
|
|
1510
|
-
nudgeControls.style.zIndex = "2";
|
|
1511
|
-
const makeButton = (glyph, ariaLabel, dx, dy, step) => {
|
|
1512
|
-
const btn = document.createElement("button");
|
|
1513
|
-
btn.type = "button";
|
|
1514
|
-
btn.textContent = glyph + (step > 1 ? " " + step : "");
|
|
1515
|
-
btn.setAttribute("aria-label", ariaLabel);
|
|
1516
|
-
btn.style.border = "1px solid #475569";
|
|
1517
|
-
btn.style.background = "#0f172a";
|
|
1518
|
-
btn.style.color = "#e2e8f0";
|
|
1519
|
-
btn.style.borderRadius = "6px";
|
|
1520
|
-
btn.style.padding = "4px 8px";
|
|
1521
|
-
btn.style.fontSize = "13px";
|
|
1522
|
-
btn.style.lineHeight = "1";
|
|
1523
|
-
btn.style.cursor = "pointer";
|
|
1524
|
-
btn.addEventListener("click", () => {
|
|
1525
|
-
const current = corners[activeNudgeCorner];
|
|
1526
|
-
setCorner(activeNudgeCorner, {
|
|
1527
|
-
x: current.x + dx * step,
|
|
1528
|
-
y: current.y + dy * step
|
|
1529
|
-
});
|
|
1530
|
-
});
|
|
1531
|
-
return btn;
|
|
1532
|
-
};
|
|
1533
|
-
for (const step of nudges.steps) {
|
|
1534
|
-
nudgeControls.appendChild(makeButton("←", `Move left ${step}px`, -1, 0, step));
|
|
1535
|
-
nudgeControls.appendChild(makeButton("→", `Move right ${step}px`, 1, 0, step));
|
|
1536
|
-
nudgeControls.appendChild(makeButton("↑", `Move up ${step}px`, 0, -1, step));
|
|
1537
|
-
nudgeControls.appendChild(makeButton("↓", `Move down ${step}px`, 0, 1, step));
|
|
1538
|
-
}
|
|
1539
|
-
container.appendChild(nudgeControls);
|
|
1540
|
-
}
|
|
1541
|
-
function handlePointerDown(event) {
|
|
1732
|
+
function onHandlePointerDown(key, event) {
|
|
1542
1733
|
if (isDestroyed) return;
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
event.preventDefault();
|
|
1548
|
-
}
|
|
1549
|
-
activeCornerKey = hitCorner;
|
|
1550
|
-
activeNudgeCorner = hitCorner;
|
|
1734
|
+
if (typeof event.preventDefault === "function") event.preventDefault();
|
|
1735
|
+
activeCornerKey = key;
|
|
1736
|
+
focusedCornerKey = key;
|
|
1737
|
+
updateSelected();
|
|
1551
1738
|
dragPointerId = event.pointerId;
|
|
1552
|
-
lastPointerPosition =
|
|
1553
|
-
|
|
1554
|
-
if (keyboardEnabled && typeof
|
|
1739
|
+
lastPointerPosition = getEventCanvasPoint(event);
|
|
1740
|
+
handleEls[key].classList.add("is-active");
|
|
1741
|
+
if (keyboardEnabled && typeof handleEls[key].focus === "function") {
|
|
1555
1742
|
try {
|
|
1556
|
-
|
|
1743
|
+
handleEls[key].focus({ preventScroll: true });
|
|
1557
1744
|
} catch (_) {
|
|
1558
|
-
|
|
1745
|
+
handleEls[key].focus();
|
|
1559
1746
|
}
|
|
1560
1747
|
}
|
|
1561
|
-
if (
|
|
1562
|
-
|
|
1748
|
+
if (handleEls[key].setPointerCapture && event.pointerId != null) {
|
|
1749
|
+
try {
|
|
1750
|
+
handleEls[key].setPointerCapture(event.pointerId);
|
|
1751
|
+
} catch (_) {
|
|
1752
|
+
}
|
|
1563
1753
|
}
|
|
1564
1754
|
scheduleRender();
|
|
1565
1755
|
}
|
|
1566
|
-
function
|
|
1567
|
-
if (isDestroyed) return;
|
|
1568
|
-
const point = getEventCanvasPoint(event);
|
|
1569
|
-
if (!activeCornerKey) {
|
|
1570
|
-
editorCanvas.style.cursor = hitTestCorner(point.x, point.y) ? "grab" : "crosshair";
|
|
1571
|
-
return;
|
|
1572
|
-
}
|
|
1756
|
+
function onHandlePointerMove(key, event) {
|
|
1757
|
+
if (isDestroyed || activeCornerKey !== key) return;
|
|
1573
1758
|
if (dragPointerId !== null && event.pointerId !== dragPointerId) return;
|
|
1574
|
-
lastPointerPosition =
|
|
1575
|
-
|
|
1576
|
-
setCorner(activeCornerKey, imagePoint);
|
|
1759
|
+
lastPointerPosition = getEventCanvasPoint(event);
|
|
1760
|
+
setCorner(key, viewToImage(lastPointerPosition.x, lastPointerPosition.y));
|
|
1577
1761
|
}
|
|
1578
|
-
function
|
|
1579
|
-
if (
|
|
1762
|
+
function onHandlePointerUp(key, event) {
|
|
1763
|
+
if (activeCornerKey !== key) return;
|
|
1580
1764
|
if (dragPointerId !== null && event.pointerId !== dragPointerId) return;
|
|
1581
|
-
if (
|
|
1765
|
+
if (handleEls[key].releasePointerCapture && dragPointerId != null) {
|
|
1582
1766
|
try {
|
|
1583
|
-
|
|
1767
|
+
handleEls[key].releasePointerCapture(dragPointerId);
|
|
1584
1768
|
} catch (_) {
|
|
1585
1769
|
}
|
|
1586
1770
|
}
|
|
1771
|
+
handleEls[key].classList.remove("is-active");
|
|
1587
1772
|
activeCornerKey = null;
|
|
1588
1773
|
dragPointerId = null;
|
|
1589
1774
|
lastPointerPosition = null;
|
|
1590
|
-
editorCanvas.style.cursor = "crosshair";
|
|
1591
1775
|
scheduleRender();
|
|
1592
1776
|
}
|
|
1593
|
-
function
|
|
1777
|
+
function onHandleKeyDown(key, event) {
|
|
1594
1778
|
if (isDestroyed || !keyboardEnabled) return;
|
|
1595
1779
|
if (event.key === "Enter") {
|
|
1596
1780
|
event.preventDefault();
|
|
@@ -1611,85 +1795,78 @@ function createCornerEditor(options = {}) {
|
|
|
1611
1795
|
else return;
|
|
1612
1796
|
event.preventDefault();
|
|
1613
1797
|
const step = event.shiftKey ? nudges.steps[nudges.steps.length - 1] || 10 : 1;
|
|
1614
|
-
|
|
1615
|
-
setCorner(activeNudgeCorner, {
|
|
1616
|
-
x: current.x + dx * step,
|
|
1617
|
-
y: current.y + dy * step
|
|
1618
|
-
});
|
|
1798
|
+
setCorner(key, { x: corners[key].x + dx * step, y: corners[key].y + dy * step });
|
|
1619
1799
|
}
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1800
|
+
let toolbarEl = null;
|
|
1801
|
+
let nudgeControls = null;
|
|
1802
|
+
let expertBtn = null;
|
|
1803
|
+
let expertVisible = false;
|
|
1804
|
+
const hasExpertToggle = toolbar.enabled && nudges.enabled;
|
|
1805
|
+
function makeButton({ html, text, title, className, onClick }) {
|
|
1806
|
+
const btn = doc.createElement("button");
|
|
1807
|
+
btn.type = "button";
|
|
1808
|
+
if (html != null) btn.innerHTML = html;
|
|
1809
|
+
else btn.textContent = text;
|
|
1810
|
+
if (title) {
|
|
1811
|
+
btn.title = title;
|
|
1812
|
+
btn.setAttribute("aria-label", title);
|
|
1813
|
+
}
|
|
1814
|
+
if (className) btn.className = className;
|
|
1815
|
+
btn.addEventListener("click", onClick);
|
|
1816
|
+
return btn;
|
|
1817
|
+
}
|
|
1818
|
+
function setExpert(on) {
|
|
1819
|
+
expertVisible = on;
|
|
1820
|
+
if (nudgeControls) nudgeControls.style.display = on ? "" : "none";
|
|
1821
|
+
if (expertBtn) {
|
|
1822
|
+
expertBtn.classList.toggle("is-on", on);
|
|
1823
|
+
expertBtn.setAttribute("aria-pressed", String(on));
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
function buildToolbar() {
|
|
1827
|
+
var _a2;
|
|
1828
|
+
if (!toolbar.enabled) return;
|
|
1829
|
+
if (!toolbar.reset && !toolbar.cancel && !toolbar.apply && !hasExpertToggle) return;
|
|
1830
|
+
toolbarEl = doc.createElement("div");
|
|
1831
|
+
toolbarEl.className = "scanic-toolbar" + (((_a2 = options.classNames) == null ? void 0 : _a2.toolbar) ? " " + options.classNames.toolbar : "");
|
|
1832
|
+
if (toolbar.reset) {
|
|
1833
|
+
toolbarEl.appendChild(makeButton({ html: ICONS.reset, title: toolbar.labels.reset, className: "scanic-btn-reset", onClick: () => publicReset() }));
|
|
1834
|
+
}
|
|
1835
|
+
if (hasExpertToggle) {
|
|
1836
|
+
expertBtn = makeButton({ html: ICONS.expert, title: "Precision nudge (expert)", className: "scanic-btn-expert", onClick: () => setExpert(!expertVisible) });
|
|
1837
|
+
expertBtn.setAttribute("aria-pressed", "false");
|
|
1838
|
+
toolbarEl.appendChild(expertBtn);
|
|
1839
|
+
}
|
|
1840
|
+
if (toolbar.cancel) {
|
|
1841
|
+
toolbarEl.appendChild(makeButton({ html: ICONS.cancel, title: toolbar.labels.cancel, className: "scanic-btn-cancel", onClick: () => cancelEditor() }));
|
|
1842
|
+
}
|
|
1843
|
+
if (toolbar.apply) {
|
|
1844
|
+
toolbarEl.appendChild(makeButton({ html: ICONS.apply, title: toolbar.labels.apply, className: "scanic-btn-apply", onClick: () => confirmEditor() }));
|
|
1845
|
+
}
|
|
1846
|
+
container.appendChild(toolbarEl);
|
|
1626
1847
|
}
|
|
1627
|
-
function
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1848
|
+
function buildNudgeControls() {
|
|
1849
|
+
var _a2;
|
|
1850
|
+
if (!nudges.enabled) return;
|
|
1851
|
+
nudgeControls = doc.createElement("div");
|
|
1852
|
+
nudgeControls.className = "scanic-nudges" + (((_a2 = options.classNames) == null ? void 0 : _a2.nudges) ? " " + options.classNames.nudges : "");
|
|
1853
|
+
const makeNudge = (glyph, label, dx, dy, step) => makeButton({
|
|
1854
|
+
text: glyph,
|
|
1855
|
+
title: label,
|
|
1856
|
+
className: "scanic-btn-nudge",
|
|
1857
|
+
onClick: () => {
|
|
1858
|
+
const current = corners[focusedCornerKey];
|
|
1859
|
+
setCorner(focusedCornerKey, { x: current.x + dx * step, y: current.y + dy * step });
|
|
1860
|
+
}
|
|
1632
1861
|
});
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
if (hitTestCorner(point.x, point.y)) {
|
|
1639
|
-
event.preventDefault();
|
|
1862
|
+
for (const step of nudges.steps) {
|
|
1863
|
+
nudgeControls.appendChild(makeNudge("←", `Move left ${step}px`, -1, 0, step));
|
|
1864
|
+
nudgeControls.appendChild(makeNudge("↑", `Move up ${step}px`, 0, -1, step));
|
|
1865
|
+
nudgeControls.appendChild(makeNudge("↓", `Move down ${step}px`, 0, 1, step));
|
|
1866
|
+
nudgeControls.appendChild(makeNudge("→", `Move right ${step}px`, 1, 0, step));
|
|
1640
1867
|
}
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
clientY: touch.clientY,
|
|
1644
|
-
pointerId: 2
|
|
1645
|
-
});
|
|
1646
|
-
}
|
|
1647
|
-
function handleTouchMove(event) {
|
|
1648
|
-
const touch = event.touches[0];
|
|
1649
|
-
if (!touch) return;
|
|
1650
|
-
event.preventDefault();
|
|
1651
|
-
handlePointerMove({
|
|
1652
|
-
clientX: touch.clientX,
|
|
1653
|
-
clientY: touch.clientY,
|
|
1654
|
-
pointerId: 2
|
|
1655
|
-
});
|
|
1656
|
-
}
|
|
1657
|
-
function attachEvents() {
|
|
1658
|
-
if (typeof runtimeGlobal.PointerEvent !== "undefined") {
|
|
1659
|
-
editorCanvas.addEventListener("pointerdown", handlePointerDown);
|
|
1660
|
-
editorCanvas.addEventListener("pointermove", handlePointerMove);
|
|
1661
|
-
editorCanvas.addEventListener("pointerup", handlePointerUp);
|
|
1662
|
-
editorCanvas.addEventListener("pointercancel", handlePointerUp);
|
|
1663
|
-
} else {
|
|
1664
|
-
editorCanvas.addEventListener("mousedown", handleMouseDown);
|
|
1665
|
-
if (typeof runtimeGlobal.addEventListener === "function") {
|
|
1666
|
-
runtimeGlobal.addEventListener("mousemove", handleMouseMove);
|
|
1667
|
-
runtimeGlobal.addEventListener("mouseup", handlePointerUp);
|
|
1668
|
-
}
|
|
1669
|
-
editorCanvas.addEventListener("touchstart", handleTouchStart, { passive: false });
|
|
1670
|
-
editorCanvas.addEventListener("touchmove", handleTouchMove, { passive: false });
|
|
1671
|
-
editorCanvas.addEventListener("touchend", handlePointerUp);
|
|
1672
|
-
editorCanvas.addEventListener("touchcancel", handlePointerUp);
|
|
1673
|
-
}
|
|
1674
|
-
if (keyboardEnabled) {
|
|
1675
|
-
editorCanvas.addEventListener("keydown", handleKeyDown);
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
|
-
function detachEvents() {
|
|
1679
|
-
editorCanvas.removeEventListener("pointerdown", handlePointerDown);
|
|
1680
|
-
editorCanvas.removeEventListener("pointermove", handlePointerMove);
|
|
1681
|
-
editorCanvas.removeEventListener("pointerup", handlePointerUp);
|
|
1682
|
-
editorCanvas.removeEventListener("pointercancel", handlePointerUp);
|
|
1683
|
-
editorCanvas.removeEventListener("mousedown", handleMouseDown);
|
|
1684
|
-
if (typeof runtimeGlobal.removeEventListener === "function") {
|
|
1685
|
-
runtimeGlobal.removeEventListener("mousemove", handleMouseMove);
|
|
1686
|
-
runtimeGlobal.removeEventListener("mouseup", handlePointerUp);
|
|
1687
|
-
}
|
|
1688
|
-
editorCanvas.removeEventListener("touchstart", handleTouchStart);
|
|
1689
|
-
editorCanvas.removeEventListener("touchmove", handleTouchMove);
|
|
1690
|
-
editorCanvas.removeEventListener("touchend", handlePointerUp);
|
|
1691
|
-
editorCanvas.removeEventListener("touchcancel", handlePointerUp);
|
|
1692
|
-
editorCanvas.removeEventListener("keydown", handleKeyDown);
|
|
1868
|
+
container.appendChild(nudgeControls);
|
|
1869
|
+
if (hasExpertToggle) nudgeControls.style.display = "none";
|
|
1693
1870
|
}
|
|
1694
1871
|
let dprCleanup = null;
|
|
1695
1872
|
function watchDevicePixelRatio() {
|
|
@@ -1725,6 +1902,11 @@ function createCornerEditor(options = {}) {
|
|
|
1725
1902
|
options.onCancel();
|
|
1726
1903
|
}
|
|
1727
1904
|
}
|
|
1905
|
+
function publicReset() {
|
|
1906
|
+
corners = cloneCorners(initialCorners);
|
|
1907
|
+
emitChange();
|
|
1908
|
+
scheduleRender();
|
|
1909
|
+
}
|
|
1728
1910
|
const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
|
|
1729
1911
|
if (isDestroyed) return;
|
|
1730
1912
|
const next = computeDisplaySize();
|
|
@@ -1735,12 +1917,13 @@ function createCornerEditor(options = {}) {
|
|
|
1735
1917
|
scheduleRender();
|
|
1736
1918
|
}) : null;
|
|
1737
1919
|
updateCanvasSize();
|
|
1920
|
+
buildToolbar();
|
|
1738
1921
|
buildNudgeControls();
|
|
1739
|
-
attachEvents();
|
|
1740
1922
|
watchDevicePixelRatio();
|
|
1741
1923
|
if (resizeObserver) {
|
|
1742
1924
|
resizeObserver.observe(container);
|
|
1743
1925
|
}
|
|
1926
|
+
updateSelected();
|
|
1744
1927
|
render();
|
|
1745
1928
|
return {
|
|
1746
1929
|
getCorners() {
|
|
@@ -1756,9 +1939,7 @@ function createCornerEditor(options = {}) {
|
|
|
1756
1939
|
return true;
|
|
1757
1940
|
},
|
|
1758
1941
|
reset() {
|
|
1759
|
-
|
|
1760
|
-
emitChange();
|
|
1761
|
-
scheduleRender();
|
|
1942
|
+
publicReset();
|
|
1762
1943
|
},
|
|
1763
1944
|
nudge(cornerKey, dx, dy, step = 1) {
|
|
1764
1945
|
if (!cornerOrder.includes(cornerKey)) {
|
|
@@ -1769,6 +1950,12 @@ function createCornerEditor(options = {}) {
|
|
|
1769
1950
|
y: corners[cornerKey].y + dy * step
|
|
1770
1951
|
});
|
|
1771
1952
|
},
|
|
1953
|
+
/** Re-read CSS variables into the canvas layer after a runtime theme change. */
|
|
1954
|
+
refreshTheme(theme) {
|
|
1955
|
+
applyThemeOption(theme);
|
|
1956
|
+
resolveTheme();
|
|
1957
|
+
scheduleRender();
|
|
1958
|
+
},
|
|
1772
1959
|
confirm() {
|
|
1773
1960
|
return confirmEditor();
|
|
1774
1961
|
},
|
|
@@ -1776,6 +1963,7 @@ function createCornerEditor(options = {}) {
|
|
|
1776
1963
|
cancelEditor();
|
|
1777
1964
|
},
|
|
1778
1965
|
destroy() {
|
|
1966
|
+
var _a2;
|
|
1779
1967
|
if (isDestroyed) return;
|
|
1780
1968
|
isDestroyed = true;
|
|
1781
1969
|
if (rafId !== null && caf) {
|
|
@@ -1789,13 +1977,15 @@ function createCornerEditor(options = {}) {
|
|
|
1789
1977
|
if (resizeObserver) {
|
|
1790
1978
|
resizeObserver.disconnect();
|
|
1791
1979
|
}
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
}
|
|
1796
|
-
if (editorCanvas.parentNode) {
|
|
1797
|
-
editorCanvas.parentNode.removeChild(editorCanvas);
|
|
1980
|
+
for (const key of cornerOrder) {
|
|
1981
|
+
const el = handleEls[key];
|
|
1982
|
+
if (el && el.parentNode) el.parentNode.removeChild(el);
|
|
1798
1983
|
}
|
|
1984
|
+
if (toolbarEl && toolbarEl.parentNode) toolbarEl.parentNode.removeChild(toolbarEl);
|
|
1985
|
+
if (nudgeControls && nudgeControls.parentNode) nudgeControls.parentNode.removeChild(nudgeControls);
|
|
1986
|
+
if (editorCanvas.parentNode) editorCanvas.parentNode.removeChild(editorCanvas);
|
|
1987
|
+
if (addedRootClass) container.classList.remove("scanic-corner-editor");
|
|
1988
|
+
if ((_a2 = options.classNames) == null ? void 0 : _a2.root) container.classList.remove(options.classNames.root);
|
|
1799
1989
|
if (changedContainerPosition) {
|
|
1800
1990
|
container.style.position = restoreContainerStyle.position;
|
|
1801
1991
|
}
|