mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-13 01:21:58 +00:00
feat: support drill down logic by kangwei to #35417409
This commit is contained in:
parent
cfde6e49b9
commit
98065b7a62
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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)) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user