) => {
+ if (e.keyCode === 13) {
+ this.saveEdit(e);
+ }
+ if (e.keyCode === 27) {
+ this.cancelEdit();
+ }
+ };
+
+ private lastInput?: HTMLInputElement;
+
+ private setCaret = (input: HTMLInputElement | null) => {
+ if (!input || this.lastInput === input) {
+ return;
+ }
+ input.focus();
+ input.select();
+ // 光标定位最后一个
+ // input.selectionStart = input.selectionEnd;
+ };
+
+ render() {
+ const { treeNode, isModal } = this.props;
+ const { editing } = this.state;
+ const isCNode = !treeNode.isRoot();
+ const { node } = treeNode;
+ const isNodeParent = node.isParental();
+ let style: any;
+ if (isCNode) {
+ const { depth } = treeNode;
+ const indent = depth * 12;
+ style = {
+ paddingLeft: indent + (isModal ? 12 : 0),
+ marginLeft: -indent,
+ };
+ }
+
+ return (
+ {
+ if (isModal) {
+ node.document.modalNodesManager.setVisible(node);
+ return;
+ }
+ if (node.conditionGroup) {
+ node.setConditionalVisible();
+ }
+ }}
+ >
+ {isModal && node.getVisible() && (
+
{
+ node.document.modalNodesManager.setInvisible(node);
+ }}
+ >
+
+
+ )}
+ {isModal && !node.getVisible() && (
+
{
+ node.document.modalNodesManager.setVisible(node);
+ }}
+ >
+
+
+ )}
+ {isCNode &&
}
+
{createIcon(treeNode.icon)}
+
+ {isCNode && isNodeParent && !isModal &&
}
+ {/* isCNode && isNodeParent &&
*/}
+
+ );
+ }
+}
+
+// @observer
+// class LockBtn extends Component<{ treeNode: TreeNode }> {
+// shouldComponentUpdate() {
+// return false;
+// }
+
+// render() {
+// const { treeNode } = this.props;
+// return (
+// {
+// e.stopPropagation();
+// treeNode.setLocked(!treeNode.locked);
+// }}
+// >
+// {treeNode.locked ? : }
+// {treeNode.locked ? intl('Unlock') : intl('Lock')}
+//
+// );
+// }
+// }
+
+@observer
+class HideBtn extends Component<{ treeNode: TreeNode }> {
+ shouldComponentUpdate() {
+ return false;
+ }
+
+ render() {
+ const { treeNode } = this.props;
+ return (
+ {
+ e.stopPropagation();
+ emitOutlineEvent(treeNode.hidden ? 'show' : 'hide', treeNode);
+ treeNode.setHidden(!treeNode.hidden);
+ }}
+ >
+ {treeNode.hidden ? : }
+ {treeNode.hidden ? intl('Show') : intl('Hide')}
+
+ );
+ }
+}
+
+@observer
+class ExpandBtn extends Component<{ treeNode: TreeNode }> {
+ shouldComponentUpdate() {
+ return false;
+ }
+
+ render() {
+ const { treeNode } = this.props;
+ if (!treeNode.expandable) {
+ return ;
+ }
+ return (
+ {
+ if (treeNode.expanded) {
+ e.stopPropagation();
+ }
+ emitOutlineEvent(treeNode.expanded ? 'collapse' : 'expand', treeNode);
+ treeNode.setExpanded(!treeNode.expanded);
+ }}
+ >
+
+
+ );
+ }
+}
+
+/*
+interface Point {
+ clientX: number;
+ clientY: number;
+}
+
+function setCaret(point: Point) {
+ debugger;
+ const range = getRangeFromPoint(point);
+ if (range) {
+ selectRange(range);
+ setTimeout(() => selectRange(range), 1);
+ }
+}
+
+function getRangeFromPoint(point: Point): Range | undefined {
+ const x = point.clientX;
+ const y = point.clientY;
+ let range;
+ let pos: CaretPosition | null = null;
+ if (document.caretRangeFromPoint) {
+ range = document.caretRangeFromPoint(x, y);
+ } else if ((pos = document.caretPositionFromPoint(x, y))) {
+ range = document.createRange();
+ range.setStart(pos.offsetNode, pos.offset);
+ range.collapse(true);
+
+ }
+ return range;
+}
+
+function selectRange(range: Range) {
+ const selection = document.getSelection();
+ if (selection) {
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+}
+
+function setCaretAfter(elem) {
+ const range = document.createRange();
+ const node = elem.lastChild;
+ if (!node) return;
+ range.setStartAfter(node);
+ range.setEndAfter(node);
+ selectRange(range);
+}
+*/
diff --git a/packages/plugin-outline-pane/src/views/tree.tsx b/packages/plugin-outline-pane/src/views/tree.tsx
new file mode 100644
index 000000000..4f7871e96
--- /dev/null
+++ b/packages/plugin-outline-pane/src/views/tree.tsx
@@ -0,0 +1,165 @@
+import { Component, MouseEvent as ReactMouseEvent } from 'react';
+import { observer, Editor, globalContext } from '@ali/lowcode-editor-core';
+import { isRootNode, Node, DragObjectType, isShaken } from '@ali/lowcode-designer';
+import { isFormEvent } from '@ali/lowcode-utils';
+import { Tree } from '../tree';
+import RootTreeNodeView from './root-tree-node';
+
+function getTreeNodeIdByEvent(e: ReactMouseEvent, stop: Element): null | string {
+ let target: Element | null = e.target as Element;
+ if (!target || !stop.contains(target)) {
+ return null;
+ }
+ target = target.closest('[data-id]');
+ if (!target || !stop.contains(target)) {
+ return null;
+ }
+
+ return (target as HTMLDivElement).dataset.id || null;
+}
+
+@observer
+export default class TreeView extends Component<{ tree: Tree }> {
+ private shell: HTMLDivElement | null = null;
+
+ private hover(e: ReactMouseEvent) {
+ const { tree } = this.props;
+
+ const doc = tree.document;
+ const { detecting } = doc.designer;
+ if (!detecting.enable) {
+ return;
+ }
+ const node = this.getTreeNodeFromEvent(e)?.node;
+ detecting.capture(node || null);
+ }
+
+ private onClick = (e: ReactMouseEvent) => {
+ if (this.ignoreUpSelected) {
+ this.boostEvent = undefined;
+ return;
+ }
+ if (this.boostEvent && isShaken(this.boostEvent, e.nativeEvent)) {
+ this.boostEvent = undefined;
+ return;
+ }
+ this.boostEvent = undefined;
+ const treeNode = this.getTreeNodeFromEvent(e);
+ if (!treeNode) {
+ return;
+ }
+ const { node } = treeNode;
+ const { designer } = treeNode;
+ const doc = node.document;
+ const { selection } = doc;
+ const { id } = node;
+ const isMulti = e.metaKey || e.ctrlKey || e.shiftKey;
+ designer.activeTracker.track(node);
+ if (isMulti && !isRootNode(node) && selection.has(id)) {
+ if (!isFormEvent(e.nativeEvent)) {
+ selection.remove(id);
+ }
+ } else {
+ selection.select(id);
+ const editor = globalContext.get(Editor);
+ const selectedNode = designer.currentSelection?.getNodes()?.[0];
+ const npm = selectedNode?.componentMeta?.npm;
+ const selected =
+ [npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
+ selectedNode?.componentMeta?.componentName ||
+ '';
+ editor?.emit('outlinePane.select', {
+ selected,
+ });
+ }
+ };
+
+ private onMouseOver = (e: ReactMouseEvent) => {
+ this.hover(e);
+ };
+
+ private getTreeNodeFromEvent(e: ReactMouseEvent) {
+ if (!this.shell) {
+ return;
+ }
+ const id = getTreeNodeIdByEvent(e, this.shell);
+ if (!id) {
+ return;
+ }
+
+ const { tree } = this.props;
+ return tree.getTreeNodeById(id);
+ }
+
+ private ignoreUpSelected = false;
+
+ private boostEvent?: MouseEvent;
+
+ private onMouseDown = (e: ReactMouseEvent) => {
+ if (isFormEvent(e.nativeEvent)) {
+ return;
+ }
+ const treeNode = this.getTreeNodeFromEvent(e);
+ if (!treeNode) {
+ return;
+ }
+
+ const { node } = treeNode;
+ const { designer } = treeNode;
+ const doc = node.document;
+ const { selection } = doc;
+
+ // TODO: shift selection
+ const isMulti = e.metaKey || e.ctrlKey || e.shiftKey;
+ const isLeftButton = e.button === 0;
+
+ if (isLeftButton && !isRootNode(node)) {
+ let nodes: Node[] = [node];
+ this.ignoreUpSelected = false;
+ if (isMulti) {
+ // multi select mode, directily add
+ if (!selection.has(node.id)) {
+ designer.activeTracker.track(node);
+ selection.add(node.id);
+ this.ignoreUpSelected = true;
+ }
+ selection.remove(doc.rootNode.id);
+ // 获得顶层 nodes
+ nodes = selection.getTopNodes();
+ } else if (selection.has(node.id)) {
+ nodes = selection.getTopNodes();
+ }
+ this.boostEvent = e.nativeEvent;
+ designer.dragon.boost(
+ {
+ type: DragObjectType.Node,
+ nodes,
+ },
+ this.boostEvent,
+ );
+ }
+ };
+
+ private onMouseLeave = () => {
+ const { tree } = this.props;
+ const doc = tree.document;
+ doc.designer.detecting.leave(doc);
+ };
+
+ render() {
+ const { tree } = this.props;
+ const { root } = tree;
+ return (
+ { this.shell = shell; }}
+ onMouseDownCapture={this.onMouseDown}
+ onMouseOver={this.onMouseOver}
+ onClick={this.onClick}
+ onMouseLeave={this.onMouseLeave}
+ >
+
+
+ );
+ }
+}