@mx-sose-front/mx-sose-graph 1.0.0

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 (337) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +343 -0
  3. package/dist/index.d.ts +3937 -0
  4. package/dist/index.esm.js +74367 -0
  5. package/dist/index.esm.js.map +1 -0
  6. package/dist/index.umd.js +38 -0
  7. package/dist/index.umd.js.map +1 -0
  8. package/dist/style.css +1 -0
  9. package/package.json +70 -0
  10. package/src/components/ContextMenu.vue +475 -0
  11. package/src/components/Diagram/StrategicTaxonomyDiagram.vue +141 -0
  12. package/src/components/Edge/Edge.vue +366 -0
  13. package/src/components/InteractionLayer.vue +2033 -0
  14. package/src/components/Label.vue +0 -0
  15. package/src/components/LineStyle/ConnectionLine.vue +126 -0
  16. package/src/components/LineStyle/LineStyleMarker.vue +87 -0
  17. package/src/components/Pin/Pin.vue +220 -0
  18. package/src/components/Pin/Port.vue +172 -0
  19. package/src/components/Shape/Action.vue +121 -0
  20. package/src/components/Shape/ActivityAction.vue +155 -0
  21. package/src/components/Shape/Block.vue +306 -0
  22. package/src/components/Shape/ConceptualRole.vue +266 -0
  23. package/src/components/Shape/Diagram.vue +220 -0
  24. package/src/components/Shape/DividingLine.vue +594 -0
  25. package/src/components/Shape/DogEar.vue +224 -0
  26. package/src/components/Shape/Package.vue +340 -0
  27. package/src/constants/edgeShapeKeys.ts +81 -0
  28. package/src/constants/index.ts +440 -0
  29. package/src/index.ts +28 -0
  30. package/src/render/shape-registry.ts +17 -0
  31. package/src/render/shape-renderer.ts +103 -0
  32. package/src/statics/icons/childIcons/relations@3x.png +0 -0
  33. package/src/statics/icons/childIcons/role@3x.png +0 -0
  34. package/src/statics/icons/childIcons//344/270/232/345/212/241/344/277/241/345/217/267@3x.png +0 -0
  35. package/src/statics/icons/childIcons//344/270/232/345/212/241/344/277/241/346/201/257@3x.png +0 -0
  36. package/src/statics/icons/childIcons//344/270/232/345/212/241/344/277/241/346/201/257/345/233/276@3x.png +0 -0
  37. package/src/statics/icons/childIcons//344/270/232/345/212/241/345/206/205/351/203/250/346/265/201/347/250/213/345/233/276@3x.png +0 -0
  38. package/src/statics/icons/childIcons//344/270/232/345/212/241/345/206/205/351/203/250/350/277/236/351/200/232/345/233/276@3x.png +0 -0
  39. package/src/statics/icons/childIcons//344/270/232/345/212/241/345/212/250/344/275/234@3x.png +0 -0
  40. package/src/statics/icons/childIcons//344/270/232/345/212/241/345/217/202/346/225/260@3x.png +0 -0
  41. package/src/statics/icons/childIcons//344/270/232/345/212/241/345/217/202/346/225/260/345/233/276@3x.png +0 -0
  42. package/src/statics/icons/childIcons//344/270/232/345/212/241/345/257/271/350/261/241/346/265/201@3x.png +0 -0
  43. package/src/statics/icons/childIcons//344/270/232/345/212/241/346/211/247/350/241/214/350/200/205@3x.png +0 -0
  44. package/src/statics/icons/childIcons//344/270/232/345/212/241/346/216/245/345/217/243@3x.png +0 -0
  45. package/src/statics/icons/childIcons//344/270/232/345/212/241/346/216/247/345/210/266/346/265/201@3x.png +0 -0
  46. package/src/statics/icons/childIcons//344/270/232/345/212/241/346/246/202/345/277/265/345/233/276@3x.png +0 -0
  47. package/src/statics/icons/childIcons//344/270/232/345/212/241/346/246/202/345/277/265/350/241/250@3x.png +0 -0
  48. package/src/statics/icons/childIcons//344/270/232/345/212/241/346/264/273/345/212/250@3x.png +0 -0
  49. package/src/statics/icons/childIcons//344/270/232/345/212/241/346/264/273/345/212/250/344/270/216/350/203/275/345/212/233/346/230/240/345/260/204/347/237/251/351/230/265@3x.png +0 -0
  50. package/src/statics/icons/childIcons//344/270/232/345/212/241/346/264/273/345/212/250/345/212/250/344/275/234@3x.png +0 -0
  51. package/src/statics/icons/childIcons//344/270/232/345/212/241/346/264/273/345/212/250/346/265/201/347/250/213/345/233/276@3x.png +0 -0
  52. package/src/statics/icons/childIcons//344/270/232/345/212/241/346/264/273/345/212/250/347/273/223/346/236/204/345/233/276@3x.png +0 -0
  53. package/src/statics/icons/childIcons//344/270/232/345/212/241/346/265/201/347/250/213/345/233/276@3x.png +0 -0
  54. package/src/statics/icons/childIcons//344/270/232/345/212/241/347/212/266/346/200/201/345/233/276@3x.png +0 -0
  55. package/src/statics/icons/childIcons//344/270/232/345/212/241/347/253/257/345/217/243@3x.png +0 -0
  56. package/src/statics/icons/childIcons//344/270/232/345/212/241/347/272/246/346/235/237@3x.png +0 -0
  57. package/src/statics/icons/childIcons//344/270/232/345/212/241/347/272/246/346/235/237/345/233/276@3x.png +0 -0
  58. package/src/statics/icons/childIcons//344/270/232/345/212/241/347/272/246/346/235/237/345/256/232/344/271/211/345/233/276@3x.png +0 -0
  59. package/src/statics/icons/childIcons//344/270/232/345/212/241/347/272/246/346/235/237/350/241/250@3x.png +0 -0
  60. package/src/statics/icons/childIcons//344/270/232/345/212/241/347/273/223/346/236/204@3x.png +0 -0
  61. package/src/statics/icons/childIcons//344/270/232/345/212/241/350/207/252/347/224/261/345/210/206/347/261/273/345/233/276@3x.png +0 -0
  62. package/src/statics/icons/childIcons//344/270/232/345/212/241/350/247/222/350/211/262@3x.png +0 -0
  63. package/src/statics/icons/childIcons//344/270/232/345/212/241/350/277/236/346/216/245/345/231/250@3x.png +0 -0
  64. package/src/statics/icons/childIcons//344/270/232/345/212/241/350/277/236/351/200/232/345/233/276@3x.png +0 -0
  65. package/src/statics/icons/childIcons//344/270/232/345/212/241/350/277/236/351/200/232/350/241/250@3x.png +0 -0
  66. package/src/statics/icons/childIcons//344/270/232/345/212/241/351/253/230/347/272/247/346/246/202/345/277/265/345/233/276@3x.png +0 -0
  67. package/src/statics/icons/childIcons//344/272/272/345/221/230@3x.png +0 -0
  68. package/src/statics/icons/childIcons//344/272/272/345/221/230/345/206/205/351/203/250/346/265/201/347/250/213/345/233/276@3x.png +0 -0
  69. package/src/statics/icons/childIcons//344/272/272/345/221/230/345/206/205/351/203/250/350/277/236/351/200/232/345/233/276@3x.png +0 -0
  70. package/src/statics/icons/childIcons//344/272/272/345/221/230/345/217/202/346/225/260/345/233/276@3x.png +0 -0
  71. package/src/statics/icons/childIcons//344/272/272/345/221/230/346/246/202/345/277/265/345/233/276@3x.png +0 -0
  72. package/src/statics/icons/childIcons//344/272/272/345/221/230/346/246/202/345/277/265/350/241/250@3x.png +0 -0
  73. package/src/statics/icons/childIcons//344/272/272/345/221/230/346/265/201/347/250/213/345/233/276@3x.png +0 -0
  74. package/src/statics/icons/childIcons//344/272/272/345/221/230/347/212/266/346/200/201/345/233/276@3x.png +0 -0
  75. package/src/statics/icons/childIcons//344/272/272/345/221/230/347/272/246/346/235/237/345/233/276@3x-2.png +0 -0
  76. package/src/statics/icons/childIcons//344/272/272/345/221/230/347/272/246/346/235/237/345/233/276@3x.png +0 -0
  77. package/src/statics/icons/childIcons//344/272/272/345/221/230/347/273/223/346/236/204/345/233/276@3x.png +0 -0
  78. package/src/statics/icons/childIcons//344/272/272/345/221/230/350/277/236/351/200/232/345/233/276@3x.png +0 -0
  79. package/src/statics/icons/childIcons//344/272/272/345/221/230/350/277/236/351/200/232/350/241/250@3x.png +0 -0
  80. package/src/statics/icons/childIcons//344/273/267/345/200/274/346/265/201@3x.png +0 -0
  81. package/src/statics/icons/childIcons//344/273/273/346/204/217/345/205/263/347/263/273@3x.png +0 -0
  82. package/src/statics/icons/childIcons//344/273/273/346/204/217/350/277/236/346/216/245/345/231/250@3x.png +0 -0
  83. package/src/statics/icons/childIcons//344/274/201/344/270/232/344/275/277/345/221/275@3x.png +0 -0
  84. package/src/statics/icons/childIcons//344/274/201/344/270/232/345/205/250/347/224/237/345/221/275/345/221/250/346/234/237@3x.png +0 -0
  85. package/src/statics/icons/childIcons//344/274/201/344/270/232/346/204/277/346/231/257@3x.png +0 -0
  86. package/src/statics/icons/childIcons//344/274/201/344/270/232/347/233/256/346/240/207@3x.png +0 -0
  87. package/src/statics/icons/childIcons//344/275/215/347/275/256@3x.png +0 -0
  88. package/src/statics/icons/childIcons//344/275/223/347/263/273/346/236/266/346/236/204@3x.png +0 -0
  89. package/src/statics/icons/childIcons//344/276/235/350/265/226@3x.png +0 -0
  90. package/src/statics/icons/childIcons//344/277/235/346/212/244@3x.png +0 -0
  91. package/src/statics/icons/childIcons//344/277/241/346/201/257/346/250/241/345/236/213@3x.png +0 -0
  92. package/src/statics/icons/childIcons//345/210/233/351/200/240@3x.png +0 -0
  93. package/src/statics/icons/childIcons//345/212/237/350/203/275@3x.png +0 -0
  94. package/src/statics/icons/childIcons//345/212/237/350/203/275/344/270/216/344/270/232/345/212/241/346/264/273/345/212/250/346/230/240/345/260/204/347/237/251/351/230/265@3x.png +0 -0
  95. package/src/statics/icons/childIcons//345/212/237/350/203/275/345/212/250/344/275/234@3x.png +0 -0
  96. package/src/statics/icons/childIcons//345/212/237/350/203/275/345/257/271/350/261/241/346/265/201@3x.png +0 -0
  97. package/src/statics/icons/childIcons//345/212/237/350/203/275/346/216/247/345/210/266/346/265/201@3x.png +0 -0
  98. package/src/statics/icons/childIcons//345/217/214/345/220/221/345/205/263/350/201/224@3x.png +0 -0
  99. package/src/statics/icons/childIcons//345/217/227/345/210/260/345/275/261/345/223/215@3x.png +0 -0
  100. package/src/statics/icons/childIcons//345/234/260/347/220/206/346/224/277/346/262/273/350/214/203/345/233/264/347/261/273/345/236/213@3x.png +0 -0
  101. package/src/statics/icons/childIcons//345/241/253/345/206/231/350/201/214/344/275/215/347/224/263/350/257/267@3x.png +0 -0
  102. package/src/statics/icons/childIcons//345/256/211/345/205/250/345/206/205/351/203/250/346/265/201/347/250/213/345/233/276@3x.png +0 -0
  103. package/src/statics/icons/childIcons//345/256/211/345/205/250/346/216/247/345/210/266@3x.png +0 -0
  104. package/src/statics/icons/childIcons//345/256/211/345/205/250/346/246/202/345/277/265/345/233/276@3x.png +0 -0
  105. package/src/statics/icons/childIcons//345/256/211/345/205/250/346/246/202/345/277/265/350/241/250@3x.png +0 -0
  106. package/src/statics/icons/childIcons//345/256/211/345/205/250/346/265/201/347/250/213@3x.png +0 -0
  107. package/src/statics/icons/childIcons//345/256/211/345/205/250/346/265/201/347/250/213/345/212/250/344/275/234@3x.png +0 -0
  108. package/src/statics/icons/childIcons//345/256/211/345/205/250/346/265/201/347/250/213/345/233/276@3x.png +0 -0
  109. package/src/statics/icons/childIcons//345/256/211/345/205/250/347/272/246/346/235/237@3x.png +0 -0
  110. package/src/statics/icons/childIcons//345/256/211/345/205/250/347/272/246/346/235/237/345/233/276@3x.png +0 -0
  111. package/src/statics/icons/childIcons//345/256/211/345/205/250/347/272/246/346/235/237/345/256/232/344/271/211/345/233/276@3x.png +0 -0
  112. package/src/statics/icons/childIcons//345/256/211/345/205/250/347/273/223/346/236/204/345/233/276@3x.png +0 -0
  113. package/src/statics/icons/childIcons//345/256/211/345/205/250/350/277/236/351/200/232/345/233/276@3x.png +0 -0
  114. package/src/statics/icons/childIcons//345/256/211/345/205/250/350/277/236/351/200/232/350/241/250@3x.png +0 -0
  115. package/src/statics/icons/childIcons//345/256/211/345/205/250/351/232/224/347/246/273@3x.png +0 -0
  116. package/src/statics/icons/childIcons//345/256/211/345/205/250/351/243/216/351/231/251@3x.png +0 -0
  117. package/src/statics/icons/childIcons//345/256/232/345/220/221/345/205/263/347/263/273@3x.png +0 -0
  118. package/src/statics/icons/childIcons//345/256/232/345/220/221/345/205/263/350/201/224@3x.png +0 -0
  119. package/src/statics/icons/childIcons//345/256/232/345/220/221/347/273/204/346/210/220@3x.png +0 -0
  120. package/src/statics/icons/childIcons//345/256/232/345/220/221/350/201/232/345/220/210@3x.png +0 -0
  121. package/src/statics/icons/childIcons//345/256/236/351/231/205/344/272/272/345/221/230@3x.png +0 -0
  122. package/src/statics/icons/childIcons//345/256/236/351/231/205/344/274/201/344/270/232/351/230/266/346/256/265@3x.png +0 -0
  123. package/src/statics/icons/childIcons//345/256/236/351/231/205/344/275/215/347/275/256@3x.png +0 -0
  124. package/src/statics/icons/childIcons//345/256/236/351/231/205/345/261/236/346/200/247/350/256/276/347/275/256@3x.png +0 -0
  125. package/src/statics/icons/childIcons//345/256/236/351/231/205/346/210/230/347/225/245/351/230/266/346/256/265/347/224/230/347/211/271/345/233/276@3x.png +0 -0
  126. package/src/statics/icons/childIcons//345/256/236/351/231/205/346/214/201/347/273/255/344/273/273/345/212/241@3x.png +0 -0
  127. package/src/statics/icons/childIcons//345/256/236/351/231/205/346/234/215/345/212/241@3x.png +0 -0
  128. package/src/statics/icons/childIcons//345/256/236/351/231/205/346/235/241/344/273/266@3x.png +0 -0
  129. package/src/statics/icons/childIcons//345/256/236/351/231/205/347/216/257/345/242/203@3x.png +0 -0
  130. package/src/statics/icons/childIcons//345/256/236/351/231/205/347/273/204/347/273/207@3x.png +0 -0
  131. package/src/statics/icons/childIcons//345/256/236/351/231/205/350/201/214/344/275/215@3x.png +0 -0
  132. package/src/statics/icons/childIcons//345/256/236/351/231/205/350/201/214/350/264/243@3x.png +0 -0
  133. package/src/statics/icons/childIcons//345/256/236/351/231/205/350/264/243/344/273/273@3x.png +0 -0
  134. package/src/statics/icons/childIcons//345/256/236/351/231/205/350/265/204/346/272/220@3x.png +0 -0
  135. package/src/statics/icons/childIcons//345/256/236/351/231/205/350/265/204/346/272/220/344/270/216/345/256/236/351/231/205/351/241/271/347/233/256/345/257/271/345/272/224/345/205/263/347/263/273/347/237/251/351/230/265@3x.png +0 -0
  136. package/src/statics/icons/childIcons//345/256/236/351/231/205/350/265/204/346/272/220/346/246/202/345/277/265/347/237/251/351/230/265@3x.png +0 -0
  137. package/src/statics/icons/childIcons//345/256/236/351/231/205/350/265/204/346/272/220/347/273/223/346/236/204/345/233/276@3x.png +0 -0
  138. package/src/statics/icons/childIcons//345/256/236/351/231/205/350/265/204/346/272/220/350/277/236/351/200/232/345/233/276@3x.png +0 -0
  139. package/src/statics/icons/childIcons//345/256/236/351/231/205/351/241/271/347/233/256@3x.png +0 -0
  140. package/src/statics/icons/childIcons//345/256/236/351/231/205/351/241/271/347/233/256/344/270/216/350/203/275/345/212/233/347/232/204/346/230/240/345/260/204/347/237/251/351/230/265@3x.png +0 -0
  141. package/src/statics/icons/childIcons//345/256/236/351/231/205/351/241/271/347/233/256/351/207/214/347/250/213/347/242/221@3x.png +0 -0
  142. package/src/statics/icons/childIcons//345/256/236/351/231/205/351/241/271/347/233/256/351/207/214/347/250/213/347/242/221/346/261/207/346/200/273/350/241/250@3x.png +0 -0
  143. package/src/statics/icons/childIcons//345/256/236/351/231/205/351/243/216/351/231/251@3x.png +0 -0
  144. package/src/statics/icons/childIcons//345/261/225/347/244/272@3x.png +0 -0
  145. package/src/statics/icons/childIcons//345/267/262/347/237/245/350/265/204/346/272/220@3x.png +0 -0
  146. package/src/statics/icons/childIcons//345/274/200/345/261/225/345/267/245/344/275/234/350/203/275/345/212/233@3x.png +0 -0
  147. package/src/statics/icons/childIcons//345/274/225/347/224/250/345/261/236/346/200/247@3x.png +0 -0
  148. package/src/statics/icons/childIcons//345/275/261/345/223/215@3x-2.png +0 -0
  149. package/src/statics/icons/childIcons//345/275/261/345/223/215@3x.png +0 -0
  150. package/src/statics/icons/childIcons//346/204/277/346/231/257/345/256/243/350/250/200@3x.png +0 -0
  151. package/src/statics/icons/childIcons//346/210/230/347/225/245@3x.png +0 -0
  152. package/src/statics/icons/childIcons//346/210/230/347/225/245/344/277/241/346/201/257@3x.png +0 -0
  153. package/src/statics/icons/childIcons//346/210/230/347/225/245/344/277/241/346/201/257/345/233/276@3x-2.png +0 -0
  154. package/src/statics/icons/childIcons//346/210/230/347/225/245/344/277/241/346/201/257/345/233/276@3x.png +0 -0
  155. package/src/statics/icons/childIcons//346/210/230/347/225/245/345/217/202/346/225/260/345/233/276@3x.png +0 -0
  156. package/src/statics/icons/childIcons//346/210/230/347/225/245/345/256/236/351/231/205/346/210/230/347/225/245/351/230/266/346/256/265/345/210/206/347/261/273/350/241/250@3x.png +0 -0
  157. package/src/statics/icons/childIcons//346/210/230/347/225/245/345/256/236/351/231/205/351/203/250/347/275/262/345/233/276@3x.png +0 -0
  158. package/src/statics/icons/childIcons//346/210/230/347/225/245/346/246/202/345/277/265/345/233/276@3x.png +0 -0
  159. package/src/statics/icons/childIcons//346/210/230/347/225/245/346/265/201/347/250/213/345/233/276@3x-2.png +0 -0
  160. package/src/statics/icons/childIcons//346/210/230/347/225/245/346/265/201/347/250/213/345/233/276@3x.png +0 -0
  161. package/src/statics/icons/childIcons//346/210/230/347/225/245/347/212/266/346/200/201/345/233/276@3x.png +0 -0
  162. package/src/statics/icons/childIcons//346/210/230/347/225/245/347/272/246/346/235/237@3x.png +0 -0
  163. package/src/statics/icons/childIcons//346/210/230/347/225/245/347/272/246/346/235/237/345/233/276@3x.png +0 -0
  164. package/src/statics/icons/childIcons//346/210/230/347/225/245/347/272/246/346/235/237/345/256/232/344/271/211/345/233/276@3x.png +0 -0
  165. package/src/statics/icons/childIcons//346/210/230/347/225/245/347/273/223/346/236/204/345/233/276@3x.png +0 -0
  166. package/src/statics/icons/childIcons//346/210/230/347/225/245/350/277/236/351/200/232/345/233/276@3x-2.png +0 -0
  167. package/src/statics/icons/childIcons//346/210/230/347/225/245/350/277/236/351/200/232/345/233/276@3x.png +0 -0
  168. package/src/statics/icons/childIcons//346/210/230/347/225/245/350/277/236/351/200/232/347/237/251/351/230/265@3x-2.png +0 -0
  169. package/src/statics/icons/childIcons//346/210/230/347/225/245/350/277/236/351/200/232/347/237/251/351/230/265@3x.png +0 -0
  170. package/src/statics/icons/childIcons//346/210/230/347/225/245/351/230/266/346/256/265@3x.png +0 -0
  171. package/src/statics/icons/childIcons//346/211/247/350/241/214@3x.png +0 -0
  172. package/src/statics/icons/childIcons//346/211/247/350/241/214/350/200/205/345/206/205/351/203/250/350/277/236/351/200/232/345/233/276@3x.png +0 -0
  173. package/src/statics/icons/childIcons//346/211/247/350/241/214/350/200/205/346/246/202/345/277/265/345/233/276@3x.png +0 -0
  174. package/src/statics/icons/childIcons//346/211/247/350/241/214/350/200/205/347/273/223/346/236/204/345/233/276@3x-2.png +0 -0
  175. package/src/statics/icons/childIcons//346/211/247/350/241/214/350/200/205/347/273/223/346/236/204/345/233/276@3x.png +0 -0
  176. package/src/statics/icons/childIcons//346/211/247/350/241/214/350/200/205/350/277/236/351/200/232/345/233/276@3x.png +0 -0
  177. package/src/statics/icons/childIcons//346/211/277/346/213/205/351/243/216/351/231/251@3x.png +0 -0
  178. package/src/statics/icons/childIcons//346/212/200/346/234/257@3x.png +0 -0
  179. package/src/statics/icons/childIcons//346/213/245/346/234/211/346/265/201/347/250/213@3x.png +0 -0
  180. package/src/statics/icons/childIcons//346/217/220/344/276/233/346/235/203/351/231/220@3x.png +0 -0
  181. package/src/statics/icons/childIcons//346/227/266/346/234/272@3x.png +0 -0
  182. package/src/statics/icons/childIcons//346/234/215/345/212/241@3x.png +0 -0
  183. package/src/statics/icons/childIcons//346/234/215/345/212/241/344/270/216/344/270/232/345/212/241/346/264/273/345/212/250/346/230/240/345/260/204/347/237/251/351/230/265@3x.png +0 -0
  184. package/src/statics/icons/childIcons//346/234/215/345/212/241/344/270/216/346/234/215/345/212/241/345/220/210/345/220/214/346/230/240/345/260/204/347/237/251/351/230/265@3x.png +0 -0
  185. package/src/statics/icons/childIcons//346/234/215/345/212/241/344/270/216/350/203/275/345/212/233/346/230/240/345/260/204/347/237/251/351/230/265@3x.png +0 -0
  186. package/src/statics/icons/childIcons//346/234/215/345/212/241/344/277/241/345/217/267@3x.png +0 -0
  187. package/src/statics/icons/childIcons//346/234/215/345/212/241/345/206/205/351/203/250/346/265/201/347/250/213/345/233/276@3x.png +0 -0
  188. package/src/statics/icons/childIcons//346/234/215/345/212/241/345/206/205/351/203/250/350/277/236/351/200/232/345/233/276@3x.png +0 -0
  189. package/src/statics/icons/childIcons//346/234/215/345/212/241/345/212/237/350/203/275@3x.png +0 -0
  190. package/src/statics/icons/childIcons//346/234/215/345/212/241/345/212/237/350/203/275/345/212/250/344/275/234@3x.png +0 -0
  191. package/src/statics/icons/childIcons//346/234/215/345/212/241/345/212/250/344/275/234@3x.png +0 -0
  192. package/src/statics/icons/childIcons//346/234/215/345/212/241/345/217/202/346/225/260@3x.png +0 -0
  193. package/src/statics/icons/childIcons//346/234/215/345/212/241/345/217/202/346/225/260/345/233/276@3x.png +0 -0
  194. package/src/statics/icons/childIcons//346/234/215/345/212/241/345/257/271/350/261/241/346/265/201@3x.png +0 -0
  195. package/src/statics/icons/childIcons//346/234/215/345/212/241/346/216/245/345/217/243@3x.png +0 -0
  196. package/src/statics/icons/childIcons//346/234/215/345/212/241/346/216/247/345/210/266/346/265/201@3x.png +0 -0
  197. package/src/statics/icons/childIcons//346/234/215/345/212/241/346/246/202/345/277/265/345/233/276@3x.png +0 -0
  198. package/src/statics/icons/childIcons//346/234/215/345/212/241/346/246/202/345/277/265/350/241/250@3x.png +0 -0
  199. package/src/statics/icons/childIcons//346/234/215/345/212/241/346/265/201/347/250/213/345/233/276@3x.png +0 -0
  200. package/src/statics/icons/childIcons//346/234/215/345/212/241/347/212/266/346/200/201/345/233/276@3x.png +0 -0
  201. package/src/statics/icons/childIcons//346/234/215/345/212/241/347/253/257/345/217/243@3x.png +0 -0
  202. package/src/statics/icons/childIcons//346/234/215/345/212/241/347/272/246/346/235/237/345/233/276@3x.png +0 -0
  203. package/src/statics/icons/childIcons//346/234/215/345/212/241/347/272/246/346/235/237/345/256/232/344/271/211/345/233/276@3x.png +0 -0
  204. package/src/statics/icons/childIcons//346/234/215/345/212/241/347/273/223/346/236/204@3x.png +0 -0
  205. package/src/statics/icons/childIcons//346/234/215/345/212/241/347/273/223/346/236/204/345/233/276@3x.png +0 -0
  206. package/src/statics/icons/childIcons//346/234/215/345/212/241/350/247/222/350/211/262@3x.png +0 -0
  207. package/src/statics/icons/childIcons//346/234/215/345/212/241/350/267/257/347/272/277/345/233/276@3x.png +0 -0
  208. package/src/statics/icons/childIcons//346/234/215/345/212/241/350/277/236/346/216/245/345/231/250@3x.png +0 -0
  209. package/src/statics/icons/childIcons//346/234/215/345/212/241/350/277/236/351/200/232/345/233/276@3x.png +0 -0
  210. package/src/statics/icons/childIcons//346/234/215/345/212/241/350/277/236/351/200/232/350/241/250@3x-2.png +0 -0
  211. package/src/statics/icons/childIcons//346/234/215/345/212/241/350/277/236/351/200/232/350/241/250@3x.png +0 -0
  212. package/src/statics/icons/childIcons//346/235/203/351/231/220@3x.png +0 -0
  213. package/src/statics/icons/childIcons//346/235/241/344/273/266@3x.png +0 -0
  214. package/src/statics/icons/childIcons//346/240/207/345/207/206/344/270/232/345/212/241/346/264/273/345/212/250@3x.png +0 -0
  215. package/src/statics/icons/childIcons//346/240/207/345/207/206/346/246/202/345/277/265/345/233/276@3x.png +0 -0
  216. package/src/statics/icons/childIcons//346/240/207/345/207/206/346/246/202/345/277/265/350/241/250@3x.png +0 -0
  217. package/src/statics/icons/childIcons//346/240/207/345/207/206/347/273/223/346/236/204/345/233/276@3x.png +0 -0
  218. package/src/statics/icons/childIcons//346/240/207/345/207/206/350/267/257/347/272/277/345/233/276@3x.png +0 -0
  219. package/src/statics/icons/childIcons//346/240/207/345/207/206/350/277/275/346/272/257/345/233/276@3x.png +0 -0
  220. package/src/statics/icons/childIcons//346/246/202/345/277/265/350/247/222/350/211/262@3x.png +0 -0
  221. package/src/statics/icons/childIcons//346/263/233/345/214/226@3x.png +0 -0
  222. package/src/statics/icons/childIcons//347/212/266/345/206/265@3x.png +0 -0
  223. package/src/statics/icons/childIcons//347/216/257/345/242/203@3x.png +0 -0
  224. package/src/statics/icons/childIcons//347/233/270/346/257/224@3x (1).png +0 -0
  225. package/src/statics/icons/childIcons//347/263/273/347/273/237@3x.png +0 -0
  226. package/src/statics/icons/childIcons//347/273/204/346/210/220@3x.png +0 -0
  227. package/src/statics/icons/childIcons//347/273/204/347/273/207@3x.png +0 -0
  228. package/src/statics/icons/childIcons//347/273/204/347/273/207/351/230/266/346/256/265@3x.png +0 -0
  229. package/src/statics/icons/childIcons//347/273/221/345/256/232/350/277/236/346/216/245/345/231/250@3x.png +0 -0
  230. package/src/statics/icons/childIcons//347/274/223/350/247/243@3x.png +0 -0
  231. package/src/statics/icons/childIcons//350/201/214/344/275/215@3x.png +0 -0
  232. package/src/statics/icons/childIcons//350/201/232/345/220/210@3x.png +0 -0
  233. package/src/statics/icons/childIcons//350/203/275/345/212/233@3x.png +0 -0
  234. package/src/statics/icons/childIcons//350/203/275/345/212/233/350/201/214/350/264/243@3x.png +0 -0
  235. package/src/statics/icons/childIcons//350/203/275/345/212/233/351/205/215/347/275/256@3x.png +0 -0
  236. package/src/statics/icons/childIcons//350/203/275/345/244/237/350/203/234/344/273/273@3x.png +0 -0
  237. package/src/statics/icons/childIcons//350/207/252/347/204/266/350/265/204/346/272/220@3x.png +0 -0
  238. package/src/statics/icons/childIcons//350/246/201/346/261/202@3x.png +0 -0
  239. package/src/statics/icons/childIcons//350/256/276/347/275/256/347/261/273/345/236/213.png +0 -0
  240. package/src/statics/icons/childIcons//350/264/237/350/264/243@3x.png +0 -0
  241. package/src/statics/icons/childIcons//350/264/243/344/273/273@3x.png +0 -0
  242. package/src/statics/icons/childIcons//350/265/204/344/272/247/351/243/216/351/231/251/346/230/240/345/260/204/347/237/251/351/230/265@3x.png +0 -0
  243. package/src/statics/icons/childIcons//350/265/204/346/272/220/344/270/216/344/270/232/345/212/241/346/264/273/345/212/250/346/230/240/345/260/204/347/237/251/351/230/265@3x.png +0 -0
  244. package/src/statics/icons/childIcons//350/265/204/346/272/220/344/270/216/350/203/275/345/212/233/346/230/240/345/260/204/347/237/251/351/230/265@3x.png +0 -0
  245. package/src/statics/icons/childIcons//350/265/204/346/272/220/344/277/241/345/217/267@3x.png +0 -0
  246. package/src/statics/icons/childIcons//350/265/204/346/272/220/344/277/241/346/201/257@3x.png +0 -0
  247. package/src/statics/icons/childIcons//350/265/204/346/272/220/344/277/241/346/201/257/345/233/276@3x.png +0 -0
  248. package/src/statics/icons/childIcons//350/265/204/346/272/220/345/206/205/351/203/250/346/265/201/347/250/213/345/233/276@3x.png +0 -0
  249. package/src/statics/icons/childIcons//350/265/204/346/272/220/345/206/205/351/203/250/350/277/236/351/200/232/345/233/276@3x.png +0 -0
  250. package/src/statics/icons/childIcons//350/265/204/346/272/220/345/212/250/344/275/234@3x.png +0 -0
  251. package/src/statics/icons/childIcons//350/265/204/346/272/220/345/217/202/346/225/260@3x.png +0 -0
  252. package/src/statics/icons/childIcons//350/265/204/346/272/220/345/217/202/346/225/260/345/233/276@3x.png +0 -0
  253. package/src/statics/icons/childIcons//350/265/204/346/272/220/345/267/245/344/273/266@3x.png +0 -0
  254. package/src/statics/icons/childIcons//350/265/204/346/272/220/346/216/245/345/217/243@3x.png +0 -0
  255. package/src/statics/icons/childIcons//350/265/204/346/272/220/346/234/215/345/212/241@3x.png +0 -0
  256. package/src/statics/icons/childIcons//350/265/204/346/272/220/346/236/266/346/236/204@3x.png +0 -0
  257. package/src/statics/icons/childIcons//350/265/204/346/272/220/346/246/202/345/277/265/345/233/276@3x.png +0 -0
  258. package/src/statics/icons/childIcons//350/265/204/346/272/220/346/246/202/345/277/265/350/241/250@3x.png +0 -0
  259. package/src/statics/icons/childIcons//350/265/204/346/272/220/346/264/273/345/212/250/346/265/201/347/250/213/345/233/276@3x.png +0 -0
  260. package/src/statics/icons/childIcons//350/265/204/346/272/220/346/264/273/345/212/250/347/273/223/346/236/204/345/233/276@3x.png +0 -0
  261. package/src/statics/icons/childIcons//350/265/204/346/272/220/346/265/201/347/250/213/345/233/276@3x.png +0 -0
  262. package/src/statics/icons/childIcons//350/265/204/346/272/220/347/212/266/346/200/201/345/233/276@3x.png +0 -0
  263. package/src/statics/icons/childIcons//350/265/204/346/272/220/347/253/257/345/217/243@3x.png +0 -0
  264. package/src/statics/icons/childIcons//350/265/204/346/272/220/347/272/246/346/235/237@3x.png +0 -0
  265. package/src/statics/icons/childIcons//350/265/204/346/272/220/347/273/223/346/236/204@3x.png +0 -0
  266. package/src/statics/icons/childIcons//350/265/204/346/272/220/347/273/223/346/236/204/345/233/276@3x.png +0 -0
  267. package/src/statics/icons/childIcons//350/265/204/346/272/220/347/274/223/350/247/243/346/216/252/346/226/275@3x.png +0 -0
  268. package/src/statics/icons/childIcons//350/265/204/346/272/220/350/247/222/350/211/262@3x.png +0 -0
  269. package/src/statics/icons/childIcons//350/265/204/346/272/220/350/277/236/346/216/245/345/231/250@3x.png +0 -0
  270. package/src/statics/icons/childIcons//350/265/204/346/272/220/350/277/236/351/200/232/345/233/276@3x.png +0 -0
  271. package/src/statics/icons/childIcons//350/265/204/346/272/220/350/277/236/351/200/232/350/241/250@3x.png +0 -0
  272. package/src/statics/icons/childIcons//350/275/257/344/273/266@3x.png +0 -0
  273. package/src/statics/icons/childIcons//350/276/223/345/205/245/346/240/223@3x.png +0 -0
  274. package/src/statics/icons/childIcons//350/276/223/345/207/272/346/240/223@3x.png +0 -0
  275. package/src/statics/icons/childIcons//350/276/276/345/210/260@3x.png +0 -0
  276. package/src/statics/icons/childIcons//350/277/236/346/216/245/345/231/250@3x.png +0 -0
  277. package/src/statics/icons/childIcons//350/277/236/347/272/277@3x.png +0 -0
  278. package/src/statics/icons/childIcons//351/207/214/347/250/213/347/242/221/344/276/235/350/265/226@3x.png +0 -0
  279. package/src/statics/icons/childIcons//351/230/266/346/256/265@3x.png +0 -0
  280. package/src/statics/icons/childIcons//351/234/200/346/261/202/346/235/203/351/231/220@3x.png +0 -0
  281. package/src/statics/icons/childIcons//351/241/266/347/272/247/344/270/232/345/212/241/346/246/202/345/277/265/345/233/276@3x.png +0 -0
  282. package/src/statics/icons/childIcons//351/241/271/347/233/256@3x.png +0 -0
  283. package/src/statics/icons/childIcons//351/241/271/347/233/256/344/270/273/351/242/230@3x.png +0 -0
  284. package/src/statics/icons/childIcons//351/241/271/347/233/256/345/206/205/351/203/250/346/265/201/347/250/213/345/233/276@3x.png +0 -0
  285. package/src/statics/icons/childIcons//351/241/271/347/233/256/346/246/202/345/277/265/345/233/276@3x.png +0 -0
  286. package/src/statics/icons/childIcons//351/241/271/347/233/256/346/246/202/345/277/265/350/241/250@3x.png +0 -0
  287. package/src/statics/icons/childIcons//351/241/271/347/233/256/346/264/273/345/212/250@3x.png +0 -0
  288. package/src/statics/icons/childIcons//351/241/271/347/233/256/346/264/273/345/212/250/344/270/216/350/203/275/345/212/233/347/232/204/346/230/240/345/260/204/347/237/251/351/230/265@3x.png +0 -0
  289. package/src/statics/icons/childIcons//351/241/271/347/233/256/346/264/273/345/212/250/345/212/250/344/275/234@3x.png +0 -0
  290. package/src/statics/icons/childIcons//351/241/271/347/233/256/346/265/201/347/250/213/345/233/276@3x.png +0 -0
  291. package/src/statics/icons/childIcons//351/241/271/347/233/256/347/273/223/346/236/204/345/233/276@3x.png +0 -0
  292. package/src/statics/icons/childIcons//351/241/271/347/233/256/350/247/222/350/211/262@3x.png +0 -0
  293. package/src/statics/icons/childIcons//351/241/271/347/233/256/350/267/257/347/272/277/345/233/276@3x.png +0 -0
  294. package/src/statics/icons/childIcons//351/241/271/347/233/256/350/277/236/351/200/232/345/233/276@3x.png +0 -0
  295. package/src/statics/icons/childIcons//351/241/271/347/233/256/351/207/214/347/250/213/347/242/221@3x.png +0 -0
  296. package/src/statics/icons/childIcons//351/241/271/347/233/256/351/207/214/347/250/213/347/242/221/350/247/222/350/211/262@3x.png +0 -0
  297. package/src/statics/icons/childIcons//351/241/272/345/272/217@3x-2.png +0 -0
  298. package/src/statics/icons/childIcons//351/241/272/345/272/217@3x.png +0 -0
  299. package/src/statics/icons/childIcons//351/243/216/351/231/251@3x.png +0 -0
  300. package/src/statics/icons/childIcons//351/243/216/351/231/251/344/270/216/345/256/211/345/205/250/346/216/247/345/210/266/346/230/240/345/260/204/347/237/251/351/230/265@3x.png +0 -0
  301. package/src/statics/icons/childIcons//351/253/230/347/272/247/344/270/232/345/212/241/346/246/202/345/277/265@3x.png +0 -0
  302. package/src/statics/icons/createMenu/config.png +0 -0
  303. package/src/statics/icons/createMenu/contact.png +0 -0
  304. package/src/statics/icons/createMenu/copy.png +0 -0
  305. package/src/statics/icons/createMenu/delete.png +0 -0
  306. package/src/statics/icons/createMenu/diagram.png +0 -0
  307. package/src/statics/icons/createMenu/element.png +0 -0
  308. package/src/statics/icons/createMenu/locateChart.png +0 -0
  309. package/src/statics/icons/createMenu/paste.png +0 -0
  310. package/src/statics/icons/createMenu/rename.png +0 -0
  311. package/src/statics/icons/createMenu/scissors.png +0 -0
  312. package/src/store/eventBus.ts +38 -0
  313. package/src/store/graphStore.ts +782 -0
  314. package/src/store/index.ts +8 -0
  315. package/src/style/index.css +1 -0
  316. package/src/style/tailwind.css +33 -0
  317. package/src/types/index.ts +200 -0
  318. package/src/utils/autoExpandParent.ts +94 -0
  319. package/src/utils/colorUtils.ts +137 -0
  320. package/src/utils/compartment.ts +534 -0
  321. package/src/utils/containers.ts +910 -0
  322. package/src/utils/diagram.ts +403 -0
  323. package/src/utils/dom.ts +21 -0
  324. package/src/utils/drag.ts +224 -0
  325. package/src/utils/edgeUtils.ts +787 -0
  326. package/src/utils/geom.ts +177 -0
  327. package/src/utils/graphDragService.ts +329 -0
  328. package/src/utils/highlightUtils.ts +162 -0
  329. package/src/utils/hittest.ts +135 -0
  330. package/src/utils/iconLoader.ts +105 -0
  331. package/src/utils/index.ts +20 -0
  332. package/src/utils/packgeMap.ts +1 -0
  333. package/src/utils/pinUtils.ts +484 -0
  334. package/src/utils/policy.ts +212 -0
  335. package/src/utils/zorder.ts +39 -0
  336. package/src/view/graph.vue +419 -0
  337. package/src/vite-env.d.ts +33 -0
