kopia lustrzana https://github.com/Tldraw/Tldraw
Improves types and imports!
rodzic
edc29dfbcf
commit
8c02e0df84
|
@ -18,7 +18,7 @@
|
|||
"main": "./dist/cjs/index.js",
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"scripts": {
|
||||
"dev": "node scripts/dev & tsc --watch --incremental --emitDeclarationOnly --outDir dist/types",
|
||||
"dev": "node scripts/dev & tsc --watch --incremental --emitDeclarationOnly --declarationMap --outDir dist/types",
|
||||
"build": "yarn clean && node scripts/build && tsc --emitDeclarationOnly --outDir dist/types",
|
||||
"lint": "eslint src/ --ext .ts,.tsx",
|
||||
"clean": "rm -rf dist",
|
||||
|
@ -26,6 +26,7 @@
|
|||
"docs": "typedoc --entryPoints src/index.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^15.0.1",
|
||||
"@types/react": "^17.0.16",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { TLBinding } from '@tldraw/core/src/types'
|
||||
import type { TLBinding } from '~types'
|
||||
|
||||
interface BindingProps {
|
||||
point: number[]
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react'
|
||||
import type { TLBounds } from '../../../types'
|
||||
import { Utils } from '../../../utils'
|
||||
import { useBoundsEvents } from '../../hooks/useBoundsEvents'
|
||||
import type { TLBounds } from '~types'
|
||||
import { Utils } from '~utils'
|
||||
import { useBoundsEvents } from '~hooks'
|
||||
|
||||
interface BoundsBgProps {
|
||||
bounds: TLBounds
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { TLBoundsEdge, TLBoundsCorner, TLBounds } from '../../../types'
|
||||
import { Utils } from '../../../utils'
|
||||
import { TLBoundsEdge, TLBoundsCorner, TLBounds } from '~types'
|
||||
import { Utils } from '~utils'
|
||||
import { CenterHandle } from './center-handle'
|
||||
import { RotateHandle } from './rotate-handle'
|
||||
import { CornerHandle } from './corner-handle'
|
|
@ -0,0 +1,20 @@
|
|||
import * as React from 'react'
|
||||
import type { TLBounds } from '~types'
|
||||
|
||||
export interface CenterHandleProps {
|
||||
bounds: TLBounds
|
||||
isLocked: boolean
|
||||
}
|
||||
|
||||
export const CenterHandle = React.memo(({ bounds, isLocked }: CenterHandleProps): JSX.Element => {
|
||||
return (
|
||||
<rect
|
||||
className={isLocked ? 'tl-bounds-center tl-dashed' : 'tl-bounds-center'}
|
||||
x={-1}
|
||||
y={-1}
|
||||
width={bounds.width + 2}
|
||||
height={bounds.height + 2}
|
||||
pointerEvents="none"
|
||||
/>
|
||||
)
|
||||
})
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { useBoundsHandleEvents } from '../../hooks'
|
||||
import { TLBoundsCorner, TLBounds } from '../../../types'
|
||||
import { useBoundsHandleEvents } from '~hooks'
|
||||
import { TLBoundsCorner, TLBounds } from '~types'
|
||||
|
||||
const cornerBgClassnames = {
|
||||
[TLBoundsCorner.TopLeft]: 'tl-transparent tl-cursor-nwse',
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { useBoundsHandleEvents } from '../../hooks'
|
||||
import { TLBoundsEdge, TLBounds } from '../../../types'
|
||||
import { useBoundsHandleEvents } from '~hooks'
|
||||
import { TLBoundsEdge, TLBounds } from '~types'
|
||||
|
||||
const edgeClassnames = {
|
||||
[TLBoundsEdge.Top]: 'tl-transparent tl-cursor-ns',
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { useBoundsHandleEvents } from '../../hooks'
|
||||
import type { TLBounds } from '../../../types'
|
||||
import { useBoundsHandleEvents } from '~hooks'
|
||||
import type { TLBounds } from '~types'
|
||||
|
||||
export const RotateHandle = React.memo(
|
||||
({ bounds, size }: { bounds: TLBounds; size: number }): JSX.Element => {
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from 'react'
|
||||
import type { TLBounds } from '../../../types'
|
||||
import type { TLBounds } from '~types'
|
||||
|
||||
export class BrushUpdater {
|
||||
ref = React.createRef<SVGRectElement>()
|
|
@ -1,5 +1,4 @@
|
|||
import * as React from 'react'
|
||||
import { TLBounds } from '../../../types'
|
||||
import { BrushUpdater } from './BrushUpdater'
|
||||
|
||||
export const brushUpdater = new BrushUpdater()
|
|
@ -1,13 +1,17 @@
|
|||
import * as React from 'react'
|
||||
import { ErrorBoundary } from 'react-error-boundary'
|
||||
import { useZoomEvents, useSafariFocusOutFix, useCanvasEvents, useCameraCss } from '../hooks'
|
||||
import {
|
||||
usePreventNavigation,
|
||||
useZoomEvents,
|
||||
useSafariFocusOutFix,
|
||||
useCanvasEvents,
|
||||
useCameraCss,
|
||||
} from '~hooks'
|
||||
import type { TLBinding, TLPage, TLPageState, TLShape } from '~types'
|
||||
import { ErrorFallback } from './error-fallback'
|
||||
import type { TLBinding, TLPage, TLPageState, TLShape } from '../../types'
|
||||
|
||||
import { Brush } from './brush'
|
||||
import { Defs } from './defs'
|
||||
import { Page } from './page'
|
||||
import { usePreventNavigation } from '../hooks/usePreventNavigation'
|
||||
|
||||
function resetError() {
|
||||
void null
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from 'react'
|
||||
import { useHandleEvents } from '../../hooks'
|
||||
import { useHandleEvents } from '~hooks'
|
||||
|
||||
interface HandleProps {
|
||||
id: string
|
|
@ -0,0 +1,35 @@
|
|||
import * as React from 'react'
|
||||
import { Vec } from '~utils'
|
||||
import type { TLShape } from '~types'
|
||||
import { useTLContext } from '~hooks'
|
||||
import { Handle } from './handle'
|
||||
|
||||
interface HandlesProps {
|
||||
shape: TLShape
|
||||
zoom: number
|
||||
}
|
||||
|
||||
const toAngle = 180 / Math.PI
|
||||
|
||||
export const Handles = React.memo(({ shape, zoom }: HandlesProps): JSX.Element | null => {
|
||||
const { shapeUtils } = useTLContext()
|
||||
|
||||
const center = shapeUtils[shape.type].getCenter(shape)
|
||||
|
||||
if (shape.handles === undefined) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<g transform={`rotate(${(shape.rotation || 0) * toAngle},${center})`}>
|
||||
{Object.values(shape.handles).map((handle) => (
|
||||
<Handle
|
||||
key={shape.id + '_' + handle.id}
|
||||
id={handle.id}
|
||||
point={Vec.add(handle.point, shape.point)}
|
||||
zoom={zoom}
|
||||
/>
|
||||
))}
|
||||
</g>
|
||||
)
|
||||
})
|
|
@ -0,0 +1,2 @@
|
|||
export * from './renderer'
|
||||
export { brushUpdater } from './brush'
|
|
@ -1,8 +1,7 @@
|
|||
import * as React from 'react'
|
||||
import { useShapeTree } from '../hooks/useShapeTree'
|
||||
import type { IShapeTreeNode, TLBinding, TLPage, TLPageState, TLShape } from '../../types'
|
||||
import type { IShapeTreeNode, TLBinding, TLPage, TLPageState, TLShape } from '~types'
|
||||
import { Shape as ShapeComponent } from './shape'
|
||||
import { useHandles, useRenderOnResize, useTLContext } from '../hooks'
|
||||
import { useShapeTree, useHandles, useRenderOnResize, useTLContext } from '../hooks'
|
||||
import { Bounds } from './bounds'
|
||||
import { BoundsBg } from './bounds/bounds-bg'
|
||||
import { useSelection } from '../hooks'
|
|
@ -10,9 +10,8 @@ import type {
|
|||
TLBounds,
|
||||
TLBinding,
|
||||
} from '../types'
|
||||
import { Canvas } from './components/canvas'
|
||||
import { useTLTheme } from './hooks/useStyle'
|
||||
import { TLContext } from './hooks/useTLContext'
|
||||
import { Canvas } from '../components/canvas'
|
||||
import { useTLTheme, TLContext } from '../hooks'
|
||||
|
||||
export interface RendererProps<T extends TLShape>
|
||||
extends Partial<TLSettings>,
|
|
@ -1,6 +1,6 @@
|
|||
import { useTLContext } from '../../hooks'
|
||||
import { useTLContext } from '~hooks'
|
||||
import * as React from 'react'
|
||||
import type { TLShapeUtil, TLRenderInfo, TLShape } from '../../../types'
|
||||
import type { TLShapeUtil, TLRenderInfo, TLShape } from '~types'
|
||||
|
||||
interface EditingShapeProps<T extends TLShape> extends TLRenderInfo {
|
||||
shape: T
|
||||
|
@ -16,13 +16,7 @@ export function EditingTextShape({
|
|||
isCurrentParent,
|
||||
}: EditingShapeProps<TLShape>) {
|
||||
const {
|
||||
callbacks: {
|
||||
onTextChange,
|
||||
onTextBlur,
|
||||
onTextFocus,
|
||||
onTextKeyDown,
|
||||
onTextKeyUp,
|
||||
},
|
||||
callbacks: { onTextChange, onTextBlur, onTextFocus, onTextKeyDown, onTextKeyUp },
|
||||
} = useTLContext()
|
||||
|
||||
const ref = React.useRef<HTMLElement>(null)
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from 'react'
|
||||
import type { TLShapeUtil, TLRenderInfo, TLShape } from '../../../types'
|
||||
import type { TLShapeUtil, TLRenderInfo, TLShape } from '~types'
|
||||
|
||||
interface RenderedShapeProps<T extends TLShape> extends TLRenderInfo {
|
||||
shape: T
|
|
@ -1,17 +1,11 @@
|
|||
import * as React from 'react'
|
||||
import { useShapeEvents, useTLContext } from '../../hooks'
|
||||
import type { IShapeTreeNode } from '../../../types'
|
||||
import { useShapeEvents, useTLContext } from '~hooks'
|
||||
import type { IShapeTreeNode } from '~types'
|
||||
import { RenderedShape } from './rendered-shape'
|
||||
import { EditingTextShape } from './editing-text-shape'
|
||||
|
||||
export const Shape = React.memo(
|
||||
({
|
||||
shape,
|
||||
isEditing,
|
||||
isBinding,
|
||||
isDarkMode,
|
||||
isCurrentParent,
|
||||
}: IShapeTreeNode) => {
|
||||
({ shape, isEditing, isBinding, isDarkMode, isCurrentParent }: IShapeTreeNode) => {
|
||||
const { shapeUtils } = useTLContext()
|
||||
const events = useShapeEvents(shape.id, isCurrentParent)
|
||||
const utils = shapeUtils[shape.type]
|
||||
|
@ -22,11 +16,7 @@ export const Shape = React.memo(
|
|||
|
||||
return (
|
||||
<g
|
||||
className={
|
||||
isCurrentParent
|
||||
? 'tl-shape-group tl-current-parent'
|
||||
: 'tl-shape-group'
|
||||
}
|
||||
className={isCurrentParent ? 'tl-shape-group tl-current-parent' : 'tl-shape-group'}
|
||||
id={shape.id}
|
||||
transform={transform}
|
||||
{...events}
|
|
@ -12,3 +12,5 @@ export * from './useRenderOnResize'
|
|||
export * from './useSelection'
|
||||
export * from './useHandleEvents'
|
||||
export * from './useHandles'
|
||||
export * from './usePreventNavigation'
|
||||
export * from './useBoundsEvents'
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from 'react'
|
||||
import { inputs } from '../../inputs'
|
||||
import { inputs } from '~inputs'
|
||||
import { useTLContext } from './useTLContext'
|
||||
|
||||
export function useBoundsEvents() {
|
|
@ -1,11 +1,9 @@
|
|||
import * as React from 'react'
|
||||
import { inputs } from '../../inputs'
|
||||
import type { TLBoundsEdge, TLBoundsCorner } from '../../types'
|
||||
import { inputs } from '~inputs'
|
||||
import type { TLBoundsEdge, TLBoundsCorner } from '~types'
|
||||
import { useTLContext } from './useTLContext'
|
||||
|
||||
export function useBoundsHandleEvents(
|
||||
id: TLBoundsCorner | TLBoundsEdge | 'rotate'
|
||||
) {
|
||||
export function useBoundsHandleEvents(id: TLBoundsCorner | TLBoundsEdge | 'rotate') {
|
||||
const { callbacks } = useTLContext()
|
||||
|
||||
const onPointerDown = React.useCallback(
|
|
@ -1,15 +1,12 @@
|
|||
import * as React from 'react'
|
||||
import type { TLPageState } from '../../types'
|
||||
import type { TLPageState } from '~types'
|
||||
|
||||
export function useCameraCss(pageState: TLPageState) {
|
||||
const rGroup = React.useRef<SVGGElement>(null)
|
||||
|
||||
// Update the tl-zoom CSS variable when the zoom changes
|
||||
React.useEffect(() => {
|
||||
document.documentElement.style.setProperty(
|
||||
'--tl-zoom',
|
||||
pageState.camera.zoom.toString()
|
||||
)
|
||||
document.documentElement.style.setProperty('--tl-zoom', pageState.camera.zoom.toString())
|
||||
}, [pageState.camera.zoom])
|
||||
|
||||
// Update the group's position when the camera moves or zooms
|
||||
|
@ -18,10 +15,7 @@ export function useCameraCss(pageState: TLPageState) {
|
|||
zoom,
|
||||
point: [x = 0, y = 0],
|
||||
} = pageState.camera
|
||||
rGroup.current?.setAttribute(
|
||||
'transform',
|
||||
`scale(${zoom}) translate(${x} ${y})`
|
||||
)
|
||||
rGroup.current?.setAttribute('transform', `scale(${zoom}) translate(${x} ${y})`)
|
||||
}, [pageState.camera])
|
||||
|
||||
return rGroup
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { useTLContext } from './useTLContext'
|
||||
import { inputs } from '../../inputs'
|
||||
import { inputs } from '~inputs'
|
||||
|
||||
export function useCanvasEvents() {
|
||||
const { callbacks } = useTLContext()
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from 'react'
|
||||
import { inputs } from '../../inputs'
|
||||
import { inputs } from '~inputs'
|
||||
import { useTLContext } from './useTLContext'
|
||||
|
||||
export function useHandleEvents(id: string) {
|
|
@ -1,4 +1,4 @@
|
|||
import type { TLBinding, TLPage, TLPageState, TLShape } from '../../types'
|
||||
import type { TLBinding, TLPage, TLPageState, TLShape } from '~types'
|
||||
|
||||
export function useHandles<T extends TLShape>(page: TLPage<T, TLBinding>, pageState: TLPageState) {
|
||||
const { selectedIds } = pageState
|
|
@ -1,8 +1,8 @@
|
|||
import * as React from 'react'
|
||||
import Utils from '../../utils'
|
||||
import Utils from '~utils'
|
||||
|
||||
export function useRenderOnResize() {
|
||||
const forceUpdate = React.useReducer(x => x + 1, 0)[1]
|
||||
const forceUpdate = React.useReducer((x) => x + 1, 0)[1]
|
||||
|
||||
React.useEffect(() => {
|
||||
const debouncedUpdate = Utils.debounce(forceUpdate, 96)
|
|
@ -1,5 +1,5 @@
|
|||
import Utils from '../../utils'
|
||||
import { useEffect } from 'react'
|
||||
import Utils from '~utils'
|
||||
import { useTLContext } from './useTLContext'
|
||||
|
||||
// Send event on iOS when a user presses the "Done" key while editing a text element.
|
|
@ -1,6 +1,6 @@
|
|||
import type { TLPage, TLPageState, TLShape, TLBounds, TLShapeUtils, TLBinding } from '../../types'
|
||||
import Utils from '../../utils'
|
||||
import { useTLContext } from '../hooks/useTLContext'
|
||||
import type { TLPage, TLPageState, TLShape, TLBounds, TLShapeUtils, TLBinding } from '~types'
|
||||
import Utils from '~utils'
|
||||
import { useTLContext } from '~hooks'
|
||||
|
||||
function canvasToScreen(point: number[], camera: TLPageState['camera']): number[] {
|
||||
return [(point[0] + camera.point[0]) * camera.zoom, (point[1] + camera.point[1]) * camera.zoom]
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react'
|
||||
import { inputs } from '../../inputs'
|
||||
import { inputs } from '~inputs'
|
||||
import { useTLContext } from './useTLContext'
|
||||
import { Utils } from '../../utils'
|
||||
import { Utils } from '~utils'
|
||||
|
||||
export function useShapeEvents(id: string, disable = false) {
|
||||
const { rPageState, rScreenBounds, callbacks } = useTLContext()
|
|
@ -7,8 +7,8 @@ import type {
|
|||
TLShapeUtils,
|
||||
TLCallbacks,
|
||||
TLBinding,
|
||||
} from '../../types'
|
||||
import Utils, { Vec } from '../../utils'
|
||||
} from '~types'
|
||||
import { Utils, Vec } from '~utils'
|
||||
|
||||
function addToShapeTree<T extends TLShape>(
|
||||
shape: TLShape,
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from 'react'
|
||||
import type { TLTheme } from '../../types'
|
||||
import type { TLTheme } from '~types'
|
||||
|
||||
const styles = new Map<string, HTMLStyleElement>()
|
||||
|
|
@ -1,11 +1,5 @@
|
|||
import * as React from 'react'
|
||||
import type {
|
||||
TLCallbacks,
|
||||
TLShape,
|
||||
TLBounds,
|
||||
TLPageState,
|
||||
TLShapeUtils,
|
||||
} from '../../types'
|
||||
import type { TLCallbacks, TLShape, TLBounds, TLPageState, TLShapeUtils } from '~types'
|
||||
|
||||
export interface TLContextType {
|
||||
callbacks: Partial<TLCallbacks>
|
|
@ -1,9 +1,9 @@
|
|||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { useRef } from 'react'
|
||||
import { useTLContext } from './useTLContext'
|
||||
import { Vec } from '../../utils'
|
||||
import { Vec } from '~utils'
|
||||
import { useWheel, usePinch } from 'react-use-gesture'
|
||||
import { inputs } from '../../inputs'
|
||||
import { inputs } from '~inputs'
|
||||
|
||||
// Capture zoom gestures (pinches, wheels and pans)
|
||||
export function useZoomEvents() {
|
||||
|
@ -54,8 +54,10 @@ export function useZoomEvents() {
|
|||
rOriginPoint.current = origin
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const [distanceDelta] = Vec.sub(rPinchDa.current!, da)
|
||||
if (!rPinchDa.current) throw Error('No pinch direction!')
|
||||
if (!rOriginPoint.current) throw Error('No origin point!')
|
||||
|
||||
const [distanceDelta] = Vec.sub(rPinchDa.current, da)
|
||||
|
||||
const info = inputs.pinch(rPinchPoint.current, origin)
|
||||
|
||||
|
@ -63,7 +65,7 @@ export function useZoomEvents() {
|
|||
{
|
||||
...info,
|
||||
point: origin,
|
||||
origin: rOriginPoint.current!,
|
||||
origin: rOriginPoint.current,
|
||||
delta: [...info.delta, distanceDelta],
|
||||
},
|
||||
e as React.WheelEvent<Element> | WheelEvent | React.TouchEvent<Element> | TouchEvent
|
|
@ -1,4 +1,4 @@
|
|||
export * from './renderer'
|
||||
export * from './components'
|
||||
export * from './types'
|
||||
export * from './utils'
|
||||
export * from './inputs'
|
||||
|
|
|
@ -11,10 +11,7 @@ class Inputs {
|
|||
|
||||
pointerUpTime = 0
|
||||
|
||||
touchStart<T extends string>(
|
||||
e: TouchEvent | React.TouchEvent,
|
||||
target: T
|
||||
): TLPointerInfo<T> {
|
||||
touchStart<T extends string>(e: TouchEvent | React.TouchEvent, target: T): TLPointerInfo<T> {
|
||||
const { shiftKey, ctrlKey, metaKey, altKey } = e
|
||||
e.preventDefault()
|
||||
|
||||
|
@ -38,10 +35,7 @@ class Inputs {
|
|||
return info
|
||||
}
|
||||
|
||||
touchMove<T extends string>(
|
||||
e: TouchEvent | React.TouchEvent,
|
||||
target: T
|
||||
): TLPointerInfo<T> {
|
||||
touchMove<T extends string>(e: TouchEvent | React.TouchEvent, target: T): TLPointerInfo<T> {
|
||||
const { shiftKey, ctrlKey, metaKey, altKey } = e
|
||||
e.preventDefault()
|
||||
|
||||
|
@ -72,10 +66,7 @@ class Inputs {
|
|||
return info
|
||||
}
|
||||
|
||||
pointerDown<T extends string>(
|
||||
e: PointerEvent | React.PointerEvent,
|
||||
target: T
|
||||
): TLPointerInfo<T> {
|
||||
pointerDown<T extends string>(e: PointerEvent | React.PointerEvent, target: T): TLPointerInfo<T> {
|
||||
const { shiftKey, ctrlKey, metaKey, altKey } = e
|
||||
|
||||
const point = Inputs.getPoint(e)
|
||||
|
@ -124,10 +115,7 @@ class Inputs {
|
|||
return info
|
||||
}
|
||||
|
||||
pointerMove<T extends string>(
|
||||
e: PointerEvent | React.PointerEvent,
|
||||
target: T
|
||||
): TLPointerInfo<T> {
|
||||
pointerMove<T extends string>(e: PointerEvent | React.PointerEvent, target: T): TLPointerInfo<T> {
|
||||
const { shiftKey, ctrlKey, metaKey, altKey } = e
|
||||
|
||||
const prev = this.pointer
|
||||
|
@ -155,10 +143,7 @@ class Inputs {
|
|||
return info
|
||||
}
|
||||
|
||||
pointerUp<T extends string>(
|
||||
e: PointerEvent | React.PointerEvent,
|
||||
target: T
|
||||
): TLPointerInfo<T> {
|
||||
pointerUp<T extends string>(e: PointerEvent | React.PointerEvent, target: T): TLPointerInfo<T> {
|
||||
const { shiftKey, ctrlKey, metaKey, altKey } = e
|
||||
|
||||
const prev = this.pointer
|
||||
|
@ -236,22 +221,12 @@ class Inputs {
|
|||
return info
|
||||
}
|
||||
|
||||
canAccept = (_pointerId: PointerEvent['pointerId']): boolean => {
|
||||
return true
|
||||
// return (
|
||||
// this.activePointerId === undefined || this.activePointerId === pointerId
|
||||
// )
|
||||
}
|
||||
|
||||
isDoubleClick() {
|
||||
if (!this.pointer) return false
|
||||
|
||||
const { origin, point } = this.pointer
|
||||
|
||||
return (
|
||||
Date.now() - this.pointerUpTime < DOUBLE_CLICK_DURATION &&
|
||||
Vec.dist(origin, point) < 4
|
||||
)
|
||||
return Date.now() - this.pointerUpTime < DOUBLE_CLICK_DURATION && Vec.dist(origin, point) < 4
|
||||
}
|
||||
|
||||
clear() {
|
||||
|
@ -334,9 +309,7 @@ class Inputs {
|
|||
return [Number(e.clientX.toPrecision(5)), Number(e.clientY.toPrecision(5))]
|
||||
}
|
||||
|
||||
static getPressure(
|
||||
e: PointerEvent | React.PointerEvent | Touch | React.Touch | WheelEvent
|
||||
) {
|
||||
static getPressure(e: PointerEvent | React.PointerEvent | Touch | React.Touch | WheelEvent) {
|
||||
return 'pressure' in e ? Number(e.pressure.toPrecision(5)) || 0.5 : 0.5
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import type { TLBounds } from '../../../types'
|
||||
|
||||
export interface CenterHandleProps {
|
||||
bounds: TLBounds
|
||||
isLocked: boolean
|
||||
}
|
||||
|
||||
export const CenterHandle = React.memo(
|
||||
({ bounds, isLocked }: CenterHandleProps): JSX.Element => {
|
||||
return (
|
||||
<rect
|
||||
className={isLocked ? 'tl-bounds-center tl-dashed' : 'tl-bounds-center'}
|
||||
x={-1}
|
||||
y={-1}
|
||||
width={bounds.width + 2}
|
||||
height={bounds.height + 2}
|
||||
pointerEvents="none"
|
||||
/>
|
||||
)
|
||||
}
|
||||
)
|
|
@ -1,37 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import { Vec } from '../../../utils'
|
||||
import type { TLShape } from '../../../types'
|
||||
import { useTLContext } from '../../hooks'
|
||||
import { Handle } from './handle'
|
||||
|
||||
interface HandlesProps {
|
||||
shape: TLShape
|
||||
zoom: number
|
||||
}
|
||||
|
||||
const toAngle = 180 / Math.PI
|
||||
|
||||
export const Handles = React.memo(
|
||||
({ shape, zoom }: HandlesProps): JSX.Element | null => {
|
||||
const { shapeUtils } = useTLContext()
|
||||
|
||||
const center = shapeUtils[shape.type].getCenter(shape)
|
||||
|
||||
if (shape.handles === undefined) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<g transform={`rotate(${(shape.rotation || 0) * toAngle},${center})`}>
|
||||
{Object.values(shape.handles).map((handle) => (
|
||||
<Handle
|
||||
key={shape.id + '_' + handle.id}
|
||||
id={handle.id}
|
||||
point={Vec.add(handle.point, shape.point)}
|
||||
zoom={zoom}
|
||||
/>
|
||||
))}
|
||||
</g>
|
||||
)
|
||||
}
|
||||
)
|
|
@ -1,2 +0,0 @@
|
|||
export * from './renderer'
|
||||
export { brushUpdater } from './components/brush'
|
|
@ -1,12 +1,7 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import * as React from 'react'
|
||||
import {
|
||||
TLShapeUtil,
|
||||
TLShape,
|
||||
TLBounds,
|
||||
TLRenderInfo,
|
||||
TLTransformInfo,
|
||||
} from '../types'
|
||||
import Utils, { Intersect } from '../utils'
|
||||
import { TLShapeUtil, TLShape, TLBounds, TLRenderInfo, TLTransformInfo } from '~types'
|
||||
import Utils, { Intersect } from '~utils'
|
||||
|
||||
export class ExampleShape extends TLShapeUtil<TLShape> {
|
||||
type = 'shape-type'
|
||||
|
@ -49,9 +44,7 @@ export class ExampleShape extends TLShapeUtil<TLShape> {
|
|||
}
|
||||
|
||||
getRotatedBounds(shape: TLShape) {
|
||||
return Utils.getBoundsFromPoints(
|
||||
Utils.getRotatedCorners(this.getBounds(shape), shape.rotation)
|
||||
)
|
||||
return Utils.getBoundsFromPoints(Utils.getRotatedCorners(this.getBounds(shape), shape.rotation))
|
||||
}
|
||||
|
||||
getCenter(shape: TLShape): number[] {
|
||||
|
@ -63,10 +56,7 @@ export class ExampleShape extends TLShapeUtil<TLShape> {
|
|||
}
|
||||
|
||||
hitTestBounds(shape: TLShape, bounds: TLBounds) {
|
||||
const rotatedCorners = Utils.getRotatedCorners(
|
||||
this.getBounds(shape),
|
||||
shape.rotation
|
||||
)
|
||||
const rotatedCorners = Utils.getRotatedCorners(this.getBounds(shape), shape.rotation)
|
||||
|
||||
return (
|
||||
rotatedCorners.every((point) => Utils.pointInBounds(point, bounds)) ||
|
||||
|
@ -74,19 +64,11 @@ export class ExampleShape extends TLShapeUtil<TLShape> {
|
|||
)
|
||||
}
|
||||
|
||||
transform(
|
||||
shape: TLShape,
|
||||
bounds: TLBounds,
|
||||
_info: TLTransformInfo<TLShape>
|
||||
): TLShape {
|
||||
transform(shape: TLShape, bounds: TLBounds, _info: TLTransformInfo<TLShape>): TLShape {
|
||||
return { ...shape, point: [bounds.minX, bounds.minY] }
|
||||
}
|
||||
|
||||
transformSingle(
|
||||
shape: TLShape,
|
||||
bounds: TLBounds,
|
||||
info: TLTransformInfo<TLShape>
|
||||
): TLShape {
|
||||
transformSingle(shape: TLShape, bounds: TLBounds, info: TLTransformInfo<TLShape>): TLShape {
|
||||
return this.transform(shape, bounds, info)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,257 +0,0 @@
|
|||
/// <reference types="react" />
|
||||
export interface TLPage<T extends TLShape> {
|
||||
id: string;
|
||||
name?: string;
|
||||
childIndex?: number;
|
||||
shapes: Record<string, T>;
|
||||
bindings: Record<string, TLBinding>;
|
||||
backgroundColor?: string;
|
||||
}
|
||||
export interface TLPageState {
|
||||
id: string;
|
||||
brush?: TLBounds;
|
||||
pointedId?: string;
|
||||
hoveredId?: string;
|
||||
editingId?: string;
|
||||
bindingId?: string;
|
||||
boundsRotation?: number;
|
||||
currentParentId?: string;
|
||||
selectedIds: string[];
|
||||
camera: {
|
||||
point: number[];
|
||||
zoom: number;
|
||||
};
|
||||
}
|
||||
export interface TLHandle {
|
||||
id: string;
|
||||
index: number;
|
||||
point: number[];
|
||||
canBind?: boolean;
|
||||
bindingId?: string;
|
||||
}
|
||||
export interface TLShape {
|
||||
id: string;
|
||||
type: string;
|
||||
parentId: string;
|
||||
childIndex: number;
|
||||
name: string;
|
||||
point: number[];
|
||||
rotation?: number;
|
||||
children?: string[];
|
||||
handles?: Record<string, TLHandle>;
|
||||
isLocked?: boolean;
|
||||
isHidden?: boolean;
|
||||
isEditing?: boolean;
|
||||
isGenerated?: boolean;
|
||||
isAspectRatioLocked?: boolean;
|
||||
}
|
||||
export declare type TLShapeUtils<T extends TLShape> = Record<string, TLShapeUtil<T>>;
|
||||
export interface TLRenderInfo<T extends SVGElement | HTMLElement = any> {
|
||||
isEditing: boolean;
|
||||
isBinding: boolean;
|
||||
isDarkMode: boolean;
|
||||
isCurrentParent: boolean;
|
||||
ref?: React.RefObject<T>;
|
||||
onTextChange?: TLCallbacks['onTextChange'];
|
||||
onTextBlur?: TLCallbacks['onTextBlur'];
|
||||
onTextFocus?: TLCallbacks['onTextFocus'];
|
||||
onTextKeyDown?: TLCallbacks['onTextKeyDown'];
|
||||
onTextKeyUp?: TLCallbacks['onTextKeyUp'];
|
||||
}
|
||||
export interface TLTool {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
export interface TLBinding {
|
||||
id: string;
|
||||
type: string;
|
||||
toId: string;
|
||||
fromId: string;
|
||||
}
|
||||
export interface TLSettings {
|
||||
isDebugMode: boolean;
|
||||
isDarkMode: boolean;
|
||||
isPenMode: boolean;
|
||||
}
|
||||
export interface TLTheme {
|
||||
brushFill?: string;
|
||||
brushStroke?: string;
|
||||
selectFill?: string;
|
||||
selectStroke?: string;
|
||||
background?: string;
|
||||
foreground?: string;
|
||||
}
|
||||
export declare type TLWheelEventHandler = (info: TLPointerInfo<string>, e: React.WheelEvent<Element> | WheelEvent) => void;
|
||||
export declare type TLPinchEventHandler = (info: TLPointerInfo<string>, e: React.WheelEvent<Element> | WheelEvent | React.TouchEvent<Element> | TouchEvent) => void;
|
||||
export declare type TLPointerEventHandler = (info: TLPointerInfo<string>, e: React.PointerEvent) => void;
|
||||
export declare type TLCanvasEventHandler = (info: TLPointerInfo<'canvas'>, e: React.PointerEvent) => void;
|
||||
export declare type TLBoundsEventHandler = (info: TLPointerInfo<'bounds'>, e: React.PointerEvent) => void;
|
||||
export declare type TLBoundsHandleEventHandler = (info: TLPointerInfo<TLBoundsCorner | TLBoundsEdge | 'rotate'>, e: React.PointerEvent) => void;
|
||||
export interface TLCallbacks {
|
||||
onChange: (ids: string[]) => void;
|
||||
onPinchStart: TLPinchEventHandler;
|
||||
onPinchEnd: TLPinchEventHandler;
|
||||
onPinch: TLPinchEventHandler;
|
||||
onPan: TLWheelEventHandler;
|
||||
onZoom: TLWheelEventHandler;
|
||||
onPointerMove: TLPointerEventHandler;
|
||||
onPointerUp: TLPointerEventHandler;
|
||||
onPointerDown: TLPointerEventHandler;
|
||||
onPointCanvas: TLCanvasEventHandler;
|
||||
onDoubleClickCanvas: TLCanvasEventHandler;
|
||||
onRightPointCanvas: TLCanvasEventHandler;
|
||||
onDragCanvas: TLCanvasEventHandler;
|
||||
onReleaseCanvas: TLCanvasEventHandler;
|
||||
onPointShape: TLPointerEventHandler;
|
||||
onDoubleClickShape: TLPointerEventHandler;
|
||||
onRightPointShape: TLPointerEventHandler;
|
||||
onDragShape: TLPointerEventHandler;
|
||||
onHoverShape: TLPointerEventHandler;
|
||||
onUnhoverShape: TLPointerEventHandler;
|
||||
onReleaseShape: TLPointerEventHandler;
|
||||
onPointBounds: TLBoundsEventHandler;
|
||||
onDoubleClickBounds: TLBoundsEventHandler;
|
||||
onRightPointBounds: TLBoundsEventHandler;
|
||||
onDragBounds: TLBoundsEventHandler;
|
||||
onHoverBounds: TLBoundsEventHandler;
|
||||
onUnhoverBounds: TLBoundsEventHandler;
|
||||
onReleaseBounds: TLBoundsEventHandler;
|
||||
onPointBoundsHandle: TLBoundsHandleEventHandler;
|
||||
onDoubleClickBoundsHandle: TLBoundsHandleEventHandler;
|
||||
onRightPointBoundsHandle: TLBoundsHandleEventHandler;
|
||||
onDragBoundsHandle: TLBoundsHandleEventHandler;
|
||||
onHoverBoundsHandle: TLBoundsHandleEventHandler;
|
||||
onUnhoverBoundsHandle: TLBoundsHandleEventHandler;
|
||||
onReleaseBoundsHandle: TLBoundsHandleEventHandler;
|
||||
onPointHandle: TLPointerEventHandler;
|
||||
onDoubleClickHandle: TLPointerEventHandler;
|
||||
onRightPointHandle: TLPointerEventHandler;
|
||||
onDragHandle: TLPointerEventHandler;
|
||||
onHoverHandle: TLPointerEventHandler;
|
||||
onUnhoverHandle: TLPointerEventHandler;
|
||||
onReleaseHandle: TLPointerEventHandler;
|
||||
onTextChange: (id: string, text: string) => void;
|
||||
onTextBlur: (id: string) => void;
|
||||
onTextFocus: (id: string) => void;
|
||||
onTextKeyDown: (id: string, key: string) => void;
|
||||
onTextKeyUp: (id: string, key: string) => void;
|
||||
onBlurEditingShape: () => void;
|
||||
onError: (error: Error) => void;
|
||||
}
|
||||
export interface TLBounds {
|
||||
minX: number;
|
||||
minY: number;
|
||||
maxX: number;
|
||||
maxY: number;
|
||||
width: number;
|
||||
height: number;
|
||||
rotation?: number;
|
||||
}
|
||||
export declare type TLIntersection = {
|
||||
didIntersect: boolean;
|
||||
message: string;
|
||||
points: number[][];
|
||||
};
|
||||
export declare enum TLBoundsEdge {
|
||||
Top = "top_edge",
|
||||
Right = "right_edge",
|
||||
Bottom = "bottom_edge",
|
||||
Left = "left_edge"
|
||||
}
|
||||
export declare enum TLBoundsCorner {
|
||||
TopLeft = "top_left_corner",
|
||||
TopRight = "top_right_corner",
|
||||
BottomRight = "bottom_right_corner",
|
||||
BottomLeft = "bottom_left_corner"
|
||||
}
|
||||
export interface TLPointerInfo<T extends string = string> {
|
||||
target: T;
|
||||
pointerId: number;
|
||||
origin: number[];
|
||||
point: number[];
|
||||
delta: number[];
|
||||
pressure: number;
|
||||
shiftKey: boolean;
|
||||
ctrlKey: boolean;
|
||||
metaKey: boolean;
|
||||
altKey: boolean;
|
||||
}
|
||||
export interface TLKeyboardInfo {
|
||||
origin: number[];
|
||||
point: number[];
|
||||
key: string;
|
||||
keys: string[];
|
||||
shiftKey: boolean;
|
||||
ctrlKey: boolean;
|
||||
metaKey: boolean;
|
||||
altKey: boolean;
|
||||
}
|
||||
export interface TLTransformInfo<T extends TLShape> {
|
||||
type: TLBoundsEdge | TLBoundsCorner;
|
||||
initialShape: T;
|
||||
scaleX: number;
|
||||
scaleY: number;
|
||||
transformOrigin: number[];
|
||||
}
|
||||
export interface TLBezierCurveSegment {
|
||||
start: number[];
|
||||
tangentStart: number[];
|
||||
normalStart: number[];
|
||||
pressureStart: number;
|
||||
end: number[];
|
||||
tangentEnd: number[];
|
||||
normalEnd: number[];
|
||||
pressureEnd: number;
|
||||
}
|
||||
export declare abstract class TLShapeUtil<T extends TLShape> {
|
||||
boundsCache: WeakMap<TLShape, TLBounds>;
|
||||
isEditableText: boolean;
|
||||
isAspectRatioLocked: boolean;
|
||||
canEdit: boolean;
|
||||
canBind: boolean;
|
||||
abstract type: T['type'];
|
||||
abstract defaultProps: T;
|
||||
abstract render(shape: T, info: TLRenderInfo): JSX.Element;
|
||||
abstract renderIndicator(shape: T): JSX.Element | null;
|
||||
abstract getBounds(shape: T): TLBounds;
|
||||
abstract getRotatedBounds(shape: T): TLBounds;
|
||||
abstract hitTest(shape: T, point: number[]): boolean;
|
||||
abstract hitTestBounds(shape: T, bounds: TLBounds): boolean;
|
||||
abstract transform(shape: T, bounds: TLBounds, info: TLTransformInfo<T>): Partial<T>;
|
||||
transformSingle(shape: T, bounds: TLBounds, info: TLTransformInfo<T>): Partial<T>;
|
||||
shouldRender(_prev: T, _next: T): boolean;
|
||||
shouldDelete(_shape: T): boolean;
|
||||
getCenter(shape: T): number[];
|
||||
getBindingPoint(shape: T, point: number[], origin: number[], direction: number[], padding: number, anywhere: boolean): {
|
||||
point: number[];
|
||||
distance: number;
|
||||
} | undefined;
|
||||
create(props: Partial<T>): T;
|
||||
mutate(shape: T, props: Partial<T>): T;
|
||||
updateChildren<K extends TLShape>(_shape: T, _children: K[]): Partial<K>[] | void;
|
||||
onChildrenChange(_shape: T, _children: TLShape[]): Partial<T> | void;
|
||||
onBindingChange(_shape: T, _binding: TLBinding, _target: TLShape, _targetBounds: TLBounds, _center: number[]): Partial<T> | void;
|
||||
onHandleChange(_shape: T, _handle: Partial<T['handles']>, _info: Partial<TLPointerInfo>): Partial<T> | void;
|
||||
onRightPointHandle(_shape: T, _handle: Partial<T['handles']>, _info: Partial<TLPointerInfo>): Partial<T> | void;
|
||||
onDoubleClickHandle(_shape: T, _handle: Partial<T['handles']>, _info: Partial<TLPointerInfo>): Partial<T> | void;
|
||||
onSessionComplete(_shape: T): Partial<T> | void;
|
||||
onBoundsReset(_shape: T): Partial<T> | void;
|
||||
onStyleChange(_shape: T): Partial<T> | void;
|
||||
}
|
||||
export interface IShapeTreeNode {
|
||||
shape: TLShape;
|
||||
children?: IShapeTreeNode[];
|
||||
isEditing: boolean;
|
||||
isBinding: boolean;
|
||||
isDarkMode: boolean;
|
||||
isCurrentParent: boolean;
|
||||
}
|
||||
export declare type MappedByType<T extends {
|
||||
type: string;
|
||||
}> = {
|
||||
[P in T['type']]: T extends any ? (P extends T['type'] ? T : never) : never;
|
||||
};
|
||||
export declare type RequiredKeys<T> = {
|
||||
[K in keyof T]-?: Record<string, unknown> extends Pick<T, K> ? never : K;
|
||||
}[keyof T];
|
||||
export {};
|
|
@ -1,3 +1,5 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* --------------------- Primary -------------------- */
|
||||
|
||||
export interface TLPage<T extends TLShape, B extends TLBinding> {
|
||||
|
@ -316,57 +318,57 @@ export abstract class TLShapeUtil<T extends TLShape> {
|
|||
return { ...shape, ...props }
|
||||
}
|
||||
|
||||
updateChildren<K extends TLShape>(_shape: T, _children: K[]): Partial<K>[] | void {
|
||||
updateChildren<K extends TLShape>(shape: T, children: K[]): Partial<K>[] | void {
|
||||
return
|
||||
}
|
||||
|
||||
onChildrenChange(_shape: T, _children: TLShape[]): Partial<T> | void {
|
||||
onChildrenChange(shape: T, children: TLShape[]): Partial<T> | void {
|
||||
return
|
||||
}
|
||||
|
||||
onBindingChange(
|
||||
_shape: T,
|
||||
_binding: TLBinding,
|
||||
_target: TLShape,
|
||||
_targetBounds: TLBounds,
|
||||
_center: number[]
|
||||
shape: T,
|
||||
binding: TLBinding,
|
||||
target: TLShape,
|
||||
targetBounds: TLBounds,
|
||||
center: number[]
|
||||
): Partial<T> | void {
|
||||
return undefined
|
||||
}
|
||||
|
||||
onHandleChange(
|
||||
_shape: T,
|
||||
_handle: Partial<T['handles']>,
|
||||
_info: Partial<TLPointerInfo>
|
||||
shape: T,
|
||||
handle: Partial<T['handles']>,
|
||||
info: Partial<TLPointerInfo>
|
||||
): Partial<T> | void {
|
||||
return
|
||||
}
|
||||
|
||||
onRightPointHandle(
|
||||
_shape: T,
|
||||
_handle: Partial<T['handles']>,
|
||||
_info: Partial<TLPointerInfo>
|
||||
shape: T,
|
||||
handle: Partial<T['handles']>,
|
||||
info: Partial<TLPointerInfo>
|
||||
): Partial<T> | void {
|
||||
return
|
||||
}
|
||||
|
||||
onDoubleClickHandle(
|
||||
_shape: T,
|
||||
_handle: Partial<T['handles']>,
|
||||
_info: Partial<TLPointerInfo>
|
||||
shape: T,
|
||||
handle: Partial<T['handles']>,
|
||||
info: Partial<TLPointerInfo>
|
||||
): Partial<T> | void {
|
||||
return
|
||||
}
|
||||
|
||||
onSessionComplete(_shape: T): Partial<T> | void {
|
||||
onSessionComplete(shape: T): Partial<T> | void {
|
||||
return
|
||||
}
|
||||
|
||||
onBoundsReset(_shape: T): Partial<T> | void {
|
||||
onBoundsReset(shape: T): Partial<T> | void {
|
||||
return
|
||||
}
|
||||
|
||||
onStyleChange(_shape: T): Partial<T> | void {
|
||||
onStyleChange(shape: T): Partial<T> | void {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -394,5 +396,3 @@ export type MappedByType<T extends { type: string }> = {
|
|||
export type RequiredKeys<T> = {
|
||||
[K in keyof T]-?: Record<string, unknown> extends Pick<T, K> ? never : K
|
||||
}[keyof T]
|
||||
|
||||
export {}
|
|
@ -1,5 +1,4 @@
|
|||
import { Utils } from './utils'
|
||||
|
||||
export { Intersect } from './intersect'
|
||||
export { Utils } from './utils'
|
||||
export { Svg } from './svg'
|
||||
|
|
|
@ -559,13 +559,21 @@ export class Intersect {
|
|||
// Get an intersection between an ellipse and a second ellipse.
|
||||
// Adapted from https://gist.github.com/drawable/92792f59b6ff8869d8b1
|
||||
ellipse(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_c1: number[],
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_rx1: number,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_ry1: number,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_r1: number,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_c2: number[],
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_rx2: number,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_ry2: number,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_r2: number
|
||||
): TLIntersection {
|
||||
// TODO
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
*/
|
||||
if (!String.prototype.replaceAll) {
|
||||
// @ts-ignore
|
||||
String.prototype.replaceAll = function(str: string, newStr: string) {
|
||||
String.prototype.replaceAll = function (str: string, newStr: string) {
|
||||
// If a regex pattern
|
||||
if (Object.prototype.toString.call(str).toLowerCase() === '[object regexp]') {
|
||||
return this.replace(str, newStr)
|
||||
|
@ -19,5 +19,3 @@ if (!String.prototype.replaceAll) {
|
|||
return this.replace(new RegExp(str, 'g'), newStr)
|
||||
}
|
||||
}
|
||||
|
||||
export {}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable @typescript-eslint/no-extra-semi */
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable no-redeclare */
|
||||
|
@ -1634,16 +1635,13 @@ left past the initial left edge) then swap points on that axis.
|
|||
let inThrottle: boolean
|
||||
let lastResult: ReturnType<T>
|
||||
|
||||
return function (this: any): ReturnType<T> {
|
||||
const args = arguments
|
||||
const context = this
|
||||
|
||||
return function (this: any, ...args: any[]): ReturnType<T> {
|
||||
if (!inThrottle) {
|
||||
inThrottle = true
|
||||
|
||||
setTimeout(() => (inThrottle = false), limit)
|
||||
|
||||
lastResult = func.apply(context, args as any)
|
||||
lastResult = func.apply(this, ...args)
|
||||
}
|
||||
|
||||
return lastResult
|
||||
|
|
|
@ -1,12 +1,42 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "**/*.test.ts", "dist"],
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"declaration": true,
|
||||
"esModuleInterop": true,
|
||||
"experimentalDecorators": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"importsNotUsedAsValues": "error",
|
||||
"incremental": true,
|
||||
"importHelpers": true,
|
||||
"moduleResolution": "node",
|
||||
"noEmit": false,
|
||||
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,
|
||||
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
|
||||
"noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
|
||||
"noUnusedLocals": false /* Report errors on unused locals. */,
|
||||
"noUnusedParameters": false /* Report errors on unused parameters. */,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": false,
|
||||
"strictFunctionTypes": true /* Enable strict checking of function types. */,
|
||||
"strictNullChecks": true /* Enable strict null checks. */,
|
||||
"target": "es5",
|
||||
"typeRoots": ["node_modules/@types", "node_modules/jest"],
|
||||
"types": ["node", "jest"],
|
||||
//
|
||||
"rootDir": "src",
|
||||
"jsx": "preserve",
|
||||
"lib": ["dom", "esnext"],
|
||||
"module": "esnext",
|
||||
"outDir": "./dist/types"
|
||||
"outDir": "./dist/types",
|
||||
"baseUrl": "src",
|
||||
"paths": {
|
||||
"~utils": ["./utils/index.ts"],
|
||||
"~types": ["./types.ts"],
|
||||
"~inputs": ["./inputs.ts"],
|
||||
"~hooks": ["./hooks/index.ts"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
import * as React from 'react'
|
||||
import { ColorStyle, DashStyle, SizeStyle, TLDrawShapeType, TLDrawState } from '@tldraw/tldraw'
|
||||
import { TLDraw, TLDrawDocument } from '@tldraw/tldraw'
|
||||
import {
|
||||
TLDraw,
|
||||
TLDrawDocument,
|
||||
ColorStyle,
|
||||
DashStyle,
|
||||
SizeStyle,
|
||||
TLDrawShapeType,
|
||||
TLDrawState,
|
||||
} from '@tldraw/tldraw'
|
||||
import { usePersistence } from '../hooks/usePersistence'
|
||||
|
||||
const initialDoc: TLDrawDocument = {
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
"types": "./dist/types/index.d.ts",
|
||||
"typings": "./dist/types/index.d.ts",
|
||||
"scripts": {
|
||||
"dev": "node scripts/dev & tsc --watch --incremental --emitDeclarationOnly --outDir dist/types",
|
||||
"dev": "node scripts/dev & tsc --watch --incremental --emitDeclarationOnly --declarationMap --outDir dist/types",
|
||||
"build": "yarn clean && node scripts/build && tsc --emitDeclarationOnly --outDir dist/types",
|
||||
"lint": "eslint src/ --ext .ts,.tsx",
|
||||
"clean": "rm -rf dist",
|
||||
|
@ -28,6 +28,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@tldraw/core": "*",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^15.0.1",
|
||||
"@types/react": "^17.0.16",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import * as React from 'react'
|
||||
import { Utils } from '@tldraw/core'
|
||||
import * as RadixContextMenu from '@radix-ui/react-context-menu'
|
||||
import styled from '../../styles'
|
||||
import { useTLDrawContext } from '../../hooks'
|
||||
import type { Data } from '../../state/state-types'
|
||||
import styled from '~styles'
|
||||
import { useTLDrawContext } from '~hooks'
|
||||
import type { Data } from '~types'
|
||||
import { Kbd } from '../kbd'
|
||||
import {
|
||||
IconWrapper,
|
||||
|
@ -17,7 +17,7 @@ import {
|
|||
ContextMenuRoot,
|
||||
MenuContent,
|
||||
} from '../shared'
|
||||
import { AlignType, DistributeType, StretchType } from '../../types'
|
||||
import { AlignType, DistributeType, StretchType } from '~types'
|
||||
import {
|
||||
ChevronRightIcon,
|
||||
AlignBottomIcon,
|
||||
|
@ -47,176 +47,169 @@ const isDebugModeSelector = (s: Data) => {
|
|||
}
|
||||
|
||||
const hasGroupSelectedSelector = (s: Data) => {
|
||||
return s.pageState.selectedIds.some(
|
||||
(id) => s.page.shapes[id].children !== undefined
|
||||
)
|
||||
return s.pageState.selectedIds.some((id) => s.page.shapes[id].children !== undefined)
|
||||
}
|
||||
|
||||
interface ContextMenuProps {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export const ContextMenu = React.memo(
|
||||
({ children }: ContextMenuProps): JSX.Element => {
|
||||
const { tlstate, useSelector } = useTLDrawContext()
|
||||
const hasSelection = useSelector(has1SelectedIdsSelector)
|
||||
const hasTwoOrMore = useSelector(has2SelectedIdsSelector)
|
||||
const hasThreeOrMore = useSelector(has3SelectedIdsSelector)
|
||||
const isDebugMode = useSelector(isDebugModeSelector)
|
||||
const hasGroupSelected = useSelector(hasGroupSelectedSelector)
|
||||
export const ContextMenu = React.memo(({ children }: ContextMenuProps): JSX.Element => {
|
||||
const { tlstate, useSelector } = useTLDrawContext()
|
||||
const hasSelection = useSelector(has1SelectedIdsSelector)
|
||||
const hasTwoOrMore = useSelector(has2SelectedIdsSelector)
|
||||
const hasThreeOrMore = useSelector(has3SelectedIdsSelector)
|
||||
const isDebugMode = useSelector(isDebugModeSelector)
|
||||
const hasGroupSelected = useSelector(hasGroupSelectedSelector)
|
||||
|
||||
const rContent = React.useRef<HTMLDivElement>(null)
|
||||
const rContent = React.useRef<HTMLDivElement>(null)
|
||||
|
||||
const handleFlipHorizontal = React.useCallback(() => {
|
||||
tlstate.flipHorizontal()
|
||||
}, [tlstate])
|
||||
const handleFlipHorizontal = React.useCallback(() => {
|
||||
tlstate.flipHorizontal()
|
||||
}, [tlstate])
|
||||
|
||||
const handleFlipVertical = React.useCallback(() => {
|
||||
tlstate.flipVertical()
|
||||
}, [tlstate])
|
||||
const handleFlipVertical = React.useCallback(() => {
|
||||
tlstate.flipVertical()
|
||||
}, [tlstate])
|
||||
|
||||
const handleDuplicate = React.useCallback(() => {
|
||||
tlstate.duplicate()
|
||||
}, [tlstate])
|
||||
const handleDuplicate = React.useCallback(() => {
|
||||
tlstate.duplicate()
|
||||
}, [tlstate])
|
||||
|
||||
const handleGroup = React.useCallback(() => {
|
||||
tlstate.group()
|
||||
}, [tlstate])
|
||||
const handleGroup = React.useCallback(() => {
|
||||
tlstate.group()
|
||||
}, [tlstate])
|
||||
|
||||
const handleMoveToBack = React.useCallback(() => {
|
||||
tlstate.moveToBack()
|
||||
}, [tlstate])
|
||||
const handleMoveToBack = React.useCallback(() => {
|
||||
tlstate.moveToBack()
|
||||
}, [tlstate])
|
||||
|
||||
const handleMoveBackward = React.useCallback(() => {
|
||||
tlstate.moveBackward()
|
||||
}, [tlstate])
|
||||
const handleMoveBackward = React.useCallback(() => {
|
||||
tlstate.moveBackward()
|
||||
}, [tlstate])
|
||||
|
||||
const handleMoveForward = React.useCallback(() => {
|
||||
tlstate.moveForward()
|
||||
}, [tlstate])
|
||||
const handleMoveForward = React.useCallback(() => {
|
||||
tlstate.moveForward()
|
||||
}, [tlstate])
|
||||
|
||||
const handleMoveToFront = React.useCallback(() => {
|
||||
tlstate.moveToFront()
|
||||
}, [tlstate])
|
||||
const handleMoveToFront = React.useCallback(() => {
|
||||
tlstate.moveToFront()
|
||||
}, [tlstate])
|
||||
|
||||
const handleDelete = React.useCallback(() => {
|
||||
tlstate.delete()
|
||||
}, [tlstate])
|
||||
const handleDelete = React.useCallback(() => {
|
||||
tlstate.delete()
|
||||
}, [tlstate])
|
||||
|
||||
const handleCopyAsJson = React.useCallback(() => {
|
||||
tlstate.copyAsJson()
|
||||
}, [tlstate])
|
||||
const handleCopyAsJson = React.useCallback(() => {
|
||||
tlstate.copyAsJson()
|
||||
}, [tlstate])
|
||||
|
||||
const handleCopyAsSvg = React.useCallback(() => {
|
||||
tlstate.copyAsSvg()
|
||||
}, [tlstate])
|
||||
const handleCopyAsSvg = React.useCallback(() => {
|
||||
tlstate.copyAsSvg()
|
||||
}, [tlstate])
|
||||
|
||||
const handleUndo = React.useCallback(() => {
|
||||
tlstate.undo()
|
||||
}, [tlstate])
|
||||
const handleUndo = React.useCallback(() => {
|
||||
tlstate.undo()
|
||||
}, [tlstate])
|
||||
|
||||
const handleRedo = React.useCallback(() => {
|
||||
tlstate.redo()
|
||||
}, [tlstate])
|
||||
const handleRedo = React.useCallback(() => {
|
||||
tlstate.redo()
|
||||
}, [tlstate])
|
||||
|
||||
if (Utils.isMobile()) {
|
||||
return <>{children}</>
|
||||
}
|
||||
if (Utils.isMobile()) {
|
||||
return <>{children}</>
|
||||
}
|
||||
|
||||
return (
|
||||
<ContextMenuRoot>
|
||||
<RadixContextMenu.Trigger>{children}</RadixContextMenu.Trigger>
|
||||
<MenuContent as={RadixContextMenu.Content} ref={rContent}>
|
||||
{hasSelection ? (
|
||||
<>
|
||||
<ContextMenuButton onSelect={handleFlipHorizontal}>
|
||||
<span>Flip Horizontal</span>
|
||||
<Kbd variant="menu">⇧H</Kbd>
|
||||
return (
|
||||
<ContextMenuRoot>
|
||||
<RadixContextMenu.Trigger>{children}</RadixContextMenu.Trigger>
|
||||
<MenuContent as={RadixContextMenu.Content} ref={rContent}>
|
||||
{hasSelection ? (
|
||||
<>
|
||||
<ContextMenuButton onSelect={handleFlipHorizontal}>
|
||||
<span>Flip Horizontal</span>
|
||||
<Kbd variant="menu">⇧H</Kbd>
|
||||
</ContextMenuButton>
|
||||
<ContextMenuButton onSelect={handleFlipVertical}>
|
||||
<span>Flip Vertical</span>
|
||||
<Kbd variant="menu">⇧V</Kbd>
|
||||
</ContextMenuButton>
|
||||
<ContextMenuButton onSelect={handleDuplicate}>
|
||||
<span>Duplicate</span>
|
||||
<Kbd variant="menu">#D</Kbd>
|
||||
</ContextMenuButton>
|
||||
<ContextMenuDivider />
|
||||
{hasGroupSelected ||
|
||||
(hasTwoOrMore && (
|
||||
<>
|
||||
{hasGroupSelected && (
|
||||
<ContextMenuButton onSelect={handleGroup}>
|
||||
<span>Ungroup</span>
|
||||
<Kbd variant="menu">#⇧G</Kbd>
|
||||
</ContextMenuButton>
|
||||
)}
|
||||
{hasTwoOrMore && (
|
||||
<ContextMenuButton onSelect={handleGroup}>
|
||||
<span>Group</span>
|
||||
<Kbd variant="menu">#G</Kbd>
|
||||
</ContextMenuButton>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
<ContextMenuSubMenu label="Move">
|
||||
<ContextMenuButton onSelect={handleMoveToFront}>
|
||||
<span>To Front</span>
|
||||
<Kbd variant="menu"># ⇧ ]</Kbd>
|
||||
</ContextMenuButton>
|
||||
<ContextMenuButton onSelect={handleFlipVertical}>
|
||||
<span>Flip Vertical</span>
|
||||
<Kbd variant="menu">⇧V</Kbd>
|
||||
<ContextMenuButton onSelect={handleMoveForward}>
|
||||
<span>Forward</span>
|
||||
<Kbd variant="menu"># ]</Kbd>
|
||||
</ContextMenuButton>
|
||||
<ContextMenuButton onSelect={handleDuplicate}>
|
||||
<span>Duplicate</span>
|
||||
<Kbd variant="menu">#D</Kbd>
|
||||
<ContextMenuButton onSelect={handleMoveBackward}>
|
||||
<span>Backward</span>
|
||||
<Kbd variant="menu"># [</Kbd>
|
||||
</ContextMenuButton>
|
||||
<ContextMenuDivider />
|
||||
{hasGroupSelected ||
|
||||
(hasTwoOrMore && (
|
||||
<>
|
||||
{hasGroupSelected && (
|
||||
<ContextMenuButton onSelect={handleGroup}>
|
||||
<span>Ungroup</span>
|
||||
<Kbd variant="menu">#⇧G</Kbd>
|
||||
</ContextMenuButton>
|
||||
)}
|
||||
{hasTwoOrMore && (
|
||||
<ContextMenuButton onSelect={handleGroup}>
|
||||
<span>Group</span>
|
||||
<Kbd variant="menu">#G</Kbd>
|
||||
</ContextMenuButton>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
<ContextMenuSubMenu label="Move">
|
||||
<ContextMenuButton onSelect={handleMoveToFront}>
|
||||
<span>To Front</span>
|
||||
<Kbd variant="menu"># ⇧ ]</Kbd>
|
||||
</ContextMenuButton>
|
||||
<ContextMenuButton onSelect={handleMoveForward}>
|
||||
<span>Forward</span>
|
||||
<Kbd variant="menu"># ]</Kbd>
|
||||
</ContextMenuButton>
|
||||
<ContextMenuButton onSelect={handleMoveBackward}>
|
||||
<span>Backward</span>
|
||||
<Kbd variant="menu"># [</Kbd>
|
||||
</ContextMenuButton>
|
||||
<ContextMenuButton onSelect={handleMoveToBack}>
|
||||
<span>To Back</span>
|
||||
<Kbd variant="menu"># ⇧ [</Kbd>
|
||||
</ContextMenuButton>
|
||||
</ContextMenuSubMenu>
|
||||
{hasTwoOrMore && (
|
||||
<AlignDistributeSubMenu
|
||||
hasTwoOrMore={hasTwoOrMore}
|
||||
hasThreeOrMore={hasThreeOrMore}
|
||||
/>
|
||||
)}
|
||||
{/* <MoveToPageMenu /> */}
|
||||
{isDebugMode && (
|
||||
<ContextMenuButton onSelect={handleCopyAsJson}>
|
||||
<span>Copy Data</span>
|
||||
<Kbd variant="menu"># ⇧ C</Kbd>
|
||||
</ContextMenuButton>
|
||||
)}
|
||||
<ContextMenuButton onSelect={handleCopyAsSvg}>
|
||||
<span>Copy to SVG</span>
|
||||
<ContextMenuButton onSelect={handleMoveToBack}>
|
||||
<span>To Back</span>
|
||||
<Kbd variant="menu"># ⇧ [</Kbd>
|
||||
</ContextMenuButton>
|
||||
</ContextMenuSubMenu>
|
||||
{hasTwoOrMore && (
|
||||
<AlignDistributeSubMenu hasTwoOrMore={hasTwoOrMore} hasThreeOrMore={hasThreeOrMore} />
|
||||
)}
|
||||
{/* <MoveToPageMenu /> */}
|
||||
{isDebugMode && (
|
||||
<ContextMenuButton onSelect={handleCopyAsJson}>
|
||||
<span>Copy Data</span>
|
||||
<Kbd variant="menu"># ⇧ C</Kbd>
|
||||
</ContextMenuButton>
|
||||
<ContextMenuDivider />
|
||||
<ContextMenuButton onSelect={handleDelete}>
|
||||
<span>Delete</span>
|
||||
<Kbd variant="menu">⌫</Kbd>
|
||||
</ContextMenuButton>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<ContextMenuButton onSelect={handleUndo}>
|
||||
<span>Undo</span>
|
||||
<Kbd variant="menu"># Z</Kbd>
|
||||
</ContextMenuButton>
|
||||
<ContextMenuButton onSelect={handleRedo}>
|
||||
<span>Redo</span>
|
||||
<Kbd variant="menu"># ⇧ Z</Kbd>
|
||||
</ContextMenuButton>
|
||||
</>
|
||||
)}
|
||||
</MenuContent>
|
||||
</ContextMenuRoot>
|
||||
)
|
||||
}
|
||||
)
|
||||
)}
|
||||
<ContextMenuButton onSelect={handleCopyAsSvg}>
|
||||
<span>Copy to SVG</span>
|
||||
<Kbd variant="menu"># ⇧ C</Kbd>
|
||||
</ContextMenuButton>
|
||||
<ContextMenuDivider />
|
||||
<ContextMenuButton onSelect={handleDelete}>
|
||||
<span>Delete</span>
|
||||
<Kbd variant="menu">⌫</Kbd>
|
||||
</ContextMenuButton>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<ContextMenuButton onSelect={handleUndo}>
|
||||
<span>Undo</span>
|
||||
<Kbd variant="menu"># Z</Kbd>
|
||||
</ContextMenuButton>
|
||||
<ContextMenuButton onSelect={handleRedo}>
|
||||
<span>Redo</span>
|
||||
<Kbd variant="menu"># ⇧ Z</Kbd>
|
||||
</ContextMenuButton>
|
||||
</>
|
||||
)}
|
||||
</MenuContent>
|
||||
</ContextMenuRoot>
|
||||
)
|
||||
})
|
||||
|
||||
function AlignDistributeSubMenu({
|
||||
hasThreeOrMore,
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5 2.49538C12.2239 2.49538 12 2.71923 12 2.99538V5.49559H9.49979C9.22364 5.49559 8.99979 5.71945 8.99979 5.99559C8.99979 6.27173 9.22364 6.49559 9.49979 6.49559H12.5C12.7761 6.49559 13 6.27173 13 5.99559V2.99538C13 2.71923 12.7761 2.49538 12.5 2.49538Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.69698 2.04877C6.62345 1.89773 5.52991 2.09968 4.58113 2.62417C3.63236 3.14867 2.87973 3.9673 2.43667 4.95673C1.99361 5.94616 1.8841 7.05278 2.12465 8.10985C2.3652 9.16693 2.94278 10.1172 3.77036 10.8175C4.59794 11.5177 5.63069 11.9301 6.713 11.9924C7.79531 12.0547 8.86855 11.7635 9.77101 11.1628C10.6735 10.5621 11.3563 9.68441 11.7165 8.66191C11.8083 8.40146 11.6715 8.11593 11.4111 8.02417C11.1506 7.93241 10.8651 8.06916 10.7733 8.32961C10.4851 9.14762 9.93888 9.84981 9.21691 10.3304C8.49493 10.811 7.63632 11.0439 6.77046 10.994C5.9046 10.9442 5.07839 10.6143 4.41631 10.0541C3.75424 9.49386 3.29217 8.73363 3.09972 7.88796C2.90728 7.04229 2.99488 6.15698 3.34934 5.36542C3.7038 4.57387 4.30591 3.91895 5.06494 3.49935C5.82398 3.07974 6.69882 2.91819 7.55765 3.03902C8.41649 3.15985 9.21279 3.55653 9.82658 4.16928L9.83745 4.17981L12.1576 6.35996C12.3588 6.54906 12.6753 6.53921 12.8644 6.33797C13.0535 6.13673 13.0436 5.8203 12.8424 5.63121L10.5276 3.4561C9.76111 2.69329 8.76794 2.19945 7.69698 2.04877Z" fill="black"/>
|
||||
</svg>
|
Przed Szerokość: | Wysokość: | Rozmiar: 1.5 KiB |
|
@ -1,6 +0,0 @@
|
|||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 4.65555C2 4.37941 2.22386 4.15555 2.5 4.15555H12.2C12.4761 4.15555 12.7 4.37941 12.7 4.65555C12.7 4.93169 12.4761 5.15555 12.2 5.15555H2.5C2.22386 5.15555 2 4.93169 2 4.65555Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.27208 3C6.11885 3 5.97189 3.06087 5.86353 3.16923C5.75518 3.27758 5.6943 3.42454 5.6943 3.57778V4.15556H9.00542V3.57778C9.00542 3.42454 8.94454 3.27758 8.83619 3.16923C8.72783 3.06087 8.58087 3 8.42764 3H6.27208ZM10.0054 4.15556V3.57778C10.0054 3.15933 9.83919 2.75801 9.54329 2.46212C9.2474 2.16623 8.84609 2 8.42764 2H6.27208C5.85363 2 5.45232 2.16623 5.15642 2.46212C4.86053 2.75801 4.6943 3.15933 4.6943 3.57778V4.15556H3.57764C3.30149 4.15556 3.07764 4.37941 3.07764 4.65556V12.2C3.07764 12.6185 3.24387 13.0198 3.53976 13.3157C3.83565 13.6115 4.23696 13.7778 4.65541 13.7778H10.0443C10.4628 13.7778 10.8641 13.6115 11.16 13.3157C11.4559 13.0198 11.6221 12.6185 11.6221 12.2V4.65556C11.6221 4.37941 11.3982 4.15556 11.1221 4.15556H10.0054ZM4.07764 5.15556V12.2C4.07764 12.3532 4.13851 12.5002 4.24686 12.6086C4.35522 12.7169 4.50218 12.7778 4.65541 12.7778H10.0443C10.1975 12.7778 10.3445 12.7169 10.4529 12.6086C10.5612 12.5002 10.6221 12.3532 10.6221 12.2V5.15556H4.07764Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.27246 6.85001C6.5486 6.85001 6.77246 7.07386 6.77246 7.35001V10.5833C6.77246 10.8595 6.5486 11.0833 6.27246 11.0833C5.99632 11.0833 5.77246 10.8595 5.77246 10.5833V7.35001C5.77246 7.07386 5.99632 6.85001 6.27246 6.85001Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.42773 6.85001C8.70388 6.85001 8.92773 7.07386 8.92773 7.35001V10.5833C8.92773 10.8595 8.70388 11.0833 8.42773 11.0833C8.15159 11.0833 7.92773 10.8595 7.92773 10.5833V7.35001C7.92773 7.07386 8.15159 6.85001 8.42773 6.85001Z" fill="black"/>
|
||||
</svg>
|
Przed Szerokość: | Wysokość: | Rozmiar: 1.9 KiB |
|
@ -1,4 +0,0 @@
|
|||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.5 2.49538C2.77614 2.49538 3 2.71923 3 2.99538V5.49559H5.50021C5.77636 5.49559 6.00021 5.71945 6.00021 5.99559C6.00021 6.27173 5.77636 6.49559 5.50021 6.49559H2.5C2.22386 6.49559 2 6.27173 2 5.99559V2.99538C2 2.71923 2.22386 2.49538 2.5 2.49538Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.30302 2.04877C8.37655 1.89773 9.47009 2.09968 10.4189 2.62417C11.3676 3.14867 12.1203 3.9673 12.5633 4.95673C13.0064 5.94616 13.1159 7.05278 12.8753 8.10985C12.6348 9.16693 12.0572 10.1172 11.2296 10.8175C10.4021 11.5177 9.36931 11.9301 8.287 11.9924C7.20469 12.0547 6.13145 11.7635 5.22899 11.1628C4.32653 10.5621 3.64374 9.68441 3.2835 8.66191C3.19174 8.40146 3.32849 8.11593 3.58894 8.02417C3.84939 7.93241 4.13492 8.06916 4.22668 8.32961C4.51488 9.14762 5.06112 9.84981 5.78309 10.3304C6.50507 10.811 7.36368 11.0439 8.22954 10.994C9.0954 10.9442 9.92161 10.6143 10.5837 10.0541C11.2458 9.49386 11.7078 8.73363 11.9003 7.88796C12.0927 7.04229 12.0051 6.15698 11.6507 5.36542C11.2962 4.57387 10.6941 3.91895 9.93506 3.49935C9.17602 3.07974 8.30118 2.91819 7.44235 3.03902C6.58351 3.15985 5.78721 3.55653 5.17342 4.16928L5.16255 4.17981L2.84239 6.35996C2.64115 6.54906 2.32472 6.53921 2.13562 6.33797C1.94653 6.13673 1.95637 5.8203 2.15761 5.63121L4.47241 3.4561C5.23889 2.69329 6.23206 2.19945 7.30302 2.04877Z" fill="black"/>
|
||||
</svg>
|
Przed Szerokość: | Wysokość: | Rozmiar: 1.5 KiB |
|
@ -1,5 +1,5 @@
|
|||
import * as React from 'react'
|
||||
import styled from '../styles'
|
||||
import styled from '~styles'
|
||||
import { Utils } from '@tldraw/core'
|
||||
|
||||
export function commandKey(): string {
|
||||
|
|
|
@ -24,7 +24,7 @@ import {
|
|||
} from '@radix-ui/react-dropdown-menu'
|
||||
import { Root as RGRoot } from '@radix-ui/react-radio-group'
|
||||
import { CheckIcon, ChevronRightIcon } from '@radix-ui/react-icons'
|
||||
import styled from '../styles'
|
||||
import styled from '~styles'
|
||||
|
||||
export const breakpoints: any = { '@initial': 'mobile', '@sm': 'small' }
|
||||
|
||||
|
@ -408,12 +408,7 @@ export function MenuButton({
|
|||
children: React.ReactNode
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<RowButton
|
||||
bp={breakpoints}
|
||||
disabled={disabled}
|
||||
warn={warn}
|
||||
onSelect={onSelect}
|
||||
>
|
||||
<RowButton bp={breakpoints} disabled={disabled} warn={warn} onSelect={onSelect}>
|
||||
{children}
|
||||
</RowButton>
|
||||
)
|
||||
|
@ -499,12 +494,7 @@ export function DropdownMenuButton({
|
|||
children: React.ReactNode
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<DMItem
|
||||
as={RowButton}
|
||||
bp={breakpoints}
|
||||
disabled={disabled}
|
||||
onSelect={onSelect}
|
||||
>
|
||||
<DMItem as={RowButton} bp={breakpoints} disabled={disabled} onSelect={onSelect}>
|
||||
{children}
|
||||
</DMItem>
|
||||
)
|
||||
|
@ -522,12 +512,7 @@ export function DropdownMenuIconButton({
|
|||
disabled = false,
|
||||
}: DropdownMenuIconButtonProps): JSX.Element {
|
||||
return (
|
||||
<DMItem
|
||||
as={IconButton}
|
||||
bp={breakpoints}
|
||||
disabled={disabled}
|
||||
onSelect={onSelect}
|
||||
>
|
||||
<DMItem as={IconButton} bp={breakpoints} disabled={disabled} onSelect={onSelect}>
|
||||
{children}
|
||||
</DMItem>
|
||||
)
|
||||
|
@ -646,12 +631,7 @@ export function ContextMenuButton({
|
|||
children: React.ReactNode
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<RowButton
|
||||
as={CMItem}
|
||||
bp={breakpoints}
|
||||
disabled={disabled}
|
||||
onSelect={onSelect}
|
||||
>
|
||||
<RowButton as={CMItem} bp={breakpoints} disabled={disabled} onSelect={onSelect}>
|
||||
{children}
|
||||
</RowButton>
|
||||
)
|
||||
|
@ -667,12 +647,7 @@ export function ContextMenuIconButton({
|
|||
children: React.ReactNode
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<CMItem
|
||||
as={IconButton}
|
||||
bp={breakpoints}
|
||||
disabled={disabled}
|
||||
onSelect={onSelect}
|
||||
>
|
||||
<CMItem as={IconButton} bp={breakpoints} disabled={disabled} onSelect={onSelect}>
|
||||
{children}
|
||||
</CMItem>
|
||||
)
|
||||
|
|
|
@ -11,9 +11,9 @@ import {
|
|||
StretchHorizontallyIcon,
|
||||
StretchVerticallyIcon,
|
||||
} from '@radix-ui/react-icons'
|
||||
import { AlignType, DistributeType, StretchType } from '../../types'
|
||||
import { AlignType, DistributeType, StretchType } from '~types'
|
||||
import { useTLDrawContext } from '~hooks'
|
||||
import { breakpoints, ButtonsRow, IconButton } from '../shared'
|
||||
import { useTLDrawContext } from '../../hooks'
|
||||
|
||||
export interface AlignDistributeProps {
|
||||
hasTwoOrMore: boolean
|
||||
|
|
|
@ -2,56 +2,50 @@ import * as React from 'react'
|
|||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
|
||||
import { BoxIcon, StyleDropdownItem, StyleDropdownContent } from './shared'
|
||||
import { DropdownMenuIconTriggerButton } from '../shared'
|
||||
import { ColorStyle, strokes } from '../../shape'
|
||||
import { useTheme } from '../../hooks/useTheme'
|
||||
import { useTLDrawContext } from '../../hooks'
|
||||
import type { Data } from '../../state'
|
||||
import { strokes } from '~shape'
|
||||
import { useTheme, useTLDrawContext } from '~hooks'
|
||||
import type { Data, ColorStyle } from '~types'
|
||||
|
||||
const selectColor = (data: Data) => data.appState.selectedStyle.color
|
||||
|
||||
export const QuickColorSelect = React.memo(
|
||||
(): JSX.Element => {
|
||||
const { theme } = useTheme()
|
||||
const { tlstate, useSelector } = useTLDrawContext()
|
||||
const color = useSelector(selectColor)
|
||||
export const QuickColorSelect = React.memo((): JSX.Element => {
|
||||
const { theme } = useTheme()
|
||||
const { tlstate, useSelector } = useTLDrawContext()
|
||||
const color = useSelector(selectColor)
|
||||
|
||||
const handleColorChange = React.useCallback(
|
||||
(color) => {
|
||||
tlstate.style({ color: color as ColorStyle })
|
||||
},
|
||||
[tlstate]
|
||||
)
|
||||
const handleColorChange = React.useCallback(
|
||||
(color) => {
|
||||
tlstate.style({ color: color as ColorStyle })
|
||||
},
|
||||
[tlstate]
|
||||
)
|
||||
|
||||
return (
|
||||
<DropdownMenu.Root dir="ltr">
|
||||
<DropdownMenuIconTriggerButton label="Color">
|
||||
<BoxIcon
|
||||
fill={strokes[theme][color]}
|
||||
stroke={strokes[theme][color]}
|
||||
/>
|
||||
</DropdownMenuIconTriggerButton>
|
||||
<DropdownMenu.Content sideOffset={8}>
|
||||
<DropdownMenu.DropdownMenuRadioGroup
|
||||
value={color as string}
|
||||
onValueChange={handleColorChange}
|
||||
as={StyleDropdownContent}
|
||||
>
|
||||
{Object.keys(strokes[theme]).map((colorStyle: string) => (
|
||||
<DropdownMenu.DropdownMenuRadioItem
|
||||
as={StyleDropdownItem}
|
||||
key={colorStyle}
|
||||
title={colorStyle}
|
||||
value={colorStyle}
|
||||
>
|
||||
<BoxIcon
|
||||
fill={strokes[theme][colorStyle as ColorStyle]}
|
||||
stroke={strokes[theme][colorStyle as ColorStyle]}
|
||||
/>
|
||||
</DropdownMenu.DropdownMenuRadioItem>
|
||||
))}
|
||||
</DropdownMenu.DropdownMenuRadioGroup>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
)
|
||||
}
|
||||
)
|
||||
return (
|
||||
<DropdownMenu.Root dir="ltr">
|
||||
<DropdownMenuIconTriggerButton label="Color">
|
||||
<BoxIcon fill={strokes[theme][color]} stroke={strokes[theme][color]} />
|
||||
</DropdownMenuIconTriggerButton>
|
||||
<DropdownMenu.Content sideOffset={8}>
|
||||
<DropdownMenu.DropdownMenuRadioGroup
|
||||
value={color as string}
|
||||
onValueChange={handleColorChange}
|
||||
as={StyleDropdownContent}
|
||||
>
|
||||
{Object.keys(strokes[theme]).map((colorStyle: string) => (
|
||||
<DropdownMenu.DropdownMenuRadioItem
|
||||
as={StyleDropdownItem}
|
||||
key={colorStyle}
|
||||
title={colorStyle}
|
||||
value={colorStyle}
|
||||
>
|
||||
<BoxIcon
|
||||
fill={strokes[theme][colorStyle as ColorStyle]}
|
||||
stroke={strokes[theme][colorStyle as ColorStyle]}
|
||||
/>
|
||||
</DropdownMenu.DropdownMenuRadioItem>
|
||||
))}
|
||||
</DropdownMenu.DropdownMenuRadioGroup>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -9,9 +9,8 @@ import {
|
|||
StyleDropdownContent,
|
||||
StyleDropdownItem,
|
||||
} from './shared'
|
||||
import { DashStyle } from '../../shape'
|
||||
import { useTLDrawContext } from '../../hooks'
|
||||
import type { Data } from '../../state'
|
||||
import { useTLDrawContext } from '~hooks'
|
||||
import { DashStyle, Data } from '~types'
|
||||
|
||||
const dashes = {
|
||||
[DashStyle.Draw]: <DashDrawIcon />,
|
||||
|
@ -22,43 +21,39 @@ const dashes = {
|
|||
|
||||
const selectDash = (data: Data) => data.appState.selectedStyle.dash
|
||||
|
||||
export const QuickDashSelect = React.memo(
|
||||
(): JSX.Element => {
|
||||
const { tlstate, useSelector } = useTLDrawContext()
|
||||
const dash = useSelector(selectDash)
|
||||
export const QuickDashSelect = React.memo((): JSX.Element => {
|
||||
const { tlstate, useSelector } = useTLDrawContext()
|
||||
const dash = useSelector(selectDash)
|
||||
|
||||
const changeDashStyle = React.useCallback(
|
||||
(dash) => {
|
||||
tlstate.style({ dash: dash as DashStyle })
|
||||
},
|
||||
[tlstate]
|
||||
)
|
||||
const changeDashStyle = React.useCallback(
|
||||
(dash) => {
|
||||
tlstate.style({ dash: dash as DashStyle })
|
||||
},
|
||||
[tlstate]
|
||||
)
|
||||
|
||||
return (
|
||||
<DropdownMenu.Root dir="ltr">
|
||||
<DropdownMenuIconTriggerButton label="Dash">
|
||||
{dashes[dash]}
|
||||
</DropdownMenuIconTriggerButton>
|
||||
<DropdownMenu.Content sideOffset={8}>
|
||||
<DropdownMenu.DropdownMenuRadioGroup
|
||||
as={StyleDropdownContent}
|
||||
direction="vertical"
|
||||
value={dash}
|
||||
onValueChange={changeDashStyle}
|
||||
>
|
||||
{Object.keys(DashStyle).map((dashStyle: string) => (
|
||||
<DropdownMenu.DropdownMenuRadioItem
|
||||
as={StyleDropdownItem}
|
||||
key={dashStyle}
|
||||
isActive={dash === dashStyle}
|
||||
value={dashStyle}
|
||||
>
|
||||
{dashes[dashStyle as DashStyle]}
|
||||
</DropdownMenu.DropdownMenuRadioItem>
|
||||
))}
|
||||
</DropdownMenu.DropdownMenuRadioGroup>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
)
|
||||
}
|
||||
)
|
||||
return (
|
||||
<DropdownMenu.Root dir="ltr">
|
||||
<DropdownMenuIconTriggerButton label="Dash">{dashes[dash]}</DropdownMenuIconTriggerButton>
|
||||
<DropdownMenu.Content sideOffset={8}>
|
||||
<DropdownMenu.DropdownMenuRadioGroup
|
||||
as={StyleDropdownContent}
|
||||
direction="vertical"
|
||||
value={dash}
|
||||
onValueChange={changeDashStyle}
|
||||
>
|
||||
{Object.keys(DashStyle).map((dashStyle: string) => (
|
||||
<DropdownMenu.DropdownMenuRadioItem
|
||||
as={StyleDropdownItem}
|
||||
key={dashStyle}
|
||||
isActive={dash === dashStyle}
|
||||
value={dashStyle}
|
||||
>
|
||||
{dashes[dashStyle as DashStyle]}
|
||||
</DropdownMenu.DropdownMenuRadioItem>
|
||||
))}
|
||||
</DropdownMenu.DropdownMenuRadioGroup>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -3,41 +3,39 @@ import * as Checkbox from '@radix-ui/react-checkbox'
|
|||
import { breakpoints, IconButton, IconWrapper } from '../shared'
|
||||
import { BoxIcon, IsFilledFillIcon } from './shared'
|
||||
import { Tooltip } from '../tooltip'
|
||||
import { useTLDrawContext } from '../../hooks'
|
||||
import type { Data } from '../../state'
|
||||
import { useTLDrawContext } from '~hooks'
|
||||
import type { Data } from '~types'
|
||||
|
||||
const isFilledSelector = (data: Data) => data.appState.selectedStyle.isFilled
|
||||
|
||||
export const QuickFillSelect = React.memo(
|
||||
(): JSX.Element => {
|
||||
const { tlstate, useSelector } = useTLDrawContext()
|
||||
export const QuickFillSelect = React.memo((): JSX.Element => {
|
||||
const { tlstate, useSelector } = useTLDrawContext()
|
||||
|
||||
const isFilled = useSelector(isFilledSelector)
|
||||
const isFilled = useSelector(isFilledSelector)
|
||||
|
||||
const handleIsFilledChange = React.useCallback(
|
||||
(isFilled: boolean) => {
|
||||
tlstate.style({ isFilled })
|
||||
},
|
||||
[tlstate]
|
||||
)
|
||||
const handleIsFilledChange = React.useCallback(
|
||||
(isFilled: boolean) => {
|
||||
tlstate.style({ isFilled })
|
||||
},
|
||||
[tlstate]
|
||||
)
|
||||
|
||||
return (
|
||||
<Checkbox.Root
|
||||
dir="ltr"
|
||||
as={IconButton}
|
||||
bp={breakpoints}
|
||||
checked={isFilled}
|
||||
onCheckedChange={handleIsFilledChange}
|
||||
>
|
||||
<Tooltip label="Fill">
|
||||
<IconWrapper>
|
||||
<BoxIcon />
|
||||
<Checkbox.Indicator>
|
||||
<IsFilledFillIcon />
|
||||
</Checkbox.Indicator>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
</Checkbox.Root>
|
||||
)
|
||||
}
|
||||
)
|
||||
return (
|
||||
<Checkbox.Root
|
||||
dir="ltr"
|
||||
as={IconButton}
|
||||
bp={breakpoints}
|
||||
checked={isFilled}
|
||||
onCheckedChange={handleIsFilledChange}
|
||||
>
|
||||
<Tooltip label="Fill">
|
||||
<IconWrapper>
|
||||
<BoxIcon />
|
||||
<Checkbox.Indicator>
|
||||
<IsFilledFillIcon />
|
||||
</Checkbox.Indicator>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
</Checkbox.Root>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -2,9 +2,8 @@ import * as React from 'react'
|
|||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
|
||||
import { DropdownMenuIconTriggerButton, CircleIcon } from '../shared'
|
||||
import { StyleDropdownContent, StyleDropdownItem } from './shared'
|
||||
import { SizeStyle } from '../../shape'
|
||||
import type { Data } from '../../state'
|
||||
import { useTLDrawContext } from '../../hooks'
|
||||
import { Data, SizeStyle } from '~types'
|
||||
import { useTLDrawContext } from '~hooks'
|
||||
|
||||
const sizes = {
|
||||
[SizeStyle.Small]: 6,
|
||||
|
@ -14,44 +13,42 @@ const sizes = {
|
|||
|
||||
const selectSize = (data: Data) => data.appState.selectedStyle.size
|
||||
|
||||
export const QuickSizeSelect = React.memo(
|
||||
(): JSX.Element => {
|
||||
const { tlstate, useSelector } = useTLDrawContext()
|
||||
export const QuickSizeSelect = React.memo((): JSX.Element => {
|
||||
const { tlstate, useSelector } = useTLDrawContext()
|
||||
|
||||
const size = useSelector(selectSize)
|
||||
const size = useSelector(selectSize)
|
||||
|
||||
const changeSizeStyle = React.useCallback(
|
||||
(size: string) => {
|
||||
tlstate.style({ size: size as SizeStyle })
|
||||
},
|
||||
[tlstate]
|
||||
)
|
||||
const changeSizeStyle = React.useCallback(
|
||||
(size: string) => {
|
||||
tlstate.style({ size: size as SizeStyle })
|
||||
},
|
||||
[tlstate]
|
||||
)
|
||||
|
||||
return (
|
||||
<DropdownMenu.Root dir="ltr">
|
||||
<DropdownMenuIconTriggerButton label="Size">
|
||||
<CircleIcon size={sizes[size]} stroke="none" fill="currentColor" />
|
||||
</DropdownMenuIconTriggerButton>
|
||||
<DropdownMenu.Content sideOffset={8}>
|
||||
<DropdownMenu.DropdownMenuRadioGroup
|
||||
as={StyleDropdownContent}
|
||||
direction="vertical"
|
||||
value={size}
|
||||
onValueChange={changeSizeStyle}
|
||||
>
|
||||
{Object.keys(SizeStyle).map((sizeStyle: string) => (
|
||||
<DropdownMenu.DropdownMenuRadioItem
|
||||
key={sizeStyle}
|
||||
as={StyleDropdownItem}
|
||||
isActive={size === sizeStyle}
|
||||
value={sizeStyle}
|
||||
>
|
||||
<CircleIcon size={sizes[sizeStyle as SizeStyle]} />
|
||||
</DropdownMenu.DropdownMenuRadioItem>
|
||||
))}
|
||||
</DropdownMenu.DropdownMenuRadioGroup>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
)
|
||||
}
|
||||
)
|
||||
return (
|
||||
<DropdownMenu.Root dir="ltr">
|
||||
<DropdownMenuIconTriggerButton label="Size">
|
||||
<CircleIcon size={sizes[size]} stroke="none" fill="currentColor" />
|
||||
</DropdownMenuIconTriggerButton>
|
||||
<DropdownMenu.Content sideOffset={8}>
|
||||
<DropdownMenu.DropdownMenuRadioGroup
|
||||
as={StyleDropdownContent}
|
||||
direction="vertical"
|
||||
value={size}
|
||||
onValueChange={changeSizeStyle}
|
||||
>
|
||||
{Object.keys(SizeStyle).map((sizeStyle: string) => (
|
||||
<DropdownMenu.DropdownMenuRadioItem
|
||||
key={sizeStyle}
|
||||
as={StyleDropdownItem}
|
||||
isActive={size === sizeStyle}
|
||||
value={sizeStyle}
|
||||
>
|
||||
<CircleIcon size={sizes[sizeStyle as SizeStyle]} />
|
||||
</DropdownMenu.DropdownMenuRadioItem>
|
||||
))}
|
||||
</DropdownMenu.DropdownMenuRadioGroup>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -14,8 +14,8 @@ import {
|
|||
PinTopIcon,
|
||||
RotateCounterClockwiseIcon,
|
||||
} from '@radix-ui/react-icons'
|
||||
import { useTLDrawContext } from '../../hooks'
|
||||
import type { Data } from '../../state'
|
||||
import { useTLDrawContext } from '~hooks'
|
||||
import type { Data } from '~types'
|
||||
|
||||
const isAllLockedSelector = (s: Data) => {
|
||||
const { selectedIds } = s.pageState
|
||||
|
@ -39,8 +39,7 @@ const isAllGroupedSelector = (s: Data) => {
|
|||
|
||||
const hasSelectionSelector = (s: Data) => s.pageState.selectedIds.length > 0
|
||||
|
||||
const hasMultipleSelectionSelector = (s: Data) =>
|
||||
s.pageState.selectedIds.length > 1
|
||||
const hasMultipleSelectionSelector = (s: Data) => s.pageState.selectedIds.length > 1
|
||||
|
||||
export const ShapesFunctions = React.memo(() => {
|
||||
const { tlstate, useSelector } = useTLDrawContext()
|
||||
|
@ -109,11 +108,7 @@ export const ShapesFunctions = React.memo(() => {
|
|||
</Tooltip>
|
||||
</IconButton>
|
||||
|
||||
<IconButton
|
||||
disabled={!hasSelection}
|
||||
size="small"
|
||||
onClick={handleRotate}
|
||||
>
|
||||
<IconButton disabled={!hasSelection} size="small" onClick={handleRotate}>
|
||||
<Tooltip label="Rotate">
|
||||
<RotateCounterClockwiseIcon />
|
||||
</Tooltip>
|
||||
|
@ -197,12 +192,7 @@ export const ShapesFunctions = React.memo(() => {
|
|||
</Tooltip>
|
||||
</IconButton>
|
||||
|
||||
<IconButton
|
||||
bp={breakpoints}
|
||||
disabled={!hasSelection}
|
||||
size="small"
|
||||
onClick={handleDelete}
|
||||
>
|
||||
<IconButton bp={breakpoints} disabled={!hasSelection} size="small" onClick={handleDelete}>
|
||||
<Tooltip label="Delete" kbd="⌫">
|
||||
<Trash />
|
||||
</Tooltip>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from 'react'
|
||||
import styled from '../../styles'
|
||||
import styled from '~styles'
|
||||
|
||||
export const StyleDropdownContent = styled('div', {
|
||||
display: 'grid',
|
||||
|
|
|
@ -1,4 +1,15 @@
|
|||
import * as React from 'react'
|
||||
import { Utils } from '@tldraw/core'
|
||||
import { DotsHorizontalIcon, Cross2Icon } from '@radix-ui/react-icons'
|
||||
import { useTLDrawContext } from '~hooks'
|
||||
import type { Data } from '~types'
|
||||
import { ShapesFunctions } from './shapes-functions'
|
||||
import { AlignDistribute } from './align-distribute'
|
||||
import { QuickColorSelect } from './quick-color-select'
|
||||
import { QuickSizeSelect } from './quick-size-select'
|
||||
import { QuickDashSelect } from './quick-dash-select'
|
||||
import { QuickFillSelect } from './quick-fill-select'
|
||||
import { Tooltip } from '../tooltip'
|
||||
import { Kbd } from '../kbd'
|
||||
import {
|
||||
IconButton,
|
||||
|
@ -9,18 +20,6 @@ import {
|
|||
Divider,
|
||||
} from '../shared'
|
||||
|
||||
import type { Data } from '../../state'
|
||||
import { ShapesFunctions } from './shapes-functions'
|
||||
import { AlignDistribute } from './align-distribute'
|
||||
import { QuickColorSelect } from './quick-color-select'
|
||||
import { QuickSizeSelect } from './quick-size-select'
|
||||
import { QuickDashSelect } from './quick-dash-select'
|
||||
import { QuickFillSelect } from './quick-fill-select'
|
||||
import { Tooltip } from '../tooltip'
|
||||
import { DotsHorizontalIcon, Cross2Icon } from '@radix-ui/react-icons'
|
||||
import { Utils } from '@tldraw/core'
|
||||
import { useTLDrawContext } from '../../hooks'
|
||||
|
||||
const isStyleOpenSelector = (s: Data) => s.appState.isStyleOpen
|
||||
|
||||
export function StylePanel(): JSX.Element {
|
||||
|
@ -79,11 +78,7 @@ function SelectedShapeContent(): JSX.Element {
|
|||
hasThreeOrMore={selectedShapesCount > 2}
|
||||
/>
|
||||
<Divider />
|
||||
<RowButton
|
||||
bp={breakpoints}
|
||||
disabled={selectedShapesCount === 0}
|
||||
onClick={handleCopy}
|
||||
>
|
||||
<RowButton bp={breakpoints} disabled={selectedShapesCount === 0} onClick={handleCopy}>
|
||||
<span>Copy</span>
|
||||
{showKbds && <Kbd variant="menu">#C</Kbd>}
|
||||
</RowButton>
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
import * as React from 'react'
|
||||
import { IdProvider } from '@radix-ui/react-id'
|
||||
import { Renderer } from '@tldraw/core'
|
||||
import styled from '~styles'
|
||||
import type { Data, TLDrawDocument } from '~types'
|
||||
import { TLDrawState } from '~state'
|
||||
import { useKeyboardShortcuts, TLDrawContext } from '~hooks'
|
||||
import { tldrawShapeUtils } from '../shape'
|
||||
import type { TLDrawDocument } from '../types'
|
||||
import { useKeyboardShortcuts } from '../hooks'
|
||||
import styled from '../styles'
|
||||
import { ContextMenu } from './context-menu'
|
||||
import { StylePanel } from './style-panel'
|
||||
import { ToolsPanel } from './tools-panel'
|
||||
import { Data, TLDrawState } from '../state'
|
||||
import { TLDrawContext } from '../hooks'
|
||||
|
||||
export interface TLDrawProps {
|
||||
document?: TLDrawDocument
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import * as React from 'react'
|
||||
import { FloatingContainer, RowButton } from '../shared'
|
||||
import styled from '../../styles'
|
||||
import type { Data } from '../../state'
|
||||
import { useTLDrawContext } from '../../hooks'
|
||||
import styled from '~styles'
|
||||
import type { Data } from '~types'
|
||||
import { useTLDrawContext } from '~hooks'
|
||||
|
||||
const isEmptyCanvasSelector = (s: Data) =>
|
||||
Object.keys(s.page.shapes).length > 0 && s.appState.isEmptyCanvas
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react'
|
||||
import { FloatingContainer } from '../shared'
|
||||
import { Tooltip } from '../tooltip'
|
||||
import styled from '../../styles'
|
||||
import styled from '~styles'
|
||||
|
||||
export const ToolButton = styled('button', {
|
||||
position: 'relative',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react'
|
||||
import { useTLDrawContext } from '../../hooks'
|
||||
import type { Data } from '../../state'
|
||||
import styled from '../../styles'
|
||||
import { useTLDrawContext } from '~hooks'
|
||||
import type { Data } from '~types'
|
||||
import styled from '~styles'
|
||||
|
||||
const activeToolSelector = (s: Data) => s.appState.activeTool
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import * as React from 'react'
|
||||
import {
|
||||
ArrowTopRightIcon,
|
||||
CircleIcon,
|
||||
|
@ -8,17 +9,15 @@ import {
|
|||
SquareIcon,
|
||||
TextIcon,
|
||||
} from '@radix-ui/react-icons'
|
||||
import * as React from 'react'
|
||||
import styled from '~styles'
|
||||
import { Data, TLDrawShapeType } from '~types'
|
||||
import { useTLDrawContext } from '~hooks'
|
||||
import { StatusBar } from './status-bar'
|
||||
import { FloatingContainer } from '../shared'
|
||||
import { PrimaryButton, SecondaryButton } from './shared'
|
||||
import styled from '../../styles'
|
||||
import { UndoRedo } from './undo-redo'
|
||||
import { Zoom } from './zoom'
|
||||
import { BackToContent } from './back-to-content'
|
||||
import { TLDrawShapeType } from '../../shape'
|
||||
import { useTLDrawContext } from '../../hooks'
|
||||
import type { Data } from '../../state'
|
||||
|
||||
const activeToolSelector = (s: Data) => s.appState.activeTool
|
||||
const isToolLockedSelector = (s: Data) => s.appState.isToolLocked
|
||||
|
@ -130,9 +129,11 @@ export const ToolsPanel = React.memo((): JSX.Element => {
|
|||
</FloatingContainer>
|
||||
<UndoRedo />
|
||||
</RightWrap>
|
||||
<StatusWrap>
|
||||
<StatusBar />
|
||||
</StatusWrap>
|
||||
{isDebugMode && (
|
||||
<StatusWrap>
|
||||
<StatusBar />
|
||||
</StatusWrap>
|
||||
)}
|
||||
</ToolsPanelContainer>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -1,32 +1,30 @@
|
|||
import * as React from 'react'
|
||||
import { useTLDrawContext } from '~hooks'
|
||||
import { TertiaryButton, TertiaryButtonsContainer } from './shared'
|
||||
import { Undo, Redo, Trash } from '../icons'
|
||||
import { useTLDrawContext } from '../../hooks'
|
||||
|
||||
export const UndoRedo = React.memo(
|
||||
(): JSX.Element => {
|
||||
const { tlstate } = useTLDrawContext()
|
||||
export const UndoRedo = React.memo((): JSX.Element => {
|
||||
const { tlstate } = useTLDrawContext()
|
||||
|
||||
const handleDelete = React.useCallback(() => {
|
||||
tlstate.delete()
|
||||
}, [tlstate])
|
||||
const handleDelete = React.useCallback(() => {
|
||||
tlstate.delete()
|
||||
}, [tlstate])
|
||||
|
||||
const handleClear = React.useCallback(() => {
|
||||
tlstate.clear()
|
||||
}, [tlstate])
|
||||
const handleClear = React.useCallback(() => {
|
||||
tlstate.clear()
|
||||
}, [tlstate])
|
||||
|
||||
return (
|
||||
<TertiaryButtonsContainer bp={{ '@initial': 'mobile', '@sm': 'small' }}>
|
||||
<TertiaryButton label="Undo" kbd="#Z" onClick={tlstate.undo}>
|
||||
<Undo />
|
||||
</TertiaryButton>
|
||||
<TertiaryButton label="Redo" kbd="#⇧" onClick={tlstate.redo}>
|
||||
<Redo />
|
||||
</TertiaryButton>
|
||||
<TertiaryButton label="Delete" kbd="⌫" onClick={handleDelete} onDoubleClick={handleClear}>
|
||||
<Trash />
|
||||
</TertiaryButton>
|
||||
</TertiaryButtonsContainer>
|
||||
)
|
||||
}
|
||||
)
|
||||
return (
|
||||
<TertiaryButtonsContainer bp={{ '@initial': 'mobile', '@sm': 'small' }}>
|
||||
<TertiaryButton label="Undo" kbd="#Z" onClick={tlstate.undo}>
|
||||
<Undo />
|
||||
</TertiaryButton>
|
||||
<TertiaryButton label="Redo" kbd="#⇧" onClick={tlstate.redo}>
|
||||
<Redo />
|
||||
</TertiaryButton>
|
||||
<TertiaryButton label="Delete" kbd="⌫" onClick={handleDelete} onDoubleClick={handleClear}>
|
||||
<Trash />
|
||||
</TertiaryButton>
|
||||
</TertiaryButtonsContainer>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -1,26 +1,24 @@
|
|||
import * as React from 'react'
|
||||
import { ZoomInIcon, ZoomOutIcon } from '@radix-ui/react-icons'
|
||||
import { TertiaryButton, TertiaryButtonsContainer } from './shared'
|
||||
import { useTLDrawContext } from '../../hooks'
|
||||
import type { Data } from '../../state'
|
||||
import { useTLDrawContext } from '~hooks'
|
||||
import type { Data } from '~types'
|
||||
|
||||
export const Zoom = React.memo(
|
||||
(): JSX.Element => {
|
||||
const { tlstate } = useTLDrawContext()
|
||||
export const Zoom = React.memo((): JSX.Element => {
|
||||
const { tlstate } = useTLDrawContext()
|
||||
|
||||
return (
|
||||
<TertiaryButtonsContainer bp={{ '@initial': 'mobile', '@sm': 'small' }}>
|
||||
<TertiaryButton label="Zoom Out" kbd={`#−`} onClick={tlstate.zoomOut}>
|
||||
<ZoomOutIcon />
|
||||
</TertiaryButton>
|
||||
<TertiaryButton label="Zoom In" kbd={`#+`} onClick={tlstate.zoomIn}>
|
||||
<ZoomInIcon />
|
||||
</TertiaryButton>
|
||||
<ZoomCounter />
|
||||
</TertiaryButtonsContainer>
|
||||
)
|
||||
}
|
||||
)
|
||||
return (
|
||||
<TertiaryButtonsContainer bp={{ '@initial': 'mobile', '@sm': 'small' }}>
|
||||
<TertiaryButton label="Zoom Out" kbd={`#−`} onClick={tlstate.zoomOut}>
|
||||
<ZoomOutIcon />
|
||||
</TertiaryButton>
|
||||
<TertiaryButton label="Zoom In" kbd={`#+`} onClick={tlstate.zoomIn}>
|
||||
<ZoomInIcon />
|
||||
</TertiaryButton>
|
||||
<ZoomCounter />
|
||||
</TertiaryButtonsContainer>
|
||||
)
|
||||
})
|
||||
|
||||
const zoomSelector = (s: Data) => s.pageState.camera.zoom
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as RadixTooltip from '@radix-ui/react-tooltip'
|
||||
import * as React from 'react'
|
||||
import styled from '../styles'
|
||||
import styled from '~styles'
|
||||
import { Kbd } from './kbd'
|
||||
|
||||
interface TooltipProps {
|
||||
|
@ -10,12 +10,7 @@ interface TooltipProps {
|
|||
side?: 'bottom' | 'left' | 'right' | 'top'
|
||||
}
|
||||
|
||||
export function Tooltip({
|
||||
children,
|
||||
label,
|
||||
kbd,
|
||||
side = 'top',
|
||||
}: TooltipProps): JSX.Element {
|
||||
export function Tooltip({ children, label, kbd, side = 'top' }: TooltipProps): JSX.Element {
|
||||
return (
|
||||
<RadixTooltip.Root>
|
||||
<RadixTooltip.Trigger as="span">{children}</RadixTooltip.Trigger>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import * as React from 'react'
|
||||
import { inputs } from '@tldraw/core'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { TLDrawShapeType } from '../shape'
|
||||
import type { TLDrawState } from '../state'
|
||||
import { TLDrawShapeType } from '~types'
|
||||
import type { TLDrawState } from '~state'
|
||||
|
||||
export function useKeyboardShortcuts(tlstate: TLDrawState) {
|
||||
React.useEffect(() => {
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
import * as React from 'react'
|
||||
import type { Data } from '../state/state-types'
|
||||
import type { Data } from '~types'
|
||||
import type { UseStore } from 'zustand'
|
||||
import type { TLDrawState } from '../state'
|
||||
import type { TLDrawState } from '~state'
|
||||
|
||||
export interface TLDrawContextType {
|
||||
tlstate: TLDrawState
|
||||
useSelector: UseStore<Data>
|
||||
}
|
||||
|
||||
export const TLDrawContext = React.createContext<TLDrawContextType>(
|
||||
{} as TLDrawContextType
|
||||
)
|
||||
export const TLDrawContext = React.createContext<TLDrawContextType>({} as TLDrawContextType)
|
||||
|
||||
export function useTLDrawContext() {
|
||||
const context = React.useContext(TLDrawContext)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { Theme } from '../shape'
|
||||
import type { Theme } from '~types'
|
||||
|
||||
export function useTheme() {
|
||||
return {
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
export * from './shape-utils'
|
||||
export * from './shape-types'
|
||||
export * from './shape-styles'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Utils } from '@tldraw/core'
|
||||
import { Theme, ColorStyle, DashStyle, ShapeStyles, SizeStyle } from './shape-types'
|
||||
import { Theme, ColorStyle, DashStyle, ShapeStyles, SizeStyle } from '~types'
|
||||
|
||||
const canvasLight = '#fafafa'
|
||||
|
||||
|
|
|
@ -1,160 +0,0 @@
|
|||
import type { TLBinding } from '@tldraw/core'
|
||||
import { TLShape, TLShapeUtil, TLHandle } from '@tldraw/core'
|
||||
|
||||
export enum TLDrawToolType {
|
||||
Draw = 'draw',
|
||||
Bounds = 'bounds',
|
||||
Point = 'point',
|
||||
Handle = 'handle',
|
||||
Points = 'points',
|
||||
Text = 'text',
|
||||
}
|
||||
|
||||
export enum TLDrawShapeType {
|
||||
Ellipse = 'ellipse',
|
||||
Rectangle = 'rectangle',
|
||||
Draw = 'draw',
|
||||
Arrow = 'arrow',
|
||||
Text = 'text',
|
||||
}
|
||||
|
||||
export enum Decoration {
|
||||
Arrow = 'Arrow',
|
||||
}
|
||||
|
||||
export interface TLDrawBaseShape extends TLShape {
|
||||
style: ShapeStyles
|
||||
type: TLDrawShapeType
|
||||
}
|
||||
|
||||
export interface DrawShape extends TLDrawBaseShape {
|
||||
type: TLDrawShapeType.Draw
|
||||
points: number[][]
|
||||
}
|
||||
|
||||
export interface ArrowShape extends TLDrawBaseShape {
|
||||
type: TLDrawShapeType.Arrow
|
||||
bend: number
|
||||
handles: {
|
||||
start: TLHandle
|
||||
bend: TLHandle
|
||||
end: TLHandle
|
||||
}
|
||||
decorations?: {
|
||||
start?: Decoration
|
||||
end?: Decoration
|
||||
middle?: Decoration
|
||||
}
|
||||
}
|
||||
export interface EllipseShape extends TLDrawBaseShape {
|
||||
type: TLDrawShapeType.Ellipse
|
||||
radius: number[]
|
||||
}
|
||||
|
||||
export interface RectangleShape extends TLDrawBaseShape {
|
||||
type: TLDrawShapeType.Rectangle
|
||||
size: number[]
|
||||
}
|
||||
|
||||
export interface TextShape extends TLDrawBaseShape {
|
||||
type: TLDrawShapeType.Text
|
||||
text: string
|
||||
}
|
||||
|
||||
export type TLDrawShape = RectangleShape | EllipseShape | DrawShape | ArrowShape | TextShape
|
||||
|
||||
export abstract class TLDrawShapeUtil<T extends TLDrawShape> extends TLShapeUtil<T> {
|
||||
abstract toolType: TLDrawToolType
|
||||
}
|
||||
|
||||
export type TLDrawShapeUtils = Record<TLDrawShapeType, TLDrawShapeUtil<TLDrawShape>>
|
||||
|
||||
export interface ArrowBinding extends TLBinding {
|
||||
type: 'arrow'
|
||||
handleId: keyof ArrowShape['handles']
|
||||
distance: number
|
||||
point: number[]
|
||||
}
|
||||
|
||||
export type TLDrawBinding = ArrowBinding
|
||||
|
||||
export enum ColorStyle {
|
||||
White = 'White',
|
||||
LightGray = 'LightGray',
|
||||
Gray = 'Gray',
|
||||
Black = 'Black',
|
||||
Green = 'Green',
|
||||
Cyan = 'Cyan',
|
||||
Blue = 'Blue',
|
||||
Indigo = 'Indigo',
|
||||
Violet = 'Violet',
|
||||
Red = 'Red',
|
||||
Orange = 'Orange',
|
||||
Yellow = 'Yellow',
|
||||
}
|
||||
|
||||
export enum SizeStyle {
|
||||
Small = 'Small',
|
||||
Medium = 'Medium',
|
||||
Large = 'Large',
|
||||
}
|
||||
|
||||
export enum DashStyle {
|
||||
Draw = 'Draw',
|
||||
Solid = 'Solid',
|
||||
Dashed = 'Dashed',
|
||||
Dotted = 'Dotted',
|
||||
}
|
||||
|
||||
export enum FontSize {
|
||||
Small = 'Small',
|
||||
Medium = 'Medium',
|
||||
Large = 'Large',
|
||||
ExtraLarge = 'ExtraLarge',
|
||||
}
|
||||
|
||||
export type ShapeStyles = {
|
||||
color: ColorStyle
|
||||
size: SizeStyle
|
||||
dash: DashStyle
|
||||
isFilled?: boolean
|
||||
scale?: number
|
||||
}
|
||||
|
||||
export type PropsOfType<U> = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[K in keyof TLDrawShape]: TLDrawShape[K] extends any
|
||||
? TLDrawShape[K] extends U
|
||||
? K
|
||||
: never
|
||||
: never
|
||||
}[keyof TLDrawShape]
|
||||
|
||||
export type Theme = 'dark' | 'light'
|
||||
|
||||
export type Difference<A, B, C = A> = A extends B ? never : C
|
||||
|
||||
export type Intersection<A, B, C = A> = A extends B ? C : never
|
||||
|
||||
export type FilteredKeys<T, U> = {
|
||||
[P in keyof T]: T[P] extends U ? P : never
|
||||
}[keyof T]
|
||||
|
||||
export type RequiredKeys<T> = {
|
||||
[K in keyof T]-?: Difference<Record<string, unknown>, Pick<T, K>, K>
|
||||
}[keyof T]
|
||||
|
||||
export type MembersWithRequiredKey<T, U> = {
|
||||
[P in keyof T]: Intersection<U, RequiredKeys<T[P]>, T[P]>
|
||||
}[keyof T]
|
||||
|
||||
export type MappedByType<U extends string, T extends { type: U }> = {
|
||||
[P in T['type']]: T extends any ? (P extends T['type'] ? T : never) : never
|
||||
}
|
||||
|
||||
export type ShapesWithProp<U> = MembersWithRequiredKey<
|
||||
MappedByType<TLDrawShapeType, TLDrawShape>,
|
||||
U
|
||||
>
|
||||
|
||||
export {}
|
|
@ -1,5 +1,5 @@
|
|||
import { Rectangle, Ellipse, Arrow, Draw, Text } from './shapes'
|
||||
import { TLDrawShapeType, TLDrawShape, TLDrawShapeUtil, TLDrawShapeUtils } from './shape-types'
|
||||
import { TLDrawShapeType, TLDrawShape, TLDrawShapeUtil, TLDrawShapeUtils } from '~types'
|
||||
|
||||
export const tldrawShapeUtils: TLDrawShapeUtils = {
|
||||
[TLDrawShapeType.Rectangle]: new Rectangle(),
|
||||
|
|
|
@ -8,11 +8,9 @@ import {
|
|||
Intersect,
|
||||
TLHandle,
|
||||
TLPointerInfo,
|
||||
TLBinding,
|
||||
TLShapeUtil,
|
||||
} from '@tldraw/core'
|
||||
import getStroke from 'perfect-freehand'
|
||||
import { defaultStyle, getPerfectDashProps, getShapeStyle } from '../../shape-styles'
|
||||
import { defaultStyle, getPerfectDashProps, getShapeStyle } from '~shape'
|
||||
import {
|
||||
ArrowShape,
|
||||
Decoration,
|
||||
|
@ -22,7 +20,7 @@ import {
|
|||
DashStyle,
|
||||
TLDrawShape,
|
||||
ArrowBinding,
|
||||
} from '../../shape-types'
|
||||
} from '~types'
|
||||
|
||||
export class Arrow extends TLDrawShapeUtil<ArrowShape> {
|
||||
type = TLDrawShapeType.Arrow as const
|
||||
|
@ -122,6 +120,7 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape> {
|
|||
strokeDashoffset="none"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
pointerEvents="stroke"
|
||||
/>
|
||||
<path
|
||||
d={path}
|
||||
|
@ -132,6 +131,7 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape> {
|
|||
strokeDashoffset={strokeDashoffset}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
pointerEvents="stroke"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
@ -181,6 +181,7 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape> {
|
|||
strokeDashoffset="none"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
pointerEvents="stroke"
|
||||
/>
|
||||
<path
|
||||
d={path}
|
||||
|
@ -191,6 +192,7 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape> {
|
|||
strokeDashoffset={strokeDashoffset}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
pointerEvents="stroke"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
@ -199,7 +201,7 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape> {
|
|||
const sw = strokeWidth * 1.618
|
||||
|
||||
return (
|
||||
<g pointerEvents="all">
|
||||
<g pointerEvents="none">
|
||||
{shaftPath}
|
||||
{shape.decorations?.start === Decoration.Arrow && (
|
||||
<path
|
||||
|
@ -586,6 +588,9 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape> {
|
|||
},
|
||||
}
|
||||
|
||||
// Zero out the handles to prevent handles with negative points. If a handle's x or y
|
||||
// is below zero, we need to move the shape left or up to make it zero.
|
||||
|
||||
const bounds = Utils.getBoundsFromPoints(
|
||||
Object.values(nextShape.handles).map((handle) => handle.point)
|
||||
)
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
import * as React from 'react'
|
||||
import { TLBounds, Utils, Vec, TLTransformInfo, TLRenderInfo, Intersect } from '@tldraw/core'
|
||||
import getStroke, { getStrokePoints } from 'perfect-freehand'
|
||||
import { defaultStyle, getShapeStyle } from '../../shape-styles'
|
||||
import {
|
||||
DrawShape,
|
||||
DashStyle,
|
||||
TLDrawShapeUtil,
|
||||
TLDrawShapeType,
|
||||
TLDrawToolType,
|
||||
} from '../../shape-types'
|
||||
import { defaultStyle, getShapeStyle } from '~shape'
|
||||
import { DrawShape, DashStyle, TLDrawShapeUtil, TLDrawShapeType, TLDrawToolType } from '~types'
|
||||
|
||||
export class Draw extends TLDrawShapeUtil<DrawShape> {
|
||||
type = TLDrawShapeType.Draw as const
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
import * as React from 'react'
|
||||
import { Utils, TLTransformInfo, TLBounds, Intersect, Vec, TLRenderInfo } from '@tldraw/core'
|
||||
import {
|
||||
DashStyle,
|
||||
EllipseShape,
|
||||
TLDrawShapeType,
|
||||
TLDrawShapeUtil,
|
||||
TLDrawToolType,
|
||||
} from '../../shape-types'
|
||||
import { defaultStyle, getPerfectDashProps, getShapeStyle } from '../../shape-styles'
|
||||
import { DashStyle, EllipseShape, TLDrawShapeType, TLDrawShapeUtil, TLDrawToolType } from '~types'
|
||||
import { defaultStyle, getPerfectDashProps, getShapeStyle } from '~shape'
|
||||
import getStroke from 'perfect-freehand'
|
||||
|
||||
export class Ellipse extends TLDrawShapeUtil<EllipseShape> {
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
import * as React from 'react'
|
||||
import { TLBounds, Utils, Vec, TLTransformInfo, TLRenderInfo, Intersect } from '@tldraw/core'
|
||||
import getStroke from 'perfect-freehand'
|
||||
import { getPerfectDashProps, defaultStyle, getShapeStyle } from '../../shape-styles'
|
||||
import {
|
||||
RectangleShape,
|
||||
DashStyle,
|
||||
TLDrawShapeUtil,
|
||||
TLDrawShapeType,
|
||||
TLDrawToolType,
|
||||
} from '../../shape-types'
|
||||
import { getPerfectDashProps, defaultStyle, getShapeStyle } from '~shape'
|
||||
import { RectangleShape, DashStyle, TLDrawShapeUtil, TLDrawShapeType, TLDrawToolType } from '~types'
|
||||
|
||||
export class Rectangle extends TLDrawShapeUtil<RectangleShape> {
|
||||
type = TLDrawShapeType.Rectangle as const
|
||||
|
@ -308,7 +302,7 @@ export class Rectangle extends TLDrawShapeUtil<RectangleShape> {
|
|||
}
|
||||
}
|
||||
|
||||
transformSingle(shape: RectangleShape, bounds: TLBounds, info: TLTransformInfo<RectangleShape>) {
|
||||
transformSingle(shape: RectangleShape, bounds: TLBounds) {
|
||||
return {
|
||||
size: Vec.round([bounds.width, bounds.height]),
|
||||
point: Vec.round([bounds.minX, bounds.minY]),
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import * as React from 'react'
|
||||
import { TLBounds, Utils, Vec, TLTransformInfo, TLRenderInfo, Intersect } from '@tldraw/core'
|
||||
import { defaultStyle, getShapeStyle, getFontSize, getFontStyle } from '../../shape-styles'
|
||||
import { TextShape, TLDrawShapeUtil, TLDrawShapeType, TLDrawToolType } from '../../shape-types'
|
||||
import styled from '../../../styles'
|
||||
import { defaultStyle, getShapeStyle, getFontSize, getFontStyle } from '~shape'
|
||||
import { TextShape, TLDrawShapeUtil, TLDrawShapeType, TLDrawToolType } from '~types'
|
||||
import styled from '~styles'
|
||||
import TextAreaUtils from './text-utils'
|
||||
|
||||
function normalizeText(text: string) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { TLDrawState } from '../../tlstate'
|
||||
import { mockDocument } from '../../test-helpers'
|
||||
import { AlignType } from '../../../types'
|
||||
import { TLDrawState } from '~state'
|
||||
import { mockDocument } from '~state/test-helpers'
|
||||
import { AlignType } from '~types'
|
||||
|
||||
describe('Align command', () => {
|
||||
const tlstate = new TLDrawState()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Utils } from '@tldraw/core'
|
||||
import { AlignType } from '../../../types'
|
||||
import type { Data, Command } from '../../state-types'
|
||||
import { TLDR } from '../../tldr'
|
||||
import { AlignType } from '~types'
|
||||
import type { Data, Command } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
||||
export function align(data: Data, ids: string[], type: AlignType): Command {
|
||||
const initialShapes = ids.map((id) => TLDR.getShape(data, id))
|
||||
|
@ -14,9 +14,7 @@ export function align(data: Data, ids: string[], type: AlignType): Command {
|
|||
}
|
||||
})
|
||||
|
||||
const commonBounds = Utils.getCommonBounds(
|
||||
boundsForShapes.map(({ bounds }) => bounds)
|
||||
)
|
||||
const commonBounds = Utils.getCommonBounds(boundsForShapes.map(({ bounds }) => bounds))
|
||||
|
||||
const midX = commonBounds.minX + commonBounds.width / 2
|
||||
const midY = commonBounds.minY + commonBounds.height / 2
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { TLDrawState } from '../../tlstate'
|
||||
import { mockDocument } from '../../test-helpers'
|
||||
import { TLDrawState } from '~state'
|
||||
import { mockDocument } from '~state/test-helpers'
|
||||
|
||||
describe('Create command', () => {
|
||||
const tlstate = new TLDrawState()
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import type { TLDrawShape } from '../../../shape'
|
||||
import type { Data, Command } from '../../state-types'
|
||||
import type { TLDrawShape, Data, Command } from '~types'
|
||||
|
||||
export function create(data: Data, shapes: TLDrawShape[]): Command {
|
||||
return {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { TLDR } from './../../tldr'
|
||||
import { TLDrawState } from '../../tlstate'
|
||||
import { mockDocument } from '../../test-helpers'
|
||||
import type { TLDrawShape } from '../../../shape'
|
||||
import { TLDR } from '~state/tldr'
|
||||
import { TLDrawState } from '~state'
|
||||
import { mockDocument } from '~state/test-helpers'
|
||||
import type { TLDrawShape } from '~types'
|
||||
|
||||
describe('Delete command', () => {
|
||||
const tlstate = new TLDrawState()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { Data, Command, PagePartial } from '../../state-types'
|
||||
import type { Data, Command, PagePartial } from '~types'
|
||||
|
||||
// - [x] Delete shapes
|
||||
// - [x] Delete bindings too
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { TLDrawState } from '../../tlstate'
|
||||
import { mockDocument } from '../../test-helpers'
|
||||
import { DistributeType } from '../../../types'
|
||||
import { TLDrawState } from '~state'
|
||||
import { mockDocument } from '~state/test-helpers'
|
||||
import { DistributeType } from '~types'
|
||||
|
||||
describe('Distribute command', () => {
|
||||
const tlstate = new TLDrawState()
|
||||
|
|
|
@ -1,18 +1,10 @@
|
|||
import { Utils } from '@tldraw/core'
|
||||
import type { TLDrawShape } from '../../../shape'
|
||||
import { DistributeType } from '../../../types'
|
||||
import type { Data, Command } from '../../state-types'
|
||||
import { TLDR } from '../../tldr'
|
||||
import { DistributeType, TLDrawShape, Data, Command } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
||||
export function distribute(
|
||||
data: Data,
|
||||
ids: string[],
|
||||
type: DistributeType
|
||||
): Command {
|
||||
export function distribute(data: Data, ids: string[], type: DistributeType): Command {
|
||||
const initialShapes = ids.map((id) => data.page.shapes[id])
|
||||
const deltaMap = Object.fromEntries(
|
||||
getDistributions(initialShapes, type).map((d) => [d.id, d])
|
||||
)
|
||||
const deltaMap = Object.fromEntries(getDistributions(initialShapes, type).map((d) => [d.id, d]))
|
||||
|
||||
const { before, after } = TLDR.mutateShapes(data, ids, (shape) => {
|
||||
if (!deltaMap[shape.id]) return shape
|
||||
|
@ -50,9 +42,7 @@ function getDistributions(initialShapes: TLDrawShape[], type: DistributeType) {
|
|||
})
|
||||
|
||||
const len = entries.length
|
||||
const commonBounds = Utils.getCommonBounds(
|
||||
entries.map(({ bounds }) => bounds)
|
||||
)
|
||||
const commonBounds = Utils.getCommonBounds(entries.map(({ bounds }) => bounds))
|
||||
|
||||
const results: { id: string; prev: number[]; next: number[] }[] = []
|
||||
|
||||
|
@ -86,7 +76,7 @@ function getDistributions(initialShapes: TLDrawShape[], type: DistributeType) {
|
|||
let x = commonBounds.minX
|
||||
const step = (commonBounds.width - span) / (len - 1)
|
||||
|
||||
entriesToMove.forEach(({ id, point, bounds }, i) => {
|
||||
entriesToMove.forEach(({ id, point, bounds }) => {
|
||||
results.push({ id, prev: point, next: [x, bounds.minY] })
|
||||
x += bounds.width + step
|
||||
})
|
||||
|
@ -122,7 +112,7 @@ function getDistributions(initialShapes: TLDrawShape[], type: DistributeType) {
|
|||
let y = commonBounds.minY
|
||||
const step = (commonBounds.height - span) / (len - 1)
|
||||
|
||||
entriesToMove.forEach(({ id, point, bounds }, i) => {
|
||||
entriesToMove.forEach(({ id, point, bounds }) => {
|
||||
results.push({ id, prev: point, next: [bounds.minX, y] })
|
||||
y += bounds.height + step
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { TLDrawState } from '../../tlstate'
|
||||
import { mockDocument } from '../../test-helpers'
|
||||
import { TLDrawState } from '~state'
|
||||
import { mockDocument } from '~state/test-helpers'
|
||||
|
||||
describe('Duplicate command', () => {
|
||||
const tlstate = new TLDrawState()
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Ładowanie…
Reference in New Issue