選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

translate-session.ts 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. import { Data, GroupShape, Shape, ShapeType } from 'types'
  2. import * as vec from 'utils/vec'
  3. import BaseSession from './base-session'
  4. import commands from 'state/commands'
  5. import { current } from 'immer'
  6. import { v4 as uuid } from 'uuid'
  7. import {
  8. getChildIndexAbove,
  9. getPage,
  10. getSelectedShapes,
  11. updateParents,
  12. } from 'utils/utils'
  13. import { getShapeUtils } from 'lib/shape-utils'
  14. export default class TranslateSession extends BaseSession {
  15. delta = [0, 0]
  16. origin: number[]
  17. snapshot: TranslateSnapshot
  18. isCloning = false
  19. constructor(data: Data, point: number[]) {
  20. super(data)
  21. this.origin = point
  22. this.snapshot = getTranslateSnapshot(data)
  23. }
  24. update(data: Data, point: number[], isAligned: boolean, isCloning: boolean) {
  25. const { currentPageId, clones, initialShapes, initialParents } =
  26. this.snapshot
  27. const { shapes } = getPage(data, currentPageId)
  28. const delta = vec.vec(this.origin, point)
  29. if (isAligned) {
  30. if (Math.abs(delta[0]) < Math.abs(delta[1])) {
  31. delta[0] = 0
  32. } else {
  33. delta[1] = 0
  34. }
  35. }
  36. if (isCloning) {
  37. if (!this.isCloning) {
  38. this.isCloning = true
  39. data.selectedIds.clear()
  40. for (const { id, point } of initialShapes) {
  41. const shape = shapes[id]
  42. getShapeUtils(shape).translateTo(shape, point)
  43. }
  44. data.selectedIds.clear()
  45. for (const clone of clones) {
  46. data.selectedIds.add(clone.id)
  47. shapes[clone.id] = { ...clone }
  48. const parent = shapes[clone.parentId]
  49. if (!parent) continue
  50. getShapeUtils(parent).setProperty(parent, 'children', [
  51. ...parent.children,
  52. clone.id,
  53. ])
  54. }
  55. }
  56. for (const { id, point } of clones) {
  57. const shape = shapes[id]
  58. getShapeUtils(shape).translateTo(shape, vec.add(point, delta))
  59. }
  60. updateParents(
  61. data,
  62. clones.map((c) => c.id)
  63. )
  64. } else {
  65. if (this.isCloning) {
  66. this.isCloning = false
  67. data.selectedIds.clear()
  68. for (const { id } of initialShapes) {
  69. data.selectedIds.add(id)
  70. }
  71. for (const clone of clones) {
  72. delete shapes[clone.id]
  73. }
  74. initialParents.forEach(
  75. (parent) =>
  76. ((shapes[parent.id] as GroupShape).children = parent.children)
  77. )
  78. }
  79. for (const initialShape of initialShapes) {
  80. const shape = shapes[initialShape.id]
  81. const next = vec.add(initialShape.point, delta)
  82. const deltaForShape = vec.sub(next, shape.point)
  83. getShapeUtils(shape).translateTo(shape, next)
  84. if (shape.type === ShapeType.Group) {
  85. for (let childId of shape.children) {
  86. const childShape = shapes[childId]
  87. getShapeUtils(childShape).translateBy(childShape, deltaForShape)
  88. }
  89. }
  90. }
  91. updateParents(
  92. data,
  93. initialShapes.map((s) => s.id)
  94. )
  95. }
  96. }
  97. cancel(data: Data) {
  98. const { initialShapes, initialParents, clones, currentPageId } =
  99. this.snapshot
  100. const { shapes } = getPage(data, currentPageId)
  101. for (const { id, point } of initialShapes) {
  102. const shape = shapes[id]
  103. const deltaForShape = vec.sub(point, shape.point)
  104. getShapeUtils(shape).translateTo(shape, point)
  105. if (shape.type === ShapeType.Group) {
  106. for (let childId of shape.children) {
  107. const childShape = shapes[childId]
  108. getShapeUtils(childShape).translateBy(childShape, deltaForShape)
  109. }
  110. }
  111. }
  112. for (const { id } of clones) {
  113. delete shapes[id]
  114. }
  115. initialParents.forEach(({ id, children }) => {
  116. const shape = shapes[id]
  117. getShapeUtils(shape).setProperty(shape, 'children', children)
  118. })
  119. updateParents(
  120. data,
  121. initialShapes.map((s) => s.id)
  122. )
  123. }
  124. complete(data: Data) {
  125. if (!this.snapshot.hasUnlockedShapes) return
  126. commands.translate(
  127. data,
  128. this.snapshot,
  129. getTranslateSnapshot(data),
  130. this.isCloning
  131. )
  132. }
  133. }
  134. export function getTranslateSnapshot(data: Data) {
  135. const cData = current(data)
  136. const page = getPage(cData)
  137. const selectedShapes = getSelectedShapes(cData).filter(
  138. (shape) => !shape.isLocked
  139. )
  140. const hasUnlockedShapes = selectedShapes.length > 0
  141. const parents = Array.from(
  142. new Set(selectedShapes.map((s) => s.parentId)).values()
  143. )
  144. .filter((id) => id !== data.currentPageId)
  145. .map((id) => page.shapes[id])
  146. return {
  147. hasUnlockedShapes,
  148. currentPageId: data.currentPageId,
  149. initialParents: parents.map(({ id, children }) => ({ id, children })),
  150. initialShapes: selectedShapes.map(({ id, point, parentId }) => ({
  151. id,
  152. point,
  153. parentId,
  154. })),
  155. clones: selectedShapes
  156. .filter((shape) => shape.type !== ShapeType.Group)
  157. .flatMap((shape) => {
  158. const clone = {
  159. ...shape,
  160. id: uuid(),
  161. parentId: shape.parentId,
  162. childIndex: getChildIndexAbove(cData, shape.id),
  163. }
  164. return clone
  165. // cloneGroup(cData, {
  166. // ...shape,
  167. // id: uuid(),
  168. // parentId: shape.parentId,
  169. // childIndex: getChildIndexAbove(cData, shape.id),
  170. // })
  171. }),
  172. }
  173. }
  174. export type TranslateSnapshot = ReturnType<typeof getTranslateSnapshot>
  175. function cloneGroup(data: Data, clone: Shape): Shape[] {
  176. if (clone.type !== ShapeType.Group) {
  177. return [clone]
  178. }
  179. const page = getPage(data)
  180. const childClones = clone.children.flatMap((id) => {
  181. const newId = uuid()
  182. const source = page.shapes[id]
  183. const next = { ...source, id: newId, parentId: clone.id }
  184. if (next.type === ShapeType.Group) {
  185. return [next, ...cloneGroup(data, next)]
  186. }
  187. return [next]
  188. })
  189. return [clone, ...childClones]
  190. }