Parcourir la source

Continues on drawing / rotation

main
Steve Ruiz il y a 4 ans
Parent
révision
c95c54dae6

+ 0
- 2
components/canvas/bounds/bounding-box.tsx Voir le fichier

@@ -7,7 +7,6 @@ import CenterHandle from './center-handle'
7 7
 import CornerHandle from './corner-handle'
8 8
 import EdgeHandle from './edge-handle'
9 9
 import RotateHandle from './rotate-handle'
10
-import Selected from '../selected'
11 10
 
12 11
 export default function Bounds() {
13 12
   const isBrushing = useSelector((s) => s.isIn('brushSelecting'))
@@ -32,7 +31,6 @@ export default function Bounds() {
32 31
         ${(bounds.minY + bounds.maxY) / 2})
33 32
         translate(${bounds.minX},${bounds.minY})`}
34 33
     >
35
-      <Selected bounds={bounds} />
36 34
       <CenterHandle bounds={bounds} />
37 35
       <EdgeHandle size={size} bounds={bounds} edge={Edge.Top} />
38 36
       <EdgeHandle size={size} bounds={bounds} edge={Edge.Right} />

+ 13
- 9
components/canvas/canvas.tsx Voir le fichier

@@ -1,5 +1,5 @@
1 1
 import styled from 'styles'
2
-import state from 'state'
2
+import state, { useSelector } from 'state'
3 3
 import inputs from 'state/inputs'
4 4
 import React, { useCallback, useRef } from 'react'
5 5
 import useZoomEvents from 'hooks/useZoomEvents'
@@ -9,6 +9,7 @@ import Page from './page'
9 9
 import Brush from './brush'
10 10
 import Bounds from './bounds/bounding-box'
11 11
 import BoundsBg from './bounds/bounds-bg'
12
+import Selected from './selected'
12 13
 
13 14
 export default function Canvas() {
14 15
   const rCanvas = useRef<SVGSVGElement>(null)
@@ -17,6 +18,8 @@ export default function Canvas() {
17 18
 
18 19
   useCamera(rGroup)
19 20
 
21
+  const isReady = useSelector((s) => s.isIn('ready'))
22
+
20 23
   const handlePointerDown = useCallback((e: React.PointerEvent) => {
21 24
     rCanvas.current.setPointerCapture(e.pointerId)
22 25
     state.send('POINTED_CANVAS', inputs.pointerDown(e, 'canvas'))
@@ -40,12 +43,15 @@ export default function Canvas() {
40 43
       onPointerUp={handlePointerUp}
41 44
     >
42 45
       <Defs />
43
-      <MainGroup ref={rGroup}>
44
-        <BoundsBg />
45
-        <Page />
46
-        <Bounds />
47
-        <Brush />
48
-      </MainGroup>
46
+      {isReady && (
47
+        <g ref={rGroup}>
48
+          <BoundsBg />
49
+          <Page />
50
+          <Bounds />
51
+          <Selected />
52
+          <Brush />
53
+        </g>
54
+      )}
49 55
     </MainSVG>
50 56
   )
51 57
 }
@@ -63,5 +69,3 @@ const MainSVG = styled('svg', {
63 69
     userSelect: 'none',
64 70
   },
65 71
 })
66
-
67
-const MainGroup = styled('g', {})

+ 4
- 4
components/canvas/defs.tsx Voir le fichier

@@ -1,6 +1,6 @@
1
-import { getShapeUtils } from "lib/shape-utils"
2
-import { useSelector } from "state"
3
-import { deepCompareArrays, getPage } from "utils/utils"
1
+import { getShapeUtils } from 'lib/shape-utils'
2
+import { useSelector } from 'state'
3
+import { deepCompareArrays, getPage } from 'utils/utils'
4 4
 
5 5
 export default function Defs() {
6 6
   const currentPageShapeIds = useSelector(({ data }) => {
@@ -20,6 +20,6 @@ export default function Defs() {
20 20
 
21 21
 export function Def({ id }: { id: string }) {
22 22
   const shape = useSelector(({ data }) => getPage(data).shapes[id])
23
-
23
+  if (!shape) return null
24 24
   return getShapeUtils(shape).render(shape)
25 25
 }

+ 16
- 16
components/canvas/selected.tsx Voir le fichier

@@ -1,48 +1,48 @@
1 1
 import styled from 'styles'
2 2
 import { useSelector } from 'state'
3
-import {
4
-  deepCompareArrays,
5
-  getBoundsCenter,
6
-  getPage,
7
-  getSelectedShapes,
8
-} from 'utils/utils'
9
-import * as vec from 'utils/vec'
3
+import { deepCompareArrays, getPage } from 'utils/utils'
10 4
 import { getShapeUtils } from 'lib/shape-utils'
11
-import { Bounds } from 'types'
12 5
 import useShapeEvents from 'hooks/useShapeEvents'
13 6
 import { useRef } from 'react'
14 7
 
15
-export default function Selected({ bounds }: { bounds: Bounds }) {
8
+export default function Selected() {
16 9
   const currentPageShapeIds = useSelector(({ data }) => {
17 10
     return Array.from(data.selectedIds.values())
18 11
   }, deepCompareArrays)
19 12
 
13
+  const isSelecting = useSelector((s) => s.isIn('selecting'))
14
+
15
+  if (!isSelecting) return null
16
+
20 17
   return (
21 18
     <g>
22 19
       {currentPageShapeIds.map((id) => (
23
-        <ShapeOutline key={id} id={id} bounds={bounds} />
20
+        <ShapeOutline key={id} id={id} />
24 21
       ))}
25 22
     </g>
26 23
   )
27 24
 }
28 25
 
29
-export function ShapeOutline({ id, bounds }: { id: string; bounds: Bounds }) {
26
+export function ShapeOutline({ id }: { id: string }) {
30 27
   const rIndicator = useRef<SVGUseElement>(null)
31 28
 
32 29
   const shape = useSelector(({ data }) => getPage(data).shapes[id])
33 30
 
34
-  const shapeBounds = getShapeUtils(shape).getBounds(shape)
35
-
36 31
   const events = useShapeEvents(id, rIndicator)
37 32
 
33
+  if (!shape) return null
34
+
35
+  const transform = `
36
+  rotate(${shape.rotation * (180 / Math.PI)},
37
+  ${getShapeUtils(shape).getCenter(shape)})
38
+  translate(${shape.point})`
39
+
38 40
   return (
39 41
     <Indicator
40 42
       ref={rIndicator}
41 43
       as="use"
42 44
       href={'#' + id}
43
-      transform={`rotate(${shape.rotation * (180 / Math.PI)},${getBoundsCenter(
44
-        shapeBounds
45
-      )}) translate(${vec.sub(shape.point, [bounds.minX, bounds.minY])})`}
45
+      transform={transform}
46 46
       {...events}
47 47
     />
48 48
   )

+ 11
- 47
components/canvas/shape.tsx Voir le fichier

@@ -1,10 +1,10 @@
1
-import React, { useCallback, useRef, memo } from 'react'
2
-import state, { useSelector } from 'state'
3
-import inputs from 'state/inputs'
1
+import React, { useRef, memo } from 'react'
2
+import { useSelector } from 'state'
4 3
 import styled from 'styles'
5 4
 import { getShapeUtils } from 'lib/shape-utils'
6 5
 import { getPage } from 'utils/utils'
7 6
 import { ShapeStyles } from 'types'
7
+import useShapeEvents from 'hooks/useShapeEvents'
8 8
 
9 9
 function Shape({ id, isSelecting }: { id: string; isSelecting: boolean }) {
10 10
   const isHovered = useSelector((state) => state.data.hoveredId === id)
@@ -15,42 +15,7 @@ function Shape({ id, isSelecting }: { id: string; isSelecting: boolean }) {
15 15
 
16 16
   const rGroup = useRef<SVGGElement>(null)
17 17
 
18
-  const handlePointerDown = useCallback(
19
-    (e: React.PointerEvent) => {
20
-      e.stopPropagation()
21
-      rGroup.current.setPointerCapture(e.pointerId)
22
-      state.send('POINTED_SHAPE', inputs.pointerDown(e, id))
23
-    },
24
-    [id]
25
-  )
26
-
27
-  const handlePointerUp = useCallback(
28
-    (e: React.PointerEvent) => {
29
-      e.stopPropagation()
30
-      rGroup.current.releasePointerCapture(e.pointerId)
31
-      state.send('STOPPED_POINTING', inputs.pointerUp(e))
32
-    },
33
-    [id]
34
-  )
35
-
36
-  const handlePointerEnter = useCallback(
37
-    (e: React.PointerEvent) => {
38
-      state.send('HOVERED_SHAPE', inputs.pointerEnter(e, id))
39
-    },
40
-    [id, shape]
41
-  )
42
-
43
-  const handlePointerMove = useCallback(
44
-    (e: React.PointerEvent) => {
45
-      state.send('MOVED_OVER_SHAPE', inputs.pointerEnter(e, id))
46
-    },
47
-    [id, shape]
48
-  )
49
-
50
-  const handlePointerLeave = useCallback(
51
-    () => state.send('UNHOVERED_SHAPE', { target: id }),
52
-    [id]
53
-  )
18
+  const events = useShapeEvents(id, rGroup)
54 19
 
55 20
   // This is a problem with deleted shapes. The hooks in this component
56 21
   // may sometimes run before the hook in the Page component, which means
@@ -58,19 +23,18 @@ function Shape({ id, isSelecting }: { id: string; isSelecting: boolean }) {
58 23
   // detects the change and pulls this component.
59 24
   if (!shape) return null
60 25
 
26
+  const transform = `
27
+  rotate(${shape.rotation * (180 / Math.PI)},
28
+  ${getShapeUtils(shape).getCenter(shape)})
29
+  translate(${shape.point})`
30
+
61 31
   return (
62 32
     <StyledGroup
63 33
       ref={rGroup}
64 34
       isHovered={isHovered}
65 35
       isSelected={isSelected}
66
-      transform={`rotate(${shape.rotation * (180 / Math.PI)},${getShapeUtils(
67
-        shape
68
-      ).getCenter(shape)}) translate(${shape.point})`}
69
-      onPointerDown={handlePointerDown}
70
-      onPointerUp={handlePointerUp}
71
-      onPointerEnter={handlePointerEnter}
72
-      onPointerLeave={handlePointerLeave}
73
-      onPointerMove={handlePointerMove}
36
+      transform={transform}
37
+      {...events}
74 38
     >
75 39
       {isSelecting && <HoverIndicator as="use" href={'#' + id} />}
76 40
       <StyledShape id={id} style={shape.style} />

+ 19
- 18
hooks/useZoomEvents.ts Voir le fichier

@@ -1,8 +1,8 @@
1
-import React, { useEffect, useRef } from "react"
2
-import state from "state"
3
-import inputs from "state/inputs"
4
-import * as vec from "utils/vec"
5
-import { usePinch } from "react-use-gesture"
1
+import React, { useEffect, useRef } from 'react'
2
+import state from 'state'
3
+import inputs from 'state/inputs'
4
+import * as vec from 'utils/vec'
5
+import { usePinch } from 'react-use-gesture'
6 6
 
7 7
 /**
8 8
  * Capture zoom gestures (pinches, wheels and pans) and send to the state.
@@ -21,16 +21,17 @@ export default function useZoomEvents(
21 21
 
22 22
     function handleWheel(e: WheelEvent) {
23 23
       e.preventDefault()
24
+      e.stopPropagation()
24 25
 
25 26
       if (e.ctrlKey) {
26
-        state.send("ZOOMED_CAMERA", {
27
+        state.send('ZOOMED_CAMERA', {
27 28
           delta: e.deltaY,
28 29
           ...inputs.wheel(e),
29 30
         })
30 31
         return
31 32
       }
32 33
 
33
-      state.send("PANNED_CAMERA", {
34
+      state.send('PANNED_CAMERA', {
34 35
         delta: [e.deltaX, e.deltaY],
35 36
         ...inputs.wheel(e),
36 37
       })
@@ -38,6 +39,7 @@ export default function useZoomEvents(
38 39
 
39 40
     function handleTouchMove(e: TouchEvent) {
40 41
       e.preventDefault()
42
+      e.stopPropagation()
41 43
 
42 44
       if (e.touches.length === 2) {
43 45
         const { clientX: x0, clientY: y0 } = e.touches[0]
@@ -46,7 +48,7 @@ export default function useZoomEvents(
46 48
         const dist = vec.dist([x0, y0], [x1, y1])
47 49
         const point = vec.med([x0, y0], [x1, y1])
48 50
 
49
-        state.send("WHEELED", {
51
+        state.send('WHEELED', {
50 52
           delta: dist - rTouchDist.current,
51 53
           point,
52 54
         })
@@ -55,38 +57,37 @@ export default function useZoomEvents(
55 57
       }
56 58
     }
57 59
 
58
-    element.addEventListener("wheel", handleWheel)
59
-    element.addEventListener("touchstart", handleTouchMove)
60
-    element.addEventListener("touchmove", handleTouchMove)
60
+    element.addEventListener('wheel', handleWheel, { passive: false })
61
+    element.addEventListener('touchstart', handleTouchMove, { passive: false })
62
+    element.addEventListener('touchmove', handleTouchMove, { passive: false })
61 63
 
62 64
     return () => {
63
-      element.removeEventListener("wheel", handleWheel)
64
-      element.removeEventListener("touchstart", handleTouchMove)
65
-      element.removeEventListener("touchmove", handleTouchMove)
65
+      element.removeEventListener('wheel', handleWheel)
66
+      element.removeEventListener('touchstart', handleTouchMove)
67
+      element.removeEventListener('touchmove', handleTouchMove)
66 68
     }
67 69
   }, [ref])
68 70
 
69 71
   const rPinchDa = useRef<number[] | undefined>(undefined)
70
-  const rPinchAngle = useRef<number>(undefined)
71 72
   const rPinchPoint = useRef<number[] | undefined>(undefined)
72 73
 
73 74
   const bind = usePinch(({ pinching, da, origin }) => {
74 75
     if (!pinching) {
75
-      state.send("STOPPED_PINCHING")
76
+      state.send('STOPPED_PINCHING')
76 77
       rPinchDa.current = undefined
77 78
       rPinchPoint.current = undefined
78 79
       return
79 80
     }
80 81
 
81 82
     if (rPinchPoint.current === undefined) {
82
-      state.send("STARTED_PINCHING")
83
+      state.send('STARTED_PINCHING')
83 84
       rPinchDa.current = da
84 85
       rPinchPoint.current = origin
85 86
     }
86 87
 
87 88
     const [distanceDelta, angleDelta] = vec.sub(rPinchDa.current, da)
88 89
 
89
-    state.send("PINCHED", {
90
+    state.send('PINCHED', {
90 91
       delta: vec.sub(rPinchPoint.current, origin),
91 92
       point: origin,
92 93
       distanceDelta,

+ 19
- 9
lib/shape-utils/draw.tsx Voir le fichier

@@ -7,11 +7,10 @@ import { boundsContainPolygon } from 'utils/bounds'
7 7
 import getStroke from 'perfect-freehand'
8 8
 import {
9 9
   getBoundsFromPoints,
10
+  getRotatedCorners,
10 11
   getSvgPathFromStroke,
11 12
   translateBounds,
12 13
 } from 'utils/utils'
13
-import { DotCircle } from 'components/canvas/misc'
14
-import { shades } from 'lib/colors'
15 14
 
16 15
 const pathCache = new WeakMap<number[][], string>([])
17 16
 
@@ -43,12 +42,17 @@ const draw = registerShapeUtils<DrawShape>({
43 42
   render(shape) {
44 43
     const { id, point, points } = shape
45 44
 
46
-    if (points.length < 2) {
47
-      return <DotCircle id={id} cx={point[0]} cy={point[1]} r={3} />
48
-    }
49
-
50 45
     if (!pathCache.has(points)) {
51
-      pathCache.set(points, getSvgPathFromStroke(getStroke(points)))
46
+      if (points.length < 2) {
47
+        const left = vec.add(point, [6, 0])
48
+        let d: number[][] = []
49
+        for (let i = 0; i < 10; i++) {
50
+          d.push(vec.rotWith(left, point, i * ((Math.PI * 2) / 8)))
51
+        }
52
+        pathCache.set(points, getSvgPathFromStroke(d))
53
+      } else {
54
+        pathCache.set(points, getSvgPathFromStroke(getStroke(points)))
55
+      }
52 56
     }
53 57
 
54 58
     return <path id={id} d={pathCache.get(points)} />
@@ -69,11 +73,13 @@ const draw = registerShapeUtils<DrawShape>({
69 73
   },
70 74
 
71 75
   getRotatedBounds(shape) {
72
-    return this.getBounds(shape)
76
+    return getBoundsFromPoints(
77
+      getRotatedCorners(this.getBounds(shape), shape.rotation)
78
+    )
73 79
   },
74 80
 
75 81
   getCenter(shape) {
76
-    const bounds = this.getBounds(shape)
82
+    const bounds = this.getRotatedBounds(shape)
77 83
     return [bounds.minX + bounds.width / 2, bounds.minY + bounds.height / 2]
78 84
   },
79 85
 
@@ -114,6 +120,10 @@ const draw = registerShapeUtils<DrawShape>({
114 120
 
115 121
   rotateTo(shape, rotation) {
116 122
     shape.rotation = rotation
123
+    // console.log(shape.points.map(([x, y]) => [x, y]))
124
+    // const bounds = this.getBounds(shape)
125
+    // const center = [bounds.width / 2, bounds.height / 2]
126
+    // shape.points = shape.points.map((pt) => vec.rotWith(pt, center, rotation))
117 127
     return this
118 128
   },
119 129
 

+ 1
- 1
package.json Voir le fichier

@@ -16,7 +16,7 @@
16 16
     "framer-motion": "^4.1.16",
17 17
     "ismobilejs": "^1.1.1",
18 18
     "next": "10.2.0",
19
-    "perfect-freehand": "^0.4.71",
19
+    "perfect-freehand": "^0.4.8",
20 20
     "prettier": "^2.3.0",
21 21
     "react": "17.0.2",
22 22
     "react-dom": "17.0.2",

+ 7
- 7
state/sessions/rotate-session.ts Voir le fichier

@@ -1,8 +1,8 @@
1
-import { Data } 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"
1
+import { Data } 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 6
 import {
7 7
   clampToRotationToSegments,
8 8
   getBoundsCenter,
@@ -10,8 +10,8 @@ import {
10 10
   getPage,
11 11
   getSelectedShapes,
12 12
   getShapeBounds,
13
-} from "utils/utils"
14
-import { getShapeUtils } from "lib/shape-utils"
13
+} from 'utils/utils'
14
+import { getShapeUtils } from 'lib/shape-utils'
15 15
 
16 16
 const PI2 = Math.PI * 2
17 17
 

+ 218
- 208
state/state.ts Voir le fichier

@@ -1,13 +1,13 @@
1
-import { createSelectorHook, createState } from "@state-designer/react"
2
-import * as vec from "utils/vec"
3
-import inputs from "./inputs"
4
-import { defaultDocument } from "./data"
5
-import { shades } from "lib/colors"
6
-import { createShape, getShapeUtils } from "lib/shape-utils"
7
-import history from "state/history"
8
-import * as Sessions from "./sessions"
9
-import commands from "./commands"
10
-import { updateFromCode } from "lib/code/generate"
1
+import { createSelectorHook, createState } from '@state-designer/react'
2
+import * as vec from 'utils/vec'
3
+import inputs from './inputs'
4
+import { defaultDocument } from './data'
5
+import { shades } from 'lib/colors'
6
+import { createShape, getShapeUtils } from 'lib/shape-utils'
7
+import history from 'state/history'
8
+import * as Sessions from './sessions'
9
+import commands from './commands'
10
+import { updateFromCode } from 'lib/code/generate'
11 11
 import {
12 12
   clamp,
13 13
   getChildren,
@@ -18,7 +18,7 @@ import {
18 18
   getShape,
19 19
   screenToWorld,
20 20
   setZoomCSS,
21
-} from "utils/utils"
21
+} from 'utils/utils'
22 22
 import {
23 23
   Data,
24 24
   PointerInfo,
@@ -32,7 +32,7 @@ import {
32 32
   DistributeType,
33 33
   AlignType,
34 34
   StretchType,
35
-} from "types"
35
+} from 'types'
36 36
 
37 37
 const initialData: Data = {
38 38
   isReadOnly: false,
@@ -55,8 +55,8 @@ const initialData: Data = {
55 55
   pointedId: null,
56 56
   hoveredId: null,
57 57
   selectedIds: new Set([]),
58
-  currentPageId: "page0",
59
-  currentCodeFileId: "file0",
58
+  currentPageId: 'page0',
59
+  currentCodeFileId: 'file0',
60 60
   codeControls: {},
61 61
   document: defaultDocument,
62 62
 }
@@ -65,109 +65,116 @@ const state = createState({
65 65
   data: initialData,
66 66
   on: {
67 67
     ZOOMED_CAMERA: {
68
-      do: "zoomCamera",
68
+      do: 'zoomCamera',
69 69
     },
70 70
     PANNED_CAMERA: {
71
-      do: "panCamera",
72
-    },
73
-    SELECTED_SELECT_TOOL: { to: "selecting" },
74
-    SELECTED_DRAW_TOOL: { unless: "isReadOnly", to: "draw" },
75
-    SELECTED_DOT_TOOL: { unless: "isReadOnly", to: "dot" },
76
-    SELECTED_CIRCLE_TOOL: { unless: "isReadOnly", to: "circle" },
77
-    SELECTED_ELLIPSE_TOOL: { unless: "isReadOnly", to: "ellipse" },
78
-    SELECTED_RAY_TOOL: { unless: "isReadOnly", to: "ray" },
79
-    SELECTED_LINE_TOOL: { unless: "isReadOnly", to: "line" },
80
-    SELECTED_POLYLINE_TOOL: { unless: "isReadOnly", to: "polyline" },
81
-    SELECTED_RECTANGLE_TOOL: { unless: "isReadOnly", to: "rectangle" },
82
-    TOGGLED_CODE_PANEL_OPEN: "toggleCodePanel",
83
-    TOGGLED_STYLE_PANEL_OPEN: "toggleStylePanel",
84
-    CHANGED_STYLE: ["updateStyles", "applyStylesToSelection"],
85
-    RESET_CAMERA: "resetCamera",
86
-    ZOOMED_TO_FIT: "zoomCameraToFit",
71
+      do: 'panCamera',
72
+    },
73
+    SELECTED_SELECT_TOOL: { to: 'selecting' },
74
+    SELECTED_DRAW_TOOL: { unless: 'isReadOnly', to: 'draw' },
75
+    SELECTED_DOT_TOOL: { unless: 'isReadOnly', to: 'dot' },
76
+    SELECTED_CIRCLE_TOOL: { unless: 'isReadOnly', to: 'circle' },
77
+    SELECTED_ELLIPSE_TOOL: { unless: 'isReadOnly', to: 'ellipse' },
78
+    SELECTED_RAY_TOOL: { unless: 'isReadOnly', to: 'ray' },
79
+    SELECTED_LINE_TOOL: { unless: 'isReadOnly', to: 'line' },
80
+    SELECTED_POLYLINE_TOOL: { unless: 'isReadOnly', to: 'polyline' },
81
+    SELECTED_RECTANGLE_TOOL: { unless: 'isReadOnly', to: 'rectangle' },
82
+    TOGGLED_CODE_PANEL_OPEN: 'toggleCodePanel',
83
+    TOGGLED_STYLE_PANEL_OPEN: 'toggleStylePanel',
84
+    CHANGED_STYLE: ['updateStyles', 'applyStylesToSelection'],
85
+    RESET_CAMERA: 'resetCamera',
86
+    ZOOMED_TO_FIT: 'zoomCameraToFit',
87 87
     ZOOMED_TO_SELECTION: {
88
-      if: "hasSelection",
89
-      do: "zoomCameraToSelection",
88
+      if: 'hasSelection',
89
+      do: 'zoomCameraToSelection',
90 90
     },
91 91
     ZOOMED_TO_ACTUAL: {
92
-      if: "hasSelection",
93
-      do: "zoomCameraToSelectionActual",
94
-      else: "zoomCameraToActual",
92
+      if: 'hasSelection',
93
+      do: 'zoomCameraToSelectionActual',
94
+      else: 'zoomCameraToActual',
95 95
     },
96
-    SELECTED_ALL: { to: "selecting", do: "selectAll" },
96
+    SELECTED_ALL: { to: 'selecting', do: 'selectAll' },
97 97
   },
98
-  initial: "loading",
98
+  initial: 'loading',
99 99
   states: {
100 100
     loading: {
101 101
       on: {
102
-        MOUNTED: {
103
-          do: ["restoreSavedData", "zoomCameraToFit"],
104
-          to: "ready",
105
-        },
102
+        MOUNTED: [
103
+          'restoreSavedData',
104
+          {
105
+            if: 'hasSelection',
106
+            do: 'zoomCameraToSelectionActual',
107
+            else: ['zoomCameraToFit', 'zoomCameraToActual'],
108
+          },
109
+          {
110
+            to: 'ready',
111
+          },
112
+        ],
106 113
       },
107 114
     },
108 115
     ready: {
109 116
       on: {
110 117
         UNMOUNTED: [
111
-          { unless: "isReadOnly", do: "forceSave" },
112
-          { to: "loading" },
118
+          { unless: 'isReadOnly', do: 'forceSave' },
119
+          { to: 'loading' },
113 120
         ],
114 121
       },
115
-      initial: "selecting",
122
+      initial: 'selecting',
116 123
       states: {
117 124
         selecting: {
118 125
           on: {
119
-            SAVED: "forceSave",
120
-            UNDO: { do: "undo" },
121
-            REDO: { do: "redo" },
122
-            CANCELLED: { do: "clearSelectedIds" },
123
-            DELETED: { do: "deleteSelectedIds" },
124
-            SAVED_CODE: "saveCode",
125
-            GENERATED_FROM_CODE: ["setCodeControls", "setGeneratedShapes"],
126
-            INCREASED_CODE_FONT_SIZE: "increaseCodeFontSize",
127
-            DECREASED_CODE_FONT_SIZE: "decreaseCodeFontSize",
128
-            CHANGED_CODE_CONTROL: "updateControls",
129
-            ALIGNED: "alignSelection",
130
-            STRETCHED: "stretchSelection",
131
-            DISTRIBUTED: "distributeSelection",
132
-            MOVED: "moveSelection",
133
-            STARTED_PINCHING: { to: "pinching" },
126
+            SAVED: 'forceSave',
127
+            UNDO: { do: 'undo' },
128
+            REDO: { do: 'redo' },
129
+            CANCELLED: { do: 'clearSelectedIds' },
130
+            DELETED: { do: 'deleteSelectedIds' },
131
+            SAVED_CODE: 'saveCode',
132
+            GENERATED_FROM_CODE: ['setCodeControls', 'setGeneratedShapes'],
133
+            INCREASED_CODE_FONT_SIZE: 'increaseCodeFontSize',
134
+            DECREASED_CODE_FONT_SIZE: 'decreaseCodeFontSize',
135
+            CHANGED_CODE_CONTROL: 'updateControls',
136
+            ALIGNED: 'alignSelection',
137
+            STRETCHED: 'stretchSelection',
138
+            DISTRIBUTED: 'distributeSelection',
139
+            MOVED: 'moveSelection',
140
+            STARTED_PINCHING: { to: 'pinching' },
134 141
           },
135
-          initial: "notPointing",
142
+          initial: 'notPointing',
136 143
           states: {
137 144
             notPointing: {
138 145
               on: {
139
-                POINTED_CANVAS: { to: "brushSelecting" },
140
-                POINTED_BOUNDS: { to: "pointingBounds" },
146
+                POINTED_CANVAS: { to: 'brushSelecting' },
147
+                POINTED_BOUNDS: { to: 'pointingBounds' },
141 148
                 POINTED_BOUNDS_HANDLE: {
142
-                  if: "isPointingRotationHandle",
143
-                  to: "rotatingSelection",
144
-                  else: { to: "transformingSelection" },
149
+                  if: 'isPointingRotationHandle',
150
+                  to: 'rotatingSelection',
151
+                  else: { to: 'transformingSelection' },
145 152
                 },
146 153
                 MOVED_OVER_SHAPE: {
147
-                  if: "pointHitsShape",
154
+                  if: 'pointHitsShape',
148 155
                   then: {
149
-                    unless: "shapeIsHovered",
150
-                    do: "setHoveredId",
156
+                    unless: 'shapeIsHovered',
157
+                    do: 'setHoveredId',
151 158
                   },
152
-                  else: { if: "shapeIsHovered", do: "clearHoveredId" },
159
+                  else: { if: 'shapeIsHovered', do: 'clearHoveredId' },
153 160
                 },
154
-                UNHOVERED_SHAPE: "clearHoveredId",
161
+                UNHOVERED_SHAPE: 'clearHoveredId',
155 162
                 POINTED_SHAPE: [
156 163
                   {
157
-                    if: "isPressingMetaKey",
158
-                    to: "brushSelecting",
164
+                    if: 'isPressingMetaKey',
165
+                    to: 'brushSelecting',
159 166
                   },
160
-                  "setPointedId",
167
+                  'setPointedId',
161 168
                   {
162
-                    unless: "isPointedShapeSelected",
169
+                    unless: 'isPointedShapeSelected',
163 170
                     then: {
164
-                      if: "isPressingShiftKey",
165
-                      do: ["pushPointedIdToSelectedIds", "clearPointedId"],
166
-                      else: ["clearSelectedIds", "pushPointedIdToSelectedIds"],
171
+                      if: 'isPressingShiftKey',
172
+                      do: ['pushPointedIdToSelectedIds', 'clearPointedId'],
173
+                      else: ['clearSelectedIds', 'pushPointedIdToSelectedIds'],
167 174
                     },
168 175
                   },
169 176
                   {
170
-                    to: "pointingBounds",
177
+                    to: 'pointingBounds',
171 178
                   },
172 179
                 ],
173 180
               },
@@ -176,151 +183,153 @@ const state = createState({
176 183
               on: {
177 184
                 STOPPED_POINTING: [
178 185
                   {
179
-                    if: "isPressingShiftKey",
186
+                    if: 'isPressingShiftKey',
180 187
                     then: {
181
-                      if: "isPointedShapeSelected",
182
-                      do: "pullPointedIdFromSelectedIds",
188
+                      if: 'isPointedShapeSelected',
189
+                      do: 'pullPointedIdFromSelectedIds',
183 190
                     },
184 191
                     else: {
185
-                      unless: "isPointingBounds",
186
-                      do: ["clearSelectedIds", "pushPointedIdToSelectedIds"],
192
+                      unless: 'isPointingBounds',
193
+                      do: ['clearSelectedIds', 'pushPointedIdToSelectedIds'],
187 194
                     },
188 195
                   },
189
-                  { to: "notPointing" },
196
+                  { to: 'notPointing' },
190 197
                 ],
191 198
                 MOVED_POINTER: {
192
-                  unless: "isReadOnly",
193
-                  if: "distanceImpliesDrag",
194
-                  to: "draggingSelection",
199
+                  unless: 'isReadOnly',
200
+                  if: 'distanceImpliesDrag',
201
+                  to: 'draggingSelection',
195 202
                 },
196 203
               },
197 204
             },
198 205
             rotatingSelection: {
199
-              onEnter: "startRotateSession",
200
-              onExit: "clearBoundsRotation",
206
+              onEnter: 'startRotateSession',
207
+              onExit: 'clearBoundsRotation',
201 208
               on: {
202
-                MOVED_POINTER: "updateRotateSession",
203
-                PANNED_CAMERA: "updateRotateSession",
204
-                PRESSED_SHIFT_KEY: "keyUpdateRotateSession",
205
-                RELEASED_SHIFT_KEY: "keyUpdateRotateSession",
206
-                STOPPED_POINTING: { do: "completeSession", to: "selecting" },
207
-                CANCELLED: { do: "cancelSession", to: "selecting" },
209
+                MOVED_POINTER: 'updateRotateSession',
210
+                PANNED_CAMERA: 'updateRotateSession',
211
+                PRESSED_SHIFT_KEY: 'keyUpdateRotateSession',
212
+                RELEASED_SHIFT_KEY: 'keyUpdateRotateSession',
213
+                STOPPED_POINTING: { do: 'completeSession', to: 'selecting' },
214
+                CANCELLED: { do: 'cancelSession', to: 'selecting' },
208 215
               },
209 216
             },
210 217
             transformingSelection: {
211
-              onEnter: "startTransformSession",
218
+              onEnter: 'startTransformSession',
212 219
               on: {
213
-                MOVED_POINTER: "updateTransformSession",
214
-                PANNED_CAMERA: "updateTransformSession",
215
-                PRESSED_SHIFT_KEY: "keyUpdateTransformSession",
216
-                RELEASED_SHIFT_KEY: "keyUpdateTransformSession",
217
-                STOPPED_POINTING: { do: "completeSession", to: "selecting" },
218
-                CANCELLED: { do: "cancelSession", to: "selecting" },
220
+                MOVED_POINTER: 'updateTransformSession',
221
+                PANNED_CAMERA: 'updateTransformSession',
222
+                PRESSED_SHIFT_KEY: 'keyUpdateTransformSession',
223
+                RELEASED_SHIFT_KEY: 'keyUpdateTransformSession',
224
+                STOPPED_POINTING: { do: 'completeSession', to: 'selecting' },
225
+                CANCELLED: { do: 'cancelSession', to: 'selecting' },
219 226
               },
220 227
             },
221 228
             draggingSelection: {
222
-              onEnter: "startTranslateSession",
229
+              onEnter: 'startTranslateSession',
223 230
               on: {
224
-                MOVED_POINTER: "updateTranslateSession",
225
-                PANNED_CAMERA: "updateTranslateSession",
226
-                PRESSED_SHIFT_KEY: "keyUpdateTranslateSession",
227
-                RELEASED_SHIFT_KEY: "keyUpdateTranslateSession",
228
-                PRESSED_ALT_KEY: "keyUpdateTranslateSession",
229
-                RELEASED_ALT_KEY: "keyUpdateTranslateSession",
230
-                STOPPED_POINTING: { do: "completeSession", to: "selecting" },
231
-                CANCELLED: { do: "cancelSession", to: "selecting" },
231
+                MOVED_POINTER: 'updateTranslateSession',
232
+                PANNED_CAMERA: 'updateTranslateSession',
233
+                PRESSED_SHIFT_KEY: 'keyUpdateTranslateSession',
234
+                RELEASED_SHIFT_KEY: 'keyUpdateTranslateSession',
235
+                PRESSED_ALT_KEY: 'keyUpdateTranslateSession',
236
+                RELEASED_ALT_KEY: 'keyUpdateTranslateSession',
237
+                STOPPED_POINTING: { do: 'completeSession', to: 'selecting' },
238
+                CANCELLED: { do: 'cancelSession', to: 'selecting' },
232 239
               },
233 240
             },
234 241
             brushSelecting: {
235 242
               onEnter: [
236 243
                 {
237
-                  unless: ["isPressingMetaKey", "isPressingShiftKey"],
238
-                  do: "clearSelectedIds",
244
+                  unless: ['isPressingMetaKey', 'isPressingShiftKey'],
245
+                  do: 'clearSelectedIds',
239 246
                 },
240
-                "clearBoundsRotation",
241
-                "startBrushSession",
247
+                'clearBoundsRotation',
248
+                'startBrushSession',
242 249
               ],
243 250
               on: {
244
-                MOVED_POINTER: "updateBrushSession",
245
-                PANNED_CAMERA: "updateBrushSession",
246
-                STOPPED_POINTING: { do: "completeSession", to: "selecting" },
247
-                CANCELLED: { do: "cancelSession", to: "selecting" },
251
+                MOVED_POINTER: 'updateBrushSession',
252
+                PANNED_CAMERA: 'updateBrushSession',
253
+                STOPPED_POINTING: { do: 'completeSession', to: 'selecting' },
254
+                CANCELLED: { do: 'cancelSession', to: 'selecting' },
248 255
               },
249 256
             },
250 257
           },
251 258
         },
252 259
         pinching: {
253 260
           on: {
254
-            STOPPED_PINCHING: { to: "selecting" },
255
-            PINCHED: { do: "pinchCamera" },
261
+            STOPPED_PINCHING: { to: 'selecting' },
262
+            PINCHED: { do: 'pinchCamera' },
256 263
           },
257 264
         },
258 265
         draw: {
259
-          initial: "creating",
266
+          initial: 'creating',
260 267
           states: {
261 268
             creating: {
262 269
               on: {
270
+                CANCELLED: { to: 'selecting' },
263 271
                 POINTED_CANVAS: {
264
-                  get: "newDraw",
265
-                  do: "createShape",
266
-                  to: "draw.editing",
272
+                  get: 'newDraw',
273
+                  do: 'createShape',
274
+                  to: 'draw.editing',
267 275
                 },
268
-                UNDO: { do: "undo" },
269
-                REDO: { do: "redo" },
276
+                UNDO: { do: 'undo' },
277
+                REDO: { do: 'redo' },
270 278
               },
271 279
             },
272 280
             editing: {
273
-              onEnter: "startDrawSession",
281
+              onEnter: 'startDrawSession',
274 282
               on: {
275 283
                 STOPPED_POINTING: {
276
-                  do: "completeSession",
277
-                  to: "draw.creating",
284
+                  do: 'completeSession',
285
+                  to: 'draw.creating',
278 286
                 },
279 287
                 CANCELLED: {
280
-                  do: ["cancelSession", "deleteSelectedIds"],
281
-                  to: "selecting",
288
+                  do: ['cancelSession', 'deleteSelectedIds'],
289
+                  to: 'selecting',
282 290
                 },
283
-                MOVED_POINTER: "updateDrawSession",
284
-                PANNED_CAMERA: "updateDrawSession",
291
+                MOVED_POINTER: 'updateDrawSession',
292
+                PANNED_CAMERA: 'updateDrawSession',
285 293
               },
286 294
             },
287 295
           },
288 296
         },
289 297
         dot: {
290
-          initial: "creating",
298
+          initial: 'creating',
291 299
           states: {
292 300
             creating: {
293 301
               on: {
302
+                CANCELLED: { to: 'selecting' },
294 303
                 POINTED_CANVAS: {
295
-                  get: "newDot",
296
-                  do: "createShape",
297
-                  to: "dot.editing",
304
+                  get: 'newDot',
305
+                  do: 'createShape',
306
+                  to: 'dot.editing',
298 307
                 },
299 308
               },
300 309
             },
301 310
             editing: {
302 311
               on: {
303
-                STOPPED_POINTING: { do: "completeSession", to: "selecting" },
312
+                STOPPED_POINTING: { do: 'completeSession', to: 'selecting' },
304 313
                 CANCELLED: {
305
-                  do: ["cancelSession", "deleteSelectedIds"],
306
-                  to: "selecting",
314
+                  do: ['cancelSession', 'deleteSelectedIds'],
315
+                  to: 'selecting',
307 316
                 },
308 317
               },
309
-              initial: "inactive",
318
+              initial: 'inactive',
310 319
               states: {
311 320
                 inactive: {
312 321
                   on: {
313 322
                     MOVED_POINTER: {
314
-                      if: "distanceImpliesDrag",
315
-                      to: "dot.editing.active",
323
+                      if: 'distanceImpliesDrag',
324
+                      to: 'dot.editing.active',
316 325
                     },
317 326
                   },
318 327
                 },
319 328
                 active: {
320
-                  onEnter: "startTranslateSession",
329
+                  onEnter: 'startTranslateSession',
321 330
                   on: {
322
-                    MOVED_POINTER: "updateTranslateSession",
323
-                    PANNED_CAMERA: "updateTranslateSession",
331
+                    MOVED_POINTER: 'updateTranslateSession',
332
+                    PANNED_CAMERA: 'updateTranslateSession',
324 333
                   },
325 334
                 },
326 335
               },
@@ -328,25 +337,26 @@ const state = createState({
328 337
           },
329 338
         },
330 339
         circle: {
331
-          initial: "creating",
340
+          initial: 'creating',
332 341
           states: {
333 342
             creating: {
334 343
               on: {
344
+                CANCELLED: { to: 'selecting' },
335 345
                 POINTED_CANVAS: {
336
-                  to: "circle.editing",
346
+                  to: 'circle.editing',
337 347
                 },
338 348
               },
339 349
             },
340 350
             editing: {
341 351
               on: {
342
-                STOPPED_POINTING: { to: "selecting" },
343
-                CANCELLED: { to: "selecting" },
352
+                STOPPED_POINTING: { to: 'selecting' },
353
+                CANCELLED: { to: 'selecting' },
344 354
                 MOVED_POINTER: {
345
-                  if: "distanceImpliesDrag",
355
+                  if: 'distanceImpliesDrag',
346 356
                   then: {
347
-                    get: "newCircle",
348
-                    do: "createShape",
349
-                    to: "drawingShape.bounds",
357
+                    get: 'newCircle',
358
+                    do: 'createShape',
359
+                    to: 'drawingShape.bounds',
350 360
                   },
351 361
                 },
352 362
               },
@@ -354,26 +364,26 @@ const state = createState({
354 364
           },
355 365
         },
356 366
         ellipse: {
357
-          initial: "creating",
367
+          initial: 'creating',
358 368
           states: {
359 369
             creating: {
360 370
               on: {
361
-                CANCELLED: { to: "selecting" },
371
+                CANCELLED: { to: 'selecting' },
362 372
                 POINTED_CANVAS: {
363
-                  to: "ellipse.editing",
373
+                  to: 'ellipse.editing',
364 374
                 },
365 375
               },
366 376
             },
367 377
             editing: {
368 378
               on: {
369
-                STOPPED_POINTING: { to: "selecting" },
370
-                CANCELLED: { to: "selecting" },
379
+                STOPPED_POINTING: { to: 'selecting' },
380
+                CANCELLED: { to: 'selecting' },
371 381
                 MOVED_POINTER: {
372
-                  if: "distanceImpliesDrag",
382
+                  if: 'distanceImpliesDrag',
373 383
                   then: {
374
-                    get: "newEllipse",
375
-                    do: "createShape",
376
-                    to: "drawingShape.bounds",
384
+                    get: 'newEllipse',
385
+                    do: 'createShape',
386
+                    to: 'drawingShape.bounds',
377 387
                   },
378 388
                 },
379 389
               },
@@ -381,26 +391,26 @@ const state = createState({
381 391
           },
382 392
         },
383 393
         rectangle: {
384
-          initial: "creating",
394
+          initial: 'creating',
385 395
           states: {
386 396
             creating: {
387 397
               on: {
388
-                CANCELLED: { to: "selecting" },
398
+                CANCELLED: { to: 'selecting' },
389 399
                 POINTED_CANVAS: {
390
-                  to: "rectangle.editing",
400
+                  to: 'rectangle.editing',
391 401
                 },
392 402
               },
393 403
             },
394 404
             editing: {
395 405
               on: {
396
-                STOPPED_POINTING: { to: "selecting" },
397
-                CANCELLED: { to: "selecting" },
406
+                STOPPED_POINTING: { to: 'selecting' },
407
+                CANCELLED: { to: 'selecting' },
398 408
                 MOVED_POINTER: {
399
-                  if: "distanceImpliesDrag",
409
+                  if: 'distanceImpliesDrag',
400 410
                   then: {
401
-                    get: "newRectangle",
402
-                    do: "createShape",
403
-                    to: "drawingShape.bounds",
411
+                    get: 'newRectangle',
412
+                    do: 'createShape',
413
+                    to: 'drawingShape.bounds',
404 414
                   },
405 415
                 },
406 416
               },
@@ -408,50 +418,50 @@ const state = createState({
408 418
           },
409 419
         },
410 420
         ray: {
411
-          initial: "creating",
421
+          initial: 'creating',
412 422
           states: {
413 423
             creating: {
414 424
               on: {
415
-                CANCELLED: { to: "selecting" },
425
+                CANCELLED: { to: 'selecting' },
416 426
                 POINTED_CANVAS: {
417
-                  get: "newRay",
418
-                  do: "createShape",
419
-                  to: "ray.editing",
427
+                  get: 'newRay',
428
+                  do: 'createShape',
429
+                  to: 'ray.editing',
420 430
                 },
421 431
               },
422 432
             },
423 433
             editing: {
424 434
               on: {
425
-                STOPPED_POINTING: { to: "selecting" },
426
-                CANCELLED: { to: "selecting" },
435
+                STOPPED_POINTING: { to: 'selecting' },
436
+                CANCELLED: { to: 'selecting' },
427 437
                 MOVED_POINTER: {
428
-                  if: "distanceImpliesDrag",
429
-                  to: "drawingShape.direction",
438
+                  if: 'distanceImpliesDrag',
439
+                  to: 'drawingShape.direction',
430 440
                 },
431 441
               },
432 442
             },
433 443
           },
434 444
         },
435 445
         line: {
436
-          initial: "creating",
446
+          initial: 'creating',
437 447
           states: {
438 448
             creating: {
439 449
               on: {
440
-                CANCELLED: { to: "selecting" },
450
+                CANCELLED: { to: 'selecting' },
441 451
                 POINTED_CANVAS: {
442
-                  get: "newLine",
443
-                  do: "createShape",
444
-                  to: "line.editing",
452
+                  get: 'newLine',
453
+                  do: 'createShape',
454
+                  to: 'line.editing',
445 455
                 },
446 456
               },
447 457
             },
448 458
             editing: {
449 459
               on: {
450
-                STOPPED_POINTING: { to: "selecting" },
451
-                CANCELLED: { to: "selecting" },
460
+                STOPPED_POINTING: { to: 'selecting' },
461
+                CANCELLED: { to: 'selecting' },
452 462
                 MOVED_POINTER: {
453
-                  if: "distanceImpliesDrag",
454
-                  to: "drawingShape.direction",
463
+                  if: 'distanceImpliesDrag',
464
+                  to: 'drawingShape.direction',
455 465
                 },
456 466
               },
457 467
             },
@@ -463,28 +473,28 @@ const state = createState({
463 473
     drawingShape: {
464 474
       on: {
465 475
         STOPPED_POINTING: {
466
-          do: "completeSession",
467
-          to: "selecting",
476
+          do: 'completeSession',
477
+          to: 'selecting',
468 478
         },
469 479
         CANCELLED: {
470
-          do: ["cancelSession", "deleteSelectedIds"],
471
-          to: "selecting",
480
+          do: ['cancelSession', 'deleteSelectedIds'],
481
+          to: 'selecting',
472 482
         },
473 483
       },
474
-      initial: "drawingShapeBounds",
484
+      initial: 'drawingShapeBounds',
475 485
       states: {
476 486
         bounds: {
477
-          onEnter: "startDrawTransformSession",
487
+          onEnter: 'startDrawTransformSession',
478 488
           on: {
479
-            MOVED_POINTER: "updateTransformSession",
480
-            PANNED_CAMERA: "updateTransformSession",
489
+            MOVED_POINTER: 'updateTransformSession',
490
+            PANNED_CAMERA: 'updateTransformSession',
481 491
           },
482 492
         },
483 493
         direction: {
484
-          onEnter: "startDirectionSession",
494
+          onEnter: 'startDirectionSession',
485 495
           on: {
486
-            MOVED_POINTER: "updateDirectionSession",
487
-            PANNED_CAMERA: "updateDirectionSession",
496
+            MOVED_POINTER: 'updateDirectionSession',
497
+            PANNED_CAMERA: 'updateDirectionSession',
488 498
           },
489 499
         },
490 500
       },
@@ -515,7 +525,7 @@ const state = createState({
515 525
   },
516 526
   conditions: {
517 527
     isPointingBounds(data, payload: PointerInfo) {
518
-      return payload.target === "bounds"
528
+      return payload.target === 'bounds'
519 529
     },
520 530
     isReadOnly(data) {
521 531
       return data.isReadOnly
@@ -545,9 +555,9 @@ const state = createState({
545 555
     },
546 556
     isPointingRotationHandle(
547 557
       data,
548
-      payload: { target: Edge | Corner | "rotate" }
558
+      payload: { target: Edge | Corner | 'rotate' }
549 559
     ) {
550
-      return payload.target === "rotate"
560
+      return payload.target === 'rotate'
551 561
     },
552 562
     hasSelection(data) {
553 563
       return data.selectedIds.size > 0
@@ -753,7 +763,7 @@ const state = createState({
753 763
       data.camera.zoom = 1
754 764
       data.camera.point = [window.innerWidth / 2, window.innerHeight / 2]
755 765
 
756
-      document.documentElement.style.setProperty("--camera-zoom", "1")
766
+      document.documentElement.style.setProperty('--camera-zoom', '1')
757 767
     },
758 768
     zoomCameraToActual(data) {
759 769
       const { camera } = data
@@ -983,7 +993,7 @@ const state = createState({
983 993
 
984 994
       if (selectedIds.size === 1) {
985 995
         if (!shapes[0]) {
986
-          console.error("Could not find that shape! Clearing selected IDs.")
996
+          console.error('Could not find that shape! Clearing selected IDs.')
987 997
           data.selectedIds.clear()
988 998
           return null
989 999
         }

+ 4
- 4
yarn.lock Voir le fichier

@@ -6392,10 +6392,10 @@ pend@~1.2.0:
6392 6392
   resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
6393 6393
   integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
6394 6394
 
6395
-perfect-freehand@^0.4.71:
6396
-  version "0.4.71"
6397
-  resolved "https://registry.yarnpkg.com/perfect-freehand/-/perfect-freehand-0.4.71.tgz#b98ffc3cbc4e3cd930528e8d74a8849ee77475fb"
6398
-  integrity sha512-bJ3w2E6WcUfZJTXWPlS7DI6FIT9rRIYSCXgDYjvST8sAe/c+zNnJnlfJp3q8Hk1uPt9dH7bFuj8Sico6CKZw7A==
6395
+perfect-freehand@^0.4.8:
6396
+  version "0.4.8"
6397
+  resolved "https://registry.yarnpkg.com/perfect-freehand/-/perfect-freehand-0.4.8.tgz#0054995322fdd9939c0c38c260d96a9d0f22eb4f"
6398
+  integrity sha512-zU0hvTh0ctjb/h5+nwFhb+/5ZnqS8Z16mn7lY2tYy3NwTkjrEKZ8CJvQ2phlOV5kk+BnCDFGVz7tkKrl9+Mr9w==
6399 6399
 
6400 6400
 performance-now@^2.1.0:
6401 6401
   version "2.1.0"

Chargement…
Annuler
Enregistrer