diff --git a/examples/engine-browser-examples/src/pages/extension/label/index.tsx b/examples/engine-browser-examples/src/pages/extension/label/index.tsx index 41d7083ed..1891f8479 100644 --- a/examples/engine-browser-examples/src/pages/extension/label/index.tsx +++ b/examples/engine-browser-examples/src/pages/extension/label/index.tsx @@ -1,7 +1,8 @@ import LogicFlow, { TextMode } from '@logicflow/core' import { Label, DndPanel } from '@logicflow/extension' import '@logicflow/core/es/index.css' -// import '@logicflow/extension/es/index.css' +import '@logicflow/extension/es/index.css' +import noteNode from './noteNode' import { Button, Card, Divider, Flex } from 'antd' import { useEffect, useRef } from 'react' @@ -134,10 +135,18 @@ const data = { }, { id: '7', - type: 'html', + type: 'note', x: 150, y: 400, - text: 'html节点', + text: '便签节点', + properties: { + width: 150, + height: 140, + _labelOption: { + isMultiple: false, + maxCount: 1, + }, + }, }, ], edges: [ @@ -176,6 +185,7 @@ export default function BasicNode() { size: 5, }, }) + lf.register(noteNode) lf.extension.dndPanel.setPatternItems([ { type: 'circle', @@ -200,6 +210,20 @@ export default function BasicNode() { label: '结束节点', icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAA1BJREFUOBFtVE1IVUEYPXOf+tq40Y3vPcmFIdSjIorWoRG0ERWUgnb5FwVhYQSl72oUoZAboxKNFtWiwKRN0M+jpfSzqJAQclHo001tKkjl3emc8V69igP3znzfnO/M9zcDcKT67azmjYWTwl9Vn7Vumeqzj1DVb6cleQY4oAVnIOPb+mKAGxQmKI5CWNJ2aLPatxWa3aB9K7/fB+/Z0jUF6TmMlFLQqrkECWQzOZxYGjTlOl8eeKaIY5yHnFn486xBustDjWT6dG7pmjHOJd+33t0iitTPkK6tEvjxq4h2MozQ6WFSX/LkDUGfFwfhEZj1Auz/U4pyAi5Sznd7uKzznXeVHlI/Aywmk6j7fsUsEuCGADrWARXXwjxWQsUbIupDHJI7kF5dRktg0eN81IbiZXiTESic50iwS+t1oJgL83jAiBupLDCQqwziaWSoAFSeIR3P5Xv5az00wyIn35QRYTwdSYbz8pH8fxUUAtxnFvYmEmgI0wYXUXcCCSpeEVpXlsRhBnCEATxWylL9+EKCAYhe1NGstUa6356kS9NVvt3DU2fd+Wtbm/+lSbylJqsqkSm9CRhvoJVlvKPvF1RKY/FcPn5j4UfIMLn8D4UYb54BNsilTDXKnF4CfTobA0FpoW/LSp306wkXM+XaOJhZaFkcNM82ASNAWMrhrUbRfmyeI1FvRBTpN06WKxa9BK0o2E4Pd3zfBBEwPsv9sQBnmLVbLEIZ/Xe9LYwJu/Er17W6HYVBc7vmuk0xUQ+pqxdom5Fnp55SiytXLPYoMXNM4u4SNSCFWnrVIzKG3EGyMXo6n/BQOe+bX3FClY4PwydVhthOZ9NnS+ntiLh0fxtlUJHAuGaFoVmttpVMeum0p3WEXbcll94l1wM/gZ0Ccczop77VvN2I7TlsZCsuXf1WHvWEhjO8DPtyOVg2/mvK9QqboEth+7pD6NUQC1HN/TwvydGBARi9MZSzLE4b8Ru3XhX2PBxf8E1er2A6516o0w4sIA+lwURhAON82Kwe2iDAC1Watq4XHaGQ7skLcFOtI5lDxuM2gZe6WFIotPAhbaeYlU4to5cuarF1QrcZ/lwrLaCJl66JBocYZnrNlvm2+MBCTmUymPrYZVbjdlr/BxlMjmNmNI3SAAAAAElFTkSuQmCC', }, + { + type: 'note', + text: '便签', + label: '便签节点', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAnBJREFUOBGdVL1rU1EcPfdGBddmaZLiEhdx1MHZQXApraCzQ7GKLgoRBxMfcRELuihWKcXFRcEWF8HBf0DdDCKYRZpnl7p0svLe9Zzbd29eQhTbC8nv+9zf130AT63jvooOGS8Vf9Nt5zxba7sXQwODfkWpkbjTQfCGUd9gIp3uuPP8bZ946g56dYQvnBg+b1HB8VIQmMFrazKcKSvFW2dQTxJnJdQ77urmXWOMBCmXM2Rke4S7UAW+/8ywwFoewmBps2tu7mbTdp8VMOkIRAkKfrVawalJTtIliclFbaOBqa0M2xImHeVIfd/nKAfVq/LGnPss5Kh00VEdSzfwnBXPUpmykNss4lUI9C1ga+8PNrBD5YeqRY2Zz8PhjooIbfJXjowvQJBqkmEkVnktWhwu2SM7SMx7Cj0N9IC0oQXRo8xwAGzQms+xrB/nNSUWVveI48ayrFGyC2+E2C+aWrZHXvOuz+CiV6iycWe1Rd1Q6+QUG07nb5SbPrL4426d+9E1axKjY3AoRrlEeSQo2Eu0T6BWAAr6COhTcWjRaYfKG5csnvytvUr/WY4rrPMB53Uo7jZRjXaG6/CFfNMaXEu75nG47X+oepU7PKJvvzGDY1YLSKHJrK7vFUwXKkaxwhCW3u+sDFMVrIju54RYYbFKpALZAo7sB6wcKyyrd+aBMryMT2gPyD6GsQoRFkGHr14TthZni9ck0z+Pnmee460mHXbRAypKNy3nuMdrWgVKj8YVV8E7PSzp1BZ9SJnJAsXdryw/h5ctboUVi4AFiCd+lQaYMw5z3LGTBKjLQOeUF35k89f58Vv/tGh+l+PE/wG0rgfIUbZK5AAAAABJRU5ErkJggg==', + properties: { + width: 180, + height: 180, + _labelOption: { + isMultiple: false, + maxCount: 1, + }, + }, + }, ]) lf.render(data) lfRef.current = lf diff --git a/examples/engine-browser-examples/src/pages/extension/label/noteNode.ts b/examples/engine-browser-examples/src/pages/extension/label/noteNode.ts new file mode 100644 index 000000000..3ca937d1d --- /dev/null +++ b/examples/engine-browser-examples/src/pages/extension/label/noteNode.ts @@ -0,0 +1,122 @@ +import LogicFlow, { RectNode, RectNodeModel, h } from '@logicflow/core' + +export class NoteView extends RectNode { + getShape() { + const { model } = this.props + const { x, y, width, height, cornerSize } = model + const style = model.getNodeStyle() + const strokeWidth = style.strokeWidth || 1 + const startPosition = strokeWidth / 2 + const noteMainPath = ` + ${startPosition},${startPosition} + ${startPosition + width},${startPosition} + ${startPosition + width},${startPosition + height - cornerSize} + ${startPosition + width - cornerSize},${startPosition + height} + ${startPosition},${startPosition + height}` + const noteCornerPath = ` + ${width - cornerSize},${startPosition + height - cornerSize} + ${width},${height - cornerSize} + ${width - cornerSize},${height}` + return h( + 'svg', + { + ...style, + x: x - width / 2, + y: y - height / 2, + width: width + strokeWidth, + height: height + strokeWidth, + viewBox: `-1 -1 ${width + strokeWidth + 2} ${height + strokeWidth + 2}`, + }, + [ + h('polygon', { + points: noteMainPath, + fill: style.fill, + stroke: style.stroke, + strokeWidth, + }), + h('polygon', { + points: noteCornerPath, + fill: style.fill, + stroke: style.stroke, + strokeWidth, + }), + ], + ) + } +} +export class NoteModel extends RectNodeModel { + initNodeData(data: LogicFlow.NodeConfig) { + super.initNodeData(data) + const { properties } = data + // const { x, y } = this; + if (!properties) return + const { width, height } = properties + this.width = width || this.width + this.height = height || this.height + this.defaultHeight = height + this.cornerSize = Math.min(this.width, this.height) * 0.1 + // 监听label:input事件输入时实时更新label的高度 + this.graphModel.eventCenter.on('label:input', ({ data: labelData }) => { + const domId = `editor-container-${labelData.id}` + const domElement = document.getElementById(domId) // 当前输入的label容器元素 + if (!domElement) return + const contentDom = domElement.children[0] as HTMLElement // 当前输入的label容器元素的内容(因为便签只有一个文本,所以内容元素即第一个子元素) + if (!contentDom) return + const lineHeight = window.getComputedStyle(contentDom).lineHeight + if (contentDom.offsetHeight > this.defaultHeight) { + this.setProperty( + 'height', + contentDom.offsetHeight + parseFloat(lineHeight), + ) + } else { + this.setProperty( + 'height', + contentDom.offsetHeight < this.defaultHeight + ? this.defaultHeight + : contentDom.offsetHeight + parseFloat(lineHeight), + ) + } + }) + } + + getNodeStyle() { + const { cornerSize } = this + const style = super.getNodeStyle() + const { style: { fill, stroke, strokeWidth } = {} } = this.properties + style.fill = fill || '#fff' + style.stroke = stroke || '#2961EF' + style.strokeWidth = strokeWidth || cornerSize / 20 + return style + } + + updateLabelInfo() { + const { width, height, x, y } = this + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const { labelWidth } = this.properties._labelOption as any + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this.properties._label?.forEach((label: any) => { + label.labelWidth = labelWidth || width - width * 0.2 + label.x = x + label.y = y + label.style = { + minHeight: `${height - height * 0.2}px`, + minWidth: `${labelWidth || width - width * 0.15}px`, + textAlign: 'left', + } + console.log('label', label) + }) + } + + setAttributes() { + super.setAttributes() + this.updateLabelInfo() + } +} + +export const note = { + type: 'note', + view: NoteView, + model: NoteModel, +} + +export default note diff --git a/packages/extension/src/tools/label/Label.tsx b/packages/extension/src/tools/label/Label.tsx index 0117f8889..610883ec5 100644 --- a/packages/extension/src/tools/label/Label.tsx +++ b/packages/extension/src/tools/label/Label.tsx @@ -182,6 +182,14 @@ export class Label extends Component { }) } + handleInput = (e: InputEvent) => { + const { label, graphModel } = this.props + graphModel.eventCenter.emit('label:input', { + e, + data: label.getData(), + }) + } + setElementModelLabelInfo(data) { const { label, element, graphModel } = this.props const { @@ -330,6 +338,7 @@ export class Label extends Component { 'lf-label-editor-hover': !isEditing && (isHovered || isSelected), [`lf-label-editor-${textOverflowMode}`]: !isEditing, })} + onInput={this.handleInput} style={{ maxWidth: `${maxLabelWidth}px`, boxSizing: 'border-box', diff --git a/packages/extension/src/tools/label/index.ts b/packages/extension/src/tools/label/index.ts index 43631284a..94230c9c9 100644 --- a/packages/extension/src/tools/label/index.ts +++ b/packages/extension/src/tools/label/index.ts @@ -221,8 +221,12 @@ export class Label implements Extension { editable: true, vertical: false, } - - if (!isMultiple || len >= (curLabelOption?.maxCount ?? maxCount)) { + // 全局的isMultiple为false,或全局isMultiple为true但局部isMultiple指明是false,或当前label长度已经达到上线时,不允许添加多个 label + if ( + !isMultiple || + (isMultiple && curLabelOption.isMultiple === false) || + len >= (curLabelOption?.maxCount ?? maxCount) + ) { return }