You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

move.ts 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import Command from './command'
  2. import history from '../history'
  3. import { Data, MoveType, Shape } from 'types'
  4. import { forceIntegerChildIndices, getChildren, getPage } from 'utils/utils'
  5. import { getShapeUtils } from 'lib/shape-utils'
  6. export default function moveCommand(data: Data, type: MoveType) {
  7. const { currentPageId } = data
  8. const page = getPage(data)
  9. const selectedIds = Array.from(data.selectedIds.values())
  10. const initialIndices = Object.fromEntries(
  11. selectedIds.map((id) => [id, page.shapes[id].childIndex])
  12. )
  13. history.execute(
  14. data,
  15. new Command({
  16. name: 'move_shapes',
  17. category: 'canvas',
  18. manualSelection: true,
  19. do(data) {
  20. const page = getPage(data, currentPageId)
  21. const shapes = selectedIds.map((id) => page.shapes[id])
  22. const shapesByParentId = shapes.reduce<Record<string, Shape[]>>(
  23. (acc, shape) => {
  24. if (acc[shape.parentId] === undefined) {
  25. acc[shape.parentId] = []
  26. }
  27. acc[shape.parentId].push(shape)
  28. return acc
  29. },
  30. {}
  31. )
  32. switch (type) {
  33. case MoveType.ToFront: {
  34. for (let id in shapesByParentId) {
  35. moveToFront(shapesByParentId[id], getChildren(data, id))
  36. }
  37. break
  38. }
  39. case MoveType.ToBack: {
  40. for (let id in shapesByParentId) {
  41. moveToBack(shapesByParentId[id], getChildren(data, id))
  42. }
  43. break
  44. }
  45. case MoveType.Forward: {
  46. for (let id in shapesByParentId) {
  47. const visited = new Set<string>()
  48. const siblings = getChildren(data, id)
  49. shapesByParentId[id]
  50. .sort((a, b) => b.childIndex - a.childIndex)
  51. .forEach((shape) => moveForward(shape, siblings, visited))
  52. }
  53. break
  54. }
  55. case MoveType.Backward: {
  56. for (let id in shapesByParentId) {
  57. const visited = new Set<string>()
  58. const siblings = getChildren(data, id)
  59. shapesByParentId[id]
  60. .sort((a, b) => a.childIndex - b.childIndex)
  61. .forEach((shape) => moveBackward(shape, siblings, visited))
  62. }
  63. break
  64. }
  65. }
  66. },
  67. undo(data) {
  68. const page = getPage(data)
  69. for (let id of selectedIds) {
  70. const shape = page.shapes[id]
  71. getShapeUtils(shape).setProperty(
  72. shape,
  73. 'childIndex',
  74. initialIndices[id]
  75. )
  76. }
  77. },
  78. })
  79. )
  80. }
  81. function moveToFront(shapes: Shape[], siblings: Shape[]) {
  82. shapes.sort((a, b) => a.childIndex - b.childIndex)
  83. const diff = siblings
  84. .filter((sib) => !shapes.includes(sib))
  85. .sort((a, b) => b.childIndex - a.childIndex)
  86. if (diff.length === 0) return
  87. const startIndex = Math.ceil(diff[0].childIndex) + 1
  88. shapes.forEach((shape, i) =>
  89. getShapeUtils(shape).setProperty(shape, 'childIndex', startIndex + i)
  90. )
  91. }
  92. function moveToBack(shapes: Shape[], siblings: Shape[]) {
  93. shapes.sort((a, b) => b.childIndex - a.childIndex)
  94. const diff = siblings
  95. .filter((sib) => !shapes.includes(sib))
  96. .sort((a, b) => a.childIndex - b.childIndex)
  97. if (diff.length === 0) return
  98. const startIndex = diff[0]?.childIndex
  99. const step = startIndex / (shapes.length + 1)
  100. shapes.forEach((shape, i) =>
  101. getShapeUtils(shape).setProperty(
  102. shape,
  103. 'childIndex',
  104. startIndex - (i + 1) * step
  105. )
  106. )
  107. }
  108. function moveForward(shape: Shape, siblings: Shape[], visited: Set<string>) {
  109. visited.add(shape.id)
  110. const index = siblings.indexOf(shape)
  111. const nextSibling = siblings[index + 1]
  112. if (nextSibling && !visited.has(nextSibling.id)) {
  113. const nextNextSibling = siblings[index + 2]
  114. let nextIndex = nextNextSibling
  115. ? (nextSibling.childIndex + nextNextSibling.childIndex) / 2
  116. : Math.ceil(nextSibling.childIndex + 1)
  117. if (nextIndex === nextSibling.childIndex) {
  118. forceIntegerChildIndices(siblings)
  119. nextIndex = nextNextSibling
  120. ? (nextSibling.childIndex + nextNextSibling.childIndex) / 2
  121. : Math.ceil(nextSibling.childIndex + 1)
  122. }
  123. getShapeUtils(shape).setProperty(shape, 'childIndex', nextIndex)
  124. siblings.sort((a, b) => a.childIndex - b.childIndex)
  125. }
  126. }
  127. function moveBackward(shape: Shape, siblings: Shape[], visited: Set<string>) {
  128. visited.add(shape.id)
  129. const index = siblings.indexOf(shape)
  130. const nextSibling = siblings[index - 1]
  131. if (nextSibling && !visited.has(nextSibling.id)) {
  132. const nextNextSibling = siblings[index - 2]
  133. let nextIndex = nextNextSibling
  134. ? (nextSibling.childIndex + nextNextSibling.childIndex) / 2
  135. : nextSibling.childIndex / 2
  136. if (shape.childIndex === nextSibling.childIndex) {
  137. forceIntegerChildIndices(siblings)
  138. nextNextSibling
  139. ? (nextSibling.childIndex + nextNextSibling.childIndex) / 2
  140. : nextSibling.childIndex / 2
  141. }
  142. getShapeUtils(shape).setProperty(shape, 'childIndex', nextIndex)
  143. siblings.sort((a, b) => a.childIndex - b.childIndex)
  144. }
  145. }