[refactor] ShapeUtils (#206)
* Starts refactor * tests passing, got it * Fix next
This commit is contained in:
parent
8d6fe119a5
commit
2e6c33342d
87 changed files with 1517 additions and 1554 deletions
|
@ -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} />)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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} />
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
}
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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>'
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -2,4 +2,4 @@ export * from './components'
|
|||
export * from './types'
|
||||
export * from './utils'
|
||||
export * from './inputs'
|
||||
export * from './shapes'
|
||||
export * from './shape-utils'
|
||||
|
|
230
packages/core/src/shape-utils/TLShapeUtil.spec.tsx
Normal file
230
packages/core/src/shape-utils/TLShapeUtil.spec.tsx
Normal 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}
|
||||
/>
|
||||
)
|
||||
})
|
||||
})
|
90
packages/core/src/shape-utils/TLShapeUtil.tsx
Normal file
90
packages/core/src/shape-utils/TLShapeUtil.tsx
Normal 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))
|
||||
}
|
||||
}
|
8
packages/core/src/shape-utils/index.ts
Normal file
8
packages/core/src/shape-utils/index.ts
Normal 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'
|
|
@ -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}
|
||||
/>
|
||||
)
|
||||
})
|
||||
})
|
|
@ -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>
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export * from './createShape'
|
|
@ -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: {
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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',
|
||||
]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export * from './components/tldraw'
|
||||
export * from './types'
|
||||
export * from './shape'
|
||||
export * from './shape-utils'
|
||||
export { TLDrawState } from './state'
|
||||
|
|
157
packages/tldraw/src/shape-utils/TLDrawShapeUtil.tsx
Normal file
157
packages/tldraw/src/shape-utils/TLDrawShapeUtil.tsx
Normal 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
|
||||
}
|
1
packages/tldraw/src/shape-utils/about-shape-utils.md
Normal file
1
packages/tldraw/src/shape-utils/about-shape-utils.md
Normal file
|
@ -0,0 +1 @@
|
|||
# Shape Utils
|
|
@ -1,4 +1,4 @@
|
|||
import { Arrow } from './arrow'
|
||||
import { Arrow } from '../'
|
||||
|
||||
describe('Arrow shape', () => {
|
||||
it('Creates a shape', () => {
|
|
@ -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 */
|
|
@ -1,4 +1,4 @@
|
|||
import { Draw } from './draw'
|
||||
import { Draw } from '../'
|
||||
|
||||
describe('Draw shape', () => {
|
||||
it('Creates a shape', () => {
|
|
@ -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'
|
|
@ -1,4 +1,4 @@
|
|||
import { Ellipse } from './ellipse'
|
||||
import { Ellipse } from '../'
|
||||
|
||||
describe('Ellipse shape', () => {
|
||||
it('Creates a shape', () => {
|
|
@ -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 */
|
|
@ -1,4 +1,4 @@
|
|||
import { Group } from './group'
|
||||
import { Group } from '../'
|
||||
|
||||
describe('Group shape', () => {
|
||||
it('Creates a shape', () => {
|
|
@ -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))',
|
51
packages/tldraw/src/shape-utils/index.ts
Normal file
51
packages/tldraw/src/shape-utils/index.ts
Normal 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>
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { Rectangle } from './rectangle'
|
||||
import { Rectangle } from '../'
|
||||
|
||||
describe('Rectangle shape', () => {
|
||||
it('Creates a shape', () => {
|
|
@ -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 */
|
|
@ -1,4 +1,4 @@
|
|||
import { Sticky } from './sticky'
|
||||
import { Sticky } from '../'
|
||||
|
||||
describe('Post-It shape', () => {
|
||||
it('Creates a shape', () => {
|
|
@ -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',
|
|
@ -1,4 +1,4 @@
|
|||
import { Text } from './text'
|
||||
import { Text } from '../'
|
||||
|
||||
describe('Text shape', () => {
|
||||
it('Creates a shape', () => {
|
|
@ -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%',
|
|
@ -1,2 +0,0 @@
|
|||
export * from './shape-utils'
|
||||
export * from './shape-styles'
|
|
@ -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>
|
||||
}
|
|
@ -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'
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 =
|
||||
| {
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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
|
@ -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
497
yarn.lock
|
@ -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==
|
||||
|
|
Loading…
Reference in a new issue