mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-11 18:42:56 +00:00
290 lines
6.8 KiB
TypeScript
290 lines
6.8 KiB
TypeScript
import {
|
||
IPublicTypeTitleContent,
|
||
IPublicTypeLocationChildrenDetail,
|
||
IPublicModelNode,
|
||
IPublicModelPluginContext,
|
||
} from '@alilc/lowcode-types';
|
||
import { isI18nData, isLocationChildrenDetail } from '@alilc/lowcode-utils';
|
||
import { Tree } from './tree';
|
||
|
||
/**
|
||
* 大纲树过滤结果
|
||
*/
|
||
export interface FilterResult {
|
||
// 过滤条件是否生效
|
||
filterWorking: boolean;
|
||
// 命中子节点
|
||
matchChild: boolean;
|
||
// 命中本节点
|
||
matchSelf: boolean;
|
||
// 关键字
|
||
keywords: string;
|
||
}
|
||
|
||
export default class TreeNode {
|
||
readonly pluginContext: IPublicModelPluginContext;
|
||
onFilterResultChanged: () => void;
|
||
onExpandedChanged: (expanded: boolean) => void;
|
||
onHiddenChanged: (hidden: boolean) => void;
|
||
onLockedChanged: (locked: boolean) => void;
|
||
onTitleLabelChanged: (treeNode: TreeNode) => void;
|
||
onExpandableChanged: (expandable: boolean) => void;
|
||
|
||
get id(): string {
|
||
return this.node.id;
|
||
}
|
||
|
||
/**
|
||
* 是否可以展开
|
||
*/
|
||
get expandable(): boolean {
|
||
if (this.locked) return false;
|
||
return this.hasChildren() || this.hasSlots() || this.dropDetail?.index != null;
|
||
}
|
||
|
||
/**
|
||
* 触发 onExpandableChanged 回调
|
||
*/
|
||
notifyExpandableChanged(): void {
|
||
this.onExpandableChanged && this.onExpandableChanged(this.expandable);
|
||
}
|
||
|
||
/**
|
||
* 插入"线"位置信息
|
||
*/
|
||
get dropDetail(): IPublicTypeLocationChildrenDetail | undefined | null {
|
||
const loc = this.pluginContext.project.getCurrentDocument()?.dropLocation;
|
||
return loc && this.isResponseDropping() && isLocationChildrenDetail(loc.detail) ? loc.detail : null;
|
||
}
|
||
|
||
get depth(): number {
|
||
return this.node.zLevel;
|
||
}
|
||
|
||
isRoot(includeOriginalRoot = false) {
|
||
const rootNode = this.pluginContext.project.getCurrentDocument()?.root;
|
||
return this.tree.root === this || (includeOriginalRoot && rootNode === this.node);
|
||
}
|
||
|
||
/**
|
||
* 是否是响应投放区
|
||
*/
|
||
isResponseDropping(): boolean {
|
||
const loc = this.pluginContext.project.getCurrentDocument()?.dropLocation;
|
||
if (!loc) {
|
||
return false;
|
||
}
|
||
return loc.target?.id === this.id;
|
||
}
|
||
|
||
isFocusingNode(): boolean {
|
||
const loc = this.pluginContext.project.getCurrentDocument()?.dropLocation;
|
||
if (!loc) {
|
||
return false;
|
||
}
|
||
return (
|
||
isLocationChildrenDetail(loc.detail) && loc.detail.focus?.type === 'node' && loc.detail?.focus?.node.id === this.id
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 默认为折叠状态
|
||
* 在初始化根节点时,设置为展开状态
|
||
*/
|
||
private _expanded = false;
|
||
|
||
get expanded(): boolean {
|
||
return this.isRoot(true) || (this.expandable && this._expanded);
|
||
}
|
||
|
||
setExpanded(value: boolean) {
|
||
this._expanded = value;
|
||
this.onExpandedChanged && this.onExpandedChanged(value);
|
||
}
|
||
|
||
get detecting() {
|
||
const doc = this.pluginContext.project.currentDocument;
|
||
return !!(doc?.isDetectingNode(this.node));
|
||
}
|
||
|
||
get hidden(): boolean {
|
||
const cv = this.node.isConditionalVisible();
|
||
if (cv == null) {
|
||
return !this.node.getVisible();
|
||
}
|
||
return !cv;
|
||
}
|
||
|
||
setHidden(flag: boolean) {
|
||
if (this.node.conditionGroup) {
|
||
return;
|
||
}
|
||
this.node.setVisible(!flag);
|
||
this.onHiddenChanged && this.onHiddenChanged(flag);
|
||
}
|
||
|
||
get locked(): boolean {
|
||
return this.node.isLocked;
|
||
}
|
||
|
||
setLocked(flag: boolean) {
|
||
this.node.lock(flag);
|
||
this.onLockedChanged && this.onLockedChanged(flag);
|
||
}
|
||
|
||
get selected(): boolean {
|
||
// TODO: check is dragging
|
||
const selection = this.pluginContext.project.getCurrentDocument()?.selection;
|
||
if (!selection) {
|
||
return false;
|
||
}
|
||
return selection?.has(this.node.id);
|
||
}
|
||
|
||
get title(): IPublicTypeTitleContent {
|
||
return this.node.title;
|
||
}
|
||
|
||
get titleLabel() {
|
||
let { title } = this;
|
||
if (!title) {
|
||
return '';
|
||
}
|
||
if ((title as any).label) {
|
||
title = (title as any).label;
|
||
}
|
||
if (typeof title === 'string') {
|
||
return title;
|
||
}
|
||
if (isI18nData(title)) {
|
||
const currentLocale = this.pluginContext.getLocale();
|
||
const currentTitle = title[currentLocale];
|
||
return currentTitle;
|
||
}
|
||
return this.node.componentName;
|
||
}
|
||
|
||
setTitleLabel(label: string) {
|
||
const origLabel = this.titleLabel;
|
||
if (label === origLabel) {
|
||
return;
|
||
}
|
||
if (label === '') {
|
||
this.node.getExtraProp('title', false)?.remove();
|
||
} else {
|
||
this.node.getExtraProp('title', true)?.setValue(label);
|
||
}
|
||
this.onTitleLabelChanged && this.onTitleLabelChanged(this);
|
||
}
|
||
|
||
get icon() {
|
||
return this.node.componentMeta.icon;
|
||
}
|
||
|
||
get parent(): TreeNode | null {
|
||
const { parent } = this.node;
|
||
if (parent) {
|
||
return this.tree.getTreeNode(parent);
|
||
}
|
||
return null;
|
||
}
|
||
|
||
get slots(): TreeNode[] {
|
||
// todo: shallowEqual
|
||
return this.node.slots.map((node) => this.tree.getTreeNode(node));
|
||
}
|
||
|
||
get children(): TreeNode[] | null {
|
||
return this.node.children?.map((node) => this.tree.getTreeNode(node)) || null;
|
||
}
|
||
|
||
/**
|
||
* 是否是容器,允许子节点拖入
|
||
*/
|
||
isContainer(): boolean {
|
||
return this.node.isContainerNode;
|
||
}
|
||
|
||
/**
|
||
* 判断是否有"插槽"
|
||
*/
|
||
hasSlots(): boolean {
|
||
return this.node.hasSlots();
|
||
}
|
||
|
||
hasChildren(): boolean {
|
||
return !!(this.isContainer() && this.node.children?.notEmptyNode);
|
||
}
|
||
|
||
select(isMulti: boolean) {
|
||
const { node } = this;
|
||
|
||
const selection = this.pluginContext.project.getCurrentDocument()?.selection;
|
||
if (isMulti) {
|
||
selection?.add(node.id);
|
||
} else {
|
||
selection?.select(node.id);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 展开节点,支持依次展开父节点
|
||
*/
|
||
expand(tryExpandParents = false) {
|
||
// 这边不能直接使用 expanded,需要额外判断是否可以展开
|
||
// 如果只使用 expanded,会漏掉不可以展开的情况,即在不可以展开的情况下,会触发展开
|
||
if (this.expandable && !this._expanded) {
|
||
this.setExpanded(true);
|
||
}
|
||
if (tryExpandParents) {
|
||
this.expandParents();
|
||
}
|
||
}
|
||
|
||
expandParents() {
|
||
let p = this.node.parent;
|
||
while (p) {
|
||
this.tree.getTreeNode(p).setExpanded(true);
|
||
p = p.parent;
|
||
}
|
||
}
|
||
|
||
private _node: IPublicModelNode;
|
||
|
||
get node(): IPublicModelNode {
|
||
return this._node;
|
||
}
|
||
|
||
readonly tree: Tree;
|
||
|
||
constructor(tree: Tree, node: IPublicModelNode, pluginContext: IPublicModelPluginContext) {
|
||
this.tree = tree;
|
||
this.pluginContext = pluginContext;
|
||
this._node = node;
|
||
}
|
||
|
||
setNode(node: IPublicModelNode) {
|
||
if (this._node !== node) {
|
||
this._node = node;
|
||
}
|
||
}
|
||
|
||
private _filterResult: FilterResult = {
|
||
filterWorking: false,
|
||
matchChild: false,
|
||
matchSelf: false,
|
||
keywords: '',
|
||
};
|
||
|
||
get filterReult(): FilterResult {
|
||
return this._filterResult;
|
||
}
|
||
|
||
setFilterReult(val: FilterResult) {
|
||
this._filterResult = val;
|
||
if (this.onFilterResultChanged) {
|
||
this.onFilterResultChanged();
|
||
}
|
||
}
|
||
}
|