canvu-react 0.4.47 → 0.4.49
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/{asset-hydration-Dc7fsnTG.d.ts → asset-hydration-BSjiek7Q.d.ts} +2 -2
- package/dist/{asset-hydration-Cy_2FyV5.d.cts → asset-hydration-F6aM5C7x.d.cts} +2 -2
- package/dist/{asset-store-TzOPvlgn.d.cts → asset-store-35ysK28r.d.cts} +1 -1
- package/dist/{asset-store-DQPRZEcy.d.ts → asset-store-D_FjW_CN.d.ts} +1 -1
- package/dist/chatbot.d.cts +6 -6
- package/dist/chatbot.d.ts +6 -6
- package/dist/index.cjs +49 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +53 -9
- package/dist/index.d.ts +53 -9
- package/dist/index.js +49 -1
- package/dist/index.js.map +1 -1
- package/dist/{link-item-DwzXOwU5.d.cts → link-item-BMV3VUCr.d.cts} +1 -1
- package/dist/{link-item-IW4GTnxl.d.ts → link-item-COoNNvCu.d.ts} +1 -1
- package/dist/native.cjs +59 -10
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +6 -6
- package/dist/native.d.ts +6 -6
- package/dist/native.js +59 -10
- package/dist/native.js.map +1 -1
- package/dist/react.cjs +259 -18
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +29 -13
- package/dist/react.d.ts +29 -13
- package/dist/react.js +259 -18
- package/dist/react.js.map +1 -1
- package/dist/realtime.cjs +3 -1
- package/dist/realtime.cjs.map +1 -1
- package/dist/realtime.d.cts +8 -8
- package/dist/realtime.d.ts +8 -8
- package/dist/realtime.js +3 -1
- package/dist/realtime.js.map +1 -1
- package/dist/realtimeNative.d.cts +4 -4
- package/dist/realtimeNative.d.ts +4 -4
- package/dist/{shape-builders-Cyh8zvDG.d.ts → shape-builders-BCOAG0pS.d.ts} +1 -1
- package/dist/{shape-builders-CKEMjivV.d.cts → shape-builders-BmLS8CNh.d.cts} +1 -1
- package/dist/tldraw.cjs +3 -1
- package/dist/tldraw.cjs.map +1 -1
- package/dist/tldraw.d.cts +1 -1
- package/dist/tldraw.d.ts +1 -1
- package/dist/tldraw.js +3 -1
- package/dist/tldraw.js.map +1 -1
- package/dist/{types-BUPc2Zgw.d.cts → types-6HszqSa5.d.cts} +1 -1
- package/dist/{types-B7xZAKVJ.d.ts → types-BAEHaIYO.d.ts} +43 -6
- package/dist/{types-B82WiQQh.d.ts → types-BMMPUak7.d.ts} +1 -1
- package/dist/{types-BQUbxMgz.d.cts → types-BOQLWyCw.d.cts} +1 -1
- package/dist/{types-CYtq9Pr9.d.ts → types-BtWbGOqh.d.ts} +1 -1
- package/dist/{types-BCCvY6ie.d.cts → types-fJNwEnHf.d.cts} +35 -1
- package/dist/{types-BCCvY6ie.d.ts → types-fJNwEnHf.d.ts} +35 -1
- package/dist/{types-C4wI3Jyc.d.cts → types-uzeExFkd.d.cts} +43 -6
- package/package.json +1 -1
package/dist/react.cjs
CHANGED
|
@@ -3362,7 +3362,9 @@ function ToolPluginComponent({
|
|
|
3362
3362
|
pluginId,
|
|
3363
3363
|
tool,
|
|
3364
3364
|
toolTransform,
|
|
3365
|
-
createItem
|
|
3365
|
+
createItem,
|
|
3366
|
+
selectAfterCreate,
|
|
3367
|
+
onSelectModeItemClick
|
|
3366
3368
|
}) {
|
|
3367
3369
|
const contribution = react.useMemo(
|
|
3368
3370
|
() => ({
|
|
@@ -3371,17 +3373,25 @@ function ToolPluginComponent({
|
|
|
3371
3373
|
customPlacements: createItem ? [
|
|
3372
3374
|
{
|
|
3373
3375
|
toolId: tool.id,
|
|
3374
|
-
createItem
|
|
3376
|
+
createItem,
|
|
3377
|
+
selectAfterCreate,
|
|
3378
|
+
onSelectModeItemClick
|
|
3375
3379
|
}
|
|
3376
3380
|
] : void 0
|
|
3377
3381
|
}),
|
|
3378
|
-
[createItem, tool, toolTransform]
|
|
3382
|
+
[createItem, onSelectModeItemClick, selectAfterCreate, tool, toolTransform]
|
|
3379
3383
|
);
|
|
3380
3384
|
useCanvuPluginContribution(pluginId, contribution);
|
|
3381
3385
|
return null;
|
|
3382
3386
|
}
|
|
3383
3387
|
function createToolPlugin(options) {
|
|
3384
|
-
const {
|
|
3388
|
+
const {
|
|
3389
|
+
createItem,
|
|
3390
|
+
toolTransform,
|
|
3391
|
+
selectAfterCreate,
|
|
3392
|
+
onSelectModeItemClick,
|
|
3393
|
+
...tool
|
|
3394
|
+
} = options;
|
|
3385
3395
|
const pluginId = `canvu.plugin.tool:${tool.id}`;
|
|
3386
3396
|
return createCanvuPlugin({
|
|
3387
3397
|
id: pluginId,
|
|
@@ -3392,7 +3402,9 @@ function createToolPlugin(options) {
|
|
|
3392
3402
|
pluginId,
|
|
3393
3403
|
tool,
|
|
3394
3404
|
toolTransform,
|
|
3395
|
-
createItem
|
|
3405
|
+
createItem,
|
|
3406
|
+
selectAfterCreate,
|
|
3407
|
+
onSelectModeItemClick
|
|
3396
3408
|
}
|
|
3397
3409
|
);
|
|
3398
3410
|
}
|
|
@@ -5993,7 +6005,43 @@ init_shape_builders();
|
|
|
5993
6005
|
|
|
5994
6006
|
// src/interaction/resize-handles.ts
|
|
5995
6007
|
init_rect();
|
|
5996
|
-
|
|
6008
|
+
init_link_item();
|
|
6009
|
+
var ALL_RESIZE_HANDLES = [
|
|
6010
|
+
"nw",
|
|
6011
|
+
"n",
|
|
6012
|
+
"ne",
|
|
6013
|
+
"e",
|
|
6014
|
+
"se",
|
|
6015
|
+
"s",
|
|
6016
|
+
"sw",
|
|
6017
|
+
"w"
|
|
6018
|
+
];
|
|
6019
|
+
var CORNER_RESIZE_HANDLES = [
|
|
6020
|
+
"nw",
|
|
6021
|
+
"ne",
|
|
6022
|
+
"se",
|
|
6023
|
+
"sw"
|
|
6024
|
+
];
|
|
6025
|
+
function dedupeHandles(handles) {
|
|
6026
|
+
const allowed = new Set(handles);
|
|
6027
|
+
return ALL_RESIZE_HANDLES.filter((handle) => allowed.has(handle));
|
|
6028
|
+
}
|
|
6029
|
+
function resolveCustomResizeHandles(handles) {
|
|
6030
|
+
if (handles === "corners") return CORNER_RESIZE_HANDLES;
|
|
6031
|
+
if (handles === "all" || handles === void 0) return ALL_RESIZE_HANDLES;
|
|
6032
|
+
return dedupeHandles(handles);
|
|
6033
|
+
}
|
|
6034
|
+
function resolveResizeHandlesForItem(item) {
|
|
6035
|
+
if (!item) return ALL_RESIZE_HANDLES;
|
|
6036
|
+
if (getLinkData(item)) return CORNER_RESIZE_HANDLES;
|
|
6037
|
+
if (item.toolKind === "custom") {
|
|
6038
|
+
return resolveCustomResizeHandles(item.customResizeHandles);
|
|
6039
|
+
}
|
|
6040
|
+
return ALL_RESIZE_HANDLES;
|
|
6041
|
+
}
|
|
6042
|
+
function itemAllowsResizeHandle(item, handle) {
|
|
6043
|
+
return resolveResizeHandlesForItem(item).includes(handle);
|
|
6044
|
+
}
|
|
5997
6045
|
function getHandleWorldPosition(bounds, id) {
|
|
5998
6046
|
const r = normalizeRect(bounds);
|
|
5999
6047
|
const cx = r.x + r.width / 2;
|
|
@@ -6017,7 +6065,7 @@ function getHandleWorldPosition(bounds, id) {
|
|
|
6017
6065
|
return { x: r.x, y: cy };
|
|
6018
6066
|
}
|
|
6019
6067
|
}
|
|
6020
|
-
function hitTestResizeHandle(bounds, worldX, worldY, radiusWorld, rotationRad = 0) {
|
|
6068
|
+
function hitTestResizeHandle(bounds, worldX, worldY, radiusWorld, rotationRad = 0, handles = ALL_RESIZE_HANDLES) {
|
|
6021
6069
|
const r = normalizeRect(bounds);
|
|
6022
6070
|
const pl = worldToItemLocal(
|
|
6023
6071
|
worldX,
|
|
@@ -6031,7 +6079,7 @@ function hitTestResizeHandle(bounds, worldX, worldY, radiusWorld, rotationRad =
|
|
|
6031
6079
|
const localBounds = { x: 0, y: 0, width: r.width, height: r.height };
|
|
6032
6080
|
let best = null;
|
|
6033
6081
|
let bestD = radiusWorld;
|
|
6034
|
-
for (const id of
|
|
6082
|
+
for (const id of handles) {
|
|
6035
6083
|
const p = getHandleWorldPosition(localBounds, id);
|
|
6036
6084
|
const d = Math.hypot(pl.x - p.x, pl.y - p.y);
|
|
6037
6085
|
if (d <= bestD) {
|
|
@@ -6663,6 +6711,9 @@ function moveItemByDelta(item, dx, dy) {
|
|
|
6663
6711
|
}
|
|
6664
6712
|
function resizeItemByHandle(item, start, handle, currentWorld) {
|
|
6665
6713
|
const sb = normalizeRect(start.bounds);
|
|
6714
|
+
if (!itemAllowsResizeHandle(item, handle)) {
|
|
6715
|
+
return item;
|
|
6716
|
+
}
|
|
6666
6717
|
const newBoundsRaw = computeNewBoundsForResize(item, sb, handle, currentWorld);
|
|
6667
6718
|
const nb = normalizeRect(newBoundsRaw);
|
|
6668
6719
|
const k = item.toolKind;
|
|
@@ -6923,6 +6974,14 @@ function formatItemPlacementTransform(item) {
|
|
|
6923
6974
|
const cy = r.height / 2;
|
|
6924
6975
|
return `translate(${item.x}, ${item.y}) translate(${cx}, ${cy}) rotate(${deg}) translate(${-cx}, ${-cy})`;
|
|
6925
6976
|
}
|
|
6977
|
+
function itemClassName(item) {
|
|
6978
|
+
const kind = item.toolKind ?? "unknown";
|
|
6979
|
+
const classes = [`canvu-item`, `canvu-item--${kind}`];
|
|
6980
|
+
if (item.svgClassName) {
|
|
6981
|
+
classes.push(item.svgClassName);
|
|
6982
|
+
}
|
|
6983
|
+
return classes.join(" ");
|
|
6984
|
+
}
|
|
6926
6985
|
var SvgVectorRenderer = class {
|
|
6927
6986
|
container;
|
|
6928
6987
|
scene;
|
|
@@ -6930,6 +6989,8 @@ var SvgVectorRenderer = class {
|
|
|
6930
6989
|
svg;
|
|
6931
6990
|
rootG;
|
|
6932
6991
|
itemNodeCache = /* @__PURE__ */ new Map();
|
|
6992
|
+
selectedIds = /* @__PURE__ */ new Set();
|
|
6993
|
+
hoveredItemId = null;
|
|
6933
6994
|
liveOverlay = null;
|
|
6934
6995
|
resizeObserver;
|
|
6935
6996
|
constructor(options) {
|
|
@@ -6950,6 +7011,20 @@ var SvgVectorRenderer = class {
|
|
|
6950
7011
|
this.resizeObserver = new ResizeObserver(() => this.render());
|
|
6951
7012
|
this.resizeObserver.observe(this.container);
|
|
6952
7013
|
}
|
|
7014
|
+
/**
|
|
7015
|
+
* Updates interaction attributes on item groups for CSS hooks.
|
|
7016
|
+
*
|
|
7017
|
+
* Use `[data-canvu-selected="true"]` and `[data-canvu-hovered="true"]`
|
|
7018
|
+
* for animations in interactive canvases where the scene SVG may ignore
|
|
7019
|
+
* pointer events.
|
|
7020
|
+
*/
|
|
7021
|
+
setInteractionState(state) {
|
|
7022
|
+
this.selectedIds = new Set(state.selectedIds ?? []);
|
|
7023
|
+
this.hoveredItemId = state.hoveredItemId ?? null;
|
|
7024
|
+
for (const [id, cached] of this.itemNodeCache) {
|
|
7025
|
+
this.applyInteractionAttributes(cached.g, id);
|
|
7026
|
+
}
|
|
7027
|
+
}
|
|
6953
7028
|
/**
|
|
6954
7029
|
* Reads container size, culls items, and updates the SVG (incrementally when possible).
|
|
6955
7030
|
*/
|
|
@@ -6986,6 +7061,7 @@ var SvgVectorRenderer = class {
|
|
|
6986
7061
|
if (!this.liveOverlay) {
|
|
6987
7062
|
const g = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
6988
7063
|
g.setAttribute("data-live-overlay", "true");
|
|
7064
|
+
g.setAttribute("data-canvu-item", "true");
|
|
6989
7065
|
this.liveOverlay = {
|
|
6990
7066
|
g,
|
|
6991
7067
|
lastChildrenSvg: "",
|
|
@@ -6995,6 +7071,11 @@ var SvgVectorRenderer = class {
|
|
|
6995
7071
|
}
|
|
6996
7072
|
const cached = this.liveOverlay;
|
|
6997
7073
|
const t = formatItemPlacementTransform(item);
|
|
7074
|
+
cached.g.setAttribute("class", itemClassName(item));
|
|
7075
|
+
cached.g.setAttribute("data-item-id", item.id);
|
|
7076
|
+
cached.g.setAttribute("data-tool-kind", item.toolKind ?? "");
|
|
7077
|
+
cached.g.setAttribute("data-canvu-selected", "false");
|
|
7078
|
+
cached.g.setAttribute("data-canvu-hovered", "false");
|
|
6998
7079
|
if (cached.lastTransform !== t) {
|
|
6999
7080
|
cached.g.setAttribute("transform", t);
|
|
7000
7081
|
cached.lastTransform = t;
|
|
@@ -7025,6 +7106,7 @@ var SvgVectorRenderer = class {
|
|
|
7025
7106
|
let cached = this.itemNodeCache.get(item.id);
|
|
7026
7107
|
if (!cached) {
|
|
7027
7108
|
const g2 = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
7109
|
+
g2.setAttribute("data-canvu-item", "true");
|
|
7028
7110
|
g2.setAttribute("data-item-id", item.id);
|
|
7029
7111
|
cached = {
|
|
7030
7112
|
g: g2,
|
|
@@ -7034,6 +7116,11 @@ var SvgVectorRenderer = class {
|
|
|
7034
7116
|
this.itemNodeCache.set(item.id, cached);
|
|
7035
7117
|
}
|
|
7036
7118
|
const { g } = cached;
|
|
7119
|
+
g.setAttribute("class", itemClassName(item));
|
|
7120
|
+
g.setAttribute("data-canvu-item", "true");
|
|
7121
|
+
g.setAttribute("data-item-id", item.id);
|
|
7122
|
+
g.setAttribute("data-tool-kind", item.toolKind ?? "");
|
|
7123
|
+
this.applyInteractionAttributes(g, item.id);
|
|
7037
7124
|
const t = formatItemPlacementTransform(item);
|
|
7038
7125
|
if (cached.lastTransform !== t) {
|
|
7039
7126
|
g.setAttribute("transform", t);
|
|
@@ -7050,6 +7137,16 @@ var SvgVectorRenderer = class {
|
|
|
7050
7137
|
previousNode = g;
|
|
7051
7138
|
}
|
|
7052
7139
|
}
|
|
7140
|
+
applyInteractionAttributes(g, itemId) {
|
|
7141
|
+
g.setAttribute(
|
|
7142
|
+
"data-canvu-selected",
|
|
7143
|
+
this.selectedIds.has(itemId) ? "true" : "false"
|
|
7144
|
+
);
|
|
7145
|
+
g.setAttribute(
|
|
7146
|
+
"data-canvu-hovered",
|
|
7147
|
+
this.hoveredItemId === itemId ? "true" : "false"
|
|
7148
|
+
);
|
|
7149
|
+
}
|
|
7053
7150
|
destroy() {
|
|
7054
7151
|
this.resizeObserver.disconnect();
|
|
7055
7152
|
this.itemNodeCache.clear();
|
|
@@ -7157,10 +7254,7 @@ function smoothFreehandPointsToPathD(points) {
|
|
|
7157
7254
|
}
|
|
7158
7255
|
|
|
7159
7256
|
// src/react/InteractionOverlay.tsx
|
|
7160
|
-
init_link_item();
|
|
7161
7257
|
init_shape_builders();
|
|
7162
|
-
var HANDLE_ORDER = ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
|
|
7163
|
-
var LINK_HANDLE_ORDER = ["nw", "ne", "se", "sw"];
|
|
7164
7258
|
var ERASER_TINT = "#cbd5e1";
|
|
7165
7259
|
var ERASER_TINT_OPACITY = 0.95;
|
|
7166
7260
|
var ERASER_PREVIEW_OPACITY = 0.3;
|
|
@@ -7213,7 +7307,7 @@ function InteractionOverlay({
|
|
|
7213
7307
|
) }, it.id);
|
|
7214
7308
|
}),
|
|
7215
7309
|
showResizeHandles && bSingle && single && rotHandlePos && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7216
|
-
(
|
|
7310
|
+
resolveResizeHandlesForItem(single).map((hid) => {
|
|
7217
7311
|
const p = getHandleWorldPositionRotated(bSingle, hid, rotSingle);
|
|
7218
7312
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7219
7313
|
"circle",
|
|
@@ -8027,7 +8121,6 @@ function defaultPlacementWorld(tool, center) {
|
|
|
8027
8121
|
lineWorld: [a, b]
|
|
8028
8122
|
};
|
|
8029
8123
|
}
|
|
8030
|
-
var LINK_CORNER_HANDLES2 = /* @__PURE__ */ new Set(["nw", "ne", "se", "sw"]);
|
|
8031
8124
|
function pointInSelectedItemBounds(item, worldX, worldY) {
|
|
8032
8125
|
const bounds = normalizeRect(item.bounds);
|
|
8033
8126
|
const local = worldToItemLocal(
|
|
@@ -8121,6 +8214,16 @@ function reorderItemsByIds(items, ids, direction) {
|
|
|
8121
8214
|
function isDefaultMarkerToolStyle(style) {
|
|
8122
8215
|
return style.stroke === MARKER_TOOL_STYLE.stroke && style.strokeWidth === MARKER_TOOL_STYLE.strokeWidth && style.strokeOpacity === MARKER_TOOL_STYLE.strokeOpacity;
|
|
8123
8216
|
}
|
|
8217
|
+
function tagCustomPlacementItem(item, toolId) {
|
|
8218
|
+
return item.customToolId === toolId ? item : { ...item, customToolId: toolId };
|
|
8219
|
+
}
|
|
8220
|
+
function findSelectModeItemClickPlacement(item, placements) {
|
|
8221
|
+
const toolId = item.customToolId;
|
|
8222
|
+
if (!toolId) return null;
|
|
8223
|
+
return [...placements].reverse().find(
|
|
8224
|
+
(placement) => placement.toolId === toolId && placement.onSelectModeItemClick
|
|
8225
|
+
) ?? null;
|
|
8226
|
+
}
|
|
8124
8227
|
function mergeToolListById(baseTools, pluginTools) {
|
|
8125
8228
|
const next = [...baseTools];
|
|
8126
8229
|
for (const tool of pluginTools) {
|
|
@@ -8285,6 +8388,7 @@ var VectorViewport = react.forwardRef(
|
|
|
8285
8388
|
const [cameraForOverlay, setCameraForOverlay] = react.useState(null);
|
|
8286
8389
|
const sceneRef = react.useRef(null);
|
|
8287
8390
|
const rendererRef = react.useRef(null);
|
|
8391
|
+
const hoveredItemIdRef = react.useRef(null);
|
|
8288
8392
|
const pluginViewportRef = react.useRef(null);
|
|
8289
8393
|
const [pluginContributions, setPluginContributions] = react.useState({});
|
|
8290
8394
|
const registerContribution = react.useCallback(
|
|
@@ -8439,6 +8543,8 @@ var VectorViewport = react.forwardRef(
|
|
|
8439
8543
|
assetStoreRef.current = assetStore;
|
|
8440
8544
|
const customPlacementRef = react.useRef(customPlacement);
|
|
8441
8545
|
customPlacementRef.current = customPlacement;
|
|
8546
|
+
const allCustomPlacementsRef = react.useRef(allCustomPlacements);
|
|
8547
|
+
allCustomPlacementsRef.current = allCustomPlacements;
|
|
8442
8548
|
const dragStateRef = react.useRef({ kind: "idle" });
|
|
8443
8549
|
const clipboardRef = react.useRef(null);
|
|
8444
8550
|
const undoStackRef = react.useRef([]);
|
|
@@ -8490,6 +8596,12 @@ var VectorViewport = react.forwardRef(
|
|
|
8490
8596
|
}, [isSelectionControlled, controlledSelectedKey, uncontrolledSel]);
|
|
8491
8597
|
const effectiveSelectedIdsRef = react.useRef([]);
|
|
8492
8598
|
effectiveSelectedIdsRef.current = effectiveSelectedIds;
|
|
8599
|
+
const syncRendererInteractionState = react.useCallback(() => {
|
|
8600
|
+
rendererRef.current?.setInteractionState({
|
|
8601
|
+
selectedIds: effectiveSelectedIdsRef.current,
|
|
8602
|
+
hoveredItemId: hoveredItemIdRef.current
|
|
8603
|
+
});
|
|
8604
|
+
}, []);
|
|
8493
8605
|
const setEffectiveSelectedIds = react.useCallback(
|
|
8494
8606
|
(ids) => {
|
|
8495
8607
|
const next = [...ids];
|
|
@@ -8932,6 +9044,10 @@ var VectorViewport = react.forwardRef(
|
|
|
8932
9044
|
pointerEventsNone: interactiveRef.current
|
|
8933
9045
|
});
|
|
8934
9046
|
rendererRef.current = renderer;
|
|
9047
|
+
renderer.setInteractionState({
|
|
9048
|
+
selectedIds: effectiveSelectedIdsRef.current,
|
|
9049
|
+
hoveredItemId: hoveredItemIdRef.current
|
|
9050
|
+
});
|
|
8935
9051
|
renderer.render();
|
|
8936
9052
|
if (!reducedMotionRef.current) {
|
|
8937
9053
|
setZoomPercent(Math.round(camera.zoom * 100));
|
|
@@ -8969,11 +9085,53 @@ var VectorViewport = react.forwardRef(
|
|
|
8969
9085
|
setCameraForOverlay(null);
|
|
8970
9086
|
};
|
|
8971
9087
|
}, [applePencilNav, renderFrame]);
|
|
9088
|
+
react.useEffect(() => {
|
|
9089
|
+
rendererRef.current?.setInteractionState({
|
|
9090
|
+
selectedIds: effectiveSelectedIds,
|
|
9091
|
+
hoveredItemId: hoveredItemIdRef.current
|
|
9092
|
+
});
|
|
9093
|
+
}, [effectiveSelectedIds]);
|
|
8972
9094
|
react.useEffect(() => {
|
|
8973
9095
|
const r = rendererRef.current;
|
|
8974
9096
|
if (!r) return;
|
|
8975
9097
|
r.setPointerEventsNone(interactive);
|
|
8976
9098
|
}, [interactive]);
|
|
9099
|
+
const clearHoveredItem = react.useCallback(() => {
|
|
9100
|
+
if (hoveredItemIdRef.current === null) return;
|
|
9101
|
+
hoveredItemIdRef.current = null;
|
|
9102
|
+
syncRendererInteractionState();
|
|
9103
|
+
}, [syncRendererInteractionState]);
|
|
9104
|
+
const updateHoveredItem = react.useCallback(
|
|
9105
|
+
(clientX, clientY) => {
|
|
9106
|
+
const cam = cameraRef.current;
|
|
9107
|
+
const rect = sceneContainerRef.current?.getBoundingClientRect();
|
|
9108
|
+
if (!cam || !rect) return;
|
|
9109
|
+
const { worldX, worldY } = cam.screenToWorld(
|
|
9110
|
+
clientX - rect.left,
|
|
9111
|
+
clientY - rect.top
|
|
9112
|
+
);
|
|
9113
|
+
const hit = hitTestWorldPoint(resolvedItemsRef.current, worldX, worldY, {
|
|
9114
|
+
lineHitWorld: 10 / cam.zoom,
|
|
9115
|
+
ignoreLocked: true
|
|
9116
|
+
});
|
|
9117
|
+
const next = hit?.id ?? null;
|
|
9118
|
+
if (hoveredItemIdRef.current === next) return;
|
|
9119
|
+
hoveredItemIdRef.current = next;
|
|
9120
|
+
syncRendererInteractionState();
|
|
9121
|
+
},
|
|
9122
|
+
[syncRendererInteractionState]
|
|
9123
|
+
);
|
|
9124
|
+
react.useEffect(() => {
|
|
9125
|
+
const root = wrapperRef.current;
|
|
9126
|
+
if (!root) return;
|
|
9127
|
+
const move = (e) => updateHoveredItem(e.clientX, e.clientY);
|
|
9128
|
+
root.addEventListener("pointermove", move, { passive: true });
|
|
9129
|
+
root.addEventListener("pointerleave", clearHoveredItem);
|
|
9130
|
+
return () => {
|
|
9131
|
+
root.removeEventListener("pointermove", move);
|
|
9132
|
+
root.removeEventListener("pointerleave", clearHoveredItem);
|
|
9133
|
+
};
|
|
9134
|
+
}, [clearHoveredItem, updateHoveredItem]);
|
|
8977
9135
|
react.useEffect(() => {
|
|
8978
9136
|
if (!eraserActive && laserTrail.length === 0) return;
|
|
8979
9137
|
const timer = window.setInterval(() => {
|
|
@@ -9952,10 +10110,10 @@ var VectorViewport = react.forwardRef(
|
|
|
9952
10110
|
worldX,
|
|
9953
10111
|
worldY,
|
|
9954
10112
|
handleRadiusWorld,
|
|
9955
|
-
rot
|
|
10113
|
+
rot,
|
|
10114
|
+
resolveResizeHandlesForItem(selected)
|
|
9956
10115
|
);
|
|
9957
|
-
|
|
9958
|
-
if (hb && isLinkResizeHandle) {
|
|
10116
|
+
if (hb) {
|
|
9959
10117
|
const snapRs = bakedSnapshot(selected.id);
|
|
9960
10118
|
if (!snapRs) return;
|
|
9961
10119
|
dragStateRef.current = {
|
|
@@ -9979,6 +10137,31 @@ var VectorViewport = react.forwardRef(
|
|
|
9979
10137
|
ignoreLocked: true
|
|
9980
10138
|
});
|
|
9981
10139
|
if (hit) {
|
|
10140
|
+
const selectModeClickPlacement = !e.shiftKey ? findSelectModeItemClickPlacement(hit, allCustomPlacementsRef.current) : null;
|
|
10141
|
+
if (selectModeClickPlacement) {
|
|
10142
|
+
const isAlreadySelected = cur.includes(hit.id);
|
|
10143
|
+
const moveIds = isAlreadySelected ? [...cur] : [hit.id];
|
|
10144
|
+
const moveSnapshots = {};
|
|
10145
|
+
for (const id of moveIds) {
|
|
10146
|
+
const snap = bakedSnapshot(id);
|
|
10147
|
+
if (snap) {
|
|
10148
|
+
moveSnapshots[id] = snap;
|
|
10149
|
+
}
|
|
10150
|
+
}
|
|
10151
|
+
dragStateRef.current = {
|
|
10152
|
+
kind: "select-mode-item-click",
|
|
10153
|
+
id: hit.id,
|
|
10154
|
+
startWorld: { x: worldX, y: worldY },
|
|
10155
|
+
startScreen: { x: e.clientX, y: e.clientY },
|
|
10156
|
+
moveIds,
|
|
10157
|
+
moveSnapshots,
|
|
10158
|
+
...isAlreadySelected ? {} : { selectIdsOnDrag: [hit.id] }
|
|
10159
|
+
};
|
|
10160
|
+
captureInteractionPointer(e.currentTarget, e.pointerId);
|
|
10161
|
+
e.preventDefault();
|
|
10162
|
+
e.stopPropagation();
|
|
10163
|
+
return;
|
|
10164
|
+
}
|
|
9982
10165
|
if (e.shiftKey) {
|
|
9983
10166
|
const next = cur.includes(hit.id) ? cur.filter((id) => id !== hit.id) : [...cur, hit.id];
|
|
9984
10167
|
setEffectiveSelectedIdsRef.current(next);
|
|
@@ -10546,6 +10729,21 @@ var VectorViewport = react.forwardRef(
|
|
|
10546
10729
|
const change = onItemsChangeRef.current;
|
|
10547
10730
|
if (!change) return;
|
|
10548
10731
|
const { worldX, worldY } = screenToWorld(ev.clientX, ev.clientY);
|
|
10732
|
+
if (st.kind === "select-mode-item-click") {
|
|
10733
|
+
const screenDx = ev.clientX - st.startScreen.x;
|
|
10734
|
+
const screenDy = ev.clientY - st.startScreen.y;
|
|
10735
|
+
if (Math.hypot(screenDx, screenDy) <= TAP_PX) return;
|
|
10736
|
+
if (st.selectIdsOnDrag) {
|
|
10737
|
+
setEffectiveSelectedIdsRef.current(st.selectIdsOnDrag);
|
|
10738
|
+
}
|
|
10739
|
+
dragStateRef.current = {
|
|
10740
|
+
kind: "move",
|
|
10741
|
+
ids: st.moveIds,
|
|
10742
|
+
snapshots: st.moveSnapshots,
|
|
10743
|
+
startWorld: st.startWorld
|
|
10744
|
+
};
|
|
10745
|
+
return;
|
|
10746
|
+
}
|
|
10549
10747
|
if (st.kind === "move") {
|
|
10550
10748
|
const dx = worldX - st.startWorld.x;
|
|
10551
10749
|
const dy = worldY - st.startWorld.y;
|
|
@@ -10664,6 +10862,43 @@ var VectorViewport = react.forwardRef(
|
|
|
10664
10862
|
releaseInteractionPointer();
|
|
10665
10863
|
return;
|
|
10666
10864
|
}
|
|
10865
|
+
if (st.kind === "select-mode-item-click") {
|
|
10866
|
+
dragStateRef.current = { kind: "idle" };
|
|
10867
|
+
releaseInteractionPointer();
|
|
10868
|
+
if (ev.type === "pointercancel") return;
|
|
10869
|
+
const dx = ev.clientX - st.startScreen.x;
|
|
10870
|
+
const dy = ev.clientY - st.startScreen.y;
|
|
10871
|
+
if (Math.hypot(dx, dy) > TAP_PX) return;
|
|
10872
|
+
const item = itemsRef.current.find((candidate) => candidate.id === st.id) ?? resolvedItemsRef.current.find((candidate) => candidate.id === st.id);
|
|
10873
|
+
if (!item) return;
|
|
10874
|
+
const placement = findSelectModeItemClickPlacement(
|
|
10875
|
+
item,
|
|
10876
|
+
allCustomPlacementsRef.current
|
|
10877
|
+
);
|
|
10878
|
+
const onSelectModeItemClick = placement?.onSelectModeItemClick;
|
|
10879
|
+
if (!onSelectModeItemClick) return;
|
|
10880
|
+
const { worldX, worldY } = screenToWorld(ev.clientX, ev.clientY);
|
|
10881
|
+
onSelectModeItemClick({
|
|
10882
|
+
item,
|
|
10883
|
+
worldX,
|
|
10884
|
+
worldY,
|
|
10885
|
+
clientX: ev.clientX,
|
|
10886
|
+
clientY: ev.clientY,
|
|
10887
|
+
pointerType: ev.pointerType,
|
|
10888
|
+
shiftKey: ev.shiftKey,
|
|
10889
|
+
altKey: ev.altKey,
|
|
10890
|
+
metaKey: ev.metaKey,
|
|
10891
|
+
ctrlKey: ev.ctrlKey,
|
|
10892
|
+
items: itemsRef.current,
|
|
10893
|
+
updateItem: (next) => {
|
|
10894
|
+
onItemsChangeRef.current?.(
|
|
10895
|
+
replaceItem(itemsRef.current, item.id, next)
|
|
10896
|
+
);
|
|
10897
|
+
},
|
|
10898
|
+
setSelectedIds: (ids) => setEffectiveSelectedIdsRef.current(ids)
|
|
10899
|
+
});
|
|
10900
|
+
return;
|
|
10901
|
+
}
|
|
10667
10902
|
if (st.kind === "marquee") {
|
|
10668
10903
|
dragStateRef.current = { kind: "idle" };
|
|
10669
10904
|
releaseInteractionPointer();
|
|
@@ -10859,8 +11094,14 @@ var VectorViewport = react.forwardRef(
|
|
|
10859
11094
|
const id = createShapeId();
|
|
10860
11095
|
const pen = strokeStyleRef.current;
|
|
10861
11096
|
if (cpUp && st.tool === cpUp.toolId) {
|
|
10862
|
-
|
|
10863
|
-
|
|
11097
|
+
const item = tagCustomPlacementItem(
|
|
11098
|
+
cpUp.createItem({ id, bounds: br }),
|
|
11099
|
+
cpUp.toolId
|
|
11100
|
+
);
|
|
11101
|
+
change(itemsRef.current.concat(item));
|
|
11102
|
+
if (cpUp.selectAfterCreate !== false) {
|
|
11103
|
+
setEffectiveSelectedIdsRef.current([id]);
|
|
11104
|
+
}
|
|
10864
11105
|
return;
|
|
10865
11106
|
}
|
|
10866
11107
|
if (st.tool === "rect") {
|