diff --git a/examples/feature-examples/src/pages/extensions/highlight/index.tsx b/examples/feature-examples/src/pages/extensions/highlight/index.tsx index 0df0ee5ca..7d316aed4 100644 --- a/examples/feature-examples/src/pages/extensions/highlight/index.tsx +++ b/examples/feature-examples/src/pages/extensions/highlight/index.tsx @@ -1,6 +1,6 @@ -import { Card, Divider, Tag } from 'antd' +import { Card, Divider, Radio, Tag } from 'antd' import LogicFlow from '@logicflow/core' -import { useEffect, useRef } from 'react' +import { useEffect, useRef, useState } from 'react' import { Highlight } from '@logicflow/extension' import './index.less' @@ -20,25 +20,41 @@ const config: Partial = { }, plugins: [Highlight], snapline: true, // 是否开启辅助对齐线 - pluginsOptions: { - highlight: { - mode: 'neighbour', - }, - }, } export default function HighLightExtension() { const containerRef = useRef(null) + const [highlightMode, setHighlightMode] = useState('path') + const lfRef = useRef(null) useEffect(() => { const lf: LogicFlow = new LogicFlow({ container: containerRef.current!, ...config, + pluginsOptions: { + highlight: { + mode: highlightMode, + }, + }, + }) + lfRef.current = lf + lfRef.current.render(data) + lfRef.current.translateCenter() + lfRef.current.on('highlight:single', ({ model }) => { + console.log('highlight:single', model) + }) + lfRef.current.on('highlight:neighbours', ({ data, relateElements }) => { + console.log('highlight:neighbours', data, relateElements) + }) + lfRef.current.on('highlight:path', ({ data, relateElements }) => { + console.log('highlight:path', data, relateElements) }) - - lf.render(data) - lf.translateCenter() }, []) + useEffect(() => { + ;(lfRef.current?.extension.highlight as Highlight)?.setMode( + highlightMode as any, + ) + }, [highlightMode]) return (

