setupDragAndClick()

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,
    );
  }