184 lines
4.0 KiB
TypeScript

import { obx, makeObservable, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
import { Node, INode, comparePosition, PositionNO } from './node/node';
import { DocumentModel } from './document-model';
import { IPublicModelSelection } from '@alilc/lowcode-types';
export interface ISelection extends Omit< IPublicModelSelection, 'getNodes' | 'getTopNodes' > {
/**
* 获取选中的节点实例
* @returns
*/
getNodes(): INode[];
/**
* 获取顶层选区节点,场景:拖拽时,建立蒙层,只蒙在最上层
*/
getTopNodes(includeRoot?: boolean): INode[];
}
export class Selection implements ISelection {
private emitter: IEventBus = createModuleEventBus('Selection');
@obx.shallow private _selected: string[] = [];
constructor(readonly doc: DocumentModel) {
makeObservable(this);
}
/**
* 选中的节点 id
*/
get selected(): string[] {
return this._selected;
}
/**
* 选中
*/
select(id: string) {
if (this._selected.length === 1 && this._selected.indexOf(id) > -1) {
// avoid cause reaction
return;
}
this._selected = [id];
this.emitter.emit('selectionchange', this._selected);
}
/**
* 批量选中
*/
selectAll(ids: string[]) {
this._selected = ids;
this.emitter.emit('selectionchange', this._selected);
}
/**
* 清除选中
*/
clear() {
if (this._selected.length < 1) {
return;
}
this._selected = [];
this.emitter.emit('selectionchange', this._selected);
}
/**
* 整理选中
*/
dispose() {
const l = this._selected.length;
let i = l;
while (i-- > 0) {
const id = this._selected[i];
if (!this.doc.hasNode(id)) {
this._selected.splice(i, 1);
}
}
if (this._selected.length !== l) {
this.emitter.emit('selectionchange', this._selected);
}
}
/**
* 添加选中
*/
add(id: string) {
if (this._selected.indexOf(id) > -1) {
return;
}
this._selected.push(id);
this.emitter.emit('selectionchange', this._selected);
}
/**
* 是否选中
*/
has(id: string) {
return this._selected.indexOf(id) > -1;
}
/**
* 移除选中
*/
remove(id: string) {
const i = this._selected.indexOf(id);
if (i > -1) {
this._selected.splice(i, 1);
this.emitter.emit('selectionchange', this._selected);
}
}
/**
* 选区是否包含节点
*/
containsNode(node: Node, excludeRoot = false) {
for (const id of this._selected) {
const parent = this.doc.getNode(id);
if (excludeRoot && parent?.contains(this.doc.focusNode)) {
continue;
}
if (parent?.contains(node)) {
return true;
}
}
return false;
}
/**
* 获取选中的节点
*/
getNodes(): Node[] {
const nodes = [];
for (const id of this._selected) {
const node = this.doc.getNode(id);
if (node) {
nodes.push(node);
}
}
return nodes;
}
/**
* 获取顶层选区节点,场景:拖拽时,建立蒙层,只蒙在最上层
*/
getTopNodes(includeRoot = false) {
const nodes = [];
for (const id of this._selected) {
const node = this.doc.getNode(id);
// 排除根节点
if (!node || (!includeRoot && node.contains(this.doc.focusNode))) {
continue;
}
let i = nodes.length;
let isTop = true;
while (i-- > 0) {
const n = comparePosition(nodes[i], node);
// nodes[i] contains node
if (n === PositionNO.Contains || n === PositionNO.TheSame) {
isTop = false;
break;
} else if (n === PositionNO.ContainedBy) {
// node contains nodes[i], delete nodes[i]
nodes.splice(i, 1);
}
}
// node is top item, push to nodes
if (isTop) {
nodes.push(node);
}
}
return nodes;
}
onSelectionChange(fn: (ids: string[]) => void): () => void {
this.emitter.on('selectionchange', fn);
return () => {
this.emitter.removeListener('selectionchange', fn);
};
}
}