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 4.7KB

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