@tmagic/stage 1.7.6 → 1.7.8-beta.1
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/LICENSE +235 -164
- package/dist/es/ActionManager.js +470 -0
- package/dist/es/DragResizeHelper.js +280 -0
- package/dist/es/MoveableActionsAble.js +64 -0
- package/dist/es/MoveableOptionsManager.js +204 -0
- package/dist/es/Rule.js +139 -0
- package/dist/es/StageCore.js +307 -0
- package/dist/es/StageDragResize.js +233 -0
- package/dist/es/StageHighlight.js +62 -0
- package/dist/es/StageMask.js +256 -0
- package/dist/es/StageMultiDragResize.js +153 -0
- package/dist/es/StageRender.js +186 -0
- package/dist/es/TargetShadow.js +68 -0
- package/dist/es/const.js +91 -0
- package/dist/es/index.js +12 -0
- package/dist/es/moveable-able.js +4 -0
- package/dist/es/style.js +4 -0
- package/dist/es/util.js +192 -0
- package/dist/tmagic-stage.umd.cjs +4811 -5308
- package/package.json +5 -4
- package/types/index.d.ts +857 -841
- package/dist/tmagic-stage.js +0 -2833
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { DRAG_EL_ID_PREFIX, GHOST_EL_ID_PREFIX, Mode, ZIndex } from "./const.js";
|
|
2
|
+
import { getAbsolutePosition, getBorderWidth, getMarginValue, getOffset } from "./util.js";
|
|
3
|
+
import TargetShadow from "./TargetShadow.js";
|
|
4
|
+
import { calcValueByFontsize, getIdFromEl, setIdToEl } from "@tmagic/core";
|
|
5
|
+
import MoveableHelper from "moveable-helper";
|
|
6
|
+
//#region packages/stage/src/DragResizeHelper.ts
|
|
7
|
+
/**
|
|
8
|
+
* 拖拽/改变大小等操作发生时,moveable会抛出各种状态事件,DragResizeHelper负责响应这些事件,对目标节点target和拖拽节点targetShadow进行修改;
|
|
9
|
+
* 其中目标节点是DragResizeHelper直接改的,targetShadow作为直接被操作的拖拽框,是调用moveableHelper改的;
|
|
10
|
+
* 有个特殊情况是流式布局下,moveableHelper不支持,targetShadow也是DragResizeHelper直接改的
|
|
11
|
+
*/
|
|
12
|
+
var DragResizeHelper = class {
|
|
13
|
+
/** 目标节点在蒙层上的占位节点,用于跟鼠标交互,避免鼠标事件直接作用到目标节点 */
|
|
14
|
+
targetShadow;
|
|
15
|
+
/** 要操作的原始目标节点 */
|
|
16
|
+
target = null;
|
|
17
|
+
/** 多选:目标节点组 */
|
|
18
|
+
targetList = [];
|
|
19
|
+
/** 响应拖拽的状态事件,修改绝对定位布局下targetShadow的dom。
|
|
20
|
+
* MoveableHelper里面的方法是成员属性,如果DragResizeHelper用继承的方式将无法通过super去调这些方法 */
|
|
21
|
+
moveableHelper;
|
|
22
|
+
/** 流式布局下,目标节点的镜像节点 */
|
|
23
|
+
ghostEl;
|
|
24
|
+
/** 用于记录节点被改变前的位置 */
|
|
25
|
+
frameSnapShot = {
|
|
26
|
+
left: 0,
|
|
27
|
+
top: 0
|
|
28
|
+
};
|
|
29
|
+
/** 多选模式下的多个节点 */
|
|
30
|
+
framesSnapShot = [];
|
|
31
|
+
/** 布局方式:流式布局、绝对定位、固定定位 */
|
|
32
|
+
mode = Mode.ABSOLUTE;
|
|
33
|
+
constructor(config) {
|
|
34
|
+
this.moveableHelper = MoveableHelper.create({
|
|
35
|
+
useBeforeRender: true,
|
|
36
|
+
useRender: false,
|
|
37
|
+
createAuto: true
|
|
38
|
+
});
|
|
39
|
+
this.targetShadow = new TargetShadow({
|
|
40
|
+
container: config.container,
|
|
41
|
+
updateDragEl: config.updateDragEl,
|
|
42
|
+
zIndex: ZIndex.DRAG_EL,
|
|
43
|
+
idPrefix: DRAG_EL_ID_PREFIX
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
destroy() {
|
|
47
|
+
this.target = null;
|
|
48
|
+
this.targetList = [];
|
|
49
|
+
this.targetShadow.destroy();
|
|
50
|
+
this.destroyGhostEl();
|
|
51
|
+
this.moveableHelper.clear();
|
|
52
|
+
}
|
|
53
|
+
destroyShadowEl() {
|
|
54
|
+
this.targetShadow.destroyEl();
|
|
55
|
+
}
|
|
56
|
+
getShadowEl() {
|
|
57
|
+
return this.targetShadow.el;
|
|
58
|
+
}
|
|
59
|
+
updateShadowEl(el) {
|
|
60
|
+
this.destroyGhostEl();
|
|
61
|
+
this.target = el;
|
|
62
|
+
this.targetShadow.update(el);
|
|
63
|
+
}
|
|
64
|
+
setMode(mode) {
|
|
65
|
+
this.mode = mode;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 改变大小事件开始
|
|
69
|
+
* @param e 包含了拖拽节点的dom,moveableHelper会直接修改拖拽节点
|
|
70
|
+
*/
|
|
71
|
+
onResizeStart(e) {
|
|
72
|
+
this.moveableHelper.onResizeStart(e);
|
|
73
|
+
this.frameSnapShot.top = this.target.offsetTop;
|
|
74
|
+
this.frameSnapShot.left = this.target.offsetLeft;
|
|
75
|
+
}
|
|
76
|
+
onResize(e) {
|
|
77
|
+
const { width, height, drag } = e;
|
|
78
|
+
const { beforeTranslate } = drag;
|
|
79
|
+
if (this.mode === Mode.SORTABLE) {
|
|
80
|
+
this.target.style.top = "0px";
|
|
81
|
+
if (this.targetShadow.el) {
|
|
82
|
+
this.targetShadow.el.style.width = `${width}px`;
|
|
83
|
+
this.targetShadow.el.style.height = `${height}px`;
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
this.moveableHelper.onResize(e);
|
|
87
|
+
const { marginLeft, marginTop } = getMarginValue(this.target);
|
|
88
|
+
this.target.style.left = `${this.frameSnapShot.left + beforeTranslate[0] - marginLeft}px`;
|
|
89
|
+
this.target.style.top = `${this.frameSnapShot.top + beforeTranslate[1] - marginTop}px`;
|
|
90
|
+
}
|
|
91
|
+
const { borderLeftWidth, borderRightWidth, borderTopWidth, borderBottomWidth } = getBorderWidth(this.target);
|
|
92
|
+
this.target.style.width = `${width + borderLeftWidth + borderRightWidth}px`;
|
|
93
|
+
this.target.style.height = `${height + borderTopWidth + borderBottomWidth}px`;
|
|
94
|
+
}
|
|
95
|
+
onDragStart(e) {
|
|
96
|
+
this.moveableHelper.onDragStart(e);
|
|
97
|
+
if (this.mode === Mode.SORTABLE) this.ghostEl = this.generateGhostEl(this.target);
|
|
98
|
+
this.frameSnapShot.top = this.target.offsetTop;
|
|
99
|
+
this.frameSnapShot.left = this.target.offsetLeft;
|
|
100
|
+
}
|
|
101
|
+
onDrag(e) {
|
|
102
|
+
if (this.ghostEl) {
|
|
103
|
+
this.ghostEl.style.top = `${this.frameSnapShot.top + e.beforeTranslate[1]}px`;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
this.moveableHelper.onDrag(e);
|
|
107
|
+
const { marginLeft, marginTop } = getMarginValue(this.target);
|
|
108
|
+
this.target.style.left = `${this.frameSnapShot.left + e.beforeTranslate[0] - marginLeft}px`;
|
|
109
|
+
this.target.style.top = `${this.frameSnapShot.top + e.beforeTranslate[1] - marginTop}px`;
|
|
110
|
+
}
|
|
111
|
+
onRotateStart(e) {
|
|
112
|
+
this.moveableHelper.onRotateStart(e);
|
|
113
|
+
}
|
|
114
|
+
onRotate(e) {
|
|
115
|
+
this.moveableHelper.onRotate(e);
|
|
116
|
+
const frame = this.moveableHelper.getFrame(e.target);
|
|
117
|
+
this.target.style.transform = frame?.toCSSObject().transform || "";
|
|
118
|
+
}
|
|
119
|
+
onScaleStart(e) {
|
|
120
|
+
this.moveableHelper.onScaleStart(e);
|
|
121
|
+
}
|
|
122
|
+
onScale(e) {
|
|
123
|
+
this.moveableHelper.onScale(e);
|
|
124
|
+
const frame = this.moveableHelper.getFrame(e.target);
|
|
125
|
+
this.target.style.transform = frame?.toCSSObject().transform || "";
|
|
126
|
+
}
|
|
127
|
+
getGhostEl() {
|
|
128
|
+
return this.ghostEl;
|
|
129
|
+
}
|
|
130
|
+
destroyGhostEl() {
|
|
131
|
+
this.ghostEl?.remove();
|
|
132
|
+
this.ghostEl = void 0;
|
|
133
|
+
}
|
|
134
|
+
clear() {
|
|
135
|
+
this.moveableHelper.clear();
|
|
136
|
+
}
|
|
137
|
+
getFrame(el) {
|
|
138
|
+
return this.moveableHelper.getFrame(el);
|
|
139
|
+
}
|
|
140
|
+
getShadowEls() {
|
|
141
|
+
return this.targetShadow.els;
|
|
142
|
+
}
|
|
143
|
+
updateGroup(els) {
|
|
144
|
+
this.targetList = els;
|
|
145
|
+
this.framesSnapShot = [];
|
|
146
|
+
this.targetShadow.updateGroup(els);
|
|
147
|
+
}
|
|
148
|
+
setTargetList(targetList) {
|
|
149
|
+
this.targetList = targetList;
|
|
150
|
+
}
|
|
151
|
+
clearMultiSelectStatus() {
|
|
152
|
+
this.targetList = [];
|
|
153
|
+
this.targetShadow.destroyEls();
|
|
154
|
+
}
|
|
155
|
+
onResizeGroupStart(e) {
|
|
156
|
+
const { events } = e;
|
|
157
|
+
this.moveableHelper.onResizeGroupStart(e);
|
|
158
|
+
this.setFramesSnapShot(events);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* 多选状态下通过拖拽边框改变大小,所有选中组件会一起改变大小
|
|
162
|
+
*/
|
|
163
|
+
onResizeGroup(e) {
|
|
164
|
+
const { events } = e;
|
|
165
|
+
this.moveableHelper.onResizeGroup(e);
|
|
166
|
+
events.forEach((ev) => {
|
|
167
|
+
const { width, height, beforeTranslate } = ev.drag;
|
|
168
|
+
const frameSnapShot = this.framesSnapShot.find((frameItem) => frameItem.id === getIdFromEl()(ev.target)?.replace(DRAG_EL_ID_PREFIX, ""));
|
|
169
|
+
if (!frameSnapShot) return;
|
|
170
|
+
const targeEl = this.targetList.find((targetItem) => getIdFromEl()(targetItem) === getIdFromEl()(ev.target)?.replace(DRAG_EL_ID_PREFIX, ""));
|
|
171
|
+
if (!targeEl) return;
|
|
172
|
+
if (!this.targetList.find((targetItem) => getIdFromEl()(targetItem) === getIdFromEl()(targeEl.parentElement))) {
|
|
173
|
+
const { marginLeft, marginTop } = getMarginValue(targeEl);
|
|
174
|
+
targeEl.style.left = `${frameSnapShot.left + beforeTranslate[0] - marginLeft}px`;
|
|
175
|
+
targeEl.style.top = `${frameSnapShot.top + beforeTranslate[1] - marginTop}px`;
|
|
176
|
+
}
|
|
177
|
+
targeEl.style.width = `${width}px`;
|
|
178
|
+
targeEl.style.height = `${height}px`;
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
onDragGroupStart(e) {
|
|
182
|
+
this.moveableHelper.onDragGroupStart(e);
|
|
183
|
+
const { events } = e;
|
|
184
|
+
this.setFramesSnapShot(events);
|
|
185
|
+
}
|
|
186
|
+
onDragGroup(e) {
|
|
187
|
+
this.moveableHelper.onDragGroup(e);
|
|
188
|
+
const { events } = e;
|
|
189
|
+
events.forEach((ev) => {
|
|
190
|
+
const frameSnapShot = this.framesSnapShot.find((frameItem) => getIdFromEl()(ev.target)?.startsWith("drag_el_") && getIdFromEl()(ev.target)?.endsWith(frameItem.id));
|
|
191
|
+
if (!frameSnapShot) return;
|
|
192
|
+
const findTargetElFuctin = (targetItem) => {
|
|
193
|
+
const getId = getIdFromEl();
|
|
194
|
+
const targetId = getId(ev.target);
|
|
195
|
+
const targetItemId = getId(targetItem);
|
|
196
|
+
return targetId?.startsWith("drag_el_") && targetItemId && targetId?.endsWith(targetItemId);
|
|
197
|
+
};
|
|
198
|
+
const targeEl = this.targetList.find(findTargetElFuctin);
|
|
199
|
+
if (!targeEl) return;
|
|
200
|
+
if (!this.targetList.find((targetItem) => getIdFromEl()(targetItem) === getIdFromEl()(targeEl.parentElement))) {
|
|
201
|
+
const { marginLeft, marginTop } = getMarginValue(targeEl);
|
|
202
|
+
targeEl.style.left = `${frameSnapShot.left + ev.beforeTranslate[0] - marginLeft}px`;
|
|
203
|
+
targeEl.style.top = `${frameSnapShot.top + ev.beforeTranslate[1] - marginTop}px`;
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
getUpdatedElRect(el, parentEl, doc) {
|
|
208
|
+
const offset = this.mode === Mode.SORTABLE ? {
|
|
209
|
+
left: 0,
|
|
210
|
+
top: 0
|
|
211
|
+
} : {
|
|
212
|
+
left: el.offsetLeft,
|
|
213
|
+
top: el.offsetTop
|
|
214
|
+
};
|
|
215
|
+
const { marginLeft, marginTop } = getMarginValue(el);
|
|
216
|
+
let left = calcValueByFontsize(doc, offset.left) - marginLeft;
|
|
217
|
+
let top = calcValueByFontsize(doc, offset.top) - marginTop;
|
|
218
|
+
const { borderLeftWidth, borderRightWidth, borderTopWidth, borderBottomWidth } = getBorderWidth(el);
|
|
219
|
+
const width = calcValueByFontsize(doc, el.clientWidth + borderLeftWidth + borderRightWidth);
|
|
220
|
+
const height = calcValueByFontsize(doc, el.clientHeight + borderTopWidth + borderBottomWidth);
|
|
221
|
+
let shadowEl = this.getShadowEl();
|
|
222
|
+
const shadowEls = this.getShadowEls();
|
|
223
|
+
if (shadowEls.length) shadowEl = shadowEls.find((item) => getIdFromEl()(item)?.endsWith(getIdFromEl()(el) || ""));
|
|
224
|
+
if (parentEl && this.mode === Mode.ABSOLUTE && shadowEl) {
|
|
225
|
+
const targetShadowHtmlEl = shadowEl;
|
|
226
|
+
const targetShadowElOffsetLeft = targetShadowHtmlEl.offsetLeft || 0;
|
|
227
|
+
const targetShadowElOffsetTop = targetShadowHtmlEl.offsetTop || 0;
|
|
228
|
+
const [translateX, translateY] = this.getFrame(shadowEl)?.properties.transform.translate.value;
|
|
229
|
+
const { left: parentLeft, top: parentTop } = getOffset(parentEl);
|
|
230
|
+
left = calcValueByFontsize(doc, targetShadowElOffsetLeft) + parseFloat(translateX) - calcValueByFontsize(doc, parentLeft);
|
|
231
|
+
top = calcValueByFontsize(doc, targetShadowElOffsetTop) + parseFloat(translateY) - calcValueByFontsize(doc, parentTop);
|
|
232
|
+
}
|
|
233
|
+
return {
|
|
234
|
+
width,
|
|
235
|
+
height,
|
|
236
|
+
left,
|
|
237
|
+
top
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* 多选状态设置多个节点的快照
|
|
242
|
+
*/
|
|
243
|
+
setFramesSnapShot(events) {
|
|
244
|
+
if (this.framesSnapShot.length > 0) return;
|
|
245
|
+
events.forEach((ev) => {
|
|
246
|
+
const matchEventTarget = this.targetList.find((targetItem) => getIdFromEl()(ev.target)?.startsWith("drag_el_") && getIdFromEl()(ev.target)?.endsWith(getIdFromEl()(targetItem) || ""));
|
|
247
|
+
if (!matchEventTarget) return;
|
|
248
|
+
const id = getIdFromEl()(matchEventTarget);
|
|
249
|
+
id && this.framesSnapShot.push({
|
|
250
|
+
left: matchEventTarget.offsetLeft,
|
|
251
|
+
top: matchEventTarget.offsetTop,
|
|
252
|
+
id
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* 流式布局把目标节点复制一份进行拖拽,在拖拽结束前不影响页面原布局样式
|
|
258
|
+
*/
|
|
259
|
+
generateGhostEl(el) {
|
|
260
|
+
if (this.ghostEl) this.destroyGhostEl();
|
|
261
|
+
const ghostEl = document.createElement("div");
|
|
262
|
+
const { top, left } = getAbsolutePosition(el, getOffset(el));
|
|
263
|
+
setIdToEl()(ghostEl, `${GHOST_EL_ID_PREFIX}${getIdFromEl()(el)}`);
|
|
264
|
+
ghostEl.style.cssText = `
|
|
265
|
+
z-index: ${ZIndex.GHOST_EL};
|
|
266
|
+
opacity: .6;
|
|
267
|
+
position: absolute;
|
|
268
|
+
left: ${left}px;
|
|
269
|
+
top: ${top}px;
|
|
270
|
+
margin: 0;
|
|
271
|
+
background: blue;
|
|
272
|
+
width: ${el.clientWidth}px;
|
|
273
|
+
height: ${el.clientHeight}px;
|
|
274
|
+
`;
|
|
275
|
+
el.after(ghostEl);
|
|
276
|
+
return ghostEl;
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
//#endregion
|
|
280
|
+
export { DragResizeHelper as default };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { AbleActionEventType } from "./const.js";
|
|
2
|
+
import moveable_able_default from "./moveable-able.js";
|
|
3
|
+
//#region packages/stage/src/MoveableActionsAble.ts
|
|
4
|
+
var MoveableActionsAble_default = (handler, customizedButton = []) => ({
|
|
5
|
+
name: "actions",
|
|
6
|
+
props: [],
|
|
7
|
+
always: true,
|
|
8
|
+
events: [],
|
|
9
|
+
render(moveable, React) {
|
|
10
|
+
const rect = moveable.getRect();
|
|
11
|
+
const { pos2 } = moveable.state;
|
|
12
|
+
const editableViewer = moveable.useCSS("div", `
|
|
13
|
+
{
|
|
14
|
+
position: absolute;
|
|
15
|
+
left: 0px;
|
|
16
|
+
top: 0px;
|
|
17
|
+
will-change: transform;
|
|
18
|
+
transform-origin: 60px 28px;
|
|
19
|
+
display: flex;
|
|
20
|
+
}
|
|
21
|
+
${moveable_able_default}
|
|
22
|
+
`);
|
|
23
|
+
return React.createElement(editableViewer, {
|
|
24
|
+
className: "moveable-editable",
|
|
25
|
+
style: { transform: `translate(${pos2[0] - (customizedButton.length + 3) * 20}px, ${pos2[1] - 28}px) rotate(${rect.rotation}deg)` }
|
|
26
|
+
}, [
|
|
27
|
+
...customizedButton.map((buttonRenderer) => {
|
|
28
|
+
const options = buttonRenderer(React);
|
|
29
|
+
return React.createElement("button", options.props || {}, ...options.children || []);
|
|
30
|
+
}),
|
|
31
|
+
React.createElement("button", {
|
|
32
|
+
className: "moveable-button moveable-rerender-button",
|
|
33
|
+
title: "重新收集依赖后渲染",
|
|
34
|
+
onClick: () => {
|
|
35
|
+
handler(AbleActionEventType.RERENDER);
|
|
36
|
+
}
|
|
37
|
+
}, React.createElement("img", {
|
|
38
|
+
src: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJpY29uIGljb24tdGFibGVyIGljb24tdGFibGVyLXJlcGxhY2UiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZT0iI2ZmZmZmZiIgZmlsbD0ibm9uZSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj4KICA8cGF0aCBzdHJva2U9Im5vbmUiIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz4KICA8cmVjdCB4PSIzIiB5PSIzIiB3aWR0aD0iNiIgaGVpZ2h0PSI2IiByeD0iMSIgLz4KICA8cmVjdCB4PSIxNSIgeT0iMTUiIHdpZHRoPSI2IiBoZWlnaHQ9IjYiIHJ4PSIxIiAvPgogIDxwYXRoIGQ9Ik0yMSAxMXYtM2EyIDIgMCAwIDAgLTIgLTJoLTZsMyAzbTAgLTZsLTMgMyIgLz4KICA8cGF0aCBkPSJNMyAxM3YzYTIgMiAwIDAgMCAyIDJoNmwtMyAtM20wIDZsMyAtMyIgLz4KPC9zdmc+CgoK",
|
|
39
|
+
width: "16",
|
|
40
|
+
height: "16"
|
|
41
|
+
})),
|
|
42
|
+
React.createElement("button", {
|
|
43
|
+
className: "moveable-button",
|
|
44
|
+
title: "选中父组件",
|
|
45
|
+
onClick: () => {
|
|
46
|
+
handler(AbleActionEventType.SELECT_PARENT);
|
|
47
|
+
}
|
|
48
|
+
}, React.createElement("div", { className: "moveable-select-parent-arrow-top-icon" }), React.createElement("div", { className: "moveable-select-parent-arrow-body-icon" })),
|
|
49
|
+
React.createElement("button", {
|
|
50
|
+
className: "moveable-button moveable-remove-button",
|
|
51
|
+
title: "删除",
|
|
52
|
+
onClick: () => {
|
|
53
|
+
handler(AbleActionEventType.REMOVE);
|
|
54
|
+
}
|
|
55
|
+
}),
|
|
56
|
+
React.createElement("button", {
|
|
57
|
+
className: "moveable-button moveable-drag-area-button",
|
|
58
|
+
title: "拖动"
|
|
59
|
+
}, React.createElement("div", { className: "moveable-select-parent-arrow-top-icon moveable-select-parent-arrow-top-icon-top" }), React.createElement("div", { className: "moveable-select-parent-arrow-top-icon moveable-select-parent-arrow-top-icon-bottom" }), React.createElement("div", { className: "moveable-select-parent-arrow-top-icon moveable-select-parent-arrow-top-icon-left" }), React.createElement("div", { className: " moveable-select-parent-arrow-top-icon moveable-select-parent-arrow-top-icon-right" }), React.createElement("div", { className: "moveable-select-parent-arrow-body-icon-horizontal" }), React.createElement("div", { className: "moveable-select-parent-arrow-body-icon-vertical" }))
|
|
60
|
+
]);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
//#endregion
|
|
64
|
+
export { MoveableActionsAble_default as default };
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { GuidesType, Mode } from "./const.js";
|
|
2
|
+
import { getOffset } from "./util.js";
|
|
3
|
+
import MoveableActionsAble_default from "./MoveableActionsAble.js";
|
|
4
|
+
import EventEmitter$1 from "events";
|
|
5
|
+
import { merge } from "lodash-es";
|
|
6
|
+
//#region packages/stage/src/MoveableOptionsManager.ts
|
|
7
|
+
/**
|
|
8
|
+
* 单选和多选的父类,用于管理moveableOptions
|
|
9
|
+
* @extends EventEmitter
|
|
10
|
+
*/
|
|
11
|
+
var MoveableOptionsManager = class extends EventEmitter$1 {
|
|
12
|
+
/** 布局方式:流式布局、绝对定位、固定定位 */
|
|
13
|
+
mode = Mode.ABSOLUTE;
|
|
14
|
+
/** 画布容器 */
|
|
15
|
+
container;
|
|
16
|
+
options = {};
|
|
17
|
+
/** 水平参考线 */
|
|
18
|
+
horizontalGuidelines = [];
|
|
19
|
+
/** 垂直参考线 */
|
|
20
|
+
verticalGuidelines = [];
|
|
21
|
+
/** 对齐元素集合 */
|
|
22
|
+
elementGuidelines = [];
|
|
23
|
+
/** 由外部调用方(编辑器)传入进来的moveable默认参数,可以为空,也可以是一个回调函数 */
|
|
24
|
+
customizedOptions;
|
|
25
|
+
/** 获取整个画布的根元素(在StageCore的mount函数中挂载的container) */
|
|
26
|
+
getRootContainer;
|
|
27
|
+
constructor(config) {
|
|
28
|
+
super();
|
|
29
|
+
this.customizedOptions = config.moveableOptions;
|
|
30
|
+
this.container = config.container;
|
|
31
|
+
this.getRootContainer = config.getRootContainer;
|
|
32
|
+
}
|
|
33
|
+
getOption(key) {
|
|
34
|
+
return this.options[key];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 设置水平/垂直参考线
|
|
38
|
+
* @param type 参考线类型
|
|
39
|
+
* @param guidelines 参考线坐标数组
|
|
40
|
+
*/
|
|
41
|
+
setGuidelines(type, guidelines) {
|
|
42
|
+
if (type === GuidesType.HORIZONTAL) this.horizontalGuidelines = guidelines;
|
|
43
|
+
else if (type === GuidesType.VERTICAL) this.verticalGuidelines = guidelines;
|
|
44
|
+
this.emit("update-moveable");
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 清除横向和纵向的参考线
|
|
48
|
+
*/
|
|
49
|
+
clearGuides() {
|
|
50
|
+
this.horizontalGuidelines = [];
|
|
51
|
+
this.verticalGuidelines = [];
|
|
52
|
+
this.emit("update-moveable");
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 设置有哪些元素要辅助对齐
|
|
56
|
+
* @param selectedElList 选中的元素列表,需要排除在对齐元素之外
|
|
57
|
+
*/
|
|
58
|
+
setElementGuidelines(selectedElList) {
|
|
59
|
+
this.elementGuidelines.forEach((node) => {
|
|
60
|
+
node.remove();
|
|
61
|
+
});
|
|
62
|
+
this.elementGuidelines = [];
|
|
63
|
+
const elementGuidelines = this.getCustomizeOptions()?.elementGuidelines || Array.from(selectedElList[0]?.parentElement?.children || []);
|
|
64
|
+
if (this.mode === Mode.ABSOLUTE) this.container.append(this.createGuidelineElements(selectedElList, elementGuidelines));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 获取moveable参数
|
|
68
|
+
* @param isMultiSelect 是否多选模式
|
|
69
|
+
* @param runtimeOptions 调用时实时传进来的的moveable参数
|
|
70
|
+
* @returns moveable所需参数
|
|
71
|
+
*/
|
|
72
|
+
getOptions(isMultiSelect, runtimeOptions = {}) {
|
|
73
|
+
this.options = merge(this.getDefaultOptions(isMultiSelect), this.getCustomizeOptions() || {}, runtimeOptions);
|
|
74
|
+
return this.options;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 获取单选和多选的moveable公共参数
|
|
78
|
+
* @returns moveable公共参数
|
|
79
|
+
*/
|
|
80
|
+
getDefaultOptions(isMultiSelect) {
|
|
81
|
+
const isSortable = this.mode === Mode.SORTABLE;
|
|
82
|
+
return merge({
|
|
83
|
+
draggable: true,
|
|
84
|
+
resizable: true,
|
|
85
|
+
rootContainer: this.getRootContainer(),
|
|
86
|
+
zoom: 1,
|
|
87
|
+
throttleDrag: 0,
|
|
88
|
+
snappable: true,
|
|
89
|
+
horizontalGuidelines: this.horizontalGuidelines,
|
|
90
|
+
verticalGuidelines: this.verticalGuidelines,
|
|
91
|
+
elementGuidelines: this.elementGuidelines,
|
|
92
|
+
bounds: {
|
|
93
|
+
top: 0,
|
|
94
|
+
left: 0,
|
|
95
|
+
right: this.container.clientWidth,
|
|
96
|
+
bottom: isSortable ? void 0 : this.container.clientHeight
|
|
97
|
+
}
|
|
98
|
+
}, isMultiSelect ? this.getMultiOptions() : this.getSingleOptions());
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 获取单选下的差异化参数
|
|
102
|
+
* @returns {MoveableOptions} moveable options参数
|
|
103
|
+
*/
|
|
104
|
+
getSingleOptions() {
|
|
105
|
+
const isAbsolute = this.mode === Mode.ABSOLUTE;
|
|
106
|
+
const isFixed = this.mode === Mode.FIXED;
|
|
107
|
+
return {
|
|
108
|
+
origin: false,
|
|
109
|
+
dragArea: false,
|
|
110
|
+
scalable: false,
|
|
111
|
+
rotatable: false,
|
|
112
|
+
snapGap: isAbsolute || isFixed,
|
|
113
|
+
snapThreshold: 5,
|
|
114
|
+
snapDigit: 0,
|
|
115
|
+
isDisplaySnapDigit: isAbsolute,
|
|
116
|
+
snapDirections: {
|
|
117
|
+
top: isAbsolute,
|
|
118
|
+
right: isAbsolute,
|
|
119
|
+
bottom: isAbsolute,
|
|
120
|
+
left: isAbsolute,
|
|
121
|
+
center: isAbsolute,
|
|
122
|
+
middle: isAbsolute
|
|
123
|
+
},
|
|
124
|
+
elementSnapDirections: {
|
|
125
|
+
top: isAbsolute,
|
|
126
|
+
right: isAbsolute,
|
|
127
|
+
bottom: isAbsolute,
|
|
128
|
+
left: isAbsolute
|
|
129
|
+
},
|
|
130
|
+
isDisplayInnerSnapDigit: true,
|
|
131
|
+
dragTarget: ".moveable-drag-area-button",
|
|
132
|
+
dragTargetSelf: true,
|
|
133
|
+
props: { actions: true },
|
|
134
|
+
ables: [MoveableActionsAble_default(this.actionHandler.bind(this))]
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* 获取多选下的差异化参数
|
|
139
|
+
* @returns {MoveableOptions} moveable options参数
|
|
140
|
+
*/
|
|
141
|
+
getMultiOptions() {
|
|
142
|
+
return {
|
|
143
|
+
defaultGroupRotate: 0,
|
|
144
|
+
defaultGroupOrigin: "50% 50%",
|
|
145
|
+
startDragRotate: 0,
|
|
146
|
+
throttleDragRotate: 0,
|
|
147
|
+
origin: true,
|
|
148
|
+
padding: {
|
|
149
|
+
left: 0,
|
|
150
|
+
top: 0,
|
|
151
|
+
right: 0,
|
|
152
|
+
bottom: 0
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* 获取业务方自定义的moveable参数
|
|
158
|
+
*/
|
|
159
|
+
getCustomizeOptions() {
|
|
160
|
+
if (typeof this.customizedOptions === "function") return this.customizedOptions();
|
|
161
|
+
return this.customizedOptions;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* 这是给selectParentAbles的回调函数,用于触发选中父元素事件
|
|
165
|
+
*/
|
|
166
|
+
actionHandler(type) {
|
|
167
|
+
this.emit(type);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 为需要辅助对齐的元素创建div
|
|
171
|
+
* @param selectedElList 选中的元素列表,需要排除在对齐元素之外
|
|
172
|
+
* @param allElList 全部元素列表
|
|
173
|
+
* @returns frame 辅助对齐元素集合的页面片
|
|
174
|
+
*/
|
|
175
|
+
createGuidelineElements(selectedElList, allElList) {
|
|
176
|
+
const frame = globalThis.document.createDocumentFragment();
|
|
177
|
+
for (const element of allElList) {
|
|
178
|
+
let node = element.element || element;
|
|
179
|
+
if (!node || typeof node === "string") continue;
|
|
180
|
+
if (typeof node === "function") node = node();
|
|
181
|
+
if (this.isInElementList(node, selectedElList)) continue;
|
|
182
|
+
const { width, height } = node.getBoundingClientRect();
|
|
183
|
+
if (!width || !height) continue;
|
|
184
|
+
const { left, top } = getOffset(node);
|
|
185
|
+
const elementGuideline = globalThis.document.createElement("div");
|
|
186
|
+
elementGuideline.style.cssText = `position: absolute;width: ${width}px;height: ${height}px;top: ${top}px;left: ${left}px`;
|
|
187
|
+
this.elementGuidelines.push(elementGuideline);
|
|
188
|
+
frame.append(elementGuideline);
|
|
189
|
+
}
|
|
190
|
+
return frame;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* 判断一个元素是否在元素列表里面
|
|
194
|
+
* @param ele 元素
|
|
195
|
+
* @param eleList 元素列表
|
|
196
|
+
* @returns 是否在元素列表里面
|
|
197
|
+
*/
|
|
198
|
+
isInElementList(ele, eleList) {
|
|
199
|
+
for (const eleItem of eleList) if (ele === eleItem) return true;
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
//#endregion
|
|
204
|
+
export { MoveableOptionsManager as default };
|
package/dist/es/Rule.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { GuidesType } from "./const.js";
|
|
2
|
+
import EventEmitter$1 from "events";
|
|
3
|
+
import Guides from "@scena/guides";
|
|
4
|
+
//#region packages/stage/src/Rule.ts
|
|
5
|
+
var guidesClass = "tmagic-stage-guides";
|
|
6
|
+
var Rule = class extends EventEmitter$1 {
|
|
7
|
+
hGuides;
|
|
8
|
+
vGuides;
|
|
9
|
+
horizontalGuidelines = [];
|
|
10
|
+
verticalGuidelines = [];
|
|
11
|
+
container;
|
|
12
|
+
containerResizeObserver;
|
|
13
|
+
isShowGuides = true;
|
|
14
|
+
guidesOptions;
|
|
15
|
+
constructor(container, options) {
|
|
16
|
+
super();
|
|
17
|
+
if (options?.disabledRule) return;
|
|
18
|
+
this.guidesOptions = options?.guidesOptions || {};
|
|
19
|
+
this.container = container;
|
|
20
|
+
this.hGuides = this.createGuides(GuidesType.HORIZONTAL, this.horizontalGuidelines);
|
|
21
|
+
this.vGuides = this.createGuides(GuidesType.VERTICAL, this.verticalGuidelines);
|
|
22
|
+
this.containerResizeObserver = new ResizeObserver(() => {
|
|
23
|
+
this.vGuides?.resize();
|
|
24
|
+
this.hGuides?.resize();
|
|
25
|
+
});
|
|
26
|
+
this.containerResizeObserver.observe(this.container);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 是否显示辅助线
|
|
30
|
+
* @param isShowGuides 是否显示
|
|
31
|
+
*/
|
|
32
|
+
showGuides(isShowGuides = true) {
|
|
33
|
+
this.isShowGuides = isShowGuides;
|
|
34
|
+
this.hGuides?.setState({ showGuides: isShowGuides });
|
|
35
|
+
this.vGuides?.setState({ showGuides: isShowGuides });
|
|
36
|
+
}
|
|
37
|
+
setGuides([hLines, vLines]) {
|
|
38
|
+
this.horizontalGuidelines = hLines;
|
|
39
|
+
this.verticalGuidelines = vLines;
|
|
40
|
+
this.hGuides?.setState({ defaultGuides: hLines });
|
|
41
|
+
this.vGuides?.setState({ defaultGuides: vLines });
|
|
42
|
+
this.emit("change-guides", {
|
|
43
|
+
type: GuidesType.HORIZONTAL,
|
|
44
|
+
guides: hLines
|
|
45
|
+
});
|
|
46
|
+
this.emit("change-guides", {
|
|
47
|
+
type: GuidesType.VERTICAL,
|
|
48
|
+
guides: vLines
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 清空所有参考线
|
|
53
|
+
*/
|
|
54
|
+
clearGuides() {
|
|
55
|
+
this.setGuides([[], []]);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 是否显示标尺
|
|
59
|
+
* @param show 是否显示
|
|
60
|
+
*/
|
|
61
|
+
showRule(show = true) {
|
|
62
|
+
if (show) {
|
|
63
|
+
this.destroyGuides();
|
|
64
|
+
this.hGuides = this.createGuides(GuidesType.HORIZONTAL, this.horizontalGuidelines);
|
|
65
|
+
this.vGuides = this.createGuides(GuidesType.VERTICAL, this.verticalGuidelines);
|
|
66
|
+
} else {
|
|
67
|
+
this.hGuides?.setState({ rulerStyle: { visibility: "hidden" } });
|
|
68
|
+
this.vGuides?.setState({ rulerStyle: { visibility: "hidden" } });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
scrollRule(scrollTop) {
|
|
72
|
+
this.hGuides?.scrollGuides(scrollTop);
|
|
73
|
+
this.hGuides?.scroll(0);
|
|
74
|
+
this.vGuides?.scrollGuides(0);
|
|
75
|
+
this.vGuides?.scroll(scrollTop);
|
|
76
|
+
}
|
|
77
|
+
destroy() {
|
|
78
|
+
this.destroyGuides();
|
|
79
|
+
this.hGuides?.off("changeGuides", this.hGuidesChangeGuidesHandler);
|
|
80
|
+
this.vGuides?.off("changeGuides", this.vGuidesChangeGuidesHandler);
|
|
81
|
+
this.containerResizeObserver?.disconnect();
|
|
82
|
+
this.removeAllListeners();
|
|
83
|
+
}
|
|
84
|
+
destroyGuides() {
|
|
85
|
+
this.hGuides?.destroy();
|
|
86
|
+
this.vGuides?.destroy();
|
|
87
|
+
this.container?.querySelectorAll(`.${guidesClass}`).forEach((el) => {
|
|
88
|
+
el.remove();
|
|
89
|
+
});
|
|
90
|
+
this.hGuides = void 0;
|
|
91
|
+
this.vGuides = void 0;
|
|
92
|
+
this.container = void 0;
|
|
93
|
+
}
|
|
94
|
+
getGuidesStyle = (type) => ({
|
|
95
|
+
position: "fixed",
|
|
96
|
+
zIndex: 1,
|
|
97
|
+
left: type === GuidesType.HORIZONTAL ? 0 : "-30px",
|
|
98
|
+
top: type === GuidesType.HORIZONTAL ? "-30px" : 0,
|
|
99
|
+
width: type === GuidesType.HORIZONTAL ? "100%" : "30px",
|
|
100
|
+
height: type === GuidesType.HORIZONTAL ? "30px" : "100%"
|
|
101
|
+
});
|
|
102
|
+
createGuides = (type, defaultGuides = []) => {
|
|
103
|
+
if (!this.container) return;
|
|
104
|
+
const guides = new Guides(this.container, {
|
|
105
|
+
type,
|
|
106
|
+
defaultGuides,
|
|
107
|
+
displayDragPos: true,
|
|
108
|
+
className: guidesClass,
|
|
109
|
+
backgroundColor: "#fff",
|
|
110
|
+
lineColor: "#000",
|
|
111
|
+
textColor: "#000",
|
|
112
|
+
style: this.getGuidesStyle(type),
|
|
113
|
+
showGuides: this.isShowGuides,
|
|
114
|
+
...this.guidesOptions
|
|
115
|
+
});
|
|
116
|
+
const changEventHandler = {
|
|
117
|
+
[GuidesType.HORIZONTAL]: this.hGuidesChangeGuidesHandler,
|
|
118
|
+
[GuidesType.VERTICAL]: this.vGuidesChangeGuidesHandler
|
|
119
|
+
}[type];
|
|
120
|
+
if (changEventHandler) guides.on("changeGuides", changEventHandler);
|
|
121
|
+
return guides;
|
|
122
|
+
};
|
|
123
|
+
hGuidesChangeGuidesHandler = (e) => {
|
|
124
|
+
this.horizontalGuidelines = e.guides;
|
|
125
|
+
this.emit("change-guides", {
|
|
126
|
+
type: GuidesType.HORIZONTAL,
|
|
127
|
+
guides: this.horizontalGuidelines
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
vGuidesChangeGuidesHandler = (e) => {
|
|
131
|
+
this.verticalGuidelines = e.guides;
|
|
132
|
+
this.emit("change-guides", {
|
|
133
|
+
type: GuidesType.VERTICAL,
|
|
134
|
+
guides: this.verticalGuidelines
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
//#endregion
|
|
139
|
+
export { Rule as default };
|