Fix text scrolling
This commit is contained in:
parent
c8c3ebce68
commit
c004ed5e56
24 changed files with 305 additions and 416 deletions
|
@ -51,11 +51,20 @@ export function Canvas<T extends TLShape, M extends Record<string, unknown>>({
|
|||
|
||||
const rLayer = useCameraCss(rContainer, pageState)
|
||||
|
||||
const preventScrolling = React.useCallback((e: React.UIEvent<HTMLDivElement, UIEvent>) => {
|
||||
e.currentTarget.scrollTo(0, 0)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="tl-container" ref={rContainer}>
|
||||
<div id="canvas" className="tl-absolute tl-canvas" ref={rCanvas} {...events}>
|
||||
<div
|
||||
id="canvas"
|
||||
className="tl-absolute tl-canvas"
|
||||
ref={rCanvas}
|
||||
onScroll={preventScrolling}
|
||||
{...events}
|
||||
>
|
||||
<ErrorBoundary FallbackComponent={ErrorFallback} onReset={resetError}>
|
||||
{/* <Defs zoom={pageState.camera.zoom} /> */}
|
||||
<div ref={rLayer} className="tl-absolute tl-layer">
|
||||
<Page
|
||||
page={page}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import * as React from 'react'
|
||||
import type { TLBinding, TLPage, TLPageState, TLShape } from '+types'
|
||||
import { useSelection, useShapeTree, useHandles, useRenderOnResize, useTLContext } from '+hooks'
|
||||
import { useSelection, useShapeTree, useHandles, useTLContext } from '+hooks'
|
||||
import { Bounds } from '+components/bounds'
|
||||
import { BoundsBg } from '+components/bounds/bounds-bg'
|
||||
import { Handles } from '+components/handles'
|
||||
|
@ -30,7 +30,14 @@ export function Page<T extends TLShape, M extends Record<string, unknown>>({
|
|||
}: PageProps<T, M>): JSX.Element {
|
||||
const { callbacks, shapeUtils, inputs } = useTLContext()
|
||||
|
||||
const shapeTree = useShapeTree(page, pageState, shapeUtils, inputs.size, meta, callbacks.onChange)
|
||||
const shapeTree = useShapeTree(
|
||||
page,
|
||||
pageState,
|
||||
shapeUtils,
|
||||
inputs.size,
|
||||
meta,
|
||||
callbacks.onRenderCountChange
|
||||
)
|
||||
|
||||
const { shapeWithHandles } = useHandles(page, pageState)
|
||||
|
||||
|
|
|
@ -15,9 +15,9 @@ import { useTLTheme, TLContext, TLContextType } from '../../hooks'
|
|||
|
||||
export interface RendererProps<
|
||||
T extends TLShape,
|
||||
E extends HTMLElement | SVGElement,
|
||||
E extends Element,
|
||||
M extends Record<string, unknown>
|
||||
> extends Partial<TLCallbacks> {
|
||||
> extends Partial<TLCallbacks<T>> {
|
||||
/**
|
||||
* An object containing instances of your shape classes.
|
||||
*/
|
||||
|
@ -66,11 +66,7 @@ export interface RendererProps<
|
|||
* @param props
|
||||
* @returns
|
||||
*/
|
||||
export function Renderer<
|
||||
T extends TLShape,
|
||||
E extends SVGElement | HTMLElement,
|
||||
M extends Record<string, unknown>
|
||||
>({
|
||||
export function Renderer<T extends TLShape, E extends Element, M extends Record<string, unknown>>({
|
||||
shapeUtils,
|
||||
page,
|
||||
pageState,
|
||||
|
@ -91,6 +87,8 @@ export function Renderer<
|
|||
rPageState.current = pageState
|
||||
}, [pageState])
|
||||
|
||||
rest
|
||||
|
||||
const [context] = React.useState<TLContextType<T, E>>(() => ({
|
||||
callbacks: rest,
|
||||
shapeUtils,
|
||||
|
@ -100,7 +98,7 @@ export function Renderer<
|
|||
}))
|
||||
|
||||
return (
|
||||
<TLContext.Provider value={context as TLContextType<T, E>}>
|
||||
<TLContext.Provider value={context as unknown as TLContextType<TLShape, Element>}>
|
||||
<Canvas
|
||||
page={page}
|
||||
pageState={pageState}
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
import { useTLContext } from '+hooks'
|
||||
import * as React from 'react'
|
||||
import type { TLShapeUtil, TLRenderInfo, TLShape } from '+types'
|
||||
|
||||
export function EditingTextShape<
|
||||
T extends TLShape,
|
||||
E extends SVGElement | HTMLElement,
|
||||
M extends Record<string, unknown>
|
||||
>({
|
||||
shape,
|
||||
utils,
|
||||
isEditing,
|
||||
isBinding,
|
||||
isHovered,
|
||||
isSelected,
|
||||
isCurrentParent,
|
||||
events,
|
||||
meta,
|
||||
}: TLRenderInfo<M, E> & {
|
||||
shape: T
|
||||
utils: TLShapeUtil<T, E>
|
||||
}) {
|
||||
const {
|
||||
callbacks: { onTextChange, onTextBlur, onTextFocus, onTextKeyDown, onTextKeyUp },
|
||||
} = useTLContext()
|
||||
|
||||
const ref = utils.getRef(shape)
|
||||
|
||||
React.useEffect(() => {
|
||||
// Firefox fix?
|
||||
setTimeout(() => {
|
||||
if (document.activeElement !== ref.current) {
|
||||
ref.current?.focus()
|
||||
}
|
||||
}, 0)
|
||||
}, [shape.id])
|
||||
|
||||
return (
|
||||
<utils.render
|
||||
ref={ref}
|
||||
{...{
|
||||
shape,
|
||||
isEditing,
|
||||
isHovered,
|
||||
isSelected,
|
||||
isCurrentParent,
|
||||
isBinding,
|
||||
meta,
|
||||
events: { ...events, onTextChange, onTextBlur, onTextFocus, onTextKeyDown, onTextKeyUp },
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
|
@ -3,7 +3,7 @@ import * as React from 'react'
|
|||
import type { TLShapeUtil, TLRenderInfo, TLShape } from '+types'
|
||||
|
||||
export const RenderedShape = React.memo(
|
||||
<T extends TLShape, E extends SVGElement | HTMLElement, M extends Record<string, unknown>>({
|
||||
<T extends TLShape, E extends Element, M extends Record<string, unknown>>({
|
||||
shape,
|
||||
utils,
|
||||
isEditing,
|
||||
|
@ -11,9 +11,11 @@ export const RenderedShape = React.memo(
|
|||
isHovered,
|
||||
isSelected,
|
||||
isCurrentParent,
|
||||
onShapeChange,
|
||||
onShapeBlur,
|
||||
events,
|
||||
meta,
|
||||
}: TLRenderInfo<M, E> & {
|
||||
}: TLRenderInfo<T, M, E> & {
|
||||
shape: T
|
||||
utils: TLShapeUtil<T, E>
|
||||
}) => {
|
||||
|
@ -22,16 +24,16 @@ export const RenderedShape = React.memo(
|
|||
return (
|
||||
<utils.render
|
||||
ref={ref}
|
||||
{...{
|
||||
shape,
|
||||
isEditing,
|
||||
isBinding,
|
||||
isHovered,
|
||||
isSelected,
|
||||
isCurrentParent,
|
||||
meta,
|
||||
events,
|
||||
}}
|
||||
shape={shape}
|
||||
isEditing={isEditing}
|
||||
isBinding={isBinding}
|
||||
isHovered={isHovered}
|
||||
isSelected={isSelected}
|
||||
isCurrentParent={isCurrentParent}
|
||||
meta={meta}
|
||||
events={events}
|
||||
onShapeChange={onShapeChange}
|
||||
onShapeBlur={onShapeBlur}
|
||||
/>
|
||||
)
|
||||
},
|
||||
|
|
|
@ -13,7 +13,7 @@ export const ShapeNode = React.memo(
|
|||
isSelected,
|
||||
isCurrentParent,
|
||||
meta,
|
||||
}: { utils: TLShapeUtils<TLShape, HTMLElement | SVGElement> } & IShapeTreeNode<TLShape, any>) => {
|
||||
}: { utils: TLShapeUtils<TLShape, Element> } & IShapeTreeNode<TLShape, any>) => {
|
||||
return (
|
||||
<>
|
||||
<Shape
|
||||
|
|
|
@ -18,5 +18,5 @@ describe('shape', () => {
|
|||
})
|
||||
})
|
||||
|
||||
// { shape: TLShape; ref: ForwardedRef<HTMLElement | SVGElement>; } & TLRenderInfo<any, any> & RefAttributes<HTMLElement | SVGElement>
|
||||
// { shape: TLShape; ref: ForwardedRef<Element>; } & TLRenderInfo<any, any> & RefAttributes<Element>
|
||||
// { shape: BoxShape; ref: ForwardedRef<any>; } & TLRenderInfo<any, any> & RefAttributes<any>'
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import * as React from 'react'
|
||||
import { useShapeEvents } from '+hooks'
|
||||
import type { IShapeTreeNode, TLShape, TLShapeUtil } from '+types'
|
||||
import { RenderedShape } from './rendered-shape'
|
||||
import { EditingTextShape } from './editing-text-shape'
|
||||
import { Container } from '+components/container'
|
||||
import { useTLContext } from '+hooks'
|
||||
|
||||
// function setTransform(elm: HTMLDivElement, bounds: TLBounds, rotation = 0) {
|
||||
// const transform = `
|
||||
|
@ -16,11 +17,7 @@ import { Container } from '+components/container'
|
|||
// elm.style.setProperty('height', `calc(${bounds.height}px + (var(--tl-padding) * 2))`)
|
||||
// }
|
||||
|
||||
export const Shape = <
|
||||
T extends TLShape,
|
||||
E extends SVGElement | HTMLElement,
|
||||
M extends Record<string, unknown>
|
||||
>({
|
||||
export const Shape = <T extends TLShape, E extends Element, M extends Record<string, unknown>>({
|
||||
shape,
|
||||
utils,
|
||||
isEditing,
|
||||
|
@ -32,6 +29,7 @@ export const Shape = <
|
|||
}: IShapeTreeNode<T, M> & {
|
||||
utils: TLShapeUtil<T, E>
|
||||
}) => {
|
||||
const { callbacks } = useTLContext()
|
||||
const bounds = utils.getBounds(shape)
|
||||
const events = useShapeEvents(shape.id, isCurrentParent)
|
||||
|
||||
|
@ -42,19 +40,6 @@ export const Shape = <
|
|||
bounds={bounds}
|
||||
rotation={shape.rotation}
|
||||
>
|
||||
{isEditing && utils.isEditableText ? (
|
||||
<EditingTextShape
|
||||
shape={shape}
|
||||
isBinding={false}
|
||||
isCurrentParent={false}
|
||||
isEditing={true}
|
||||
isHovered={isHovered}
|
||||
isSelected={isSelected}
|
||||
utils={utils as any}
|
||||
meta={meta as any}
|
||||
events={events}
|
||||
/>
|
||||
) : (
|
||||
<RenderedShape
|
||||
shape={shape}
|
||||
isBinding={isBinding}
|
||||
|
@ -65,8 +50,8 @@ export const Shape = <
|
|||
utils={utils as any}
|
||||
meta={meta as any}
|
||||
events={events}
|
||||
onShapeChange={callbacks.onShapeChange}
|
||||
/>
|
||||
)}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ export * from './useStyle'
|
|||
export * from './useCanvasEvents'
|
||||
export * from './useBoundsHandleEvents'
|
||||
export * from './useCameraCss'
|
||||
export * from './useRenderOnResize'
|
||||
export * from './useSelection'
|
||||
export * from './useHandleEvents'
|
||||
export * from './useHandles'
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
import * as React from 'react'
|
||||
import Utils from '+utils'
|
||||
|
||||
export function useRenderOnResize() {
|
||||
const forceUpdate = React.useReducer((x) => x + 1, 0)[1]
|
||||
|
||||
React.useEffect(() => {
|
||||
const debouncedUpdate = Utils.debounce(forceUpdate, 96)
|
||||
window.addEventListener('resize', debouncedUpdate)
|
||||
return () => {
|
||||
window.removeEventListener('resize', debouncedUpdate)
|
||||
}
|
||||
}, [forceUpdate])
|
||||
}
|
|
@ -2,7 +2,7 @@ import { useTLContext } from '+hooks'
|
|||
import * as React from 'react'
|
||||
import { Utils } from '+utils'
|
||||
|
||||
export function useResizeObserver<T extends HTMLElement | SVGElement>(ref: React.RefObject<T>) {
|
||||
export function useResizeObserver<T extends Element>(ref: React.RefObject<T>) {
|
||||
const { inputs } = useTLContext()
|
||||
const rIsMounted = React.useRef(false)
|
||||
const forceUpdate = React.useReducer((x) => x + 1, 0)[1]
|
||||
|
|
|
@ -9,7 +9,7 @@ export function useSafariFocusOutFix(): void {
|
|||
|
||||
useEffect(() => {
|
||||
function handleFocusOut() {
|
||||
callbacks.onBlurEditingShape?.()
|
||||
callbacks.onShapeBlur?.()
|
||||
}
|
||||
|
||||
if (Utils.isMobileSafari()) {
|
||||
|
|
|
@ -6,7 +6,7 @@ function canvasToScreen(point: number[], camera: TLPageState['camera']): number[
|
|||
return [(point[0] + camera.point[0]) * camera.zoom, (point[1] + camera.point[1]) * camera.zoom]
|
||||
}
|
||||
|
||||
export function useSelection<T extends TLShape, E extends HTMLElement | SVGElement>(
|
||||
export function useSelection<T extends TLShape, E extends Element>(
|
||||
page: TLPage<T, TLBinding>,
|
||||
pageState: TLPageState,
|
||||
shapeUtils: TLShapeUtils<T, E>
|
||||
|
|
|
@ -61,7 +61,7 @@ function shapeIsInViewport(bounds: TLBounds, viewport: TLBounds) {
|
|||
|
||||
export function useShapeTree<
|
||||
T extends TLShape,
|
||||
E extends SVGElement | HTMLElement,
|
||||
E extends Element,
|
||||
M extends Record<string, unknown>
|
||||
>(
|
||||
page: TLPage<T, TLBinding>,
|
||||
|
@ -69,7 +69,7 @@ export function useShapeTree<
|
|||
shapeUtils: TLShapeUtils<T, E>,
|
||||
size: number[],
|
||||
meta?: M,
|
||||
onChange?: TLCallbacks['onChange']
|
||||
onRenderCountChange?: TLCallbacks<T>['onRenderCountChange']
|
||||
) {
|
||||
const rTimeout = React.useRef<unknown>()
|
||||
const rPreviousCount = React.useRef(0)
|
||||
|
@ -128,7 +128,7 @@ export function useShapeTree<
|
|||
clearTimeout(rTimeout.current as number)
|
||||
}
|
||||
rTimeout.current = setTimeout(() => {
|
||||
onChange?.(Array.from(shapesIdsToRender.values()))
|
||||
onRenderCountChange?.(Array.from(shapesIdsToRender.values()))
|
||||
}, 100)
|
||||
rPreviousCount.current = shapesToRender.size
|
||||
}
|
||||
|
|
|
@ -115,11 +115,16 @@ const tlcss = css`
|
|||
--tl-camera-y: 0px;
|
||||
--tl-padding: calc(64px * var(--tl-scale));
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
z-index: 100;
|
||||
touch-action: none;
|
||||
overscroll-behavior: none;
|
||||
background-color: var(--tl-background);
|
||||
|
@ -130,6 +135,25 @@ const tlcss = css`
|
|||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tl-canvas {
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
touch-action: none;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.tl-layer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 0;
|
||||
width: 0;
|
||||
transform: scale(var(--tl-zoom), var(--tl-zoom))
|
||||
translate(var(--tl-camera-x), var(--tl-camera-y));
|
||||
}
|
||||
|
||||
.tl-absolute {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
|
@ -141,6 +165,7 @@ const tlcss = css`
|
|||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
overflow: hidden;
|
||||
transform-origin: center center;
|
||||
pointer-events: none;
|
||||
display: flex;
|
||||
|
@ -151,22 +176,17 @@ const tlcss = css`
|
|||
.tl-positioned-svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tl-positioned-div {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
padding: var(--tl-padding);
|
||||
}
|
||||
|
||||
.tl-layer {
|
||||
transform: scale(var(--tl-zoom), var(--tl-zoom))
|
||||
translate(var(--tl-camera-x), var(--tl-camera-y));
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.tl-counter-scaled {
|
||||
transform: scale(var(--tl-scale));
|
||||
}
|
||||
|
@ -253,14 +273,6 @@ const tlcss = css`
|
|||
pointer-events: none;
|
||||
}
|
||||
|
||||
.tl-canvas {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
touch-action: none;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.tl-dot {
|
||||
fill: var(--tl-background);
|
||||
stroke: var(--tl-foreground);
|
||||
|
|
|
@ -2,18 +2,16 @@ import * as React from 'react'
|
|||
import type { Inputs } from '+inputs'
|
||||
import type { TLCallbacks, TLShape, TLBounds, TLPageState, TLShapeUtils } from '+types'
|
||||
|
||||
export interface TLContextType<T extends TLShape, E extends HTMLElement | SVGElement> {
|
||||
export interface TLContextType<T extends TLShape, E extends Element> {
|
||||
id?: string
|
||||
callbacks: Partial<TLCallbacks>
|
||||
callbacks: Partial<TLCallbacks<T>>
|
||||
shapeUtils: TLShapeUtils<T, E>
|
||||
rPageState: React.MutableRefObject<TLPageState>
|
||||
rScreenBounds: React.MutableRefObject<TLBounds | null>
|
||||
inputs: Inputs
|
||||
}
|
||||
|
||||
export const TLContext = React.createContext<TLContextType<any, any>>(
|
||||
{} as TLContextType<TLShape, HTMLElement | SVGElement>
|
||||
)
|
||||
export const TLContext = React.createContext({} as TLContextType<TLShape, Element>)
|
||||
|
||||
export function useTLContext() {
|
||||
const context = React.useContext(TLContext)
|
||||
|
|
|
@ -6,7 +6,7 @@ import Utils, { Vec } from '+utils'
|
|||
import { useGesture } from '@use-gesture/react'
|
||||
|
||||
// Capture zoom gestures (pinches, wheels and pans)
|
||||
export function useZoomEvents<T extends HTMLElement | SVGElement>(ref: React.RefObject<T>) {
|
||||
export function useZoomEvents<T extends Element>(ref: React.RefObject<T>) {
|
||||
const rOriginPoint = React.useRef<number[] | undefined>(undefined)
|
||||
const rPinchPoint = React.useRef<number[] | undefined>(undefined)
|
||||
const rDelta = React.useRef<number[]>([0, 0])
|
||||
|
|
|
@ -57,33 +57,27 @@ export interface TLShape {
|
|||
isAspectRatioLocked?: boolean
|
||||
}
|
||||
|
||||
export type TLShapeUtils<T extends TLShape, E extends SVGElement | HTMLElement> = Record<
|
||||
string,
|
||||
TLShapeUtil<T, E>
|
||||
>
|
||||
export type TLShapeUtils<T extends TLShape, E extends Element> = Record<string, TLShapeUtil<T, E>>
|
||||
|
||||
export interface TLRenderInfo<M = any, E = any> {
|
||||
export interface TLRenderInfo<T extends TLShape, M = any, E = any> {
|
||||
isEditing: boolean
|
||||
isBinding: boolean
|
||||
isHovered: boolean
|
||||
isSelected: boolean
|
||||
isCurrentParent: boolean
|
||||
meta: M extends any ? M : never
|
||||
onShapeChange?: TLCallbacks<T>['onShapeChange']
|
||||
onShapeBlur?: TLCallbacks<T>['onShapeBlur']
|
||||
events: {
|
||||
onPointerDown: (e: React.PointerEvent<E>) => void
|
||||
onPointerUp: (e: React.PointerEvent<E>) => void
|
||||
onPointerEnter: (e: React.PointerEvent<E>) => void
|
||||
onPointerMove: (e: React.PointerEvent<E>) => void
|
||||
onPointerLeave: (e: React.PointerEvent<E>) => void
|
||||
onTextChange?: TLCallbacks['onTextChange']
|
||||
onTextBlur?: TLCallbacks['onTextBlur']
|
||||
onTextFocus?: TLCallbacks['onTextFocus']
|
||||
onTextKeyDown?: TLCallbacks['onTextKeyDown']
|
||||
onTextKeyUp?: TLCallbacks['onTextKeyUp']
|
||||
}
|
||||
}
|
||||
|
||||
export interface TLShapeProps<T extends TLShape, E = any, M = any> extends TLRenderInfo<M, E> {
|
||||
export interface TLShapeProps<T extends TLShape, E = any, M = any> extends TLRenderInfo<T, M, E> {
|
||||
ref: ForwardedRef<E>
|
||||
shape: T
|
||||
}
|
||||
|
@ -131,9 +125,7 @@ export type TLBoundsHandleEventHandler = (
|
|||
e: React.PointerEvent
|
||||
) => void
|
||||
|
||||
export interface TLCallbacks {
|
||||
onChange: (ids: string[]) => void
|
||||
|
||||
export interface TLCallbacks<T extends TLShape> {
|
||||
// Camera events
|
||||
onPinchStart: TLPinchEventHandler
|
||||
onPinchEnd: TLPinchEventHandler
|
||||
|
@ -189,15 +181,10 @@ export interface TLCallbacks {
|
|||
onUnhoverHandle: TLPointerEventHandler
|
||||
onReleaseHandle: TLPointerEventHandler
|
||||
|
||||
// Text
|
||||
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
|
||||
|
||||
// Misc
|
||||
onBlurEditingShape: () => void
|
||||
onRenderCountChange: (ids: string[]) => void
|
||||
onShapeChange: (shape: { id: string } & Partial<T>) => void
|
||||
onShapeBlur: () => void
|
||||
onError: (error: Error) => void
|
||||
}
|
||||
|
||||
|
@ -278,7 +265,7 @@ export interface TLBezierCurveSegment {
|
|||
/* Shape Utility */
|
||||
/* -------------------------------------------------- */
|
||||
|
||||
export abstract class TLShapeUtil<T extends TLShape, E extends HTMLElement | SVGElement> {
|
||||
export abstract class TLShapeUtil<T extends TLShape, E extends Element> {
|
||||
refMap = new Map<string, React.RefObject<E>>()
|
||||
|
||||
boundsCache = new WeakMap<TLShape, TLBounds>()
|
||||
|
@ -296,7 +283,7 @@ export abstract class TLShapeUtil<T extends TLShape, E extends HTMLElement | SVG
|
|||
abstract defaultProps: T
|
||||
|
||||
abstract render: React.ForwardRefExoticComponent<
|
||||
{ shape: T; ref: React.ForwardedRef<E> } & TLRenderInfo & React.RefAttributes<E>
|
||||
{ shape: T; ref: React.ForwardedRef<E> } & TLRenderInfo<T> & React.RefAttributes<E>
|
||||
>
|
||||
|
||||
abstract renderIndicator(shape: T): JSX.Element | null
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as React from 'react'
|
|||
import { IdProvider } from '@radix-ui/react-id'
|
||||
import { Renderer } from '@tldraw/core'
|
||||
import styled from '~styles'
|
||||
import { Data, TLDrawDocument, TLDrawStatus, TLDrawToolType } from '~types'
|
||||
import { Data, TLDrawDocument, TLDrawStatus } from '~types'
|
||||
import { TLDrawState } from '~state'
|
||||
import { TLDrawContext, useCustomFonts, useKeyboardShortcuts, useTLDrawContext } from '~hooks'
|
||||
import { tldrawShapeUtils } from '~shape'
|
||||
|
@ -198,14 +198,10 @@ function InnerTldraw({
|
|||
onHoverHandle={tlstate.onHoverHandle}
|
||||
onUnhoverHandle={tlstate.onUnhoverHandle}
|
||||
onReleaseHandle={tlstate.onReleaseHandle}
|
||||
onChange={tlstate.onChange}
|
||||
onError={tlstate.onError}
|
||||
onBlurEditingShape={tlstate.onBlurEditingShape}
|
||||
onTextBlur={tlstate.onTextBlur}
|
||||
onTextChange={tlstate.onTextChange}
|
||||
onTextKeyDown={tlstate.onTextKeyDown}
|
||||
onTextFocus={tlstate.onTextFocus}
|
||||
onTextKeyUp={tlstate.onTextKeyUp}
|
||||
onRenderCountChange={tlstate.onRenderCountChange}
|
||||
onShapeChange={tlstate.onShapeChange}
|
||||
onShapeBlur={tlstate.onShapeBlur}
|
||||
/>
|
||||
</ContextMenu>
|
||||
<MenuButtons>
|
||||
|
@ -220,10 +216,14 @@ function InnerTldraw({
|
|||
}
|
||||
|
||||
const Layout = styled('div', {
|
||||
overflow: 'hidden',
|
||||
position: 'absolute',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
minHeight: 0,
|
||||
minWidth: 0,
|
||||
maxHeight: '100%',
|
||||
maxWidth: '100%',
|
||||
overflow: 'hidden',
|
||||
padding: '8px 8px 0 8px',
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
|
@ -241,9 +241,6 @@ const Layout = styled('div', {
|
|||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
zIndex: 100,
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
import * as React from 'react'
|
||||
import {
|
||||
SVGContainer,
|
||||
TLBounds,
|
||||
Utils,
|
||||
Vec,
|
||||
TLTransformInfo,
|
||||
Intersect,
|
||||
TLShapeProps,
|
||||
} from '@tldraw/core'
|
||||
import { SVGContainer, TLBounds, Utils, Vec, TLTransformInfo, Intersect } from '@tldraw/core'
|
||||
import getStroke, { getStrokePoints } from 'perfect-freehand'
|
||||
import { defaultStyle, getShapeStyle } from '~shape/shape-styles'
|
||||
import { DrawShape, DashStyle, TLDrawShapeUtil, TLDrawShapeType, TLDrawToolType } from '~types'
|
||||
import {
|
||||
DrawShape,
|
||||
DashStyle,
|
||||
TLDrawShapeUtil,
|
||||
TLDrawShapeType,
|
||||
TLDrawToolType,
|
||||
TLDrawShapeProps,
|
||||
} from '~types'
|
||||
|
||||
export class Draw extends TLDrawShapeUtil<DrawShape, SVGSVGElement> {
|
||||
type = TLDrawShapeType.Draw as const
|
||||
|
@ -38,7 +37,7 @@ export class Draw extends TLDrawShapeUtil<DrawShape, SVGSVGElement> {
|
|||
return next.points !== prev.points || next.style !== prev.style
|
||||
}
|
||||
|
||||
render = React.forwardRef<SVGSVGElement, TLShapeProps<DrawShape, SVGSVGElement>>(
|
||||
render = React.forwardRef<SVGSVGElement, TLDrawShapeProps<DrawShape, SVGSVGElement>>(
|
||||
({ shape, meta, events, isEditing }, ref) => {
|
||||
const { points, style } = shape
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import * as React from 'react'
|
||||
import { HTMLContainer, TLBounds, Utils, Vec, TLTransformInfo, Intersect } from '@tldraw/core'
|
||||
import { getShapeStyle, getFontStyle, defaultStyle } from '~shape/shape-styles'
|
||||
import {
|
||||
SVGContainer,
|
||||
TLBounds,
|
||||
Utils,
|
||||
Vec,
|
||||
TLTransformInfo,
|
||||
Intersect,
|
||||
TLShapeProps,
|
||||
} from '@tldraw/core'
|
||||
import { getShapeStyle, getFontSize, getFontStyle, defaultStyle } from '~shape/shape-styles'
|
||||
import { TextShape, TLDrawShapeUtil, TLDrawShapeType, TLDrawToolType, ArrowShape } from '~types'
|
||||
TextShape,
|
||||
TLDrawShapeUtil,
|
||||
TLDrawShapeType,
|
||||
TLDrawToolType,
|
||||
ArrowShape,
|
||||
TLDrawShapeProps,
|
||||
} from '~types'
|
||||
import styled from '~styles'
|
||||
import TextAreaUtils from './text-utils'
|
||||
|
||||
|
@ -57,7 +57,7 @@ if (typeof window !== 'undefined') {
|
|||
melm = getMeasurementDiv()
|
||||
}
|
||||
|
||||
export class Text extends TLDrawShapeUtil<TextShape, SVGSVGElement> {
|
||||
export class Text extends TLDrawShapeUtil<TextShape, HTMLDivElement> {
|
||||
type = TLDrawShapeType.Text as const
|
||||
toolType = TLDrawToolType.Text
|
||||
isAspectRatioLocked = true
|
||||
|
@ -91,21 +91,22 @@ export class Text extends TLDrawShapeUtil<TextShape, SVGSVGElement> {
|
|||
)
|
||||
}
|
||||
|
||||
render = React.forwardRef<SVGSVGElement, TLShapeProps<TextShape, SVGSVGElement>>(
|
||||
({ shape, meta, isEditing, isBinding, events }, ref) => {
|
||||
render = React.forwardRef<HTMLDivElement, TLDrawShapeProps<TextShape, HTMLDivElement>>(
|
||||
({ shape, meta, isEditing, isBinding, onShapeChange, onShapeBlur, events }, ref) => {
|
||||
const rInput = React.useRef<HTMLTextAreaElement>(null)
|
||||
const { id, text, style } = shape
|
||||
const { text, style } = shape
|
||||
const styles = getShapeStyle(style, meta.isDarkMode)
|
||||
const font = getFontStyle(shape.style)
|
||||
const bounds = this.getBounds(shape)
|
||||
|
||||
function handleChange(e: React.ChangeEvent<HTMLTextAreaElement>) {
|
||||
events.onTextChange?.(id, normalizeText(e.currentTarget.value))
|
||||
}
|
||||
|
||||
function handleKeyDown(e: React.KeyboardEvent<HTMLTextAreaElement>) {
|
||||
events.onTextKeyDown?.(id, e.key)
|
||||
const handleChange = React.useCallback(
|
||||
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
onShapeChange?.({ ...shape, text: normalizeText(e.currentTarget.value) })
|
||||
},
|
||||
[shape]
|
||||
)
|
||||
|
||||
const handleKeyDown = React.useCallback(
|
||||
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (e.key === 'Escape') return
|
||||
|
||||
e.stopPropagation()
|
||||
|
@ -118,91 +119,55 @@ export class Text extends TLDrawShapeUtil<TextShape, SVGSVGElement> {
|
|||
TextAreaUtils.indent(e.currentTarget)
|
||||
}
|
||||
|
||||
events.onTextChange?.(id, normalizeText(e.currentTarget.value))
|
||||
}
|
||||
onShapeChange?.({ ...shape, text: normalizeText(e.currentTarget.value) })
|
||||
}
|
||||
},
|
||||
[shape, onShapeChange]
|
||||
)
|
||||
|
||||
function handleKeyUp(e: React.KeyboardEvent<HTMLTextAreaElement>) {
|
||||
events.onTextKeyUp?.(id, e.key)
|
||||
}
|
||||
const handleBlur = React.useCallback(
|
||||
(e: React.FocusEvent<HTMLTextAreaElement>) => {
|
||||
e.currentTarget.setSelectionRange(0, 0)
|
||||
onShapeBlur?.()
|
||||
},
|
||||
[isEditing, shape]
|
||||
)
|
||||
|
||||
function handleBlur(e: React.FocusEvent<HTMLTextAreaElement>) {
|
||||
if (isEditing) {
|
||||
e.currentTarget.focus()
|
||||
e.currentTarget.select()
|
||||
return
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
events.onTextBlur?.(id)
|
||||
}, 0)
|
||||
}
|
||||
|
||||
function handleFocus(e: React.FocusEvent<HTMLTextAreaElement>) {
|
||||
const handleFocus = React.useCallback(
|
||||
(e: React.FocusEvent<HTMLTextAreaElement>) => {
|
||||
if (!isEditing) return
|
||||
if (document.activeElement === e.currentTarget) {
|
||||
e.currentTarget.select()
|
||||
events.onTextFocus?.(id)
|
||||
}
|
||||
}
|
||||
|
||||
function handlePointerDown() {
|
||||
const elm = rInput.current
|
||||
if (!elm) return
|
||||
if (elm.selectionEnd !== 0) {
|
||||
elm.selectionEnd = 0
|
||||
}
|
||||
}
|
||||
|
||||
const fontSize = getFontSize(shape.style.size) * (shape.style.scale || 1)
|
||||
|
||||
const lineHeight = fontSize * 1.3
|
||||
|
||||
if (!isEditing) {
|
||||
return (
|
||||
<SVGContainer ref={ref} {...events}>
|
||||
{isBinding && (
|
||||
<rect
|
||||
className="tl-binding-indicator"
|
||||
x={-16}
|
||||
y={-16}
|
||||
width={bounds.width + 32}
|
||||
height={bounds.height + 32}
|
||||
/>
|
||||
)}
|
||||
{text.split('\n').map((str, i) => (
|
||||
<text
|
||||
key={i}
|
||||
x={4}
|
||||
y={4 + fontSize / 2 + i * lineHeight}
|
||||
fontFamily="Caveat Brush"
|
||||
fontStyle="normal"
|
||||
fontWeight="500"
|
||||
letterSpacing={LETTER_SPACING}
|
||||
fontSize={fontSize}
|
||||
width={bounds.width}
|
||||
height={bounds.height}
|
||||
fill={styles.stroke}
|
||||
color={styles.stroke}
|
||||
stroke="none"
|
||||
xmlSpace="preserve"
|
||||
dominantBaseline="mathematical"
|
||||
alignmentBaseline="mathematical"
|
||||
>
|
||||
{str}
|
||||
</text>
|
||||
))}
|
||||
</SVGContainer>
|
||||
},
|
||||
[isEditing]
|
||||
)
|
||||
|
||||
const handlePointerDown = React.useCallback(
|
||||
(e) => {
|
||||
if (isEditing) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
},
|
||||
[isEditing]
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isEditing) {
|
||||
setTimeout(() => {
|
||||
const elm = rInput.current!
|
||||
elm.focus()
|
||||
elm.select()
|
||||
}, 0)
|
||||
} else {
|
||||
const elm = rInput.current!
|
||||
elm.setSelectionRange(0, 0)
|
||||
}
|
||||
}, [isEditing])
|
||||
|
||||
return (
|
||||
<SVGContainer ref={ref} {...events}>
|
||||
<foreignObject
|
||||
width={bounds.width}
|
||||
height={bounds.height}
|
||||
pointerEvents="none"
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
<HTMLContainer ref={ref} {...events}>
|
||||
<StyledWrapper isEditing={isEditing} onPointerDown={handlePointerDown}>
|
||||
<StyledTextArea
|
||||
ref={rInput}
|
||||
style={{
|
||||
|
@ -218,16 +183,21 @@ export class Text extends TLDrawShapeUtil<TextShape, SVGSVGElement> {
|
|||
autoSave="false"
|
||||
placeholder=""
|
||||
color={styles.stroke}
|
||||
autoFocus={true}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyUp={handleKeyUp}
|
||||
onChange={handleChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
onPointerDown={handlePointerDown}
|
||||
autoFocus={isEditing}
|
||||
isEditing={isEditing}
|
||||
isBinding={isBinding}
|
||||
readOnly={!isEditing}
|
||||
wrap="off"
|
||||
dir="auto"
|
||||
datatype="wysiwyg"
|
||||
/>
|
||||
</foreignObject>
|
||||
</SVGContainer>
|
||||
</StyledWrapper>
|
||||
</HTMLContainer>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
@ -252,7 +222,8 @@ export class Text extends TLDrawShapeUtil<TextShape, SVGSVGElement> {
|
|||
melm.style.font = getFontStyle(shape.style)
|
||||
|
||||
// In tests, offsetWidth and offsetHeight will be 0
|
||||
const [width, height] = [melm.offsetWidth || 1, melm.offsetHeight || 1]
|
||||
const width = melm.offsetWidth || 1
|
||||
const height = melm.offsetHeight || 1
|
||||
|
||||
return {
|
||||
minX: 0,
|
||||
|
@ -291,7 +262,7 @@ export class Text extends TLDrawShapeUtil<TextShape, SVGSVGElement> {
|
|||
transform(
|
||||
_shape: TextShape,
|
||||
bounds: TLBounds,
|
||||
{ initialShape, scaleX, scaleY, transformOrigin }: TLTransformInfo<TextShape>
|
||||
{ initialShape, scaleX, scaleY }: TLTransformInfo<TextShape>
|
||||
): Partial<TextShape> {
|
||||
const {
|
||||
rotation = 0,
|
||||
|
@ -441,67 +412,30 @@ export class Text extends TLDrawShapeUtil<TextShape, SVGSVGElement> {
|
|||
distance,
|
||||
}
|
||||
}
|
||||
// getBindingPoint(shape, point, origin, direction, expandDistance) {
|
||||
// const bounds = this.getBounds(shape)
|
||||
|
||||
// const expandedBounds = expandBounds(bounds, expandDistance)
|
||||
|
||||
// let bindingPoint: number[]
|
||||
// let distance: number
|
||||
|
||||
// if (!HitTest.bounds(point, expandedBounds)) return
|
||||
|
||||
// // The point is inside of the box, so we'll assume the user is
|
||||
// // indicating a specific point inside of the box.
|
||||
// if (HitTest.bounds(point, bounds)) {
|
||||
// bindingPoint = vec.divV(vec.sub(point, [expandedBounds.minX, expandedBounds.minY]), [
|
||||
// expandedBounds.width,
|
||||
// expandedBounds.height,
|
||||
// ])
|
||||
|
||||
// distance = 0
|
||||
// } else {
|
||||
// // Find furthest intersection between ray from
|
||||
// // origin through point and expanded bounds.
|
||||
// const intersection = Intersect.ray
|
||||
// .bounds(origin, direction, expandedBounds)
|
||||
// .filter(int => int.didIntersect)
|
||||
// .map(int => int.points[0])
|
||||
// .sort((a, b) => vec.dist(b, origin) - vec.dist(a, origin))[0]
|
||||
|
||||
// // The anchor is a point between the handle and the intersection
|
||||
// const anchor = vec.med(point, intersection)
|
||||
|
||||
// // Find the distance between the point and the real bounds of the shape
|
||||
// const distanceFromShape = getBoundsSides(bounds)
|
||||
// .map(side => vec.distanceToLineSegment(side[1][0], side[1][1], point))
|
||||
// .sort((a, b) => a - b)[0]
|
||||
|
||||
// if (vec.distanceToLineSegment(point, anchor, this.getCenter(shape)) < 12) {
|
||||
// // If we're close to the center, snap to the center
|
||||
// bindingPoint = [0.5, 0.5]
|
||||
// } else {
|
||||
// // Or else calculate a normalized point
|
||||
// bindingPoint = vec.divV(vec.sub(anchor, [expandedBounds.minX, expandedBounds.minY]), [
|
||||
// expandedBounds.width,
|
||||
// expandedBounds.height,
|
||||
// ])
|
||||
// }
|
||||
|
||||
// distance = distanceFromShape
|
||||
// }
|
||||
|
||||
// return {
|
||||
// point: bindingPoint,
|
||||
// distance,
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
const StyledTextArea = styled('textarea', {
|
||||
zIndex: 1,
|
||||
const StyledWrapper = styled('div', {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
variants: {
|
||||
isEditing: {
|
||||
false: {
|
||||
pointerEvents: 'all',
|
||||
},
|
||||
true: {
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const StyledTextArea = styled('textarea', {
|
||||
position: 'absolute',
|
||||
top: 'var(--tl-padding)',
|
||||
left: 'var(--tl-padding)',
|
||||
zIndex: 1,
|
||||
width: 'calc(100% - (var(--tl-padding) * 2))',
|
||||
height: 'calc(100% - (var(--tl-padding) * 2))',
|
||||
border: 'none',
|
||||
padding: '4px',
|
||||
whiteSpace: 'pre',
|
||||
|
@ -514,12 +448,46 @@ const StyledTextArea = styled('textarea', {
|
|||
letterSpacing: LETTER_SPACING,
|
||||
outline: 0,
|
||||
fontWeight: '500',
|
||||
backgroundColor: '$boundsBg',
|
||||
overflow: 'hidden',
|
||||
pointerEvents: 'all',
|
||||
backfaceVisibility: 'hidden',
|
||||
display: 'inline-block',
|
||||
userSelect: 'text',
|
||||
WebkitUserSelect: 'text',
|
||||
WebkitTouchCallout: 'none',
|
||||
variants: {
|
||||
isBinding: {
|
||||
false: {},
|
||||
true: {
|
||||
background: '$boundsBg',
|
||||
},
|
||||
},
|
||||
isEditing: {
|
||||
false: {
|
||||
pointerEvents: 'none',
|
||||
userSelect: 'none',
|
||||
background: 'none',
|
||||
WebkitUserSelect: 'none',
|
||||
},
|
||||
true: {
|
||||
pointerEvents: 'all',
|
||||
userSelect: 'text',
|
||||
background: '$boundsBg',
|
||||
WebkitUserSelect: 'text',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const NormalText = styled('div', {
|
||||
display: 'block',
|
||||
whiteSpace: 'pre',
|
||||
alignmentBaseline: 'mathematical',
|
||||
dominantBaseline: 'mathematical',
|
||||
pointerEvents: 'none',
|
||||
opacity: '0.5',
|
||||
padding: '4px',
|
||||
margin: '0',
|
||||
outline: 0,
|
||||
fontWeight: '500',
|
||||
lineHeight: 1.4,
|
||||
letterSpacing: LETTER_SPACING,
|
||||
})
|
||||
|
|
|
@ -30,22 +30,24 @@ export class TextSession implements Session {
|
|||
const { initialShape } = this
|
||||
const pageId = data.appState.currentPageId
|
||||
|
||||
let nextShape: TextShape = {
|
||||
...TLDR.getShape<TextShape>(data, initialShape.id, pageId),
|
||||
text,
|
||||
}
|
||||
// let nextShape: TextShape = {
|
||||
// ...TLDR.getShape<TextShape>(data, initialShape.id, pageId),
|
||||
// text,
|
||||
// }
|
||||
|
||||
nextShape = {
|
||||
...nextShape,
|
||||
...TLDR.getShapeUtils(nextShape).onStyleChange(nextShape),
|
||||
} as TextShape
|
||||
// nextShape = {
|
||||
// ...nextShape,
|
||||
// ...TLDR.getShapeUtils(nextShape).onStyleChange(nextShape),
|
||||
// } as TextShape
|
||||
|
||||
return {
|
||||
document: {
|
||||
pages: {
|
||||
[pageId]: {
|
||||
shapes: {
|
||||
[initialShape.id]: nextShape,
|
||||
[initialShape.id]: {
|
||||
text,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -16,7 +16,6 @@ import {
|
|||
TLPointerInfo,
|
||||
inputs,
|
||||
TLBounds,
|
||||
Patch,
|
||||
} from '@tldraw/core'
|
||||
import {
|
||||
FlipType,
|
||||
|
@ -2234,11 +2233,11 @@ export class TLDrawState extends StateManager<Data> {
|
|||
}
|
||||
|
||||
onPinchEnd: TLPinchEventHandler = () => {
|
||||
if (this.state.settings.isZoomSnap) {
|
||||
const i = Math.round((this.pageState.camera.zoom * 100) / 25)
|
||||
const nextZoom = TLDR.getCameraZoom(i * 0.25)
|
||||
this.zoomTo(nextZoom, inputs.pointer?.point)
|
||||
}
|
||||
// if (this.state.settings.isZoomSnap) {
|
||||
// const i = Math.round((this.pageState.camera.zoom * 100) / 25)
|
||||
// const nextZoom = TLDR.getCameraZoom(i * 0.25)
|
||||
// this.zoomTo(nextZoom, inputs.pointer?.point)
|
||||
// }
|
||||
this.setStatus(TLDrawStatus.Idle)
|
||||
}
|
||||
|
||||
|
@ -2405,7 +2404,7 @@ export class TLDrawState extends StateManager<Data> {
|
|||
}
|
||||
}
|
||||
|
||||
onDoubleClickCanvas: TLCanvasEventHandler = (info) => {
|
||||
onDoubleClickCanvas: TLCanvasEventHandler = () => {
|
||||
// Unused
|
||||
switch (this.appState.status.current) {
|
||||
case TLDrawStatus.Idle: {
|
||||
|
@ -2704,31 +2703,24 @@ export class TLDrawState extends StateManager<Data> {
|
|||
// Unused
|
||||
}
|
||||
|
||||
onTextChange = (id: string, text: string) => {
|
||||
this.updateTextSession(text)
|
||||
onShapeChange = (shape: { id: string } & Partial<TLDrawShape>) => {
|
||||
switch (shape.type) {
|
||||
case TLDrawShapeType.Text: {
|
||||
this.updateTextSession(shape.text || '')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
onTextBlur = (id: string) => {
|
||||
onShapeBlur = () => {
|
||||
switch (this.appState.status.current) {
|
||||
case TLDrawStatus.EditingText: {
|
||||
this.completeSession()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
onTextFocus = (id: string) => {
|
||||
// Unused
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
onTextKeyDown = (id: string, key: string) => {
|
||||
// Unused
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
onTextKeyUp = (id: string, key: string) => {
|
||||
// Unused
|
||||
}
|
||||
|
||||
onChange = (ids: string[]) => {
|
||||
onRenderCountChange = (ids: string[]) => {
|
||||
const appState = this.getAppState()
|
||||
if (appState.isEmptyCanvas && ids.length > 0) {
|
||||
this.patchState(
|
||||
|
@ -2754,8 +2746,4 @@ export class TLDrawState extends StateManager<Data> {
|
|||
onError = () => {
|
||||
// TODO
|
||||
}
|
||||
|
||||
onBlurEditingShape = () => {
|
||||
this.completeSession()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
import type { TLBinding, TLRenderInfo } from '@tldraw/core'
|
||||
import type { TLBinding, TLShapeProps } from '@tldraw/core'
|
||||
import { TLShape, TLShapeUtil, TLHandle } from '@tldraw/core'
|
||||
import type { TLPage, TLPageState } from '@tldraw/core'
|
||||
import type { StoreApi } from 'zustand'
|
||||
|
@ -32,7 +32,11 @@ export interface TLDrawMeta {
|
|||
isDarkMode: boolean
|
||||
}
|
||||
|
||||
export type TLDrawRenderInfo = TLRenderInfo<TLDrawMeta>
|
||||
export type TLDrawShapeProps<T extends TLDrawShape, E extends Element> = TLShapeProps<
|
||||
T,
|
||||
E,
|
||||
TLDrawMeta
|
||||
>
|
||||
|
||||
export interface Data {
|
||||
document: TLDrawDocument
|
||||
|
@ -171,6 +175,7 @@ export interface ArrowShape extends TLDrawBaseShape {
|
|||
middle?: Decoration
|
||||
}
|
||||
}
|
||||
|
||||
export interface EllipseShape extends TLDrawBaseShape {
|
||||
type: TLDrawShapeType.Ellipse
|
||||
radius: number[]
|
||||
|
|
Loading…
Reference in a new issue