Adds bounds

This commit is contained in:
Steve Ruiz 2021-05-12 23:08:53 +01:00
parent 3d52d9e9d2
commit 89d9ddcb1d
14 changed files with 531 additions and 41 deletions

View file

@ -1 +1,33 @@
export default function BoundsBg() {} import state, { useSelector } from "state"
import styled from "styles"
export default function BoundsBg() {
const bounds = useSelector((state) => state.values.selectedBounds)
if (!bounds) return null
const { minX, minY, width, height } = bounds
return (
<StyledBoundsBg
x={minX}
y={minY}
width={width}
height={height}
onPointerDown={(e) => {
if (e.buttons !== 1) return
state.send("POINTED_BOUNDS", {
shiftKey: e.shiftKey,
optionKey: e.altKey,
metaKey: e.metaKey || e.ctrlKey,
ctrlKey: e.ctrlKey,
buttons: e.buttons,
})
}}
/>
)
}
const StyledBoundsBg = styled("rect", {
fill: "$boundsBg",
})

View file

@ -1 +1,297 @@
export default function Bounds() {} import state, { useSelector } from "state"
import { motion } from "framer-motion"
import styled from "styles"
export default function Bounds() {
const bounds = useSelector((state) => state.values.selectedBounds)
const isBrushing = useSelector((state) => state.isIn("brushSelecting"))
const zoom = useSelector((state) => state.data.camera.zoom)
if (!bounds) return null
const { minX, minY, maxX, maxY, width, height } = bounds
const p = 4 / zoom
const cp = p * 2
return (
<g pointerEvents={isBrushing ? "none" : "all"}>
<StyledBounds
x={minX}
y={minY}
width={width}
height={height}
pointerEvents="none"
/>
<Corner
x={minX}
y={minY}
corner={0}
width={cp}
height={cp}
cursor="nwse-resize"
/>
<Corner
x={maxX}
y={minY}
corner={1}
width={cp}
height={cp}
cursor="nesw-resize"
/>
<Corner
x={maxX}
y={maxY}
corner={2}
width={cp}
height={cp}
cursor="nwse-resize"
/>
<Corner
x={minX}
y={maxY}
corner={3}
width={cp}
height={cp}
cursor="nesw-resize"
/>
<EdgeHorizontal
x={minX + p}
y={minY}
width={Math.max(0, width - p * 2)}
height={p}
onSelect={(e) => {
e.stopPropagation()
if (e.buttons !== 1) return
state.send("POINTED_BOUNDS_EDGE", {
edge: 0,
shiftKey: e.shiftKey,
optionKey: e.altKey,
metaKey: e.metaKey,
ctrlKey: e.ctrlKey,
buttons: e.buttons,
})
document.body.style.cursor = "ns-resize"
}}
/>
<EdgeVertical
x={maxX}
y={minY + p}
width={p}
height={Math.max(0, height - p * 2)}
onSelect={(e) => {
e.stopPropagation()
if (e.buttons !== 1) return
state.send("POINTED_BOUNDS_EDGE", {
edge: 1,
shiftKey: e.shiftKey,
optionKey: e.altKey,
metaKey: e.metaKey,
ctrlKey: e.ctrlKey,
buttons: e.buttons,
})
document.body.style.cursor = "ew-resize"
}}
/>
<EdgeHorizontal
x={minX + p}
y={maxY}
width={Math.max(0, width - p * 2)}
height={p}
onSelect={(e) => {
e.stopPropagation()
if (e.buttons !== 1) return
state.send("POINTED_BOUNDS_EDGE", {
edge: 2,
shiftKey: e.shiftKey,
optionKey: e.altKey,
metaKey: e.metaKey,
ctrlKey: e.ctrlKey,
buttons: e.buttons,
})
document.body.style.cursor = "ns-resize"
}}
/>
<EdgeVertical
x={minX}
y={minY + p}
width={p}
height={Math.max(0, height - p * 2)}
onSelect={(e) => {
e.stopPropagation()
if (e.buttons !== 1) return
state.send("POINTED_BOUNDS_EDGE", {
edge: 3,
shiftKey: e.shiftKey,
optionKey: e.altKey,
metaKey: e.metaKey,
ctrlKey: e.ctrlKey,
buttons: e.buttons,
})
document.body.style.cursor = "ew-resize"
}}
/>
</g>
)
}
function Corner({
x,
y,
width,
height,
cursor,
onHover,
corner,
}: {
x: number
y: number
width: number
height: number
cursor: string
corner: number
onHover?: () => void
}) {
const isTop = corner === 0 || corner === 1
const isLeft = corner === 0 || corner === 3
return (
<g>
<motion.rect
x={x + width * (isLeft ? -1.25 : -0.5)} // + width * 2 * transformOffset[0]}
y={y + width * (isTop ? -1.25 : -0.5)} // + height * 2 * transformOffset[1]}
width={width * 1.75}
height={height * 1.75}
onPanEnd={restoreCursor}
onTap={restoreCursor}
onPointerDown={(e) => {
e.stopPropagation()
if (e.buttons !== 1) return
state.send("POINTED_ROTATE_CORNER", {
corner,
shiftKey: e.shiftKey,
optionKey: e.altKey,
metaKey: e.metaKey,
ctrlKey: e.ctrlKey,
buttons: e.buttons,
})
document.body.style.cursor = "grabbing"
}}
style={{ cursor: "grab" }}
fill="transparent"
/>
<StyledCorner
x={x + width * -0.5}
y={y + height * -0.5}
width={width}
height={height}
onPointerEnter={onHover}
onPointerDown={(e) => {
e.stopPropagation()
if (e.buttons !== 1) return
state.send("POINTED_BOUNDS_CORNER", {
corner,
shiftKey: e.shiftKey,
optionKey: e.altKey,
metaKey: e.metaKey,
ctrlKey: e.ctrlKey,
buttons: e.buttons,
})
document.body.style.cursor = "nesw-resize"
}}
onPanEnd={restoreCursor}
onTap={restoreCursor}
style={{ cursor }}
className="strokewidth-ui stroke-bounds fill-corner"
/>
</g>
)
}
function EdgeHorizontal({
x,
y,
width,
height,
onHover,
onSelect,
}: {
x: number
y: number
width: number
height: number
onHover?: () => void
onSelect?: (e: React.PointerEvent) => void
}) {
return (
<StyledEdge
x={x}
y={y - height / 2}
width={width}
height={height}
onPointerEnter={onHover}
onPointerDown={onSelect}
onPanEnd={restoreCursor}
onTap={restoreCursor}
style={{ cursor: "ns-resize" }}
direction="horizontal"
/>
)
}
function EdgeVertical({
x,
y,
width,
height,
onHover,
onSelect,
}: {
x: number
y: number
width: number
height: number
onHover?: () => void
onSelect?: (e: React.PointerEvent) => void
}) {
return (
<StyledEdge
x={x - width / 2}
y={y}
width={width}
height={height}
onPointerEnter={onHover}
onPointerDown={onSelect}
onPanEnd={restoreCursor}
onTap={restoreCursor}
direction="vertical"
/>
)
}
function restoreCursor() {
document.body.style.cursor = "default"
state.send("STOPPED_POINTING")
}
const StyledEdge = styled(motion.rect, {
stroke: "none",
fill: "none",
variant: {
direction: {
horizontal: { cursor: "ns-resize" },
vertical: { cursor: "ew-resize" },
},
},
})
const StyledCorner = styled(motion.rect, {
stroke: "$bounds",
fill: "#fff",
zStrokeWidth: 2,
})
const StyledBounds = styled("rect", {
fill: "none",
stroke: "$bounds",
zStrokeWidth: 2,
})

