363 lines
8.6 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,
IPublicTypeDisposable,
} from '@alilc/lowcode-types';
import { isI18nData, isLocationChildrenDetail } from '@alilc/lowcode-utils';
import EventEmitter from 'events';
import { Tree } from './tree';
import { IOutlinePanelPluginContext } from './tree-master';
/**
* 大纲树过滤结果
*/
export interface FilterResult {
// 过滤条件是否生效
filterWorking: boolean;
// 命中子节点
matchChild: boolean;
// 命中本节点
matchSelf: boolean;
// 关键字
keywords: string;
}
enum EVENT_NAMES {
filterResultChanged = 'filterResultChanged',
expandedChanged = 'expandedChanged',
hiddenChanged = 'hiddenChanged',
lockedChanged = 'lockedChanged',
titleLabelChanged = 'titleLabelChanged',
expandableChanged = 'expandableChanged',
conditionChanged = 'conditionChanged',
}
export default class TreeNode {
readonly pluginContext: IOutlinePanelPluginContext;
event = new EventEmitter();
private _node: IPublicModelNode;
readonly tree: Tree;
private _filterResult: FilterResult = {
filterWorking: false,
matchChild: false,
matchSelf: false,
keywords: '',
};
/**
* 默认为折叠状态
* 在初始化根节点时,设置为展开状态
*/
private _expanded = false;
get id(): string {
return this.node.id;
}
/**
* 是否可以展开
*/
get expandable(): boolean {
if (this.locked) return false;
return this.hasChildren() || this.hasSlots() || this.dropDetail?.index != null;
}
get expanded(): boolean {
return this.isRoot(true) || (this.expandable && this._expanded);
}
/**
* 插入"线"位置信息
*/
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;
}
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.visible;
}
return !cv;
}
get locked(): boolean {
return this.node.isLocked;
}
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;
}
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 condition(): boolean {
return this.node.hasCondition() && !this.node.conditionGroup;
}
get children(): TreeNode[] | null {
return this.node.children?.map((node) => this.tree.getTreeNode(node)) || null;
}
get node(): IPublicModelNode {
return this._node;
}
constructor(tree: Tree, node: IPublicModelNode) {
this.tree = tree;
this.pluginContext = tree.pluginContext;
this._node = node;
}
setLocked(flag: boolean) {
this.node.lock(flag);
this.event.emit(EVENT_NAMES.lockedChanged, flag);
}
onFilterResultChanged(fn: () => void): IPublicTypeDisposable {
this.event.on(EVENT_NAMES.filterResultChanged, fn);
return () => {
this.event.off(EVENT_NAMES.filterResultChanged, fn);
};
}
onExpandedChanged(fn: (expanded: boolean) => void): IPublicTypeDisposable {
this.event.on(EVENT_NAMES.expandedChanged, fn);
return () => {
this.event.off(EVENT_NAMES.expandedChanged, fn);
};
}
onHiddenChanged(fn: (hidden: boolean) => void): IPublicTypeDisposable {
this.event.on(EVENT_NAMES.hiddenChanged, fn);
return () => {
this.event.off(EVENT_NAMES.hiddenChanged, fn);
};
}
onLockedChanged(fn: (locked: boolean) => void): IPublicTypeDisposable {
this.event.on(EVENT_NAMES.lockedChanged, fn);
return () => {
this.event.off(EVENT_NAMES.lockedChanged, fn);
};
}
onTitleLabelChanged(fn: (treeNode: TreeNode) => void): IPublicTypeDisposable {
this.event.on(EVENT_NAMES.titleLabelChanged, fn);
return () => {
this.event.off(EVENT_NAMES.titleLabelChanged, fn);
};
}
onConditionChanged(fn: (treeNode: TreeNode) => void): IPublicTypeDisposable {
this.event.on(EVENT_NAMES.conditionChanged, fn);
return () => {
this.event.off(EVENT_NAMES.conditionChanged, fn);
};
}
onExpandableChanged(fn: (expandable: boolean) => void): IPublicTypeDisposable {
this.event.on(EVENT_NAMES.expandableChanged, fn);
return () => {
this.event.off(EVENT_NAMES.expandableChanged, fn);
};
}
/**
* 触发 onExpandableChanged 回调
*/
notifyExpandableChanged(): void {
this.event.emit(EVENT_NAMES.expandableChanged, this.expandable);
}
notifyTitleLabelChanged(): void {
this.event.emit(EVENT_NAMES.titleLabelChanged, this.title);
}
notifyConditionChanged(): void {
this.event.emit(EVENT_NAMES.conditionChanged, this.condition);
}
setHidden(flag: boolean) {
if (this.node.conditionGroup) {
return;
}
if (this.node.visible !== !flag) {
this.node.visible = !flag;
}
this.event.emit(EVENT_NAMES.hiddenChanged, flag);
}
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
);
}
setExpanded(value: boolean) {
this._expanded = value;
this.event.emit(EVENT_NAMES.expandedChanged, value);
}
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;
}
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.event.emit(EVENT_NAMES.titleLabelChanged, this);
}
/**
* 是否是容器,允许子节点拖入
*/
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;
}
}
setNode(node: IPublicModelNode) {
if (this._node !== node) {
this._node = node;
}
}
get filterReult(): FilterResult {
return this._filterResult;
}
setFilterReult(val: FilterResult) {
this._filterResult = val;
this.event.emit(EVENT_NAMES.filterResultChanged);
}
}