@logicflow/extension 2.0.0-beta.0 → 2.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/.turbo/turbo-build.log +146 -491
  2. package/dist/index.css +63 -0
  3. package/dist/index.min.js +42 -0
  4. package/dist/index.min.js.map +1 -0
  5. package/es/NodeResize/control/Control.d.ts +3 -3
  6. package/es/NodeResize/control/Control.js +13 -3
  7. package/es/NodeResize/control/Control.js.map +1 -1
  8. package/es/NodeResize/index.d.ts +4 -0
  9. package/es/NodeResize/index.js.map +1 -1
  10. package/es/NodeResize/node/DiamondResize.d.ts +1 -0
  11. package/es/NodeResize/node/DiamondResize.js +2 -1
  12. package/es/NodeResize/node/DiamondResize.js.map +1 -1
  13. package/es/NodeResize/node/EllipseResize.d.ts +1 -0
  14. package/es/NodeResize/node/EllipseResize.js +2 -1
  15. package/es/NodeResize/node/EllipseResize.js.map +1 -1
  16. package/es/NodeResize/node/HtmlResize.d.ts +1 -0
  17. package/es/NodeResize/node/HtmlResize.js +2 -1
  18. package/es/NodeResize/node/HtmlResize.js.map +1 -1
  19. package/es/bpmn/constant.d.ts +1 -1
  20. package/es/bpmn/constant.js +3 -0
  21. package/es/bpmn/constant.js.map +1 -1
  22. package/es/bpmn/index.d.ts +3 -6
  23. package/es/bpmn/index.js +5 -7
  24. package/es/bpmn/index.js.map +1 -1
  25. package/es/bpmn-elements/presets/Pool/Pool.d.ts +21 -1
  26. package/es/components/control/index.d.ts +4 -4
  27. package/es/components/control/index.js.map +1 -1
  28. package/es/components/highlight/index.d.ts +6 -4
  29. package/es/components/highlight/index.js +32 -5
  30. package/es/components/highlight/index.js.map +1 -1
  31. package/es/components/menu/index.d.ts +1 -1
  32. package/es/components/menu/index.js +9 -10
  33. package/es/components/menu/index.js.map +1 -1
  34. package/es/components/mini-map/index.d.ts +1 -1
  35. package/es/components/mini-map/index.js +2 -2
  36. package/es/components/mini-map/index.js.map +1 -1
  37. package/es/components/selection-select/index.d.ts +1 -1
  38. package/es/components/selection-select/index.js.map +1 -1
  39. package/es/dynamic-group/index.d.ts +106 -0
  40. package/es/dynamic-group/index.js +536 -0
  41. package/es/dynamic-group/index.js.map +1 -0
  42. package/es/dynamic-group/model.d.ts +135 -0
  43. package/es/dynamic-group/model.js +413 -0
  44. package/es/dynamic-group/model.js.map +1 -0
  45. package/es/dynamic-group/node.d.ts +16 -0
  46. package/es/dynamic-group/node.js +143 -0
  47. package/es/dynamic-group/node.js.map +1 -0
  48. package/es/dynamic-group/utils.d.ts +17 -0
  49. package/es/dynamic-group/utils.js +27 -0
  50. package/es/dynamic-group/utils.js.map +1 -0
  51. package/es/index.css +63 -0
  52. package/es/index.d.ts +16 -8
  53. package/es/index.js +24 -8
  54. package/es/index.js.map +1 -1
  55. package/es/insert-node-in-polyline/index.js +3 -3
  56. package/es/insert-node-in-polyline/index.js.map +1 -1
  57. package/es/materials/group/GroupNode.d.ts +6 -10
  58. package/es/materials/group/GroupNode.js +8 -6
  59. package/es/materials/group/GroupNode.js.map +1 -1
  60. package/es/materials/group/index.d.ts +5 -5
  61. package/es/materials/group/index.js +25 -26
  62. package/es/materials/group/index.js.map +1 -1
  63. package/es/materials/node-selection/index.d.ts +6 -1
  64. package/es/materials/node-selection/index.js +64 -56
  65. package/es/materials/node-selection/index.js.map +1 -1
  66. package/es/mindmap/index.d.ts +2 -2
  67. package/es/style/index.css +63 -0
  68. package/es/style/index.less +73 -0
  69. package/es/style/raw.d.ts +1 -1
  70. package/es/style/raw.js +1 -1
  71. package/es/style/raw.js.map +1 -1
  72. package/es/tools/flow-path/index.js +0 -1
  73. package/es/tools/flow-path/index.js.map +1 -1
  74. package/es/tools/label/Label.d.ts +30 -0
  75. package/es/tools/label/Label.js +241 -0
  76. package/es/tools/label/Label.js.map +1 -0
  77. package/es/tools/label/LabelModel.d.ts +26 -0
  78. package/es/tools/label/LabelModel.js +86 -0
  79. package/es/tools/label/LabelModel.js.map +1 -0
  80. package/es/tools/label/LabelOverlay.d.ts +28 -0
  81. package/es/tools/label/LabelOverlay.js +161 -0
  82. package/es/tools/label/LabelOverlay.js.map +1 -0
  83. package/es/tools/label/algorithm.d.ts +16 -0
  84. package/es/tools/label/algorithm.js +27 -0
  85. package/es/tools/label/algorithm.js.map +1 -0
  86. package/es/tools/label/index.d.ts +59 -0
  87. package/es/tools/label/index.js +292 -0
  88. package/es/tools/label/index.js.map +1 -0
  89. package/es/tools/label/mediumEditor.d.ts +17 -0
  90. package/es/tools/label/mediumEditor.js +92 -0
  91. package/es/tools/label/mediumEditor.js.map +1 -0
  92. package/es/tools/label/utils.d.ts +64 -0
  93. package/es/tools/label/utils.js +336 -0
  94. package/es/tools/label/utils.js.map +1 -0
  95. package/es/tools/snapshot/index.d.ts +107 -11
  96. package/es/tools/snapshot/index.js +366 -149
  97. package/es/tools/snapshot/index.js.map +1 -1
  98. package/es/tools/snapshot/utils.d.ts +35 -0
  99. package/es/tools/snapshot/utils.js +238 -0
  100. package/es/tools/snapshot/utils.js.map +1 -0
  101. package/lib/NodeResize/control/Control.d.ts +3 -3
  102. package/lib/NodeResize/control/Control.js +13 -3
  103. package/lib/NodeResize/control/Control.js.map +1 -1
  104. package/lib/NodeResize/index.d.ts +4 -0
  105. package/lib/NodeResize/index.js.map +1 -1
  106. package/lib/NodeResize/node/DiamondResize.d.ts +1 -0
  107. package/lib/NodeResize/node/DiamondResize.js +2 -1
  108. package/lib/NodeResize/node/DiamondResize.js.map +1 -1
  109. package/lib/NodeResize/node/EllipseResize.d.ts +1 -0
  110. package/lib/NodeResize/node/EllipseResize.js +2 -1
  111. package/lib/NodeResize/node/EllipseResize.js.map +1 -1
  112. package/lib/NodeResize/node/HtmlResize.d.ts +1 -0
  113. package/lib/NodeResize/node/HtmlResize.js +2 -1
  114. package/lib/NodeResize/node/HtmlResize.js.map +1 -1
  115. package/lib/bpmn/constant.d.ts +1 -1
  116. package/lib/bpmn/constant.js +3 -0
  117. package/lib/bpmn/constant.js.map +1 -1
  118. package/lib/bpmn/index.d.ts +3 -6
  119. package/lib/bpmn/index.js +5 -7
  120. package/lib/bpmn/index.js.map +1 -1
  121. package/lib/bpmn-elements/presets/Pool/Pool.d.ts +21 -1
  122. package/lib/components/control/index.d.ts +4 -4
  123. package/lib/components/control/index.js.map +1 -1
  124. package/lib/components/highlight/index.d.ts +6 -4
  125. package/lib/components/highlight/index.js +32 -5
  126. package/lib/components/highlight/index.js.map +1 -1
  127. package/lib/components/menu/index.d.ts +1 -1
  128. package/lib/components/menu/index.js +9 -10
  129. package/lib/components/menu/index.js.map +1 -1
  130. package/lib/components/mini-map/index.d.ts +1 -1
  131. package/lib/components/mini-map/index.js +2 -2
  132. package/lib/components/mini-map/index.js.map +1 -1
  133. package/lib/components/selection-select/index.d.ts +1 -1
  134. package/lib/components/selection-select/index.js.map +1 -1
  135. package/lib/dynamic-group/index.d.ts +106 -0
  136. package/lib/dynamic-group/index.js +553 -0
  137. package/lib/dynamic-group/index.js.map +1 -0
  138. package/lib/dynamic-group/model.d.ts +135 -0
  139. package/lib/dynamic-group/model.js +416 -0
  140. package/lib/dynamic-group/model.js.map +1 -0
  141. package/lib/dynamic-group/node.d.ts +16 -0
  142. package/lib/dynamic-group/node.js +146 -0
  143. package/lib/dynamic-group/node.js.map +1 -0
  144. package/lib/dynamic-group/utils.d.ts +17 -0
  145. package/lib/dynamic-group/utils.js +32 -0
  146. package/lib/dynamic-group/utils.js.map +1 -0
  147. package/lib/index.css +63 -0
  148. package/lib/index.d.ts +16 -8
  149. package/lib/index.js +24 -8
  150. package/lib/index.js.map +1 -1
  151. package/lib/insert-node-in-polyline/index.js +2 -2
  152. package/lib/insert-node-in-polyline/index.js.map +1 -1
  153. package/lib/materials/group/GroupNode.d.ts +6 -10
  154. package/lib/materials/group/GroupNode.js +8 -6
  155. package/lib/materials/group/GroupNode.js.map +1 -1
  156. package/lib/materials/group/index.d.ts +5 -5
  157. package/lib/materials/group/index.js +24 -25
  158. package/lib/materials/group/index.js.map +1 -1
  159. package/lib/materials/node-selection/index.d.ts +6 -1
  160. package/lib/materials/node-selection/index.js +63 -55
  161. package/lib/materials/node-selection/index.js.map +1 -1
  162. package/lib/mindmap/index.d.ts +2 -2
  163. package/lib/style/index.css +63 -0
  164. package/lib/style/index.less +73 -0
  165. package/lib/style/raw.d.ts +1 -1
  166. package/lib/style/raw.js +1 -1
  167. package/lib/style/raw.js.map +1 -1
  168. package/lib/tools/flow-path/index.js +0 -1
  169. package/lib/tools/flow-path/index.js.map +1 -1
  170. package/lib/tools/label/Label.d.ts +30 -0
  171. package/lib/tools/label/Label.js +247 -0
  172. package/lib/tools/label/Label.js.map +1 -0
  173. package/lib/tools/label/LabelModel.d.ts +26 -0
  174. package/lib/tools/label/LabelModel.js +89 -0
  175. package/lib/tools/label/LabelModel.js.map +1 -0
  176. package/lib/tools/label/LabelOverlay.d.ts +28 -0
  177. package/lib/tools/label/LabelOverlay.js +167 -0
  178. package/lib/tools/label/LabelOverlay.js.map +1 -0
  179. package/lib/tools/label/algorithm.d.ts +16 -0
  180. package/lib/tools/label/algorithm.js +32 -0
  181. package/lib/tools/label/algorithm.js.map +1 -0
  182. package/lib/tools/label/index.d.ts +59 -0
  183. package/lib/tools/label/index.js +298 -0
  184. package/lib/tools/label/index.js.map +1 -0
  185. package/lib/tools/label/mediumEditor.d.ts +17 -0
  186. package/lib/tools/label/mediumEditor.js +98 -0
  187. package/lib/tools/label/mediumEditor.js.map +1 -0
  188. package/lib/tools/label/utils.d.ts +64 -0
  189. package/lib/tools/label/utils.js +349 -0
  190. package/lib/tools/label/utils.js.map +1 -0
  191. package/lib/tools/snapshot/index.d.ts +107 -11
  192. package/lib/tools/snapshot/index.js +366 -149
  193. package/lib/tools/snapshot/index.js.map +1 -1
  194. package/lib/tools/snapshot/utils.d.ts +35 -0
  195. package/lib/tools/snapshot/utils.js +247 -0
  196. package/lib/tools/snapshot/utils.js.map +1 -0
  197. package/package.json +20 -3
  198. package/rollup.config.js +1 -1
  199. package/src/NodeResize/control/Control.tsx +13 -3
  200. package/src/NodeResize/index.ts +4 -0
  201. package/src/NodeResize/node/DiamondResize.tsx +2 -1
  202. package/src/NodeResize/node/EllipseResize.tsx +2 -1
  203. package/src/NodeResize/node/HtmlResize.tsx +2 -1
  204. package/src/bpmn/constant.ts +4 -1
  205. package/src/bpmn/index.ts +7 -4
  206. package/src/bpmn-elements-adapter/README.md +1 -3
  207. package/src/components/control/index.ts +4 -4
  208. package/src/components/highlight/index.ts +33 -6
  209. package/src/components/menu/index.ts +16 -13
  210. package/src/components/mini-map/index.ts +3 -3
  211. package/src/components/selection-select/index.ts +6 -2
  212. package/src/dynamic-group/index.ts +609 -0
  213. package/src/dynamic-group/model.ts +503 -0
  214. package/src/dynamic-group/node.ts +140 -0
  215. package/src/dynamic-group/utils.ts +33 -0
  216. package/src/index.ts +30 -8
  217. package/src/insert-node-in-polyline/index.ts +3 -3
  218. package/src/materials/group/GroupNode.ts +12 -12
  219. package/src/materials/group/index.ts +40 -40
  220. package/src/materials/node-selection/index.ts +78 -70
  221. package/src/style/index.less +73 -0
  222. package/src/style/raw.ts +64 -1
  223. package/src/tools/flow-path/index.ts +0 -1
  224. package/src/tools/label/Label.tsx +297 -0
  225. package/src/tools/label/LabelModel.ts +82 -0
  226. package/src/tools/label/LabelOverlay.tsx +159 -0
  227. package/src/tools/label/algorithm.ts +42 -0
  228. package/src/tools/label/index.ts +401 -0
  229. package/src/tools/label/mediumEditor.ts +94 -0
  230. package/src/tools/label/utils.ts +395 -0
  231. package/src/tools/snapshot/README.md +141 -5
  232. package/src/tools/snapshot/index.ts +288 -101
  233. package/src/tools/snapshot/utils.ts +163 -0
  234. package/dist/index.js +0 -26
  235. package/dist/index.js.map +0 -1
