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.

arrow-session.ts 3.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import { ArrowShape, Data } from 'types'
  2. import vec from 'utils/vec'
  3. import BaseSession from './base-session'
  4. import commands from 'state/commands'
  5. import { deepClone, getBoundsFromPoints, setToArray } from 'utils'
  6. import { getShapeUtils } from 'state/shape-utils'
  7. import tld from 'utils/tld'
  8. export default class ArrowSession extends BaseSession {
  9. points: number[][]
  10. origin: number[]
  11. snapshot: ArrowSnapshot
  12. isLocked: boolean
  13. lockedDirection: 'horizontal' | 'vertical'
  14. constructor(data: Data, id: string, point: number[], isLocked: boolean) {
  15. super(data)
  16. isLocked
  17. this.origin = point
  18. this.points = [[0, 0]]
  19. this.snapshot = getArrowSnapshot(data, id)
  20. }
  21. update(data: Data, point: number[], isLocked = false): void {
  22. const { id } = this.snapshot
  23. const delta = vec.vec(this.origin, point)
  24. if (isLocked) {
  25. if (!this.isLocked && this.points.length > 1) {
  26. this.isLocked = true
  27. if (Math.abs(delta[0]) < Math.abs(delta[1])) {
  28. this.lockedDirection = 'vertical'
  29. } else {
  30. this.lockedDirection = 'horizontal'
  31. }
  32. }
  33. } else {
  34. if (this.isLocked) {
  35. this.isLocked = false
  36. }
  37. }
  38. if (this.isLocked) {
  39. if (this.lockedDirection === 'vertical') {
  40. point[0] = this.origin[0]
  41. } else {
  42. point[1] = this.origin[1]
  43. }
  44. }
  45. const shape = tld.getPage(data).shapes[id] as ArrowShape
  46. getShapeUtils(shape).onHandleChange(shape, {
  47. end: {
  48. ...shape.handles.end,
  49. point: vec.sub(point, shape.point),
  50. },
  51. })
  52. tld.updateParents(data, [shape.id])
  53. }
  54. cancel(data: Data): void {
  55. const { id, initialShape } = this.snapshot
  56. const shape = tld.getPage(data).shapes[id] as ArrowShape
  57. getShapeUtils(shape)
  58. .onHandleChange(shape, { end: initialShape.handles.end })
  59. .setProperty(shape, 'point', initialShape.point)
  60. tld.updateParents(data, [shape.id])
  61. }
  62. complete(data: Data): void {
  63. const { id } = this.snapshot
  64. const shape = tld.getPage(data).shapes[id] as ArrowShape
  65. const { start, end, bend } = shape.handles
  66. // Normalize point and handles
  67. const bounds = getBoundsFromPoints([start.point, end.point])
  68. const corner = [bounds.minX, bounds.minY]
  69. const newPoint = vec.add(shape.point, corner)
  70. const nextHandles = {
  71. start: { ...start, point: vec.sub(start.point, corner) },
  72. end: { ...end, point: vec.sub(end.point, corner) },
  73. bend: { ...bend, point: vec.sub(bend.point, corner) },
  74. }
  75. getShapeUtils(shape)
  76. .setProperty(shape, 'handles', nextHandles)
  77. .setProperty(shape, 'point', newPoint)
  78. .onHandleChange(shape, nextHandles)
  79. commands.arrow(
  80. data,
  81. this.snapshot,
  82. getArrowSnapshot(data, this.snapshot.id)
  83. )
  84. }
  85. }
  86. // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
  87. export function getArrowSnapshot(data: Data, id: string) {
  88. const initialShape = deepClone(tld.getPage(data).shapes[id]) as ArrowShape
  89. return {
  90. id,
  91. initialShape,
  92. selectedIds: setToArray(tld.getSelectedIds(data)),
  93. currentPageId: data.currentPageId,
  94. }
  95. }
  96. export type ArrowSnapshot = ReturnType<typeof getArrowSnapshot>