@anuma/sdk 1.0.0 → 1.1.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 +54 -53
- package/dist/api/spec.cjs +30 -0
- package/dist/api/spec.d.mts +3 -0
- package/dist/api/spec.d.ts +3 -0
- package/dist/api/spec.mjs +12 -0
- package/dist/chunk-4ETHWSXJ.mjs +25 -0
- package/dist/client/index.cjs +2344 -0
- package/dist/client/index.d.mts +10368 -0
- package/dist/client/index.d.ts +10368 -0
- package/dist/client/index.mjs +2168 -0
- package/dist/clientConfig-WITO4T6L.mjs +12 -0
- package/dist/constants/index.cjs +30 -0
- package/dist/constants/index.d.mts +20 -0
- package/dist/constants/index.d.ts +20 -0
- package/dist/constants/index.mjs +5 -0
- package/dist/design/index.cjs +1523 -0
- package/dist/design/index.d.mts +561 -0
- package/dist/design/index.d.ts +561 -0
- package/dist/design/index.mjs +1468 -0
- package/dist/expo/chunk-DFVCS3QQ.mjs +246 -0
- package/dist/{react/chunk-MJJIYFAX.mjs → expo/chunk-GYKJOWEO.mjs} +13 -1
- package/dist/expo/chunk-PUKP5H4F.mjs +5278 -0
- package/dist/expo/chunk-Y7XT2VNQ.mjs +646 -0
- package/dist/expo/clientConfig-VPOGQRNP.mjs +12 -0
- package/dist/expo/consolidate-HEMQGOUD.mjs +131 -0
- package/dist/expo/index.cjs +16784 -6689
- package/dist/expo/index.d.mts +5746 -1474
- package/dist/expo/index.d.ts +5746 -1474
- package/dist/expo/index.mjs +9669 -6189
- package/dist/expo/recall-YRWAAU6K.mjs +8 -0
- package/dist/index.cjs +2992 -42
- package/dist/index.d.mts +7719 -1835
- package/dist/index.d.ts +7719 -1835
- package/dist/index.mjs +2859 -39
- package/dist/next/index.cjs +6 -2
- package/dist/next/index.d.mts +24 -1
- package/dist/next/index.d.ts +24 -1
- package/dist/next/index.mjs +6 -2
- package/dist/pii/transformers.cjs +126 -0
- package/dist/pii/transformers.d.mts +103 -0
- package/dist/pii/transformers.d.ts +103 -0
- package/dist/pii/transformers.mjs +90 -0
- package/dist/react/chunk-DFVCS3QQ.mjs +246 -0
- package/dist/{expo/chunk-MJJIYFAX.mjs → react/chunk-GYKJOWEO.mjs} +13 -1
- package/dist/react/chunk-Q2ANFWEP.mjs +5596 -0
- package/dist/react/chunk-TP6CJZ25.mjs +645 -0
- package/dist/react/clientConfig-VPOGQRNP.mjs +12 -0
- package/dist/react/consolidate-IJH7LD2T.mjs +131 -0
- package/dist/react/index.cjs +25453 -11029
- package/dist/react/index.d.mts +7388 -1406
- package/dist/react/index.d.ts +7388 -1406
- package/dist/react/index.mjs +16101 -8648
- package/dist/react/recall-YBC7YCNC.mjs +8 -0
- package/dist/server/chunk-FMGZTVXQ.mjs +62 -0
- package/dist/server/chunk-HPURS22B.mjs +19452 -0
- package/dist/server/chunk-PXN5Q3MD.mjs +643 -0
- package/dist/server/chunk-RCCKRTBI.mjs +230 -0
- package/dist/server/clientConfig-GMADOBPK.mjs +12 -0
- package/dist/server/consolidate-A2ZIX5WF.mjs +131 -0
- package/dist/server/index.cjs +32109 -0
- package/dist/server/index.d.mts +7310 -0
- package/dist/server/index.d.ts +7310 -0
- package/dist/server/index.mjs +11168 -0
- package/dist/server/recall-PFYFVYMS.mjs +8 -0
- package/dist/tools/index.cjs +12682 -34
- package/dist/tools/index.d.mts +2733 -18
- package/dist/tools/index.d.ts +2733 -18
- package/dist/tools/index.mjs +12565 -33
- package/dist/utils/index.cjs +165 -0
- package/dist/utils/index.d.mts +14 -0
- package/dist/utils/index.d.ts +14 -0
- package/dist/utils/index.mjs +145 -0
- package/dist/vercel/index.d.mts +0 -3
- package/dist/vercel/index.d.ts +0 -3
- package/package.json +127 -16
- package/dist/expo/chunk-LJYAMK62.mjs +0 -25
- package/dist/expo/chunk-PJCZO4BQ.mjs +0 -25
- package/dist/expo/clientConfig-2MI4KULF.mjs +0 -10
- package/dist/expo/clientConfig-QDAXFL3W.mjs +0 -10
- package/dist/expo/clientConfig-R4IOW7I2.mjs +0 -10
- package/dist/react/chunk-LJYAMK62.mjs +0 -25
- package/dist/react/chunk-PJCZO4BQ.mjs +0 -25
- package/dist/react/clientConfig-2MI4KULF.mjs +0 -10
- package/dist/react/clientConfig-QDAXFL3W.mjs +0 -10
- package/dist/react/clientConfig-R4IOW7I2.mjs +0 -10
- package/dist/tools/chunk-KDFGY4SK.mjs +0 -13
- package/dist/tools/clientConfig-RMDOT5YM.mjs +0 -10
|
@@ -0,0 +1,1523 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/design/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
CanvasOverlays: () => CanvasOverlays,
|
|
24
|
+
NON_SELECTABLE_TAGS: () => NON_SELECTABLE_TAGS,
|
|
25
|
+
RESIZE_HANDLE_CURSORS: () => RESIZE_HANDLE_CURSORS,
|
|
26
|
+
RESIZE_HANDLE_DIRS: () => RESIZE_HANDLE_DIRS,
|
|
27
|
+
canMultiDrag: () => canMultiDrag,
|
|
28
|
+
commitDrop: () => commitDrop,
|
|
29
|
+
commitMultiDrag: () => commitMultiDrag,
|
|
30
|
+
commitResize: () => commitResize,
|
|
31
|
+
commitRotation: () => commitRotation,
|
|
32
|
+
commitTextChange: () => commitTextChange,
|
|
33
|
+
computeFlexDropTarget: () => computeFlexDropTarget,
|
|
34
|
+
computeSnap: () => computeSnap,
|
|
35
|
+
containerLayoutMode: () => containerLayoutMode,
|
|
36
|
+
findContainerAt: () => findContainerAt,
|
|
37
|
+
findElementIdFromEvent: () => findElementIdFromEvent,
|
|
38
|
+
findElementsInRect: () => findElementsInRect,
|
|
39
|
+
findResizeHandleFromEvent: () => findResizeHandleFromEvent,
|
|
40
|
+
findRotateHandleFromEvent: () => findRotateHandleFromEvent,
|
|
41
|
+
getLayoutModeOf: () => getLayoutModeOf,
|
|
42
|
+
getStageRelativeBounds: () => getStageRelativeBounds,
|
|
43
|
+
getTextContent: () => getTextContent,
|
|
44
|
+
isFlexChild: () => isFlexChild,
|
|
45
|
+
resolveDropTarget: () => resolveDropTarget,
|
|
46
|
+
unionBounds: () => unionBounds,
|
|
47
|
+
useCanvasGestures: () => useCanvasGestures,
|
|
48
|
+
useCanvasKeyboard: () => useCanvasKeyboard,
|
|
49
|
+
useDeckHistory: () => useDeckHistory,
|
|
50
|
+
useMultiSelectionBounds: () => useMultiSelectionBounds,
|
|
51
|
+
useSelectionBounds: () => useSelectionBounds
|
|
52
|
+
});
|
|
53
|
+
module.exports = __toCommonJS(index_exports);
|
|
54
|
+
|
|
55
|
+
// src/tools/slides/jsx.ts
|
|
56
|
+
var import_parser = require("@babel/parser");
|
|
57
|
+
var ANUMA_TAGS = [
|
|
58
|
+
"Deck",
|
|
59
|
+
"Slide",
|
|
60
|
+
"Screen",
|
|
61
|
+
"Group",
|
|
62
|
+
"Text",
|
|
63
|
+
"Span",
|
|
64
|
+
"Image",
|
|
65
|
+
"Rect",
|
|
66
|
+
"Circle",
|
|
67
|
+
"Line",
|
|
68
|
+
"Icon"
|
|
69
|
+
];
|
|
70
|
+
var ANUMA_TAG_SET = new Set(ANUMA_TAGS);
|
|
71
|
+
var STYLE_ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
72
|
+
"alignItems",
|
|
73
|
+
"alignSelf",
|
|
74
|
+
"background",
|
|
75
|
+
"backgroundColor",
|
|
76
|
+
"backgroundImage",
|
|
77
|
+
"backgroundPosition",
|
|
78
|
+
"backgroundRepeat",
|
|
79
|
+
"backgroundSize",
|
|
80
|
+
"border",
|
|
81
|
+
"borderColor",
|
|
82
|
+
"borderRadius",
|
|
83
|
+
"borderStyle",
|
|
84
|
+
"borderWidth",
|
|
85
|
+
"bottom",
|
|
86
|
+
"boxShadow",
|
|
87
|
+
"boxSizing",
|
|
88
|
+
"color",
|
|
89
|
+
"display",
|
|
90
|
+
"filter",
|
|
91
|
+
"flex",
|
|
92
|
+
"flexBasis",
|
|
93
|
+
"flexDirection",
|
|
94
|
+
"flexGrow",
|
|
95
|
+
"flexShrink",
|
|
96
|
+
"flexWrap",
|
|
97
|
+
"fontFamily",
|
|
98
|
+
"fontSize",
|
|
99
|
+
"fontStyle",
|
|
100
|
+
"fontVariant",
|
|
101
|
+
"fontWeight",
|
|
102
|
+
"gap",
|
|
103
|
+
"height",
|
|
104
|
+
"justifyContent",
|
|
105
|
+
"left",
|
|
106
|
+
"letterSpacing",
|
|
107
|
+
"lineHeight",
|
|
108
|
+
"listStyle",
|
|
109
|
+
"margin",
|
|
110
|
+
"marginBottom",
|
|
111
|
+
"marginLeft",
|
|
112
|
+
"marginRight",
|
|
113
|
+
"marginTop",
|
|
114
|
+
"maxHeight",
|
|
115
|
+
"maxWidth",
|
|
116
|
+
"minHeight",
|
|
117
|
+
"minWidth",
|
|
118
|
+
"objectFit",
|
|
119
|
+
"objectPosition",
|
|
120
|
+
"opacity",
|
|
121
|
+
"overflow",
|
|
122
|
+
"overflowX",
|
|
123
|
+
"overflowY",
|
|
124
|
+
"padding",
|
|
125
|
+
"paddingBottom",
|
|
126
|
+
"paddingLeft",
|
|
127
|
+
"paddingRight",
|
|
128
|
+
"paddingTop",
|
|
129
|
+
"position",
|
|
130
|
+
"right",
|
|
131
|
+
"textAlign",
|
|
132
|
+
"textDecoration",
|
|
133
|
+
"textTransform",
|
|
134
|
+
"top",
|
|
135
|
+
"transform",
|
|
136
|
+
"transformOrigin",
|
|
137
|
+
"verticalAlign",
|
|
138
|
+
"visibility",
|
|
139
|
+
"whiteSpace",
|
|
140
|
+
"width",
|
|
141
|
+
"wordBreak",
|
|
142
|
+
"wordSpacing",
|
|
143
|
+
"zIndex"
|
|
144
|
+
]);
|
|
145
|
+
var STYLE_KEY_LOWER_TO_CAMEL = new Map(
|
|
146
|
+
Array.from(STYLE_ALLOWED_KEYS, (k) => [k.toLowerCase(), k])
|
|
147
|
+
);
|
|
148
|
+
function getId(node) {
|
|
149
|
+
const id = node.attrs.id;
|
|
150
|
+
return typeof id === "string" ? id : void 0;
|
|
151
|
+
}
|
|
152
|
+
function walk(root, visitor) {
|
|
153
|
+
function visit(node, parent) {
|
|
154
|
+
const cont = visitor(node, parent);
|
|
155
|
+
if (cont === false) return;
|
|
156
|
+
for (const child of node.children) {
|
|
157
|
+
if (typeof child !== "string") visit(child, node);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
visit(root, null);
|
|
161
|
+
}
|
|
162
|
+
function findById(root, id) {
|
|
163
|
+
let found = null;
|
|
164
|
+
walk(root, (node) => {
|
|
165
|
+
if (getId(node) === id) {
|
|
166
|
+
found = node;
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
return void 0;
|
|
170
|
+
});
|
|
171
|
+
return found;
|
|
172
|
+
}
|
|
173
|
+
function findParentOfId(root, id) {
|
|
174
|
+
let parent = null;
|
|
175
|
+
walk(root, (node, p) => {
|
|
176
|
+
if (getId(node) === id) {
|
|
177
|
+
parent = p;
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
return void 0;
|
|
181
|
+
});
|
|
182
|
+
return parent;
|
|
183
|
+
}
|
|
184
|
+
function removeById(root, id) {
|
|
185
|
+
let removed = false;
|
|
186
|
+
walk(root, (node) => {
|
|
187
|
+
if (removed) return false;
|
|
188
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
189
|
+
const child = node.children[i];
|
|
190
|
+
if (typeof child !== "string" && getId(child) === id) {
|
|
191
|
+
node.children.splice(i, 1);
|
|
192
|
+
removed = true;
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return void 0;
|
|
197
|
+
});
|
|
198
|
+
return removed;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// src/design/dragLogic.ts
|
|
202
|
+
var NON_SELECTABLE_TAGS = /* @__PURE__ */ new Set(["Deck", "Slide", "Screen"]);
|
|
203
|
+
function getLayoutModeOf(deck, childId) {
|
|
204
|
+
const parent = findParentOfId(deck, childId);
|
|
205
|
+
if (!parent) return "absolute";
|
|
206
|
+
const layout = parent.attrs.layout;
|
|
207
|
+
if (layout === "row") return "flex-row";
|
|
208
|
+
if (layout === "column") return "flex-column";
|
|
209
|
+
return "absolute";
|
|
210
|
+
}
|
|
211
|
+
function isFlexChild(deck, childId) {
|
|
212
|
+
const mode = getLayoutModeOf(deck, childId);
|
|
213
|
+
return mode === "flex-row" || mode === "flex-column";
|
|
214
|
+
}
|
|
215
|
+
function clone(deck) {
|
|
216
|
+
return structuredClone(deck);
|
|
217
|
+
}
|
|
218
|
+
function containerLayoutMode(node) {
|
|
219
|
+
if (node.attrs.layout === "row") return "flex-row";
|
|
220
|
+
if (node.attrs.layout === "column") return "flex-column";
|
|
221
|
+
return "absolute";
|
|
222
|
+
}
|
|
223
|
+
function commitDrop(deck, elementId, target) {
|
|
224
|
+
const elOrig = findById(deck, elementId);
|
|
225
|
+
const parentOrig = findParentOfId(deck, elementId);
|
|
226
|
+
if (!elOrig || !parentOrig) return deck;
|
|
227
|
+
if (!findById(deck, target.parentId)) return deck;
|
|
228
|
+
const sameParentOrig = getId2(parentOrig) === target.parentId;
|
|
229
|
+
if (sameParentOrig) {
|
|
230
|
+
if (target.kind === "absolute") {
|
|
231
|
+
if (elOrig.attrs.x === target.x && elOrig.attrs.y === target.y && elOrig.attrs.w === target.w && elOrig.attrs.h === target.h) {
|
|
232
|
+
return deck;
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
const currentIdx = parentOrig.children.findIndex(
|
|
236
|
+
(c) => typeof c !== "string" && getId2(c) === elementId
|
|
237
|
+
);
|
|
238
|
+
if (target.dropIndex === currentIdx && elOrig.attrs.x === void 0 && elOrig.attrs.y === void 0) {
|
|
239
|
+
return deck;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
const next = clone(deck);
|
|
244
|
+
const el = findById(next, elementId);
|
|
245
|
+
const currentParent = findParentOfId(next, elementId);
|
|
246
|
+
const newParent = findById(next, target.parentId);
|
|
247
|
+
if (!el || !currentParent || !newParent) return deck;
|
|
248
|
+
const currentParentId = getId2(currentParent);
|
|
249
|
+
const sameParent = currentParentId === target.parentId;
|
|
250
|
+
if (target.kind === "absolute") {
|
|
251
|
+
el.attrs.x = target.x;
|
|
252
|
+
el.attrs.y = target.y;
|
|
253
|
+
el.attrs.w = target.w;
|
|
254
|
+
el.attrs.h = target.h;
|
|
255
|
+
if (sameParent) {
|
|
256
|
+
return next;
|
|
257
|
+
}
|
|
258
|
+
} else {
|
|
259
|
+
delete el.attrs.x;
|
|
260
|
+
delete el.attrs.y;
|
|
261
|
+
}
|
|
262
|
+
const oldIdx = currentParent.children.findIndex(
|
|
263
|
+
(c) => typeof c !== "string" && getId2(c) === elementId
|
|
264
|
+
);
|
|
265
|
+
if (oldIdx === -1) return deck;
|
|
266
|
+
currentParent.children.splice(oldIdx, 1);
|
|
267
|
+
if (target.kind === "flex") {
|
|
268
|
+
const np = findById(next, target.parentId);
|
|
269
|
+
if (!np) return deck;
|
|
270
|
+
const clamped = Math.max(0, Math.min(target.dropIndex, np.children.length));
|
|
271
|
+
np.children.splice(clamped, 0, el);
|
|
272
|
+
} else {
|
|
273
|
+
const np = findById(next, target.parentId);
|
|
274
|
+
if (!np) return deck;
|
|
275
|
+
np.children.push(el);
|
|
276
|
+
}
|
|
277
|
+
return next;
|
|
278
|
+
}
|
|
279
|
+
function getId2(node) {
|
|
280
|
+
return typeof node.attrs.id === "string" ? node.attrs.id : void 0;
|
|
281
|
+
}
|
|
282
|
+
var RESIZE_HANDLE_DIRS = {
|
|
283
|
+
nw: { dx: -1, dy: -1 },
|
|
284
|
+
n: { dx: 0, dy: -1 },
|
|
285
|
+
ne: { dx: 1, dy: -1 },
|
|
286
|
+
e: { dx: 1, dy: 0 },
|
|
287
|
+
se: { dx: 1, dy: 1 },
|
|
288
|
+
s: { dx: 0, dy: 1 },
|
|
289
|
+
sw: { dx: -1, dy: 1 },
|
|
290
|
+
w: { dx: -1, dy: 0 }
|
|
291
|
+
};
|
|
292
|
+
var RESIZE_HANDLE_CURSORS = {
|
|
293
|
+
nw: "nwse-resize",
|
|
294
|
+
se: "nwse-resize",
|
|
295
|
+
ne: "nesw-resize",
|
|
296
|
+
sw: "nesw-resize",
|
|
297
|
+
n: "ns-resize",
|
|
298
|
+
s: "ns-resize",
|
|
299
|
+
e: "ew-resize",
|
|
300
|
+
w: "ew-resize"
|
|
301
|
+
};
|
|
302
|
+
function commitResize(deck, elementId, bounds) {
|
|
303
|
+
const next = clone(deck);
|
|
304
|
+
const el = findById(next, elementId);
|
|
305
|
+
if (!el) return deck;
|
|
306
|
+
const parent = findParentOfId(next, elementId);
|
|
307
|
+
const layout = parent?.attrs.layout;
|
|
308
|
+
if (layout === "column") {
|
|
309
|
+
el.attrs.h = bounds.h;
|
|
310
|
+
} else if (layout === "row") {
|
|
311
|
+
el.attrs.w = bounds.w;
|
|
312
|
+
} else {
|
|
313
|
+
el.attrs.x = bounds.x;
|
|
314
|
+
el.attrs.y = bounds.y;
|
|
315
|
+
el.attrs.w = bounds.w;
|
|
316
|
+
el.attrs.h = bounds.h;
|
|
317
|
+
}
|
|
318
|
+
return next;
|
|
319
|
+
}
|
|
320
|
+
function commitRotation(deck, elementId, rotation) {
|
|
321
|
+
const next = clone(deck);
|
|
322
|
+
const el = findById(next, elementId);
|
|
323
|
+
if (!el) return deck;
|
|
324
|
+
el.attrs.rotation = rotation;
|
|
325
|
+
return next;
|
|
326
|
+
}
|
|
327
|
+
function canMultiDrag(deck, ids) {
|
|
328
|
+
if (ids.length <= 1) return false;
|
|
329
|
+
const firstId = ids[0];
|
|
330
|
+
if (firstId === void 0) return false;
|
|
331
|
+
const firstParent = findParentOfId(deck, firstId);
|
|
332
|
+
if (!firstParent) return false;
|
|
333
|
+
if (firstParent.attrs.layout === "row" || firstParent.attrs.layout === "column") return false;
|
|
334
|
+
for (const id of ids) {
|
|
335
|
+
if (findParentOfId(deck, id) !== firstParent) return false;
|
|
336
|
+
}
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
function commitMultiDrag(deck, elementIds, delta) {
|
|
340
|
+
if (elementIds.length === 0) return deck;
|
|
341
|
+
if (delta.x === 0 && delta.y === 0) return deck;
|
|
342
|
+
const next = clone(deck);
|
|
343
|
+
let any = false;
|
|
344
|
+
for (const id of elementIds) {
|
|
345
|
+
const el = findById(next, id);
|
|
346
|
+
if (!el) continue;
|
|
347
|
+
if (typeof el.attrs.x !== "number" || typeof el.attrs.y !== "number") continue;
|
|
348
|
+
el.attrs.x += delta.x;
|
|
349
|
+
el.attrs.y += delta.y;
|
|
350
|
+
any = true;
|
|
351
|
+
}
|
|
352
|
+
return any ? next : deck;
|
|
353
|
+
}
|
|
354
|
+
function commitTextChange(deck, elementId, newText) {
|
|
355
|
+
const existing = findById(deck, elementId);
|
|
356
|
+
if (!existing) return deck;
|
|
357
|
+
if (getTextContent(existing) === newText) return deck;
|
|
358
|
+
const next = clone(deck);
|
|
359
|
+
const el = findById(next, elementId);
|
|
360
|
+
if (!el) return deck;
|
|
361
|
+
el.children = [newText];
|
|
362
|
+
return next;
|
|
363
|
+
}
|
|
364
|
+
function getTextContent(node) {
|
|
365
|
+
return node.children.filter((c) => typeof c === "string").join("");
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/design/dropTarget.ts
|
|
369
|
+
function computeFlexDropTarget(stage, parentId, draggedId, axis, pointerClient, scale) {
|
|
370
|
+
const parentEl = stage.querySelector(`[data-id="${parentId}"]`);
|
|
371
|
+
if (!parentEl) return { index: 0, bounds: null };
|
|
372
|
+
const stageRect = stage.getBoundingClientRect();
|
|
373
|
+
const siblings = Array.from(parentEl.children).filter(
|
|
374
|
+
(el) => el instanceof HTMLElement && typeof el.dataset.id === "string" && el.dataset.id !== draggedId
|
|
375
|
+
);
|
|
376
|
+
if (siblings.length === 0) {
|
|
377
|
+
const pr = parentEl.getBoundingClientRect();
|
|
378
|
+
return {
|
|
379
|
+
index: 0,
|
|
380
|
+
bounds: {
|
|
381
|
+
x: (pr.left - stageRect.left) / scale,
|
|
382
|
+
y: (pr.top - stageRect.top) / scale,
|
|
383
|
+
w: pr.width / scale,
|
|
384
|
+
h: 2
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
const siblingRects = siblings.map((el) => el.getBoundingClientRect());
|
|
389
|
+
let dropIndex = siblings.length;
|
|
390
|
+
for (let i = 0; i < siblingRects.length; i++) {
|
|
391
|
+
const r = siblingRects[i];
|
|
392
|
+
const center = axis === "column" ? r.top + r.height / 2 : r.left + r.width / 2;
|
|
393
|
+
const p = axis === "column" ? pointerClient.y : pointerClient.x;
|
|
394
|
+
if (p < center) {
|
|
395
|
+
dropIndex = i;
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
const parentRect = parentEl.getBoundingClientRect();
|
|
400
|
+
const first = siblingRects[0];
|
|
401
|
+
const last = siblingRects[siblingRects.length - 1];
|
|
402
|
+
let lineClient;
|
|
403
|
+
if (axis === "column") {
|
|
404
|
+
let yClient;
|
|
405
|
+
if (dropIndex === 0) yClient = first.top;
|
|
406
|
+
else if (dropIndex === siblingRects.length) yClient = last.bottom;
|
|
407
|
+
else {
|
|
408
|
+
const a = siblingRects[dropIndex - 1];
|
|
409
|
+
const b = siblingRects[dropIndex];
|
|
410
|
+
yClient = (a.bottom + b.top) / 2;
|
|
411
|
+
}
|
|
412
|
+
lineClient = {
|
|
413
|
+
left: parentRect.left,
|
|
414
|
+
top: yClient,
|
|
415
|
+
width: parentRect.width,
|
|
416
|
+
height: 2
|
|
417
|
+
};
|
|
418
|
+
} else {
|
|
419
|
+
let xClient;
|
|
420
|
+
if (dropIndex === 0) xClient = first.left;
|
|
421
|
+
else if (dropIndex === siblingRects.length) xClient = last.right;
|
|
422
|
+
else {
|
|
423
|
+
const a = siblingRects[dropIndex - 1];
|
|
424
|
+
const b = siblingRects[dropIndex];
|
|
425
|
+
xClient = (a.right + b.left) / 2;
|
|
426
|
+
}
|
|
427
|
+
lineClient = {
|
|
428
|
+
left: xClient,
|
|
429
|
+
top: parentRect.top,
|
|
430
|
+
width: 2,
|
|
431
|
+
height: parentRect.height
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
return {
|
|
435
|
+
index: dropIndex,
|
|
436
|
+
bounds: {
|
|
437
|
+
x: (lineClient.left - stageRect.left) / scale,
|
|
438
|
+
y: (lineClient.top - stageRect.top) / scale,
|
|
439
|
+
w: lineClient.width / scale,
|
|
440
|
+
h: lineClient.height / scale
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
function findContainerAt(stage, draggedId, pointerClient) {
|
|
445
|
+
if (typeof document === "undefined") return null;
|
|
446
|
+
const draggedEl = stage.querySelector(`[data-id="${draggedId}"]`);
|
|
447
|
+
const candidates = document.elementsFromPoint(pointerClient.x, pointerClient.y);
|
|
448
|
+
for (const el of candidates) {
|
|
449
|
+
if (!(el instanceof HTMLElement)) continue;
|
|
450
|
+
if (!stage.contains(el)) continue;
|
|
451
|
+
if (draggedEl && (el === draggedEl || draggedEl.contains(el))) continue;
|
|
452
|
+
const tag = el.dataset.anumaTag;
|
|
453
|
+
if (tag === "Slide" || tag === "Screen" || tag === "Group") return el;
|
|
454
|
+
}
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
function resolveDropTarget(deck, stage, draggedId, pointerClient, scale) {
|
|
458
|
+
const containerEl = findContainerAt(stage, draggedId, pointerClient);
|
|
459
|
+
if (!containerEl) return null;
|
|
460
|
+
const parentId = containerEl.dataset.id;
|
|
461
|
+
if (!parentId) return null;
|
|
462
|
+
const parentNode = findById(deck, parentId);
|
|
463
|
+
if (!parentNode) return null;
|
|
464
|
+
const layout = parentNode.attrs.layout;
|
|
465
|
+
if (layout === "row" || layout === "column") {
|
|
466
|
+
const t = computeFlexDropTarget(stage, parentId, draggedId, layout, pointerClient, scale);
|
|
467
|
+
return {
|
|
468
|
+
drop: { kind: "flex", parentId, dropIndex: t.index },
|
|
469
|
+
indicatorBounds: t.bounds
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
const draggedEl = stage.querySelector(`[data-id="${draggedId}"]`);
|
|
473
|
+
if (!draggedEl) return null;
|
|
474
|
+
const dr = draggedEl.getBoundingClientRect();
|
|
475
|
+
const cr = containerEl.getBoundingClientRect();
|
|
476
|
+
const draggedNode = findById(deck, draggedId);
|
|
477
|
+
const w = typeof draggedNode?.attrs.w === "number" ? draggedNode.attrs.w : dr.width / scale;
|
|
478
|
+
const h = typeof draggedNode?.attrs.h === "number" ? draggedNode.attrs.h : dr.height / scale;
|
|
479
|
+
const centerClientX = dr.left + dr.width / 2;
|
|
480
|
+
const centerClientY = dr.top + dr.height / 2;
|
|
481
|
+
const topLeftClientX = centerClientX - w * scale / 2;
|
|
482
|
+
const topLeftClientY = centerClientY - h * scale / 2;
|
|
483
|
+
return {
|
|
484
|
+
drop: {
|
|
485
|
+
kind: "absolute",
|
|
486
|
+
parentId,
|
|
487
|
+
x: (topLeftClientX - cr.left) / scale,
|
|
488
|
+
y: (topLeftClientY - cr.top) / scale,
|
|
489
|
+
w,
|
|
490
|
+
h
|
|
491
|
+
},
|
|
492
|
+
indicatorBounds: null
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// src/design/eventHelpers.ts
|
|
497
|
+
function findElementIdFromEvent(e, root) {
|
|
498
|
+
const native = "nativeEvent" in e ? e.nativeEvent : e;
|
|
499
|
+
const path = typeof native.composedPath === "function" ? native.composedPath() : [];
|
|
500
|
+
for (const node of path) {
|
|
501
|
+
if (root && node === root) break;
|
|
502
|
+
if (!(node instanceof HTMLElement)) continue;
|
|
503
|
+
const id = node.dataset.id;
|
|
504
|
+
const tag = node.dataset.anumaTag;
|
|
505
|
+
if (id && tag && !NON_SELECTABLE_TAGS.has(tag)) return id;
|
|
506
|
+
}
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
var RESIZE_HANDLE_NAMES = /* @__PURE__ */ new Set(["nw", "n", "ne", "e", "se", "s", "sw", "w"]);
|
|
510
|
+
function findResizeHandleFromEvent(e) {
|
|
511
|
+
const path = typeof e.composedPath === "function" ? e.composedPath() : [];
|
|
512
|
+
for (const node of path) {
|
|
513
|
+
if (!(node instanceof HTMLElement)) continue;
|
|
514
|
+
const dir = node.dataset.resizeHandle;
|
|
515
|
+
if (dir && RESIZE_HANDLE_NAMES.has(dir)) return dir;
|
|
516
|
+
}
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
function findRotateHandleFromEvent(e) {
|
|
520
|
+
const path = typeof e.composedPath === "function" ? e.composedPath() : [];
|
|
521
|
+
for (const node of path) {
|
|
522
|
+
if (!(node instanceof HTMLElement)) continue;
|
|
523
|
+
if (node.dataset.rotateHandle === "true") return true;
|
|
524
|
+
}
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
function findElementsInRect(stage, rect) {
|
|
528
|
+
const ids = [];
|
|
529
|
+
const els = stage.querySelectorAll("[data-id]");
|
|
530
|
+
for (const el of els) {
|
|
531
|
+
const tag = el.dataset.anumaTag;
|
|
532
|
+
if (tag && NON_SELECTABLE_TAGS.has(tag)) continue;
|
|
533
|
+
const id = el.dataset.id;
|
|
534
|
+
if (!id) continue;
|
|
535
|
+
const er = el.getBoundingClientRect();
|
|
536
|
+
const cx = er.left + er.width / 2;
|
|
537
|
+
const cy = er.top + er.height / 2;
|
|
538
|
+
if (cx >= rect.left && cx <= rect.right && cy >= rect.top && cy <= rect.bottom) {
|
|
539
|
+
ids.push(id);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
return ids;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// src/design/overlays.tsx
|
|
546
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
547
|
+
var HANDLE_SIZE = 10;
|
|
548
|
+
var ROTATE_HANDLE_OFFSET = 28;
|
|
549
|
+
var PRIMARY_COLOR = "#2563eb";
|
|
550
|
+
var SNAP_COLOR = "#ec4899";
|
|
551
|
+
function CanvasOverlays({
|
|
552
|
+
gesture,
|
|
553
|
+
selectedId,
|
|
554
|
+
selectedIds,
|
|
555
|
+
selectionBounds,
|
|
556
|
+
multiBounds,
|
|
557
|
+
editingId,
|
|
558
|
+
deck,
|
|
559
|
+
scale
|
|
560
|
+
}) {
|
|
561
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
562
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
563
|
+
SelectionWithHandles,
|
|
564
|
+
{
|
|
565
|
+
selectedId,
|
|
566
|
+
selectionBounds,
|
|
567
|
+
gesture,
|
|
568
|
+
deck,
|
|
569
|
+
scale,
|
|
570
|
+
editingId
|
|
571
|
+
}
|
|
572
|
+
),
|
|
573
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
574
|
+
MultiSelectionOutlines,
|
|
575
|
+
{
|
|
576
|
+
bounds: multiBounds,
|
|
577
|
+
scale,
|
|
578
|
+
visible: selectedIds.size > 1 && !editingId && !isDragOrRotate(gesture)
|
|
579
|
+
}
|
|
580
|
+
),
|
|
581
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MarqueeRect, { gesture }),
|
|
582
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SnapGuideLines, { gesture, scale }),
|
|
583
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FlexDropIndicator, { gesture, scale })
|
|
584
|
+
] });
|
|
585
|
+
}
|
|
586
|
+
function isDragOrRotate(gesture) {
|
|
587
|
+
return gesture?.phase === "dragging" || gesture?.phase === "rotating";
|
|
588
|
+
}
|
|
589
|
+
function MultiSelectionOutlines({
|
|
590
|
+
bounds,
|
|
591
|
+
scale,
|
|
592
|
+
visible
|
|
593
|
+
}) {
|
|
594
|
+
if (!visible) return null;
|
|
595
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: bounds.map((b) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
596
|
+
"div",
|
|
597
|
+
{
|
|
598
|
+
style: {
|
|
599
|
+
position: "absolute",
|
|
600
|
+
left: b.x * scale,
|
|
601
|
+
top: b.y * scale,
|
|
602
|
+
width: b.w * scale,
|
|
603
|
+
height: b.h * scale,
|
|
604
|
+
transform: b.rotation !== 0 ? `rotate(${b.rotation}deg)` : void 0,
|
|
605
|
+
transformOrigin: "50% 50%",
|
|
606
|
+
border: `1.5px solid ${PRIMARY_COLOR}`,
|
|
607
|
+
boxSizing: "border-box",
|
|
608
|
+
pointerEvents: "none"
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
b.id
|
|
612
|
+
)) });
|
|
613
|
+
}
|
|
614
|
+
function MarqueeRect({ gesture }) {
|
|
615
|
+
if (gesture?.phase !== "marquee") return null;
|
|
616
|
+
const left = Math.min(gesture.startClient.x, gesture.currentClient.x);
|
|
617
|
+
const top = Math.min(gesture.startClient.y, gesture.currentClient.y);
|
|
618
|
+
const width = Math.abs(gesture.currentClient.x - gesture.startClient.x);
|
|
619
|
+
const height = Math.abs(gesture.currentClient.y - gesture.startClient.y);
|
|
620
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
621
|
+
"div",
|
|
622
|
+
{
|
|
623
|
+
style: {
|
|
624
|
+
position: "fixed",
|
|
625
|
+
left,
|
|
626
|
+
top,
|
|
627
|
+
width,
|
|
628
|
+
height,
|
|
629
|
+
border: `1px solid ${PRIMARY_COLOR}`,
|
|
630
|
+
background: "rgba(37, 99, 235, 0.08)",
|
|
631
|
+
pointerEvents: "none"
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
function SnapGuideLines({ gesture, scale }) {
|
|
637
|
+
if (gesture?.phase !== "dragging") return null;
|
|
638
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: gesture.guides.map((g, i) => {
|
|
639
|
+
const isVertical = g.axis === "x";
|
|
640
|
+
const style = isVertical ? {
|
|
641
|
+
position: "absolute",
|
|
642
|
+
left: g.position * scale - 0.5,
|
|
643
|
+
top: g.start * scale,
|
|
644
|
+
width: 1,
|
|
645
|
+
height: (g.end - g.start) * scale,
|
|
646
|
+
background: SNAP_COLOR,
|
|
647
|
+
pointerEvents: "none"
|
|
648
|
+
} : {
|
|
649
|
+
position: "absolute",
|
|
650
|
+
left: g.start * scale,
|
|
651
|
+
top: g.position * scale - 0.5,
|
|
652
|
+
width: (g.end - g.start) * scale,
|
|
653
|
+
height: 1,
|
|
654
|
+
background: SNAP_COLOR,
|
|
655
|
+
pointerEvents: "none"
|
|
656
|
+
};
|
|
657
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style }, i);
|
|
658
|
+
}) });
|
|
659
|
+
}
|
|
660
|
+
function FlexDropIndicator({ gesture, scale }) {
|
|
661
|
+
if (gesture?.phase !== "dragging" || !gesture.indicatorBounds) return null;
|
|
662
|
+
const { x, y, w, h } = gesture.indicatorBounds;
|
|
663
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
664
|
+
"div",
|
|
665
|
+
{
|
|
666
|
+
style: {
|
|
667
|
+
position: "absolute",
|
|
668
|
+
left: x * scale,
|
|
669
|
+
top: y * scale,
|
|
670
|
+
width: Math.max(w * scale, 2),
|
|
671
|
+
height: Math.max(h * scale, 2),
|
|
672
|
+
background: PRIMARY_COLOR,
|
|
673
|
+
pointerEvents: "none",
|
|
674
|
+
borderRadius: 1
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
function visibleResizeHandles(deck, selectedId) {
|
|
680
|
+
const mode = getLayoutModeOf(deck, selectedId);
|
|
681
|
+
if (mode === "absolute") return ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
|
|
682
|
+
if (mode === "flex-column") return ["s"];
|
|
683
|
+
return ["e"];
|
|
684
|
+
}
|
|
685
|
+
function SelectionWithHandles({
|
|
686
|
+
selectedId,
|
|
687
|
+
selectionBounds,
|
|
688
|
+
gesture,
|
|
689
|
+
deck,
|
|
690
|
+
scale,
|
|
691
|
+
editingId
|
|
692
|
+
}) {
|
|
693
|
+
if (!selectedId || editingId || isDragOrRotate(gesture)) return null;
|
|
694
|
+
const bounds = gesture?.phase === "resizing" ? gesture.currentBounds : selectionBounds;
|
|
695
|
+
if (!bounds) return null;
|
|
696
|
+
const rotation = gesture?.phase === "resizing" ? gesture.rotation : selectionBounds?.rotation ?? 0;
|
|
697
|
+
const visibleHandles = visibleResizeHandles(deck, selectedId);
|
|
698
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
699
|
+
"div",
|
|
700
|
+
{
|
|
701
|
+
style: {
|
|
702
|
+
position: "absolute",
|
|
703
|
+
left: bounds.x * scale,
|
|
704
|
+
top: bounds.y * scale,
|
|
705
|
+
width: bounds.w * scale,
|
|
706
|
+
height: bounds.h * scale,
|
|
707
|
+
transform: rotation !== 0 ? `rotate(${rotation}deg)` : void 0,
|
|
708
|
+
transformOrigin: "50% 50%",
|
|
709
|
+
// Outline frame is non-interactive; only the handles below are.
|
|
710
|
+
// Lets clicks fall through to the element body for drag selection.
|
|
711
|
+
pointerEvents: "none"
|
|
712
|
+
},
|
|
713
|
+
children: [
|
|
714
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
715
|
+
"div",
|
|
716
|
+
{
|
|
717
|
+
style: {
|
|
718
|
+
position: "absolute",
|
|
719
|
+
inset: 0,
|
|
720
|
+
border: `2px solid ${PRIMARY_COLOR}`,
|
|
721
|
+
boxSizing: "border-box"
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
),
|
|
725
|
+
visibleHandles.map((handle) => {
|
|
726
|
+
const dir = RESIZE_HANDLE_DIRS[handle];
|
|
727
|
+
const cxLocal = bounds.w * (dir.dx + 1) / 2;
|
|
728
|
+
const cyLocal = bounds.h * (dir.dy + 1) / 2;
|
|
729
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
730
|
+
"div",
|
|
731
|
+
{
|
|
732
|
+
"data-resize-handle": handle,
|
|
733
|
+
style: {
|
|
734
|
+
position: "absolute",
|
|
735
|
+
left: cxLocal * scale - HANDLE_SIZE / 2,
|
|
736
|
+
top: cyLocal * scale - HANDLE_SIZE / 2,
|
|
737
|
+
width: HANDLE_SIZE,
|
|
738
|
+
height: HANDLE_SIZE,
|
|
739
|
+
background: "#fff",
|
|
740
|
+
border: `1.5px solid ${PRIMARY_COLOR}`,
|
|
741
|
+
borderRadius: "50%",
|
|
742
|
+
cursor: RESIZE_HANDLE_CURSORS[handle],
|
|
743
|
+
boxSizing: "border-box",
|
|
744
|
+
pointerEvents: "auto"
|
|
745
|
+
}
|
|
746
|
+
},
|
|
747
|
+
handle
|
|
748
|
+
);
|
|
749
|
+
}),
|
|
750
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
751
|
+
"div",
|
|
752
|
+
{
|
|
753
|
+
"data-rotate-handle": "true",
|
|
754
|
+
style: {
|
|
755
|
+
position: "absolute",
|
|
756
|
+
left: bounds.w / 2 * scale - HANDLE_SIZE / 2,
|
|
757
|
+
top: -ROTATE_HANDLE_OFFSET * scale - HANDLE_SIZE / 2,
|
|
758
|
+
width: HANDLE_SIZE,
|
|
759
|
+
height: HANDLE_SIZE,
|
|
760
|
+
background: "#fff",
|
|
761
|
+
border: `1.5px solid ${PRIMARY_COLOR}`,
|
|
762
|
+
borderRadius: "50%",
|
|
763
|
+
cursor: "grab",
|
|
764
|
+
boxSizing: "border-box",
|
|
765
|
+
pointerEvents: "auto"
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
)
|
|
769
|
+
]
|
|
770
|
+
}
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// src/design/snap.ts
|
|
775
|
+
function getStageRelativeBounds(el, stage, scale) {
|
|
776
|
+
const sr = stage.getBoundingClientRect();
|
|
777
|
+
const er = el.getBoundingClientRect();
|
|
778
|
+
return {
|
|
779
|
+
left: (er.left - sr.left) / scale,
|
|
780
|
+
right: (er.right - sr.left) / scale,
|
|
781
|
+
top: (er.top - sr.top) / scale,
|
|
782
|
+
bottom: (er.bottom - sr.top) / scale,
|
|
783
|
+
cx: (er.left + er.width / 2 - sr.left) / scale,
|
|
784
|
+
cy: (er.top + er.height / 2 - sr.top) / scale
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
function unionBounds(arr) {
|
|
788
|
+
if (arr.length === 0) return null;
|
|
789
|
+
let left = Infinity;
|
|
790
|
+
let right = -Infinity;
|
|
791
|
+
let top = Infinity;
|
|
792
|
+
let bottom = -Infinity;
|
|
793
|
+
for (const b of arr) {
|
|
794
|
+
if (b.left < left) left = b.left;
|
|
795
|
+
if (b.right > right) right = b.right;
|
|
796
|
+
if (b.top < top) top = b.top;
|
|
797
|
+
if (b.bottom > bottom) bottom = b.bottom;
|
|
798
|
+
}
|
|
799
|
+
return {
|
|
800
|
+
left,
|
|
801
|
+
right,
|
|
802
|
+
top,
|
|
803
|
+
bottom,
|
|
804
|
+
cx: (left + right) / 2,
|
|
805
|
+
cy: (top + bottom) / 2
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
var X_KEYS = ["left", "cx", "right"];
|
|
809
|
+
var Y_KEYS = ["top", "cy", "bottom"];
|
|
810
|
+
function findBestAxisMatch(moved, others, keys, thresholdSlide) {
|
|
811
|
+
let best = null;
|
|
812
|
+
for (const o of others) {
|
|
813
|
+
for (const dk of keys) {
|
|
814
|
+
for (const ok of keys) {
|
|
815
|
+
const adjust = o[ok] - moved[dk];
|
|
816
|
+
if (Math.abs(adjust) > thresholdSlide) continue;
|
|
817
|
+
if (!best || Math.abs(adjust) < Math.abs(best.adjust)) {
|
|
818
|
+
best = { adjust, pos: o[ok], other: o };
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
return best;
|
|
824
|
+
}
|
|
825
|
+
function computeSnap(start, rawDelta, others, thresholdSlide) {
|
|
826
|
+
const moved = {
|
|
827
|
+
left: start.left + rawDelta.x,
|
|
828
|
+
right: start.right + rawDelta.x,
|
|
829
|
+
cx: start.cx + rawDelta.x,
|
|
830
|
+
top: start.top + rawDelta.y,
|
|
831
|
+
bottom: start.bottom + rawDelta.y,
|
|
832
|
+
cy: start.cy + rawDelta.y
|
|
833
|
+
};
|
|
834
|
+
const bestX = findBestAxisMatch(moved, others, X_KEYS, thresholdSlide);
|
|
835
|
+
const bestY = findBestAxisMatch(moved, others, Y_KEYS, thresholdSlide);
|
|
836
|
+
const delta = {
|
|
837
|
+
x: rawDelta.x + (bestX?.adjust ?? 0),
|
|
838
|
+
y: rawDelta.y + (bestY?.adjust ?? 0)
|
|
839
|
+
};
|
|
840
|
+
const guides = [];
|
|
841
|
+
if (bestX) {
|
|
842
|
+
const top = start.top + delta.y;
|
|
843
|
+
const bottom = start.bottom + delta.y;
|
|
844
|
+
guides.push({
|
|
845
|
+
axis: "x",
|
|
846
|
+
position: bestX.pos,
|
|
847
|
+
start: Math.min(top, bestX.other.top),
|
|
848
|
+
end: Math.max(bottom, bestX.other.bottom)
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
if (bestY) {
|
|
852
|
+
const left = start.left + delta.x;
|
|
853
|
+
const right = start.right + delta.x;
|
|
854
|
+
guides.push({
|
|
855
|
+
axis: "y",
|
|
856
|
+
position: bestY.pos,
|
|
857
|
+
start: Math.min(left, bestY.other.left),
|
|
858
|
+
end: Math.max(right, bestY.other.right)
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
return { delta, guides };
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// src/design/useCanvasGestures.ts
|
|
865
|
+
var import_react = require("react");
|
|
866
|
+
|
|
867
|
+
// src/design/transitions.ts
|
|
868
|
+
var RESIZE_MIN = 8;
|
|
869
|
+
var DRAG_THRESHOLD_SQ = 4 * 4;
|
|
870
|
+
var SNAP_THRESHOLD_CLIENT = 5;
|
|
871
|
+
function detectHandleGesture(e, deck, selectedId, stage, pointer, scale) {
|
|
872
|
+
if (!selectedId || !stage) return null;
|
|
873
|
+
if (findRotateHandleFromEvent(e)) {
|
|
874
|
+
return startRotateGesture(deck, stage, selectedId, pointer);
|
|
875
|
+
}
|
|
876
|
+
const handle = findResizeHandleFromEvent(e);
|
|
877
|
+
if (handle) {
|
|
878
|
+
return startResizeGesture(deck, stage, selectedId, handle, pointer, scale);
|
|
879
|
+
}
|
|
880
|
+
return null;
|
|
881
|
+
}
|
|
882
|
+
function startRotateGesture(deck, stage, selectedId, pointer) {
|
|
883
|
+
const el = stage.querySelector(`[data-id="${selectedId}"]`);
|
|
884
|
+
if (!el) return null;
|
|
885
|
+
const er = el.getBoundingClientRect();
|
|
886
|
+
const centerClient = {
|
|
887
|
+
x: er.left + er.width / 2,
|
|
888
|
+
y: er.top + er.height / 2
|
|
889
|
+
};
|
|
890
|
+
const startAngle = Math.atan2(pointer.y - centerClient.y, pointer.x - centerClient.x);
|
|
891
|
+
const node = findById(deck, selectedId);
|
|
892
|
+
const startRotation = typeof node?.attrs.rotation === "number" ? node.attrs.rotation : 0;
|
|
893
|
+
return {
|
|
894
|
+
phase: "rotating",
|
|
895
|
+
elementId: selectedId,
|
|
896
|
+
centerClient,
|
|
897
|
+
startAngle,
|
|
898
|
+
startRotation,
|
|
899
|
+
currentRotation: startRotation
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
function startResizeGesture(deck, stage, selectedId, handle, pointer, scale) {
|
|
903
|
+
const el = stage.querySelector(`[data-id="${selectedId}"]`);
|
|
904
|
+
if (!el) return null;
|
|
905
|
+
const node = findById(deck, selectedId);
|
|
906
|
+
if (!node) return null;
|
|
907
|
+
const sr = stage.getBoundingClientRect();
|
|
908
|
+
const er = el.getBoundingClientRect();
|
|
909
|
+
const startCenter = {
|
|
910
|
+
x: (er.left + er.width / 2 - sr.left) / scale,
|
|
911
|
+
y: (er.top + er.height / 2 - sr.top) / scale
|
|
912
|
+
};
|
|
913
|
+
const w = typeof node.attrs.w === "number" ? node.attrs.w : er.width / scale;
|
|
914
|
+
const h = typeof node.attrs.h === "number" ? node.attrs.h : er.height / scale;
|
|
915
|
+
const rotation = typeof node.attrs.rotation === "number" ? node.attrs.rotation : 0;
|
|
916
|
+
const startBounds = {
|
|
917
|
+
x: startCenter.x - w / 2,
|
|
918
|
+
y: startCenter.y - h / 2,
|
|
919
|
+
w,
|
|
920
|
+
h
|
|
921
|
+
};
|
|
922
|
+
return {
|
|
923
|
+
phase: "resizing",
|
|
924
|
+
elementId: selectedId,
|
|
925
|
+
handle,
|
|
926
|
+
startClient: pointer,
|
|
927
|
+
startBounds,
|
|
928
|
+
startCenter,
|
|
929
|
+
rotation,
|
|
930
|
+
layoutMode: getLayoutModeOf(deck, selectedId),
|
|
931
|
+
currentBounds: startBounds
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
function promotePendingToDragging(pending, deck, stage, pointer, scale, currentSelection) {
|
|
935
|
+
if (pending.elementId === null) return null;
|
|
936
|
+
const elementId = pending.elementId;
|
|
937
|
+
const resolved = resolveDropTarget(deck, stage, elementId, pointer, scale);
|
|
938
|
+
const ids = Array.from(currentSelection);
|
|
939
|
+
const elementIds = canMultiDrag(deck, ids) ? ids : [elementId];
|
|
940
|
+
const startBoundsList = [];
|
|
941
|
+
for (const id of elementIds) {
|
|
942
|
+
const el = stage.querySelector(`[data-id="${id}"]`);
|
|
943
|
+
if (el) startBoundsList.push(getStageRelativeBounds(el, stage, scale));
|
|
944
|
+
}
|
|
945
|
+
return {
|
|
946
|
+
phase: "dragging",
|
|
947
|
+
elementId,
|
|
948
|
+
elementIds,
|
|
949
|
+
startClient: pending.startClient,
|
|
950
|
+
delta: {
|
|
951
|
+
x: (pointer.x - pending.startClient.x) / scale,
|
|
952
|
+
y: (pointer.y - pending.startClient.y) / scale
|
|
953
|
+
},
|
|
954
|
+
drop: resolved?.drop ?? null,
|
|
955
|
+
indicatorBounds: resolved?.indicatorBounds ?? null,
|
|
956
|
+
startGroupBounds: unionBounds(startBoundsList),
|
|
957
|
+
guides: []
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
function promotePendingToMarquee(pending, pointer, currentSelection) {
|
|
961
|
+
return {
|
|
962
|
+
phase: "marquee",
|
|
963
|
+
startClient: pending.startClient,
|
|
964
|
+
currentClient: pointer,
|
|
965
|
+
additive: pending.shiftKey,
|
|
966
|
+
initialSelection: currentSelection
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
function transitionMarqueeOnMove(marquee, pointer, stage) {
|
|
970
|
+
const left = Math.min(marquee.startClient.x, pointer.x);
|
|
971
|
+
const right = Math.max(marquee.startClient.x, pointer.x);
|
|
972
|
+
const top = Math.min(marquee.startClient.y, pointer.y);
|
|
973
|
+
const bottom = Math.max(marquee.startClient.y, pointer.y);
|
|
974
|
+
const hits = findElementsInRect(stage, { left, right, top, bottom });
|
|
975
|
+
const selection = marquee.additive ? new Set(marquee.initialSelection) : /* @__PURE__ */ new Set();
|
|
976
|
+
for (const id of hits) selection.add(id);
|
|
977
|
+
return {
|
|
978
|
+
gesture: { ...marquee, currentClient: pointer },
|
|
979
|
+
selection
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
function transitionDraggingOnMove(drag, pointer, deck, stage, scale) {
|
|
983
|
+
const rawDx = (pointer.x - drag.startClient.x) / scale;
|
|
984
|
+
const rawDy = (pointer.y - drag.startClient.y) / scale;
|
|
985
|
+
const resolved = resolveDropTarget(deck, stage, drag.elementId, pointer, scale);
|
|
986
|
+
let snappedDelta = { x: rawDx, y: rawDy };
|
|
987
|
+
let guides = [];
|
|
988
|
+
if (drag.startGroupBounds) {
|
|
989
|
+
const draggedSet = new Set(drag.elementIds);
|
|
990
|
+
const others = [];
|
|
991
|
+
for (const el of stage.querySelectorAll("[data-id]")) {
|
|
992
|
+
const id = el.dataset.id;
|
|
993
|
+
if (!id || draggedSet.has(id)) continue;
|
|
994
|
+
others.push(getStageRelativeBounds(el, stage, scale));
|
|
995
|
+
}
|
|
996
|
+
const result = computeSnap(
|
|
997
|
+
drag.startGroupBounds,
|
|
998
|
+
{ x: rawDx, y: rawDy },
|
|
999
|
+
others,
|
|
1000
|
+
SNAP_THRESHOLD_CLIENT / scale
|
|
1001
|
+
);
|
|
1002
|
+
snappedDelta = result.delta;
|
|
1003
|
+
guides = result.guides;
|
|
1004
|
+
}
|
|
1005
|
+
return {
|
|
1006
|
+
...drag,
|
|
1007
|
+
delta: snappedDelta,
|
|
1008
|
+
drop: resolved?.drop ?? null,
|
|
1009
|
+
indicatorBounds: resolved?.indicatorBounds ?? null,
|
|
1010
|
+
guides
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
function transitionResizingOnMove(resize, pointer, scale) {
|
|
1014
|
+
const cdxS = (pointer.x - resize.startClient.x) / scale;
|
|
1015
|
+
const cdyS = (pointer.y - resize.startClient.y) / scale;
|
|
1016
|
+
const Rrad = resize.rotation * Math.PI / 180;
|
|
1017
|
+
const cosR = Math.cos(Rrad);
|
|
1018
|
+
const sinR = Math.sin(Rrad);
|
|
1019
|
+
const cdxL = cdxS * cosR + cdyS * sinR;
|
|
1020
|
+
const cdyL = -cdxS * sinR + cdyS * cosR;
|
|
1021
|
+
const dir = RESIZE_HANDLE_DIRS[resize.handle];
|
|
1022
|
+
const sb = resize.startBounds;
|
|
1023
|
+
const newW = dir.dx !== 0 ? Math.max(RESIZE_MIN, sb.w + dir.dx * cdxL) : sb.w;
|
|
1024
|
+
const newH = dir.dy !== 0 ? Math.max(RESIZE_MIN, sb.h + dir.dy * cdyL) : sb.h;
|
|
1025
|
+
const dw = newW - sb.w;
|
|
1026
|
+
const dh = newH - sb.h;
|
|
1027
|
+
const lsx = dir.dx * dw / 2;
|
|
1028
|
+
const lsy = dir.dy * dh / 2;
|
|
1029
|
+
const isAbs = resize.layoutMode === "absolute";
|
|
1030
|
+
const centerShiftX = isAbs ? lsx * cosR - lsy * sinR : lsx;
|
|
1031
|
+
const centerShiftY = isAbs ? lsx * sinR + lsy * cosR : lsy;
|
|
1032
|
+
const newCenterX = resize.startCenter.x + centerShiftX;
|
|
1033
|
+
const newCenterY = resize.startCenter.y + centerShiftY;
|
|
1034
|
+
return {
|
|
1035
|
+
...resize,
|
|
1036
|
+
currentBounds: {
|
|
1037
|
+
x: newCenterX - newW / 2,
|
|
1038
|
+
y: newCenterY - newH / 2,
|
|
1039
|
+
w: newW,
|
|
1040
|
+
h: newH
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
function transitionRotatingOnMove(rotate, pointer) {
|
|
1045
|
+
const angle = Math.atan2(pointer.y - rotate.centerClient.y, pointer.x - rotate.centerClient.x);
|
|
1046
|
+
let d = angle - rotate.startAngle;
|
|
1047
|
+
while (d > Math.PI) d -= 2 * Math.PI;
|
|
1048
|
+
while (d < -Math.PI) d += 2 * Math.PI;
|
|
1049
|
+
const newRotation = rotate.startRotation + d * 180 / Math.PI;
|
|
1050
|
+
return { ...rotate, currentRotation: newRotation };
|
|
1051
|
+
}
|
|
1052
|
+
function buildSelectionAfterClick(prev, id, shiftKey) {
|
|
1053
|
+
if (shiftKey) {
|
|
1054
|
+
const next = new Set(prev);
|
|
1055
|
+
if (next.has(id)) next.delete(id);
|
|
1056
|
+
else next.add(id);
|
|
1057
|
+
return next;
|
|
1058
|
+
}
|
|
1059
|
+
return prev.has(id) ? prev : /* @__PURE__ */ new Set([id]);
|
|
1060
|
+
}
|
|
1061
|
+
function gestureCommit(gesture) {
|
|
1062
|
+
if (gesture.phase === "resizing") {
|
|
1063
|
+
const { elementId, startBounds, currentBounds } = gesture;
|
|
1064
|
+
const moved = currentBounds.x !== startBounds.x || currentBounds.y !== startBounds.y || currentBounds.w !== startBounds.w || currentBounds.h !== startBounds.h;
|
|
1065
|
+
return moved ? (d) => commitResize(d, elementId, currentBounds) : null;
|
|
1066
|
+
}
|
|
1067
|
+
if (gesture.phase === "rotating") {
|
|
1068
|
+
const { elementId, startRotation, currentRotation } = gesture;
|
|
1069
|
+
return currentRotation !== startRotation ? (d) => commitRotation(d, elementId, currentRotation) : null;
|
|
1070
|
+
}
|
|
1071
|
+
if (gesture.phase === "dragging") {
|
|
1072
|
+
if (gesture.elementIds.length > 1) {
|
|
1073
|
+
const { elementIds, delta } = gesture;
|
|
1074
|
+
return (d) => commitMultiDrag(d, elementIds, delta);
|
|
1075
|
+
}
|
|
1076
|
+
if (gesture.drop) {
|
|
1077
|
+
const { drop, elementId } = gesture;
|
|
1078
|
+
return (d) => commitDrop(d, elementId, drop);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
return null;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
// src/design/useCanvasGestures.ts
|
|
1085
|
+
function useCanvasGestures(opts) {
|
|
1086
|
+
const {
|
|
1087
|
+
stageRef,
|
|
1088
|
+
deck,
|
|
1089
|
+
setDeck,
|
|
1090
|
+
scale,
|
|
1091
|
+
disabled = false,
|
|
1092
|
+
selectedIds: selectedIdsControlled,
|
|
1093
|
+
onSelectionChange,
|
|
1094
|
+
onTextDoubleClick,
|
|
1095
|
+
shouldIgnorePointerDown,
|
|
1096
|
+
beforePointerDown
|
|
1097
|
+
} = opts;
|
|
1098
|
+
const [selectedIdsInternal, setSelectedIdsInternal] = (0, import_react.useState)(
|
|
1099
|
+
() => /* @__PURE__ */ new Set()
|
|
1100
|
+
);
|
|
1101
|
+
const selectedIds = selectedIdsControlled ?? selectedIdsInternal;
|
|
1102
|
+
const selectedId = selectedIds.size === 1 ? Array.from(selectedIds)[0] ?? null : null;
|
|
1103
|
+
const setSelectedIds = (0, import_react.useCallback)(
|
|
1104
|
+
(next) => {
|
|
1105
|
+
if (typeof next === "function") {
|
|
1106
|
+
const fn = next;
|
|
1107
|
+
setSelectedIdsInternal((prev) => {
|
|
1108
|
+
const resolved = selectedIdsControlled ?? prev;
|
|
1109
|
+
const updated = fn(resolved);
|
|
1110
|
+
if (onSelectionChange) onSelectionChange(updated);
|
|
1111
|
+
return updated;
|
|
1112
|
+
});
|
|
1113
|
+
} else {
|
|
1114
|
+
setSelectedIdsInternal(next);
|
|
1115
|
+
if (onSelectionChange) onSelectionChange(next);
|
|
1116
|
+
}
|
|
1117
|
+
},
|
|
1118
|
+
[selectedIdsControlled, onSelectionChange]
|
|
1119
|
+
);
|
|
1120
|
+
const [gesture, setGesture] = (0, import_react.useState)(null);
|
|
1121
|
+
const selectedIdsRef = (0, import_react.useRef)(selectedIds);
|
|
1122
|
+
selectedIdsRef.current = selectedIds;
|
|
1123
|
+
const handlePointerDown = (0, import_react.useCallback)(
|
|
1124
|
+
(e) => {
|
|
1125
|
+
if (disabled) return;
|
|
1126
|
+
if (shouldIgnorePointerDown?.(e)) return;
|
|
1127
|
+
beforePointerDown?.();
|
|
1128
|
+
const stage = stageRef.current;
|
|
1129
|
+
const sel = selectedIdsRef.current;
|
|
1130
|
+
const selId = sel.size === 1 ? Array.from(sel)[0] ?? null : null;
|
|
1131
|
+
const pointer = { x: e.clientX, y: e.clientY };
|
|
1132
|
+
const handleGesture = detectHandleGesture(e.nativeEvent, deck, selId, stage, pointer, scale);
|
|
1133
|
+
if (handleGesture) {
|
|
1134
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
1135
|
+
setGesture(handleGesture);
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
const id = findElementIdFromEvent(e.nativeEvent, stage);
|
|
1139
|
+
if (id !== null) {
|
|
1140
|
+
setSelectedIds((prev) => buildSelectionAfterClick(prev, id, e.shiftKey));
|
|
1141
|
+
}
|
|
1142
|
+
if (id !== null && e.shiftKey) {
|
|
1143
|
+
setGesture(null);
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
setGesture({ phase: "pending", elementId: id, startClient: pointer, shiftKey: e.shiftKey });
|
|
1147
|
+
},
|
|
1148
|
+
[disabled, shouldIgnorePointerDown, beforePointerDown, stageRef, deck, scale, setSelectedIds]
|
|
1149
|
+
);
|
|
1150
|
+
const handleDoubleClick = (0, import_react.useCallback)(
|
|
1151
|
+
(e) => {
|
|
1152
|
+
if (disabled) return;
|
|
1153
|
+
const id = findElementIdFromEvent(e.nativeEvent, stageRef.current);
|
|
1154
|
+
if (!id) return;
|
|
1155
|
+
const node = findById(deck, id);
|
|
1156
|
+
if (!node || node.tag !== "Text") return;
|
|
1157
|
+
setSelectedIds(/* @__PURE__ */ new Set([id]));
|
|
1158
|
+
setGesture(null);
|
|
1159
|
+
onTextDoubleClick?.(id);
|
|
1160
|
+
},
|
|
1161
|
+
[disabled, stageRef, deck, setSelectedIds, onTextDoubleClick]
|
|
1162
|
+
);
|
|
1163
|
+
const handlePointerMove = (0, import_react.useCallback)(
|
|
1164
|
+
(e) => {
|
|
1165
|
+
if (!gesture) return;
|
|
1166
|
+
const stage = stageRef.current;
|
|
1167
|
+
if (!stage) return;
|
|
1168
|
+
const pointer = { x: e.clientX, y: e.clientY };
|
|
1169
|
+
if (gesture.phase === "pending") {
|
|
1170
|
+
const dx = pointer.x - gesture.startClient.x;
|
|
1171
|
+
const dy = pointer.y - gesture.startClient.y;
|
|
1172
|
+
if (dx * dx + dy * dy < DRAG_THRESHOLD_SQ) return;
|
|
1173
|
+
try {
|
|
1174
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
1175
|
+
} catch {
|
|
1176
|
+
}
|
|
1177
|
+
if (gesture.elementId === null) {
|
|
1178
|
+
setGesture(promotePendingToMarquee(gesture, pointer, selectedIdsRef.current));
|
|
1179
|
+
} else {
|
|
1180
|
+
const next = promotePendingToDragging(
|
|
1181
|
+
gesture,
|
|
1182
|
+
deck,
|
|
1183
|
+
stage,
|
|
1184
|
+
pointer,
|
|
1185
|
+
scale,
|
|
1186
|
+
selectedIdsRef.current
|
|
1187
|
+
);
|
|
1188
|
+
if (next) setGesture(next);
|
|
1189
|
+
}
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
if (gesture.phase === "marquee") {
|
|
1193
|
+
const result = transitionMarqueeOnMove(gesture, pointer, stage);
|
|
1194
|
+
setGesture(result.gesture);
|
|
1195
|
+
setSelectedIds(result.selection);
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
if (gesture.phase === "dragging") {
|
|
1199
|
+
setGesture(transitionDraggingOnMove(gesture, pointer, deck, stage, scale));
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
if (gesture.phase === "resizing") {
|
|
1203
|
+
setGesture(transitionResizingOnMove(gesture, pointer, scale));
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
setGesture(transitionRotatingOnMove(gesture, pointer));
|
|
1207
|
+
},
|
|
1208
|
+
[gesture, stageRef, deck, scale, setSelectedIds]
|
|
1209
|
+
);
|
|
1210
|
+
const handlePointerUp = (0, import_react.useCallback)(
|
|
1211
|
+
(e) => {
|
|
1212
|
+
try {
|
|
1213
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
1214
|
+
} catch {
|
|
1215
|
+
}
|
|
1216
|
+
if (!gesture) return;
|
|
1217
|
+
if (gesture.phase === "pending" && gesture.elementId === null && !gesture.shiftKey) {
|
|
1218
|
+
setSelectedIds(/* @__PURE__ */ new Set());
|
|
1219
|
+
}
|
|
1220
|
+
const updater = gestureCommit(gesture);
|
|
1221
|
+
if (updater) setDeck(updater);
|
|
1222
|
+
setGesture(null);
|
|
1223
|
+
},
|
|
1224
|
+
[gesture, setDeck, setSelectedIds]
|
|
1225
|
+
);
|
|
1226
|
+
const handlePointerCancel = (0, import_react.useCallback)((e) => {
|
|
1227
|
+
try {
|
|
1228
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
1229
|
+
} catch {
|
|
1230
|
+
}
|
|
1231
|
+
setGesture(null);
|
|
1232
|
+
}, []);
|
|
1233
|
+
useDragPreviewEffect(stageRef, gesture, deck);
|
|
1234
|
+
const renderedDeck = useRenderedDeck(deck, gesture);
|
|
1235
|
+
return {
|
|
1236
|
+
selectedIds,
|
|
1237
|
+
selectedId,
|
|
1238
|
+
setSelectedIds,
|
|
1239
|
+
gesture,
|
|
1240
|
+
renderedDeck,
|
|
1241
|
+
onPointerDown: handlePointerDown,
|
|
1242
|
+
onPointerMove: handlePointerMove,
|
|
1243
|
+
onPointerUp: handlePointerUp,
|
|
1244
|
+
onPointerCancel: handlePointerCancel,
|
|
1245
|
+
onDoubleClick: handleDoubleClick
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
function useDragPreviewEffect(stageRef, gesture, deck) {
|
|
1249
|
+
(0, import_react.useLayoutEffect)(() => {
|
|
1250
|
+
if (!gesture || gesture.phase !== "dragging") return;
|
|
1251
|
+
const stage = stageRef.current;
|
|
1252
|
+
if (!stage) return;
|
|
1253
|
+
const { elementIds, delta } = gesture;
|
|
1254
|
+
const restorers = [];
|
|
1255
|
+
for (const elementId of elementIds) {
|
|
1256
|
+
const el = stage.querySelector(`[data-id="${elementId}"]`);
|
|
1257
|
+
if (!el) continue;
|
|
1258
|
+
const node = findById(deck, elementId);
|
|
1259
|
+
const rot = typeof node?.attrs.rotation === "number" ? node.attrs.rotation : 0;
|
|
1260
|
+
const rotateStr = rot !== 0 ? ` rotate(${rot}deg)` : "";
|
|
1261
|
+
const prevTransform = el.style.transform;
|
|
1262
|
+
const prevOpacity = el.style.opacity;
|
|
1263
|
+
el.style.transform = `translate(${delta.x}px, ${delta.y}px)${rotateStr}`;
|
|
1264
|
+
el.style.opacity = "0.7";
|
|
1265
|
+
restorers.push(() => {
|
|
1266
|
+
const currentEl = stageRef.current?.querySelector(`[data-id="${elementId}"]`);
|
|
1267
|
+
if (!currentEl) return;
|
|
1268
|
+
currentEl.style.transform = prevTransform;
|
|
1269
|
+
currentEl.style.opacity = prevOpacity;
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
1272
|
+
return () => {
|
|
1273
|
+
for (const fn of restorers) fn();
|
|
1274
|
+
};
|
|
1275
|
+
}, [gesture, deck, stageRef]);
|
|
1276
|
+
}
|
|
1277
|
+
function useRenderedDeck(deck, gesture) {
|
|
1278
|
+
return (0, import_react.useMemo)(() => {
|
|
1279
|
+
if (!gesture) return deck;
|
|
1280
|
+
if (gesture.phase !== "resizing" && gesture.phase !== "rotating") return deck;
|
|
1281
|
+
const next = structuredClone(deck);
|
|
1282
|
+
const el = findById(next, gesture.elementId);
|
|
1283
|
+
if (!el) return deck;
|
|
1284
|
+
if (gesture.phase === "resizing") {
|
|
1285
|
+
const cb = gesture.currentBounds;
|
|
1286
|
+
const layout = findParentOfId(next, gesture.elementId)?.attrs.layout;
|
|
1287
|
+
if (layout === "column") {
|
|
1288
|
+
el.attrs.h = cb.h;
|
|
1289
|
+
} else if (layout === "row") {
|
|
1290
|
+
el.attrs.w = cb.w;
|
|
1291
|
+
} else {
|
|
1292
|
+
el.attrs.x = cb.x;
|
|
1293
|
+
el.attrs.y = cb.y;
|
|
1294
|
+
el.attrs.w = cb.w;
|
|
1295
|
+
el.attrs.h = cb.h;
|
|
1296
|
+
}
|
|
1297
|
+
} else {
|
|
1298
|
+
el.attrs.rotation = gesture.currentRotation;
|
|
1299
|
+
}
|
|
1300
|
+
return next;
|
|
1301
|
+
}, [deck, gesture]);
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
// src/design/useCanvasKeyboard.ts
|
|
1305
|
+
var import_react2 = require("react");
|
|
1306
|
+
function isEditableTarget(target) {
|
|
1307
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
1308
|
+
if (target instanceof HTMLInputElement) return true;
|
|
1309
|
+
if (target instanceof HTMLTextAreaElement) return true;
|
|
1310
|
+
if (target instanceof HTMLSelectElement) return true;
|
|
1311
|
+
return target.isContentEditable;
|
|
1312
|
+
}
|
|
1313
|
+
function arrowKeyDelta(key, shiftKey) {
|
|
1314
|
+
const step = shiftKey ? 10 : 1;
|
|
1315
|
+
if (key === "ArrowLeft") return { dx: -step, dy: 0 };
|
|
1316
|
+
if (key === "ArrowRight") return { dx: step, dy: 0 };
|
|
1317
|
+
if (key === "ArrowUp") return { dx: 0, dy: -step };
|
|
1318
|
+
if (key === "ArrowDown") return { dx: 0, dy: step };
|
|
1319
|
+
return null;
|
|
1320
|
+
}
|
|
1321
|
+
function handleModifierShortcut(e, undo, redo) {
|
|
1322
|
+
const k = e.key.toLowerCase();
|
|
1323
|
+
if (k === "z") {
|
|
1324
|
+
e.preventDefault();
|
|
1325
|
+
if (e.shiftKey) redo?.();
|
|
1326
|
+
else undo?.();
|
|
1327
|
+
return true;
|
|
1328
|
+
}
|
|
1329
|
+
if (k === "y") {
|
|
1330
|
+
e.preventDefault();
|
|
1331
|
+
redo?.();
|
|
1332
|
+
return true;
|
|
1333
|
+
}
|
|
1334
|
+
return false;
|
|
1335
|
+
}
|
|
1336
|
+
function applyDelete(ids) {
|
|
1337
|
+
return (d) => {
|
|
1338
|
+
const next = structuredClone(d);
|
|
1339
|
+
let any = false;
|
|
1340
|
+
for (const id of ids) {
|
|
1341
|
+
if (removeById(next, id)) any = true;
|
|
1342
|
+
}
|
|
1343
|
+
return any ? next : d;
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
function applyArrowNudge(ids, dx, dy) {
|
|
1347
|
+
return (d) => {
|
|
1348
|
+
const next = structuredClone(d);
|
|
1349
|
+
let any = false;
|
|
1350
|
+
for (const id of ids) {
|
|
1351
|
+
if (getLayoutModeOf(d, id) !== "absolute") continue;
|
|
1352
|
+
const el = findById(next, id);
|
|
1353
|
+
if (!el) continue;
|
|
1354
|
+
const x = typeof el.attrs.x === "number" ? el.attrs.x : 0;
|
|
1355
|
+
const y = typeof el.attrs.y === "number" ? el.attrs.y : 0;
|
|
1356
|
+
el.attrs.x = x + dx;
|
|
1357
|
+
el.attrs.y = y + dy;
|
|
1358
|
+
any = true;
|
|
1359
|
+
}
|
|
1360
|
+
return any ? next : d;
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
function useCanvasKeyboard(opts) {
|
|
1364
|
+
const { setDeck, selectedIds, setSelectedIds, disabled = false, undo, redo } = opts;
|
|
1365
|
+
(0, import_react2.useEffect)(() => {
|
|
1366
|
+
if (disabled) return;
|
|
1367
|
+
const handler = (e) => {
|
|
1368
|
+
if (isEditableTarget(e.target)) return;
|
|
1369
|
+
if (e.metaKey || e.ctrlKey) {
|
|
1370
|
+
handleModifierShortcut(e, undo, redo);
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
if (e.altKey) return;
|
|
1374
|
+
if (selectedIds.size === 0) return;
|
|
1375
|
+
if (e.key === "Delete" || e.key === "Backspace") {
|
|
1376
|
+
e.preventDefault();
|
|
1377
|
+
setDeck(applyDelete(selectedIds));
|
|
1378
|
+
setSelectedIds(/* @__PURE__ */ new Set());
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
const arrow = arrowKeyDelta(e.key, e.shiftKey);
|
|
1382
|
+
if (arrow) {
|
|
1383
|
+
e.preventDefault();
|
|
1384
|
+
setDeck(applyArrowNudge(selectedIds, arrow.dx, arrow.dy));
|
|
1385
|
+
}
|
|
1386
|
+
};
|
|
1387
|
+
window.addEventListener("keydown", handler);
|
|
1388
|
+
return () => window.removeEventListener("keydown", handler);
|
|
1389
|
+
}, [disabled, setDeck, selectedIds, setSelectedIds, undo, redo]);
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
// src/design/useDeckHistory.ts
|
|
1393
|
+
var import_react3 = require("react");
|
|
1394
|
+
function useDeckHistory(initial) {
|
|
1395
|
+
const [history, setHistory] = (0, import_react3.useState)(() => ({
|
|
1396
|
+
snapshots: [initial],
|
|
1397
|
+
index: 0
|
|
1398
|
+
}));
|
|
1399
|
+
const deck = history.snapshots[history.index] ?? initial;
|
|
1400
|
+
const setDeck = (0, import_react3.useCallback)(
|
|
1401
|
+
(updater) => {
|
|
1402
|
+
setHistory((prev) => {
|
|
1403
|
+
const current = prev.snapshots[prev.index] ?? initial;
|
|
1404
|
+
const next = typeof updater === "function" ? updater(current) : updater;
|
|
1405
|
+
if (next === current) return prev;
|
|
1406
|
+
const snapshots = prev.snapshots.slice(0, prev.index + 1);
|
|
1407
|
+
snapshots.push(next);
|
|
1408
|
+
return { snapshots, index: prev.index + 1 };
|
|
1409
|
+
});
|
|
1410
|
+
},
|
|
1411
|
+
[initial]
|
|
1412
|
+
);
|
|
1413
|
+
const undo = (0, import_react3.useCallback)(() => {
|
|
1414
|
+
setHistory((prev) => prev.index > 0 ? { ...prev, index: prev.index - 1 } : prev);
|
|
1415
|
+
}, []);
|
|
1416
|
+
const redo = (0, import_react3.useCallback)(() => {
|
|
1417
|
+
setHistory(
|
|
1418
|
+
(prev) => prev.index < prev.snapshots.length - 1 ? { ...prev, index: prev.index + 1 } : prev
|
|
1419
|
+
);
|
|
1420
|
+
}, []);
|
|
1421
|
+
return {
|
|
1422
|
+
deck,
|
|
1423
|
+
setDeck,
|
|
1424
|
+
undo,
|
|
1425
|
+
redo,
|
|
1426
|
+
canUndo: history.index > 0,
|
|
1427
|
+
canRedo: history.index < history.snapshots.length - 1,
|
|
1428
|
+
undoCount: history.index,
|
|
1429
|
+
redoCount: history.snapshots.length - 1 - history.index
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
// src/design/useSelectionBounds.ts
|
|
1434
|
+
var import_react4 = require("react");
|
|
1435
|
+
function useSelectionBounds(stageRef, selectedId, deck, scale) {
|
|
1436
|
+
const [bounds, setBounds] = (0, import_react4.useState)(null);
|
|
1437
|
+
(0, import_react4.useLayoutEffect)(() => {
|
|
1438
|
+
const stage = stageRef.current;
|
|
1439
|
+
if (!stage || !selectedId) {
|
|
1440
|
+
setBounds(null);
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
const el = stage.querySelector(`[data-id="${selectedId}"]`);
|
|
1444
|
+
const node = findById(deck, selectedId);
|
|
1445
|
+
if (!el || !node) {
|
|
1446
|
+
setBounds(null);
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
const sr = stage.getBoundingClientRect();
|
|
1450
|
+
const er = el.getBoundingClientRect();
|
|
1451
|
+
const centerX = (er.left + er.width / 2 - sr.left) / scale;
|
|
1452
|
+
const centerY = (er.top + er.height / 2 - sr.top) / scale;
|
|
1453
|
+
const w = typeof node.attrs.w === "number" ? node.attrs.w : er.width / scale;
|
|
1454
|
+
const h = typeof node.attrs.h === "number" ? node.attrs.h : er.height / scale;
|
|
1455
|
+
const rotation = typeof node.attrs.rotation === "number" ? node.attrs.rotation : 0;
|
|
1456
|
+
setBounds({
|
|
1457
|
+
x: centerX - w / 2,
|
|
1458
|
+
y: centerY - h / 2,
|
|
1459
|
+
w,
|
|
1460
|
+
h,
|
|
1461
|
+
rotation
|
|
1462
|
+
});
|
|
1463
|
+
}, [stageRef, selectedId, deck, scale]);
|
|
1464
|
+
return bounds;
|
|
1465
|
+
}
|
|
1466
|
+
function useMultiSelectionBounds(stageRef, selectedIds, deck, scale) {
|
|
1467
|
+
const [bounds, setBounds] = (0, import_react4.useState)([]);
|
|
1468
|
+
(0, import_react4.useLayoutEffect)(() => {
|
|
1469
|
+
const stage = stageRef.current;
|
|
1470
|
+
if (!stage || selectedIds.size === 0) {
|
|
1471
|
+
setBounds([]);
|
|
1472
|
+
return;
|
|
1473
|
+
}
|
|
1474
|
+
const sr = stage.getBoundingClientRect();
|
|
1475
|
+
const out = [];
|
|
1476
|
+
for (const id of selectedIds) {
|
|
1477
|
+
const el = stage.querySelector(`[data-id="${id}"]`);
|
|
1478
|
+
const node = findById(deck, id);
|
|
1479
|
+
if (!el || !node) continue;
|
|
1480
|
+
const er = el.getBoundingClientRect();
|
|
1481
|
+
const centerX = (er.left + er.width / 2 - sr.left) / scale;
|
|
1482
|
+
const centerY = (er.top + er.height / 2 - sr.top) / scale;
|
|
1483
|
+
const w = typeof node.attrs.w === "number" ? node.attrs.w : er.width / scale;
|
|
1484
|
+
const h = typeof node.attrs.h === "number" ? node.attrs.h : er.height / scale;
|
|
1485
|
+
const rotation = typeof node.attrs.rotation === "number" ? node.attrs.rotation : 0;
|
|
1486
|
+
out.push({ id, x: centerX - w / 2, y: centerY - h / 2, w, h, rotation });
|
|
1487
|
+
}
|
|
1488
|
+
setBounds(out);
|
|
1489
|
+
}, [stageRef, selectedIds, deck, scale]);
|
|
1490
|
+
return bounds;
|
|
1491
|
+
}
|
|
1492
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1493
|
+
0 && (module.exports = {
|
|
1494
|
+
CanvasOverlays,
|
|
1495
|
+
NON_SELECTABLE_TAGS,
|
|
1496
|
+
RESIZE_HANDLE_CURSORS,
|
|
1497
|
+
RESIZE_HANDLE_DIRS,
|
|
1498
|
+
canMultiDrag,
|
|
1499
|
+
commitDrop,
|
|
1500
|
+
commitMultiDrag,
|
|
1501
|
+
commitResize,
|
|
1502
|
+
commitRotation,
|
|
1503
|
+
commitTextChange,
|
|
1504
|
+
computeFlexDropTarget,
|
|
1505
|
+
computeSnap,
|
|
1506
|
+
containerLayoutMode,
|
|
1507
|
+
findContainerAt,
|
|
1508
|
+
findElementIdFromEvent,
|
|
1509
|
+
findElementsInRect,
|
|
1510
|
+
findResizeHandleFromEvent,
|
|
1511
|
+
findRotateHandleFromEvent,
|
|
1512
|
+
getLayoutModeOf,
|
|
1513
|
+
getStageRelativeBounds,
|
|
1514
|
+
getTextContent,
|
|
1515
|
+
isFlexChild,
|
|
1516
|
+
resolveDropTarget,
|
|
1517
|
+
unionBounds,
|
|
1518
|
+
useCanvasGestures,
|
|
1519
|
+
useCanvasKeyboard,
|
|
1520
|
+
useDeckHistory,
|
|
1521
|
+
useMultiSelectionBounds,
|
|
1522
|
+
useSelectionBounds
|
|
1523
|
+
});
|