in packages/designer/src/builtin-simulator/host.ts [535:712]
setupDragAndClick() {
const { designer } = this;
const doc = this.contentDocument!;
// TODO: think of lock when edit a node
// 事件路由
doc.addEventListener(
'mousedown',
(downEvent: MouseEvent) => {
// fix for popups close logic
document.dispatchEvent(new Event('mousedown'));
const documentModel = this.project.currentDocument;
if (this.liveEditing.editing || !documentModel) {
return;
}
const { selection } = documentModel;
let isMulti = false;
if (this.designMode === 'design') {
isMulti = downEvent.metaKey || downEvent.ctrlKey;
} else if (!downEvent.metaKey) {
return;
}
// FIXME: dirty fix remove label-for fro liveEditing
downEvent.target?.removeAttribute('for');
const nodeInst = this.getNodeInstanceFromElement(downEvent.target);
const { focusNode } = documentModel;
const node = getClosestClickableNode(nodeInst?.node || focusNode, downEvent);
// 如果找不到可点击的节点,直接返回
if (!node) {
return;
}
// 触发 onMouseDownHook 钩子
const onMouseDownHook = node.componentMeta.advanced.callbacks?.onMouseDownHook;
if (onMouseDownHook) {
onMouseDownHook(downEvent, node.internalToShellNode());
}
const rglNode = node?.getParent();
const isRGLNode = rglNode?.isRGLContainer;
if (isRGLNode) {
// 如果拖拽的是磁铁块的右下角 handle,则直接跳过
if (downEvent.target?.classList.contains('react-resizable-handle')) return;
// 禁止多选
isMulti = false;
designer.dragon.emitter.emit('rgl.switch', {
action: 'start',
rglNode,
});
} else {
// stop response document focus event
// 禁止原生拖拽
downEvent.stopPropagation();
downEvent.preventDefault();
}
// if (!node?.isValidComponent()) {
// // 对于未注册组件直接返回
// return;
// }
const isLeftButton = downEvent.which === 1 || downEvent.button === 0;
const checkSelect = (e: MouseEvent) => {
doc.removeEventListener('mouseup', checkSelect, true);
// 取消移动;
designer.dragon.emitter.emit('rgl.switch', {
action: 'end',
rglNode,
});
// 鼠标是否移动 ? - 鼠标抖动应该也需要支持选中事件,偶尔点击不能选中,磁帖块移除 shaken 检测
if (!isShaken(downEvent, e) || isRGLNode) {
let { id } = node;
designer.activeTracker.track({ node, instance: nodeInst?.instance });
if (isMulti && focusNode && !node.contains(focusNode) && selection.has(id)) {
selection.remove(id);
} else {
// TODO: 避免选中 Page 组件,默认选中第一个子节点;新增规则 或 判断 Live 模式
if (node.isPage() && node.getChildren()?.notEmpty() && this.designMode === 'live') {
const firstChildId = node.getChildren()?.get(0)?.getId();
if (firstChildId) id = firstChildId;
}
if (focusNode) {
selection.select(node.contains(focusNode) ? focusNode.id : id);
}
// dirty code should refector
const editor = this.designer?.editor;
const npm = node?.componentMeta?.npm;
const selected =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
node?.componentMeta?.componentName ||
'';
editor?.eventBus.emit('designer.builtinSimulator.select', {
selected,
});
}
}
};
if (isLeftButton && focusNode && !node.contains(focusNode)) {
let nodes: INode[] = [node];
let ignoreUpSelected = false;
if (isMulti) {
// multi select mode, directily add
if (!selection.has(node.id)) {
designer.activeTracker.track({ node, instance: nodeInst?.instance });
selection.add(node.id);
ignoreUpSelected = true;
}
focusNode?.id && selection.remove(focusNode.id);
// 获得顶层 nodes
nodes = selection.getTopNodes();
} else if (selection.containsNode(node, true)) {
nodes = selection.getTopNodes();
} else {
// will clear current selection & select dragment in dragstart
}
designer.dragon.boost(
{
type: IPublicEnumDragObjectType.Node,
nodes,
},
downEvent,
isRGLNode ? rglNode : undefined,
);
if (ignoreUpSelected) {
// multi select mode has add selected, should return
return;
}
}
doc.addEventListener('mouseup', checkSelect, true);
},
true,
);
doc.addEventListener(
'click',
(e) => {
// fix for popups close logic
const x = new Event('click');
x.initEvent('click', true);
this._iframe?.dispatchEvent(x);
const { target } = e;
const customizeIgnoreSelectors = engineConfig.get('customizeIgnoreSelectors');
// TODO: need more elegant solution to ignore click events of components in designer
const defaultIgnoreSelectors: string[] = [
'.next-input-group',
'.next-checkbox-group',
'.next-checkbox-wrapper',
'.next-date-picker',
'.next-input',
'.next-month-picker',
'.next-number-picker',
'.next-radio-group',
'.next-range',
'.next-range-picker',
'.next-rating',
'.next-select',
'.next-switch',
'.next-time-picker',
'.next-upload',
'.next-year-picker',
'.next-breadcrumb-item',
'.next-calendar-header',
'.next-calendar-table',
'.editor-container', // 富文本组件
];
const ignoreSelectors = customizeIgnoreSelectors?.(defaultIgnoreSelectors, e) || defaultIgnoreSelectors;
const ignoreSelectorsString = ignoreSelectors.join(',');
// 提供了 customizeIgnoreSelectors 的情况下,忽略 isFormEvent() 判断
if ((!customizeIgnoreSelectors && isFormEvent(e)) || target?.closest(ignoreSelectorsString)) {
e.preventDefault();
e.stopPropagation();
}
// stop response document click event
// todo: catch link redirect
},
true,
);
}