Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

group.ts 5.1KB

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