[refactor] ShapeUtils (#206)

* Starts refactor

* tests passing, got it

* Fix next
This commit is contained in:
Steve Ruiz 2021-10-27 16:15:01 +01:00 committed by GitHub
parent 8d6fe119a5
commit 2e6c33342d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
87 changed files with 1517 additions and 1554 deletions

View file

@ -1,9 +1,10 @@
import * as React from 'react'
import { mockUtils, renderWithContext } from '+test'
import { renderWithContext } from '+test'
import { Handles } from './handles'
import { boxShape } from '+shape-utils/TLShapeUtil.spec'
describe('handles', () => {
test('mounts component without crashing', () => {
renderWithContext(<Handles shape={mockUtils.box.create({ id: 'box' })} zoom={1} />)
renderWithContext(<Handles shape={boxShape} zoom={1} />)
})
})

View file

@ -1,12 +1,13 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as React from 'react'
import type { TLBinding, TLPage, TLPageState, TLShape, TLShapeUtil } from '+types'
import type { TLBinding, TLPage, TLPageState, TLShape } from '+types'
import { useSelection, useShapeTree, useTLContext } from '+hooks'
import { Bounds } from '+components/bounds'
import { BoundsBg } from '+components/bounds/bounds-bg'
import { Handles } from '+components/handles'
import { ShapeNode } from '+components/shape'
import { ShapeIndicator } from '+components/shape-indicator'
import type { TLShapeUtil } from '+shape-utils'
interface PageProps<T extends TLShape, M extends Record<string, unknown>> {
page: TLPage<T, TLBinding>

View file

@ -12,10 +12,10 @@ import type {
import { Canvas } from '../canvas'
import { Inputs } from '../../inputs'
import { useTLTheme, TLContext, TLContextType } from '../../hooks'
import type { TLShapeUtil, TLSnapLine, TLUsers } from '+index'
import type { TLSnapLine, TLUsers } from '+index'
import type { TLShapeUtilsMap } from '+shape-utils'
export interface RendererProps<T extends TLShape, E extends Element = any, M = any>
extends Partial<TLCallbacks<T>> {
export interface RendererProps<T extends TLShape, M = any> extends Partial<TLCallbacks<T>> {
/**
* (optional) A unique id to be applied to the renderer element, used to scope styles.
*/
@ -27,7 +27,7 @@ export interface RendererProps<T extends TLShape, E extends Element = any, M = a
/**
* An object containing instances of your shape classes.
*/
shapeUtils: Record<T['type'], TLShapeUtil<T, E, M>>
shapeUtils: TLShapeUtilsMap<T>
/**
* The current page, containing shapes and bindings.
*/
@ -104,7 +104,7 @@ export interface RendererProps<T extends TLShape, E extends Element = any, M = a
* @param props
* @returns
*/
export function Renderer<T extends TLShape, E extends Element, M extends Record<string, unknown>>({
export function Renderer<T extends TLShape, M extends Record<string, unknown>>({
id = 'tl',
shapeUtils,
page,
@ -123,7 +123,7 @@ export function Renderer<T extends TLShape, E extends Element, M extends Record<
hideBounds = false,
onMount,
...rest
}: RendererProps<T, E, M>): JSX.Element {
}: RendererProps<T, M>): JSX.Element {
useTLTheme(theme, '#' + id)
const rSelectionBounds = React.useRef<TLBounds>(null)
@ -134,7 +134,7 @@ export function Renderer<T extends TLShape, E extends Element, M extends Record<
rPageState.current = pageState
}, [pageState])
const [context] = React.useState<TLContextType<T, E, M>>(() => ({
const [context] = React.useState<TLContextType<T>>(() => ({
callbacks: rest,
shapeUtils,
rSelectionBounds,
@ -147,7 +147,7 @@ export function Renderer<T extends TLShape, E extends Element, M extends Record<
}, [context])
return (
<TLContext.Provider value={context as unknown as TLContextType<TLShape, Element>}>
<TLContext.Provider value={context as unknown as TLContextType<TLShape>}>
<Canvas
id={id}
page={page}

View file

@ -1,16 +1,12 @@
import * as React from 'react'
import { mockUtils, renderWithSvg } from '+test'
import { renderWithSvg } from '+test'
import { ShapeIndicator } from './shape-indicator'
import { boxShape } from '+shape-utils/TLShapeUtil.spec'
describe('shape indicator', () => {
test('mounts component without crashing', () => {
renderWithSvg(
<ShapeIndicator
shape={mockUtils.box.create({ id: 'box1' })}
isSelected={true}
isHovered={false}
meta={undefined}
/>
<ShapeIndicator shape={boxShape} isSelected={true} isHovered={false} meta={undefined} />
)
})
})

View file

@ -13,8 +13,8 @@ interface IndicatorProps<T extends TLShape, M = any> {
export const ShapeIndicator = React.memo(
<T extends TLShape, M = any>({
isHovered,
isSelected,
isHovered = false,
isSelected = false,
shape,
color,
meta,

View file

@ -1,9 +1,10 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import * as React from 'react'
import type { TLShapeUtil, TLRenderInfo, TLShape } from '+types'
import type { TLComponentProps, TLShape } from '+types'
import type { TLShapeUtil } from '+shape-utils'
interface RenderedShapeProps<T extends TLShape, E extends Element, M>
extends TLRenderInfo<T, E, M> {
extends TLComponentProps<T, E, M> {
shape: T
utils: TLShapeUtil<T, E, M>
}

View file

@ -1,13 +1,14 @@
import * as React from 'react'
import type { IShapeTreeNode, TLShape, TLShapeUtils } from '+types'
import type { IShapeTreeNode, TLShape } from '+types'
import { Shape } from './shape'
import type { TLShapeUtilsMap } from '+shape-utils'
interface ShapeNodeProps<T extends TLShape, E extends Element> extends IShapeTreeNode<T> {
utils: TLShapeUtils<T, E>
interface ShapeNodeProps<T extends TLShape> extends IShapeTreeNode<T> {
utils: TLShapeUtilsMap<TLShape>
}
export const ShapeNode = React.memo(
<T extends TLShape, E extends Element>({
<T extends TLShape>({
shape,
utils,
children,
@ -17,7 +18,7 @@ export const ShapeNode = React.memo(
isSelected,
isCurrentParent,
meta,
}: ShapeNodeProps<T, E>) => {
}: ShapeNodeProps<T>) => {
return (
<>
<Shape
@ -27,7 +28,7 @@ export const ShapeNode = React.memo(
isHovered={isHovered}
isSelected={isSelected}
isCurrentParent={isCurrentParent}
utils={utils[shape.type]}
utils={utils[shape.type as T['type']]}
meta={meta}
/>
{children &&

View file

@ -1,13 +1,16 @@
import * as React from 'react'
import { mockUtils, renderWithContext } from '+test'
import { renderWithContext } from '+test'
import { Shape } from './shape'
import { BoxUtil, boxShape } from '+shape-utils/TLShapeUtil.spec'
import type { TLShapeUtil } from '+shape-utils'
import type { TLShape } from '+types'
describe('shape', () => {
test('mounts component without crashing', () => {
renderWithContext(
<Shape
shape={mockUtils.box.create({ id: 'box' })}
utils={mockUtils[mockUtils.box.type]}
shape={boxShape}
utils={new BoxUtil() as unknown as TLShapeUtil<TLShape>}
isEditing={false}
isBinding={false}
isHovered={false}
@ -18,5 +21,5 @@ describe('shape', () => {
})
})
// { shape: TLShape; ref: ForwardedRef<Element>; } & TLRenderInfo<any, any> & RefAttributes<Element>
// { shape: BoxShape; ref: ForwardedRef<any>; } & TLRenderInfo<any, any> & RefAttributes<any>'
// { shape: TLShape; ref: ForwardedRef<Element>; } & TLComponentProps<any, any> & RefAttributes<Element>
// { shape: BoxShape; ref: ForwardedRef<any>; } & TLComponentProps<any, any> & RefAttributes<any>'

View file

@ -2,18 +2,19 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import * as React from 'react'
import { useShapeEvents } from '+hooks'
import type { IShapeTreeNode, TLShape, TLShapeUtil } from '+types'
import type { IShapeTreeNode, TLShape } from '+types'
import { RenderedShape } from './rendered-shape'
import { Container } from '+components/container'
import { useTLContext } from '+hooks'
import { useForceUpdate } from '+hooks/useForceUpdate'
import type { TLShapeUtil } from '+shape-utils'
interface ShapeProps<T extends TLShape, E extends Element, M> extends IShapeTreeNode<T, M> {
utils: TLShapeUtil<T, E, M>
interface ShapeProps<T extends TLShape, M> extends IShapeTreeNode<T, M> {
utils: TLShapeUtil<T>
}
export const Shape = React.memo(
<T extends TLShape, E extends Element, M>({
<T extends TLShape, M>({
shape,
utils,
isEditing,
@ -22,7 +23,7 @@ export const Shape = React.memo(
isSelected,
isCurrentParent,
meta,
}: ShapeProps<T, E, M>) => {
}: ShapeProps<T, M>) => {
const { callbacks } = useTLContext()
const bounds = utils.getBounds(shape)
const events = useShapeEvents(shape.id, isCurrentParent)

View file

@ -1,16 +1,21 @@
import * as React from 'react'
import type { TLPage, TLPageState, TLShape, TLBounds, TLShapeUtils, TLBinding } from '+types'
import type { TLPage, TLPageState, TLShape, TLBounds, TLBinding } from '+types'
import Utils from '+utils'
import { useTLContext } from '+hooks'
import type { TLShapeUtil, TLShapeUtilsMap } from '+shape-utils'
function canvasToScreen(point: number[], camera: TLPageState['camera']): number[] {
return [(point[0] + camera.point[0]) * camera.zoom, (point[1] + camera.point[1]) * camera.zoom]
}
export function useSelection<T extends TLShape, E extends Element>(
function getShapeUtils<T extends TLShape>(shapeUtils: TLShapeUtilsMap<T>, shape: T) {
return shapeUtils[shape.type as T['type']] as unknown as TLShapeUtil<T>
}
export function useSelection<T extends TLShape>(
page: TLPage<T, TLBinding>,
pageState: TLPageState,
shapeUtils: TLShapeUtils<T, E>
shapeUtils: TLShapeUtilsMap<T>
) {
const { rSelectionBounds } = useTLContext()
const { selectedIds } = pageState
@ -30,7 +35,7 @@ export function useSelection<T extends TLShape, E extends Element>(
isLocked = shape.isLocked || false
bounds = shapeUtils[shape.type as T['type']].getBounds(shape)
bounds = getShapeUtils(shapeUtils, shape).getBounds(shape)
} else if (selectedIds.length > 1) {
const selectedShapes = selectedIds.map((id) => page.shapes[id])
@ -40,12 +45,9 @@ export function useSelection<T extends TLShape, E extends Element>(
bounds = selectedShapes.reduce((acc, shape, i) => {
if (i === 0) {
return shapeUtils[shape.type as T['type']].getRotatedBounds(shape)
return getShapeUtils(shapeUtils, shape).getRotatedBounds(shape)
}
return Utils.getExpandedBounds(
acc,
shapeUtils[shape.type as T['type']].getRotatedBounds(shape)
)
return Utils.getExpandedBounds(acc, getShapeUtils(shapeUtils, shape).getRotatedBounds(shape))
}, {} as TLBounds)
}

View file

@ -6,14 +6,13 @@ import type {
TLPage,
TLPageState,
TLShape,
TLShapeUtils,
TLCallbacks,
TLBinding,
TLBounds,
TLShapeUtil,
} from '+types'
import { Utils } from '+utils'
import { Vec } from '@tldraw/vec'
import type { TLShapeUtilsMap } from '+shape-utils'
function addToShapeTree<T extends TLShape, M extends Record<string, unknown>>(
shape: T,
@ -70,21 +69,10 @@ function shapeIsInViewport(bounds: TLBounds, viewport: TLBounds) {
return Utils.boundsContain(viewport, bounds) || Utils.boundsCollide(viewport, bounds)
}
function getShapeUtils<T extends TLShape, E extends Element>(
shape: T,
shapeUtils: TLShapeUtils<T, E>
) {
return shapeUtils[shape.type] as TLShapeUtil<T, E>
}
export function useShapeTree<
T extends TLShape,
E extends Element,
M extends Record<string, unknown>
>(
export function useShapeTree<T extends TLShape, M extends Record<string, unknown>>(
page: TLPage<T, TLBinding>,
pageState: TLPageState,
shapeUtils: TLShapeUtils<T, E>,
shapeUtils: TLShapeUtilsMap<T>,
size: number[],
meta?: M,
onRenderCountChange?: TLCallbacks<T>['onRenderCountChange']
@ -122,11 +110,11 @@ export function useShapeTree<
.filter(
(shape) =>
// Always render shapes that are flagged as stateful
getShapeUtils(shape, shapeUtils).isStateful ||
shapeUtils[shape.type as T['type']].isStateful ||
// Always render selected shapes (this preserves certain drag interactions)
selectedIds.includes(shape.id) ||
// Otherwise, only render shapes that are in view
shapeIsInViewport(shapeUtils[shape.type as T['type']].getBounds(shape), viewport)
shapeIsInViewport(shapeUtils[shape.type as T['type']].getBounds(shape as any), viewport)
)
.sort((a, b) => a.childIndex - b.childIndex)
.forEach((shape) => {

View file

@ -1,18 +1,19 @@
import * as React from 'react'
import type { Inputs } from '+inputs'
import type { TLCallbacks, TLShape, TLBounds, TLPageState, TLShapeUtils } from '+types'
import type { TLCallbacks, TLShape, TLBounds, TLPageState } from '+types'
import type { TLShapeUtilsMap } from '+shape-utils'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export interface TLContextType<T extends TLShape, E extends Element, M = any> {
export interface TLContextType<T extends TLShape> {
id?: string
callbacks: Partial<TLCallbacks<T>>
shapeUtils: TLShapeUtils<T, E, M>
shapeUtils: TLShapeUtilsMap<T>
rPageState: React.MutableRefObject<TLPageState>
rSelectionBounds: React.MutableRefObject<TLBounds | null>
inputs: Inputs
}
export const TLContext = React.createContext({} as TLContextType<TLShape, Element>)
export const TLContext = React.createContext({} as TLContextType<TLShape>)
export function useTLContext() {
const context = React.useContext(TLContext)

View file

@ -2,4 +2,4 @@ export * from './components'
export * from './types'
export * from './utils'
export * from './inputs'
export * from './shapes'
export * from './shape-utils'

View file

@ -0,0 +1,230 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as React from 'react'
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { TLShape, TLBounds } from '+types'
import { TLShapeUtil } from './TLShapeUtil'
import { render } from '@testing-library/react'
import { SVGContainer } from '+components'
import Utils from '+utils'
import type { TLComponent, TLIndicator } from '+shape-utils'
export interface BoxShape extends TLShape {
type: 'box'
size: number[]
}
const meta = { legs: 93 }
type Meta = typeof meta
export const boxShape: BoxShape = {
id: 'example1',
type: 'box',
parentId: 'page',
childIndex: 0,
name: 'Example Shape',
point: [0, 0],
size: [100, 100],
rotation: 0,
}
export class BoxUtil extends TLShapeUtil<BoxShape, SVGSVGElement, Meta> {
age = 100
Component: TLComponent<BoxShape, SVGSVGElement, Meta> = ({ shape, events, meta }, ref) => {
type T = typeof meta.legs
type C = T['toFixed']
return (
<SVGContainer ref={ref}>
<g {...events}>
<rect width={shape.size[0]} height={shape.size[1]} fill="none" stroke="black" />
</g>
</SVGContainer>
)
}
Indicator: TLIndicator<BoxShape, SVGSVGElement, Meta> = ({ shape }) => {
return (
<SVGContainer>
<rect width={shape.size[0]} height={shape.size[1]} fill="none" stroke="black" />
</SVGContainer>
)
}
getBounds = (shape: BoxShape) => {
const bounds = Utils.getFromCache(this.boundsCache, shape, () => {
const [width, height] = shape.size
return {
minX: 0,
maxX: width,
minY: 0,
maxY: height,
width,
height,
} as TLBounds
})
return Utils.translateBounds(bounds, shape.point)
}
}
describe('When creating a minimal ShapeUtil', () => {
const Box = new BoxUtil()
it('creates a shape utils', () => {
expect(Box).toBeTruthy()
})
test('accesses this in an override method', () => {
expect(Box.shouldRender(boxShape, { ...boxShape, point: [1, 1] })).toBe(true)
})
test('mounts component without crashing', () => {
const ref = React.createRef<SVGSVGElement>()
const ref2 = React.createRef<HTMLDivElement>()
const H = React.forwardRef<HTMLDivElement, { message: string }>((props, ref) => {
return <div ref={ref2}>{props.message}</div>
})
render(<H message="Hello" />)
render(
<Box._Component
ref={ref}
shape={boxShape}
isEditing={false}
isBinding={false}
isHovered={false}
isSelected={false}
isCurrentParent={false}
meta={{} as any}
events={{} as any}
/>
)
})
})
abstract class TLRealisticShapeUtil<
T extends TLShape,
E extends Element = any,
M = any
> extends TLShapeUtil<T, E, M> {
abstract type: T['type']
abstract getShape: (props: Partial<T>) => T
create = (props: { id: string } & Partial<T>) => {
this.refMap.set(props.id, React.createRef())
return this.getShape(props)
}
}
describe('When creating a realistic API around TLShapeUtil', () => {
class ExtendedBoxUtil extends TLRealisticShapeUtil<BoxShape, SVGSVGElement, Meta> {
type = 'box' as const
age = 100
Component: TLComponent<BoxShape, SVGSVGElement, Meta> = ({ shape, events, meta }, ref) => {
type T = typeof meta.legs
type C = T['toFixed']
return (
<SVGContainer ref={ref}>
<g {...events}>
<rect width={shape.size[0]} height={shape.size[1]} fill="none" stroke="black" />
</g>
</SVGContainer>
)
}
Indicator: TLIndicator<BoxShape, SVGSVGElement, Meta> = ({ shape }) => {
return (
<SVGContainer>
<rect width={shape.size[0]} height={shape.size[1]} fill="none" stroke="black" />
</SVGContainer>
)
}
getShape = (props: Partial<BoxShape>): BoxShape => ({
id: 'example1',
type: 'box',
parentId: 'page',
childIndex: 0,
name: 'Example Shape',
point: [0, 0],
size: [100, 100],
rotation: 0,
...props,
})
getBounds = (shape: BoxShape) => {
const bounds = Utils.getFromCache(this.boundsCache, shape, () => {
const [width, height] = shape.size
return {
minX: 0,
maxX: width,
minY: 0,
maxY: height,
width,
height,
} as TLBounds
})
return Utils.translateBounds(bounds, shape.point)
}
}
const Box = new ExtendedBoxUtil()
it('creates a shape utils', () => {
expect(Box).toBeTruthy()
})
it('creates a shape utils with extended properties', () => {
expect(Box.age).toBe(100)
})
it('creates a shape', () => {
expect(Box.create({ id: 'box1' })).toStrictEqual({
...boxShape,
id: 'box1',
})
})
test('accesses this in an override method', () => {
expect(Box.shouldRender(boxShape, { ...boxShape, point: [1, 1] })).toBe(true)
})
test('mounts component without crashing', () => {
const box = Box.create({ id: 'box1' })
const ref = React.createRef<SVGSVGElement>()
const ref2 = React.createRef<HTMLDivElement>()
const H = React.forwardRef<HTMLDivElement, { message: string }>((props, ref) => {
return <div ref={ref2}>{props.message}</div>
})
render(<H message="Hello" />)
render(
<Box._Component
ref={ref}
shape={box}
isEditing={false}
isBinding={false}
isHovered={false}
isSelected={false}
isCurrentParent={false}
meta={meta}
events={{} as any}
/>
)
})
})

View file

@ -0,0 +1,90 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as React from 'react'
import Utils from '+utils'
import { intersectPolylineBounds } from '@tldraw/intersect'
import type { TLBounds, TLForwardedRef, TLComponentProps, TLShape } from 'types'
export interface TLComponent<T extends TLShape, E extends Element = any, M = any> {
(
this: TLShapeUtil<T, E, M>,
props: TLComponentProps<T, E, M>,
ref: TLForwardedRef<E>
): React.ReactElement<TLComponentProps<T, E, M>, E['tagName']> | null
}
export interface TLIndicator<T extends TLShape, E extends Element = any, M = any> {
(
this: TLShapeUtil<T, E, M>,
props: { shape: T; meta: M; isHovered: boolean; isSelected: boolean }
): React.ReactElement | null
}
export abstract class TLShapeUtil<T extends TLShape, E extends Element = any, M = any> {
_Component: React.ForwardRefExoticComponent<any>
constructor() {
this._Component = React.forwardRef(this.Component)
}
refMap = new Map<string, React.RefObject<E>>()
boundsCache = new WeakMap<TLShape, TLBounds>()
canEdit = false
canBind = false
canClone = false
showBounds = true
isStateful = false
isAspectRatioLocked = false
Component: TLComponent<T, E, M> = () => null
Indicator: TLIndicator<T, E, M> = () => null
shouldRender: (prev: T, next: T) => boolean = () => true
getRef = (shape: T): React.RefObject<E> => {
if (!this.refMap.has(shape.id)) {
this.refMap.set(shape.id, React.createRef<E>())
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.refMap.get(shape.id)!
}
hitTest = (shape: T, point: number[]): boolean => {
const bounds = this.getBounds(shape)
return shape.rotation
? Utils.pointInPolygon(point, Utils.getRotatedCorners(bounds, shape.rotation))
: Utils.pointInBounds(point, bounds)
}
hitTestBounds = (shape: T, bounds: TLBounds) => {
const shapeBounds = this.getBounds(shape)
if (!shape.rotation) {
return (
Utils.boundsContain(bounds, shapeBounds) ||
Utils.boundsContain(shapeBounds, bounds) ||
Utils.boundsCollide(shapeBounds, bounds)
)
}
const corners = Utils.getRotatedCorners(shapeBounds, shape.rotation)
return (
corners.every((point) => Utils.pointInBounds(point, bounds)) ||
intersectPolylineBounds(corners, bounds).length > 0
)
}
abstract getBounds: (shape: T) => TLBounds
getRotatedBounds: (shape: T) => TLBounds = (shape) => {
return Utils.getBoundsFromPoints(Utils.getRotatedCorners(this.getBounds(shape), shape.rotation))
}
}

View file

@ -0,0 +1,8 @@
import type { TLShape } from '+types'
import type { TLShapeUtil } from './TLShapeUtil'
export type TLShapeUtilsMap<T extends TLShape> = {
[K in T['type']]: TLShapeUtil<Extract<T, { type: K }>>
}
export * from './TLShapeUtil'

View file

@ -1,147 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as React from 'react'
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { TLShape, TLBounds } from '+types'
import { ShapeUtil } from './createShape'
import { render } from '@testing-library/react'
import { SVGContainer } from '+components'
import Utils from '+utils'
export interface BoxShape extends TLShape {
size: number[]
}
export const Box = new ShapeUtil<BoxShape, SVGSVGElement, null, { age: number }>(() => {
return {
age: 100,
type: 'box',
defaultProps: {
id: 'example1',
type: 'box',
parentId: 'page',
childIndex: 0,
name: 'Example Shape',
point: [0, 0],
size: [100, 100],
rotation: 0,
},
Component({ shape, events, meta }, ref) {
return (
<SVGContainer ref={ref}>
<g {...events}>
<rect width={shape.size[0]} height={shape.size[1]} fill="none" stroke="black" />
</g>
</SVGContainer>
)
},
Indicator({ shape }) {
return (
<SVGContainer>
<rect width={shape.size[0]} height={shape.size[1]} fill="none" stroke="black" />
</SVGContainer>
)
},
getBounds(shape) {
const bounds = Utils.getFromCache(this.boundsCache, shape, () => {
const [width, height] = shape.size
return {
minX: 0,
maxX: width,
minY: 0,
maxY: height,
width,
height,
} as TLBounds
})
return Utils.translateBounds(bounds, shape.point)
},
getRotatedBounds(shape) {
return {
minX: 0,
minY: 0,
maxX: 100,
maxY: 100,
width: 100,
height: 100,
}
},
shouldRender(prev, next) {
return prev.point !== next.point
},
}
})
const boxShape = {
id: 'example1',
type: 'box',
parentId: 'page',
childIndex: 0,
name: 'Example Shape',
point: [0, 0],
size: [100, 100],
rotation: 0,
}
const box = Box.create({ id: 'box1' })
describe('shape utils', () => {
it('creates a shape utils', () => {
expect(Box).toBeTruthy()
})
it('creates a shape utils with extended properties', () => {
expect(Box.age).toBe(100)
})
it('creates a shape', () => {
expect(Box.create({ id: 'box1' })).toStrictEqual({
...boxShape,
id: 'box1',
})
})
it('sets config', () => {
const bounds = Box.getRotatedBounds(box)
expect(bounds).toStrictEqual({
minX: 0,
minY: 0,
maxX: 100,
maxY: 100,
width: 100,
height: 100,
})
})
test('accesses this in an override method', () => {
expect(Box.shouldRender(box, { ...box, point: [1, 1] })).toBeTruthy()
})
test('mounts component without crashing', () => {
const box = Box.create({ id: 'box1' })
const ref = React.createRef<SVGSVGElement>()
const ref2 = React.createRef<HTMLDivElement>()
const H = React.forwardRef<HTMLDivElement, { message: string }>((props, ref) => {
return <div ref={ref2}>{props.message}</div>
})
render(<H message="Hello" />)
render(
<Box._Component
ref={ref}
shape={box}
isEditing={false}
isBinding={false}
isHovered={false}
isSelected={false}
isCurrentParent={false}
meta={{} as any}
events={{} as any}
/>
)
})
})

View file

@ -1,232 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as React from 'react'
import { Vec } from '@tldraw/vec'
import type { TLShape, TLShapeUtil } from '+types'
import Utils from '+utils'
import { intersectPolylineBounds, intersectRayBounds } from '@tldraw/intersect'
export const ShapeUtil = function <T extends TLShape, E extends Element, M = any, K = unknown>(
this: TLShapeUtil<T, E, M> & K,
fn: (
this: TLShapeUtil<T, E, M> & K
) => Partial<TLShapeUtil<T, E, M>> &
Pick<TLShapeUtil<T, E, M>, 'type' | 'defaultProps' | 'Component' | 'Indicator' | 'getBounds'> &
K
): TLShapeUtil<T, E, M> & ReturnType<typeof fn> {
const defaults: Partial<TLShapeUtil<T, E, M>> = {
refMap: new Map(),
boundsCache: new WeakMap(),
canEdit: false,
canBind: false,
showBounds: true,
isStateful: false,
canClone: false,
isAspectRatioLocked: false,
create: (props) => {
this.refMap.set(props.id, React.createRef())
const defaults = this.defaultProps
return { ...defaults, ...props }
},
getRef: (shape) => {
if (!this.refMap.has(shape.id)) {
this.refMap.set(shape.id, React.createRef<E>())
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.refMap.get(shape.id)!
},
mutate: (shape, props) => {
return { ...shape, ...props }
},
transform: (shape, bounds) => {
return { ...shape, point: [bounds.minX, bounds.minY] }
},
transformSingle: (shape, bounds, info) => {
return this.transform(shape, bounds, info)
},
shouldRender: () => {
return true
},
getRotatedBounds: (shape) => {
return Utils.getBoundsFromPoints(
Utils.getRotatedCorners(this.getBounds(shape), shape.rotation)
)
},
getCenter: (shape) => {
return Utils.getBoundsCenter(this.getBounds(shape))
},
hitTest: (shape, point) => {
return Utils.pointInBounds(point, this.getBounds(shape))
},
hitTestBounds: (shape, bounds) => {
const { minX, minY, maxX, maxY, width, height } = this.getBounds(shape)
const center = [minX + width / 2, minY + height / 2]
const corners = [
[minX, minY],
[maxX, minY],
[maxX, maxY],
[minX, maxY],
].map((point) => Vec.rotWith(point, center, shape.rotation || 0))
return (
corners.every(
(point) =>
!(
point[0] < bounds.minX ||
point[0] > bounds.maxX ||
point[1] < bounds.minY ||
point[1] > bounds.maxY
)
) || intersectPolylineBounds(corners, bounds).length > 0
)
},
getBindingPoint: (shape, fromShape, point, origin, direction, padding, bindAnywhere) => {
// Algorithm time! We need to find the binding point (a normalized point inside of the shape, or around the shape, where the arrow will point to) and the distance from the binding shape to the anchor.
let bindingPoint: number[]
let distance: number
const bounds = this.getBounds(shape)
const expandedBounds = Utils.expandBounds(bounds, padding)
// The point must be inside of the expanded bounding box
if (!Utils.pointInBounds(point, expandedBounds)) return
// The point is inside of the shape, so we'll assume the user is indicating a specific point inside of the shape.
if (bindAnywhere) {
if (Vec.dist(point, this.getCenter(shape)) < 12) {
bindingPoint = [0.5, 0.5]
} else {
bindingPoint = Vec.divV(Vec.sub(point, [expandedBounds.minX, expandedBounds.minY]), [
expandedBounds.width,
expandedBounds.height,
])
}
distance = 0
} else {
// (1) Binding point
// Find furthest intersection between ray from origin through point and expanded bounds. TODO: What if the shape has a curve? In that case, should we intersect the circle-from-three-points instead?
const intersection = intersectRayBounds(origin, direction, expandedBounds)
.filter((int) => int.didIntersect)
.map((int) => int.points[0])
.sort((a, b) => Vec.dist(b, origin) - Vec.dist(a, origin))[0]
// The anchor is a point between the handle and the intersection
const anchor = Vec.med(point, intersection)
// If we're close to the center, snap to the center, or else calculate a normalized point based on the anchor and the expanded bounds.
if (Vec.distanceToLineSegment(point, anchor, this.getCenter(shape)) < 12) {
bindingPoint = [0.5, 0.5]
} else {
//
bindingPoint = Vec.divV(Vec.sub(anchor, [expandedBounds.minX, expandedBounds.minY]), [
expandedBounds.width,
expandedBounds.height,
])
}
// (3) Distance
// If the point is inside of the bounds, set the distance to a fixed value.
if (Utils.pointInBounds(point, bounds)) {
distance = 16
} else {
// If the binding point was close to the shape's center, snap to to the center. Find the distance between the point and the real bounds of the shape
distance = Math.max(
16,
Utils.getBoundsSides(bounds)
.map((side) => Vec.distanceToLineSegment(side[1][0], side[1][1], point))
.sort((a, b) => a - b)[0]
)
}
}
return {
point: Vec.clampV(bindingPoint, 0, 1),
distance,
}
},
onDoubleClickBoundsHandle() {
return
},
onDoubleClickHandle() {
return
},
onHandleChange() {
return
},
onRightPointHandle() {
return
},
onSessionComplete() {
return
},
onBindingChange() {
return
},
onChildrenChange() {
return
},
updateChildren() {
return
},
}
Object.assign(this, defaults)
Object.assign(this, fn.call(this))
Object.assign(this, fn.call(this))
// Make sure all functions are bound to this
for (const entry of Object.entries(this)) {
if (entry[1] instanceof Function) {
this[entry[0] as keyof typeof this] = this[entry[0]].bind(this)
}
}
this._Component = React.forwardRef(this.Component)
return this
} as unknown as {
new <T extends TLShape, E extends Element, M = any, K = unknown>(
fn: (
this: TLShapeUtil<T, E, M>
) => Partial<TLShapeUtil<T, E, M>> &
Pick<
TLShapeUtil<T, E, M>,
'type' | 'defaultProps' | 'Component' | 'Indicator' | 'getBounds'
> &
K
): TLShapeUtil<T, E, M> & ReturnType<typeof fn>
}

View file

@ -1 +0,0 @@
export * from './createShape'

View file

@ -1,8 +1,5 @@
import type { TLBinding, TLPage, TLPageState, TLShape } from '+types'
interface BoxShape extends TLShape {
size: number[]
}
import type { BoxShape } from '+shape-utils/TLShapeUtil.spec'
import type { TLBinding, TLPage, TLPageState } from '+types'
export const mockDocument: { page: TLPage<BoxShape, TLBinding>; pageState: TLPageState } = {
page: {

View file

@ -1,6 +1,5 @@
import type { TLShapeUtils } from '+types'
import { Box } from '../shapes/createShape.spec'
import { BoxUtil } from '+shape-utils/TLShapeUtil.spec'
export const mockUtils: TLShapeUtils = {
box: Box,
export const mockUtils = {
box: new BoxUtil(),
}

View file

@ -6,7 +6,10 @@ import type React from 'react'
export type Patch<T> = Partial<{ [P in keyof T]: T | Partial<T> | Patch<T[P]> }>
type ForwardedRef<T> = ((instance: T | null) => void) | React.MutableRefObject<T | null> | null
export type TLForwardedRef<T> =
| ((instance: T | null) => void)
| React.MutableRefObject<T | null>
| null
export interface TLPage<T extends TLShape, B extends TLBinding> {
id: string
@ -68,14 +71,7 @@ export interface TLShape {
isAspectRatioLocked?: boolean
}
export type TLShapeUtils<
T extends TLShape = any,
E extends Element = any,
M = any,
K = any
> = Record<string, TLShapeUtil<T, E, M, K>>
export interface TLRenderInfo<T extends TLShape, E = any, M = any> {
export interface TLComponentProps<T extends TLShape, E = any, M = any> {
shape: T
isEditing: boolean
isBinding: boolean
@ -94,8 +90,9 @@ export interface TLRenderInfo<T extends TLShape, E = any, M = any> {
}
}
export interface TLShapeProps<T extends TLShape, E = any, M = any> extends TLRenderInfo<T, E, M> {
ref: ForwardedRef<E>
export interface TLShapeProps<T extends TLShape, E = any, M = any>
extends TLComponentProps<T, E, M> {
ref: TLForwardedRef<E>
shape: T
}
@ -336,148 +333,6 @@ export type Snap =
distance: number
}
/* -------------------------------------------------- */
/* Shape Utility */
/* -------------------------------------------------- */
export type TLShapeUtil<
T extends TLShape,
E extends Element,
M = any,
K = { [key: string]: any }
> = K & {
type: T['type']
defaultProps: T
Component(
this: TLShapeUtil<T, E, M>,
props: TLRenderInfo<T, E, M>,
ref: ForwardedRef<E>
): React.ReactElement<TLRenderInfo<T, E, M>, E['tagName']>
Indicator(
this: TLShapeUtil<T, E, M>,
props: { shape: T; meta: M; isHovered: boolean; isSelected: boolean }
): React.ReactElement | null
getBounds(this: TLShapeUtil<T, E, M>, shape: T): TLBounds
refMap: Map<string, React.RefObject<E>>
boundsCache: WeakMap<TLShape, TLBounds>
isAspectRatioLocked: boolean
canEdit: boolean
canClone: boolean
canBind: boolean
isStateful: boolean
showBounds: boolean
getRotatedBounds(this: TLShapeUtil<T, E, M>, shape: T): TLBounds
hitTest(this: TLShapeUtil<T, E, M>, shape: T, point: number[]): boolean
hitTestBounds(this: TLShapeUtil<T, E, M>, shape: T, bounds: TLBounds): boolean
shouldRender(this: TLShapeUtil<T, E, M>, prev: T, next: T): boolean
getCenter(this: TLShapeUtil<T, E, M>, shape: T): number[]
getRef(this: TLShapeUtil<T, E, M>, shape: T): React.RefObject<E>
getBindingPoint<K extends TLShape>(
this: TLShapeUtil<T, E, M>,
shape: T,
fromShape: K,
point: number[],
origin: number[],
direction: number[],
padding: number,
bindAnywhere: boolean
): { point: number[]; distance: number } | undefined
create: (this: TLShapeUtil<T, E, M>, props: { id: string } & Partial<T>) => T
mutate: (this: TLShapeUtil<T, E, M>, shape: T, props: Partial<T>) => Partial<T>
transform: (
this: TLShapeUtil<T, E, M>,
shape: T,
bounds: TLBounds,
info: TLTransformInfo<T>
) => Partial<T> | void
transformSingle: (
this: TLShapeUtil<T, E, M>,
shape: T,
bounds: TLBounds,
info: TLTransformInfo<T>
) => Partial<T> | void
updateChildren: <K extends TLShape>(
this: TLShapeUtil<T, E, M>,
shape: T,
children: K[]
) => Partial<K>[] | void
onChildrenChange: (this: TLShapeUtil<T, E, M>, shape: T, children: TLShape[]) => Partial<T> | void
onBindingChange: (
this: TLShapeUtil<T, E, M>,
shape: T,
binding: TLBinding,
target: TLShape,
targetBounds: TLBounds,
center: number[]
) => Partial<T> | void
onHandleChange: (
this: TLShapeUtil<T, E, M>,
shape: T,
handle: Partial<T['handles']>,
info: Partial<TLPointerInfo>
) => Partial<T> | void
onRightPointHandle: (
this: TLShapeUtil<T, E, M>,
shape: T,
handle: Partial<T['handles']>,
info: Partial<TLPointerInfo>
) => Partial<T> | void
onDoubleClickHandle: (
this: TLShapeUtil<T, E, M>,
shape: T,
handle: Partial<T['handles']>,
info: Partial<TLPointerInfo>
) => Partial<T> | void
onDoubleClickBoundsHandle: (this: TLShapeUtil<T, E, M>, shape: T) => Partial<T> | void
onSessionComplete: (this: TLShapeUtil<T, E, M>, shape: T) => Partial<T> | void
_Component: React.ForwardRefExoticComponent<any>
}
// export interface TLShapeUtil<T extends TLShape, E extends Element, M = any>
// extends TLShapeUtilRequired<T, E, M>,
// Required<TLShapeUtilDefaults<T, E>> {
// _Component: React.ForwardRefExoticComponent<any> & {
// defaultProps: any
// propTypes: any
// }
// }
// export interface TLShapeUtilConfig<T extends TLShape, E extends Element, M = any>
// extends TLShapeUtilRequired<T, E, M>,
// Partial<TLShapeUtilDefaults<T, E>> {}
/* -------------------- Internal -------------------- */
export interface IShapeTreeNode<T extends TLShape, M = any> {

View file

@ -2,7 +2,7 @@ import * as React from 'react'
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
import { BoxIcon, dropdownItem, dropdownContent } from './styled'
import { DropdownMenuIconTriggerButton } from '../shared'
import { strokes } from '~shape'
import { strokes } from '~shape-utils'
import { useTheme, useTLDrawContext } from '~hooks'
import type { Data, ColorStyle } from '~types'

View file

@ -5,7 +5,7 @@ import css, { dark } from '~styles'
import { Data, TLDrawDocument, TLDrawStatus, TLDrawUser } from '~types'
import { TLDrawState } from '~state'
import { TLDrawContext, useCustomFonts, useKeyboardShortcuts, useTLDrawContext } from '~hooks'
import { tldrawShapeUtils } from '~shape'
import { shapeUtils } from '~shape-utils'
import { StylePanel } from '~components/style-panel'
import { ToolsPanel } from '~components/tools-panel'
import { PagePanel } from '~components/page-panel'
@ -14,7 +14,6 @@ import { breakpoints, iconButton } from '~components'
import { DotFilledIcon } from '@radix-ui/react-icons'
import { TLDR } from '~state/tldr'
import { ContextMenu } from '~components/context-menu'
import { EMPTY_ARRAY } from '~state/constants'
// Selectors
const isInSelectSelector = (s: Data) => s.appState.activeTool === 'select'
@ -225,7 +224,7 @@ function InnerTldraw({
snapLines={snapLines}
users={users}
userId={tlstate.state.room?.userId}
shapeUtils={tldrawShapeUtils}
shapeUtils={shapeUtils}
theme={theme}
meta={meta}
hideBounds={hideBounds}

View file

@ -1,2 +1,78 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
export const BINDING_DISTANCE = 24
export const CLONING_DISTANCE = 32
export const FIT_TO_SCREEN_PADDING = 128
export const SNAP_DISTANCE = 5
export const EMPTY_ARRAY = [] as any[]
export const SLOW_SPEED = 10
export const VERY_SLOW_SPEED = 2.5
import type { Easing } from '~types'
export const PI2 = Math.PI * 2
export const EASINGS: Record<Easing, (t: number) => number> = {
linear: (t) => t,
easeInQuad: (t) => t * t,
easeOutQuad: (t) => t * (2 - t),
easeInOutQuad: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
easeInCubic: (t) => t * t * t,
easeOutCubic: (t) => --t * t * t + 1,
easeInOutCubic: (t) => (t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1),
easeInQuart: (t) => t * t * t * t,
easeOutQuart: (t) => 1 - --t * t * t * t,
easeInOutQuart: (t) => (t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t),
easeInQuint: (t) => t * t * t * t * t,
easeOutQuint: (t) => 1 + --t * t * t * t * t,
easeInOutQuint: (t) => (t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t),
easeInSine: (t) => 1 - Math.cos((t * Math.PI) / 2),
easeOutSine: (t) => Math.sin((t * Math.PI) / 2),
easeInOutSine: (t) => -(Math.cos(Math.PI * t) - 1) / 2,
easeInExpo: (t) => (t <= 0 ? 0 : Math.pow(2, 10 * t - 10)),
easeOutExpo: (t) => (t >= 1 ? 1 : 1 - Math.pow(2, -10 * t)),
easeInOutExpo: (t) =>
t <= 0
? 0
: t >= 1
? 1
: t < 0.5
? Math.pow(2, 20 * t - 10) / 2
: (2 - Math.pow(2, -20 * t + 10)) / 2,
}
export const EASING_STRINGS: Record<Easing, string> = {
linear: `(t) => t`,
easeInQuad: `(t) => t * t`,
easeOutQuad: `(t) => t * (2 - t)`,
easeInOutQuad: `(t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t)`,
easeInCubic: `(t) => t * t * t`,
easeOutCubic: `(t) => --t * t * t + 1`,
easeInOutCubic: `(t) => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1`,
easeInQuart: `(t) => t * t * t * t`,
easeOutQuart: `(t) => 1 - --t * t * t * t`,
easeInOutQuart: `(t) => t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t`,
easeInQuint: `(t) => t * t * t * t * t`,
easeOutQuint: `(t) => 1 + --t * t * t * t * t`,
easeInOutQuint: `(t) => t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t`,
easeInSine: `(t) => 1 - Math.cos((t * Math.PI) / 2)`,
easeOutSine: `(t) => Math.sin((t * Math.PI) / 2)`,
easeInOutSine: `(t) => -(Math.cos(Math.PI * t) - 1) / 2`,
easeInExpo: `(t) => (t <= 0 ? 0 : Math.pow(2, 10 * t - 10))`,
easeOutExpo: `(t) => (t >= 1 ? 1 : 1 - Math.pow(2, -10 * t))`,
easeInOutExpo: `(t) => t <= 0 ? 0 : t >= 1 ? 1 : t < 0.5 ? Math.pow(2, 20 * t - 10) / 2 : (2 - Math.pow(2, -20 * t + 10)) / 2`,
}
export const USER_COLORS = [
'#EC5E41',
'#F2555A',
'#F04F88',
'#E34BA9',
'#BD54C6',
'#9D5BD2',
'#7B66DC',
'#02B1CC',
'#11B3A3',
'#39B178',
'#55B467',
'#FF802B',
]

View file

@ -1,4 +1,4 @@
export * from './components/tldraw'
export * from './types'
export * from './shape'
export * from './shape-utils'
export { TLDrawState } from './state'

View file

@ -0,0 +1,157 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Utils, TLShapeUtil } from '@tldraw/core'
import type { TLPointerInfo, TLBinding, TLBounds } from '@tldraw/core'
import { intersectRayBounds } from '@tldraw/intersect'
import { Vec } from '@tldraw/vec'
import type { TLDrawMeta, TLDrawShape, TLDrawTransformInfo } from '~types'
import * as React from 'react'
export abstract class TLDrawShapeUtil<
T extends TLDrawShape,
E extends Element = any
> extends TLShapeUtil<T, E, TLDrawMeta> {
abstract type: T['type']
abstract getShape: (props: Partial<T>) => T
create = (props: { id: string } & Partial<T>) => {
this.refMap.set(props.id, React.createRef())
return this.getShape(props)
}
getCenter = (shape: T) => {
return Utils.getBoundsCenter(this.getBounds(shape))
}
getBindingPoint = <K extends TLDrawShape>(
shape: T,
fromShape: K,
point: number[],
origin: number[],
direction: number[],
padding: number,
bindAnywhere: boolean
) => {
// Algorithm time! We need to find the binding point (a normalized point inside of the shape, or around the shape, where the arrow will point to) and the distance from the binding shape to the anchor.
let bindingPoint: number[]
let distance: number
const bounds = this.getBounds(shape)
const expandedBounds = Utils.expandBounds(bounds, padding)
// The point must be inside of the expanded bounding box
if (!Utils.pointInBounds(point, expandedBounds)) return
// The point is inside of the shape, so we'll assume the user is indicating a specific point inside of the shape.
if (bindAnywhere) {
if (Vec.dist(point, this.getCenter(shape)) < 12) {
bindingPoint = [0.5, 0.5]
} else {
bindingPoint = Vec.divV(Vec.sub(point, [expandedBounds.minX, expandedBounds.minY]), [
expandedBounds.width,
expandedBounds.height,
])
}
distance = 0
} else {
// (1) Binding point
// Find furthest intersection between ray from origin through point and expanded bounds. TODO: What if the shape has a curve? In that case, should we intersect the circle-from-three-points instead?
const intersection = intersectRayBounds(origin, direction, expandedBounds)
.filter((int) => int.didIntersect)
.map((int) => int.points[0])
.sort((a, b) => Vec.dist(b, origin) - Vec.dist(a, origin))[0]
// The anchor is a point between the handle and the intersection
const anchor = Vec.med(point, intersection)
// If we're close to the center, snap to the center, or else calculate a normalized point based on the anchor and the expanded bounds.
if (Vec.distanceToLineSegment(point, anchor, this.getCenter(shape)) < 12) {
bindingPoint = [0.5, 0.5]
} else {
//
bindingPoint = Vec.divV(Vec.sub(anchor, [expandedBounds.minX, expandedBounds.minY]), [
expandedBounds.width,
expandedBounds.height,
])
}
// (3) Distance
// If the point is inside of the bounds, set the distance to a fixed value.
if (Utils.pointInBounds(point, bounds)) {
distance = 16
} else {
// If the binding point was close to the shape's center, snap to to the center. Find the distance between the point and the real bounds of the shape
distance = Math.max(
16,
Utils.getBoundsSides(bounds)
.map((side) => Vec.distanceToLineSegment(side[1][0], side[1][1], point))
.sort((a, b) => a - b)[0]
)
}
}
return {
point: Vec.clampV(bindingPoint, 0, 1),
distance,
}
}
mutate = (shape: T, props: Partial<T>): Partial<T> => {
return props
}
transform = (shape: T, bounds: TLBounds, info: TLDrawTransformInfo<T>): Partial<T> => {
return { ...shape, point: [bounds.minX, bounds.minY] }
}
transformSingle = (
shape: T,
bounds: TLBounds,
info: TLDrawTransformInfo<T>
): Partial<T> | void => {
return this.transform(shape, bounds, info)
}
updateChildren?: <K extends TLDrawShape>(shape: T, children: K[]) => Partial<K>[] | void
onChildrenChange?: (shape: T, children: TLDrawShape[]) => Partial<T> | void
onBindingChange?: (
shape: T,
binding: TLBinding,
target: TLDrawShape,
targetBounds: TLBounds,
center: number[]
) => Partial<T> | void
onHandleChange?: (
shape: T,
handles: Partial<T['handles']>,
info: Partial<TLPointerInfo>
) => Partial<T> | void
onRightPointHandle?: (
shape: T,
handles: Partial<T['handles']>,
info: Partial<TLPointerInfo>
) => Partial<T> | void
onDoubleClickHandle?: (
shape: T,
handles: Partial<T['handles']>,
info: Partial<TLPointerInfo>
) => Partial<T> | void
onDoubleClickBoundsHandle?: (shape: T) => Partial<T> | void
onSessionComplete?: (shape: T) => Partial<T> | void
}

View file

@ -0,0 +1 @@
# Shape Utils

View file

@ -1,4 +1,4 @@
import { Arrow } from './arrow'
import { Arrow } from '../'
describe('Arrow shape', () => {
it('Creates a shape', () => {

View file

@ -1,17 +1,27 @@
import * as React from 'react'
import { ShapeUtil, SVGContainer, TLBounds, Utils, TLHandle } from '@tldraw/core'
import {
Utils,
TLHandle,
SVGContainer,
TLBinding,
TLBounds,
TLIndicator,
TLComponent,
TLPointerInfo,
} from '@tldraw/core'
import { Vec } from '@tldraw/vec'
import getStroke from 'perfect-freehand'
import { defaultStyle, getShapeStyle } from '~shape/shape-styles'
import { defaultStyle, getShapeStyle } from '../shape-styles'
import {
ArrowShape,
DashStyle,
TLDrawTransformInfo,
Decoration,
TLDrawShapeType,
DashStyle,
ArrowBinding,
TLDrawMeta,
TLDrawShape,
EllipseShape,
} from '~types'
import { TLDrawShapeUtil } from '../TLDrawShapeUtil'
import {
intersectArcBounds,
intersectCircleCircle,
@ -20,19 +30,23 @@ import {
intersectRayBounds,
intersectRayEllipse,
} from '@tldraw/intersect'
import { EASINGS } from '~state/utils'
import { BINDING_DISTANCE } from '~constants'
import { EASINGS, BINDING_DISTANCE } from '~constants'
export const Arrow = new ShapeUtil<ArrowShape, SVGSVGElement, TLDrawMeta>(() => ({
type: TLDrawShapeType.Arrow,
type T = ArrowShape
type E = SVGSVGElement
canStyleFill: false,
export class ArrowUtil extends TLDrawShapeUtil<T, E> {
type = TLDrawShapeType.Arrow as const
showBounds: false,
canStyleFill = false
pathCache: new WeakMap<ArrowShape, string>(),
showBounds = false
defaultProps: {
pathCache = new WeakMap<T, string>()
getShape = (props: Partial<T>): T => {
return Utils.deepMerge<T>(
{
id: 'id',
type: TLDrawShapeType.Arrow,
name: 'Arrow',
@ -68,8 +82,11 @@ export const Arrow = new ShapeUtil<ArrowShape, SVGSVGElement, TLDrawMeta>(() =>
isFilled: false,
},
},
props
)
}
Component({ shape, meta, events }, ref) {
Component: TLComponent<T, E> = ({ shape, meta, events }, ref) => {
const {
handles: { start, bend, end },
decorations = {},
@ -245,32 +262,24 @@ export const Arrow = new ShapeUtil<ArrowShape, SVGSVGElement, TLDrawMeta>(() =>
</g>
</SVGContainer>
)
},
}
Indicator({ shape }) {
Indicator: TLIndicator<T> = ({ shape }) => {
const path = getArrowPath(shape)
return <path d={path} />
},
}
shouldRender(prev, next) {
return (
next.decorations !== prev.decorations ||
next.handles !== prev.handles ||
next.style !== prev.style
)
},
getBounds(shape) {
getBounds = (shape: T) => {
const bounds = Utils.getFromCache(this.boundsCache, shape, () => {
const points = getArcPoints(shape)
return Utils.getBoundsFromPoints(points)
})
return Utils.translateBounds(bounds, shape.point)
},
}
getRotatedBounds(shape) {
getRotatedBounds = (shape: T) => {
let points = getArcPoints(shape)
const { minX, minY, maxX, maxY } = Utils.getBoundsFromPoints(points)
@ -282,39 +291,51 @@ export const Arrow = new ShapeUtil<ArrowShape, SVGSVGElement, TLDrawMeta>(() =>
}
return Utils.translateBounds(Utils.getBoundsFromPoints(points), shape.point)
},
}
getCenter(shape) {
getCenter = (shape: T) => {
const { start, end } = shape.handles
return Vec.add(shape.point, Vec.med(start.point, end.point))
},
}
hitTestBounds(shape, brushBounds: TLBounds) {
shouldRender = (prev: T, next: T) => {
return (
next.decorations !== prev.decorations ||
next.handles !== prev.handles ||
next.style !== prev.style
)
}
hitTestBounds = (shape: T, bounds: TLBounds) => {
const { start, end, bend } = shape.handles
const sp = Vec.add(shape.point, start.point)
const ep = Vec.add(shape.point, end.point)
if (Utils.pointInBounds(sp, brushBounds) || Utils.pointInBounds(ep, brushBounds)) {
if (Utils.pointInBounds(sp, bounds) || Utils.pointInBounds(ep, bounds)) {
return true
}
if (Vec.isEqual(Vec.med(start.point, end.point), bend.point)) {
return intersectLineSegmentBounds(sp, ep, brushBounds).length > 0
return intersectLineSegmentBounds(sp, ep, bounds).length > 0
} else {
const [cx, cy, r] = getCtp(shape)
const cp = Vec.add(shape.point, [cx, cy])
return intersectArcBounds(cp, r, sp, ep, brushBounds).length > 0
return intersectArcBounds(cp, r, sp, ep, bounds).length > 0
}
}
},
transform(_shape, bounds, { initialShape, scaleX, scaleY }) {
transform = (
shape: T,
bounds: TLBounds,
{ initialShape, scaleX, scaleY }: TLDrawTransformInfo<T>
): Partial<T> => {
const initialShapeBounds = this.getBounds(initialShape)
const handles: (keyof ArrowShape['handles'])[] = ['start', 'end']
const handles: (keyof T['handles'])[] = ['start', 'end']
const nextHandles = { ...initialShape.handles }
@ -355,9 +376,9 @@ export const Arrow = new ShapeUtil<ArrowShape, SVGSVGElement, TLDrawMeta>(() =>
point: Vec.round([bounds.minX, bounds.minY]),
handles: nextHandles,
}
},
}
onDoubleClickHandle(shape, handle) {
onDoubleClickHandle = (shape: T, handle: Partial<T['handles']>): Partial<T> | void => {
switch (handle) {
case 'bend': {
return {
@ -390,9 +411,15 @@ export const Arrow = new ShapeUtil<ArrowShape, SVGSVGElement, TLDrawMeta>(() =>
}
return this
},
}
onBindingChange(shape, binding: ArrowBinding, target, targetBounds, center) {
onBindingChange = (
shape: T,
binding: TLBinding,
target: TLDrawShape,
targetBounds: TLBounds,
center: number[]
): Partial<T> | void => {
const handle = shape.handles[binding.meta.handleId as keyof ArrowShape['handles']]
const expandedBounds = Utils.expandBounds(targetBounds, BINDING_DISTANCE)
@ -467,9 +494,13 @@ export const Arrow = new ShapeUtil<ArrowShape, SVGSVGElement, TLDrawMeta>(() =>
},
{ shiftKey: false }
)
},
}
onHandleChange(shape, handles, { shiftKey }) {
onHandleChange = (
shape: T,
handles: Partial<T['handles']>,
{ shiftKey }: Partial<TLPointerInfo>
): Partial<T> | void => {
let nextHandles = Utils.deepMerge<ArrowShape['handles']>(shape.handles, handles)
let nextBend = shape.bend
@ -575,8 +606,8 @@ export const Arrow = new ShapeUtil<ArrowShape, SVGSVGElement, TLDrawMeta>(() =>
}
return nextShape
},
}))
}
}
/* -------------------------------------------------- */
/* Helpers */

View file

@ -1,4 +1,4 @@
import { Draw } from './draw'
import { Draw } from '../'
describe('Draw shape', () => {
it('Creates a shape', () => {

View file

@ -1,20 +1,29 @@
import * as React from 'react'
import { SVGContainer, TLBounds, Utils, TLTransformInfo, ShapeUtil } from '@tldraw/core'
import { Utils, SVGContainer, TLBounds, TLIndicator, TLComponent } from '@tldraw/core'
import { Vec } from '@tldraw/vec'
import { intersectBoundsBounds, intersectBoundsPolyline } from '@tldraw/intersect'
import { getStrokeOutlinePoints, getStrokePoints, StrokeOptions } from 'perfect-freehand'
import { defaultStyle, getShapeStyle } from '~shape/shape-styles'
import { DrawShape, DashStyle, TLDrawShapeType, TLDrawMeta } from '~types'
import { defaultStyle, getShapeStyle } from '../shape-styles'
import { DrawShape, DashStyle, TLDrawShapeType, TLDrawTransformInfo } from '~types'
import { TLDrawShapeUtil } from '../TLDrawShapeUtil'
import { intersectBoundsBounds, intersectBoundsPolyline } from '@tldraw/intersect'
const pointsBoundsCache = new WeakMap<DrawShape['points'], TLBounds>([])
const shapeBoundsCache = new Map<string, TLBounds>()
const rotatedCache = new WeakMap<DrawShape, number[][]>([])
const pointCache: Record<string, number[]> = {}
type T = DrawShape
type E = SVGSVGElement
export const Draw = new ShapeUtil<DrawShape, SVGSVGElement, TLDrawMeta>(() => ({
type: TLDrawShapeType.Draw,
export class DrawUtil extends TLDrawShapeUtil<T, E> {
type = TLDrawShapeType.Draw as const
defaultProps: {
pointsBoundsCache = new WeakMap<T['points'], TLBounds>([])
shapeBoundsCache = new Map<string, TLBounds>()
rotatedCache = new WeakMap<T, number[][]>([])
pointCache: Record<string, number[]> = {}
getShape = (props: Partial<T>): T => {
return Utils.deepMerge<T>(
{
id: 'id',
type: TLDrawShapeType.Draw,
name: 'Draw',
@ -26,8 +35,11 @@ export const Draw = new ShapeUtil<DrawShape, SVGSVGElement, TLDrawMeta>(() => ({
points: [],
isComplete: false,
},
props
)
}
Component({ shape, meta, events }, ref) {
Component: TLComponent<T, E> = ({ shape, meta, events }, ref) => {
const { points, style, isComplete } = shape
const polygonPathData = React.useMemo(() => {
@ -132,9 +144,9 @@ export const Draw = new ShapeUtil<DrawShape, SVGSVGElement, TLDrawMeta>(() => ({
/>
</SVGContainer>
)
},
}
Indicator({ shape }) {
Indicator: TLIndicator<T> = ({ shape }) => {
const { points } = shape
const pathData = React.useMemo(() => {
@ -150,85 +162,12 @@ export const Draw = new ShapeUtil<DrawShape, SVGSVGElement, TLDrawMeta>(() => ({
}
return <path d={pathData} />
},
getBounds(shape: DrawShape): TLBounds {
// return Utils.translateBounds(Utils.getBoundsFromPoints(shape.points), shape.point)
// The goal here is to avoid recalculating the bounds from the
// points array, which is expensive. However, we still need a
// new bounds if the point has changed, but we will reuse the
// previous bounds-from-points result if we can.
const pointsHaveChanged = !pointsBoundsCache.has(shape.points)
const pointHasChanged = !(pointCache[shape.id] === shape.point)
if (pointsHaveChanged) {
// If the points have changed, then bust the points cache
const bounds = Utils.getBoundsFromPoints(shape.points)
pointsBoundsCache.set(shape.points, bounds)
shapeBoundsCache.set(shape.id, Utils.translateBounds(bounds, shape.point))
pointCache[shape.id] = shape.point
} else if (pointHasChanged && !pointsHaveChanged) {
// If the point have has changed, then bust the point cache
pointCache[shape.id] = shape.point
shapeBoundsCache.set(
shape.id,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
Utils.translateBounds(pointsBoundsCache.get(shape.points)!, shape.point)
)
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return shapeBoundsCache.get(shape.id)!
},
shouldRender(prev: DrawShape, next: DrawShape): boolean {
return (
next.points !== prev.points ||
next.style !== prev.style ||
next.isComplete !== prev.isComplete
)
},
hitTestBounds(shape: DrawShape, brushBounds: TLBounds): boolean {
// Test axis-aligned shape
if (!shape.rotation) {
const bounds = this.getBounds(shape)
return (
Utils.boundsContain(brushBounds, bounds) ||
((Utils.boundsContain(bounds, brushBounds) ||
intersectBoundsBounds(bounds, brushBounds).length > 0) &&
intersectBoundsPolyline(
Utils.translateBounds(brushBounds, Vec.neg(shape.point)),
shape.points
).length > 0)
)
}
// Test rotated shape
const rBounds = this.getRotatedBounds(shape)
const rotatedBounds = Utils.getFromCache(rotatedCache, shape, () => {
const c = Utils.getBoundsCenter(Utils.getBoundsFromPoints(shape.points))
return shape.points.map((pt) => Vec.rotWith(pt, c, shape.rotation || 0))
})
return (
Utils.boundsContain(brushBounds, rBounds) ||
intersectBoundsPolyline(
Utils.translateBounds(brushBounds, Vec.neg(shape.point)),
rotatedBounds
).length > 0
)
},
transform(
shape: DrawShape,
transform = (
shape: T,
bounds: TLBounds,
{ initialShape, scaleX, scaleY }: TLTransformInfo<DrawShape>
): Partial<DrawShape> {
{ initialShape, scaleX, scaleY }: TLDrawTransformInfo<T>
): Partial<T> => {
const initialShapeBounds = Utils.getFromCache(this.boundsCache, initialShape, () =>
Utils.getBoundsFromPoints(initialShape.points)
)
@ -255,8 +194,74 @@ export const Draw = new ShapeUtil<DrawShape, SVGSVGElement, TLDrawMeta>(() => ({
points,
point,
}
},
}))
}
getBounds = (shape: T) => {
// The goal here is to avoid recalculating the bounds from the
// points array, which is expensive. However, we still need a
// new bounds if the point has changed, but we will reuse the
// previous bounds-from-points result if we can.
const pointsHaveChanged = !this.pointsBoundsCache.has(shape.points)
const pointHasChanged = !(this.pointCache[shape.id] === shape.point)
if (pointsHaveChanged) {
// If the points have changed, then bust the points cache
const bounds = Utils.getBoundsFromPoints(shape.points)
this.pointsBoundsCache.set(shape.points, bounds)
this.shapeBoundsCache.set(shape.id, Utils.translateBounds(bounds, shape.point))
this.pointCache[shape.id] = shape.point
} else if (pointHasChanged && !pointsHaveChanged) {
// If the point have has changed, then bust the point cache
this.pointCache[shape.id] = shape.point
this.shapeBoundsCache.set(
shape.id,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
Utils.translateBounds(this.pointsBoundsCache.get(shape.points)!, shape.point)
)
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.shapeBoundsCache.get(shape.id)!
}
shouldRender = (prev: T, next: T) => {
return (
next.points !== prev.points ||
next.style !== prev.style ||
next.isComplete !== prev.isComplete
)
}
hitTestBounds = (shape: T, bounds: TLBounds) => {
// Test axis-aligned shape
if (!shape.rotation) {
const shapeBounds = this.getBounds(shape)
return (
Utils.boundsContain(bounds, shapeBounds) ||
((Utils.boundsContain(shapeBounds, bounds) ||
intersectBoundsBounds(shapeBounds, bounds).length > 0) &&
intersectBoundsPolyline(Utils.translateBounds(bounds, Vec.neg(shape.point)), shape.points)
.length > 0)
)
}
// Test rotated shape
const rBounds = this.getRotatedBounds(shape)
const rotatedBounds = Utils.getFromCache(this.rotatedCache, shape, () => {
const c = Utils.getBoundsCenter(Utils.getBoundsFromPoints(shape.points))
return shape.points.map((pt) => Vec.rotWith(pt, c, shape.rotation || 0))
})
return (
Utils.boundsContain(bounds, rBounds) ||
intersectBoundsPolyline(Utils.translateBounds(bounds, Vec.neg(shape.point)), rotatedBounds)
.length > 0
)
}
}
/* -------------------------------------------------- */
/* Helpers */
@ -272,7 +277,7 @@ const realPressureSettings: StrokeOptions = {
simulatePressure: false,
}
function getOptions(shape: DrawShape) {
function getOptions(shape: T) {
const styles = getShapeStyle(shape.style)
const options: StrokeOptions = {
@ -287,7 +292,7 @@ function getOptions(shape: DrawShape) {
return options
}
function getFillPath(shape: DrawShape) {
function getFillPath(shape: T) {
if (shape.points.length < 2) return ''
return Utils.getSvgPathFromStroke(
@ -295,14 +300,14 @@ function getFillPath(shape: DrawShape) {
)
}
function getDrawStrokePoints(shape: DrawShape, options: StrokeOptions) {
function getDrawStrokePoints(shape: T, options: StrokeOptions) {
return getStrokePoints(shape.points, options)
}
/**
* Get path data for a stroke with the DashStyle.Draw dash style.
*/
function getDrawStrokePathData(shape: DrawShape) {
function getDrawStrokePathData(shape: T) {
if (shape.points.length < 2) return ''
const options = getOptions(shape)
@ -319,7 +324,7 @@ function getDrawStrokePathData(shape: DrawShape) {
/**
* Get SVG path data for a shape that has a DashStyle other than DashStyles.Draw.
*/
function getSolidStrokePathData(shape: DrawShape) {
function getSolidStrokePathData(shape: T) {
const { points } = shape
if (points.length < 2) return 'M 0 0 L 0 0'

View file

@ -1,4 +1,4 @@
import { Ellipse } from './ellipse'
import { Ellipse } from '../'
describe('Ellipse shape', () => {
it('Creates a shape', () => {

View file

@ -1,25 +1,24 @@
import * as React from 'react'
import { SVGContainer, Utils, ShapeUtil, TLTransformInfo, TLBounds } from '@tldraw/core'
import { Utils, SVGContainer, TLIndicator, TLComponent, TLBounds } from '@tldraw/core'
import { Vec } from '@tldraw/vec'
import { DashStyle, EllipseShape, TLDrawShapeType, TLDrawMeta } from '~types'
import { defaultStyle, getShapeStyle } from '~shape/shape-styles'
import { getStrokeOutlinePoints, getStrokePoints } from 'perfect-freehand'
import {
intersectBoundsEllipse,
intersectLineSegmentEllipse,
intersectRayEllipse,
} from '@tldraw/intersect'
import { EASINGS } from '~state/utils'
import { BINDING_DISTANCE } from '~constants'
import { defaultStyle, getShapeStyle } from '../shape-styles'
import { EllipseShape, DashStyle, TLDrawShapeType, TLDrawShape, TLDrawTransformInfo } from '~types'
import { EASINGS, BINDING_DISTANCE } from '~constants'
import { TLDrawShapeUtil } from '../TLDrawShapeUtil'
import { intersectLineSegmentEllipse, intersectRayEllipse } from '@tldraw/intersect'
export const Ellipse = new ShapeUtil<EllipseShape, SVGSVGElement, TLDrawMeta>(() => ({
type: TLDrawShapeType.Ellipse,
type T = EllipseShape
type E = SVGSVGElement
pathCache: new WeakMap<EllipseShape, string>([]),
export class EllipseUtil extends TLDrawShapeUtil<T, E> {
type = TLDrawShapeType.Ellipse as const
canBind: true,
canBind = true
defaultProps: {
getShape = (props: Partial<T>): T => {
return Utils.deepMerge<T>(
{
id: 'id',
type: TLDrawShapeType.Ellipse,
name: 'Ellipse',
@ -30,8 +29,11 @@ export const Ellipse = new ShapeUtil<EllipseShape, SVGSVGElement, TLDrawMeta>(()
rotation: 0,
style: defaultStyle,
},
props
)
}
Component({ shape, meta, isBinding, events }, ref) {
Component: TLComponent<T, E> = ({ shape, isBinding, meta, events }, ref) => {
const {
radius: [radiusX, radiusY],
style,
@ -117,17 +119,13 @@ export const Ellipse = new ShapeUtil<EllipseShape, SVGSVGElement, TLDrawMeta>(()
/>
</SVGContainer>
)
},
}
Indicator({ shape }) {
Indicator: TLIndicator<T> = ({ shape }) => {
return <path d={getEllipseIndicatorPathData(shape, this.getCenter(shape))} />
},
}
shouldRender(prev, next) {
return next.radius !== prev.radius || next.style !== prev.style
},
getBounds(shape) {
getBounds = (shape: T) => {
return Utils.getFromCache(this.boundsCache, shape, () => {
return Utils.getRotatedEllipseBounds(
shape.point[0],
@ -137,9 +135,9 @@ export const Ellipse = new ShapeUtil<EllipseShape, SVGSVGElement, TLDrawMeta>(()
0
)
})
},
}
getRotatedBounds(shape) {
getRotatedBounds = (shape: T): TLBounds => {
return Utils.getRotatedEllipseBounds(
shape.point[0],
shape.point[1],
@ -147,36 +145,25 @@ export const Ellipse = new ShapeUtil<EllipseShape, SVGSVGElement, TLDrawMeta>(()
shape.radius[1],
shape.rotation
)
},
}
getCenter(shape): number[] {
shouldRender = (prev: T, next: T): boolean => {
return next.radius !== prev.radius || next.style !== prev.style
}
getCenter = (shape: T): number[] => {
return [shape.point[0] + shape.radius[0], shape.point[1] + shape.radius[1]]
},
}
hitTest(shape, point: number[]) {
return Utils.pointInEllipse(
point,
this.getCenter(shape),
shape.radius[0],
shape.radius[1],
shape.rotation
)
},
hitTestBounds(shape, bounds) {
return (
Utils.boundsContain(bounds, this.getBounds(shape)) ||
intersectBoundsEllipse(
bounds,
this.getCenter(shape),
shape.radius[0],
shape.radius[1],
shape.rotation
).length > 0
)
},
getBindingPoint(shape, fromShape, point, origin, direction, padding, anywhere) {
getBindingPoint = <K extends TLDrawShape>(
shape: T,
fromShape: K,
point: number[],
origin: number[],
direction: number[],
padding: number,
bindAnywhere: boolean
) => {
{
const bounds = this.getBounds(shape)
@ -197,7 +184,7 @@ export const Ellipse = new ShapeUtil<EllipseShape, SVGSVGElement, TLDrawMeta>(()
)
return
if (anywhere) {
if (bindAnywhere) {
if (Vec.dist(point, this.getCenter(shape)) < 12) {
bindingPoint = [0.5, 0.5]
} else {
@ -272,13 +259,13 @@ export const Ellipse = new ShapeUtil<EllipseShape, SVGSVGElement, TLDrawMeta>(()
distance,
}
}
},
}
transform(
_shape,
transform = (
shape: T,
bounds: TLBounds,
{ scaleX, scaleY, initialShape }: TLTransformInfo<EllipseShape>
) {
{ scaleX, scaleY, initialShape }: TLDrawTransformInfo<T>
): Partial<T> => {
const { rotation = 0 } = initialShape
return {
@ -289,15 +276,15 @@ export const Ellipse = new ShapeUtil<EllipseShape, SVGSVGElement, TLDrawMeta>(()
? -(rotation || 0)
: rotation || 0,
}
},
}
transformSingle(shape, bounds: TLBounds) {
transformSingle = (shape: T, bounds: TLBounds): Partial<T> => {
return {
point: Vec.round([bounds.minX, bounds.minY]),
radius: Vec.div([bounds.width, bounds.height], 2),
}
},
}))
}
}
/* -------------------------------------------------- */
/* Helpers */

View file

@ -1,4 +1,4 @@
import { Group } from './group'
import { Group } from '../'
describe('Group shape', () => {
it('Creates a shape', () => {

View file

@ -1,17 +1,23 @@
import * as React from 'react'
import { SVGContainer, ShapeUtil } from '@tldraw/core'
import { defaultStyle } from '~shape/shape-styles'
import { GroupShape, TLDrawShapeType, ColorStyle, TLDrawMeta } from '~types'
import { Utils, SVGContainer, TLIndicator, TLComponent } from '@tldraw/core'
import { defaultStyle } from '../shape-styles'
import { TLDrawShapeType, GroupShape, ColorStyle } from '~types'
import { getBoundsRectangle } from '../shared'
import css from '~styles'
import { BINDING_DISTANCE } from '~constants'
import { TLDrawShapeUtil } from '../TLDrawShapeUtil'
import css from '~styles'
export const Group = new ShapeUtil<GroupShape, SVGSVGElement, TLDrawMeta>(() => ({
type: TLDrawShapeType.Group,
type T = GroupShape
type E = SVGSVGElement
canBind: true,
export class GroupUtil extends TLDrawShapeUtil<T, E> {
type = TLDrawShapeType.Group as const
defaultProps: {
canBind = true
getShape = (props: Partial<T>): T => {
return Utils.deepMerge<T>(
{
id: 'id',
type: TLDrawShapeType.Group,
name: 'Group',
@ -23,8 +29,11 @@ export const Group = new ShapeUtil<GroupShape, SVGSVGElement, TLDrawMeta>(() =>
children: [],
style: defaultStyle,
},
props
)
}
Component({ shape, isBinding, isHovered, isSelected, events }, ref) {
Component: TLComponent<T, E> = ({ shape, isBinding, isHovered, isSelected, events }, ref) => {
const { id, size } = shape
const sw = 2
@ -65,9 +74,9 @@ export const Group = new ShapeUtil<GroupShape, SVGSVGElement, TLDrawMeta>(() =>
</g>
</SVGContainer>
)
},
}
Indicator({ shape }) {
Indicator: TLIndicator<T> = ({ shape }) => {
const { id, size } = shape
const sw = 2
@ -90,16 +99,16 @@ export const Group = new ShapeUtil<GroupShape, SVGSVGElement, TLDrawMeta>(() =>
{paths}
</g>
)
},
}
shouldRender(prev, next) {
return next.size !== prev.size || next.style !== prev.style
},
getBounds(shape) {
getBounds = (shape: T) => {
return getBoundsRectangle(shape, this.boundsCache)
},
}))
}
shouldRender = (prev: T, next: T) => {
return next.size !== prev.size || next.style !== prev.style
}
}
const scaledLines = css({
strokeWidth: 'calc(1.5px * var(--tl-scale))',

View file

@ -0,0 +1,51 @@
import type { TLDrawShapeUtil } from './TLDrawShapeUtil'
import { RectangleUtil } from './rectangle'
import { EllipseUtil } from './ellipse'
import { ArrowUtil } from './arrow'
import { GroupUtil } from './group'
import { StickyUtil } from './sticky'
import { TextUtil } from './text'
import { DrawUtil } from './draw'
import { TLDrawShape, TLDrawShapeType } from '~types'
import type { TLShapeUtilsMap } from '@tldraw/core'
export * from './shape-styles'
export * from './TLDrawShapeUtil'
export const Rectangle = new RectangleUtil()
export const Ellipse = new EllipseUtil()
export const Draw = new DrawUtil()
export const Arrow = new ArrowUtil()
export const Text = new TextUtil()
export const Group = new GroupUtil()
export const Sticky = new StickyUtil()
// type Utils = RectangleUtil | EllipseUtil | DrawUtil | ArrowUtil | TextUtil | GroupUtil | StickyUtil
// type MappedByKey<U extends string | number, K extends string | number, T extends Record<K, U>> = {
// [P in T[K]]: T extends any ? (P extends T[K] ? T : never) : never
// }
// export type ShapeUtilsMap = MappedByKey<Utils['type'], 'type', Utils>
// export const getShapeUtils = <T extends Utils['type']>(
// type: T | Extract<TLDrawShape, { type: T }>
// ): ShapeUtilsMap[T] => {
// if (typeof type === 'string') return shapeUtils[type]
// return shapeUtils[type.type]
// }
export const shapeUtils = {
[TLDrawShapeType.Rectangle]: Rectangle,
[TLDrawShapeType.Ellipse]: Ellipse,
[TLDrawShapeType.Draw]: Draw,
[TLDrawShapeType.Arrow]: Arrow,
[TLDrawShapeType.Text]: Text,
[TLDrawShapeType.Group]: Group,
[TLDrawShapeType.Sticky]: Sticky,
}
export const getShapeUtils = <T extends TLDrawShape>(shape: T | T['type']) => {
if (typeof shape === 'string') return shapeUtils[shape] as unknown as TLDrawShapeUtil<T>
return shapeUtils[shape.type] as unknown as TLDrawShapeUtil<T>
}

View file

@ -1,4 +1,4 @@
import { Rectangle } from './rectangle'
import { Rectangle } from '../'
describe('Rectangle shape', () => {
it('Creates a shape', () => {

View file

@ -1,18 +1,24 @@
import * as React from 'react'
import { Utils, SVGContainer, ShapeUtil } from '@tldraw/core'
import { Utils, SVGContainer, TLIndicator, TLComponent } from '@tldraw/core'
import { Vec } from '@tldraw/vec'
import getStroke, { getStrokePoints } from 'perfect-freehand'
import { defaultStyle, getShapeStyle } from '~shape/shape-styles'
import { RectangleShape, DashStyle, TLDrawShapeType, TLDrawMeta } from '~types'
import { getStroke, getStrokePoints } from 'perfect-freehand'
import { defaultStyle, getShapeStyle } from '../shape-styles'
import { RectangleShape, DashStyle, TLDrawShapeType } from '~types'
import { getBoundsRectangle, transformRectangle, transformSingleRectangle } from '../shared'
import { BINDING_DISTANCE } from '~constants'
import { TLDrawShapeUtil } from '../TLDrawShapeUtil'
export const Rectangle = new ShapeUtil<RectangleShape, SVGSVGElement, TLDrawMeta>(() => ({
type: TLDrawShapeType.Rectangle,
type T = RectangleShape
type E = SVGSVGElement
canBind: true,
export class RectangleUtil extends TLDrawShapeUtil<T, E> {
type = TLDrawShapeType.Rectangle as const
defaultProps: {
canBind = true
getShape = (props: Partial<T>): T => {
return Utils.deepMerge<T>(
{
id: 'id',
type: TLDrawShapeType.Rectangle,
name: 'Rectangle',
@ -23,12 +29,11 @@ export const Rectangle = new ShapeUtil<RectangleShape, SVGSVGElement, TLDrawMeta
rotation: 0,
style: defaultStyle,
},
props
)
}
shouldRender(prev, next) {
return next.size !== prev.size || next.style !== prev.style
},
Component({ shape, isBinding, meta, events }, ref) {
Component: TLComponent<T, E> = ({ shape, isBinding, meta, events }, ref) => {
const { id, size, style } = shape
const styles = getShapeStyle(style, meta.isDarkMode)
const strokeWidth = +styles.strokeWidth
@ -124,9 +129,9 @@ export const Rectangle = new ShapeUtil<RectangleShape, SVGSVGElement, TLDrawMeta
<g pointerEvents="stroke">{paths}</g>
</SVGContainer>
)
},
}
Indicator({ shape }) {
Indicator: TLIndicator<T> = ({ shape }) => {
const {
style,
size: [width, height],
@ -151,16 +156,20 @@ export const Rectangle = new ShapeUtil<RectangleShape, SVGSVGElement, TLDrawMeta
height={Math.max(1, height - sw * 2)}
/>
)
},
}
getBounds(shape) {
getBounds = (shape: T) => {
return getBoundsRectangle(shape, this.boundsCache)
},
}
transform: transformRectangle,
shouldRender = (prev: T, next: T) => {
return next.size !== prev.size || next.style !== prev.style
}
transformSingle: transformSingleRectangle,
}))
transform = transformRectangle
transformSingle = transformSingleRectangle
}
/* -------------------------------------------------- */
/* Helpers */

View file

@ -1,4 +1,4 @@
import { Sticky } from './sticky'
import { Sticky } from '../'
describe('Post-It shape', () => {
it('Creates a shape', () => {

View file

@ -1,37 +1,25 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import * as React from 'react'
import { css } from '@stitches/core'
import { HTMLContainer, ShapeUtil } from '@tldraw/core'
import { defaultStyle } from '~shape/shape-styles'
import { StickyShape, TLDrawMeta, TLDrawShapeType } from '~types'
import { getBoundsRectangle } from '../shared'
import { getStickyFontStyle, getStickyShapeStyle } from '~shape'
import { TextAreaUtils } from '../shared'
import { Utils, HTMLContainer, TLBounds, TLIndicator, TLComponent } from '@tldraw/core'
import { defaultStyle } from '../shape-styles'
import { StickyShape, TLDrawShapeType, TLDrawTransformInfo } from '~types'
import { getBoundsRectangle, TextAreaUtils } from '../shared'
import { TLDrawShapeUtil } from '../TLDrawShapeUtil'
import { getStickyFontStyle, getStickyShapeStyle } from '../shape-styles'
import css from '~styles'
import Vec from '@tldraw/vec'
const PADDING = 16
const MIN_CONTAINER_HEIGHT = 200
type T = StickyShape
type E = HTMLDivElement
function normalizeText(text: string) {
return text.replace(/\r?\n|\r/g, '\n')
}
export class StickyUtil extends TLDrawShapeUtil<T, E> {
type = TLDrawShapeType.Sticky as const
export const Sticky = new ShapeUtil<StickyShape, HTMLDivElement, TLDrawMeta>(() => ({
type: TLDrawShapeType.Sticky,
canBind = true
showBounds: false,
isStateful: false,
canBind: true,
canEdit: true,
canClone: true,
pathCache: new WeakMap<number[], string>([]),
defaultProps: {
getShape = (props: Partial<T>): T => {
return Utils.deepMerge<T>(
{
id: 'id',
type: TLDrawShapeType.Sticky,
name: 'Sticky',
@ -43,12 +31,14 @@ export const Sticky = new ShapeUtil<StickyShape, HTMLDivElement, TLDrawMeta>(()
rotation: 0,
style: defaultStyle,
},
props
)
}
shouldRender(prev, next) {
return next.size !== prev.size || next.style !== prev.style || next.text !== prev.text
},
Component({ events, shape, isEditing, onShapeBlur, onShapeChange, meta }, ref) {
Component: TLComponent<T, E> = (
{ shape, meta, events, isEditing, onShapeBlur, onShapeChange },
ref
) => {
const font = getStickyFontStyle(shape.style)
const { color, fill } = getStickyShapeStyle(shape.style, meta.isDarkMode)
@ -202,9 +192,9 @@ export const Sticky = new ShapeUtil<StickyShape, HTMLDivElement, TLDrawMeta>(()
</div>
</HTMLContainer>
)
},
}
Indicator({ shape }) {
Indicator: TLIndicator<T> = ({ shape }) => {
const {
size: [width, height],
} = shape
@ -212,13 +202,21 @@ export const Sticky = new ShapeUtil<StickyShape, HTMLDivElement, TLDrawMeta>(()
return (
<rect x={0} y={0} rx={3} ry={3} width={Math.max(1, width)} height={Math.max(1, height)} />
)
},
}
getBounds(shape) {
getBounds = (shape: T) => {
return getBoundsRectangle(shape, this.boundsCache)
},
}
transform(shape, bounds, { transformOrigin, scaleX, scaleY }) {
shouldRender = (prev: T, next: T) => {
return next.size !== prev.size || next.style !== prev.style || next.text !== prev.text
}
transform = (
shape: T,
bounds: TLBounds,
{ scaleX, scaleY, transformOrigin }: TLDrawTransformInfo<T>
): Partial<T> => {
const point = Vec.round([
bounds.minX +
(bounds.width - shape.size[0]) * (scaleX < 0 ? 1 - transformOrigin[0] : transformOrigin[0]),
@ -230,12 +228,23 @@ export const Sticky = new ShapeUtil<StickyShape, HTMLDivElement, TLDrawMeta>(()
return {
point,
}
},
}
transformSingle(shape) {
transformSingle = (shape: T): Partial<T> => {
return shape
},
}))
}
}
/* -------------------------------------------------- */
/* Helpers */
/* -------------------------------------------------- */
const PADDING = 16
const MIN_CONTAINER_HEIGHT = 200
function normalizeText(text: string) {
return text.replace(/\r?\n|\r/g, '\n')
}
const styledStickyContainer = css({
pointerEvents: 'all',

View file

@ -1,4 +1,4 @@
import { Text } from './text'
import { Text } from '../'
describe('Text shape', () => {
it('Creates a shape', () => {

View file

@ -1,67 +1,29 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import * as React from 'react'
import { HTMLContainer, TLBounds, Utils, ShapeUtil } from '@tldraw/core'
import { Vec } from '@tldraw/vec'
import { getShapeStyle, getFontStyle, defaultStyle } from '~shape/shape-styles'
import { TextShape, TLDrawShapeType, TLDrawMeta } from '~types'
import css from '~styles'
import { Utils, HTMLContainer, TLIndicator, TLComponent, TLBounds } from '@tldraw/core'
import { defaultStyle, getShapeStyle, getFontStyle } from '../shape-styles'
import { TextShape, TLDrawShapeType, TLDrawTransformInfo } from '~types'
import { TextAreaUtils } from '../shared'
import { BINDING_DISTANCE } from '~constants'
import { TLDrawShapeUtil } from '../TLDrawShapeUtil'
import css from '~styles'
import Vec from '@tldraw/vec'
const LETTER_SPACING = -1.5
type T = TextShape
type E = HTMLDivElement
function normalizeText(text: string) {
return text.replace(/\r?\n|\r/g, '\n')
}
export class TextUtil extends TLDrawShapeUtil<T, E> {
type = TLDrawShapeType.Text as const
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let melm: any
isAspectRatioLocked = true
function getMeasurementDiv() {
// A div used for measurement
document.getElementById('__textMeasure')?.remove()
canEdit = true
const pre = document.createElement('pre')
pre.id = '__textMeasure'
canBind = true
Object.assign(pre.style, {
whiteSpace: 'pre',
width: 'auto',
border: '1px solid red',
padding: '4px',
margin: '0px',
letterSpacing: `${LETTER_SPACING}px`,
opacity: '0',
position: 'absolute',
top: '-500px',
left: '0px',
zIndex: '9999',
pointerEvents: 'none',
userSelect: 'none',
alignmentBaseline: 'mathematical',
dominantBaseline: 'mathematical',
})
pre.tabIndex = -1
document.body.appendChild(pre)
return pre
}
if (typeof window !== 'undefined') {
melm = getMeasurementDiv()
}
export const Text = new ShapeUtil<TextShape, HTMLDivElement, TLDrawMeta>(() => ({
type: TLDrawShapeType.Text,
isAspectRatioLocked: true,
canEdit: true,
canBind: true,
defaultProps: {
getShape = (props: Partial<T>): T => {
return Utils.deepMerge<T>(
{
id: 'id',
type: TLDrawShapeType.Text,
name: 'Text',
@ -72,14 +34,14 @@ export const Text = new ShapeUtil<TextShape, HTMLDivElement, TLDrawMeta>(() => (
text: ' ',
style: defaultStyle,
},
shouldRender(prev, next): boolean {
return (
next.text !== prev.text || next.style.scale !== prev.style.scale || next.style !== prev.style
props
)
},
}
Component({ shape, meta, isEditing, isBinding, onShapeChange, onShapeBlur, events }, ref) {
Component: TLComponent<T, E> = (
{ shape, isBinding, isEditing, onShapeBlur, onShapeChange, meta, events },
ref
) => {
const rInput = React.useRef<HTMLTextAreaElement>(null)
const { text, style } = shape
const styles = getShapeStyle(style, meta.isDarkMode)
@ -217,14 +179,14 @@ export const Text = new ShapeUtil<TextShape, HTMLDivElement, TLDrawMeta>(() => (
</div>
</HTMLContainer>
)
},
}
Indicator({ shape }) {
Indicator: TLIndicator<T> = ({ shape }) => {
const { width, height } = this.getBounds(shape)
return <rect x={0} y={0} width={width} height={height} />
},
}
getBounds(shape): TLBounds {
getBounds = (shape: T) => {
const bounds = Utils.getFromCache(this.boundsCache, shape, () => {
if (!melm) {
// We're in SSR
@ -249,9 +211,19 @@ export const Text = new ShapeUtil<TextShape, HTMLDivElement, TLDrawMeta>(() => (
})
return Utils.translateBounds(bounds, shape.point)
},
}
transform(_shape, bounds, { initialShape, scaleX, scaleY }) {
shouldRender = (prev: T, next: T): boolean => {
return (
next.text !== prev.text || next.style.scale !== prev.style.scale || next.style !== prev.style
)
}
transform = (
shape: T,
bounds: TLBounds,
{ initialShape, scaleX, scaleY }: TLDrawTransformInfo<T>
): Partial<T> => {
const {
rotation = 0,
style: { scale = 1 },
@ -268,9 +240,13 @@ export const Text = new ShapeUtil<TextShape, HTMLDivElement, TLDrawMeta>(() => (
scale: nextScale,
},
}
},
}
transformSingle(_shape, bounds, { initialShape, scaleX, scaleY }) {
transformSingle = (
shape: T,
bounds: TLBounds,
{ initialShape, scaleX, scaleY }: TLDrawTransformInfo<T>
): Partial<T> | void => {
const {
style: { scale = 1 },
} = initialShape
@ -282,9 +258,9 @@ export const Text = new ShapeUtil<TextShape, HTMLDivElement, TLDrawMeta>(() => (
scale: scale * Math.max(Math.abs(scaleY), Math.abs(scaleX)),
},
}
},
}
onDoubleClickBoundsHandle(shape) {
onDoubleClickBoundsHandle = (shape: T) => {
const center = this.getCenter(shape)
const newCenter = this.getCenter({
@ -302,13 +278,57 @@ export const Text = new ShapeUtil<TextShape, HTMLDivElement, TLDrawMeta>(() => (
},
point: Vec.round(Vec.add(shape.point, Vec.sub(center, newCenter))),
}
},
}))
}
}
/* -------------------------------------------------- */
/* Helpers */
/* -------------------------------------------------- */
const LETTER_SPACING = -1.5
function normalizeText(text: string) {
return text.replace(/\r?\n|\r/g, '\n')
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let melm: any
function getMeasurementDiv() {
// A div used for measurement
document.getElementById('__textMeasure')?.remove()
const pre = document.createElement('pre')
pre.id = '__textMeasure'
Object.assign(pre.style, {
whiteSpace: 'pre',
width: 'auto',
border: '1px solid red',
padding: '4px',
margin: '0px',
letterSpacing: `${LETTER_SPACING}px`,
opacity: '0',
position: 'absolute',
top: '-500px',
left: '0px',
zIndex: '9999',
pointerEvents: 'none',
userSelect: 'none',
alignmentBaseline: 'mathematical',
dominantBaseline: 'mathematical',
})
pre.tabIndex = -1
document.body.appendChild(pre)
return pre
}
if (typeof window !== 'undefined') {
melm = getMeasurementDiv()
}
const wrapper = css({
width: '100%',
height: '100%',

View file

@ -1,2 +0,0 @@
export * from './shape-utils'
export * from './shape-styles'

View file

@ -1,19 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Rectangle, Ellipse, Arrow, Draw, Text, Group, Sticky } from './shapes'
import { TLDrawShapeType, TLDrawShape, TLDrawShapeUtil } from '~types'
// This is a bad "any", but the "this" context stuff we're doing doesn't allow us to union the types
export const tldrawShapeUtils: Record<TLDrawShapeType, any> = {
[TLDrawShapeType.Rectangle]: Rectangle,
[TLDrawShapeType.Ellipse]: Ellipse,
[TLDrawShapeType.Draw]: Draw,
[TLDrawShapeType.Arrow]: Arrow,
[TLDrawShapeType.Text]: Text,
[TLDrawShapeType.Group]: Group,
[TLDrawShapeType.Sticky]: Sticky,
}
export function getShapeUtils<T extends TLDrawShape>(type: T['type']) {
if (!tldrawShapeUtils[type]) throw Error(`Could not find a util of type ${type}`)
return tldrawShapeUtils[type] as TLDrawShapeUtil<T>
}

View file

@ -1,7 +0,0 @@
export * from './draw'
export * from './arrow'
export * from './rectangle'
export * from './ellipse'
export * from './text'
export * from './group'
export * from './sticky'

View file

@ -7,7 +7,7 @@ export function resetBounds(data: Data, ids: string[], pageId: string): TLDrawCo
const { before, after } = TLDR.mutateShapes(
data,
ids,
(shape) => TLDR.getShapeUtils(shape).onDoubleClickBoundsHandle(shape),
(shape) => TLDR.getShapeUtils(shape).onDoubleClickBoundsHandle?.(shape),
pageId
)

View file

@ -1,5 +1 @@
export const FIT_TO_SCREEN_PADDING = 128
export const SNAP_DISTANCE = 5
export const EMPTY_ARRAY = [] as any[]
export const SLOW_SPEED = 10
export const VERY_SLOW_SPEED = 2.5
/* eslint-disable @typescript-eslint/no-explicit-any */

View file

@ -7,11 +7,13 @@ import {
Session,
TLDrawStatus,
SessionType,
TLDrawShapeType,
} from '~types'
import { Vec } from '@tldraw/vec'
import { Utils, TLBounds } from '@tldraw/core'
import { TLDR } from '~state/tldr'
import { BINDING_DISTANCE } from '~constants'
import { shapeUtils } from '~shape-utils'
export class ArrowSession extends Session {
static type = SessionType.Arrow
@ -108,9 +110,9 @@ export class ArrowSession extends Session {
bindingId: undefined,
}
const utils = TLDR.getShapeUtils<ArrowShape>(shape.type)
const utils = shapeUtils[TLDrawShapeType.Arrow]
const change = utils.onHandleChange(
const change = utils.onHandleChange?.(
shape,
{
[handleId]: handle,
@ -181,7 +183,7 @@ export class ArrowSession extends Session {
const targetUtils = TLDR.getShapeUtils(target)
const arrowChange = TLDR.getShapeUtils<ArrowShape>(next.shape.type).onBindingChange(
const arrowChange = TLDR.getShapeUtils<ArrowShape>(next.shape.type).onBindingChange?.(
next.shape,
startBinding,
target,
@ -257,7 +259,9 @@ export class ArrowSession extends Session {
const targetUtils = TLDR.getShapeUtils(target)
const arrowChange = TLDR.getShapeUtils<ArrowShape>(next.shape.type).onBindingChange(
const utils = shapeUtils[TLDrawShapeType.Arrow]
const arrowChange = utils.onBindingChange(
next.shape,
draggedBinding,
target,

View file

@ -20,8 +20,8 @@ export class BrushSession extends Session {
update = (
data: Data,
point: number[],
shiftKey = false,
altKey = false,
_shiftKey = false,
_altKey = false,
metaKey = false
): TLDrawPatch => {
const { snapshot, origin } = this

View file

@ -54,7 +54,7 @@ export class HandleSession extends Session {
// First update the handle's next point
const change = TLDR.getShapeUtils(shape).onHandleChange(
const change = TLDR.getShapeUtils(shape).onHandleChange?.(
shape,
{
[handleId]: handle,

View file

@ -11,7 +11,7 @@ import { SessionType, TLDrawShape, TLDrawStatus } from '~types'
import { Session } from '~types'
import type { Data } from '~types'
import { TLDR } from '~state/tldr'
import { SLOW_SPEED, SNAP_DISTANCE } from '~state/constants'
import { SLOW_SPEED, SNAP_DISTANCE } from '~constants'
type SnapInfo =
| {
@ -54,7 +54,7 @@ export class TransformSingleSession extends Session {
return void null
}
update = (data: Data, point: number[], shiftKey = false, altKey = false, metaKey = false) => {
update = (data: Data, point: number[], shiftKey = false, _altKey = false, metaKey = false) => {
const { transformType } = this
const { currentPageId, initialShapeBounds, initialShape, id } = this.snapshot

View file

@ -5,7 +5,7 @@ import { Session, SessionType, TLDrawShape, TLDrawStatus } from '~types'
import type { Data } from '~types'
import { TLDR } from '~state/tldr'
import type { Command } from 'rko'
import { SLOW_SPEED, SNAP_DISTANCE } from '~state/constants'
import { SLOW_SPEED, SNAP_DISTANCE } from '~constants'
type SnapInfo =
| {

View file

@ -14,7 +14,7 @@ import {
ArrowBinding,
TLDrawShapeType,
} from '~types'
import { SLOW_SPEED, SNAP_DISTANCE } from '~state/constants'
import { SLOW_SPEED, SNAP_DISTANCE } from '~constants'
import { TLDR } from '~state/tldr'
import type { Patch } from 'rko'

View file

@ -1,5 +1,5 @@
import { TLBounds, TLTransformInfo, Utils, TLPageState } from '@tldraw/core'
import { getShapeUtils } from '~shape'
import { getShapeUtils, TLDrawShapeUtil } from '~shape-utils'
import {
Data,
ShapeStyles,
@ -9,7 +9,6 @@ import {
TLDrawPage,
TLDrawCommand,
TLDrawPatch,
TLDrawShapeUtil,
TLDrawShapeType,
ArrowShape,
} from '~types'
@ -21,7 +20,7 @@ export class TLDR {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static getShapeUtils<T extends TLDrawShape>(shape: T): TLDrawShapeUtil<T>
static getShapeUtils<T extends TLDrawShape>(shape: T | T['type']) {
return getShapeUtils<T>(typeof shape === 'string' ? shape : shape.type)
return getShapeUtils<T>(shape)
}
static getSelectedShapes(data: Data, pageId: string) {
@ -607,7 +606,7 @@ export class TLDR {
}
static onSessionComplete<T extends TLDrawShape>(shape: T) {
const delta = TLDR.getShapeUtils(shape).onSessionComplete(shape)
const delta = TLDR.getShapeUtils(shape).onSessionComplete?.(shape)
if (!delta) return shape
return { ...shape, ...delta }
}
@ -615,7 +614,7 @@ export class TLDR {
static onChildrenChange<T extends TLDrawShape>(data: Data, shape: T, pageId: string) {
if (!shape.children) return
const delta = TLDR.getShapeUtils(shape).onChildrenChange(
const delta = TLDR.getShapeUtils(shape).onChildrenChange?.(
shape,
shape.children.map((id) => TLDR.getShape(data, id, pageId))
)
@ -630,7 +629,7 @@ export class TLDR {
binding: TLDrawBinding,
otherShape: TLDrawShape
) {
const delta = TLDR.getShapeUtils(shape).onBindingChange(
const delta = TLDR.getShapeUtils(shape).onBindingChange?.(
shape,
binding,
otherShape,
@ -684,7 +683,7 @@ export class TLDR {
// of rotating the shape. Shapes with handles should never be rotated,
// because that makes a lot of other things incredible difficult.
if (shape.handles !== undefined) {
const change = this.getShapeUtils(shape).onHandleChange(
const change = this.getShapeUtils(shape).onHandleChange?.(
// Base the change on a shape with the next point
{ ...shape, point: nextPoint },
Object.fromEntries(

View file

@ -2,7 +2,6 @@
import { TLDrawState } from './tlstate'
import { mockDocument, TLStateUtils } from '~test'
import { ArrowShape, ColorStyle, SessionType, TLDrawShapeType } from '~types'
import * as idb from 'idb-keyval'
import type { SelectTool } from './tool/SelectTool'
describe('TLDrawState', () => {

View file

@ -39,13 +39,13 @@ import {
ExceptFirstTwo,
} from '~types'
import { TLDR } from './tldr'
import { defaultStyle, tldrawShapeUtils } from '~shape'
import { defaultStyle, shapeUtils } from '~shape-utils'
import * as Commands from './command'
import { ArgsOfType, getSession } from './session'
import { sample, USER_COLORS } from './utils'
import { sample } from './utils'
import { createTools, ToolType } from './tool'
import type { BaseTool } from './tool/BaseTool'
import * as constants from './constants'
import { USER_COLORS, FIT_TO_SCREEN_PADDING } from '~constants'
const uuid = Utils.uniqueId()
@ -218,7 +218,7 @@ export class TLDrawState extends StateManager<Data> {
const fromUtils = TLDR.getShapeUtils(fromShape)
// We only need to update the binding's "from" shape
const fromDelta = fromUtils.onBindingChange(
const fromDelta = fromUtils.onBindingChange?.(
fromShape,
binding,
toShape,
@ -1428,8 +1428,8 @@ export class TLDrawState extends StateManager<Data> {
let zoom = TLDR.getCameraZoom(
Math.min(
(this.bounds.width - constants.FIT_TO_SCREEN_PADDING) / bounds.width,
(this.bounds.height - constants.FIT_TO_SCREEN_PADDING) / bounds.height
(this.bounds.width - FIT_TO_SCREEN_PADDING) / bounds.width,
(this.bounds.height - FIT_TO_SCREEN_PADDING) / bounds.height
)
)
@ -1458,8 +1458,8 @@ export class TLDrawState extends StateManager<Data> {
let zoom = TLDR.getCameraZoom(
Math.min(
(this.bounds.width - constants.FIT_TO_SCREEN_PADDING) / bounds.width,
(this.bounds.height - constants.FIT_TO_SCREEN_PADDING) / bounds.height
(this.bounds.width - FIT_TO_SCREEN_PADDING) / bounds.width,
(this.bounds.height - FIT_TO_SCREEN_PADDING) / bounds.height
)
)
@ -1901,7 +1901,7 @@ export class TLDrawState extends StateManager<Data> {
const id = Utils.uniqueId()
const Text = tldrawShapeUtils.text
const Text = shapeUtils[TLDrawShapeType.Text]
const newShape = Text.create({
id,

View file

@ -1,6 +1,6 @@
import Vec from '@tldraw/vec'
import { Utils, TLPointerEventHandler } from '@tldraw/core'
import { Arrow } from '~shape/shapes'
import { Arrow } from '~shape-utils'
import { SessionType, TLDrawShapeType } from '~types'
import { BaseTool, Status } from '../BaseTool'

View file

@ -1,6 +1,6 @@
import Vec from '@tldraw/vec'
import { Utils, TLPointerEventHandler } from '@tldraw/core'
import { Draw } from '~shape/shapes'
import { Draw } from '~shape-utils'
import { SessionType, TLDrawShapeType } from '~types'
import { BaseTool, Status } from '../BaseTool'

View file

@ -1,6 +1,6 @@
import Vec from '@tldraw/vec'
import { Utils, TLPointerEventHandler, TLBoundsCorner } from '@tldraw/core'
import { Ellipse } from '~shape/shapes'
import { Ellipse } from '~shape-utils'
import { SessionType, TLDrawShapeType } from '~types'
import { BaseTool, Status } from '../BaseTool'

View file

@ -1,6 +1,6 @@
import Vec from '@tldraw/vec'
import { Utils, TLPointerEventHandler, TLBoundsCorner } from '@tldraw/core'
import { Rectangle } from '~shape/shapes'
import { Rectangle } from '~shape-utils'
import { SessionType, TLDrawShapeType } from '~types'
import { BaseTool, Status } from '../BaseTool'

View file

@ -1,7 +1,7 @@
import Vec from '@tldraw/vec'
import type { TLPointerEventHandler } from '@tldraw/core'
import { Utils } from '@tldraw/core'
import { Sticky } from '~shape/shapes'
import { Sticky } from '~shape-utils'
import { SessionType, TLDrawShapeType } from '~types'
import { BaseTool, Status } from '../BaseTool'

View file

@ -1,71 +1 @@
import type { Easing } from '~types'
export const PI2 = Math.PI * 2
export const EASINGS: Record<Easing, (t: number) => number> = {
linear: (t) => t,
easeInQuad: (t) => t * t,
easeOutQuad: (t) => t * (2 - t),
easeInOutQuad: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
easeInCubic: (t) => t * t * t,
easeOutCubic: (t) => --t * t * t + 1,
easeInOutCubic: (t) => (t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1),
easeInQuart: (t) => t * t * t * t,
easeOutQuart: (t) => 1 - --t * t * t * t,
easeInOutQuart: (t) => (t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t),
easeInQuint: (t) => t * t * t * t * t,
easeOutQuint: (t) => 1 + --t * t * t * t * t,
easeInOutQuint: (t) => (t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t),
easeInSine: (t) => 1 - Math.cos((t * Math.PI) / 2),
easeOutSine: (t) => Math.sin((t * Math.PI) / 2),
easeInOutSine: (t) => -(Math.cos(Math.PI * t) - 1) / 2,
easeInExpo: (t) => (t <= 0 ? 0 : Math.pow(2, 10 * t - 10)),
easeOutExpo: (t) => (t >= 1 ? 1 : 1 - Math.pow(2, -10 * t)),
easeInOutExpo: (t) =>
t <= 0
? 0
: t >= 1
? 1
: t < 0.5
? Math.pow(2, 20 * t - 10) / 2
: (2 - Math.pow(2, -20 * t + 10)) / 2,
}
export const EASING_STRINGS: Record<Easing, string> = {
linear: `(t) => t`,
easeInQuad: `(t) => t * t`,
easeOutQuad: `(t) => t * (2 - t)`,
easeInOutQuad: `(t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t)`,
easeInCubic: `(t) => t * t * t`,
easeOutCubic: `(t) => --t * t * t + 1`,
easeInOutCubic: `(t) => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1`,
easeInQuart: `(t) => t * t * t * t`,
easeOutQuart: `(t) => 1 - --t * t * t * t`,
easeInOutQuart: `(t) => t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t`,
easeInQuint: `(t) => t * t * t * t * t`,
easeOutQuint: `(t) => 1 + --t * t * t * t * t`,
easeInOutQuint: `(t) => t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t`,
easeInSine: `(t) => 1 - Math.cos((t * Math.PI) / 2)`,
easeOutSine: `(t) => Math.sin((t * Math.PI) / 2)`,
easeInOutSine: `(t) => -(Math.cos(Math.PI * t) - 1) / 2`,
easeInExpo: `(t) => (t <= 0 ? 0 : Math.pow(2, 10 * t - 10))`,
easeOutExpo: `(t) => (t >= 1 ? 1 : 1 - Math.pow(2, -10 * t))`,
easeInOutExpo: `(t) => t <= 0 ? 0 : t >= 1 ? 1 : t < 0.5 ? Math.pow(2, 20 * t - 10) / 2 : (2 - Math.pow(2, -20 * t + 10)) / 2`,
}
export const USER_COLORS = [
'#EC5E41',
'#F2555A',
'#F04F88',
'#E34BA9',
'#BD54C6',
'#9D5BD2',
'#7B66DC',
'#02B1CC',
'#11B3A3',
'#39B178',
'#55B467',
'#FF802B',
]
export const sample = (arr: any[]) => arr[Math.floor(Math.random() * arr.length)]

View file

@ -1,11 +1,28 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/ban-types */
import type { TLBinding, TLShapeProps, TLBounds, TLSnapLine } from '@tldraw/core'
import type { TLShape, TLShapeUtil, TLHandle } from '@tldraw/core'
import type {
TLBinding,
TLBoundsCorner,
TLBoundsEdge,
TLShapeProps,
TLShape,
TLHandle,
TLBounds,
TLSnapLine,
} from '@tldraw/core'
import type { TLPage, TLUser, TLPageState } from '@tldraw/core'
import type { StoreApi } from 'zustand'
import type { Command, Patch } from 'rko'
export interface TLDrawTransformInfo<T extends TLShape> {
type: TLBoundsEdge | TLBoundsCorner
initialShape: T
scaleX: number
scaleY: number
transformOrigin: number[]
}
// old
export type TLStore = StoreApi<Data>
export type TLChange = Data
@ -257,8 +274,6 @@ export type TLDrawShape =
| GroupShape
| StickyShape
export type TLDrawShapeUtil<T extends TLDrawShape> = TLShapeUtil<T, any, TLDrawMeta>
export type ArrowBinding = TLBinding<{
handleId: keyof ArrowShape['handles']
distance: number

View file

@ -264,7 +264,7 @@ export class Vec {
* @param A
* @param r rotation in radians
*/
static rot = (A: number[], r: number): number[] => {
static rot = (A: number[], r = 0): number[] => {
return [A[0] * Math.cos(r) - A[1] * Math.sin(r), A[0] * Math.sin(r) + A[1] * Math.cos(r)]
}
@ -274,7 +274,7 @@ export class Vec {
* @param C center
* @param r rotation in radians
*/
static rotWith = (A: number[], C: number[], r: number): number[] => {
static rotWith = (A: number[], C: number[], r = 0): number[] => {
if (r === 0) return A
const s = Math.sin(r)

View file

@ -1,6 +1,5 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { TLDraw, TLDrawState, Data, TLDrawDocument, TLDrawUser } from '@tldraw/tldraw'
import * as gtag from '-utils/gtag'
import * as React from 'react'
import { createClient, Presence } from '@liveblocks/client'
import { LiveblocksProvider, RoomProvider, useObject, useErrorListener } from '@liveblocks/react'
@ -59,7 +58,8 @@ function Editor({ id }: { id: string }) {
})
// Put the tlstate into the window, for debugging.
const handleMount = React.useCallback((tlstate: TLDrawState) => {
const handleMount = React.useCallback(
(tlstate: TLDrawState) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.tlstate = tlstate
@ -67,7 +67,9 @@ function Editor({ id }: { id: string }) {
tlstate.loadRoom(id)
setTlstate(tlstate)
}, [])
},
[id]
)
const handleChange = React.useCallback(
(_tlstate: TLDrawState, state: Data, reason: string) => {

View file

@ -24,11 +24,12 @@
"@sentry/tracing": "^6.13.2",
"@stitches/react": "^1.0.0",
"@tldraw/tldraw": "^0.0.128",
"next": "^11.1.2",
"@types/next-auth": "^3.15.0",
"next": "^12.0.1",
"next-auth": "^3.29.0",
"next-pwa": "^5.2.23",
"next-pwa": "^5.4.0",
"next-themes": "^0.0.15",
"next-transpile-modules": "^8.0.0",
"next-transpile-modules": "^9.0.0",
"react": "17.0.2",
"react-dom": "17.0.2"
},

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -2,7 +2,11 @@
"compilerOptions": {
"composite": true,
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
@ -17,11 +21,29 @@
"baseUrl": ".",
"rootDir": ".",
"paths": {
"-*": ["./*"],
"@tldraw/tldraw": ["../tldraw"]
}
"-*": [
"./*"
],
"@tldraw/tldraw": [
"../tldraw"
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"],
"references": [{ "path": "../tldraw" }, { "path": "../core" }]
"incremental": true
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
],
"references": [
{
"path": "../tldraw"
},
{
"path": "../core"
}
]
}

497
yarn.lock
View file

@ -881,14 +881,7 @@
core-js-pure "^3.16.0"
regenerator-runtime "^0.13.4"
"@babel/runtime@7.15.3":
version "7.15.3"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.3.tgz#2e1c2880ca118e5b2f9988322bd8a7656a32502b"
integrity sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.0", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
"@babel/runtime@7.15.4", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.0", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
version "7.15.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a"
integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==
@ -2673,10 +2666,10 @@
require_optional "^1.0.1"
typeorm "^0.2.30"
"@next/env@11.1.2":
version "11.1.2"
resolved "https://registry.yarnpkg.com/@next/env/-/env-11.1.2.tgz#27996efbbc54c5f949f5e8c0a156e3aa48369b99"
integrity sha512-+fteyVdQ7C/OoulfcF6vd1Yk0FEli4453gr8kSFbU8sKseNSizYq6df5MKz/AjwLptsxrUeIkgBdAzbziyJ3mA==
"@next/env@12.0.1":
version "12.0.1"
resolved "https://registry.yarnpkg.com/@next/env/-/env-12.0.1.tgz#d57141ef1fe844f6f7c18cdaf29a712788c18ca4"
integrity sha512-+eJ8mQbAcV/ZILRAgIx9xwDg6hrqm6m/7QLfEvsf2BPnsh+fwU4Xf1zgcbyqD2V4ja4OTWG6ow+Hiukgap3mZQ==
"@next/eslint-plugin-next@11.1.2":
version "11.1.2"
@ -2685,15 +2678,15 @@
dependencies:
glob "7.1.7"
"@next/polyfill-module@11.1.2":
version "11.1.2"
resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-11.1.2.tgz#1fe92c364fdc81add775a16c678f5057c6aace98"
integrity sha512-xZmixqADM3xxtqBV0TpAwSFzWJP0MOQzRfzItHXf1LdQHWb0yofHHC+7eOrPFic8+ZGz5y7BdPkkgR1S25OymA==
"@next/polyfill-module@12.0.1":
version "12.0.1"
resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-12.0.1.tgz#d20abf06f686ee7a8bd0d9056accfd0662f19e87"
integrity sha512-fTrndwGuvrQO+4myVGcPtsYI4/tmZBhHHJId7MSHWz+9gW4NFgsmDlr8OI9Th2ZXpqk5WHLsTYQ+dLiQp1zV4g==
"@next/react-dev-overlay@11.1.2":
version "11.1.2"
resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-11.1.2.tgz#73795dc5454b7af168bac93df7099965ebb603be"
integrity sha512-rDF/mGY2NC69mMg2vDqzVpCOlWqnwPUXB2zkARhvknUHyS6QJphPYv9ozoPJuoT/QBs49JJd9KWaAzVBvq920A==
"@next/react-dev-overlay@12.0.1":
version "12.0.1"
resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-12.0.1.tgz#134299ae44fad5a59448d5e671518074f37cba95"
integrity sha512-dLv1to40bvadbr0VO8pBsbr9ddgktCLilfejOpEFQkOOrdQBUuIfegqqEDiCL9THkAO3QGYY4t/ZPfv9wrxLZQ==
dependencies:
"@babel/code-frame" "7.12.11"
anser "1.4.9"
@ -2705,32 +2698,67 @@
shell-quote "1.7.2"
source-map "0.8.0-beta.0"
stacktrace-parser "0.1.10"
strip-ansi "6.0.0"
strip-ansi "6.0.1"
"@next/react-refresh-utils@11.1.2":
version "11.1.2"
resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-11.1.2.tgz#44ea40d8e773e4b77bad85e24f6ac041d5e4b4a5"
integrity sha512-hsoJmPfhVqjZ8w4IFzoo8SyECVnN+8WMnImTbTKrRUHOVJcYMmKLL7xf7T0ft00tWwAl/3f3Q3poWIN2Ueql/Q==
"@next/react-refresh-utils@12.0.1":
version "12.0.1"
resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-12.0.1.tgz#219be7a81696a7bd1e2d4ee397ca100eb8262f23"
integrity sha512-CjTBR9a6ai+2fUT8KFya9AiTaCnfDY34H6pDmtdJdkD+vY08AwtPpv10kzsgNEhsL06210yVzH59IsEQLBIllA==
"@next/swc-darwin-arm64@11.1.2":
version "11.1.2"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-11.1.2.tgz#93226c38db488c4b62b30a53b530e87c969b8251"
integrity sha512-hZuwOlGOwBZADA8EyDYyjx3+4JGIGjSHDHWrmpI7g5rFmQNltjlbaefAbiU5Kk7j3BUSDwt30quJRFv3nyJQ0w==
"@next/swc-android-arm64@12.0.1":
version "12.0.1"
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.0.1.tgz#c776853e0911c12fcc69a69cd7ab111dff29f8d2"
integrity sha512-zI/6zsZuO2igknzHzfaQep0PeD3d4/qdjXUcQLwLHJQtGdhPvZFMke1z3BBWZqePHVsR1JPjE4QTii7udF5qsQ==
"@next/swc-darwin-x64@11.1.2":
version "11.1.2"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-11.1.2.tgz#792003989f560c00677b5daeff360b35b510db83"
integrity sha512-PGOp0E1GisU+EJJlsmJVGE+aPYD0Uh7zqgsrpD3F/Y3766Ptfbe1lEPPWnRDl+OzSSrSrX1lkyM/Jlmh5OwNvA==
"@next/swc-darwin-arm64@12.0.1":
version "12.0.1"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.0.1.tgz#bee9c5932511c993ab384ef9aedb86c02532d41e"
integrity sha512-vRfHz7rEt9+TTfwi3uY9ObUSLhzMmgVZ96b+yOSmZ6Kxs/V46IXHOLawCnoldXylpskZ/+HTWcrB1D3aimGeZA==
"@next/swc-linux-x64-gnu@11.1.2":
version "11.1.2"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-11.1.2.tgz#8216b2ae1f21f0112958735c39dd861088108f37"
integrity sha512-YcDHTJjn/8RqvyJVB6pvEKXihDcdrOwga3GfMv/QtVeLphTouY4BIcEUfrG5+26Nf37MP1ywN3RRl1TxpurAsQ==
"@next/swc-darwin-x64@12.0.1":
version "12.0.1"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.0.1.tgz#a0bdcbcf11b8b62190ec8e0406cecbbcc810b7fc"
integrity sha512-mM7QLIqRUqR8I74gbZ4Uq+dY8k3Whrs98wK+vPurmDTBhXhaVnAYblEkEwe0DJGqlmjD4w6faYfCydmFI69jqw==
"@next/swc-win32-x64-msvc@11.1.2":
version "11.1.2"
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-11.1.2.tgz#e15824405df137129918205e43cb5e9339589745"
integrity sha512-e/pIKVdB+tGQYa1cW3sAeHm8gzEri/HYLZHT4WZojrUxgWXqx8pk7S7Xs47uBcFTqBDRvK3EcQpPLf3XdVsDdg==
"@next/swc-linux-arm-gnueabihf@12.0.1":
version "12.0.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.0.1.tgz#d0132637288f452ad5c6a6161e42aebcd4355f82"
integrity sha512-QF5LVyAWTah5i1p/yG4a8nTGRXerHoDkS3kWYCdjcwlALOiAJ9m0GUTks/O47izNokBAbZnL7URUdvtGFjP0Ng==
"@next/swc-linux-arm64-gnu@12.0.1":
version "12.0.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.0.1.tgz#6b310344b9bac7700eaff8f4c536540b1226e378"
integrity sha512-ETFUh373WsjUJJr32GHSDlVSgwFwS+EJUJuSH40Pr4xB6250YxuRk8ccF6QR5LHqTL4tbbVEEfCD8sZVnccP8w==
"@next/swc-linux-arm64-musl@12.0.1":
version "12.0.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.0.1.tgz#8ab1fc81d18bbb70bb15bcc4250382257bba6298"
integrity sha512-pfnXNjKywXyp2DJsjFhkfOlvcNu9xa8HgEhCUKXm1OZ4pGnpeb1+UD4t5Pn9b9ggiWPzauZK1abR/9nShvbSzw==
"@next/swc-linux-x64-gnu@12.0.1":
version "12.0.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.0.1.tgz#a664268aedec402da5df34efac1b337d9b0e492e"
integrity sha512-d9cXS27Ar7TTtA3BJ8gxosDDdVNSFy4MQiwsszKlEiqfGrnINeXKdVgeiOa+xxq+JxNvPzonp4sbX6k8InIocg==
"@next/swc-linux-x64-musl@12.0.1":
version "12.0.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.0.1.tgz#4b7e5fee5a62adb6d9c9aad1a4aa00a6a09b53dc"
integrity sha512-4SAmi7riavU6TFGX7wQFioFi/vx8uJ2/Cx7ZfrYiZzzKmmuu2eM8onW1kcKu+aQD777x/kvzW4+2pWkM2gyPOA==
"@next/swc-win32-arm64-msvc@12.0.1":
version "12.0.1"
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.0.1.tgz#9ff0c2a2f00f41d40bd44d6da195bdf649d807c6"
integrity sha512-JRad3QyXvs5zDkeEmc6z5tEvm/ZZnjnsBY921zWw7OIcIZR5wAs+1AnRVjIxHTEHSExxOvBgPyEMpgVkB8OyxQ==
"@next/swc-win32-ia32-msvc@12.0.1":
version "12.0.1"
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.0.1.tgz#5a927ec832b184ce2e35f8ec668daa34175e47d0"
integrity sha512-ierQmzVWPi6a7PqrdgfI6nrQ/SWJ9W5jllByyQeFIOKhOzZiz030Tw+U6V7NqE3gGNeRwpj56Iya8nUb3hlM1g==
"@next/swc-win32-x64-msvc@12.0.1":
version "12.0.1"
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.0.1.tgz#fe145cf7baf79564159a87a545e430f96c835578"
integrity sha512-li3CCXpdMX0+wJlQpy0xZmHCgHMebaBf5X2BIAJrv8cQXYc6dejeojttXLFNCF0dNAo3UzlbP6h7N+8p6Wbakw==
"@node-rs/helper@1.2.1", "@node-rs/helper@^1.0.0":
version "1.2.1"
@ -3837,6 +3865,13 @@
resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c"
integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==
"@types/next-auth@^3.15.0":
version "3.15.0"
resolved "https://registry.yarnpkg.com/@types/next-auth/-/next-auth-3.15.0.tgz#b602f4689b35fff3d2af09500c79ab11c4745bca"
integrity sha512-ZVfejlu81YiIRX1m0iKAfvZ3nK7K9EyZWhNARNKsFop8kNAgEvMnlKpTpwN59xkK2OhyWLagPuiDAVBYSO9jSA==
dependencies:
next-auth "*"
"@types/node@*", "@types/node@>= 8", "@types/node@^16.7.10":
version "16.9.4"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.4.tgz#a12f0ee7847cf17a97f6fdf1093cb7a9af23cca4"
@ -3933,21 +3968,11 @@
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
"@types/source-list-map@*":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==
"@types/stack-utils@^2.0.0":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
"@types/tapable@^1":
version "1.0.8"
resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310"
integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==
"@types/testing-library__jest-dom@^5.9.1":
version "5.14.1"
resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.1.tgz#014162a5cee6571819d48e999980694e2f657c3c"
@ -3960,34 +3985,6 @@
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756"
integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==
"@types/uglify-js@*":
version "3.13.1"
resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.13.1.tgz#5e889e9e81e94245c75b6450600e1c5ea2878aea"
integrity sha512-O3MmRAk6ZuAKa9CHgg0Pr0+lUOqoMLpc9AS4R8ano2auvsg7IE8syF3Xh/NPr26TWklxYcqoEEFdzLLs1fV9PQ==
dependencies:
source-map "^0.6.1"
"@types/webpack-sources@*":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-3.2.0.tgz#16d759ba096c289034b26553d2df1bf45248d38b"
integrity sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==
dependencies:
"@types/node" "*"
"@types/source-list-map" "*"
source-map "^0.7.3"
"@types/webpack@^4.4.31":
version "4.41.31"
resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.31.tgz#c35f252a3559ddf9c85c0d8b0b42019025e581aa"
integrity sha512-/i0J7sepXFIp1ZT7FjUGi1eXMCg8HCCzLJEQkKsOtbJFontsJLolBcDC+3qxn5pPwiCt1G0ZdRmYRzNBtvpuGQ==
dependencies:
"@types/node" "*"
"@types/tapable" "^1"
"@types/uglify-js" "*"
"@types/webpack-sources" "*"
anymatch "^3.0.0"
source-map "^0.6.0"
"@types/yargs-parser@*":
version "20.2.1"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129"
@ -4136,16 +4133,21 @@ acorn-walk@^8.1.1:
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
acorn@8.5.0, acorn@^8.2.4, acorn@^8.4.1:
version "8.5.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2"
integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==
acorn@^6.2.1:
version "6.4.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==
acorn@^7.1.1, acorn@^7.4.0:
version "7.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
acorn@^8.2.4, acorn@^8.4.1:
version "8.5.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2"
integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==
add-stream@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa"
@ -4258,7 +4260,7 @@ ansi-regex@^4.1.0:
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
ansi-regex@^5.0.0:
ansi-regex@^5.0.0, ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
@ -4292,7 +4294,7 @@ any-promise@^1.0.0:
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
anymatch@^3.0.0, anymatch@^3.0.3, anymatch@~3.1.1:
anymatch@^3.0.3, anymatch@~3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
@ -4489,14 +4491,6 @@ assert@2.0.0:
object-is "^1.0.1"
util "^0.12.0"
assert@^1.1.1:
version "1.5.0"
resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb"
integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==
dependencies:
object-assign "^4.1.1"
util "0.10.3"
assign-symbols@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
@ -4507,11 +4501,6 @@ ast-types-flow@^0.0.7:
resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0=
ast-types@0.13.2:
version "0.13.2"
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.2.tgz#df39b677a911a83f3a049644fb74fdded23cea48"
integrity sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA==
astral-regex@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
@ -4824,7 +4813,7 @@ browserify-sign@^4.0.0:
readable-stream "^3.6.0"
safe-buffer "^5.2.0"
browserify-zlib@0.2.0, browserify-zlib@^0.2.0:
browserify-zlib@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f"
integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==
@ -4888,15 +4877,6 @@ buffer@5.6.0:
base64-js "^1.0.2"
ieee754 "^1.1.4"
buffer@^4.3.0:
version "4.9.2"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8"
integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"
isarray "^1.0.0"
buffer@^6.0.3:
version "6.0.3"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
@ -5215,12 +5195,11 @@ clean-stack@^2.0.0:
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
clean-webpack-plugin@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/clean-webpack-plugin/-/clean-webpack-plugin-3.0.0.tgz#a99d8ec34c1c628a4541567aa7b457446460c62b"
integrity sha512-MciirUH5r+cYLGCOL5JX/ZLzOZbVr1ot3Fw+KcvbhUb6PM+yycqd9ZhIlcigQ5gl+XhppNmw3bEFuaaMNyLj3A==
clean-webpack-plugin@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz#72947d4403d452f38ed61a9ff0ada8122aacd729"
integrity sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==
dependencies:
"@types/webpack" "^4.4.31"
del "^4.1.1"
cli-cursor@^2.1.0:
@ -5446,17 +5425,12 @@ config-chain@^1.1.11, config-chain@^1.1.12:
ini "^1.3.4"
proto-list "~1.2.1"
console-browserify@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==
console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
constants-browserify@1.0.0, constants-browserify@^1.0.0:
constants-browserify@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=
@ -5738,7 +5712,7 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
crypto-browserify@3.12.0, crypto-browserify@^3.11.0:
crypto-browserify@3.12.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==
@ -6103,11 +6077,6 @@ domain-browser@4.19.0:
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.19.0.tgz#1093e17c0a17dbd521182fe90d49ac1370054af1"
integrity sha512-fRA+BaAWOR/yr/t7T9E9GJztHPeFjj8U35ajyAjCDtAAnTn1Rc1f6W6VGPJrO1tkQv9zWu+JRof7z6oQtiYVFQ==
domain-browser@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==
domexception@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
@ -6722,7 +6691,7 @@ eventemitter3@^4.0.4:
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
events@^3.0.0:
events@3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
@ -7683,7 +7652,7 @@ http-signature@~1.2.0:
jsprim "^1.2.2"
sshpk "^1.7.0"
https-browserify@1.0.0, https-browserify@^1.0.0:
https-browserify@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
@ -7853,21 +7822,11 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4:
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
inherits@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
inherits@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
ini@^1.3.2, ini@^1.3.4:
version "1.3.8"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
@ -8328,7 +8287,7 @@ isarray@0.0.1:
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
isarray@1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
@ -9955,13 +9914,6 @@ nanomatch@^1.2.9:
snapdragon "^0.8.1"
to-regex "^3.0.1"
native-url@0.3.4:
version "0.3.4"
resolved "https://registry.yarnpkg.com/native-url/-/native-url-0.3.4.tgz#29c943172aed86c63cee62c8c04db7f5756661f8"
integrity sha512-6iM8R99ze45ivyH8vybJ7X0yekIcPf5GgLV5K0ENCbmRcaRIDoj37BC8iLEmaaBfqqb8enuZ5p0uhY+lVAbAcA==
dependencies:
querystring "^0.2.0"
natural-compare@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
@ -9972,12 +9924,12 @@ negotiator@^0.6.2:
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
neo-async@^2.6.0:
neo-async@^2.6.0, neo-async@^2.6.1:
version "2.6.2"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
next-auth@^3.29.0:
next-auth@*, next-auth@^3.29.0:
version "3.29.0"
resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-3.29.0.tgz#60ddbfc7ed8ae7d43ebb02c16dc58eebf5dcb337"
integrity sha512-B//4QTv/1Of0D+roZ82URmI6L2JSbkKgeaKI7Mdrioq8lAzp9ff8NdmouvZL/7zwrPe2cUyM6MLYlasfuI3ZIQ==
@ -9995,45 +9947,45 @@ next-auth@^3.29.0:
preact-render-to-string "^5.1.14"
querystring "^0.2.0"
next-pwa@^5.2.23:
version "5.3.1"
resolved "https://registry.yarnpkg.com/next-pwa/-/next-pwa-5.3.1.tgz#5bcd854422a452fd1fd56500d1c483664d9733d9"
integrity sha512-Os6bf/lEYoztvsILYkAhDjiYfLNTprqEumxuv5DjhoFh4OpIzO7U0GKRI6MZluH4SfAoe0IdHz9knnZxybSnag==
next-pwa@^5.4.0:
version "5.4.0"
resolved "https://registry.yarnpkg.com/next-pwa/-/next-pwa-5.4.0.tgz#61587d0d5fc3c5304f23dd4f187888c1c717ec5e"
integrity sha512-0tTgZsGGFAd2L151Y8Tv8gB8Q6NdWA0aHZRGRm4mHPnq06ZeVXAucVNpHcAcqALKcdp54J2puV+BQAY72tShSA==
dependencies:
babel-loader "^8.2.2"
clean-webpack-plugin "^3.0.0"
clean-webpack-plugin "^4.0.0"
globby "^11.0.4"
terser-webpack-plugin "^5.1.4"
workbox-webpack-plugin "^6.2.4"
workbox-window "^6.2.4"
terser-webpack-plugin "^5.2.4"
workbox-webpack-plugin "^6.3.0"
workbox-window "^6.3.0"
next-themes@^0.0.15:
version "0.0.15"
resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.0.15.tgz#ab0cee69cd763b77d41211f631e108beab39bf7d"
integrity sha512-LTmtqYi03c4gMTJmWwVK9XkHL7h0/+XrtR970Ujvtu3s0kZNeJN24aJsi4rkZOI8i19+qq6f8j+8Duwy5jqcrQ==
next-transpile-modules@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/next-transpile-modules/-/next-transpile-modules-8.0.0.tgz#56375cdc25ae5d23a834195f277fc2737b26cb97"
integrity sha512-Q2f2yB0zMJ8KJbIYAeZoIxG6cSfVk813zr6B5HzsLMBVcJ3FaF8lKr7WG66n0KlHCwjLSmf/6EkgI6QQVWHrDw==
next-transpile-modules@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/next-transpile-modules/-/next-transpile-modules-9.0.0.tgz#133b1742af082e61cc76b02a0f12ffd40ce2bf90"
integrity sha512-VCNFOazIAnXn1hvgYYSTYMnoWgKgwlYh4lm1pKbSfiB3kj5ZYLcKVhfh3jkPOg1cnd9DP+pte9yCUocdPEUBTQ==
dependencies:
enhanced-resolve "^5.7.0"
escalade "^3.1.1"
next@^11.1.2:
version "11.1.2"
resolved "https://registry.yarnpkg.com/next/-/next-11.1.2.tgz#527475787a9a362f1bc916962b0c0655cc05bc91"
integrity sha512-azEYL0L+wFjv8lstLru3bgvrzPvK0P7/bz6B/4EJ9sYkXeW8r5Bjh78D/Ol7VOg0EIPz0CXoe72hzAlSAXo9hw==
next@^12.0.1:
version "12.0.1"
resolved "https://registry.yarnpkg.com/next/-/next-12.0.1.tgz#7b82a73bc185bfda7372e7e8309f9b38e6be9cb0"
integrity sha512-4MNXAbD9+Tmtejg0TOKbaP52Cgu4mIn2ejKMLHWV0acxWGkkcE7QvdZwvg5pkg3fQBMrgucOxxtmw4D7yWaZvg==
dependencies:
"@babel/runtime" "7.15.3"
"@babel/runtime" "7.15.4"
"@hapi/accept" "5.0.2"
"@next/env" "11.1.2"
"@next/polyfill-module" "11.1.2"
"@next/react-dev-overlay" "11.1.2"
"@next/react-refresh-utils" "11.1.2"
"@next/env" "12.0.1"
"@next/polyfill-module" "12.0.1"
"@next/react-dev-overlay" "12.0.1"
"@next/react-refresh-utils" "12.0.1"
"@node-rs/helper" "1.2.1"
acorn "8.5.0"
assert "2.0.0"
ast-types "0.13.2"
browserify-zlib "0.2.0"
browserslist "4.16.6"
buffer "5.6.0"
@ -10046,40 +9998,48 @@ next@^11.1.2:
domain-browser "4.19.0"
encoding "0.1.13"
etag "1.8.1"
events "3.3.0"
find-cache-dir "3.3.1"
get-orientation "1.1.2"
https-browserify "1.0.0"
image-size "1.0.0"
jest-worker "27.0.0-next.5"
native-url "0.3.4"
node-fetch "2.6.1"
node-html-parser "1.4.9"
node-libs-browser "^2.2.1"
os-browserify "0.3.0"
p-limit "3.1.0"
path-browserify "1.0.1"
pnp-webpack-plugin "1.6.4"
postcss "8.2.15"
process "0.11.10"
querystring-es3 "0.2.1"
raw-body "2.4.1"
react-is "17.0.2"
react-refresh "0.8.3"
react-server-dom-webpack "0.0.0-experimental-3c4c1c470-20211021"
regenerator-runtime "0.13.4"
stream-browserify "3.0.0"
stream-http "3.1.1"
string_decoder "1.3.0"
styled-jsx "4.0.1"
styled-jsx "5.0.0-beta.3"
timers-browserify "2.0.12"
tty-browserify "0.0.1"
use-subscription "1.5.1"
util "0.12.4"
vm-browserify "1.1.2"
watchpack "2.1.1"
web-streams-polyfill "3.0.3"
optionalDependencies:
"@next/swc-darwin-arm64" "11.1.2"
"@next/swc-darwin-x64" "11.1.2"
"@next/swc-linux-x64-gnu" "11.1.2"
"@next/swc-win32-x64-msvc" "11.1.2"
"@next/swc-android-arm64" "12.0.1"
"@next/swc-darwin-arm64" "12.0.1"
"@next/swc-darwin-x64" "12.0.1"
"@next/swc-linux-arm-gnueabihf" "12.0.1"
"@next/swc-linux-arm64-gnu" "12.0.1"
"@next/swc-linux-arm64-musl" "12.0.1"
"@next/swc-linux-x64-gnu" "12.0.1"
"@next/swc-linux-x64-musl" "12.0.1"
"@next/swc-win32-arm64-msvc" "12.0.1"
"@next/swc-win32-ia32-msvc" "12.0.1"
"@next/swc-win32-x64-msvc" "12.0.1"
nice-try@^1.0.4:
version "1.0.5"
@ -10157,35 +10117,6 @@ node-int64@^0.4.0:
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=
node-libs-browser@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425"
integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==
dependencies:
assert "^1.1.1"
browserify-zlib "^0.2.0"
buffer "^4.3.0"
console-browserify "^1.1.0"
constants-browserify "^1.0.0"
crypto-browserify "^3.11.0"
domain-browser "^1.1.1"
events "^3.0.0"
https-browserify "^1.0.0"
os-browserify "^0.3.0"
path-browserify "0.0.1"
process "^0.11.10"
punycode "^1.2.4"
querystring-es3 "^0.2.0"
readable-stream "^2.3.3"
stream-browserify "^2.0.1"
stream-http "^2.7.2"
string_decoder "^1.0.0"
timers-browserify "^2.0.4"
tty-browserify "0.0.0"
url "^0.11.0"
util "^0.11.0"
vm-browserify "^1.0.1"
node-modules-regexp@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40"
@ -10556,7 +10487,7 @@ optionator@^0.9.1:
type-check "^0.4.0"
word-wrap "^1.2.3"
os-browserify@0.3.0, os-browserify@^0.3.0:
os-browserify@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=
@ -10863,11 +10794,6 @@ pascalcase@^0.1.1:
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
path-browserify@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
path-browserify@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
@ -11051,13 +10977,6 @@ platform@1.3.6:
resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7"
integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==
pnp-webpack-plugin@1.6.4:
version "1.6.4"
resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149"
integrity sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==
dependencies:
ts-pnp "^1.1.6"
posix-character-classes@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
@ -11119,7 +11038,7 @@ process-nextick-args@~2.0.0:
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
process@0.11.10, process@^0.11.10:
process@0.11.10:
version "0.11.10"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
@ -11238,16 +11157,6 @@ pumpify@^1.3.3:
inherits "^2.0.3"
pump "^2.0.0"
punycode@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
punycode@^1.2.4:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
punycode@^2.1.0, punycode@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
@ -11280,16 +11189,11 @@ query-string@^6.13.8:
split-on-first "^1.0.0"
strict-uri-encode "^2.0.0"
querystring-es3@0.2.1, querystring-es3@^0.2.0:
querystring-es3@0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=
querystring@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
querystring@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd"
@ -11421,6 +11325,16 @@ react-router@5.2.1, react-router@^5.2.1:
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
react-server-dom-webpack@0.0.0-experimental-3c4c1c470-20211021:
version "0.0.0-experimental-3c4c1c470-20211021"
resolved "https://registry.yarnpkg.com/react-server-dom-webpack/-/react-server-dom-webpack-0.0.0-experimental-3c4c1c470-20211021.tgz#cdcaa2f19c8d820c1f4d31252319fb05e2de0e88"
integrity sha512-YyRlED5kR0C2aQ3IJ/8BR2TELt51RcDZhnUDKz+m/HU+Gb/qak0CZkG0A8Zxffom9VI6HFkUj1dRFZqm0Lh9Pg==
dependencies:
acorn "^6.2.1"
loose-envify "^1.1.0"
neo-async "^2.6.1"
object-assign "^4.1.1"
react-style-singleton@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.1.1.tgz#ce7f90b67618be2b6b94902a30aaea152ce52e66"
@ -11557,7 +11471,7 @@ read@1, read@~1.0.1:
dependencies:
mute-stream "~0.0.4"
"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6:
"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.6, readable-stream@~2.3.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@ -11647,6 +11561,11 @@ regenerate@^1.4.2:
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
regenerator-runtime@0.13.4:
version "0.13.4"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz#e96bf612a3362d12bb69f7e8f74ffeab25c7ac91"
integrity sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==
regenerator-runtime@^0.13.4:
version "0.13.9"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
@ -12454,14 +12373,6 @@ stream-browserify@3.0.0:
inherits "~2.0.4"
readable-stream "^3.5.0"
stream-browserify@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b"
integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==
dependencies:
inherits "~2.0.1"
readable-stream "^2.0.2"
stream-each@^1.1.0:
version "1.2.3"
resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae"
@ -12480,17 +12391,6 @@ stream-http@3.1.1:
readable-stream "^3.6.0"
xtend "^4.0.2"
stream-http@^2.7.2:
version "2.8.3"
resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc"
integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==
dependencies:
builtin-status-codes "^3.0.0"
inherits "^2.0.1"
readable-stream "^2.3.6"
to-arraybuffer "^1.0.0"
xtend "^4.0.0"
stream-parser@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/stream-parser/-/stream-parser-0.3.1.tgz#1618548694420021a1182ff0af1911c129761773"
@ -12586,7 +12486,7 @@ string.prototype.trimstart@^1.0.4:
call-bind "^1.0.2"
define-properties "^1.1.3"
string_decoder@1.3.0, string_decoder@^1.0.0, string_decoder@^1.1.1:
string_decoder@1.3.0, string_decoder@^1.1.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
@ -12609,12 +12509,12 @@ stringify-object@^3.3.0:
is-obj "^1.0.1"
is-regexp "^1.0.0"
strip-ansi@6.0.0, strip-ansi@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==
strip-ansi@6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.0"
ansi-regex "^5.0.1"
strip-ansi@^3.0.0, strip-ansi@^3.0.1:
version "3.0.1"
@ -12637,6 +12537,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
dependencies:
ansi-regex "^4.1.0"
strip-ansi@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==
dependencies:
ansi-regex "^5.0.0"
strip-bom@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
@ -12702,10 +12609,10 @@ strong-log-transformer@^2.0.0, strong-log-transformer@^2.1.0:
minimist "^1.2.0"
through "^2.3.4"
styled-jsx@4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-4.0.1.tgz#ae3f716eacc0792f7050389de88add6d5245b9e9"
integrity sha512-Gcb49/dRB1k8B4hdK8vhW27Rlb2zujCk1fISrizCcToIs+55B4vmUM0N9Gi4nnVfFZWe55jRdWpAqH1ldAKWvQ==
styled-jsx@5.0.0-beta.3:
version "5.0.0-beta.3"
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.0-beta.3.tgz#400d16179b5dff10d5954ab8be27a9a1b7780dd2"
integrity sha512-HtDDGSFPvmjHIqWf9n8Oo54tAoY/DTplvlyOH2+YOtD80Sp31Ap8ffSmxhgk5EkUoJ7xepdXMGT650mSffWuRA==
dependencies:
"@babel/plugin-syntax-jsx" "7.14.5"
"@babel/types" "7.15.0"
@ -12858,7 +12765,7 @@ terminal-link@^2.0.0:
ansi-escapes "^4.2.1"
supports-hyperlinks "^2.0.0"
terser-webpack-plugin@^5.1.4:
terser-webpack-plugin@^5.2.4:
version "5.2.4"
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.2.4.tgz#ad1be7639b1cbe3ea49fab995cbe7224b31747a1"
integrity sha512-E2CkNMN+1cho04YpdANyRrn8CyN4yMy+WdFKZIySFZrGXZxJwJP6PMNGGc/Mcr6qygQHUUqRxnAPmi0M9f00XA==
@ -12945,7 +12852,7 @@ through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6:
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
timers-browserify@2.0.12, timers-browserify@^2.0.4:
timers-browserify@2.0.12:
version "2.0.12"
resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee"
integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==
@ -12974,11 +12881,6 @@ tmpl@1.0.x:
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==
to-arraybuffer@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
@ -13095,11 +12997,6 @@ ts-node@^10.2.1:
make-error "^1.1.1"
yn "3.1.1"
ts-pnp@^1.1.6:
version "1.2.0"
resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92"
integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==
tsconfig-paths@^3.11.0, tsconfig-paths@^3.9.0:
version "3.11.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz#954c1fe973da6339c78e06b03ce2e48810b65f36"
@ -13135,11 +13032,6 @@ tsutils@^3.21.0:
dependencies:
tslib "^1.8.1"
tty-browserify@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=
tty-browserify@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811"
@ -13427,14 +13319,6 @@ urix@^0.1.0:
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
url@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=
dependencies:
punycode "1.3.2"
querystring "0.2.0"
use-callback-ref@^1.2.3:
version "1.2.5"
resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.2.5.tgz#6115ed242cfbaed5915499c0a9842ca2912f38a5"
@ -13472,13 +13356,6 @@ util-promisify@^2.1.0:
dependencies:
object.getownpropertydescriptors "^2.0.3"
util@0.10.3:
version "0.10.3"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk=
dependencies:
inherits "2.0.1"
util@0.12.4, util@^0.12.0:
version "0.12.4"
resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253"
@ -13491,13 +13368,6 @@ util@0.12.4, util@^0.12.0:
safe-buffer "^5.1.2"
which-typed-array "^1.1.2"
util@^0.11.0:
version "0.11.1"
resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61"
integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==
dependencies:
inherits "2.0.3"
uuid@^3.0.1, uuid@^3.3.2:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
@ -13546,7 +13416,7 @@ verror@1.10.0:
core-util-is "1.0.2"
extsprintf "^1.2.0"
vm-browserify@1.1.2, vm-browserify@^1.0.1:
vm-browserify@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
@ -13592,6 +13462,11 @@ wcwidth@^1.0.0:
dependencies:
defaults "^1.0.3"
web-streams-polyfill@3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.0.3.tgz#f49e487eedeca47a207c1aee41ee5578f884b42f"
integrity sha512-d2H/t0eqRNM4w2WvmTdoeIvzAUSpK7JmATB8Nr2lb7nQ9BTIJVjbQ/TRFVEh2gUH1HwclPdoPtfMoFfetXaZnA==
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
@ -13875,7 +13750,7 @@ workbox-sw@6.3.0:
resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-6.3.0.tgz#8b805a3ac5339e8df0e6ba36c491d9cd01aa9f3f"
integrity sha512-xwrXRBzw5jwJ7VdAQkTSNTbNZ4S6VhXtbZZ0vY6XKNQARO5nuGphNdif+hJFIejHUgtV6ESpQnixPj5hYB2jKQ==
workbox-webpack-plugin@^6.2.4:
workbox-webpack-plugin@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-6.3.0.tgz#86c6c6fcb5fc151e6b4ccd8d7041d3da6a4a4271"
integrity sha512-3l5H8h7O2eUgTAISQoglDe4VJDDYTZaDnkRY0FY2+eFOXA+fZoWuDSmLiMnA0uYqPC4NWVTZwP549E0dWgiWjw==
@ -13887,7 +13762,7 @@ workbox-webpack-plugin@^6.2.4:
webpack-sources "^1.4.3"
workbox-build "6.3.0"
workbox-window@6.3.0, workbox-window@^6.2.4:
workbox-window@6.3.0, workbox-window@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-6.3.0.tgz#f669a0715363c35e519d1b6d919e04da7ce369ea"
integrity sha512-CFP84assX9srH/TOx4OD8z4EBPO/Cq4WKdV2YLcJIFJmVTS/cB63XKeidKl2KJk8qOOLVIKnaO7BLmb0MxGFtA==
@ -14018,7 +13893,7 @@ xmlchars@^2.2.0:
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.1:
xtend@^4.0.2, xtend@~4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==