mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-12 03:01:16 +00:00
refactor: use callbacks to replace events
This commit is contained in:
parent
840e70e3d0
commit
bcdec0989e
@ -42,6 +42,12 @@ sidebar_position: 0
|
||||
|
||||
参见 [模态节点管理](./modal-nodes-manager)
|
||||
|
||||
### dropLocation
|
||||
文档的 dropLocation
|
||||
相关类型:[IPublicModelDropLocation](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/drop-location.ts)
|
||||
|
||||
**@since v1.1.0**
|
||||
|
||||
## 方法签名
|
||||
### getNodeById
|
||||
|
||||
@ -90,6 +96,24 @@ removeNode(idOrNode: string | Node)
|
||||
function checkNesting(dropTarget: Node, dragObject: DragNodeObject | DragNodeDataObject): boolean {}
|
||||
```
|
||||
|
||||
### isDetectingNode
|
||||
检查拖拽放置的目标节点是否可以放置该拖拽对象
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* 判断是否当前节点处于被探测状态
|
||||
* check is node being detected
|
||||
* @param node
|
||||
* @since v1.1.0
|
||||
*/
|
||||
isDetectingNode(node: IPublicModelNode): boolean;
|
||||
```
|
||||
相关类型:[IPublicModelNode](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/node.ts)
|
||||
|
||||
|
||||
**@since v1.1.0**
|
||||
|
||||
|
||||
## 事件
|
||||
### onAddNode
|
||||
|
||||
@ -144,7 +168,53 @@ onChangeNodeProp(fn: (info: IPublicTypePropChangeOptions) => void)
|
||||
|
||||
### onImportSchema
|
||||
当前 document 导入新的 schema 事件
|
||||
版本 >= 1.0.15
|
||||
```typescript
|
||||
onImportSchema(fn: (schema: any) => void)
|
||||
/**
|
||||
* import schema event
|
||||
* @param fn
|
||||
* @since v1.0.15
|
||||
*/
|
||||
onImportSchema(fn: (schema: IPublicTypeRootSchema) => void): IPublicTypeDisposable;
|
||||
```
|
||||
相关类型:
|
||||
- [IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts)
|
||||
- [IPublicTypeRootSchema](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/root-schema.ts)
|
||||
|
||||
**@since v1.0.15**
|
||||
|
||||
### onFocusNodeChanged
|
||||
设置聚焦节点变化的回调
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* 设置聚焦节点变化的回调
|
||||
* triggered focused node is set mannually from plugin
|
||||
* @param fn
|
||||
* @since v1.1.0
|
||||
*/
|
||||
onFocusNodeChanged(
|
||||
fn: (doc: IPublicModelDocumentModel, focusNode: IPublicModelNode) => void,
|
||||
): IPublicTypeDisposable;
|
||||
```
|
||||
相关类型:
|
||||
- [IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts)
|
||||
- [IPublicModelNode](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/node.ts)
|
||||
|
||||
**@since v1.1.0**
|
||||
|
||||
### onDropLocationChanged
|
||||
设置 DropLocation 变化的回调
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* 设置 DropLocation 变化的回调
|
||||
* triggered when drop location changed
|
||||
* @param fn
|
||||
* @since v1.1.0
|
||||
*/
|
||||
onDropLocationChanged(fn: (doc: IPublicModelDocumentModel) => void): IPublicTypeDisposable;
|
||||
```
|
||||
|
||||
相关类型:[IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts)
|
||||
|
||||
**@since v1.1.0**
|
||||
@ -7,8 +7,9 @@ import {
|
||||
import { isNode } from '@alilc/lowcode-utils';
|
||||
|
||||
export interface IActiveTracker extends IPublicModelActiveTracker {
|
||||
|
||||
track(originalTarget: IPublicTypeActiveTarget | INode): void;
|
||||
}
|
||||
|
||||
export class ActiveTracker implements IActiveTracker {
|
||||
private emitter: IEventBus = createModuleEventBus('ActiveTracker');
|
||||
|
||||
|
||||
@ -103,15 +103,11 @@ export interface IDropLocation extends IPublicModelDropLocation {
|
||||
|
||||
readonly target: INode;
|
||||
|
||||
readonly detail: IPublicTypeLocationDetail;
|
||||
|
||||
readonly event: ILocateEvent;
|
||||
|
||||
readonly source: string;
|
||||
|
||||
get document(): IPublicModelDocumentModel;
|
||||
|
||||
clone(event: ILocateEvent): IDropLocation;
|
||||
}
|
||||
|
||||
export class DropLocation implements IDropLocation {
|
||||
|
||||
@ -14,7 +14,6 @@ import {
|
||||
IPublicModelNode,
|
||||
IPublicApiProject,
|
||||
IPublicModelDropLocation,
|
||||
IPublicEnumEventNames,
|
||||
IPublicEnumTransformStage,
|
||||
} from '@alilc/lowcode-types';
|
||||
import { Project } from '../project';
|
||||
@ -326,7 +325,7 @@ export class DocumentModel implements IDocumentModel {
|
||||
this._dropLocation = loc;
|
||||
// pub event
|
||||
this.designer.editor.eventBus.emit(
|
||||
IPublicEnumEventNames.DOCUMENT_DROPLOCATION_CHANGED,
|
||||
'document.dropLocation.changed',
|
||||
{ document: this, location: loc },
|
||||
);
|
||||
}
|
||||
|
||||
@ -26,7 +26,6 @@ import {
|
||||
PluginClassSet,
|
||||
IPublicTypeWidgetBaseConfig,
|
||||
IPublicTypeWidgetConfigArea,
|
||||
IPublicEnumEventNames,
|
||||
} from '@alilc/lowcode-types';
|
||||
|
||||
const logger = new Logger({ level: 'warn', bizName: 'skeleton' });
|
||||
@ -174,7 +173,7 @@ export class Skeleton {
|
||||
*/
|
||||
setupEvents() {
|
||||
// adjust pinned status when panel shown
|
||||
this.editor.eventBus.on(IPublicEnumEventNames.SKELETON_PANEL_SHOW, (panelName, panel) => {
|
||||
this.editor.eventBus.on(SkeletonEvents.PANEL_SHOW, (panelName, panel) => {
|
||||
const panelNameKey = `${panelName}-pinned-status-isFloat`;
|
||||
const isInFloatAreaPreferenceExists = engineConfig.getPreference()?.contains(panelNameKey, 'skeleton');
|
||||
if (isInFloatAreaPreferenceExists) {
|
||||
|
||||
@ -171,7 +171,7 @@ export async function init(
|
||||
engineConfig.setEngineOptions(engineOptions as any);
|
||||
|
||||
// 注册一批内置插件
|
||||
await plugins.register(OutlinePlugin);
|
||||
await plugins.register(OutlinePlugin, {}, { autoInit: true });
|
||||
await plugins.register(componentMetaParser(designer));
|
||||
await plugins.register(setterRegistry, {}, { autoInit: true });
|
||||
await plugins.register(defaultPanelRegistry(editor, designer));
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
isLocationChildrenDetail,
|
||||
} from '@alilc/lowcode-utils';
|
||||
import {
|
||||
DragObject,
|
||||
IPublicModelDragObject,
|
||||
IPublicModelScrollable,
|
||||
ISensor,
|
||||
IPublicTypeLocationChildrenDetail,
|
||||
@ -50,7 +50,6 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab
|
||||
setup();
|
||||
}
|
||||
|
||||
|
||||
/** -------------------- ISensor begin -------------------- */
|
||||
|
||||
private indentTrack = new IndentTrack();
|
||||
@ -107,12 +106,12 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab
|
||||
const document = project.getCurrentDocument();
|
||||
const pos = getPosFromEvent(e, this._shell);
|
||||
const irect = this.getInsertionRect();
|
||||
const originLoc = document.dropLocation;
|
||||
const originLoc = document?.dropLocation;
|
||||
|
||||
const componentMeta = e.dragObject.nodes ? e.dragObject.nodes[0].componentMeta : null;
|
||||
if (e.dragObject.type === 'node' && componentMeta && componentMeta.isModal) {
|
||||
const componentMeta = e.dragObject?.nodes ? e.dragObject.nodes[0].componentMeta : null;
|
||||
if (e.dragObject?.type === 'node' && componentMeta && componentMeta.isModal && document?.focusNode) {
|
||||
return canvas.createLocation({
|
||||
target: document.focusNode,
|
||||
target: document?.focusNode,
|
||||
detail: {
|
||||
type: IPublicTypeLocationDetailType.Children,
|
||||
index: 0,
|
||||
@ -123,7 +122,9 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab
|
||||
});
|
||||
}
|
||||
|
||||
if (originLoc && ((pos && pos === 'unchanged') || (irect && globalY >= irect.top && globalY <= irect.bottom))) {
|
||||
if (originLoc
|
||||
&& ((pos && pos === 'unchanged') || (irect && globalY >= irect.top && globalY <= irect.bottom))
|
||||
&& dragObject) {
|
||||
const loc = originLoc.clone(e);
|
||||
const indented = this.indentTrack.getIndentParent(originLoc, loc);
|
||||
if (indented) {
|
||||
@ -138,7 +139,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab
|
||||
detail: {
|
||||
type: IPublicTypeLocationDetailType.Children,
|
||||
index,
|
||||
valid: document.checkNesting(parent, e.dragObject as any),
|
||||
valid: document?.checkNesting(parent, e.dragObject as any),
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -177,7 +178,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab
|
||||
}
|
||||
}
|
||||
if (p !== node) {
|
||||
node = p || document.focusNode;
|
||||
node = p || document?.focusNode;
|
||||
treeNode = tree.getTreeNode(node);
|
||||
focusSlots = false;
|
||||
}
|
||||
@ -258,7 +259,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab
|
||||
cancelIdleCallback(this.tryScrollAgain);
|
||||
this.tryScrollAgain = null;
|
||||
}
|
||||
if (this.sensing || !this.bounds || !this.scroller || !this.scrollTarget) {
|
||||
if (!this.bounds || !this.scroller || !this.scrollTarget) {
|
||||
// is a active sensor
|
||||
return;
|
||||
}
|
||||
@ -305,7 +306,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab
|
||||
focus = { type: 'slots' };
|
||||
} else {
|
||||
index = 0;
|
||||
valid = document.checkNesting(target, event.dragObject as any);
|
||||
valid = !!document?.checkNesting(target, event.dragObject as any);
|
||||
}
|
||||
canvas.createLocation({
|
||||
target,
|
||||
@ -320,23 +321,28 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab
|
||||
});
|
||||
});
|
||||
|
||||
private getNear(treeNode: TreeNode, e: IPublicModelLocateEvent, index?: number, rect?: DOMRect) {
|
||||
private getNear(treeNode: TreeNode, e: IPublicModelLocateEvent, originalIndex?: number, originalRect?: DOMRect) {
|
||||
const { canvas, project } = this.pluginContext;
|
||||
const document = project.getCurrentDocument();
|
||||
const { globalY, dragObject } = e;
|
||||
if (!dragObject) {
|
||||
return null;
|
||||
}
|
||||
// TODO: check dragObject is anyData
|
||||
const { node, expanded } = treeNode;
|
||||
let rect = originalRect;
|
||||
if (!rect) {
|
||||
rect = this.getTreeNodeRect(treeNode);
|
||||
if (!rect) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
let index = originalIndex;
|
||||
if (index == null) {
|
||||
index = node.index;
|
||||
}
|
||||
|
||||
if (node.isSlot) {
|
||||
if (node.isSlotNode) {
|
||||
// 是个插槽根节点
|
||||
if (!treeNode.isContainer() && !treeNode.hasSlots()) {
|
||||
return canvas.createLocation({
|
||||
@ -385,7 +391,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab
|
||||
detail: {
|
||||
type: IPublicTypeLocationDetailType.Children,
|
||||
index,
|
||||
valid: document.checkNesting(node.parent!, dragObject as any),
|
||||
valid: document?.checkNesting(node.parent!, dragObject as any),
|
||||
near: { node, pos: 'before' },
|
||||
focus: checkRecursion(focusNode, dragObject) ? { type: 'node', node: focusNode } : undefined,
|
||||
},
|
||||
@ -412,7 +418,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab
|
||||
detail: {
|
||||
type: IPublicTypeLocationDetailType.Children,
|
||||
index: index + 1,
|
||||
valid: document.checkNesting(node.parent!, dragObject as any),
|
||||
valid: document?.checkNesting(node.parent!, dragObject as any),
|
||||
near: { node, pos: 'after' },
|
||||
focus: checkRecursion(focusNode, dragObject) ? { type: 'node', node: focusNode } : undefined,
|
||||
},
|
||||
@ -423,6 +429,9 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab
|
||||
const { canvas, project } = this.pluginContext;
|
||||
const document = project.getCurrentDocument();
|
||||
const { dragObject, globalY } = e;
|
||||
if (!dragObject) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!checkRecursion(treeNode.node, dragObject)) {
|
||||
return null;
|
||||
@ -454,7 +463,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab
|
||||
detail.valid = false;
|
||||
} else {
|
||||
detail.index = 0;
|
||||
detail.valid = document.checkNesting(container, dragObject);
|
||||
detail.valid = document?.checkNesting(container, dragObject);
|
||||
}
|
||||
}
|
||||
|
||||
@ -526,7 +535,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab
|
||||
} else {
|
||||
detail.index = l;
|
||||
}
|
||||
detail.valid = document.checkNesting(container, dragObject);
|
||||
detail.valid = document?.checkNesting(container, dragObject);
|
||||
}
|
||||
|
||||
return canvas.createLocation(locationData);
|
||||
@ -572,9 +581,26 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab
|
||||
return;
|
||||
}
|
||||
this._shell = shell;
|
||||
const { canvas, project } = this.pluginContext;
|
||||
if (shell) {
|
||||
this._scrollTarget = this.pluginContext.canvas.createScrollTarget(shell);
|
||||
this._scrollTarget = canvas.createScrollTarget(shell);
|
||||
this._sensorAvailable = true;
|
||||
|
||||
// check if there is current selection and scroll to it
|
||||
const selection = project.currentDocument?.selection;
|
||||
const topNodes = selection?.getTopNodes(true);
|
||||
const tree = this.treeMaster?.currentTree;
|
||||
if (topNodes && topNodes[0] && tree) {
|
||||
const treeNode = tree.getTreeNodeById(topNodes[0].id);
|
||||
if (treeNode) {
|
||||
// at this moment, it is possible that pane is not ready yet, so
|
||||
// put ui related operations to the next loop
|
||||
setTimeout(() => {
|
||||
tree.setNodeSelected(treeNode.id);
|
||||
this.scrollToNode(treeNode, null, 4);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this._scrollTarget = undefined;
|
||||
this._sensorAvailable = false;
|
||||
@ -610,7 +636,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab
|
||||
}
|
||||
}
|
||||
|
||||
function checkRecursion(parent: IPublicModelNode | undefined | null, dragObject: DragObject): boolean {
|
||||
function checkRecursion(parent: IPublicModelNode | undefined | null, dragObject: IPublicModelDragObject): boolean {
|
||||
if (!parent) {
|
||||
return false;
|
||||
}
|
||||
@ -633,14 +659,14 @@ function getPosFromEvent(
|
||||
if (target.matches('.insertion')) {
|
||||
return 'unchanged';
|
||||
}
|
||||
target = target.closest('[data-id]');
|
||||
if (!target || !stop.contains(target)) {
|
||||
const closest = target.closest('[data-id]');
|
||||
if (!closest || !stop.contains(closest)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const nodeId = (target as HTMLDivElement).dataset.id!;
|
||||
const nodeId = (closest as HTMLDivElement).dataset.id!;
|
||||
return {
|
||||
focusSlots: target.matches('.tree-node-slots'),
|
||||
focusSlots: closest.matches('.tree-node-slots'),
|
||||
nodeId,
|
||||
};
|
||||
}
|
||||
|
||||
1
packages/plugin-outline-pane/src/controllers/ric-shim.d.ts
vendored
Normal file
1
packages/plugin-outline-pane/src/controllers/ric-shim.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
declare module 'ric-shim';
|
||||
@ -1,5 +1,5 @@
|
||||
import { isLocationChildrenDetail } from '@alilc/lowcode-utils';
|
||||
import { IPublicModelPluginContext, IPublicTypeActiveTarget, IPublicModelNode, IPublicEnumEventNames } from '@alilc/lowcode-types';
|
||||
import { IPublicModelPluginContext, IPublicTypeActiveTarget, IPublicModelNode } from '@alilc/lowcode-types';
|
||||
import TreeNode from './tree-node';
|
||||
import { Tree } from './tree';
|
||||
|
||||
@ -54,8 +54,8 @@ export class TreeMaster {
|
||||
time: (endTime - startTime).toFixed(2),
|
||||
});
|
||||
});
|
||||
event.on(IPublicEnumEventNames.DESIGNER_DOCUMENT_REMOVE, (doc) => {
|
||||
const { id } = doc as any;
|
||||
project.onRemoveDocument((data: {id: string}) => {
|
||||
const { id } = data;
|
||||
this.treeMap.delete(id);
|
||||
});
|
||||
}
|
||||
|
||||
@ -24,6 +24,10 @@ export interface FilterResult {
|
||||
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;
|
||||
|
||||
get id(): string {
|
||||
return this.node.id;
|
||||
@ -87,7 +91,7 @@ export default class TreeNode {
|
||||
|
||||
setExpanded(value: boolean) {
|
||||
this._expanded = value;
|
||||
this.pluginContext.pluginEvent.emit('tree-node.expandedChanged', { expanded: value, nodeId: this.id });
|
||||
this.onExpandedChanged && this.onExpandedChanged(value);
|
||||
}
|
||||
|
||||
get detecting() {
|
||||
@ -108,7 +112,7 @@ export default class TreeNode {
|
||||
return;
|
||||
}
|
||||
this.node.setVisible(!flag);
|
||||
this.pluginContext.pluginEvent.emit('tree-node.hiddenChanged', { hidden: flag, nodeId: this.id });
|
||||
this.onHiddenChanged && this.onHiddenChanged(flag);
|
||||
}
|
||||
|
||||
get locked(): boolean {
|
||||
@ -117,7 +121,7 @@ export default class TreeNode {
|
||||
|
||||
setLocked(flag: boolean) {
|
||||
this.node.lock(flag);
|
||||
this.pluginContext.pluginEvent.emit('tree-node.lockedChanged', { locked: flag, nodeId: this.id });
|
||||
this.onLockedChanged && this.onLockedChanged(flag);
|
||||
}
|
||||
|
||||
get selected(): boolean {
|
||||
@ -162,7 +166,7 @@ export default class TreeNode {
|
||||
} else {
|
||||
this.node.getExtraProp('title', true)?.setValue(label);
|
||||
}
|
||||
this.pluginContext.pluginEvent.emit('tree-node.titleLabelChanged', { titleLabel: label, nodeId: this.id });
|
||||
this.onTitleLabelChanged && this.onTitleLabelChanged(this);
|
||||
}
|
||||
|
||||
get icon() {
|
||||
|
||||
@ -20,6 +20,15 @@ export class Tree {
|
||||
this.id = this.pluginContext.project.currentDocument?.id;
|
||||
}
|
||||
|
||||
setNodeSelected(nodeId: string): void {
|
||||
// 目标节点选中,其他节点展开
|
||||
const treeNode = this.treeNodesMap.get(nodeId);
|
||||
if (!treeNode) {
|
||||
return;
|
||||
}
|
||||
this.expandAllAncestors(treeNode);
|
||||
}
|
||||
|
||||
getTreeNode(node: IPublicModelNode): TreeNode {
|
||||
if (this.treeNodesMap.has(node.id)) {
|
||||
const tnode = this.treeNodesMap.get(node.id)!;
|
||||
@ -36,7 +45,29 @@ export class Tree {
|
||||
return this.treeNodesMap.get(id);
|
||||
}
|
||||
|
||||
expandAllDecendants(treeNode: TreeNode | undefined) {
|
||||
expandAllAncestors(treeNode: TreeNode | undefined | null) {
|
||||
if (!treeNode) {
|
||||
return;
|
||||
}
|
||||
if (treeNode.isRoot()) {
|
||||
return;
|
||||
}
|
||||
const ancestors = [];
|
||||
let currentNode: TreeNode | null | undefined = treeNode;
|
||||
while (!treeNode.isRoot()) {
|
||||
currentNode = currentNode?.parent;
|
||||
if (currentNode) {
|
||||
ancestors.unshift(currentNode);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ancestors.forEach((ancestor) => {
|
||||
ancestor.setExpanded(true);
|
||||
});
|
||||
}
|
||||
|
||||
expandAllDecendants(treeNode: TreeNode | undefined | null) {
|
||||
if (!treeNode) {
|
||||
return;
|
||||
}
|
||||
@ -49,7 +80,7 @@ export class Tree {
|
||||
}
|
||||
}
|
||||
|
||||
collapseAllDecendants(treeNode: TreeNode | undefined) {
|
||||
collapseAllDecendants(treeNode: TreeNode | undefined | null): void {
|
||||
if (!treeNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
import { globalContext } from '@alilc/lowcode-editor-core';
|
||||
import { ReactElement } from 'react';
|
||||
|
||||
const TREE_TITLE_EXTRA_KEY = 'TREE_TITLE_EXTRA_KEY';
|
||||
|
||||
export const registerTreeTitleExtra = (extra: ReactElement) => {
|
||||
if (extra && !globalContext.has(TREE_TITLE_EXTRA_KEY)) {
|
||||
globalContext.register(extra, TREE_TITLE_EXTRA_KEY);
|
||||
}
|
||||
};
|
||||
|
||||
export const getTreeTitleExtra = () => {
|
||||
try {
|
||||
return globalContext.get(TREE_TITLE_EXTRA_KEY);
|
||||
} catch (e) {
|
||||
// console.error('getTreeTitleExtra Error', e);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
@ -1,11 +1,13 @@
|
||||
import { Pane } from './views/pane';
|
||||
import { IconOutline } from './icons/outline';
|
||||
import { IPublicModelPluginContext, IPublicEnumEventNames } from '@alilc/lowcode-types';
|
||||
import { IPublicModelPluginContext, IPublicModelDocumentModel } from '@alilc/lowcode-types';
|
||||
import { enUS, zhCN } from './locale';
|
||||
import { MasterPaneName, BackupPaneName } from './helper/consts';
|
||||
import { getTreeMaster } from './controllers/tree-master';
|
||||
import { PaneController } from './controllers/pane-controller';
|
||||
|
||||
export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => {
|
||||
const { skeleton, config, common, event, canvas } = ctx;
|
||||
const { skeleton, config, common, event, canvas, project } = ctx;
|
||||
const { intl, intlNode, getLocale } = common.utils.createIntl({
|
||||
'en-US': enUS,
|
||||
'zh-CN': zhCN,
|
||||
@ -24,7 +26,9 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => {
|
||||
masterPane: false,
|
||||
backupPane: false,
|
||||
};
|
||||
|
||||
const treeMaster = getTreeMaster(ctx);
|
||||
let masterPaneController: PaneController | null = null;
|
||||
let backupPaneController: PaneController | null = null;
|
||||
return {
|
||||
async init() {
|
||||
skeleton.add({
|
||||
@ -39,10 +43,13 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => {
|
||||
description: intlNode('Outline Tree'),
|
||||
},
|
||||
content: (props: any) => {
|
||||
masterPaneController = new PaneController(MasterPaneName, ctx, treeMaster);
|
||||
return (
|
||||
<Pane
|
||||
config={config}
|
||||
pluginContext={ctx}
|
||||
treeMaster={treeMaster}
|
||||
controller={masterPaneController}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@ -65,12 +72,17 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => {
|
||||
props: {
|
||||
hiddenWhenInit: true,
|
||||
},
|
||||
content: (props: any) => (
|
||||
<Pane
|
||||
pluginContext={ctx}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
content: (props: any) => {
|
||||
backupPaneController = new PaneController(BackupPaneName, ctx, treeMaster);
|
||||
return (
|
||||
<Pane
|
||||
pluginContext={ctx}
|
||||
treeMaster={treeMaster}
|
||||
controller={backupPaneController}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// 处理 master pane 和 backup pane 切换
|
||||
@ -91,8 +103,7 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => {
|
||||
canvas.dragon?.onDragend(() => {
|
||||
switchPanes();
|
||||
});
|
||||
|
||||
event.on(IPublicEnumEventNames.SKELETON_PANEL_SHOW, (key: string) => {
|
||||
skeleton.onShowPanel((key: string) => {
|
||||
if (key === MasterPaneName) {
|
||||
showingPanes.masterPane = true;
|
||||
}
|
||||
@ -100,7 +111,7 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => {
|
||||
showingPanes.backupPane = true;
|
||||
}
|
||||
});
|
||||
event.on(IPublicEnumEventNames.SKELETON_PANEL_HIDE, (key: string) => {
|
||||
skeleton.onHidePanel((key: string) => {
|
||||
if (key === MasterPaneName) {
|
||||
showingPanes.masterPane = false;
|
||||
switchPanes();
|
||||
@ -109,6 +120,25 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => {
|
||||
showingPanes.backupPane = false;
|
||||
}
|
||||
});
|
||||
project.onChangeDocument((document: IPublicModelDocumentModel) => {
|
||||
if (!document) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { selection } = document;
|
||||
|
||||
selection?.onSelectionChange(() => {
|
||||
const selectedNodes = selection?.getNodes();
|
||||
if (!selectedNodes || selectedNodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
const tree = treeMaster.currentTree;
|
||||
selectedNodes.forEach((node) => {
|
||||
const treeNode = tree?.getTreeNodeById(node.id);
|
||||
tree?.expandAllAncestors(treeNode);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@ -4,35 +4,29 @@ import TreeView from './tree';
|
||||
import './style.less';
|
||||
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
|
||||
import Filter from './filter';
|
||||
import { registerTreeTitleExtra } from '../helper/tree-title-extra';
|
||||
import { getTreeMaster, TreeMaster } from '../controllers/tree-master';
|
||||
import { TreeMaster } from '../controllers/tree-master';
|
||||
|
||||
|
||||
export class Pane extends Component<{
|
||||
config: any;
|
||||
pluginContext: IPublicModelPluginContext;
|
||||
treeMaster: TreeMaster;
|
||||
controller: PaneController;
|
||||
}> {
|
||||
private controller;
|
||||
private treeMaster: TreeMaster;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.treeMaster = getTreeMaster(this.props.pluginContext);
|
||||
this.controller = new PaneController(
|
||||
this.props.config.name || this.props.config.pluginKey,
|
||||
this.props.pluginContext,
|
||||
this.treeMaster,
|
||||
);
|
||||
const { controller, treeMaster } = props;
|
||||
this.treeMaster = treeMaster;
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.controller.purge();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
registerTreeTitleExtra(this.props?.config?.contentProps?.treeTitleExtra);
|
||||
}
|
||||
|
||||
render() {
|
||||
const tree = this.treeMaster.currentTree;
|
||||
|
||||
|
||||
@ -1,179 +0,0 @@
|
||||
import { Component } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import TreeNode from '../controllers/tree-node';
|
||||
import TreeTitle from './tree-title';
|
||||
import TreeBranches from './tree-branches';
|
||||
import { IconEyeClose } from '../icons/eye-close';
|
||||
import { IPublicModelPluginContext, IPublicModelModalNodesManager, IPublicEnumEventNames } from '@alilc/lowcode-types';
|
||||
|
||||
class ModalTreeNodeView extends Component<{
|
||||
treeNode: TreeNode;
|
||||
pluginContext: IPublicModelPluginContext;
|
||||
}> {
|
||||
private modalNodesManager: IPublicModelModalNodesManager | undefined | null;
|
||||
readonly pluginContext: IPublicModelPluginContext;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
// 模态管理对象
|
||||
this.pluginContext = props.pluginContext;
|
||||
const { project } = this.pluginContext;
|
||||
this.modalNodesManager = project.currentDocument?.modalNodesManager;
|
||||
}
|
||||
|
||||
hideAllNodes() {
|
||||
this.modalNodesManager?.hideModalNodes();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
// 当指定了新的根节点时,要从原始的根节点去获取模态节点
|
||||
const { project } = this.pluginContext;
|
||||
const rootNode = project.currentDocument?.root;
|
||||
const rootTreeNode = treeNode.tree.getTreeNode(rootNode!);
|
||||
const modalNodes = rootTreeNode.children?.filter((item) => {
|
||||
return item.node.componentMeta?.isModal;
|
||||
});
|
||||
if (!modalNodes || modalNodes.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const hasVisibleModalNode = !!this.modalNodesManager?.getVisibleModalNode();
|
||||
return (
|
||||
<div className="tree-node-modal">
|
||||
<div className="tree-node-modal-title">
|
||||
<span>模态视图层</span>
|
||||
<div
|
||||
className="tree-node-modal-title-visible-icon"
|
||||
onClick={this.hideAllNodes.bind(this)}
|
||||
>
|
||||
{hasVisibleModalNode ? <IconEyeClose /> : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="tree-pane-modal-content">
|
||||
<TreeBranches treeNode={rootTreeNode} isModal pluginContext={this.pluginContext} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default class RootTreeNodeView extends Component<{
|
||||
treeNode: TreeNode;
|
||||
pluginContext: IPublicModelPluginContext;
|
||||
}> {
|
||||
state = {
|
||||
expanded: false,
|
||||
selected: false,
|
||||
hidden: false,
|
||||
locked: false,
|
||||
detecting: false,
|
||||
isRoot: false,
|
||||
highlight: false,
|
||||
dropping: false,
|
||||
conditionFlow: false,
|
||||
};
|
||||
|
||||
eventOffCallbacks: Array<() => void> = [];
|
||||
|
||||
componentDidMount() {
|
||||
const { treeNode, pluginContext } = this.props;
|
||||
const { pluginEvent, event } = pluginContext;
|
||||
const { id } = treeNode;
|
||||
|
||||
this.state = {
|
||||
expanded: false,
|
||||
selected: false,
|
||||
hidden: false,
|
||||
locked: false,
|
||||
detecting: false,
|
||||
isRoot: treeNode.isRoot(),
|
||||
// 是否投放响应
|
||||
dropping: treeNode.dropDetail?.index != null,
|
||||
conditionFlow: treeNode.node.conditionGroup != null,
|
||||
highlight: treeNode.isFocusingNode(),
|
||||
};
|
||||
|
||||
const doc = pluginContext.project.currentDocument;
|
||||
|
||||
this.eventOffCallbacks.push(pluginEvent.on('tree-node.hiddenChanged', (payload: any) => {
|
||||
const { hidden, nodeId } = payload;
|
||||
if (nodeId === id) {
|
||||
this.setState({ hidden });
|
||||
}
|
||||
}));
|
||||
|
||||
this.eventOffCallbacks.push(pluginEvent.on('tree-node.expandedChanged', (payload: any) => {
|
||||
const { expanded, nodeId } = payload;
|
||||
if (nodeId === id) {
|
||||
this.setState({ expanded });
|
||||
}
|
||||
}));
|
||||
|
||||
this.eventOffCallbacks.push(pluginEvent.on('tree-node.lockedChanged', (payload: any) => {
|
||||
const { locked, nodeId } = payload;
|
||||
if (nodeId === id) {
|
||||
this.setState({ locked });
|
||||
}
|
||||
}));
|
||||
|
||||
this.eventOffCallbacks.push(
|
||||
event.on(
|
||||
IPublicEnumEventNames.DOCUMENT_DROPLOCATION_CHANGED,
|
||||
(payload: any) => {
|
||||
const { document } = payload;
|
||||
if (document.id === doc?.id) {
|
||||
this.setState({
|
||||
dropping: treeNode.dropDetail?.index != null,
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
const offSelectionChange = doc?.selection?.onSelectionChange(() => {
|
||||
this.setState({ selected: treeNode.selected });
|
||||
});
|
||||
this.eventOffCallbacks.push(offSelectionChange!);
|
||||
const offDetectingChange = doc?.detecting?.onDetectingChange(() => {
|
||||
this.setState({ detecting: treeNode.detecting });
|
||||
});
|
||||
this.eventOffCallbacks.push(offDetectingChange!);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
this.eventOffCallbacks?.forEach((offFun: () => void) => {
|
||||
offFun();
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
const className = classNames('tree-node', {
|
||||
// 是否展开
|
||||
expanded: this.state.expanded,
|
||||
// 是否选中的
|
||||
selected: this.state.selected,
|
||||
// 是否隐藏的
|
||||
hidden: this.state.hidden,
|
||||
// 是否锁定的
|
||||
locked: this.state.locked,
|
||||
// 是否悬停中
|
||||
detecting: this.state.detecting,
|
||||
// 是否投放响应
|
||||
dropping: this.state.dropping,
|
||||
'is-root': this.state.isRoot,
|
||||
'condition-flow': this.state.conditionFlow,
|
||||
highlight: this.state.highlight,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={className} data-id={treeNode.id}>
|
||||
<TreeTitle treeNode={treeNode} pluginContext={this.props.pluginContext} />
|
||||
<ModalTreeNodeView treeNode={treeNode} pluginContext={this.props.pluginContext} />
|
||||
<TreeBranches treeNode={treeNode} pluginContext={this.props.pluginContext} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -2,38 +2,33 @@ import { Component } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import TreeNode from '../controllers/tree-node';
|
||||
import TreeNodeView from './tree-node';
|
||||
import { IPublicModelPluginContext, IPublicModelExclusiveGroup, IPublicEnumEventNames, IPublicTypeLocationDetailType } from '@alilc/lowcode-types';
|
||||
import { IPublicModelPluginContext, IPublicModelExclusiveGroup } from '@alilc/lowcode-types';
|
||||
|
||||
export default class TreeBranches extends Component<{
|
||||
treeNode: TreeNode;
|
||||
isModal?: boolean;
|
||||
pluginContext: IPublicModelPluginContext;
|
||||
expanded: boolean;
|
||||
}> {
|
||||
state = {
|
||||
expanded: false,
|
||||
filterWorking: false,
|
||||
matchChild: false,
|
||||
};
|
||||
private offExpandedChanged: (() => void) | null;
|
||||
componentDidMount() {
|
||||
const { treeNode, pluginContext } = this.props;
|
||||
const { expanded } = treeNode;
|
||||
const { pluginEvent } = pluginContext;
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
const { treeNode } = this.props;
|
||||
const { filterWorking, matchChild } = treeNode.filterReult;
|
||||
this.setState({ expanded, filterWorking, matchChild });
|
||||
this.setState({ filterWorking, matchChild });
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { treeNode } = this.props;
|
||||
treeNode.onFilterResultChanged = () => {
|
||||
const { filterWorking: newFilterWorking, matchChild: newMatchChild } = treeNode.filterReult;
|
||||
this.setState({ filterWorking: newFilterWorking, matchChild: newMatchChild });
|
||||
};
|
||||
|
||||
this.offExpandedChanged = pluginEvent.on('tree-node.expandedChanged', (payload: any) => {
|
||||
const { expanded: value, nodeId } = payload;
|
||||
const { id } = this.props.treeNode;
|
||||
if (nodeId === id) {
|
||||
this.setState({ expanded: value });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
@ -43,8 +38,8 @@ export default class TreeBranches extends Component<{
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode, isModal } = this.props;
|
||||
const { filterWorking, matchChild, expanded } = this.state;
|
||||
const { treeNode, isModal, expanded } = this.props;
|
||||
const { filterWorking, matchChild } = this.state;
|
||||
// 条件过滤生效时,如果命中了子节点,需要将该节点展开
|
||||
const expandInFilterResult = filterWorking && matchChild;
|
||||
|
||||
@ -57,7 +52,11 @@ export default class TreeBranches extends Component<{
|
||||
{
|
||||
!isModal && <TreeNodeSlots treeNode={treeNode} pluginContext={this.props.pluginContext} />
|
||||
}
|
||||
<TreeNodeChildren treeNode={treeNode} isModal={isModal || false} pluginContext={this.props.pluginContext} />
|
||||
<TreeNodeChildren
|
||||
treeNode={treeNode}
|
||||
isModal={isModal || false}
|
||||
pluginContext={this.props.pluginContext}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -77,7 +76,7 @@ class TreeNodeChildren extends Component<{
|
||||
offLocationChanged: () => void;
|
||||
componentDidMount() {
|
||||
const { treeNode, pluginContext } = this.props;
|
||||
const { event } = pluginContext;
|
||||
const { project } = pluginContext;
|
||||
const { filterWorking, matchSelf, keywords } = treeNode.filterReult;
|
||||
const { dropDetail } = treeNode;
|
||||
this.setState({
|
||||
@ -98,11 +97,10 @@ class TreeNodeChildren extends Component<{
|
||||
keywords: newKeywords,
|
||||
});
|
||||
};
|
||||
this.offLocationChanged = event.on(
|
||||
IPublicEnumEventNames.DOCUMENT_DROPLOCATION_CHANGED,
|
||||
(payload: any) => {
|
||||
this.setState({ dropDetail: treeNode.dropDetail });
|
||||
},
|
||||
this.offLocationChanged = project.currentDocument?.onDropLocationChanged(
|
||||
() => {
|
||||
this.setState({ dropDetail: treeNode.dropDetail });
|
||||
},
|
||||
);
|
||||
}
|
||||
componentWillUnmount(): void {
|
||||
|
||||
@ -3,12 +3,67 @@ import classNames from 'classnames';
|
||||
import TreeNode from '../controllers/tree-node';
|
||||
import TreeTitle from './tree-title';
|
||||
import TreeBranches from './tree-branches';
|
||||
import { IPublicModelPluginContext, IPublicEnumEventNames } from '@alilc/lowcode-types';
|
||||
import { IconEyeClose } from '../icons/eye-close';
|
||||
import { IPublicModelPluginContext, IPublicModelModalNodesManager, IPublicModelDocumentModel } from '@alilc/lowcode-types';
|
||||
|
||||
class ModalTreeNodeView extends Component<{
|
||||
treeNode: TreeNode;
|
||||
pluginContext: IPublicModelPluginContext;
|
||||
}> {
|
||||
private modalNodesManager: IPublicModelModalNodesManager | undefined | null;
|
||||
readonly pluginContext: IPublicModelPluginContext;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
// 模态管理对象
|
||||
this.pluginContext = props.pluginContext;
|
||||
const { project } = this.pluginContext;
|
||||
this.modalNodesManager = project.currentDocument?.modalNodesManager;
|
||||
}
|
||||
|
||||
hideAllNodes() {
|
||||
this.modalNodesManager?.hideModalNodes();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
// 当指定了新的根节点时,要从原始的根节点去获取模态节点
|
||||
const { project } = this.pluginContext;
|
||||
const rootNode = project.currentDocument?.root;
|
||||
const rootTreeNode = treeNode.tree.getTreeNode(rootNode!);
|
||||
const expanded = rootTreeNode.expanded;
|
||||
|
||||
const hasVisibleModalNode = !!this.modalNodesManager?.getVisibleModalNode();
|
||||
return (
|
||||
<div className="tree-node-modal">
|
||||
<div className="tree-node-modal-title">
|
||||
<span>模态视图层</span>
|
||||
<div
|
||||
className="tree-node-modal-title-visible-icon"
|
||||
onClick={this.hideAllNodes.bind(this)}
|
||||
>
|
||||
{hasVisibleModalNode ? <IconEyeClose /> : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="tree-pane-modal-content">
|
||||
<TreeBranches
|
||||
treeNode={rootTreeNode}
|
||||
expanded={expanded}
|
||||
isModal
|
||||
pluginContext={this.pluginContext}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default class TreeNodeView extends Component<{
|
||||
treeNode: TreeNode;
|
||||
isModal?: boolean;
|
||||
pluginContext: IPublicModelPluginContext;
|
||||
isRootNode: boolean;
|
||||
}> {
|
||||
state = {
|
||||
expanded: false,
|
||||
@ -20,64 +75,51 @@ export default class TreeNodeView extends Component<{
|
||||
highlight: false,
|
||||
dropping: false,
|
||||
conditionFlow: false,
|
||||
expandable: false,
|
||||
};
|
||||
|
||||
eventOffCallbacks: Array<() => void> = [];
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
componentDidMount() {
|
||||
const { treeNode, pluginContext } = this.props;
|
||||
const { pluginEvent, event } = pluginContext;
|
||||
const { id } = treeNode;
|
||||
|
||||
const { treeNode, isRootNode } = this.props;
|
||||
this.state = {
|
||||
expanded: false,
|
||||
selected: false,
|
||||
hidden: false,
|
||||
locked: false,
|
||||
detecting: false,
|
||||
expanded: isRootNode ? true : treeNode.expanded,
|
||||
selected: treeNode.selected,
|
||||
hidden: treeNode.hidden,
|
||||
locked: treeNode.locked,
|
||||
detecting: treeNode.detecting,
|
||||
isRoot: treeNode.isRoot(),
|
||||
// 是否投放响应
|
||||
dropping: treeNode.dropDetail?.index != null,
|
||||
conditionFlow: treeNode.node.conditionGroup != null,
|
||||
highlight: treeNode.isFocusingNode(),
|
||||
expandable: treeNode.expandable,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { treeNode, pluginContext } = this.props;
|
||||
const { event, project } = pluginContext;
|
||||
|
||||
const doc = project.currentDocument;
|
||||
|
||||
treeNode.onExpandedChanged = ((expanded: boolean) => {
|
||||
this.setState({ expanded });
|
||||
});
|
||||
treeNode.onHiddenChanged = (hidden: boolean) => {
|
||||
this.setState({ hidden });
|
||||
};
|
||||
treeNode.onLockedChanged = (locked: boolean) => {
|
||||
this.setState({ locked });
|
||||
};
|
||||
|
||||
const doc = pluginContext.project.currentDocument;
|
||||
|
||||
|
||||
this.eventOffCallbacks.push(pluginEvent.on('tree-node.hiddenChanged', (payload: any) => {
|
||||
const { hidden, nodeId } = payload;
|
||||
if (nodeId === id) {
|
||||
this.setState({ hidden });
|
||||
}
|
||||
}));
|
||||
|
||||
this.eventOffCallbacks.push(pluginEvent.on('tree-node.expandedChanged', (payload: any) => {
|
||||
const { expanded, nodeId } = payload;
|
||||
if (nodeId === id) {
|
||||
this.setState({ expanded });
|
||||
}
|
||||
}));
|
||||
|
||||
this.eventOffCallbacks.push(pluginEvent.on('tree-node.lockedChanged', (payload: any) => {
|
||||
const { locked, nodeId } = payload;
|
||||
if (nodeId === id) {
|
||||
this.setState({ locked });
|
||||
}
|
||||
}));
|
||||
|
||||
this.eventOffCallbacks.push(
|
||||
event.on(
|
||||
IPublicEnumEventNames.DOCUMENT_DROPLOCATION_CHANGED,
|
||||
(payload: any) => {
|
||||
const { document } = payload;
|
||||
if (document.id === doc?.id) {
|
||||
this.setState({
|
||||
dropping: treeNode.dropDetail?.index != null,
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
doc?.onDropLocationChanged((document: IPublicModelDocumentModel) => {
|
||||
this.setState({
|
||||
dropping: treeNode.dropDetail?.index != null,
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
const offSelectionChange = doc?.selection?.onSelectionChange(() => {
|
||||
@ -95,8 +137,25 @@ export default class TreeNodeView extends Component<{
|
||||
});
|
||||
}
|
||||
|
||||
shouldShowModalTreeNode(): boolean {
|
||||
const { treeNode, isRootNode, pluginContext } = this.props;
|
||||
if (!isRootNode) {
|
||||
// 只在 当前树 的根节点展示模态节点
|
||||
return false;
|
||||
}
|
||||
|
||||
// 当指定了新的根节点时,要从原始的根节点去获取模态节点
|
||||
const { project } = pluginContext;
|
||||
const rootNode = project.currentDocument?.root;
|
||||
const rootTreeNode = treeNode.tree.getTreeNode(rootNode!);
|
||||
const modalNodes = rootTreeNode.children?.filter((item) => {
|
||||
return item.node.componentMeta?.isModal;
|
||||
});
|
||||
return !!(modalNodes && modalNodes.length > 0);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode, isModal } = this.props;
|
||||
const { treeNode, isModal, isRootNode } = this.props;
|
||||
const className = classNames('tree-node', {
|
||||
// 是否展开
|
||||
expanded: this.state.expanded,
|
||||
@ -114,24 +173,39 @@ export default class TreeNodeView extends Component<{
|
||||
'condition-flow': this.state.conditionFlow,
|
||||
highlight: this.state.highlight,
|
||||
});
|
||||
let shouldShowModalTreeNode: boolean = this.shouldShowModalTreeNode();
|
||||
|
||||
// filter 处理
|
||||
const { filterWorking, matchChild, matchSelf } = treeNode.filterReult;
|
||||
|
||||
// 条件过滤生效时,如果未命中本节点或子节点,则不展示该节点
|
||||
if (filterWorking && !matchChild && !matchSelf) {
|
||||
if (!isRootNode && filterWorking && !matchChild && !matchSelf) {
|
||||
// 条件过滤生效时,如果未命中本节点或子节点,则不展示该节点
|
||||
// 根节点始终展示
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className} data-id={treeNode.id}>
|
||||
<div
|
||||
className={className}
|
||||
data-id={treeNode.id}
|
||||
>
|
||||
<TreeTitle
|
||||
treeNode={treeNode}
|
||||
isModal={isModal}
|
||||
expanded={this.state.expanded}
|
||||
hidden={this.state.hidden}
|
||||
locked={this.state.locked}
|
||||
expandable={this.state.expandable}
|
||||
pluginContext={this.props.pluginContext}
|
||||
/>
|
||||
{shouldShowModalTreeNode &&
|
||||
<ModalTreeNodeView
|
||||
treeNode={treeNode}
|
||||
pluginContext={this.props.pluginContext}
|
||||
/>
|
||||
}
|
||||
<TreeBranches
|
||||
treeNode={treeNode}
|
||||
isModal={false}
|
||||
expanded={this.state.expanded}
|
||||
pluginContext={this.props.pluginContext}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -21,6 +21,10 @@ function emitOutlineEvent(event: IPublicApiEvent, type: string, treeNode: TreeNo
|
||||
export default class TreeTitle extends Component<{
|
||||
treeNode: TreeNode;
|
||||
isModal?: boolean;
|
||||
expanded: boolean;
|
||||
hidden: boolean;
|
||||
locked: boolean;
|
||||
expandable: boolean;
|
||||
pluginContext: IPublicModelPluginContext;
|
||||
}> {
|
||||
state: {
|
||||
@ -73,30 +77,18 @@ export default class TreeTitle extends Component<{
|
||||
// 光标定位最后一个
|
||||
// input.selectionStart = input.selectionEnd;
|
||||
};
|
||||
offTitleLabelChanged: (() => void) | undefined;
|
||||
|
||||
componentDidMount() {
|
||||
const { treeNode, pluginContext } = this.props;
|
||||
const { id } = treeNode;
|
||||
const { pluginEvent } = pluginContext;
|
||||
const { treeNode } = this.props;
|
||||
this.setState({
|
||||
editing: false,
|
||||
title: treeNode.titleLabel,
|
||||
});
|
||||
this.offTitleLabelChanged = pluginEvent.on('tree-node.titleLabelChanged', (payload: any) => {
|
||||
const { nodeId } = payload;
|
||||
if (nodeId === id) {
|
||||
this.setState({
|
||||
title: treeNode.titleLabel,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
if (this.offTitleLabelChanged) {
|
||||
this.offTitleLabelChanged();
|
||||
}
|
||||
treeNode.onTitleLabelChanged = () => {
|
||||
this.setState({
|
||||
title: treeNode.titleLabel,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -158,7 +150,7 @@ export default class TreeTitle extends Component<{
|
||||
<IconRadio className="tree-node-modal-radio" />
|
||||
</div>
|
||||
)}
|
||||
{isCNode && <ExpandBtn treeNode={treeNode} pluginContext={this.props.pluginContext} />}
|
||||
{isCNode && <ExpandBtn expandable={this.props.expandable} expanded={this.props.expanded} treeNode={treeNode} pluginContext={this.props.pluginContext} />}
|
||||
<div className="tree-node-icon">{createIcon(treeNode.icon)}</div>
|
||||
<div className="tree-node-title-label">
|
||||
{editing ? (
|
||||
@ -200,8 +192,8 @@ export default class TreeTitle extends Component<{
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
{shouldShowHideBtn && <HideBtn treeNode={treeNode} pluginContext={this.props.pluginContext} />}
|
||||
{shouldShowLockBtn && <LockBtn treeNode={treeNode} pluginContext={this.props.pluginContext} />}
|
||||
{shouldShowHideBtn && <HideBtn hidden={this.props.hidden} treeNode={treeNode} pluginContext={this.props.pluginContext} />}
|
||||
{shouldShowLockBtn && <LockBtn locked={this.props.locked} treeNode={treeNode} pluginContext={this.props.pluginContext} />}
|
||||
{shouldEditBtn && <RenameBtn treeNode={treeNode} pluginContext={this.props.pluginContext} onClick={this.enableEdit} /> }
|
||||
|
||||
</div>
|
||||
@ -233,39 +225,10 @@ class RenameBtn extends Component<{
|
||||
class LockBtn extends Component<{
|
||||
treeNode: TreeNode;
|
||||
pluginContext: IPublicModelPluginContext;
|
||||
}, {
|
||||
locked: boolean;
|
||||
}> {
|
||||
state = {
|
||||
locked: false,
|
||||
};
|
||||
offLockedChanged: () => void;
|
||||
|
||||
componentDidMount(): void {
|
||||
const { treeNode, pluginContext } = this.props;
|
||||
const { id } = treeNode;
|
||||
const { pluginEvent } = pluginContext;
|
||||
|
||||
this.setState({
|
||||
locked: treeNode.locked,
|
||||
});
|
||||
|
||||
this.offLockedChanged = pluginEvent.on('tree-node.lockedChanged', (payload: any) => {
|
||||
const { locked, nodeId } = payload;
|
||||
if (nodeId === id) {
|
||||
this.setState({ locked });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.offLockedChanged) {
|
||||
this.offLockedChanged();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
const { treeNode, locked } = this.props;
|
||||
const { intl, common } = this.props.pluginContext;
|
||||
const Tip = common.editorCabin.Tip;
|
||||
return (
|
||||
@ -273,11 +236,11 @@ class LockBtn extends Component<{
|
||||
className="tree-node-lock-btn"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
treeNode.setLocked(!this.state.locked);
|
||||
treeNode.setLocked(!locked);
|
||||
}}
|
||||
>
|
||||
{this.state.locked ? <IconUnlock /> : <IconLock /> }
|
||||
<Tip>{this.state.locked ? intl('Unlock') : intl('Lock')}</Tip>
|
||||
{locked ? <IconUnlock /> : <IconLock /> }
|
||||
<Tip>{locked ? intl('Unlock') : intl('Lock')}</Tip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -285,35 +248,13 @@ class LockBtn extends Component<{
|
||||
|
||||
class HideBtn extends Component<{
|
||||
treeNode: TreeNode;
|
||||
hidden: boolean;
|
||||
pluginContext: IPublicModelPluginContext;
|
||||
}, {
|
||||
hidden: boolean;
|
||||
}> {
|
||||
state = {
|
||||
hidden: false,
|
||||
};
|
||||
offHiddenChanged: () => void;
|
||||
componentDidMount(): void {
|
||||
const { treeNode, pluginContext } = this.props;
|
||||
const { pluginEvent } = pluginContext;
|
||||
const { id } = treeNode;
|
||||
this.setState({
|
||||
hidden: treeNode.hidden,
|
||||
});
|
||||
this.offHiddenChanged = pluginEvent.on('tree-node.hiddenChanged', (payload: any) => {
|
||||
const { hidden, nodeId } = payload;
|
||||
if (nodeId === id) {
|
||||
this.setState({ hidden });
|
||||
}
|
||||
});
|
||||
}
|
||||
componentWillUnmount(): void {
|
||||
if (this.offHiddenChanged) {
|
||||
this.offHiddenChanged();
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
const { treeNode, hidden } = this.props;
|
||||
const { intl, common } = this.props.pluginContext;
|
||||
const Tip = common.editorCabin.Tip;
|
||||
return (
|
||||
@ -321,12 +262,12 @@ class HideBtn extends Component<{
|
||||
className="tree-node-hide-btn"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
emitOutlineEvent(this.props.pluginContext.event, this.state.hidden ? 'show' : 'hide', treeNode);
|
||||
treeNode.setHidden(!this.state.hidden);
|
||||
emitOutlineEvent(this.props.pluginContext.event, hidden ? 'show' : 'hide', treeNode);
|
||||
treeNode.setHidden(!hidden);
|
||||
}}
|
||||
>
|
||||
{this.state.hidden ? <IconEye /> : <IconEyeClose />}
|
||||
<Tip>{this.state.hidden ? intl('Show') : intl('Hide')}</Tip>
|
||||
{hidden ? <IconEye /> : <IconEyeClose />}
|
||||
<Tip>{hidden ? intl('Show') : intl('Hide')}</Tip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -336,52 +277,24 @@ class HideBtn extends Component<{
|
||||
class ExpandBtn extends Component<{
|
||||
treeNode: TreeNode;
|
||||
pluginContext: IPublicModelPluginContext;
|
||||
}, {
|
||||
expanded: boolean;
|
||||
expandable: boolean;
|
||||
}> {
|
||||
state = {
|
||||
expanded: false,
|
||||
expandable: false,
|
||||
};
|
||||
|
||||
offExpandedChanged: () => void;
|
||||
|
||||
componentDidMount(): void {
|
||||
const { treeNode, pluginContext } = this.props;
|
||||
const { id } = treeNode;
|
||||
const { pluginEvent } = pluginContext;
|
||||
this.setState({
|
||||
expanded: treeNode.expanded,
|
||||
expandable: treeNode.expandable,
|
||||
});
|
||||
this.offExpandedChanged = pluginEvent.on('tree-node.expandedChanged', (payload: any) => {
|
||||
const { expanded, nodeId } = payload;
|
||||
if (nodeId === id) {
|
||||
this.setState({ expanded });
|
||||
}
|
||||
});
|
||||
}
|
||||
componentWillUnmount(): void {
|
||||
if (this.offExpandedChanged) {
|
||||
this.offExpandedChanged();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { treeNode } = this.props;
|
||||
if (!this.state.expandable) {
|
||||
const { treeNode, expanded, expandable } = this.props;
|
||||
if (!expandable) {
|
||||
return <i className="tree-node-expand-placeholder" />;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="tree-node-expand-btn"
|
||||
onClick={(e) => {
|
||||
if (this.state.expanded) {
|
||||
if (expanded) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
emitOutlineEvent(this.props.pluginContext.event, this.state.expanded ? 'collapse' : 'expand', treeNode);
|
||||
treeNode.setExpanded(!this.state.expanded);
|
||||
emitOutlineEvent(this.props.pluginContext.event, expanded ? 'collapse' : 'expand', treeNode);
|
||||
treeNode.setExpanded(!expanded);
|
||||
}}
|
||||
>
|
||||
<IconArrowRight size="small" />
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { Component, MouseEvent as ReactMouseEvent } from 'react';
|
||||
import { isFormEvent, canClickNode, isShaken } from '@alilc/lowcode-utils';
|
||||
import { Tree } from '../controllers/tree';
|
||||
import RootTreeNodeView from './root-tree-node';
|
||||
import { IPublicEnumDragObjectType, IPublicModelPluginContext, IPublicModelNode, IPublicEnumEventNames } from '@alilc/lowcode-types';
|
||||
import TreeNodeView from './tree-node';
|
||||
import { IPublicEnumDragObjectType, IPublicModelPluginContext, IPublicModelNode } from '@alilc/lowcode-types';
|
||||
|
||||
function getTreeNodeIdByEvent(e: ReactMouseEvent, stop: Element): null | string {
|
||||
let target: Element | null = e.target as Element;
|
||||
@ -81,6 +81,9 @@ export default class TreeView extends Component<{
|
||||
private onDoubleClick = (e: ReactMouseEvent) => {
|
||||
e.preventDefault();
|
||||
const treeNode = this.getTreeNodeFromEvent(e);
|
||||
if (treeNode?.id === this.state.root?.id) {
|
||||
return;
|
||||
}
|
||||
if (!treeNode?.expanded) {
|
||||
this.props.tree.expandAllDecendants(treeNode);
|
||||
} else {
|
||||
@ -173,17 +176,13 @@ export default class TreeView extends Component<{
|
||||
componentDidMount() {
|
||||
const { tree, pluginContext } = this.props;
|
||||
const { root } = tree;
|
||||
const { event, project } = pluginContext;
|
||||
const { project } = pluginContext;
|
||||
this.setState({ root });
|
||||
const doc = project.currentDocument;
|
||||
// root 变化
|
||||
event.on(IPublicEnumEventNames.DOCUMENT_FOCUS_NODE_CHANGED, (payload: any) => {
|
||||
const { document } = payload;
|
||||
if (document.id === doc?.id) {
|
||||
this.setState({
|
||||
root: tree.root,
|
||||
});
|
||||
}
|
||||
doc?.onFocusNodeChanged(() => {
|
||||
this.setState({
|
||||
root: tree.root,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -201,7 +200,12 @@ export default class TreeView extends Component<{
|
||||
onDoubleClick={this.onDoubleClick}
|
||||
onMouseLeave={this.onMouseLeave}
|
||||
>
|
||||
<RootTreeNodeView key={this.state.root?.id} treeNode={this.state.root} pluginContext={this.props.pluginContext} />
|
||||
<TreeNodeView
|
||||
key={this.state.root?.id}
|
||||
treeNode={this.state.root}
|
||||
pluginContext={this.props.pluginContext}
|
||||
isRootNode
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -13,9 +13,11 @@ import {
|
||||
import {
|
||||
ScrollTarget as InnerScrollTarget,
|
||||
} from '@alilc/lowcode-designer';
|
||||
import { editorSymbol, designerSymbol } from '../symbols';
|
||||
import { editorSymbol, designerSymbol, nodeSymbol } from '../symbols';
|
||||
import { Dragon } from '../model';
|
||||
import { DropLocation } from '../model/drop-location';
|
||||
import { ActiveTracker } from '../model/active-tracker';
|
||||
|
||||
|
||||
export class Canvas implements IPublicApiCanvas {
|
||||
private readonly [editorSymbol]: IPublicModelEditor;
|
||||
@ -40,7 +42,10 @@ export class Canvas implements IPublicApiCanvas {
|
||||
* 创建插入位置,考虑放到 dragon 中
|
||||
*/
|
||||
createLocation(locationData: IPublicTypeLocationData): IPublicModelDropLocation {
|
||||
return this[designerSymbol].createLocation(locationData);
|
||||
return this[designerSymbol].createLocation({
|
||||
...locationData,
|
||||
target: (locationData.target as any)[nodeSymbol],
|
||||
});
|
||||
}
|
||||
|
||||
get dragon(): IPublicModelDragon | null {
|
||||
@ -48,8 +53,10 @@ export class Canvas implements IPublicApiCanvas {
|
||||
}
|
||||
|
||||
get activeTracker(): IPublicModelActiveTracker | null {
|
||||
return this[designerSymbol].activeTracker;
|
||||
const activeTracker = new ActiveTracker(this[designerSymbol].activeTracker);
|
||||
return activeTracker;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Editor as InnerEditor, EventBus } from '@alilc/lowcode-editor-core';
|
||||
import { getLogger, isPublicEventName, isPluginEventName } from '@alilc/lowcode-utils';
|
||||
import { getLogger, isPluginEventName } from '@alilc/lowcode-utils';
|
||||
import { IPublicApiEvent, IPublicTypeDisposable } from '@alilc/lowcode-types';
|
||||
|
||||
const logger = getLogger({ level: 'warn', bizName: 'shell-event' });
|
||||
@ -34,10 +34,10 @@ export class Event implements IPublicApiEvent {
|
||||
* @param listener 事件回调
|
||||
*/
|
||||
on(event: string, listener: (...args: any[]) => void): IPublicTypeDisposable {
|
||||
if (isPluginEventName(event) || isPublicEventName(event)) {
|
||||
if (isPluginEventName(event)) {
|
||||
return this[eventBusSymbol].on(event, listener);
|
||||
} else {
|
||||
logger.warn(`fail to monitor on event ${event}, which is neither a engine public event nor a plugin event`);
|
||||
logger.warn(`fail to monitor on event ${event}, event should have a prefix like 'somePrefix:eventName'`);
|
||||
return () => {};
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,6 @@ import {
|
||||
IPublicApiSimulatorHost,
|
||||
IPublicModelDocumentModel,
|
||||
IPublicTypePropsTransducer,
|
||||
IPublicEnumEventNames,
|
||||
IPublicEnumTransformStage,
|
||||
IPublicTypeDisposable,
|
||||
} from '@alilc/lowcode-types';
|
||||
@ -166,7 +165,7 @@ export class Project implements IPublicApiProject {
|
||||
*/
|
||||
onRemoveDocument(fn: (data: { id: string}) => void): IPublicTypeDisposable {
|
||||
return this[editorSymbol].eventBus.on(
|
||||
IPublicEnumEventNames.DESIGNER_DOCUMENT_REMOVE,
|
||||
'designer.document.remove',
|
||||
(data: { id: string }) => fn(data),
|
||||
);
|
||||
}
|
||||
|
||||
36
packages/shell/src/model/active-tracker.ts
Normal file
36
packages/shell/src/model/active-tracker.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { IPublicModelActiveTracker, IPublicModelNode, IPublicTypeActiveTarget } from '@alilc/lowcode-types';
|
||||
import { IActiveTracker } from '@alilc/lowcode-designer';
|
||||
import { Node } from './node';
|
||||
import { nodeSymbol } from '../symbols';
|
||||
|
||||
const activeTrackerSymbol = Symbol('activeTracker');
|
||||
|
||||
|
||||
export class ActiveTracker implements IPublicModelActiveTracker {
|
||||
private readonly [activeTrackerSymbol]: IActiveTracker;
|
||||
|
||||
|
||||
constructor(innerTracker: IActiveTracker) {
|
||||
this[activeTrackerSymbol] = innerTracker;
|
||||
}
|
||||
|
||||
onChange(fn: (target: IPublicTypeActiveTarget) => void): () => void {
|
||||
if (!fn) {
|
||||
return () => {};
|
||||
}
|
||||
return this[activeTrackerSymbol].onChange((t: IPublicTypeActiveTarget) => {
|
||||
const { node: innerNode, detail, instance } = t;
|
||||
const publicNode = Node.create(innerNode);
|
||||
const publicActiveTarget = {
|
||||
node: publicNode!,
|
||||
detail,
|
||||
instance,
|
||||
};
|
||||
fn(publicActiveTarget);
|
||||
});
|
||||
}
|
||||
|
||||
track(node: IPublicModelNode) {
|
||||
this[activeTrackerSymbol].track((node as any)[nodeSymbol]);
|
||||
}
|
||||
}
|
||||
@ -22,8 +22,8 @@ import {
|
||||
IPublicModelModalNodesManager,
|
||||
IPublicTypePropChangeOptions,
|
||||
IPublicModelDropLocation,
|
||||
IPublicEnumEventNames,
|
||||
IPublicApiCanvas,
|
||||
IPublicTypeDisposable,
|
||||
} from '@alilc/lowcode-types';
|
||||
import { Node } from './node';
|
||||
import { Selection } from './selection';
|
||||
@ -108,7 +108,7 @@ export class DocumentModel implements IPublicModelDocumentModel {
|
||||
set focusNode(node: IPublicModelNode | null) {
|
||||
this._focusNode = node;
|
||||
this[editorSymbol].eventBus.emit(
|
||||
IPublicEnumEventNames.DOCUMENT_FOCUS_NODE_CHANGED,
|
||||
'shell.document.focusNodeChanged',
|
||||
{ document: this, focusNode: node },
|
||||
);
|
||||
}
|
||||
@ -157,7 +157,7 @@ export class DocumentModel implements IPublicModelDocumentModel {
|
||||
*/
|
||||
importSchema(schema: IPublicTypeRootSchema): void {
|
||||
this[documentSymbol].import(schema);
|
||||
this[editorSymbol].eventBus.emit(IPublicEnumEventNames.DOCUMENT_IMPORT_SCHEMA, schema);
|
||||
this[editorSymbol].eventBus.emit('shell.document.importSchema', schema);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -241,7 +241,7 @@ export class DocumentModel implements IPublicModelDocumentModel {
|
||||
/**
|
||||
* 当前 document 新增节点事件
|
||||
*/
|
||||
onAddNode(fn: (node: IPublicModelNode) => void): () => void {
|
||||
onAddNode(fn: (node: IPublicModelNode) => void): IPublicTypeDisposable {
|
||||
return this[documentSymbol].onNodeCreate((node: InnerNode) => {
|
||||
fn(Node.create(node)!);
|
||||
});
|
||||
@ -250,7 +250,7 @@ export class DocumentModel implements IPublicModelDocumentModel {
|
||||
/**
|
||||
* 当前 document 新增节点事件,此时节点已经挂载到 document 上
|
||||
*/
|
||||
onMountNode(fn: (payload: { node: IPublicModelNode }) => void): () => void {
|
||||
onMountNode(fn: (payload: { node: IPublicModelNode }) => void): IPublicTypeDisposable {
|
||||
this[editorSymbol].eventBus.on('node.add', fn as any);
|
||||
return () => {
|
||||
this[editorSymbol].eventBus.off('node.add', fn as any);
|
||||
@ -260,7 +260,7 @@ export class DocumentModel implements IPublicModelDocumentModel {
|
||||
/**
|
||||
* 当前 document 删除节点事件
|
||||
*/
|
||||
onRemoveNode(fn: (node: IPublicModelNode) => void): () => void {
|
||||
onRemoveNode(fn: (node: IPublicModelNode) => void): IPublicTypeDisposable {
|
||||
return this[documentSymbol].onNodeDestroy((node: InnerNode) => {
|
||||
fn(Node.create(node)!);
|
||||
});
|
||||
@ -269,7 +269,7 @@ export class DocumentModel implements IPublicModelDocumentModel {
|
||||
/**
|
||||
* 当前 document 的 hover 变更事件
|
||||
*/
|
||||
onChangeDetecting(fn: (node: IPublicModelNode) => void): () => void {
|
||||
onChangeDetecting(fn: (node: IPublicModelNode) => void): IPublicTypeDisposable {
|
||||
return this[documentSymbol].designer.detecting.onDetectingChange((node: InnerNode) => {
|
||||
fn(Node.create(node)!);
|
||||
});
|
||||
@ -278,7 +278,7 @@ export class DocumentModel implements IPublicModelDocumentModel {
|
||||
/**
|
||||
* 当前 document 的选中变更事件
|
||||
*/
|
||||
onChangeSelection(fn: (ids: string[]) => void): () => void {
|
||||
onChangeSelection(fn: (ids: string[]) => void): IPublicTypeDisposable {
|
||||
return this[documentSymbol].selection.onSelectionChange((ids: string[]) => {
|
||||
fn(ids);
|
||||
});
|
||||
@ -338,11 +338,39 @@ export class DocumentModel implements IPublicModelDocumentModel {
|
||||
* import schema event
|
||||
* @param fn
|
||||
*/
|
||||
onImportSchema(fn: (schema: IPublicTypeRootSchema) => void): void {
|
||||
this[editorSymbol].on(IPublicEnumEventNames.DOCUMENT_IMPORT_SCHEMA, fn as any);
|
||||
onImportSchema(fn: (schema: IPublicTypeRootSchema) => void): IPublicTypeDisposable {
|
||||
return this[editorSymbol].eventBus.on('shell.document.importSchema', fn as any);
|
||||
}
|
||||
|
||||
isDetectingNode(node: IPublicModelNode): boolean {
|
||||
return this.detecting.current === node;
|
||||
}
|
||||
|
||||
onFocusNodeChanged(
|
||||
fn: (doc: IPublicModelDocumentModel, focusNode: IPublicModelNode) => void,
|
||||
): IPublicTypeDisposable {
|
||||
if (!fn) {
|
||||
return () => {};
|
||||
}
|
||||
return this[editorSymbol].eventBus.on(
|
||||
'shell.document.focusNodeChanged',
|
||||
(payload) => {
|
||||
const { document, focusNode } = payload;
|
||||
fn(document, focusNode);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
onDropLocationChanged(fn: (doc: IPublicModelDocumentModel) => void): IPublicTypeDisposable {
|
||||
if (!fn) {
|
||||
return () => {};
|
||||
}
|
||||
return this[editorSymbol].eventBus.on(
|
||||
'document.dropLocation.changed',
|
||||
(payload) => {
|
||||
const { document } = payload;
|
||||
fn(document);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import {
|
||||
ILocateEvent as InnerLocateEvent,
|
||||
} from '@alilc/lowcode-designer';
|
||||
import { dragonSymbol } from '../symbols';
|
||||
import { dragonSymbol, nodeSymbol } from '../symbols';
|
||||
import LocateEvent from './locate-event';
|
||||
import { DragObject } from './drag-object';
|
||||
import { globalContext } from '@alilc/lowcode-editor-core';
|
||||
@ -98,7 +98,10 @@ export class Dragon implements IPublicModelDragon {
|
||||
* @param boostEvent 拖拽初始时事件
|
||||
*/
|
||||
boost(dragObject: DragObject, boostEvent: MouseEvent | DragEvent, fromRglNode?: Node | IPublicModelNode): void {
|
||||
return this[dragonSymbol].boost(dragObject, boostEvent, fromRglNode);
|
||||
return this[dragonSymbol].boost({
|
||||
...dragObject,
|
||||
nodes: dragObject.nodes.map((node: any) => node[nodeSymbol]),
|
||||
}, boostEvent, fromRglNode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -6,10 +6,4 @@
|
||||
*/
|
||||
// eslint-disable-next-line no-shadow
|
||||
export enum IPublicEnumEventNames {
|
||||
DOCUMENT_IMPORT_SCHEMA = 'shell.document.importSchema',
|
||||
DOCUMENT_FOCUS_NODE_CHANGED = 'shell.document.focusNodeChanged',
|
||||
SKELETON_PANEL_SHOW = 'skeleton.panel.show',
|
||||
SKELETON_PANEL_HIDE = 'skeleton.panel.hide',
|
||||
DESIGNER_DOCUMENT_REMOVE = 'designer.document.remove',
|
||||
DOCUMENT_DROPLOCATION_CHANGED = 'document.dropLocation.changed',
|
||||
}
|
||||
@ -1,5 +1,8 @@
|
||||
import { IPublicTypeActiveTarget } from '../type';
|
||||
import { IPublicModelNode } from './node';
|
||||
|
||||
export interface IPublicModelActiveTracker {
|
||||
onChange(fn: (target: IPublicTypeActiveTarget) => void): () => void;
|
||||
|
||||
track(node: IPublicModelNode): void;
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { IPublicTypeRootSchema, IPublicTypeDragNodeDataObject, IPublicTypeDragNodeObject, IPublicTypePropChangeOptions } from '../type';
|
||||
import { IPublicTypeRootSchema, IPublicTypeDragNodeDataObject, IPublicTypeDragNodeObject, IPublicTypePropChangeOptions, IPublicTypeDisposable } from '../type';
|
||||
import { IPublicEnumTransformStage } from '../enum';
|
||||
import { IPublicApiProject } from '../api';
|
||||
import { IPublicModelDropLocation, IPublicModelDetecting, IPublicModelNode, IPublicModelSelection, IPublicModelHistory, IPublicModelModalNodesManager } from './';
|
||||
@ -114,27 +114,27 @@ export interface IPublicModelDocumentModel {
|
||||
/**
|
||||
* 当前 document 新增节点事件
|
||||
*/
|
||||
onAddNode(fn: (node: IPublicModelNode) => void): () => void;
|
||||
onAddNode(fn: (node: IPublicModelNode) => void): IPublicTypeDisposable;
|
||||
|
||||
/**
|
||||
* 当前 document 新增节点事件,此时节点已经挂载到 document 上
|
||||
*/
|
||||
onMountNode(fn: (payload: { node: IPublicModelNode }) => void): () => void;
|
||||
onMountNode(fn: (payload: { node: IPublicModelNode }) => void): IPublicTypeDisposable;
|
||||
|
||||
/**
|
||||
* 当前 document 删除节点事件
|
||||
*/
|
||||
onRemoveNode(fn: (node: IPublicModelNode) => void): () => void;
|
||||
onRemoveNode(fn: (node: IPublicModelNode) => void): IPublicTypeDisposable;
|
||||
|
||||
/**
|
||||
* 当前 document 的 hover 变更事件
|
||||
*/
|
||||
onChangeDetecting(fn: (node: IPublicModelNode) => void): () => void;
|
||||
onChangeDetecting(fn: (node: IPublicModelNode) => void): IPublicTypeDisposable;
|
||||
|
||||
/**
|
||||
* 当前 document 的选中变更事件
|
||||
*/
|
||||
onChangeSelection(fn: (ids: string[]) => void): () => void;
|
||||
onChangeSelection(fn: (ids: string[]) => void): IPublicTypeDisposable;
|
||||
|
||||
/**
|
||||
* 当前 document 的节点显隐状态变更事件
|
||||
@ -152,16 +152,48 @@ export interface IPublicModelDocumentModel {
|
||||
/**
|
||||
* import schema event
|
||||
* @param fn
|
||||
* @since v1.0.15
|
||||
*/
|
||||
onImportSchema(fn: (schema: IPublicTypeRootSchema) => void): void;
|
||||
onImportSchema(fn: (schema: IPublicTypeRootSchema) => void): IPublicTypeDisposable;
|
||||
|
||||
/**
|
||||
* 判断是否当前节点处于被探测状态
|
||||
* check is node being detected
|
||||
* @param node
|
||||
* @since v1.1.0
|
||||
*/
|
||||
isDetectingNode(node: IPublicModelNode): boolean;
|
||||
|
||||
/**
|
||||
* TODO: 待补充说明
|
||||
* 获取当前的 DropLocation 信息
|
||||
* get current drop location
|
||||
* @since v1.1.0
|
||||
*/
|
||||
get dropLocation(): IPublicModelDropLocation;
|
||||
|
||||
|
||||
/**
|
||||
* 设置当前的 DropLocation 信息
|
||||
* set current drop location
|
||||
* @since v1.1.0
|
||||
*/
|
||||
set dropLocation(loc: IPublicModelDropLocation | null);
|
||||
|
||||
|
||||
/**
|
||||
* 设置聚焦节点变化的回调
|
||||
* triggered focused node is set mannually from plugin
|
||||
* @param fn
|
||||
* @since v1.1.0
|
||||
*/
|
||||
onFocusNodeChanged(
|
||||
fn: (doc: IPublicModelDocumentModel, focusNode: IPublicModelNode) => void,
|
||||
): IPublicTypeDisposable;
|
||||
|
||||
/**
|
||||
* 设置 DropLocation 变化的回调
|
||||
* triggered when drop location changed
|
||||
* @param fn
|
||||
* @since v1.1.0
|
||||
*/
|
||||
onDropLocationChanged(fn: (doc: IPublicModelDocumentModel) => void): IPublicTypeDisposable;
|
||||
}
|
||||
|
||||
@ -1,3 +1,10 @@
|
||||
import { IPublicTypeLocationDetail } from '../type';
|
||||
import { IPublicModelLocateEvent } from './';
|
||||
|
||||
export interface IPublicModelDropLocation {
|
||||
get target(): any;
|
||||
|
||||
readonly detail: IPublicTypeLocationDetail;
|
||||
|
||||
clone(event: IPublicModelLocateEvent): IPublicModelDropLocation;
|
||||
}
|
||||
|
||||
@ -16,7 +16,10 @@ export function isAssetBundle(obj: any): obj is AssetBundle {
|
||||
return obj && obj.type === AssetType.Bundle;
|
||||
}
|
||||
|
||||
export function assetBundle(assets?: Asset | AssetList | null, level?: AssetLevel): AssetBundle | null {
|
||||
export function assetBundle(
|
||||
assets?: Asset | AssetList | null,
|
||||
level?: AssetLevel,
|
||||
): AssetBundle | null {
|
||||
if (!assets) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { ComponentType, forwardRef, createElement, FunctionComponent } from 'react';
|
||||
import { IPublicTypeNpmInfo, IPublicTypeComponentSchema } from '@alilc/lowcode-types';
|
||||
import { Component } from '@alilc/lowcode-designer';
|
||||
import { isESModule } from './is-es-module';
|
||||
import { isReactComponent, acceptsRef, wrapReactClass } from './is-react';
|
||||
import { isObject } from './is-object';
|
||||
|
||||
type Component = ComponentType<any> | object;
|
||||
interface LibraryMap {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { ReactNode, ComponentType, isValidElement, cloneElement, createElement } from 'react';
|
||||
import { isReactComponent } from './is-react';
|
||||
|
||||
export function createContent(content: ReactNode | ComponentType<any>, props?: Record<string, unknown>): ReactNode {
|
||||
export function createContent(
|
||||
content: ReactNode | ComponentType<any>,
|
||||
props?: Record<string, unknown>,
|
||||
): ReactNode {
|
||||
if (isValidElement(content)) {
|
||||
return props ? cloneElement(content, props) : content;
|
||||
}
|
||||
|
||||
@ -6,7 +6,10 @@ import { isESModule } from './is-es-module';
|
||||
|
||||
const URL_RE = /^(https?:)\/\//i;
|
||||
|
||||
export function createIcon(icon?: IPublicTypeIconType | null, props?: Record<string, unknown>): ReactNode {
|
||||
export function createIcon(
|
||||
icon?: IPublicTypeIconType | null,
|
||||
props?: Record<string, unknown>,
|
||||
): ReactNode {
|
||||
if (!icon) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -26,7 +26,6 @@ export * from './node-helper';
|
||||
export * from './clone-enumerable-property';
|
||||
export * from './logger';
|
||||
export * from './is-shaken';
|
||||
export * from './is-public-event-name';
|
||||
export * from './is-plugin-event-name';
|
||||
export * as css from './css-helper';
|
||||
export { transactionManager } from './transaction-manager';
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
import { IPublicEnumEventNames } from '@alilc/lowcode-types';
|
||||
|
||||
export function isPublicEventName(eventName: string): boolean {
|
||||
for (const publicEvent in IPublicEnumEventNames) {
|
||||
if (eventName === (IPublicEnumEventNames as any)[publicEvent]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
|
||||
import { isI18NObject } from './is-object';
|
||||
import { get } from 'lodash';
|
||||
import { ComponentMeta } from '@alilc/lowcode-designer';
|
||||
import { TransformStage } from '@alilc/lowcode-types';
|
||||
import { IPublicEnumTransformStage, IPublicModelComponentMeta } from '@alilc/lowcode-types';
|
||||
|
||||
interface Variable {
|
||||
type: 'variable';
|
||||
variable: string;
|
||||
@ -65,7 +65,7 @@ export function arrShallowEquals(arr1: any[], arr2: any[]): boolean {
|
||||
* 判断当前 meta 是否从 vc prototype 转换而来
|
||||
* @param meta
|
||||
*/
|
||||
export function isFromVC(meta: ComponentMeta) {
|
||||
export function isFromVC(meta: IPublicModelComponentMeta) {
|
||||
return !!meta?.getMetadata().configure?.advanced;
|
||||
}
|
||||
|
||||
@ -86,12 +86,12 @@ const stageList = [
|
||||
* @param stage
|
||||
* @returns
|
||||
*/
|
||||
export function compatStage(stage: TransformStage | number): TransformStage {
|
||||
export function compatStage(stage: IPublicEnumTransformStage | number): IPublicEnumTransformStage {
|
||||
if (typeof stage === 'number') {
|
||||
console.warn('stage 直接指定为数字的使用方式已经过时,将在下一版本移除,请直接使用 TransformStage.Render|Serilize|Save|Clone|Init|Upgrade');
|
||||
return stageList[stage - 1] as TransformStage;
|
||||
console.warn('stage 直接指定为数字的使用方式已经过时,将在下一版本移除,请直接使用 IPublicEnumTransformStage.Render|Serilize|Save|Clone|Init|Upgrade');
|
||||
return stageList[stage - 1] as IPublicEnumTransformStage;
|
||||
}
|
||||
return stage as TransformStage;
|
||||
return stage as IPublicEnumTransformStage;
|
||||
}
|
||||
|
||||
export function invariant(check: any, message: string, thing?: any) {
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
// 仅使用类型
|
||||
import { Node } from '@alilc/lowcode-designer';
|
||||
import { IPublicModelNode } from '@alilc/lowcode-types';
|
||||
|
||||
export const getClosestNode = (node: Node, until: (node: Node) => boolean): Node | undefined => {
|
||||
export const getClosestNode = (
|
||||
node: IPublicModelNode,
|
||||
until: (n: IPublicModelNode) => boolean,
|
||||
): IPublicModelNode | undefined => {
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
@ -19,7 +22,7 @@ export const getClosestNode = (node: Node, until: (node: Node) => boolean): Node
|
||||
* @param {unknown} e 点击事件
|
||||
* @returns {boolean} 是否可点击,true表示可点击
|
||||
*/
|
||||
export const canClickNode = (node: Node, e: unknown): boolean => {
|
||||
export const canClickNode = (node: IPublicModelNode, e: unknown): boolean => {
|
||||
const onClickHook = node.componentMeta?.getMetadata().configure?.advanced?.callbacks?.onClickHook;
|
||||
const canClick = typeof onClickHook === 'function' ? onClickHook(e as MouseEvent, node) : true;
|
||||
return canClick;
|
||||
|
||||
@ -28,11 +28,13 @@ import {
|
||||
Common,
|
||||
Logger,
|
||||
Workspace,
|
||||
Canvas,
|
||||
} from '@alilc/lowcode-shell';
|
||||
import {
|
||||
IPublicTypePluginMeta,
|
||||
} from '@alilc/lowcode-types';
|
||||
import { getLogger } from '@alilc/lowcode-utils';
|
||||
import { OutlinePlugin } from '@alilc/lowcode-plugin-outline-pane';
|
||||
import { setterRegistry } from '../../engine/src/inner-plugins/setter-registry';
|
||||
import { componentMetaParser } from '../../engine/src/inner-plugins/component-meta-parser';
|
||||
import defaultPanelRegistry from '../../engine/src/inner-plugins/default-panel-registry';
|
||||
@ -58,6 +60,7 @@ export class BasicContext {
|
||||
innerSkeleton: any;
|
||||
innerHotkey: InnerHotkey;
|
||||
innerPlugins: LowCodePluginManager;
|
||||
canvas: Canvas;
|
||||
|
||||
constructor(innerWorkspace: any, viewName: string, public editorWindow?: EditorWindow) {
|
||||
const editor = new Editor(viewName, true);
|
||||
@ -84,6 +87,7 @@ export class BasicContext {
|
||||
const event = new Event(commonEvent, { prefix: 'common' });
|
||||
const logger = getLogger({ level: 'warn', bizName: 'common' });
|
||||
const skeleton = new Skeleton(innerSkeleton, 'any', true);
|
||||
const canvas = new Canvas(editor);
|
||||
editor.set('setters', setters);
|
||||
editor.set('project', project);
|
||||
editor.set('material', material);
|
||||
@ -103,6 +107,7 @@ export class BasicContext {
|
||||
this.innerHotkey = innerHotkey;
|
||||
this.editor = editor;
|
||||
this.designer = designer;
|
||||
this.canvas = canvas;
|
||||
const common = new Common(editor, innerSkeleton);
|
||||
let plugins: any;
|
||||
|
||||
@ -120,6 +125,7 @@ export class BasicContext {
|
||||
context.common = common;
|
||||
context.plugins = plugins;
|
||||
context.logger = new Logger({ level: 'warn', bizName: `plugin:${pluginName}` });
|
||||
context.canvas = canvas;
|
||||
},
|
||||
};
|
||||
|
||||
@ -132,6 +138,7 @@ export class BasicContext {
|
||||
|
||||
// 注册一批内置插件
|
||||
this.registerInnerPlugins = async function registerPlugins() {
|
||||
await plugins.register(OutlinePlugin, {}, { autoInit: true });
|
||||
await plugins.register(componentMetaParser(designer));
|
||||
await plugins.register(setterRegistry, {}, { autoInit: true });
|
||||
await plugins.register(defaultPanelRegistry(editor, designer));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user