Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

group.ts 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import Command from './command'
  2. import history from '../history'
  3. import { Data, GroupShape, ShapeType } from 'types'
  4. import { deepClone, getCommonBounds } from 'utils'
  5. import tld from 'utils/tld'
  6. import { createShape, getShapeUtils } from 'state/shape-utils'
  7. import commands from '.'
  8. export default function groupCommand(data: Data): void {
  9. const { currentPageId } = data
  10. const oldSelectedIds = tld.getSelectedIds(data)
  11. const initialShapes = tld
  12. .getSelectedShapes(data)
  13. .sort((a, b) => a.childIndex - b.childIndex)
  14. .map((shape) => deepClone(shape))
  15. const isAllSameParent = initialShapes.every(
  16. (shape, i) => i === 0 || shape.parentId === initialShapes[i - 1].parentId
  17. )
  18. // Do we need to ungroup the selected shapes shapes, rather than group them?
  19. if (isAllSameParent && initialShapes[0]?.parentId !== currentPageId) {
  20. const parent = tld.getShape(data, initialShapes[0]?.parentId) as GroupShape
  21. if (parent.children.length === initialShapes.length) {
  22. commands.ungroup(data)
  23. return
  24. }
  25. }
  26. let newGroupParentId: string
  27. const initialShapeIds = initialShapes.map((s) => s.id)
  28. const commonBounds = getCommonBounds(
  29. ...initialShapes.map((shape) =>
  30. getShapeUtils(shape).getRotatedBounds(shape)
  31. )
  32. )
  33. if (isAllSameParent) {
  34. const parentId = initialShapes[0].parentId
  35. if (parentId === currentPageId) {
  36. // Create the new group on the current page
  37. newGroupParentId = currentPageId
  38. } else {
  39. // Create the new group as a child of the shapes' current parent group
  40. newGroupParentId = parentId
  41. }
  42. } else {
  43. // Find the least-deep parent among the shapes and add the group as a child
  44. let minDepth = Infinity
  45. for (const parentId of initialShapes.map((shape) => shape.parentId)) {
  46. const depth = getShapeDepth(data, parentId)
  47. if (depth < minDepth) {
  48. minDepth = depth
  49. newGroupParentId = parentId
  50. }
  51. }
  52. }
  53. const newGroupShape = createShape(ShapeType.Group, {
  54. parentId: newGroupParentId,
  55. point: [commonBounds.minX, commonBounds.minY],
  56. size: [commonBounds.width, commonBounds.height],
  57. children: initialShapeIds,
  58. childIndex: initialShapes[0].childIndex,
  59. })
  60. history.execute(
  61. data,
  62. new Command({
  63. name: 'group_shapes',
  64. category: 'canvas',
  65. manualSelection: true,
  66. do(data) {
  67. const { shapes } = tld.getPage(data)
  68. // Create the new group
  69. shapes[newGroupShape.id] = newGroupShape
  70. // Assign the group to its new parent
  71. if (newGroupParentId !== data.currentPageId) {
  72. const parent = shapes[newGroupParentId]
  73. getShapeUtils(parent).setProperty(parent, 'children', [
  74. ...parent.children,
  75. newGroupShape.id,
  76. ])
  77. }
  78. // Assign the shapes to their new parent
  79. initialShapes.forEach((initialShape, i) => {
  80. // Remove shape from its old parent
  81. if (initialShape.parentId !== currentPageId) {
  82. const oldParent = shapes[initialShape.parentId] as GroupShape
  83. getShapeUtils(oldParent).setProperty(
  84. oldParent,
  85. 'children',
  86. oldParent.children.filter((id) => !oldSelectedIds.has(id))
  87. )
  88. }
  89. // Assign the shape to its new parent, with its new childIndex
  90. const shape = shapes[initialShape.id]
  91. getShapeUtils(shape)
  92. .setProperty(shape, 'childIndex', i)
  93. .setProperty(shape, 'parentId', newGroupShape.id)
  94. })
  95. tld.setSelectedIds(data, [newGroupShape.id])
  96. },
  97. undo(data) {
  98. const { shapes } = tld.getPage(data)
  99. const group = shapes[newGroupShape.id]
  100. // remove the group from its parent
  101. if (group.parentId !== data.currentPageId) {
  102. const parent = shapes[group.parentId]
  103. getShapeUtils(parent).setProperty(
  104. parent,
  105. 'children',
  106. parent.children.filter((id) => id !== newGroupShape.id)
  107. )
  108. }
  109. // Move the shapes back to their previous parent / childIndex
  110. initialShapes.forEach(({ id, parentId, childIndex }) => {
  111. const shape = shapes[id]
  112. getShapeUtils(shape)
  113. .setProperty(shape, 'parentId', parentId)
  114. .setProperty(shape, 'childIndex', childIndex)
  115. if (parentId !== data.currentPageId) {
  116. const parent = shapes[parentId]
  117. getShapeUtils(parent).setProperty(parent, 'children', [
  118. ...parent.children,
  119. id,
  120. ])
  121. }
  122. })
  123. // Delete the group
  124. delete shapes[newGroupShape.id]
  125. // Reselect the children of the group
  126. tld.setSelectedIds(data, initialShapeIds)
  127. },
  128. })
  129. )
  130. }
  131. function getShapeDepth(data: Data, id: string, depth = 0) {
  132. if (id === data.currentPageId) {
  133. return depth
  134. }
  135. return getShapeDepth(data, tld.getShape(data, id).parentId, depth + 1)
  136. }