@@ -0,0 +1,910 @@
1
+ // 画布容器/层级/命中等通用工具
2
+ import type { Shape } from '../types'
3
+ import {
4
+ allowAsParent,
5
+ ensureCompartmentAfterParentMoved,
6
+ getCompartmentZones,
7
+ isCompartment,
8
+ } from '../utils/compartment'
9
+ import { getBounds } from './geom'
10
+ import { canNest } from './policy';
11
+ import { useGraphStore } from '../store/graphStore'
12
+ /** 统一的几何矩形类型 */
13
+ export type Rect = { x: number; y: number; width: number; height: number }
14
+ /** 更新回调签名( */
15
+ export type Updater = (id: string, updates: Partial<Shape>) => void
16
+
17
+ /** 安全读取 zIndex(未设置时为 0)*/
18
+ const getZ = (s?: Shape) =>
19
+ (typeof s?.style?.zIndex === 'number' ? (s!.style!.zIndex as number) : 0)
20
+
21
+ /** 把 bounds-like 转为 Rect */
22
+ const toRect = (b: any): Rect => ({
23
+ x: b?.x ?? 0,
24
+ y: b?.y ?? 0,
25
+ width: b?.width ?? 0,
26
+ height: b?.height ?? 0,
27
+ })
28
+ /** 取得 shape 的矩形(兼容不同 shape 的 bounds 结构) */
29
+ const rectOf = (s: Shape): Rect => toRect(getBounds(s))
30
+ // 布局/内容区工具
31
+
32
+ /** 读取内边距(padding)。支持 style.padding 为数字;默认 20 */
33
+ export const readPadding = (s?: Shape) => {
34
+ const n = typeof s?.style?.padding === 'number' ? (s!.style!.padding as number) : 20
35
+ // 如果是“普通容器”(非隔间),强制 top = 70,其它方向仍然用 n
36
+ if (s && !isCompartment(s)) {
37
+ return {
38
+ top: 75, // 固定预留 70px 的上内边距
39
+ right: n,
40
+ bottom: n,
41
+ left: n,
42
+ }
43
+ }
44
+
45
+ // 如果是隔间 / 或者没有传 s 就用原来的逻辑
46
+ return {
47
+ top: n,
48
+ right: n,
49
+ bottom: n,
50
+ left: n,
51
+ }
52
+ }
53
+
54
+ /** 获取父容器的“内容区”(内边距之后的可放置区域) */
55
+ export const getContentRect = (parent: Shape) => {
56
+ const pad = readPadding(parent)
57
+ const px = parent.bounds?.x ?? 0
58
+ const py = parent.bounds?.y ?? 0
59
+ const pw = parent.bounds?.width ?? 0
60
+ const ph = parent.bounds?.height ?? 0
61
+ return {
62
+ left: px + pad.left,
63
+ top: py + pad.top,
64
+ right: px + pw - pad.right,
65
+ bottom: py + ph - pad.bottom,
66
+ }
67
+ }
68
+
69
+ // 关系/后代
70
+
71
+ /** 判断 childId 是否是 ancestorId 的后代 */
72
+ export const isDescendant = (shapes: Shape[], childId: string, ancestorId: string) => {
73
+ let p = shapes.find(s => s.id === childId)?.parenShapeId ?? null
74
+ while (p) {
75
+ if (p === ancestorId) return true
76
+ p = shapes.find(s => s.id === p)?.parenShapeId ?? null
77
+ }
78
+ return false
79
+ }
80
+
81
+ /** 收集整棵子树(仅 id) */
82
+ export const collectDescendantIds = (shapes: Shape[], parentId: string): string[] => {
83
+ const out: string[] = []
84
+ const stack = [parentId]
85
+ while (stack.length) {
86
+ const id = stack.pop()!
87
+ const kids = shapes.filter(s => s.parenShapeId === id)
88
+ kids.forEach(k => { out.push(k.id); stack.push(k.id) })
89
+ }
90
+ return out
91
+ }
92
+ /** 计算 child 与 region 的相交面积(region 是一个 {left,top,right,bottom}) */
93
+ export const intersectArea = (
94
+ child: Rect,
95
+ region: { left: number; top: number; right: number; bottom: number }
96
+ ) => {
97
+ const ix = Math.max(child.x, region.left)
98
+ const iy = Math.max(child.y, region.top)
99
+ const ax = Math.min(child.x + child.width, region.right)
100
+ const ay = Math.min(child.y + child.height, region.bottom)
101
+ const w = Math.max(0, ax - ix)
102
+ const h = Math.max(0, ay - iy)
103
+ return w * h
104
+ }
105
+
106
+ /** 计算子矩形在父“内容区”内的覆盖率(0~1) */
107
+ export const coverageIn = (child: Rect, parent: Shape) => {
108
+ const pc = getContentRect(parent)
109
+ const overlap = intersectArea(child, pc)
110
+ const childArea = Math.max(1, child.width * child.height)
111
+ return overlap / childArea
112
+ }
113
+
114
+ /** 把 child 夹回 parent 的内容区(不改变尺寸) */
115
+ export const clampChildIntoParent = (
116
+ parent: Shape,
117
+ child: Rect,
118
+ alignLarge: 'center' | 'start' | 'end' = 'center'
119
+ ): Rect => {
120
+ const c = getContentRect(parent)
121
+ const cw = Math.max(0, c.right - c.left)
122
+ const ch = Math.max(0, c.bottom - c.top)
123
+
124
+ let nx: number
125
+ let ny: number
126
+
127
+ // 水平方向
128
+ if (child.width <= cw) {
129
+ nx = Math.min(Math.max(child.x, c.left), c.right - child.width)
130
+ } else {
131
+ if (alignLarge === 'center') nx = c.left + (cw - child.width) / 2
132
+ else if (alignLarge === 'start') nx = c.left
133
+ else nx = c.right - child.width
134
+ }
135
+
136
+ // 垂直方向
137
+ if (child.height <= ch) {
138
+ ny = Math.min(Math.max(child.y, c.top), c.bottom - child.height)
139
+ } else {
140
+ if (alignLarge === 'center') ny = c.top + (ch - child.height) / 2
141
+ else if (alignLarge === 'start') ny = c.top
142
+ else ny = c.bottom - child.height
143
+ }
144
+
145
+ return { x: nx, y: ny, width: child.width, height: child.height }
146
+ }
147
+
148
+ /**
149
+ * 让 parent “刚好包下所有直接子 + padding”
150
+ * - 仅增大,不缩小;必要时向左/上扩张(同时移动 x/y)
151
+ * - 采用 floor-ε 抑制抖动:避免 0.x 的毛刺每次都向上取整导致“慢慢变大”
152
+ */
153
+ export const ensureParentFitsChildren = (
154
+ shapes: Shape[],
155
+ parent: Shape,
156
+ updateShape: Updater,
157
+ overrideRects?: Record<string, Rect>
158
+ ) => {
159
+ const kids = shapes.filter(
160
+ s => s.parenShapeId === parent.id && !isPinShape(s)
161
+ )
162
+ if (kids.length === 0) return
163
+
164
+ const pad = readPadding(parent)
165
+ const EPS = 1.25
166
+
167
+ let left = Infinity, top = Infinity
168
+ let right = -Infinity, bot = -Infinity
169
+ for (const k of kids) {
170
+ const r = overrideRects?.[k.id] ?? rectOf(k)
171
+ left = Math.min(left, r.x)
172
+ top = Math.min(top, r.y)
173
+ right = Math.max(right, r.x + r.width)
174
+ bot = Math.max(bot, r.y + r.height)
175
+ }
176
+
177
+ const px = parent.bounds.x ?? 0
178
+ const py = parent.bounds.y ?? 0
179
+ const pw = parent.bounds.width ?? 0
180
+ const ph = parent.bounds.height ?? 0
181
+
182
+ // 当前内容区
183
+ const cL = px + pad.left
184
+ const cT = py + pad.top
185
+ const cR = px + pw - pad.right
186
+ const cB = py + ph - pad.bottom
187
+
188
+ // 需要增长(四向,未截断)
189
+ const dL = Math.max(0, cL - left)
190
+ const dT = Math.max(0, cT - top)
191
+ const dR = Math.max(0, right - cR)
192
+ const dB = Math.max(0, bot - cB)
193
+
194
+ if (dL < EPS && dT < EPS && dR < EPS && dB < EPS) return
195
+
196
+ const down = (v: number, eps = EPS) => Math.max(0, Math.floor(v - eps))
197
+ let growL = down(dL)
198
+ let growT = down(dT)
199
+ const growR = down(dR)
200
+ const growB = down(dB)
201
+
202
+ // === 关键:左/上不允许越过 (0,0),截断扩量 ===
203
+ // 位置候选(未截断)
204
+ let nx = px - growL
205
+ let ny = py - growT
206
+
207
+ // 如果会越界,就把扩量截断到恰好贴 0
208
+ if (nx < 0) {
209
+ growL = Math.min(growL, px) // 最多把 x 拉到 0
210
+ nx = px - growL // 重新计算截断后的 nx
211
+ }
212
+ if (ny < 0) {
213
+ growT = Math.min(growT, py) // 最多把 y 拉到 0
214
+ ny = py - growT
215
+ }
216
+
217
+ // 宽/高按对称扩:左右/上下扩量之和
218
+ const needW = Math.max(pw, pw + growL + growR)
219
+ const needH = Math.max(ph, ph + growT + growB)
220
+
221
+ // 最终再夹一次,确保不越 0(双保险)
222
+ nx = Math.max(0, nx)
223
+ ny = Math.max(0, ny)
224
+
225
+ if (nx !== px || ny !== py || needW !== pw || needH !== ph) {
226
+ const snap = (v: number) => Math.round(v)
227
+ updateShape(parent.id, {
228
+ bounds: { x: snap(nx), y: snap(ny), width: snap(needW), height: snap(needH) }
229
+ })
230
+ }
231
+ }
232
+ /** 从当前父开始,逐层向上做 ensureParentFitsChildren */
233
+ export const bubbleAutoFitUp = (
234
+ shapes: Shape[],
235
+ startParentId: string | null | undefined,
236
+ updateShape: Updater
237
+ ) => {
238
+ let pid = startParentId ?? null
239
+ while (pid) {
240
+ const p = shapes.find(s => s.id === pid)
241
+ if (!p) break
242
+ ensureParentFitsChildren(shapes, p, updateShape)
243
+ pid = p.parenShapeId ?? null
244
+ }
245
+ }
246
+ /**
247
+ * 挂接:把 child 挂到 parent 下,同时把“整棵子树的 zIndex”平移为 parent.z + 1(相对层级不变)
248
+ * - 注意:这里只负责数据层,不做归一化(normalizeZOrder 建议在调用方统一执行)
249
+ */
250
+ export const attachToParent = (
251
+ shapes: Shape[],
252
+ childId: string,
253
+ parentId: string,
254
+ updateShape: Updater
255
+ ) => {
256
+ const child = shapes.find(s => s.id === childId)
257
+ const parent = shapes.find(s => s.id === parentId)
258
+ if (!child || !parent) return
259
+ if (isDescendant(shapes, parentId, childId)) return // 防环
260
+
261
+ // 目标:child 的 z = parent.z + 1,并平移整棵子树
262
+ const parentZ = getZ(parent)
263
+ const childZ = getZ(child)
264
+ const wantChildZ = parentZ + 1
265
+ const delta = wantChildZ - childZ
266
+
267
+ const subtreeIds = [childId, ...collectDescendantIds(shapes, childId)]
268
+ if (delta !== 0) {
269
+ for (const id of subtreeIds) {
270
+ const n = shapes.find(s => s.id === id)!
271
+ const nz = getZ(n) + delta
272
+ updateShape(id, { style: { ...(n.style ?? {}), zIndex: nz } as any })
273
+ }
274
+ }
275
+
276
+ // 最后落盘父子关系(避免中途触发 watch 干扰)
277
+ updateShape(childId, { parenShapeId: parentId, diagramId: parent.diagramId })
278
+ }
279
+
280
+ /** 脱离父子关系(仅清 parenShapeId) */
281
+ export const detachFromParent = (
282
+ shapes: Shape[],
283
+ childId: string,
284
+ updateShape: Updater
285
+ ) => {
286
+ const child = shapes.find(s => s.id === childId)
287
+ if (!child || !child.parenShapeId) return
288
+ updateShape(childId, { parenShapeId: undefined })
289
+ }
290
+
291
+ /**
292
+ * (旧)自某节点起,强制“子 = 父+1”
293
+ */
294
+ export const cascadeZIndexFrom = (
295
+ shapes: Shape[],
296
+ startId: string,
297
+ updateShape: Updater
298
+ ) => {
299
+ const start = shapes.find(s => s.id === startId); if (!start) return
300
+ const queue: Shape[] = [start]
301
+
302
+ while (queue.length) {
303
+ const parent = queue.shift()!
304
+ const pz = getZ(parent)
305
+ const kids = shapes.filter(s => s.parenShapeId === parent.id)
306
+
307
+ kids.forEach(k => {
308
+ const target = pz + 1
309
+ if (getZ(k) !== target) {
310
+ updateShape(k.id, { style: { ...(k.style ?? {}), zIndex: target } as any })
311
+ }
312
+ queue.push(k)
313
+ })
314
+ }
315
+ }
316
+
317
+ /** 计算“直接子元素”的联合包围盒 */
318
+ export const childrenUnionBounds = (
319
+ shapes: Shape[],
320
+ parentId: string,
321
+ overrideRects?: Record<string, Rect>
322
+ ) => {
323
+ const kids = shapes.filter(
324
+ s => s.parenShapeId === parentId && !isPinShape(s)
325
+ )
326
+ if (!kids.length) return null
327
+ let L = Infinity, T = Infinity, R = -Infinity, B = -Infinity
328
+ for (const k of kids) {
329
+ const r = overrideRects?.[k.id] ?? getBounds(k)
330
+ L = Math.min(L, r.x)
331
+ T = Math.min(T, r.y)
332
+ R = Math.max(R, r.x + r.width)
333
+ B = Math.max(B, r.y + r.height)
334
+ }
335
+ return { L, T, R, B }
336
+ }
337
+
338
+ /**
339
+ * 在“缩放父元素”手势中,约束 next:content(next) 覆盖 childrenUnion ± gap
340
+ * - dir 决定修正哪条边:左/上需要改 x/y 和宽高;右/下只改宽高
341
+ */
342
+ export const clampParentRectToChildrenGap = (
343
+ shapes: Shape[],
344
+ parent: Shape,
345
+ next: Rect,
346
+ dir: 'nw' | 'ne' | 'sw' | 'se',
347
+ gap = 0,
348
+ minW = 1,
349
+ minH = 1,
350
+ overrideRects?: Record<string, Rect>
351
+ ): Rect => {
352
+ const union = childrenUnionBounds(shapes, parent.id, overrideRects)
353
+ if (!union) return next
354
+
355
+ const pad = readPadding(parent)
356
+ // 目标内容区
357
+ const reqL = union.L - gap
358
+ const reqT = union.T - gap
359
+ const reqR = union.R + gap
360
+ const reqB = union.B + gap
361
+
362
+ // 现状内容区
363
+ let cL = next.x + pad.left
364
+ let cT = next.y + pad.top
365
+ let cR = next.x + next.width - pad.right
366
+ let cB = next.y + next.height - pad.bottom
367
+
368
+ // 左侧
369
+ if ((dir === 'nw' || dir === 'sw') && cL > reqL) {
370
+ const newX = reqL - pad.left
371
+ next = { ...next, x: newX, width: Math.max((next.x + next.width) - newX, minW) }
372
+ }
373
+ // 上侧
374
+ if ((dir === 'nw' || dir === 'ne') && cT > reqT) {
375
+ const newY = reqT - pad.top
376
+ next = { ...next, y: newY, height: Math.max((next.y + next.height) - newY, minH) }
377
+ }
378
+ // 右侧
379
+ cR = next.x + next.width - pad.right
380
+ if ((dir === 'ne' || dir === 'se') && cR < reqR) {
381
+ const newW = (reqR + pad.right) - next.x
382
+ next = { ...next, width: Math.max(newW, minW) }
383
+ }
384
+ // 下侧
385
+ cB = next.y + next.height - pad.bottom
386
+ if ((dir === 'sw' || dir === 'se') && cB < reqB) {
387
+ const newH = (reqB + pad.bottom) - next.y
388
+ next = { ...next, height: Math.max(newH, minH) }
389
+ }
390
+ return next
391
+ }
392
+
393
+ /**
394
+ * 直接把 parent 扩到“刚好 ≥ 子并集 + gap”(对称扩容)
395
+ * - 注意:这是“立即扩父”的版本,通常用于非交互状态下的矫正或强制收口
396
+ */
397
+ export const ensureParentCoversChildrenWithGap = (
398
+ shapes: Shape[],
399
+ parent: Shape,
400
+ updateShape: Updater,
401
+ gap = 0,
402
+ overrideRects?: Record<string, Rect>
403
+ ) => {
404
+ const union = childrenUnionBounds(shapes, parent.id, overrideRects)
405
+ if (!union) return
406
+
407
+ const pad = readPadding(parent)
408
+ const px = parent.bounds.x ?? 0
409
+ const py = parent.bounds.y ?? 0
410
+ const pw = parent.bounds.width ?? 0
411
+ const ph = parent.bounds.height ?? 0
412
+
413
+ const reqL = union.L - gap
414
+ const reqT = union.T - gap
415
+ const reqR = union.R + gap
416
+ const reqB = union.B + gap
417
+
418
+ const cL = px + pad.left
419
+ const cT = py + pad.top
420
+ const cR = px + pw - pad.right
421
+ const cB = py + ph - pad.bottom
422
+
423
+ const needL = Math.max(0, reqL - cL)
424
+ const needT = Math.max(0, reqT - cT)
425
+ const needR = Math.max(0, reqR - cR)
426
+ const needB = Math.max(0, reqB - cB)
427
+ if (needL <= 0 && needT <= 0 && needR <= 0 && needB <= 0) return
428
+
429
+ const nx = px - Math.ceil(needL)
430
+ const ny = py - Math.ceil(needT)
431
+ const nw = Math.max(pw, pw + Math.ceil(needL) + Math.ceil(needR))
432
+ const nh = Math.max(ph, ph + Math.ceil(needT) + Math.ceil(needB))
433
+ const snap = (v: number) => Math.round(v)
434
+ updateShape(parent.id, { bounds: { x: snap(nx), y: snap(ny), width: snap(nw), height: snap(nh) } })
435
+ }
436
+
437
+ /** 从当前父起,逐层把所有祖先都扩到“刚好 ≥ 子并集 + gap” */
438
+ export const bubbleCoverWithGapUp = (
439
+ shapes: Shape[],
440
+ startParentId: string | null | undefined,
441
+ updateShape: Updater,
442
+ gap = 0
443
+ ) => {
444
+ let pid = startParentId ?? null
445
+ while (pid) {
446
+ const p = shapes.find(s => s.id === pid)
447
+ if (!p) break
448
+ ensureParentCoversChildrenWithGap(shapes, p, updateShape, gap)
449
+ pid = p.parenShapeId ?? null
450
+ }
451
+ }
452
+
453
+ /** 轻微越界容忍(px) */
454
+ export const CONTENT_EPS = 3
455
+
456
+ /** 计算“最终矩形”(ghost 优先,兜底实体 bounds) */
457
+ export const finalRectOf = (ghost: Record<string, Rect>, s: Shape): Rect => {
458
+ const g = ghost[s.id]
459
+ return g ? g : {
460
+ x: s.bounds?.x ?? 0,
461
+ y: s.bounds?.y ?? 0,
462
+ width: s.bounds?.width ?? 0,
463
+ height: s.bounds?.height ?? 0,
464
+ }
465
+ }
466
+ /** 计算 childRect 相对 parent 内容区的“最大越界量”(>0 表示越界) */
467
+ export const overflowIn = (parent: Shape, childRect: Rect) => {
468
+ const c = getContentRect(parent)
469
+ const overL = c.left - childRect.x
470
+ const overT = c.top - childRect.y
471
+ const overR = (childRect.x + childRect.width) - c.right
472
+ const overB = (childRect.y + childRect.height) - c.bottom
473
+ return Math.max(0, overL, overT, overR, overB)
474
+ }
475
+
476
+ /** 轻微越界 → 夹回;显著越界 → 扩父(并向上冒泡) */
477
+ export const clampOrGrowIntoParent = (
478
+ parent: Shape,
479
+ childRect: Rect,
480
+ shapes: Shape[],
481
+ updateShape: Updater,
482
+ childId: string,
483
+ eps = CONTENT_EPS
484
+ ) => {
485
+ const overflow = overflowIn(parent, childRect)
486
+ if (overflow <= eps) {
487
+ const clamped = clampChildIntoParent(parent, childRect)
488
+ updateShape(childId, { bounds: clamped })
489
+ } else {
490
+ ensureParentFitsChildren(shapes, parent, updateShape, { [childId]: childRect })
491
+ bubbleAutoFitUp(shapes, parent.parenShapeId, updateShape)
492
+ }
493
+ }
494
+
495
+ /** 组拖辅助:该节点是否有“正在拖动”的祖先(有则禁止 reparent) */
496
+ export const hasDraggedAncestor = (
497
+ shapes: Shape[],
498
+ sid: string,
499
+ draggingSet: Set<string>
500
+ ) => {
501
+ let pid = shapes.find(s => s.id === sid)?.parenShapeId ?? null
502
+ while (pid) {
503
+ if (draggingSet.has(pid)) return true
504
+ pid = shapes.find(s => s.id === pid)?.parenShapeId ?? null
505
+ }
506
+ return false
507
+ }
508
+ /**
509
+ * “拖拽结束后的归属策略”:封装了保留原父/候选父/夹回/扩父的完整流程
510
+ * - keptParent 优先:只要仍在原父内部,就不找候选父
511
+ * - 不在原父:找候选父(exclude = 正在拖的 id)
512
+ * - 组拖后代:禁止 reparent;只夹回/必要才扩父
513
+ * - 返回:是否发生 reparent(可用于统计/调试)
514
+ */
515
+ const DETACH_THRESHOLD = 0.4 // 覆盖率低于该阈值就脱离,不再扩父
516
+ /** 哪些图元一旦挂上父,就不允许脱离父(拖动时只能让父扩容) */
517
+ export const isStickyChild = (s: Shape): boolean => {
518
+ if (!Object.prototype.hasOwnProperty.call(s, 'isMovableComparents')) {
519
+ return false
520
+ }
521
+ // 只有为 true 时才视为不能脱离
522
+ return (s as any).isMovableComparents == true
523
+ }
524
+ export const resolveParentAfterDrag = (
525
+ shapes: Shape[],
526
+ s: Shape,
527
+ finalRect: Rect,
528
+ draggingIds: string[],
529
+ diagramId: string | null | undefined,
530
+ updateShape: (id: string, updates: Partial<Shape>) => void,
531
+ _minCoverage = 0.5,
532
+ hoverCandidateId?: string | null,
533
+ options?: {
534
+ /** 当发生“换父(嵌套)”时,是否只更新父子关系与位置,不立刻扩父,留给外部在接口成功后再扩父 */
535
+ deferExpandOnReparent?: boolean
536
+ }
537
+ ): boolean => {
538
+ const byId = (id?: string | null) => (id ? shapes.find(x => x.id === id) || null : null)
539
+ const draggingSet = new Set(draggingIds)
540
+ // 本次拖动的子图元是否是“黏在父上的类型”
541
+ const stickyToParent = isStickyChild(s)
542
+ // 配置:是否在“换父”时只改父子关系 / 坐标,不立刻扩父
543
+ const deferExpandOnReparent = !!options?.deferExpandOnReparent
544
+ // ===== 组拖保护:若该节点存在“正在拖动的祖先”,禁止换父 =====
545
+ if (hasDraggedAncestor(shapes, s.id, draggingSet)) {
546
+ const curParent = byId(s.parenShapeId)
547
+ if (!curParent) { updateShape(s.id, { bounds: finalRect }); return false }
548
+ // 子就地落位(不回弹/不clamp,避免跳动)
549
+ updateShape(s.id, { bounds: finalRect })
550
+ if (isCompartment(curParent)) {
551
+ // 如果父是隔间,拖动时父移动了,需要刷新隔间内部布局
552
+ ensureCompartmentAfterParentMoved(shapes, curParent, updateShape) // 新增调用
553
+ } else {
554
+ // 非隔间:维持原有逻辑——只在“放不下”时扩父 + 冒泡 + clamp
555
+ if (overflowIn(curParent, finalRect) > CONTENT_EPS) {
556
+ ensureParentFitsChildren(shapes, curParent, updateShape, { [s.id]: finalRect })
557
+ bubbleAutoFitUp(shapes, curParent.parenShapeId, updateShape)
558
+ // 扩父完还轻微越界,再轻夹一次(尽量少动)
559
+ const pNow = byId(curParent.id)!
560
+ if (overflowIn(pNow, finalRect) > CONTENT_EPS) {
561
+ const clamped = clampChildIntoParent(pNow, finalRect)
562
+ if (clamped.x !== finalRect.x || clamped.y !== finalRect.y) {
563
+ updateShape(s.id, { bounds: clamped })
564
+ }
565
+ }
566
+ }
567
+ }
568
+ return false
569
+ }
570
+ // 当前“原父”
571
+ const origParent = byId(s.parenShapeId)
572
+ // ===== hover 判定:只要合法就用 hover,完全“指哪选哪” =====
573
+ const isValidParent = (p: Shape | null): p is Shape => {
574
+ if (!p) return false
575
+ if (p.diagramId !== (diagramId || s.diagramId)) return false
576
+ if (p.id === s.id) return false
577
+ if (draggingSet.has(p.id)) return false
578
+ if (isDescendant(shapes, p.id, s.id)) return false // 防把后代当父
579
+
580
+ // sticky 类型一旦有了 origParent,只允许这个父作为候选父
581
+ if (stickyToParent && origParent && p.id !== origParent.id) {
582
+ return false
583
+ }
584
+
585
+ // 在隔间里的元素禁止当父(直到脱离隔间)
586
+ if (allowAsParent) {
587
+ if (!allowAsParent(shapes, p)) return false
588
+ } else {
589
+ // 或者直接内联一版最小实现:
590
+ let q = shapes.find(x => x.id === p.parenShapeId) || null
591
+ while (q) {
592
+ if (isCompartment(q)) return false
593
+ const parentId = q.parenShapeId
594
+ q = parentId ? shapes.find(x => x.id === parentId) || null : null
595
+ }
596
+ }
597
+ // if (!canNest(s, p, shapes)) return false
598
+ return true
599
+ }
600
+ // 选择目标父(优先 hover,其次原父,最后可能脱离)
601
+ const hover = byId(hoverCandidateId)
602
+ // const origParent = byId(s.parenShapeId)
603
+
604
+ // ===== Pin 硬性保护:永不置空父(不走后续脱离/覆盖率判断) =====
605
+ const __isPin = s.shapeType === 'pin' || String((s as any).shapeKey || '').toLowerCase().includes('pin')
606
+ if (__isPin) {
607
+ const pinTarget = isValidParent(hover) ? hover : origParent
608
+ if (pinTarget) {
609
+ updateShape(s.id, { parenShapeId: pinTarget.id, diagramId: pinTarget.diagramId, bounds: finalRect })
610
+ } else {
611
+ // 没有候选父且也没有原父:仅落位,不触碰 parenShapeId
612
+ updateShape(s.id, { bounds: finalRect })
613
+ }
614
+ return false
615
+ }
616
+
617
+ // ===== 目标父选择 =====
618
+ let target: Shape | null = null// 最终目标父
619
+ let reparent = false // 是否发生“换父”(包括挂到 null 上)
620
+
621
+ if (isValidParent(hover)) {
622
+ target = hover
623
+ reparent = hover.id !== s.parenShapeId
624
+ } else {
625
+ // 没有 hover 容器时,不再盲目回退原父;
626
+ // 若覆盖率低于阈值,直接脱离
627
+ if (origParent) {
628
+ const cover = coverageIn(finalRect, origParent)
629
+ if (!stickyToParent && cover < DETACH_THRESHOLD) {
630
+ // —— 脱离:不扩父
631
+ updateShape(s.id, { parenShapeId: undefined, bounds: finalRect })
632
+ return true
633
+ }
634
+ // 覆盖仍可接受 → 继续把原父作为 target(未换父)
635
+ target = origParent
636
+ reparent = false
637
+ } else {
638
+ target = null
639
+ }
640
+ }
641
+
642
+ // ===== 无目标父:保持无父,仅落位(Pin 兜底:保留原父) =====
643
+ if (!target) {
644
+ const isPin = s.shapeType === 'pin' || String((s as any).shapeKey || '').toLowerCase().includes('pin')
645
+ if (isPin && origParent) {
646
+ // Pin 没有 hover 候选时,强制保留原父,避免 parenShapeId 被清空
647
+ updateShape(s.id, { parenShapeId: origParent.id, bounds: finalRect })
648
+ return false
649
+ }
650
+ if (s.parenShapeId) {
651
+ updateShape(s.id, { parenShapeId: undefined })
652
+ }
653
+ updateShape(s.id, { bounds: finalRect })
654
+ return false
655
+ }
656
+ let placeRect = finalRect
657
+ // ===== 有目标父:先落位 finalRect;仅当放不下时才扩父(四向对称),避免位移 =====
658
+ if (reparent) {
659
+ // 只更新父子关系 + 位置
660
+ updateShape(s.id, {
661
+ parenShapeId: target.id,
662
+ diagramId: target.diagramId,
663
+ bounds: placeRect,
664
+ })
665
+ // updateShape(s.id, { parenShapeId: target.id, diagramId: target.diagramId, bounds: placeRect })
666
+ // ( zIndex 平移保持不变)
667
+ const parentZ = (typeof target.style?.zIndex === 'number') ? (target.style!.zIndex as number) : 0
668
+ const childZ = (typeof s.style?.zIndex === 'number') ? (s.style!.zIndex as number) : 0
669
+ const delta = (parentZ + 1) - childZ
670
+ if (delta !== 0) {
671
+ const subtree = [s.id, ...collectDescendantIds(shapes, s.id)]
672
+ for (const id of subtree) {
673
+ const node = shapes.find(x => x.id === id)!
674
+ const nz = ((typeof node.style?.zIndex === 'number') ? node.style!.zIndex as number : 0) + delta
675
+ updateShape(id, { style: { ...(node.style ?? {}), zIndex: nz } as any })
676
+ }
677
+ }
678
+ // 若不需要延迟扩父,仍可在这里同步扩父(兼容老行为)
679
+ if (!deferExpandOnReparent) {
680
+ expandParentToFitChildBounds(shapes, target, s.id, placeRect, updateShape)
681
+ }
682
+ return true
683
+ }
684
+ // 先更新位置
685
+ updateShape(s.id, { bounds: placeRect })
686
+ // ===== 未换父:只是父内部移动 —— 这类通常不走“嵌套接口”,可以继续即时扩父 =====
687
+ expandParentToFitChildBounds(shapes, target, s.id, placeRect, updateShape)
688
+
689
+ return false
690
+ }
691
+ const pointInRect = (pt: { x: number; y: number }, r: Rect, margin = 0) =>
692
+ pt.x >= r.x + margin &&
693
+ pt.x <= r.x + r.width - margin &&
694
+ pt.y >= r.y + margin &&
695
+ pt.y <= r.y + r.height - margin
696
+
697
+ const depthOf = (shapes: Shape[], id: string): number => {
698
+ let d = 0
699
+ let p = shapes.find(s => s.id === id)?.parenShapeId ?? null
700
+ while (p) { d++; p = shapes.find(s => s.id === p)?.parenShapeId ?? null }
701
+ return d
702
+ }
703
+
704
+ /**
705
+ * 只返回“视觉上最上面”的命中容器:
706
+ * - 命中条件:指针在 shape.bounds 内(不强制内容区)
707
+ * - 先找候选里 zIndex 的最大值,只在这个 z 层里挑一个
708
+ * - 同 z 打平:更深(层级大)> 内容区更小 > id 稳定
709
+ */
710
+ export const pickContainerByPointerTopmost = (
711
+ shapes: Shape[],
712
+ pointer: { x: number; y: number },
713
+ draggingId: string,
714
+ diagramId: string,
715
+ excludeIds: string[] = [],
716
+ boundsMargin = 0// 负值略放宽,边缘更容易点中
717
+ ): Shape | null => {
718
+ const ex = new Set(excludeIds)
719
+
720
+ // 1) 收集候选(在同一图里、不是自己/自己的后代/排除集,且指针在其 bounds 内)
721
+ const candidates = shapes.filter(s => {
722
+ if (s.diagramId !== diagramId) return false
723
+ if (s.shapeType !== 'shape') return false
724
+ if (s.id === draggingId) return false
725
+ if (ex.has(s.id)) return false
726
+ if (isDescendant(shapes, s.id, draggingId)) return false
727
+ const b = getBounds(s) as Rect
728
+ return pointInRect(pointer, b, boundsMargin)
729
+ })
730
+ if (!candidates.length) return null
731
+
732
+ // 2) 只看“最上层”那一批(z 最大)
733
+ const getZ = (s: Shape) => (typeof s.style?.zIndex === 'number' ? s.style!.zIndex as number : 0)
734
+ let maxZ = -Infinity
735
+ for (const s of candidates) maxZ = Math.max(maxZ, getZ(s))
736
+ const topLayer = candidates.filter(s => getZ(s) === maxZ)
737
+
738
+ if (topLayer.length === 1) return topLayer[0]
739
+
740
+ // 3) 同 z 打平:更深层级 > 内容区更小 > id 稳定
741
+ let best = topLayer[0]
742
+ let bestDepth = depthOf(shapes, best.id)
743
+ let bestArea = (() => {
744
+ const c = getContentRect(best); return Math.max(1, (c.right - c.left) * (c.bottom - c.top))
745
+ })()
746
+ for (let i = 1; i < topLayer.length; i++) {
747
+ const s = topLayer[i]
748
+ const d = depthOf(shapes, s.id)
749
+ const c = getContentRect(s)
750
+ const area = Math.max(1, (c.right - c.left) * (c.bottom - c.top))
751
+ if (d > bestDepth || (d === bestDepth && area < bestArea) || (d === bestDepth && area === bestArea && s.id < best.id)) {
752
+ best = s; bestDepth = d; bestArea = area
753
+ }
754
+ }
755
+ return best
756
+ }
757
+ //是否命中元素
758
+ export function hitContainerAtPoint(
759
+ shapes: Shape[],
760
+ pointer: { x: number; y: number },
761
+ diagramId: string,
762
+ excludeIds: string[] = []
763
+ ) {
764
+ // 拖“新建图元”时没有真实 id,用占位即可
765
+ return pickContainerByPointerTopmost(
766
+ shapes,
767
+ pointer,
768
+ '__NEW__', // 非自身/非后代的过滤占位
769
+ diagramId,
770
+ excludeIds,
771
+ 0
772
+ )
773
+ }
774
+ /**
775
+ * 根据某个子元素的最终矩形,自动扩容父元素,并在必要时把子元素夹回父元素内部。
776
+ * 注意:
777
+ * - 这个方法可以在拖拽逻辑之外单独调用(比如:后端嵌套接口成功后,再调用它来扩父)
778
+ * - 仅依赖当前 shapes + parent + childRect,不改变父子关系
779
+ */
780
+ export const expandParentToFitChildBounds = (
781
+ shapes: Shape[],
782
+ parent: Shape,
783
+ childId: string,
784
+ childRect: Rect,
785
+ updateShape: (id: string, updates: Partial<Shape>) => void,
786
+ ): { expanded: boolean; affectedIds: string[] } => {
787
+ // child 是 pin 时,永远不扩父,直接返回
788
+ const childShape = shapes.find(s => s.id === childId) || null
789
+ if (isPinShape(childShape)) {
790
+ return { expanded: false, affectedIds: [] }
791
+ }
792
+
793
+ let expanded = false
794
+ const affected = new Set<string>()
795
+
796
+ // 子自己一定是受影响对象之一
797
+ affected.add(childId)
798
+ affected.add(parent.id)
799
+ // 当前使用的子矩形(后面 clamp 时会更新它)
800
+ let placeRect = childRect
801
+
802
+ // ================== 1. 若父是隔间(compartment),先处理顶部 header / 说明区 ==================
803
+ if (isCompartment(parent)) {
804
+ const INSET = 6 // 子内容与“childrenTop”之间预留的安全内边距
805
+ const DIVIDER = 1 // 标题与内容区之间的分隔线高度
806
+ const PAD_TOP = 6 // 内容区顶部 padding
807
+ const MARGIN = 2 // 额外的冗余间距,避免贴得太死
808
+ const EPS = 0.5
809
+ const CANVAS_TOP = 0 // 画布上边界(如果有全局 padding,可以在这里留出来)
810
+
811
+ const pb = parent.bounds!
812
+ const pX = Number(pb.x ?? 0)
813
+ const pY = Number(pb.y ?? 0)
814
+ const pW = Number(pb.width ?? 0)
815
+ const pH = Number(pb.height ?? 0)
816
+
817
+ // 若外面有 getCompartmentZones,则优先用测量得到的 childrenArea
818
+ const zones =
819
+ typeof getCompartmentZones === 'function'
820
+ ? getCompartmentZones(parent.id)
821
+ : null
822
+
823
+ let zonalTop: number | null = null
824
+ if (zones?.childrenArea) {
825
+ const ca = zones.childrenArea
826
+ const parentYAtMeasure = zones.parentBounds?.y ?? 0
827
+ const rel = ca.y - parentYAtMeasure
828
+ // 换算到“当前父的绝对坐标系”
829
+ zonalTop = pY + rel
830
+ }
831
+
832
+ // 兜底:用 meta 中记录的 header / content 高度估算 childrenTop
833
+ const headerH =
834
+ Number(parent.meta?.headerH) ||
835
+ Number((parent.meta as any)?.upperHeight) || 30
836
+ const contentH =
837
+ Number(parent.meta?.contentH) ||
838
+ Number((parent as any).keywordsBounds?.height) ||
839
+ Math.ceil((Number((parent as any).keywordsStyle?.fontSize) || 14) * 1.3)
840
+
841
+ const metaTop = pY + headerH + DIVIDER + contentH + PAD_TOP
842
+
843
+ // 取更“严格”的 childrenTop
844
+ const childrenTopAbs = Math.max((zonalTop ?? -Infinity), metaTop)
845
+
846
+ const childTop = placeRect.y
847
+ const minAllowedTop = childrenTopAbs + INSET + MARGIN
848
+
849
+ // 只有在“确实侵入禁区”时才扩父,尽量少动
850
+ if (childTop < minAllowedTop - EPS) {
851
+ // 扩容目标:扩完后 newChildrenTop <= childTop - INSET - MARGIN
852
+ const targetTop = childTop - INSET - MARGIN
853
+ let needGrowUp = Math.max(0, childrenTopAbs - targetTop)
854
+
855
+ // 限制向上扩的量,不能超过画布顶部
856
+ const availUp = Math.max(0, pY - CANVAS_TOP)
857
+ let growUp = Math.min(needGrowUp, availUp)
858
+ if (growUp < EPS) growUp = 0
859
+
860
+ if (growUp > 0) {
861
+ const ny = pY - growUp
862
+ const nh = pH + growUp
863
+ if (ny !== pY || nh !== pH) {
864
+ updateShape(parent.id, {
865
+ bounds: { x: pX, y: ny, width: pW, height: nh },
866
+ })
867
+ expanded = true
868
+ }
869
+ }
870
+ }
871
+ // 左 / 右 / 下的越界问题交给下面的 overflowIn → ensureParentFitsChildren 统一处理
872
+ }
873
+
874
+ // ================== 2. 通用“放不下就扩父 + 向上冒泡 + 轻量 clamp 子元素” ==================
875
+ if (overflowIn(parent, placeRect) > CONTENT_EPS) {
876
+ // 这里只关心这个 childId 对父的影响,其他子元素的影响在 ensureParentFitsChildren 内部综合计算
877
+ ensureParentFitsChildren(shapes, parent, updateShape, {
878
+ [childId]: placeRect,
879
+ })
880
+
881
+ // 把“扩父影响”向上冒泡到更高层父节点
882
+ bubbleAutoFitUp(shapes, parent.parenShapeId, updateShape)
883
+
884
+ // 再看一眼,若父仍然轻微越界,则把子轻夹回去
885
+ const pNow = shapes.find(s => s.id === parent.id)
886
+ if (pNow && overflowIn(pNow, placeRect) > CONTENT_EPS) {
887
+ const clamped = clampChildIntoParent(pNow, placeRect)
888
+ if (clamped.x !== placeRect.x || clamped.y !== placeRect.y) {
889
+ updateShape(childId, { bounds: clamped })
890
+ }
891
+ }
892
+ expanded = true
893
+ }
894
+ return {
895
+ expanded,
896
+ affectedIds: expanded ? Array.from(affected) : [],
897
+ }
898
+ }
899
+ /**
900
+ * 统一判定:这个 shape 是否“禁止扩父”(pin / port)
901
+ */
902
+ const isPinShape = (shape?: Shape | null): boolean => {
903
+ if (!shape) return false
904
+ const key = (shape as any).shapeKey ?? ''
905
+ const store = useGraphStore()
906
+ const pins = store?.pinsTypes ?? []
907
+ const ports = store?.portsTypes ?? []
908
+ // 在 pinsTypes 或 portsTypes 里就视为“不参与扩父”
909
+ return pins.includes(key) || ports.includes(key)
910
+ }