feat: support drill down logic by kangwei to #35417409

This commit is contained in:
alex.mm 2021-07-15 14:07:09 +08:00
parent cfde6e49b9
commit 98065b7a62
11 changed files with 115 additions and 48 deletions

View File

@ -80,6 +80,26 @@ export class BorderDetecting extends Component<{ host: BuiltinSimulatorHost }> {
if (!canHover || !current || host.viewport.scrolling || host.liveEditing.editing) {
return null;
}
// rootNode, hover whole viewport
const focusNode = current.document.focusNode;
if (current.contains(focusNode)) {
const bounds = host.viewport.bounds;
return (
<BorderDetectingInstance
key="line-root"
title={current.title}
scale={this.scale}
scrollX={host.viewport.scrollX}
scrollY={host.viewport.scrollY}
rect={new DOMRect(0, 0, bounds.width, bounds.height)}
/>
);
} else if (!focusNode.contains(current)) {
return null;
}
const instances = host.getComponentInstances(current);
if (!instances || instances.length < 1) {
return null;

View File

@ -540,7 +540,8 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
// FIXME: dirty fix remove label-for fro liveEditing
(downEvent.target as HTMLElement).removeAttribute('for');
const nodeInst = this.getNodeInstanceFromElement(downEvent.target as Element);
const node = getClosestClickableNode(nodeInst?.node || documentModel?.rootNode, downEvent);
const focusNode = documentModel.focusNode;
const node = getClosestClickableNode(nodeInst?.node || focusNode, downEvent);
// 如果找不到可点击的节点, 直接返回
if (!node) {
return;
@ -624,7 +625,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
if (!isShaken(downEvent, e) || isRGLNode) {
let { id } = node;
designer.activeTracker.track({ node, instance: nodeInst?.instance });
if (isMulti && !isRootNode(node) && selection.has(id)) {
if (isMulti && !node.contains(focusNode) && selection.has(id)) {
selection.remove(id);
} else {
// TODO: 避免选中 Page 组件,默认选中第一个子节点;新增规则 或 判断 Live 模式
@ -632,7 +633,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
const firstChildId = node.getChildren()?.get(0)?.getId();
if (firstChildId) id = firstChildId;
}
selection.select(id);
selection.select(node.contains(focusNode) ? focusNode.id : id);
// dirty code should refector
const editor = this.designer?.editor;
@ -647,7 +648,8 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
}
}
};
if (isLeftButton && !isRootNode(node)) {
if (isLeftButton && !node.contains(focusNode)) {
let nodes: Node[] = [node];
let ignoreUpSelected = false;
if (isMulti) {
@ -657,7 +659,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
selection.add(node.id);
ignoreUpSelected = true;
}
selection.remove(documentModel.rootNode.id);
selection.remove(focusNode.id);
// 获得顶层 nodes
nodes = selection.getTopNodes();
} else if (selection.containsNode(node, true)) {
@ -748,7 +750,16 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
return;
}
const nodeInst = this.getNodeInstanceFromElement(e.target as Element);
detecting.capture(nodeInst?.node || null);
if (nodeInst?.node) {
let node = nodeInst.node;
const focusNode = node.document.focusNode;
if (node.contains(focusNode)) {
node = focusNode;
}
detecting.capture(node);
} else {
detecting.capture(null);
}
if (!engineConfig.get('enableMouseEventPropagationInCanvas', false) || dragon.dragging) {
e.stopPropagation();
}
@ -794,7 +805,8 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
if (!nodeInst) {
return;
}
const node = nodeInst.node || this.project.currentDocument?.rootNode;
const focusNode = this.project.currentDocument!.focusNode;
const node = nodeInst.node || focusNode;
if (!node || isLowCodeComponent(node)) {
return;
}
@ -851,7 +863,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
if (!nodeInst) {
return;
}
const node = nodeInst.node || this.project.currentDocument?.rootNode;
const node = nodeInst.node || this.project.currentDocument?.focusNode;
if (!node) {
return;
}
@ -1300,7 +1312,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
e.dragObject.nodes[0].componentMeta.isModal
) {
return this.designer.createLocation({
target: document.rootNode,
target: document.focusNode,
detail,
source: `simulator${document.id}`,
event: e,
@ -1454,7 +1466,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
}
}
if (p !== container) {
container = p || document.rootNode;
container = p || document.focusNode;
drillDownExcludes.add(container);
}
}
@ -1543,8 +1555,9 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
handleAccept({ container, instance }: DropContainer, e: LocateEvent): boolean {
const { dragObject } = e;
const document = this.currentDocument!;
if (isRootNode(container)) {
return document.checkDropTarget(container, dragObject as any);
const focusNode = document.focusNode;
if (isRootNode(container) || container.contains(focusNode)) {
return document.checkDropTarget(focusNode, dragObject as any);
}
const meta = (container as Node).componentMeta;

View File

@ -31,7 +31,13 @@ export default class InstanceNodeSelector extends React.Component<IProps, IState
// 获取节点的父级节点最多获取5层
getParentNodes = (node: Node) => {
const parentNodes = [];
const parentNodes: any[] = [];
const focusNode = node.document.focusNode;
if (node.contains(focusNode) || !focusNode.contains(node)) {
return parentNodes;
}
let currentNode: UnionNode = node;
while (currentNode && parentNodes.length < 5) {
@ -39,6 +45,9 @@ export default class InstanceNodeSelector extends React.Component<IProps, IState
if (currentNode) {
parentNodes.push(currentNode);
}
if (currentNode === focusNode) {
break;
}
}
return parentNodes;
};
@ -72,7 +81,10 @@ export default class InstanceNodeSelector extends React.Component<IProps, IState
};
renderNodes = (/* node: Node */) => {
const nodes = this.state.parentNodes || [];
const nodes = this.state.parentNodes;
if (!nodes || nodes.length < 1) {
return null;
}
const children = nodes.map((node, key) => {
return (
<div

View File

@ -13,14 +13,7 @@ import {
} from '@ali/lowcode-types';
import { megreAssets, AssetsJson } from '@ali/lowcode-utils';
import { Project } from '../project';
import {
Node,
DocumentModel,
insertChildren,
isRootNode,
ParentalNode,
TransformStage,
} from '../document';
import { Node, DocumentModel, insertChildren, ParentalNode, TransformStage } from '../document';
import { ComponentMeta } from '../component-meta';
import { INodeSelector, Component } from '../simulator';
import { Scroller, IScrollable } from './scroller';
@ -45,6 +38,7 @@ export interface DesignerProps {
suspensed?: boolean;
componentMetadatas?: ComponentMetadata[];
globalComponentActions?: ComponentAction[];
focusNodeSelector?: (rootNode: Node) => Node;
onMount?: (designer: Designer) => void;
onDragstart?: (e: LocateEvent) => void;
onDrag?: (e: LocateEvent) => void;
@ -328,19 +322,20 @@ export class Designer {
target: activedDoc.rootNode as ParentalNode,
};
}
const focusNode = activedDoc.focusNode;
const nodes = activedDoc.selection.getNodes();
const refNode = nodes.find(item => focusNode.contains(item));
let target;
let index: number | undefined;
if (!nodes || nodes.length < 1) {
target = activedDoc.rootNode;
if (!refNode || refNode === focusNode) {
target = focusNode;
} else {
const node = nodes[0];
if (isRootNode(node) || node.componentMeta.isContainer) {
target = node;
if (refNode.componentMeta.isContainer) {
target = refNode;
} else {
// FIXME!!, parent maybe null
target = node.parent!;
index = node.index + 1;
target = refNode.parent!;
index = refNode.index + 1;
}
}

View File

@ -105,7 +105,8 @@ export class OffsetObserver {
this.node = node;
const doc = node.document;
const host = doc.simulator!;
this.isRoot = isRootNode(node);
const focusNode = doc.focusNode;
this.isRoot = node.contains(focusNode);
this.viewport = host.viewport;
if (this.isRoot) {
this.hasOffset = true;

View File

@ -93,6 +93,23 @@ export class DocumentModel {
this.rootNode?.getExtraProp('fileName', true)?.setValue(fileName);
}
@computed get focusNode() {
if (this._drillDownNode) {
return this._drillDownNode;
}
const selector = this.designer.get('focusNodeSelector');
if (typeof selector === 'function') {
return selector(this.rootNode);
}
return this.rootNode;
}
@obx.ref private _drillDownNode: Node | null = null;
drillDown(node: Node | null) {
this._drillDownNode = node;
}
private _modalNode?: ParentalNode;
private _blank?: boolean;
@ -151,7 +168,7 @@ export class DocumentModel {
}
get currentRoot() {
return this.modalNode || this.rootNode;
return this.modalNode || this.focusNode;
}
addWillPurge(node: Node) {
@ -346,6 +363,7 @@ export class DocumentModel {
}
import(schema: RootSchema, checkId = false) {
const drillDownNodeId = this._drillDownNode?.id;
// TODO: 暂时用饱和式删除,原因是 Slot 节点并不是树节点,无法正常递归删除
this.nodes.forEach(node => {
if (node.isRoot()) return;
@ -363,6 +381,9 @@ export class DocumentModel {
}
// todo: select added and active track added
if (drillDownNodeId) {
this.drillDown(this.getNode(drillDownNodeId));
}
}
export(stage: TransformStage = TransformStage.Serilize) {

View File

@ -102,7 +102,7 @@ export class Selection {
containsNode(node: Node, excludeRoot = false) {
for (const id of this._selected) {
const parent = this.doc.getNode(id);
if (excludeRoot && parent === this.doc.rootNode) {
if (excludeRoot && parent?.contains(this.doc.focusNode)) {
continue;
}
if (parent?.contains(node)) {
@ -134,7 +134,7 @@ export class Selection {
for (const id of this._selected) {
const node = this.doc.getNode(id);
// 排除根节点
if (!node || (!includeRoot && node === this.doc.rootNode)) {
if (!node || (!includeRoot && node.contains(this.doc.focusNode))) {
continue;
}
let i = nodes.length;

View File

@ -64,13 +64,18 @@ export class SettingsPrimaryPane extends Component<{ editor: Editor; config: any
const designer = editor.get(Designer);
const current = designer?.currentSelection?.getNodes()?.[0];
let node: Node | null = settings.first;
const focusNode = node.document.focusNode;
const items = [];
let l = 3;
while (l-- > 0 && node) {
const _node = node;
// dirty code: should remove
if (shouldIgnoreRoot && node.isRoot()) {
node = null;
continue;
break;
}
if (node.contains(focusNode)) {
l = 0;
}
const props =
l === 2

View File

@ -164,7 +164,7 @@ export class OutlineMain implements ISensor, ITreeBoard, IScrollable {
const componentMeta = e.dragObject.nodes ? e.dragObject.nodes[0].componentMeta : null;
if (e.dragObject.type === 'node' && componentMeta && componentMeta.isModal) {
return designer.createLocation({
target: document.rootNode,
target: document.focusNode,
detail: {
type: LocationDetailType.Children,
index: 0,
@ -229,7 +229,7 @@ export class OutlineMain implements ISensor, ITreeBoard, IScrollable {
}
}
if (p !== node) {
node = p || document.rootNode;
node = p || document.focusNode;
treeNode = tree.getTreeNode(node);
focusSlots = false;
}

View File

@ -1,18 +1,17 @@
import { DocumentModel, Node } from '@ali/lowcode-designer';
import { computed } from '@ali/lowcode-editor-core';
import TreeNode from './tree-node';
export class Tree {
private treeNodesMap = new Map<string, TreeNode>();
readonly root: TreeNode;
readonly id: string;
readonly document: DocumentModel;
@computed get root(): TreeNode {
return this.getTreeNode(this.document.focusNode);
}
constructor(document: DocumentModel) {
this.document = document;
this.root = this.getTreeNode(document.rootNode);
constructor(readonly document: DocumentModel) {
this.id = document.id;
}

View File

@ -51,11 +51,11 @@ export default class TreeView extends Component<{ tree: Tree }> {
const { node } = treeNode;
const { designer } = treeNode;
const doc = node.document;
const { selection } = doc;
const { selection, focusNode } = doc;
const { id } = node;
const isMulti = e.metaKey || e.ctrlKey || e.shiftKey;
designer.activeTracker.track(node);
if (isMulti && !isRootNode(node) && selection.has(id)) {
if (isMulti && !node.contains(focusNode) && selection.has(id)) {
if (!isFormEvent(e.nativeEvent)) {
selection.remove(id);
}
@ -107,13 +107,13 @@ export default class TreeView extends Component<{ tree: Tree }> {
const { node } = treeNode;
const { designer } = treeNode;
const doc = node.document;
const { selection } = doc;
const { selection, focusNode } = doc;
// TODO: shift selection
const isMulti = e.metaKey || e.ctrlKey || e.shiftKey;
const isLeftButton = e.button === 0;
if (isLeftButton && !isRootNode(node)) {
if (isLeftButton && !node.contains(focusNode)) {
let nodes: Node[] = [node];
this.ignoreUpSelected = false;
if (isMulti) {
@ -123,7 +123,8 @@ export default class TreeView extends Component<{ tree: Tree }> {
selection.add(node.id);
this.ignoreUpSelected = true;
}
selection.remove(doc.rootNode.id);
// todo: remove rootNodes id
selection.remove(focusNode.id);
// 获得顶层 nodes
nodes = selection.getTopNodes();
} else if (selection.has(node.id)) {