manager.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /**
  2. * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
  3. * SPDX-License-Identifier: MIT
  4. */
  5. import { debounce } from 'lodash-es';
  6. import { inject, injectable } from 'inversify';
  7. import { domUtils } from '@flowgram.ai/utils';
  8. import { Disposable } from '@flowgram.ai/utils';
  9. import {
  10. WorkflowHoverService,
  11. WorkflowNodeEntity,
  12. WorkflowSelectService,
  13. } from '@flowgram.ai/free-layout-core';
  14. import { WorkflowLineEntity } from '@flowgram.ai/free-layout-core';
  15. import { WorkflowDocument } from '@flowgram.ai/free-layout-core';
  16. import { FlowNodeRenderData } from '@flowgram.ai/document';
  17. import { EntityManager, PipelineRegistry, PipelineRenderer } from '@flowgram.ai/core';
  18. import type { StackContextManagerOptions, StackingContext } from './type';
  19. import { StackingComputing } from './stacking-computing';
  20. import { BASE_Z_INDEX } from './constant';
  21. @injectable()
  22. export class StackingContextManager {
  23. @inject(WorkflowDocument) private readonly document: WorkflowDocument;
  24. @inject(EntityManager) private readonly entityManager: EntityManager;
  25. @inject(PipelineRenderer)
  26. private readonly pipelineRenderer: PipelineRenderer;
  27. @inject(PipelineRegistry)
  28. private readonly pipelineRegistry: PipelineRegistry;
  29. @inject(WorkflowHoverService)
  30. private readonly hoverService: WorkflowHoverService;
  31. @inject(WorkflowSelectService)
  32. private readonly selectService: WorkflowSelectService;
  33. public readonly node = domUtils.createDivWithClass(
  34. 'gedit-playground-layer gedit-flow-render-layer'
  35. );
  36. private options: StackContextManagerOptions = {
  37. sortNodes: (nodes: WorkflowNodeEntity[]) => nodes,
  38. };
  39. private disposers: Disposable[] = [];
  40. constructor() {}
  41. public init(options: Partial<StackContextManagerOptions> = {}): void {
  42. this.options = { ...this.options, ...options };
  43. this.pipelineRenderer.node.appendChild(this.node);
  44. this.mountListener();
  45. }
  46. public ready(): void {
  47. this.compute();
  48. }
  49. public dispose(): void {
  50. this.disposers.forEach((disposer) => disposer.dispose());
  51. }
  52. /**
  53. * 触发计算
  54. * 10ms内仅计算一次
  55. */
  56. private compute = debounce(this._compute, 10);
  57. private _compute(): void {
  58. const context = this.context;
  59. const stackingComputing = new StackingComputing();
  60. const { nodeLevel, lineLevel } = stackingComputing.compute({
  61. root: this.document.root,
  62. nodes: this.nodes,
  63. context,
  64. });
  65. this.nodes.forEach((node) => {
  66. const level = nodeLevel.get(node.id);
  67. const nodeRenderData = node.getData<FlowNodeRenderData>(FlowNodeRenderData);
  68. const element = nodeRenderData.node;
  69. element.style.position = 'absolute';
  70. if (level === undefined) {
  71. nodeRenderData.stackIndex = 0;
  72. element.style.zIndex = 'auto';
  73. return;
  74. }
  75. nodeRenderData.stackIndex = level;
  76. const zIndex = BASE_Z_INDEX + level;
  77. element.style.zIndex = String(zIndex);
  78. });
  79. this.lines.forEach((line) => {
  80. const level = lineLevel.get(line.id);
  81. const element = line.node;
  82. element.style.position = 'absolute';
  83. if (level === undefined) {
  84. line.stackIndex = 0;
  85. element.style.zIndex = 'auto';
  86. return;
  87. }
  88. line.stackIndex = level;
  89. const zIndex = BASE_Z_INDEX + level;
  90. element.style.zIndex = String(zIndex);
  91. });
  92. }
  93. private get nodes(): WorkflowNodeEntity[] {
  94. return this.entityManager.getEntities<WorkflowNodeEntity>(WorkflowNodeEntity);
  95. }
  96. private get lines(): WorkflowLineEntity[] {
  97. return this.entityManager.getEntities<WorkflowLineEntity>(WorkflowLineEntity);
  98. }
  99. private get context(): StackingContext {
  100. return {
  101. hoveredEntityID: this.hoverService.someHovered?.id,
  102. selectedNodes: this.selectService.selectedNodes,
  103. selectedIDs: new Set(this.selectService.selection.map((entity) => entity.id)),
  104. sortNodes: this.options.sortNodes,
  105. };
  106. }
  107. private mountListener(): void {
  108. const entityChangeDisposer = this.onEntityChange();
  109. const zoomDisposer = this.onZoom();
  110. const hoverDisposer = this.onHover();
  111. const selectDisposer = this.onSelect();
  112. this.disposers = [entityChangeDisposer, zoomDisposer, hoverDisposer, selectDisposer];
  113. }
  114. private onZoom(): Disposable {
  115. return this.pipelineRegistry.onZoom((scale: number) => {
  116. this.node.style.transform = `scale(${scale})`;
  117. });
  118. }
  119. private onHover(): Disposable {
  120. return this.hoverService.onHoveredChange(() => {
  121. this.compute();
  122. });
  123. }
  124. private onEntityChange(): Disposable {
  125. return this.entityManager.onEntityChange(() => {
  126. this.compute();
  127. });
  128. }
  129. private onSelect(): Disposable {
  130. return this.selectService.onSelectionChanged(() => {
  131. this.compute();
  132. });
  133. }
  134. }