290 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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