View file

@ -6,6 +6,8 @@ import useCamera from "hooks/useCamera"
import Page from "./page" import Page from "./page"
import Brush from "./brush" import Brush from "./brush"
import state from "state" import state from "state"
import Bounds from "./bounds"
import BoundsBg from "./bounds-bg"
export default function Canvas() { export default function Canvas() {
const rCanvas = useRef<SVGSVGElement>(null) const rCanvas = useRef<SVGSVGElement>(null)
@ -37,7 +39,9 @@ export default function Canvas() {
onPointerUp={handlePointerUp} onPointerUp={handlePointerUp}
> >
<MainGroup ref={rGroup}> <MainGroup ref={rGroup}>
<BoundsBg />
<Page /> <Page />
<Bounds />
<Brush /> <Brush />
</MainGroup> </MainGroup>
</MainSVG> </MainSVG>

View file

@ -1,19 +1,8 @@
import React, { useCallback, useRef } from "react" import React, { useCallback, useRef, memo } from "react"
import state, { useSelector } from "state" import state, { useSelector } from "state"
import styled from "styles"
import { getPointerEventInfo } from "utils/utils" import { getPointerEventInfo } from "utils/utils"
import { memo } from "react" import shapes from "lib/shapes"
import Shapes from "lib/shapes" import styled from "styles"
/*
Gets the shape from the current page's shapes, using the
provided ID. Depending on the shape's type, return the
component for that type.
This component takes an SVG shape as its children. It handles
events for the shape as well as provides indicators for hover
and selected status
*/
function Shape({ id }: { id: string }) { function Shape({ id }: { id: string }) {
const rGroup = useRef<SVGGElement>(null) const rGroup = useRef<SVGGElement>(null)
@ -66,7 +55,7 @@ function Shape({ id }: { id: string }) {
onPointerLeave={handlePointerLeave} onPointerLeave={handlePointerLeave}
> >
<defs> <defs>
{Shapes[shape.type] ? Shapes[shape.type].render(shape) : null} {shapes[shape.type] ? shapes[shape.type].render(shape) : null}
</defs> </defs>
<HoverIndicator as="use" xlinkHref={"#" + id} /> <HoverIndicator as="use" xlinkHref={"#" + id} />
<use xlinkHref={"#" + id} {...shape.style} /> <use xlinkHref={"#" + id} {...shape.style} />
@ -78,7 +67,7 @@ function Shape({ id }: { id: string }) {
const Indicator = styled("path", { const Indicator = styled("path", {
fill: "none", fill: "none",
stroke: "transparent", stroke: "transparent",
strokeWidth: "max(1, calc(2 / var(--camera-zoom)))", zStrokeWidth: 1,
pointerEvents: "none", pointerEvents: "none",
strokeLineCap: "round", strokeLineCap: "round",
strokeLinejoin: "round", strokeLinejoin: "round",
@ -87,7 +76,7 @@ const Indicator = styled("path", {
const HoverIndicator = styled("path", { const HoverIndicator = styled("path", {
fill: "none", fill: "none",
stroke: "transparent", stroke: "transparent",
strokeWidth: "max(1, calc(8 / var(--camera-zoom)))", zStrokeWidth: 8,
pointerEvents: "all", pointerEvents: "all",
strokeLinecap: "round", strokeLinecap: "round",
strokeLinejoin: "round", strokeLinejoin: "round",

View file

@ -1,6 +1,7 @@
import { v4 as uuid } from "uuid" import { v4 as uuid } from "uuid"
import * as vec from "utils/vec" import * as vec from "utils/vec"
import { BaseLibShape, CircleShape, ShapeType } from "types" import { BaseLibShape, CircleShape, ShapeType } from "types"
import { boundsCache } from "./index"
const Circle: BaseLibShape<ShapeType.Circle> = { const Circle: BaseLibShape<ShapeType.Circle> = {
create(props): CircleShape { create(props): CircleShape {
@ -23,19 +24,26 @@ const Circle: BaseLibShape<ShapeType.Circle> = {
}, },
getBounds(shape) { getBounds(shape) {
if (boundsCache.has(shape)) {
return boundsCache.get(shape)
}
const { const {
point: [cx, cy], point: [x, y],
radius, radius,
} = shape } = shape
return { const bounds = {
minX: cx, minX: x,
maxX: cx + radius * 2, maxX: x + radius * 2,
minY: cy, minY: y,
maxY: cy + radius * 2, maxY: y + radius * 2,
width: radius * 2, width: radius * 2,
height: radius * 2, height: radius * 2,
} }
boundsCache.set(shape, bounds)
return bounds
}, },
hitTest(shape, test) { hitTest(shape, test) {

View file

@ -1,6 +1,7 @@
import { v4 as uuid } from "uuid" import { v4 as uuid } from "uuid"
import * as vec from "utils/vec" import * as vec from "utils/vec"
import { BaseLibShape, DotShape, ShapeType } from "types" import { BaseLibShape, DotShape, ShapeType } from "types"
import { boundsCache } from "./index"
const Dot: BaseLibShape<ShapeType.Dot> = { const Dot: BaseLibShape<ShapeType.Dot> = {
create(props): DotShape { create(props): DotShape {
@ -22,18 +23,25 @@ const Dot: BaseLibShape<ShapeType.Dot> = {
}, },
getBounds(shape) { getBounds(shape) {
if (boundsCache.has(shape)) {
return boundsCache.get(shape)
}
const { const {
point: [cx, cy], point: [x, y],
} = shape } = shape
return { const bounds = {
minX: cx, minX: x,
maxX: cx + 4, maxX: x + 8,
minY: cy, minY: y,
maxY: cy + 4, maxY: y + 8,
width: 4, width: 8,
height: 4, height: 8,
} }
boundsCache.set(shape, bounds)
return bounds
}, },
hitTest(shape, test) { hitTest(shape, test) {

View file

@ -3,11 +3,15 @@ import Dot from "./dot"
import Polyline from "./polyline" import Polyline from "./polyline"
import Rectangle from "./rectangle" import Rectangle from "./rectangle"
import { ShapeType } from "types" import { Bounds, Shape, ShapeType } from "types"
export default { export const boundsCache = new WeakMap<Shape, Bounds>([])
const shapes = {
[ShapeType.Circle]: Circle, [ShapeType.Circle]: Circle,
[ShapeType.Dot]: Dot, [ShapeType.Dot]: Dot,
[ShapeType.Polyline]: Polyline, [ShapeType.Polyline]: Polyline,
[ShapeType.Rectangle]: Rectangle, [ShapeType.Rectangle]: Rectangle,
} }
export default shapes

View file

@ -1,6 +1,7 @@
import { v4 as uuid } from "uuid" import { v4 as uuid } from "uuid"
import * as vec from "utils/vec" import * as vec from "utils/vec"
import { BaseLibShape, PolylineShape, ShapeType } from "types" import { BaseLibShape, PolylineShape, ShapeType } from "types"
import { boundsCache } from "./index"
const Polyline: BaseLibShape<ShapeType.Polyline> = { const Polyline: BaseLibShape<ShapeType.Polyline> = {
create(props): PolylineShape { create(props): PolylineShape {
@ -23,6 +24,10 @@ const Polyline: BaseLibShape<ShapeType.Polyline> = {
}, },
getBounds(shape) { getBounds(shape) {
if (boundsCache.has(shape)) {
return boundsCache.get(shape)
}
let minX = 0 let minX = 0
let minY = 0 let minY = 0
let maxX = 0 let maxX = 0
@ -35,7 +40,7 @@ const Polyline: BaseLibShape<ShapeType.Polyline> = {
maxY = Math.max(y, maxY) maxY = Math.max(y, maxY)
} }
return { const bounds = {
minX: minX + shape.point[0], minX: minX + shape.point[0],
minY: minY + shape.point[1], minY: minY + shape.point[1],
maxX: maxX + shape.point[0], maxX: maxX + shape.point[0],
@ -43,6 +48,9 @@ const Polyline: BaseLibShape<ShapeType.Polyline> = {
width: maxX - minX, width: maxX - minX,
height: maxY - minY, height: maxY - minY,
} }
boundsCache.set(shape, bounds)
return bounds
}, },
hitTest(shape) { hitTest(shape) {

View file

@ -1,6 +1,7 @@
import { v4 as uuid } from "uuid" import { v4 as uuid } from "uuid"
import * as vec from "utils/vec" import * as vec from "utils/vec"
import { BaseLibShape, RectangleShape, ShapeType } from "types" import { BaseLibShape, RectangleShape, ShapeType } from "types"
import { boundsCache } from "./index"
const Rectangle: BaseLibShape<ShapeType.Rectangle> = { const Rectangle: BaseLibShape<ShapeType.Rectangle> = {
create(props): RectangleShape { create(props): RectangleShape {
@ -23,12 +24,16 @@ const Rectangle: BaseLibShape<ShapeType.Rectangle> = {
}, },
getBounds(shape) { getBounds(shape) {
if (boundsCache.has(shape)) {
return boundsCache.get(shape)
}
const { const {
point: [x, y], point: [x, y],
size: [width, height], size: [width, height],
} = shape } = shape
return { const bounds = {
minX: x, minX: x,
maxX: x + width, maxX: x + width,
minY: y, minY: y,
@ -36,6 +41,9 @@ const Rectangle: BaseLibShape<ShapeType.Rectangle> = {
width, width,
height, height,
} }
boundsCache.set(shape, bounds)
return bounds
}, },
hitTest(shape) { hitTest(shape) {

View file

@ -11,6 +11,7 @@
"@state-designer/react": "^1.7.1", "@state-designer/react": "^1.7.1",
"@stitches/react": "^0.1.9", "@stitches/react": "^0.1.9",
"@types/uuid": "^8.3.0", "@types/uuid": "^8.3.0",
"framer-motion": "^4.1.16",
"next": "10.2.0", "next": "10.2.0",
"perfect-freehand": "^0.4.7", "perfect-freehand": "^0.4.7",
"react": "17.0.2", "react": "17.0.2",

View file

@ -1,8 +1,9 @@
import { createSelectorHook, createState } from "@state-designer/react" import { createSelectorHook, createState } from "@state-designer/react"
import { clamp, screenToWorld } from "utils/utils" import { clamp, getCommonBounds, screenToWorld } from "utils/utils"
import * as vec from "utils/vec" import * as vec from "utils/vec"
import { Data } from "types" import { Bounds, Data, Shape, ShapeType } from "types"
import { defaultDocument } from "./data" import { defaultDocument } from "./data"
import Shapes from "lib/shapes"
import * as Sessions from "./sessions" import * as Sessions from "./sessions"
const initialData: Data = { const initialData: Data = {
@ -131,6 +132,42 @@ const state = createState({
selectedIds(data) { selectedIds(data) {
return new Set(data.selectedIds) return new Set(data.selectedIds)
}, },
selectedBounds(data) {
const {
selectedIds,
currentPageId,
document: { pages },
} = data
return getCommonBounds(
...Array.from(selectedIds.values())
.map((id) => {
const shape = pages[currentPageId].shapes[id]
switch (shape.type) {
case ShapeType.Dot: {
return Shapes[shape.type].getBounds(shape)
}
case ShapeType.Circle: {
return Shapes[shape.type].getBounds(shape)
}
case ShapeType.Line: {
return Shapes[shape.type].getBounds(shape)
}
case ShapeType.Polyline: {
return Shapes[shape.type].getBounds(shape)
}
case ShapeType.Rectangle: {
return Shapes[shape.type].getBounds(shape)
}
default: {
return null
}
}
})
.filter(Boolean)
)
},
}, },
}) })

View file

@ -10,6 +10,8 @@ const { styled, global, css, theme, getCssString } = createCss({
brushStroke: "rgba(0,0,0,.5)", brushStroke: "rgba(0,0,0,.5)",
hint: "rgba(66, 133, 244, 0.200)", hint: "rgba(66, 133, 244, 0.200)",
selected: "rgba(66, 133, 244, 1.000)", selected: "rgba(66, 133, 244, 1.000)",
bounds: "rgba(65, 132, 244, 1.000)",
boundsBg: "rgba(65, 132, 244, 0.100)",
}, },
space: {}, space: {},
fontSizes: { fontSizes: {
@ -33,6 +35,11 @@ const { styled, global, css, theme, getCssString } = createCss({
zIndices: {}, zIndices: {},
transitions: {}, transitions: {},
}, },
utils: {
zStrokeWidth: () => (value: number) => ({
strokeWidth: `calc(${value}px / var(--camera-zoom))`,
}),
},
}) })
const light = theme({}) const light = theme({})

View file

@ -1,4 +1,4 @@
import { Data } from "types" import { Data, Bounds } from "types"
import * as svg from "./svg" import * as svg from "./svg"
import * as vec from "./vec" import * as vec from "./vec"
@ -6,6 +6,39 @@ export function screenToWorld(point: number[], data: Data) {
return vec.sub(vec.div(point, data.camera.zoom), data.camera.point) return vec.sub(vec.div(point, data.camera.zoom), data.camera.point)
} }
/**
* Get a bounding box that includes two bounding boxes.
* @param a Bounding box
* @param b Bounding box
* @returns
*/
export function getExpandedBounds(a: Bounds, b: Bounds) {
const minX = Math.min(a.minX, b.minX),
minY = Math.min(a.minY, b.minY),
maxX = Math.max(a.maxX, b.maxX),
maxY = Math.max(a.maxY, b.maxY),
width = Math.abs(maxX - minX),
height = Math.abs(maxY - minY)
return { minX, minY, maxX, maxY, width, height }
}
/**
* Get the common bounds of a group of bounds.
* @returns
*/
export function getCommonBounds(...b: Bounds[]) {
if (b.length < 2) return b[0]
let bounds = b[0]
for (let i = 1; i < b.length; i++) {
bounds = getExpandedBounds(bounds, b[i])
}
return bounds
}
export function getBoundsFromPoints(a: number[], b: number[]) { export function getBoundsFromPoints(a: number[], b: number[]) {
const minX = Math.min(a[0], b[0]) const minX = Math.min(a[0], b[0])
const maxX = Math.max(a[0], b[0]) const maxX = Math.max(a[0], b[0])

View file

@ -939,6 +939,18 @@
exec-sh "^0.3.2" exec-sh "^0.3.2"
minimist "^1.2.0" minimist "^1.2.0"
"@emotion/is-prop-valid@^0.8.2":
version "0.8.8"
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a"
integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==
dependencies:
"@emotion/memoize" "0.7.4"
"@emotion/memoize@0.7.4":
version "0.7.4"
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb"
integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
"@hapi/accept@5.0.1": "@hapi/accept@5.0.1":
version "5.0.1" version "5.0.1"
resolved "https://registry.yarnpkg.com/@hapi/accept/-/accept-5.0.1.tgz#068553e867f0f63225a506ed74e899441af53e10" resolved "https://registry.yarnpkg.com/@hapi/accept/-/accept-5.0.1.tgz#068553e867f0f63225a506ed74e899441af53e10"
@ -3402,6 +3414,26 @@ fragment-cache@^0.2.1:
dependencies: dependencies:
map-cache "^0.2.2" map-cache "^0.2.2"
framer-motion@^4.1.16:
version "4.1.16"
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-4.1.16.tgz#dc715334847d0a146acf47f61019222d0d1c46c9"
integrity sha512-sEc3UI3oncwE+RUzdd86TxbmpEaX/Ki/T0AmFYSsbxEqGZ3feLvzGL7BJlkhERIyyuAC9+OzI4BnhJM0GSUAMA==
dependencies:
framesync "5.3.0"
hey-listen "^1.0.8"
popmotion "9.3.6"
style-value-types "4.1.4"
tslib "^2.1.0"
optionalDependencies:
"@emotion/is-prop-valid" "^0.8.2"
framesync@5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/framesync/-/framesync-5.3.0.tgz#0ecfc955e8f5a6ddc8fdb0cc024070947e1a0d9b"
integrity sha512-oc5m68HDO/tuK2blj7ZcdEBRx3p1PjrgHazL8GYEpvULhrtGIFbQArN6cQS2QhW8mitffaB+VYzMjDqBxxQeoA==
dependencies:
tslib "^2.1.0"
fs-extra@8.1.0: fs-extra@8.1.0:
version "8.1.0" version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
@ -3652,6 +3684,11 @@ he@1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
hey-listen@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68"
integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==
hmac-drbg@^1.0.1: hmac-drbg@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
@ -5622,6 +5659,16 @@ pnp-webpack-plugin@1.6.4:
dependencies: dependencies:
ts-pnp "^1.1.6" ts-pnp "^1.1.6"
popmotion@9.3.6:
version "9.3.6"
resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-9.3.6.tgz#b5236fa28f242aff3871b9e23721f093133248d1"
integrity sha512-ZTbXiu6zIggXzIliMi8LGxXBF5ST+wkpXGEjeTUDUOCdSQ356hij/xjeUdv0F8zCQNeqB1+PR5/BB+gC+QLAPw==
dependencies:
framesync "5.3.0"
hey-listen "^1.0.8"
style-value-types "4.1.4"
tslib "^2.1.0"
posix-character-classes@^0.1.0: posix-character-classes@^0.1.0:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
@ -6734,6 +6781,14 @@ strip-json-comments@^3.0.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
style-value-types@4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-4.1.4.tgz#80f37cb4fb024d6394087403dfb275e8bb627e75"
integrity sha512-LCJL6tB+vPSUoxgUBt9juXIlNJHtBMy8jkXzUJSBzeHWdBu6lhzHqCvLVkXFGsFIlNa2ln1sQHya/gzaFmB2Lg==
dependencies:
hey-listen "^1.0.8"
tslib "^2.1.0"
styled-jsx@3.3.2: styled-jsx@3.3.2:
version "3.3.2" version "3.3.2"
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-3.3.2.tgz#2474601a26670a6049fb4d3f94bd91695b3ce018" resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-3.3.2.tgz#2474601a26670a6049fb4d3f94bd91695b3ce018"
@ -7051,7 +7106,7 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.3: tslib@^2.0.3, tslib@^2.1.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==