@@ -0,0 +1,401 @@
1
+ import LogicFlow, { createUuid, GraphModel, TextMode } from '@logicflow/core'
2
+ import { cloneDeep, forEach, isArray, isObject, map } from 'lodash-es'
3
+ import LabelOverlay, { LabelConfigType } from './LabelOverlay'
4
+
5
+ import Position = LogicFlow.Position
6
+ import NodeData = LogicFlow.NodeData
7
+ import EdgeData = LogicFlow.EdgeData
8
+ import Extension = LogicFlow.Extension
9
+ import LabelConfig = LogicFlow.LabelConfig
10
+ import GraphElement = LogicFlow.GraphElement
11
+ import {
12
+ BBoxInfo,
13
+ calcPointAfterResize,
14
+ rotatePointAroundCenter,
15
+ } from './utils'
16
+
17
+ // 类型定义,如果 isMultiple 为 true 的话,maxCount 为数值且大于 1
18
+ export type ILabelOptions = {
19
+ isMultiple?: boolean
20
+ maxCount?: number
21
+ labelWidth?: number
22
+ textOverflowMode?: 'ellipsis' | 'wrap' | 'clip' | 'nowrap' | 'default'
23
+ }
24
+
25
+ export class Label implements Extension {
26
+ static pluginName = 'label'
27
+
28
+ lf: LogicFlow
29
+ options: ILabelOptions
30
+
31
+ textOverflowMode: 'ellipsis' | 'wrap' | 'clip' | 'nowrap' | 'default'
32
+ isMultiple: boolean
33
+ labelWidth?: number
34
+ maxCount: number // 默认值给无限大数值
35
+
36
+ labelInitPositionMap: Map<string, Position> = new Map()
37
+
38
+ constructor({ lf, options }: { lf: LogicFlow; options: ILabelOptions }) {
39
+ this.lf = lf
40
+ // DONE: 根据 options 初始化一些插件配置,比如是否支持多个 label 等,生效在所有 label 中
41
+ this.options = options ?? {}
42
+
43
+ this.textOverflowMode = options.textOverflowMode ?? 'default'
44
+ this.isMultiple = options.isMultiple ?? true
45
+ this.labelWidth = options.labelWidth
46
+ this.maxCount = options.maxCount ?? Infinity
47
+
48
+ // DONE: 1. 启用插件时,将当前画布的 textMode 更新为 TextMode.LABEL。
49
+ // 如果将其又重新设置为 TextModel.TEXT,则需要 disable 掉 Label 工具,enable TextEditTool
50
+ lf.graphModel.editConfigModel.updateTextMode(TextMode.LABEL)
51
+
52
+ // TODO: 2. 做一些插件需要的事件监听
53
+ this.addEventListeners()
54
+
55
+ // TODO: 3. 自定义快捷键,比如 delete,选中 label 时,移除 label
56
+ // this.rewriteShortcut()
57
+
58
+ // 插件中注册 LabelOverlay 工具,用于 label 的编辑
59
+ lf.tool.registerTool(LabelOverlay.toolName, LabelOverlay)
60
+ // LabelOverlay 和 TextEditTool 互斥,所以将它 disable 掉
61
+ lf.tool.disableTool('text-edit-tool')
62
+ }
63
+
64
+ /**
65
+ * 格式化元素的 Label 配置,后续初始化 Label 用统一的数据格式
66
+ * 主要是将 _label 类型 string | LabelConfig | LabelConfig[] 统一转换为 LabelConfig[]
67
+ * @param graphModel 当前图的 model
68
+ * @param element 当前元素 model
69
+ * @return LabelConfig[]
70
+ */
71
+ private formatConfig(
72
+ graphModel: GraphModel,
73
+ element: GraphElement,
74
+ ): LabelConfig[] {
75
+ const {
76
+ editConfigModel: {
77
+ nodeTextEdit,
78
+ edgeTextEdit,
79
+ nodeTextDraggable,
80
+ edgeTextDraggable,
81
+ },
82
+ } = graphModel
83
+ const { textOverflowMode, isMultiple, maxCount, labelWidth } = this
84
+ const {
85
+ text,
86
+ zIndex,
87
+ properties: { _label, _labelOption = {} },
88
+ } = element
89
+
90
+ // 当前元素的 Label 相关配置
91
+ const curLabelConfig = _label as LabelConfigType
92
+ const { isMultiple: curIsMultiple, maxCount: curMaxCount }: ILabelOptions =
93
+ _labelOption as ILabelOptions
94
+
95
+ // REMIND: 对 3 种可能得数据类型进行处理
96
+ let formatConfig: LabelConfig[] = [] // 保存格式化后的 LabelConfig
97
+ if (isArray(curLabelConfig)) {
98
+ // 1. 数组的话就是 LabelConfig[] 类型
99
+ // 判断是否开启 isMultiple, 如果开启了,判断是否超过最大数量。超出就截取
100
+ const size = curMaxCount ?? maxCount // 优先级,当设置 multiple 时,元素的 maxCount 优先级高于插件的 maxCount
101
+ if (isMultiple && curIsMultiple) {
102
+ if (curLabelConfig.length > size) {
103
+ formatConfig = curLabelConfig.slice(0, size)
104
+ } else {
105
+ formatConfig = curLabelConfig
106
+ }
107
+ } else {
108
+ formatConfig = [curLabelConfig[0]]
109
+ }
110
+ } else if (isObject(curLabelConfig)) {
111
+ // 2. 对象的话就是 LabelConfig 类型
112
+ formatConfig = [curLabelConfig]
113
+ } else if (typeof curLabelConfig === 'string' || !curLabelConfig) {
114
+ // 3. 字符串或者为空的话就是 string 类型,基于 text 的数据合成 LabelConfig 信息(主要复用 text 的 x,y 信息)
115
+ const config: LabelConfig = {
116
+ ...text,
117
+ content: curLabelConfig || text.value,
118
+ draggable:
119
+ element.BaseType === 'edge' ? edgeTextDraggable : nodeTextDraggable,
120
+ }
121
+ formatConfig = config.value ? [config] : []
122
+ }
123
+
124
+ // TODO: 针对 Edge,在 edge 更新时 重新计算 Label 的位置
125
+ if (element.BaseType === 'edge') {
126
+ // 判断当前 label,是否在 edge 的路径上,如果不在,就重新计算位置
127
+ formatConfig = map(formatConfig, (config) => {
128
+ const { x, y } = config
129
+ console.log('x, y --->>>', x, y)
130
+
131
+ return config
132
+ })
133
+ }
134
+
135
+ // DONE: 再根据一些全局配置,比如是否支持垂直显示等,对 LabelConfig 进行二次处理
136
+ // 优先级:全局配置 > 元素配置。比如全局设置 isMultiple 为 true 时,才可以使用 局部的 isMultiple 设置才生效
137
+ // 当全局 isMultiple 为 false 时,局部的 isMultiple 不生效
138
+ return map(formatConfig, (config) => {
139
+ if (!config.id) {
140
+ config.id = createUuid()
141
+ }
142
+
143
+ const {
144
+ value,
145
+ content,
146
+ vertical,
147
+ editable,
148
+ draggable,
149
+ textOverflowMode: labelTextOverflowMode,
150
+ } = config
151
+
152
+ const textEdit = element.BaseType === 'node' ? nodeTextEdit : edgeTextEdit
153
+ const textDraggable =
154
+ element.BaseType === 'node' ? nodeTextDraggable : edgeTextDraggable
155
+
156
+ return {
157
+ ...config,
158
+ zIndex,
159
+ labelWidth,
160
+ content: content ?? value,
161
+ vertical: vertical ?? false,
162
+ editable: textEdit && editable,
163
+ draggable: textDraggable && draggable,
164
+ textOverflowMode: labelTextOverflowMode ?? textOverflowMode,
165
+ }
166
+ })
167
+ }
168
+
169
+ /**
170
+ * 根据初始化的数据,格式化 Label 的数据格式后,统一更新到元素的 properties._label 中,保证后续的渲染以这个数据格式进行
171
+ * @param graphModel
172
+ */
173
+ private setupLabels(graphModel: GraphModel) {
174
+ // const elements = [...graphModel.nodes, ...graphModel.edges]
175
+ const elements = graphModel.sortElements
176
+
177
+ // TODO: 1. 筛选出当前画布上,textMode 为 TextMode.LABEL 的元素(在支持元素级别的 textMode 时,需要做这个筛选)
178
+ // REMIND: 本期先只支持全局配置,所以判断全局的 textMode 即可
179
+ forEach(elements, (element) => {
180
+ // DONE: 2. 在此处做数据的转换
181
+ // 输入:NodeConfig.properties._label: string | LabelConfig | LabelConfig[]
182
+ // 输出:NodeData.properties._label: LabelData | LabelData[]
183
+ // 是否需要根据 isMultiple 控制是否返回数组或对象 or 直接全部返回数组 ❓❓❓ -> 目前直接全部返回数组
184
+
185
+ this.rewriteInnerMethods(element)
186
+
187
+ const formatLabelConfig = this.formatConfig(graphModel, element)
188
+ // FIX: BUG Here: 格式化后的 labelConfig 没有同步到 element 上,导致每次重新渲染时,都会重新格式化,且重新生成 id
189
+ // 但如果在此处通过 setProperty 更新元素的 _label 时,又会导致死循环
190
+ element.setProperty('_label', formatLabelConfig)
191
+ })
192
+ }
193
+
194
+ /**
195
+ * 给元素添加一个 label
196
+ * @param element
197
+ * @param position
198
+ */
199
+ private addLabel(element: GraphElement, position: Position) {
200
+ const { maxCount } = this
201
+ const {
202
+ properties: { _label, _labelOption },
203
+ } = element
204
+ const curLabelConfig = (_label as LabelConfig[]) ?? []
205
+ const curLabelOption = (_labelOption as ILabelOptions) ?? {}
206
+
207
+ const len = curLabelConfig.length
208
+ const newLabel = {
209
+ id: createUuid(),
210
+ x: position.x,
211
+ y: position.y,
212
+ content: `Label${len + 1}`,
213
+ value: `Label${len + 1}`,
214
+ style: {},
215
+ draggable: true,
216
+ editable: true,
217
+ vertical: false,
218
+ }
219
+
220
+ if (len >= (curLabelOption?.maxCount ?? maxCount)) {
221
+ return
222
+ }
223
+
224
+ curLabelConfig.push(newLabel)
225
+ element.setProperty('_label', curLabelConfig)
226
+ }
227
+
228
+ /**
229
+ * 移除元素的某个 label
230
+ * @private
231
+ */
232
+ // private removeLabel() {}
233
+
234
+ private addEventListeners() {
235
+ const { graphModel } = this.lf
236
+ const { eventCenter, editConfigModel } = graphModel
237
+
238
+ eventCenter.on('graph:rendered', ({ graphModel }) => {
239
+ this.setupLabels(graphModel)
240
+ })
241
+
242
+ // 监听元素双击事件,给元素增加 Label
243
+ eventCenter.on(
244
+ 'node:dbclick,edge:dbclick',
245
+ ({ e, data }: { e: MouseEvent; data: NodeData | EdgeData }) => {
246
+ // DONE: 增加 label 的数据信息到 element model
247
+ const target: GraphElement | undefined = graphModel.getElement(data.id)
248
+ // DONE: 将 clientX 和 clientY 转换为画布坐标
249
+ const {
250
+ canvasOverlayPosition: { x: x1, y: y1 },
251
+ } = graphModel.getPointByClient({
252
+ x: e.clientX,
253
+ y: e.clientY,
254
+ })
255
+
256
+ const point: Position = {
257
+ x: x1,
258
+ y: y1,
259
+ }
260
+ if (target && editConfigModel.textMode === TextMode.LABEL) {
261
+ this.addLabel(target, point)
262
+ }
263
+ },
264
+ )
265
+
266
+ // 监听 node:resize 事件,在 resize 时,重新计算 label 的位置信息
267
+ eventCenter.on('node:resize', ({ preData, data, model }) => {
268
+ const {
269
+ width: preWidth,
270
+ height: preHeight,
271
+ _label = [],
272
+ } = preData.properties ?? {}
273
+ const { width: curWidth, height: curHeight } = data.properties ?? {}
274
+
275
+ if (preWidth && preHeight && curWidth && curHeight) {
276
+ const origin: BBoxInfo = {
277
+ x: preData.x,
278
+ y: preData.y,
279
+ width: preWidth,
280
+ height: preHeight,
281
+ }
282
+ const scaled: BBoxInfo = {
283
+ x: data.x,
284
+ y: data.y,
285
+ width: curWidth,
286
+ height: curHeight,
287
+ }
288
+ const newLabelConfig = map(_label as LabelConfig[], (label) => {
289
+ const { x, y } = label
290
+ const newPoint = calcPointAfterResize(origin, scaled, { x, y })
291
+ return {
292
+ ...label,
293
+ ...newPoint,
294
+ }
295
+ })
296
+
297
+ model.setProperty('_label', newLabelConfig)
298
+ }
299
+ })
300
+
301
+ // 监听 node:rotate 事件,在 rotate 时,重新计算 Label 的位置信息
302
+ eventCenter.on('node:rotate', ({ model }) => {
303
+ const {
304
+ x,
305
+ y,
306
+ rotate,
307
+ properties: { _label = [] },
308
+ } = model
309
+ const center: Position = { x, y }
310
+
311
+ const newLabelConfig = map(_label as LabelConfig[], (label) => {
312
+ if (!label.id) return label
313
+
314
+ let point: Position = { x: label.x, y: label.y }
315
+ if (this.labelInitPositionMap.has(label.id)) {
316
+ point = this.labelInitPositionMap.get(label.id)!
317
+ } else {
318
+ this.labelInitPositionMap.set(label.id, point)
319
+ }
320
+
321
+ // 弧度转角度
322
+ let theta = rotate * (180 / Math.PI)
323
+ if (theta < 0) theta += 360
324
+ const radian = theta * (Math.PI / 180)
325
+
326
+ const newPoint = rotatePointAroundCenter(point, center, radian)
327
+ return {
328
+ ...label,
329
+ ...newPoint,
330
+ rotate: theta,
331
+ }
332
+ })
333
+
334
+ model.setProperty('_label', newLabelConfig)
335
+ })
336
+ }
337
+
338
+ /**
339
+ * 重写元素的一些方法,以支持 Label 的拖拽、编辑等
340
+ * @param element
341
+ */
342
+ private rewriteInnerMethods(element: GraphElement) {
343
+ // 重写 edgeModel/nodeModel moveText 方法,在 move text 时,以相同的逻辑移动 label
344
+ element.moveText = (deltaX: number, deltaY: number) => {
345
+ if (!element.text) return
346
+ const {
347
+ text: { x, y, value, draggable, editable },
348
+ } = element
349
+
350
+ element.text = {
351
+ value,
352
+ editable,
353
+ draggable,
354
+ x: x + deltaX,
355
+ y: y + deltaY,
356
+ }
357
+ const properties = cloneDeep(element.getProperties())
358
+ // 重新计算新的 label 位置信息
359
+ if (isArray(properties._label)) {
360
+ const nextLabel = map(properties._label as LabelConfig[], (label) => {
361
+ return {
362
+ ...label,
363
+ x: label.x + deltaX,
364
+ y: label.y + deltaY,
365
+ }
366
+ })
367
+ // console.log('Label --->>>', nextLabel)
368
+ element?.setProperty('_label', nextLabel)
369
+ }
370
+ }
371
+
372
+ // TODO: others methods ???
373
+ }
374
+
375
+ // private rewriteShortcut() {}
376
+
377
+ /**
378
+ * 更新当前渲染使用的 Text or Label 模式
379
+ */
380
+ updateTextMode(textMode: TextMode) {
381
+ const {
382
+ graphModel: { editConfigModel },
383
+ } = this.lf
384
+ if (textMode === editConfigModel.textMode) return
385
+
386
+ editConfigModel.updateTextMode(textMode)
387
+ if (textMode === TextMode.LABEL) {
388
+ this.lf.tool.enableTool(LabelOverlay.toolName)
389
+ this.lf.tool.disableTool('text-edit-tool')
390
+ } else if (textMode === TextMode.TEXT) {
391
+ this.lf.tool.enableTool('text-edit-tool')
392
+ this.lf.tool.disableTool(LabelOverlay.toolName)
393
+ }
394
+ }
395
+
396
+ render() {}
397
+
398
+ destroy() {}
399
+ }
400
+
401
+ export default Label
@@ -0,0 +1,94 @@
1
+ import MediumEditor from 'medium-editor'
2
+ import Picker from 'vanilla-picker'
3
+ import rangy from 'rangy'
4
+ import 'rangy/lib/rangy-classapplier'
5
+
6
+ export const defaultOptions = {
7
+ toolbar: {
8
+ allowMultiParagraphSelection: true,
9
+ buttons: [
10
+ 'bold',
11
+ 'colorpicker',
12
+ 'italic',
13
+ 'underline',
14
+ 'strikethrough',
15
+ 'quote',
16
+ 'justifyLeft',
17
+ 'justifyCenter',
18
+ 'justifyRight',
19
+ 'justifyFull',
20
+ 'superscript',
21
+ 'subscript',
22
+ 'orderedlist',
23
+ 'unorderedlist',
24
+ 'pre',
25
+ 'removeFormat',
26
+ 'outdent',
27
+ 'indent',
28
+ 'h2',
29
+ 'h3',
30
+ ],
31
+ standardizeSelectionStart: false,
32
+ updateOnEmptySelection: false,
33
+ },
34
+
35
+ placeholder: {
36
+ text: '请输入内容',
37
+ hideOnClick: true,
38
+ },
39
+ disableEditing: true,
40
+ }
41
+
42
+ export const ColorPickerButton = MediumEditor.extensions.button.extend({
43
+ name: 'colorpicker',
44
+ tagNames: ['mark'],
45
+ contentDefault: '<b>Color</b>',
46
+ aria: 'Color Picker',
47
+ action: 'colorPicker',
48
+ init: function () {
49
+ rangy.init()
50
+ MediumEditor.extensions.button.prototype.init.call(this)
51
+ this.colorPicker = new Picker({
52
+ parent: this.button,
53
+ color: '#000',
54
+ onDone: (res) => {
55
+ if (this.coloredText && this.coloredText.isAppliedToSelection()) {
56
+ this.coloredText.undoToSelection()
57
+ }
58
+ this.coloredText = rangy.createClassApplier('colored', {
59
+ elementTagName: 'span',
60
+ elementProperties: {
61
+ style: {
62
+ color: res.hex,
63
+ },
64
+ },
65
+ normalize: true,
66
+ })
67
+ this.coloredText.toggleSelection()
68
+ this.base.checkContentChanged()
69
+ this.setInactive()
70
+ },
71
+ })
72
+ },
73
+ getButton: function () {
74
+ return this.button
75
+ },
76
+ handleClick: function () {
77
+ this.setActive()
78
+ this.colorPicker.show()
79
+ },
80
+ isAlreadyApplied: function (node) {
81
+ return node.nodeName.toLowerCase() === 'mark'
82
+ },
83
+ isActive: function () {
84
+ return this.button.classList.contains('medium-editor-button-active')
85
+ },
86
+ setInactive: function () {
87
+ this.button.classList.remove('medium-editor-button-active')
88
+ },
89
+ setActive: function () {
90
+ this.button.classList.add('medium-editor-button-active')
91
+ },
92
+ })
93
+
94
+ export { MediumEditor }