flow-node-render-data.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import { Compare, Disposable, domUtils, Emitter } from '@flowgram.ai/utils';
  2. import { EntityData } from '@flowgram.ai/core';
  3. import { FlowNodeBaseType } from '../typings';
  4. import type { FlowNodeEntity } from '../entities';
  5. import { FlowNodeTransformData } from './index';
  6. export interface FlowNodeRenderSchema {
  7. addable: boolean; // 是否可添加节点
  8. expandable: boolean; // 是否可展开
  9. collapsed?: boolean; // 复合节点是否收起
  10. expanded: boolean;
  11. activated: boolean; // 是否高亮节点
  12. hovered: boolean; // 是否悬浮在节点上
  13. dragging: boolean; // 是否正在拖拽
  14. stackIndex: number; // 渲染层级
  15. extInfo?: Record<string, any>; // 扩展渲染状态字段
  16. }
  17. /**
  18. * 节点渲染状态相关数据
  19. */
  20. export class FlowNodeRenderData extends EntityData<FlowNodeRenderSchema> {
  21. static type = 'FlowNodeRenderData';
  22. declare entity: FlowNodeEntity;
  23. private _node?: HTMLDivElement;
  24. protected onExtInfoChangeEmitter = new Emitter<{ newInfo: any; oldInfo: any }>();
  25. readonly onExtInfoChange = this.onExtInfoChangeEmitter.event;
  26. get key(): string {
  27. return this.entity.id;
  28. }
  29. getDefaultData(): FlowNodeRenderSchema {
  30. const { addable, expandable, defaultExpanded } = this.entity.getNodeMeta();
  31. return {
  32. addable,
  33. expandable,
  34. expanded: defaultExpanded || false,
  35. activated: false,
  36. hovered: false,
  37. dragging: false,
  38. stackIndex: 0,
  39. };
  40. }
  41. updateExtInfo(info: Record<string, any>) {
  42. if (Compare.isChanged(this.data.extInfo, info)) {
  43. const oldInfo = this.data.extInfo;
  44. this.update({
  45. extInfo: info,
  46. });
  47. this.onExtInfoChangeEmitter.fire({ oldInfo, newInfo: info });
  48. }
  49. }
  50. getExtInfo(): Record<string, any> | undefined {
  51. return this.data.extInfo;
  52. }
  53. constructor(entity: FlowNodeEntity) {
  54. super(entity);
  55. this.toDispose.push(
  56. Disposable.create(() => {
  57. if (this._node) this._node.remove();
  58. })
  59. );
  60. }
  61. get addable(): boolean {
  62. return this.data.addable;
  63. }
  64. get expandable(): boolean {
  65. return this.data.expandable;
  66. }
  67. get draggable(): boolean {
  68. const { draggable } = this.entity.getNodeMeta();
  69. if (typeof draggable === 'function') {
  70. return draggable(this.entity);
  71. }
  72. return draggable;
  73. }
  74. get expanded(): boolean {
  75. return this.data.expanded;
  76. }
  77. set expanded(expanded: boolean) {
  78. if (this.expandable && this.data.expanded !== expanded) {
  79. this.data.expanded = expanded;
  80. this.fireChange();
  81. }
  82. }
  83. toggleExpand() {
  84. this.expanded = !this.expanded;
  85. }
  86. mouseLeaveTimeout?: ReturnType<typeof setTimeout>;
  87. toggleMouseEnter(silent = false) {
  88. this.entity.document.renderState.setNodeHovered(this.entity);
  89. if (silent) return;
  90. const transform = this.entity.getData(FlowNodeTransformData)!;
  91. if (transform.renderState.hidden) {
  92. return;
  93. }
  94. if (this.mouseLeaveTimeout) {
  95. clearTimeout(this.mouseLeaveTimeout);
  96. this.mouseLeaveTimeout = undefined;
  97. }
  98. transform.renderState.hovered = true;
  99. if (this.entity.isFirst && this.entity.parent?.id !== 'root') {
  100. // 分支中第一个节点 hover,parent activated 设置为 true
  101. transform.parent!.renderState.activated = true;
  102. } else {
  103. transform.renderState.activated = true;
  104. }
  105. }
  106. toggleMouseLeave(silent = false) {
  107. this.entity.document.renderState.setNodeHovered(undefined);
  108. if (silent) return;
  109. const transform = this.entity.getData(FlowNodeTransformData)!;
  110. this.mouseLeaveTimeout = setTimeout(() => {
  111. transform.renderState.hovered = false;
  112. if (this.entity.isFirst && this.entity.parent?.id !== 'root') {
  113. transform.parent!.renderState.activated = false;
  114. }
  115. transform.renderState.activated = false;
  116. }, 200);
  117. }
  118. get hidden(): boolean {
  119. return this.entity.hidden;
  120. }
  121. set hovered(hovered: boolean) {
  122. this.data.hovered = hovered;
  123. this.fireChange();
  124. }
  125. get hovered() {
  126. return this.data.hovered;
  127. }
  128. get dragging(): boolean {
  129. return this.data.dragging;
  130. }
  131. set dragging(dragging: boolean) {
  132. if (this.data.dragging !== dragging) {
  133. this.data.dragging = dragging;
  134. this.fireChange();
  135. }
  136. }
  137. set activated(activated: boolean) {
  138. if (this.entity.flowNodeType === FlowNodeBaseType.BLOCK_ICON && this.entity.parent) {
  139. this.entity.parent.getData<FlowNodeRenderData>(FlowNodeRenderData)!.activated = activated;
  140. return;
  141. }
  142. if (this.data.activated !== activated) {
  143. this.data.activated = activated;
  144. this.fireChange();
  145. }
  146. }
  147. get activated() {
  148. const { entity } = this;
  149. if (entity.parent && entity.parent.getData<FlowNodeRenderData>(FlowNodeRenderData)!.activated) {
  150. return true;
  151. }
  152. return this.data.activated;
  153. }
  154. get stackIndex(): number {
  155. return this.data.stackIndex;
  156. }
  157. set stackIndex(index: number) {
  158. this.data.stackIndex = index;
  159. }
  160. get lineActivated() {
  161. const { activated } = this;
  162. if (!activated) return false;
  163. // 只有 parent 高亮的情况才高亮下面的线条,否则只高亮 node
  164. // inlineBlock 仅看自身
  165. // 圈选情况下个节点被高量,则也跟着高量
  166. return Boolean(
  167. this.entity.parent?.getData(FlowNodeRenderData)?.activated ||
  168. this.entity.isInlineBlock ||
  169. this.entity.next?.getData(FlowNodeRenderData)!.activated
  170. );
  171. }
  172. get node(): HTMLDivElement {
  173. if (this._node) return this._node;
  174. this._node = domUtils.createDivWithClass('gedit-flow-activity-node');
  175. this._node.dataset.testid = 'sdk.workflow.canvas.node';
  176. this._node.dataset.nodeId = this.entity.id;
  177. return this._node;
  178. }
  179. dispose() {
  180. super.dispose();
  181. this.onExtInfoChangeEmitter.dispose();
  182. }
  183. }