@@ -50,6 +66,17 @@ export default function HighLightExtension() {

节点:高亮这个节点前后相关的所有边和节点

边:高亮这个边指向的节点前后相关的所有边和节点

+ { + setHighlightMode(e.target.value) + }} + style={{ marginBottom: 16 }} + > + 全路径高亮 + 单元素高亮 + 相邻元素高亮 +
diff --git a/packages/extension/src/components/highlight/index.ts b/packages/extension/src/components/highlight/index.ts index 0d4593a1d..beb972aae 100644 --- a/packages/extension/src/components/highlight/index.ts +++ b/packages/extension/src/components/highlight/index.ts @@ -1,5 +1,5 @@ -import LogicFlow, { BaseNodeModel } from '@logicflow/core' -import { concat } from 'lodash-es' +import LogicFlow, { BaseEdgeModel, BaseNodeModel } from '@logicflow/core' +import { uniqBy, concat } from 'lodash-es' // 后续并入FlowPath const getPath = (id: string, lf: LogicFlow) => { @@ -99,7 +99,7 @@ export class Highlight { this.enable = enable } - setMode(mode: IMode) { + public setMode(mode: IMode) { this.mode = mode } @@ -119,11 +119,14 @@ export class Highlight { model.sourceNode.updateStyles(this.tempStyles[model.sourceNode.id]) model.targetNode.updateStyles(this.tempStyles[model.targetNode.id]) } + this.lf.emit('highlight:single', { + data: model, + }) } private highlightNeighbours(id: string) { const model = this.lf.getModelById(id) - + let relateElements: (BaseNodeModel | BaseEdgeModel)[] = [] if (model?.BaseType === 'node') { // 高亮节点 model.updateStyles(this.tempStyles[id]) @@ -135,19 +138,41 @@ export class Highlight { concat(incomingEdges, outgoingEdges).forEach((edge) => { edge.updateStyles(this.tempStyles[edge.id]) }) + relateElements = uniqBy( + concat( + relateElements, + incomingNodes, + outgoingNodes, + incomingEdges, + outgoingEdges, + ), + 'id', + ) } else if (model?.BaseType === 'edge') { // 高亮边及对应的节点 model.updateStyles(this.tempStyles[id]) model.sourceNode.updateStyles(this.tempStyles[model.sourceNode.id]) model.targetNode.updateStyles(this.tempStyles[model.targetNode.id]) + relateElements = [model.sourceNode, model.targetNode] } + this.lf.emit('highlight:neighbours', { + data: model, + relateElements, + }) } private highlightPath(id: string) { const path = getPath(id, this.lf) + const relateElements: any[] = [] path.forEach((_id) => { + const elementModel = this.lf.getModelById(_id) // 高亮路径上所有的边和节点 - this.lf.getModelById(_id)?.updateStyles(this.tempStyles[_id]) + elementModel?.updateStyles(this.tempStyles[_id]) + relateElements.push(elementModel) + }) + this.lf.emit('highlight:path', { + data: this.lf.getModelById(id), + relateElements, }) } diff --git a/packages/extension/src/dynamic-group/index.ts b/packages/extension/src/dynamic-group/index.ts index 2af4f4fc3..ed3192a68 100644 --- a/packages/extension/src/dynamic-group/index.ts +++ b/packages/extension/src/dynamic-group/index.ts @@ -513,9 +513,6 @@ export class DynamicGroup { } }) - // TODO: 确认,递归的方式,是否将所有嵌套的边数据都有返回 - console.log('allRelatedEdges -->>', allRelatedEdges) - // 1. 判断每一条边的开始节点、目标节点是否在 Group 中 const edgesInnerGroup = filter(allRelatedEdges, (edge) => { return ( diff --git a/packages/extension/src/tools/proximity-connect/index.ts b/packages/extension/src/tools/proximity-connect/index.ts index b4f5887cb..1e5a6691b 100644 --- a/packages/extension/src/tools/proximity-connect/index.ts +++ b/packages/extension/src/tools/proximity-connect/index.ts @@ -3,6 +3,7 @@ import LogicFlow, { twoPointDistance, BaseNodeModel, BaseEdgeModel, + isInNode, } from '@logicflow/core' import { assign, isEmpty, isEqual, isNil, isFinite, reduce } from 'lodash-es' @@ -78,10 +79,37 @@ export class ProximityConnect { }, ) // 节点、锚点拖拽结束事件 - this.lf.graphModel.eventCenter.on('node:drop,anchor:dragend', () => { + this.lf.graphModel.eventCenter.on('node:drop', () => { if (!this.enable) return this.handleDrop() }) + // 锚点拖拽需要单独判断一下当前拖拽终点是否在某个锚点上,如果是,就不触发插件的连线,以免出现创建了两条连线的问题,表现见 issue 2140 + this.lf.graphModel.eventCenter.on('anchor:dragend', ({ e, edgeModel }) => { + if (!this.enable) return + const { + canvasOverlayPosition: { x: eventX, y: eventY }, + } = this.lf.graphModel.getPointByClient({ + x: e.clientX, + y: e.clientY, + }) + + if (edgeModel && this.virtualEdge) { + const { id: virtualEdgeId } = this.virtualEdge as BaseEdgeModel + const { targetNodeId } = edgeModel as BaseEdgeModel + const targetNodeModel = + this.lf.graphModel.getNodeModelById(targetNodeId) + if ( + targetNodeModel && + isInNode({ x: eventX, y: eventY }, targetNodeModel) + ) { + // 如果当前拖拽点在锚点上,就不触发插件的连线 + this.lf.deleteEdge(virtualEdgeId) + return + } + } + + this.handleDrop() + }) } // 节点拖拽动作 @@ -346,6 +374,7 @@ export class ProximityConnect { endPoint, pointsList, } = this.virtualEdge + this.lf.deleteEdge(this.virtualEdge.id) this.lf.addEdge({ type, sourceNodeId, @@ -356,7 +385,6 @@ export class ProximityConnect { endPoint, pointsList, }) - this.lf.deleteEdge(this.virtualEdge.id) } // 设置虚拟边样式 @@ -369,7 +397,6 @@ export class ProximityConnect { // 设置连线阈值 public setThresholdDistance(distance: number) { - console.log('distance', distance) if (!isFinite(distance)) return this.thresholdDistance = distance } diff --git a/packages/react-node-registry/src/view.ts b/packages/react-node-registry/src/view.ts index f5eebf818..83627f897 100644 --- a/packages/react-node-registry/src/view.ts +++ b/packages/react-node-registry/src/view.ts @@ -25,13 +25,12 @@ export class ReactNodeView extends HtmlNode { rootEl.appendChild(el) } - confirmUpdate(_rootEl: SVGForeignObjectElement) { - // TODO: 如有需要,可以先通过继承的方式,自定义该节点的更新逻辑;我们后续会根据实际需求,丰富该功能 - console.log('_rootEl', _rootEl) - } + // confirmUpdate(_rootEl: SVGForeignObjectElement) { + // // TODO: 如有需要,可以先通过继承的方式,自定义该节点的更新逻辑;我们后续会根据实际需求,丰富该功能 + // // console.log('_rootEl', _rootEl) + // } protected renderReactComponent(container: HTMLElement) { - console.log('render render render ===>>>') this.unmountReactComponent() const { model, graphModel } = this.props diff --git a/sites/docs/docs/api/eventCenter.en.md b/sites/docs/docs/api/eventCenter.en.md index 9d2aa2ba6..32fd0b16c 100644 --- a/sites/docs/docs/api/eventCenter.en.md +++ b/sites/docs/docs/api/eventCenter.en.md @@ -50,8 +50,9 @@ The event object contains the following: | Event names | Description | Event object | | :--------------------------------------------- | :--------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| element:click | Click on the element | data, e, position | | edge:click | Click on the edge | data, e, position | -| edge:dbclick | Double-click on the edge | data, e, position | +| edge:dbclick | Double-click on the edge | data, e | | edge:mouseenter | Move the mouse pointer into the edge | data, e | | edge:mouseleave | Move the mouse pointer out of the edge | data, e | | edge:add | Add edge | data | @@ -62,24 +63,27 @@ The event object contains the following: | edge:connect2.0 Added | Edge connection completed | data | | edge:beforeConnect2.0 Added | Before edge connection | data, msg | | edge:properties-change2.0 Added | Edge custom properties change | id: Current edge id
keys: Set of keys for current changes
preProperties: Properties before change
properties: Properties after change | +| connection:not-allowed | Connection is not allowed | data, msg | The event object contains the following: -| Property | 类型 | 值 | -| :------- | :--------- | :------------------------------------------------------------------------------------------------------------------------------------------- | -| data | Object | The [data attribute](./model/edgeModel.en.md#data-attributes) | -| e | MouseEvent | Native mouse event object | -| position | Object | Coordinates of the mouse trigger point in the canvas(Refer to the return value of [getPointByClient](./detail/index.en.md#getpointbyclient)) | -| msg | string | Verification information of the edge | +| Property | Type | Description | +| :------- | :--------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | +| data | Object | The [data attribute](./model/edgeModel.en.md#data-attributes) | +| e | MouseEvent | Native mouse event object | +| position | Object | Coordinates of the mouse trigger point in the canvas (Refer to the return value of [getPointByClient](./detail/index.en.md#getpointbyclient)) | +| msg | string | Verification information of the edge | ## Anchor Events -| Event names | Description | Event object | -| :---------------------------------------- | :--------------------------------------- | :----------- | -| anchor:mouseenter2.0 Added | Move the mouse pointer into the anchor | data, e | -| anchor:mouseleave2.0 Added | Move the mouse pointer out of the anchor | data, e | -| anchor:dragstart2.0 Added | Start dragging from anchor | data, e | -| anchor:drop2.0 Added | Drop on anchor | data, e | +| Event names | Description | Event object | +| :---------------------------------------- | :------------------------------------------------------------------------------------------------------------------- | :---------------------------- | +| anchor:mouseenter2.0 Added | Move the mouse pointer into the anchor | data, e | +| anchor:mouseleave2.0 Added | Move the mouse pointer out of the anchor | data, e | +| anchor:dragstart2.0 Added | Start dragging from anchor | data, e, nodeModel | +| anchor:drop2.0 Added | Drop on anchor, only triggered when connecting successfully. Used to distinguish manual and auto-created connections | data, e, nodeModel, edgeModel | +| anchor:drag2.0 Added | Anchor connection dragging | data, e, nodeModel | +| anchor:dragend2.0 Added | End of anchor connection, triggered regardless of whether a connection is created | data, e, nodeModel | The event object contains the following: @@ -89,26 +93,52 @@ The event object contains the following: | e | MouseEvent | Native mouse event object | | nodeModel | Object | The node to which the anchor point belongs | - - ## Canvas Events -| Event names | Description | Event object | -| :---------------------------------------- | :------------------------------- | :--------------------------------------------- | -| blank:mousedown | Mouse down on blank area | e | -| blank:mousemove | Mouse move on blank area | e | -| blank:mouseup | Mouse up on blank area | e | -| blank:click | Click on blank area | e | -| blank:contextmenu | Right-click on blank area | e | -| blank:dragstart | Start dragging on blank area | e | -| blank:drag | Dragging on blank area | e | -| blank:drop | Drop on blank area | e | -| graph:transform | Graph transformation (zoom, pan) | transform: { transform: string; zoom: number } | -| graph:rendered | Graph rendering completed | graphData | -| history:change | History state change | data: { undoAble: boolean; redoAble: boolean } | -| graph:rendered2.0 Added | Graph rendering completed | graphData | -| graph:transform2.0 Added | Graph transformation (zoom, pan) | transform: { transform: string; zoom: number } | -| graph:data-change2.0 Added | Graph data change | graphData | +| Event names | Description | Event object | +| :--------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------- | +| blank:mousedown | Mouse down on blank area | e | +| blank:mousemove | Mouse move on blank area | e | +| blank:mouseup | Mouse up on blank area | e | +| blank:click | Click on blank area | e | +| blank:contextmenu | Right-click on blank area | e, position | +| blank:dragstart | Start dragging on blank area | e | +| blank:drag | Dragging on blank area | e | +| blank:drop | Drop on blank area | e | +| text:update | Update text | data | +| graph:transform | Graph transformation (zoom, pan) | transform: { transform: string; zoom: number } | +| graph:rendered1.1.0 Added | Graph rendering completed | graphData | +| graph:updated2.0.0 Added | Canvas updated. Triggered after lf.render(graphData) or after changing properties on the canvas. If you need to perform operations after the canvas updates, it's recommended to use once event instead of on event | - | +| history:change | History state change | data: { undos: Array, redos: Array, undoAble: boolean; redoAble: boolean } | + +The event object contains the following: + +| Property | Type | Description | +| :------- | :--------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | +| e | MouseEvent | Native mouse event object | +| position | Object | Coordinates of the mouse trigger point in the canvas (Refer to the return value of [getPointByClient](./detail/index.en.md#getpointbyclient)) | + +## Selection Events + +When multiple nodes are selected to form a selection area, the following events are triggered: + +| Event names | Description | Event object | +| :-------------------- | :----------------------------- | :---------------- | +| selection:selected | Triggered after area selection | Selected elements | +| selection:mousedown | Mouse down on selection | e | +| selection:dragstart | Start dragging selection | e | +| selection:drag | Dragging selection | e | +| selection:drop | Drop selection | e | +| selection:mousemove | Mouse move in selection | e, position | +| selection:mouseup | Mouse up in selection | e | +| selection:contextmenu | Right-click on selection | e | + +The event object contains the following: + +| Property | Type | Description | +| :------- | :--------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | +| e | MouseEvent | Native mouse event object | +| position | Object | Coordinates of the mouse trigger point in the canvas (Refer to the return value of [getPointByClient](./detail/index.en.md#getpointbyclient)) | ## Text events @@ -145,6 +175,62 @@ The event object contains the following: | e | MouseEvent | Native mouse event object | | data | Object | NodeModel/EdgeModel | +## Plugin Events + +The following are events triggered by different plugins: + +### DndPanel + +| Event name | Description | Event object | +| :-------------------- | :------------------------- | :----------- | +| dnd:panel-dbclick | Double-click on drag panel | e, data | +| dnd:panel-click | Left-click on drag panel | e, data | +| dnd:panel-contextmenu | Right-click on drag panel | e, data | + +The event object contains the following: + +| Property | Type | Value | +| :------- | :--------- | :------------------------ | +| e | MouseEvent | Native mouse event object | +| data | Object | NodeModel/EdgeModel | + +### MiniMap + +| Event name | Description | Event object | +| :------------ | :------------------------------- | :----------- | +| miniMap:close | Triggered when minimap is hidden | - | + +### SelectionSelect + +| Event name | Description | Event object | +| :---------------------- | :----------------------------------------------------------- | :------------------------------------------------------------------------ | +| selection:selected-area | Selection area | topLeft: Top-left coordinate, bottomRight: Bottom-right coordinate | +| selection:drop | Triggered when elements are selected after mouse is released | e | +| selection:selected | Triggered after selection is complete | elements: Selected elements, topLeft: Top-left, bottomRight: Bottom-right | + +### DynamicGroup/Group + +| Event name | Description | Event object | +| :---------------- | :----------------------------------------------- | :----------------------------------------------------- | +| group:add-node | Triggered when a node is added to a group | data: Group data, childId: ID of the newly added node | +| group:remove-node | Triggered when a node is removed from a group | data: Group data | +| group:not-allowed | Triggered when a node cannot be added to a group | group: Group data, node: Information of forbidden node | + +### Highlight + +| Event name | Description | Event object | +| :------------------- | :--------------------------------------------- | :------------------- | +| highlight:single | Element highlight in single element mode | data | +| highlight:neighbours | Element highlight in neighboring elements mode | data, relateElements | +| highlight:path | Element highlight in path elements mode | data, relateElements | + +The event object contains the following: + +| Property | Type | Value | +| :------------- | :----- | :--------------------------- | +| data | Object | NodeModel/EdgeModel | +| relateElements | Array | Array of NodeModel/EdgeModel | + ## on Register events. @@ -167,7 +253,6 @@ eventCenter.on("node:click", (args) => { eventCenter.on("element:click", (args) => { console.log("element:click", args.e.target); }); - ``` ## off @@ -181,10 +266,9 @@ Parameters: | evt | string | - | - | Event Name | | callback | string | - | - | Callback function | -When evt is empty, remove all evt listeners. -When evt is present and callback is empty, remove all registered methods for the corresponding -event. -When both evt and callback are present, compare the callback objects and remove matching methods. +- When evt is empty, remove all evt listeners. +- When evt is present and callback is empty, remove all registered methods for the corresponding event. +- When both evt and callback are present, compare the callback objects and remove matching methods. Example: @@ -219,7 +303,6 @@ const { eventCenter } = lf.graphModel; eventCenter.once("node:click", () => { console.log("node:click"); }); - ``` ## emit diff --git a/sites/docs/docs/api/eventCenter.zh.md b/sites/docs/docs/api/eventCenter.zh.md index f698ef5cf..f31caf7ff 100644 --- a/sites/docs/docs/api/eventCenter.zh.md +++ b/sites/docs/docs/api/eventCenter.zh.md @@ -170,8 +170,71 @@ History 用来记录画布上的每一次改动,当画布上的元素发生变 | text:mouseup | 鼠标在文本区内放开 | e, data | | text:update | 更新文本 | data | -当Label文本位置、内容出现变更时,文本触发的事件 +事件对象包含如下内容: + +| 属性 | 类型 | 值 | +| :--- | :--------- | :------------------ | +| e | MouseEvent | 原生的鼠标事件对象 | +| data | Object | NodeModel/EdgeModel | + +## 插件事件 + +下面是不同插件中触发的事件 + +### DndPanel + +| 事件名 | 说明 | 事件对象 | +| :-------------------- | :--------------- | :------- | +| dnd:panel-dbclick | 拖拽面板双击 | e, data | +| dnd:panel-click | 拖拽面板左键单击 | e, data | +| dnd:panel-contextmenu | 拖拽面板右键单击 | e, data | + + +事件对象包含如下内容: + +| 属性 | 类型 | 值 | +| :--- | :--------- | :------------------ | +| e | MouseEvent | 原生的鼠标事件对象 | +| data | Object | NodeModel/EdgeModel | + +### MiniMap + +| 事件名 | 说明 | 事件对象 | +| :------------ | :--------------- | :------- | +| miniMap:close | 小地图隐藏时触发 | - | + +### SelectionSelect + +| 事件名 | 说明 | 事件对象 | +| :---------------------- | :------------------------------------- | :------------------------------------------------------------------- | +| selection:selected-area | 选框范围 | topLeft: 左上角坐标, bottomRight: 右下角坐标 | +| selection:drop | 鼠标放开后如果存在框选选中的元素时触发 | e | +| selection:selected | 框选完成时触发 | elements: 框选元素集合, topLeft: 左上角坐标, bottomRight: 右下角坐标 | + +### DynamicGroup/Group + +| 事件名 | 说明 | 事件对象 | +| :---------------- | :------------------------------- | :-------------------------------------------- | +| group:add-node | 节点加入到分组中触发 | data: 分组数据, childId: 新假如节点的id | +| group:remove-node | 节点从分组中移除触发 | data: 分组数据 | +| group:not-allowed | 命中节点不允许加入到分组中时触发 | group: 分组数据, node: 被禁止加入的节点的信息 | + +### Highlight + +| 事件名 | 说明 | 事件对象 | +| :------------------- | :------------------------------- | :------------------- | +| highlight:single | 单元素高亮模式下,元素触发高亮 | data | +| highlight:neighbours | 相邻元素高亮模式下,元素触发高亮 | data, relateElements | +| highlight:path | 路径元素高亮模式下,元素触发高亮 | data, relateElements | + +事件对象包含如下内容: + +| 属性 | 类型 | 值 | +| :------------- | :----- | :---------------------------- | +| data | Object | NodeModel/EdgeModel | +| relateElements | Array | NodeModel/EdgeModel组成的数组 | +### Label | 事件名 | 说明 | 事件对象 | | :-------------- | :------------------- | :---------------------- | | label:mousedown | 鼠标按下文本 | e, data |