Renaming types, shape utils, tools (#1513)

This PR renames all exported types to include the `TL` prefix. It also
removes the `TL` prefix from things that are not types, including:
- shape utils (e.g. `TLArrowUtil` becomes `ArrowShapeUtil`)
- tools (e.g. `TLArrowTool` becomes `ArrowShapeTool`, `TLSelectTool`
becomes `SelectTool`)

### Change Type

- [x] `major` — Breaking Change

### Release Notes

- Renaming of types, shape utils, tools
This commit is contained in:
Steve Ruiz 2023-06-04 11:38:53 +01:00 committed by GitHub
parent 10bb677035
commit 0f89309604
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
175 changed files with 1804 additions and 1921 deletions

View file

@ -0,0 +1,9 @@
import { BaseBoxShapeTool } from '@tldraw/tldraw'
// Extending the base box shape tool gives us a lot of functionality for free.
export class CardShapeTool extends BaseBoxShapeTool {
static override id = 'card'
static override initial = 'idle'
override shapeType = 'card'
}

View file

@ -1,7 +1,7 @@
import { HTMLContainer, TLBoxUtil } from '@tldraw/tldraw' import { BaseBoxShapeUtil, HTMLContainer } from '@tldraw/tldraw'
import { CardShape } from './CardShape' import { CardShape } from './CardShape'
export class CardUtil extends TLBoxUtil<CardShape> { export class CardShapeUtil extends BaseBoxShapeUtil<CardShape> {
// Id — the shape util's id // Id — the shape util's id
static override type = 'card' as const static override type = 'card' as const

View file

@ -1,13 +0,0 @@
// Tool
// ----
// Because the card tool can be just a rectangle, we can extend the
import { TLBoxTool } from '@tldraw/tldraw'
// TLBoxTool class. This gives us a lot of functionality for free.
export class CardTool extends TLBoxTool {
static override id = 'card'
static override initial = 'idle'
override shapeType = 'card'
}

View file

@ -1,11 +1,11 @@
import { TLUiMenuGroup, Tldraw, menuItem, toolbarItem } from '@tldraw/tldraw' import { TLUiMenuGroup, Tldraw, menuItem, toolbarItem } from '@tldraw/tldraw'
import '@tldraw/tldraw/editor.css' import '@tldraw/tldraw/editor.css'
import '@tldraw/tldraw/ui.css' import '@tldraw/tldraw/ui.css'
import { CardTool } from './CardTool' import { CardShapeTool } from './CardShapeTool'
import { CardUtil } from './CardUtil' import { CardShapeUtil } from './CardShapeUtil'
const shapes = { card: { util: CardUtil } } const shapes = { card: { util: CardShapeUtil } }
const tools = [CardTool] const tools = [CardShapeTool]
export default function CustomConfigExample() { export default function CustomConfigExample() {
return ( return (

View file

@ -1,11 +1,11 @@
import { createShapeId, Tldraw } from '@tldraw/tldraw' import { createShapeId, Tldraw } from '@tldraw/tldraw'
import '@tldraw/tldraw/editor.css' import '@tldraw/tldraw/editor.css'
import '@tldraw/tldraw/ui.css' import '@tldraw/tldraw/ui.css'
import { ErrorUtil } from './ErrorUtil' import { ErrorShapeUtil } from './ErrorShapeUtil'
const shapes = { const shapes = {
error: { error: {
util: ErrorUtil, // a custom shape that will always error util: ErrorShapeUtil, // a custom shape that will always error
}, },
} }

View file

@ -1,7 +1,7 @@
import { TLBoxUtil } from '@tldraw/tldraw' import { BaseBoxShapeUtil } from '@tldraw/tldraw'
import { ErrorShape } from './ErrorShape' import { ErrorShape } from './ErrorShape'
export class ErrorUtil extends TLBoxUtil<ErrorShape> { export class ErrorShapeUtil extends BaseBoxShapeUtil<ErrorShape> {
static override type = 'error' static override type = 'error'
override type = 'error' as const override type = 'error' as const

File diff suppressed because it is too large Load diff

View file

@ -1,16 +1,8 @@
// Important! don't move this tlschema re-export to lib/index.ts, doing so causes esbuild to produce // Important! don't move this tlschema re-export to lib/index.ts, doing so causes esbuild to produce
// incorrect output. https://github.com/evanw/esbuild/issues/1737 // incorrect output. https://github.com/evanw/esbuild/issues/1737
export { // eslint-disable-next-line local/no-export-star
getIndexAbove, export * from '@tldraw/indices'
getIndexBelow,
getIndexBetween,
getIndices,
getIndicesAbove,
getIndicesBelow,
getIndicesBetween,
sortByIndex,
} from '@tldraw/indices'
// eslint-disable-next-line local/no-export-star // eslint-disable-next-line local/no-export-star
export * from '@tldraw/tlschema' export * from '@tldraw/tlschema'
export { getHashForString } from '@tldraw/utils' export { getHashForString } from '@tldraw/utils'
@ -20,56 +12,53 @@ export {
TldrawEditor, TldrawEditor,
type TldrawEditorProps, type TldrawEditorProps,
} from './lib/TldrawEditor' } from './lib/TldrawEditor'
export { Editor, type TLAnimationOptions, type TLEditorOptions } from './lib/app/Editor'
export { ArrowShapeUtil } from './lib/app/shapeutils/ArrowShapeUtil/ArrowShapeUtil'
export { BaseBoxShapeUtil, type TLBaseBoxShape } from './lib/app/shapeutils/BaseBoxShapeUtil'
export { BookmarkShapeUtil } from './lib/app/shapeutils/BookmarkShapeUtil/BookmarkShapeUtil'
export { DrawShapeUtil } from './lib/app/shapeutils/DrawShapeUtil/DrawShapeUtil'
export { EmbedShapeUtil } from './lib/app/shapeutils/EmbedShapeUtil/EmbedShapeUtil'
export { FrameShapeUtil } from './lib/app/shapeutils/FrameShapeUtil/FrameShapeUtil'
export { GeoShapeUtil } from './lib/app/shapeutils/GeoShapeUtil/GeoShapeUtil'
export { GroupShapeUtil } from './lib/app/shapeutils/GroupShapeUtil/GroupShapeUtil'
export { HighlightShapeUtil } from './lib/app/shapeutils/HighlightShapeUtil/HighlightShapeUtil'
export { ImageShapeUtil } from './lib/app/shapeutils/ImageShapeUtil/ImageShapeUtil'
export { export {
Editor, LineShapeUtil,
isShapeWithHandles, getSplineForLineShape,
type AnimationOptions, } from './lib/app/shapeutils/LineShapeUtil/LineShapeUtil'
type AppOptions, export { NoteShapeUtil } from './lib/app/shapeutils/NoteShapeUtil/NoteShapeUtil'
type TLChange,
} from './lib/app/Editor'
export { TLArrowUtil } from './lib/app/shapeutils/TLArrowUtil/TLArrowUtil'
export { TLBookmarkUtil } from './lib/app/shapeutils/TLBookmarkUtil/TLBookmarkUtil'
export { TLBoxUtil } from './lib/app/shapeutils/TLBoxUtil'
export { TLDrawUtil } from './lib/app/shapeutils/TLDrawUtil/TLDrawUtil'
export { TLEmbedUtil } from './lib/app/shapeutils/TLEmbedUtil/TLEmbedUtil'
export { TLFrameUtil } from './lib/app/shapeutils/TLFrameUtil/TLFrameUtil'
export { TLGeoUtil } from './lib/app/shapeutils/TLGeoUtil/TLGeoUtil'
export { TLGroupUtil } from './lib/app/shapeutils/TLGroupUtil/TLGroupUtil'
export { TLHighlightUtil } from './lib/app/shapeutils/TLHighlightUtil/TLHighlightUtil'
export { TLImageUtil } from './lib/app/shapeutils/TLImageUtil/TLImageUtil'
export { TLLineUtil, getSplineForLineShape } from './lib/app/shapeutils/TLLineUtil/TLLineUtil'
export { TLNoteUtil } from './lib/app/shapeutils/TLNoteUtil/TLNoteUtil'
export { export {
TLShapeUtil, ShapeUtil,
type OnBeforeCreateHandler, type TLOnBeforeCreateHandler,
type OnBeforeUpdateHandler, type TLOnBeforeUpdateHandler,
type OnBindingChangeHandler, type TLOnBindingChangeHandler,
type OnChildrenChangeHandler, type TLOnChildrenChangeHandler,
type OnClickHandler, type TLOnClickHandler,
type OnDoubleClickHandleHandler, type TLOnDoubleClickHandleHandler,
type OnDoubleClickHandler, type TLOnDoubleClickHandler,
type OnDragHandler, type TLOnDragHandler,
type OnEditEndHandler, type TLOnEditEndHandler,
type OnHandleChangeHandler, type TLOnHandleChangeHandler,
type OnResizeEndHandler, type TLOnResizeEndHandler,
type OnResizeHandler, type TLOnResizeHandler,
type OnResizeStartHandler, type TLOnResizeStartHandler,
type OnRotateEndHandler, type TLOnRotateEndHandler,
type OnRotateHandler, type TLOnRotateHandler,
type OnRotateStartHandler, type TLOnRotateStartHandler,
type OnTranslateEndHandler, type TLOnTranslateEndHandler,
type OnTranslateHandler, type TLOnTranslateHandler,
type OnTranslateStartHandler, type TLOnTranslateStartHandler,
type TLResizeInfo, type TLResizeInfo,
type TLResizeMode, type TLResizeMode,
type TLShapeUtilConstructor, type TLShapeUtilConstructor,
type TLShapeUtilFlag, type TLShapeUtilFlag,
} from './lib/app/shapeutils/TLShapeUtil' } from './lib/app/shapeutils/ShapeUtil'
export { INDENT, TLTextUtil } from './lib/app/shapeutils/TLTextUtil/TLTextUtil' export { INDENT, TextShapeUtil } from './lib/app/shapeutils/TextShapeUtil/TextShapeUtil'
export { TLVideoUtil } from './lib/app/shapeutils/TLVideoUtil/TLVideoUtil' export { VideoShapeUtil } from './lib/app/shapeutils/VideoShapeUtil/VideoShapeUtil'
export { StateNode, type StateNodeConstructor } from './lib/app/statechart/StateNode' export { BaseBoxShapeTool } from './lib/app/tools/BaseBoxShapeTool/BaseBoxShapeTool'
export { TLBoxTool, type TLBoxLike } from './lib/app/statechart/TLBoxTool/TLBoxTool' export { StateNode, type TLStateNodeConstructor } from './lib/app/tools/StateNode'
export { type ClipboardPayload, type TLClipboardModel } from './lib/app/types/clipboard-types' export { type TLContent } from './lib/app/types/clipboard-types'
export { type TLEventMap, type TLEventMapHandler } from './lib/app/types/emit-types' export { type TLEventMap, type TLEventMapHandler } from './lib/app/types/emit-types'
export { export {
EVENT_NAME_MAP, EVENT_NAME_MAP,
@ -81,9 +70,11 @@ export {
type TLClickEventInfo, type TLClickEventInfo,
type TLCompleteEvent, type TLCompleteEvent,
type TLCompleteEventInfo, type TLCompleteEventInfo,
type TLEnterEventHandler,
type TLEventHandlers, type TLEventHandlers,
type TLEventInfo, type TLEventInfo,
type TLEventName, type TLEventName,
type TLExitEventHandler,
type TLInterruptEvent, type TLInterruptEvent,
type TLInterruptEventInfo, type TLInterruptEventInfo,
type TLKeyboardEvent, type TLKeyboardEvent,
@ -99,30 +90,28 @@ export {
type TLTickEvent, type TLTickEvent,
type TLWheelEvent, type TLWheelEvent,
type TLWheelEventInfo, type TLWheelEventInfo,
type UiEnterHandler,
type UiEvent, type UiEvent,
type UiEventType, type UiEventType,
type UiExitHandler,
} from './lib/app/types/event-types' } from './lib/app/types/event-types'
export { export {
type TLCommand, type TLCommand,
type TLCommandHandler, type TLCommandHandler,
type TLHistoryEntry, type TLHistoryEntry,
type TLMark, type TLHistoryMark,
} from './lib/app/types/history-types' } from './lib/app/types/history-types'
export { type RequiredKeys, type TLEasingType } from './lib/app/types/misc-types' export { type RequiredKeys } from './lib/app/types/misc-types'
export { type TLResizeHandle, type TLSelectionHandle } from './lib/app/types/selection-types' export { type TLResizeHandle, type TLSelectionHandle } from './lib/app/types/selection-types'
export { export {
defaultEditorAssetUrls, defaultEditorAssetUrls,
setDefaultEditorAssetUrls, setDefaultEditorAssetUrls,
type EditorAssetUrls, type TLEditorAssetUrls,
} from './lib/assetUrls' } from './lib/assetUrls'
export { Canvas } from './lib/components/Canvas' export { Canvas } from './lib/components/Canvas'
export { DefaultErrorFallback } from './lib/components/DefaultErrorFallback' export { DefaultErrorFallback } from './lib/components/DefaultErrorFallback'
export { export {
ErrorBoundary, ErrorBoundary,
OptionalErrorBoundary, OptionalErrorBoundary,
type ErrorBoundaryProps, type TLErrorBoundaryProps,
} from './lib/components/ErrorBoundary' } from './lib/components/ErrorBoundary'
export { HTMLContainer, type HTMLContainerProps } from './lib/components/HTMLContainer' export { HTMLContainer, type HTMLContainerProps } from './lib/components/HTMLContainer'
export { SVGContainer, type SVGContainerProps } from './lib/components/SVGContainer' export { SVGContainer, type SVGContainerProps } from './lib/components/SVGContainer'
@ -227,7 +216,7 @@ export {
getEmbedInfoUnsafely, getEmbedInfoUnsafely,
matchEmbedUrl, matchEmbedUrl,
matchUrl, matchUrl,
type EmbedResult, type TLEmbedResult,
} from './lib/utils/embeds' } from './lib/utils/embeds'
export { export {
downloadDataURLAsFile, downloadDataURLAsFile,
@ -245,11 +234,6 @@ export { hardResetEditor } from './lib/utils/hard-reset'
export { isAnimated, isGIF } from './lib/utils/is-gif-animated' export { isAnimated, isGIF } from './lib/utils/is-gif-animated'
export { setPropsForNextShape } from './lib/utils/props-for-next-shape' export { setPropsForNextShape } from './lib/utils/props-for-next-shape'
export { refreshPage } from './lib/utils/refresh-page' export { refreshPage } from './lib/utils/refresh-page'
export {
applyRotationToSnapshotShapes,
getRotationSnapshot,
type RotationSnapshot,
} from './lib/utils/rotation'
export { runtime, setRuntimeOverrides } from './lib/utils/runtime' export { runtime, setRuntimeOverrides } from './lib/utils/runtime'
export { export {
blobAsString, blobAsString,
@ -258,7 +242,7 @@ export {
defaultEmptyAs, defaultEmptyAs,
} from './lib/utils/string' } from './lib/utils/string'
export { getPointerInfo, getSvgPathFromStroke, getSvgPathFromStrokePoints } from './lib/utils/svg' export { getPointerInfo, getSvgPathFromStroke, getSvgPathFromStrokePoints } from './lib/utils/svg'
export { type StoreWithStatus } from './lib/utils/sync/StoreWithStatus' export { type TLStoreWithStatus } from './lib/utils/sync/StoreWithStatus'
export { hardReset } from './lib/utils/sync/hardReset' export { hardReset } from './lib/utils/sync/hardReset'
export { TAB_ID } from './lib/utils/sync/persistence-constants' export { TAB_ID } from './lib/utils/sync/persistence-constants'
export { openWindow } from './lib/utils/window-open' export { openWindow } from './lib/utils/window-open'

View file

@ -3,11 +3,11 @@ import { TLAsset, TLInstanceId, TLRecord, TLStore } from '@tldraw/tlschema'
import { annotateError } from '@tldraw/utils' import { annotateError } from '@tldraw/utils'
import React, { memo, useCallback, useLayoutEffect, useState, useSyncExternalStore } from 'react' import React, { memo, useCallback, useLayoutEffect, useState, useSyncExternalStore } from 'react'
import { Editor } from './app/Editor' import { Editor } from './app/Editor'
import { StateNodeConstructor } from './app/statechart/StateNode' import { TLStateNodeConstructor } from './app/tools/StateNode'
import { EditorAssetUrls, defaultEditorAssetUrls } from './assetUrls' import { TLEditorAssetUrls, defaultEditorAssetUrls } from './assetUrls'
import { DefaultErrorFallback } from './components/DefaultErrorFallback' import { DefaultErrorFallback } from './components/DefaultErrorFallback'
import { OptionalErrorBoundary } from './components/ErrorBoundary' import { OptionalErrorBoundary } from './components/ErrorBoundary'
import { ShapeInfo } from './config/createTLStore' import { TLShapeInfo } from './config/createTLStore'
import { ContainerProvider, useContainer } from './hooks/useContainer' import { ContainerProvider, useContainer } from './hooks/useContainer'
import { useCursor } from './hooks/useCursor' import { useCursor } from './hooks/useCursor'
import { useDarkMode } from './hooks/useDarkMode' import { useDarkMode } from './hooks/useDarkMode'
@ -23,7 +23,7 @@ import { useLocalStore } from './hooks/useLocalStore'
import { usePreloadAssets } from './hooks/usePreloadAssets' import { usePreloadAssets } from './hooks/usePreloadAssets'
import { useSafariFocusOutFix } from './hooks/useSafariFocusOutFix' import { useSafariFocusOutFix } from './hooks/useSafariFocusOutFix'
import { useZoomCss } from './hooks/useZoomCss' import { useZoomCss } from './hooks/useZoomCss'
import { StoreWithStatus } from './utils/sync/StoreWithStatus' import { TLStoreWithStatus } from './utils/sync/StoreWithStatus'
import { TAB_ID } from './utils/sync/persistence-constants' import { TAB_ID } from './utils/sync/persistence-constants'
/** @public */ /** @public */
@ -32,15 +32,15 @@ export type TldrawEditorProps = {
/** /**
* An array of shape utils to use in the editor. * An array of shape utils to use in the editor.
*/ */
shapes?: Record<string, ShapeInfo> shapes?: Record<string, TLShapeInfo>
/** /**
* An array of tools to use in the editor. * An array of tools to use in the editor.
*/ */
tools?: StateNodeConstructor[] tools?: TLStateNodeConstructor[]
/** /**
* Urls for where to find fonts and other assets. * Urls for where to find fonts and other assets.
*/ */
assetUrls?: EditorAssetUrls assetUrls?: TLEditorAssetUrls
/** /**
* Whether to automatically focus the editor when it mounts. * Whether to automatically focus the editor when it mounts.
*/ */
@ -102,7 +102,7 @@ export type TldrawEditorProps = {
* The Store instance to use for keeping the editor's data. This may be prepopulated, e.g. by loading * The Store instance to use for keeping the editor's data. This may be prepopulated, e.g. by loading
* from a server or database. * from a server or database.
*/ */
store: TLStore | StoreWithStatus store: TLStore | TLStoreWithStatus
} }
| { | {
store?: undefined store?: undefined
@ -190,7 +190,7 @@ const TldrawEditorWithLoadingStore = memo(function TldrawEditorBeforeLoading({
store, store,
assetUrls, assetUrls,
...rest ...rest
}: TldrawEditorProps & { store: StoreWithStatus }) { }: TldrawEditorProps & { store: TLStoreWithStatus }) {
const { done: preloadingComplete, error: preloadingError } = usePreloadAssets( const { done: preloadingComplete, error: preloadingError } = usePreloadAssets(
assetUrls ?? defaultEditorAssetUrls assetUrls ?? defaultEditorAssetUrls
) )

View file

@ -21,7 +21,7 @@ import {
intersectPolygonPolygon, intersectPolygonPolygon,
pointInPolygon, pointInPolygon,
} from '@tldraw/primitives' } from '@tldraw/primitives'
import { ComputedCache, HistoryEntry, RecordType, UnknownRecord } from '@tldraw/store' import { ComputedCache, RecordType } from '@tldraw/store'
import { import {
Box2dModel, Box2dModel,
CameraRecordType, CameraRecordType,
@ -77,7 +77,7 @@ import {
import { EventEmitter } from 'eventemitter3' import { EventEmitter } from 'eventemitter3'
import { nanoid } from 'nanoid' import { nanoid } from 'nanoid'
import { EMPTY_ARRAY, atom, computed, transact } from 'signia' import { EMPTY_ARRAY, atom, computed, transact } from 'signia'
import { ShapeInfo } from '../config/createTLStore' import { TLShapeInfo } from '../config/createTLStore'
import { TLUser, createTLUser } from '../config/createTLUser' import { TLUser, createTLUser } from '../config/createTLUser'
import { coreShapes, defaultShapes } from '../config/defaultShapes' import { coreShapes, defaultShapes } from '../config/defaultShapes'
import { defaultTools } from '../config/defaultTools' import { defaultTools } from '../config/defaultTools'
@ -122,43 +122,40 @@ import { SnapManager } from './managers/SnapManager'
import { TextManager } from './managers/TextManager' import { TextManager } from './managers/TextManager'
import { TickManager } from './managers/TickManager' import { TickManager } from './managers/TickManager'
import { UserPreferencesManager } from './managers/UserPreferencesManager' import { UserPreferencesManager } from './managers/UserPreferencesManager'
import { TLArrowUtil } from './shapeutils/TLArrowUtil/TLArrowUtil' import { ArrowShapeUtil } from './shapeutils/ArrowShapeUtil/ArrowShapeUtil'
import { getCurvedArrowInfo } from './shapeutils/TLArrowUtil/arrow/curved-arrow' import { getCurvedArrowInfo } from './shapeutils/ArrowShapeUtil/arrow/curved-arrow'
import { import {
getArrowTerminalsInArrowSpace, getArrowTerminalsInArrowSpace,
getIsArrowStraight, getIsArrowStraight,
} from './shapeutils/TLArrowUtil/arrow/shared' } from './shapeutils/ArrowShapeUtil/arrow/shared'
import { getStraightArrowInfo } from './shapeutils/TLArrowUtil/arrow/straight-arrow' import { getStraightArrowInfo } from './shapeutils/ArrowShapeUtil/arrow/straight-arrow'
import { TLFrameUtil } from './shapeutils/TLFrameUtil/TLFrameUtil' import { FrameShapeUtil } from './shapeutils/FrameShapeUtil/FrameShapeUtil'
import { TLGroupUtil } from './shapeutils/TLGroupUtil/TLGroupUtil' import { GroupShapeUtil } from './shapeutils/GroupShapeUtil/GroupShapeUtil'
import { TLResizeMode, TLShapeUtil } from './shapeutils/TLShapeUtil' import { ShapeUtil, TLResizeMode } from './shapeutils/ShapeUtil'
import { TLTextUtil } from './shapeutils/TLTextUtil/TLTextUtil' import { TextShapeUtil } from './shapeutils/TextShapeUtil/TextShapeUtil'
import { TLExportColors } from './shapeutils/shared/TLExportColors' import { TLExportColors } from './shapeutils/shared/TLExportColors'
import { RootState } from './statechart/RootState' import { RootState } from './tools/RootState'
import { StateNode, StateNodeConstructor } from './statechart/StateNode' import { StateNode, TLStateNodeConstructor } from './tools/StateNode'
import { TLClipboardModel } from './types/clipboard-types' import { TLContent } from './types/clipboard-types'
import { TLEventMap } from './types/emit-types' import { TLEventMap } from './types/emit-types'
import { TLEventInfo, TLPinchEventInfo, TLPointerEventInfo } from './types/event-types' import { TLEventInfo, TLPinchEventInfo, TLPointerEventInfo } from './types/event-types'
import { RequiredKeys } from './types/misc-types' import { RequiredKeys } from './types/misc-types'
import { TLResizeHandle } from './types/selection-types' import { TLResizeHandle } from './types/selection-types'
/** @public */ /** @public */
export type TLChange<T extends UnknownRecord = any> = HistoryEntry<T> export type TLAnimationOptions = Partial<{
/** @public */
export type AnimationOptions = Partial<{
duration: number duration: number
easing: typeof EASINGS.easeInOutCubic easing: typeof EASINGS.easeInOutCubic
}> }>
/** @public */ /** @public */
export type ViewportOptions = Partial<{ export type TLViewportOptions = Partial<{
/** Whether to animate the viewport change or not. Defaults to true. */ /** Whether to animate the viewport change or not. Defaults to true. */
stopFollowing: boolean stopFollowing: boolean
}> }>
/** @public */ /** @public */
export interface AppOptions { export interface TLEditorOptions {
/** /**
* The Store instance to use for keeping the app's data. This may be prepopulated, e.g. by loading * The Store instance to use for keeping the app's data. This may be prepopulated, e.g. by loading
* from a server or database. * from a server or database.
@ -167,11 +164,11 @@ export interface AppOptions {
/** /**
* An array of shapes to use in the editor. These will be used to create and manage shapes in the editor. * An array of shapes to use in the editor. These will be used to create and manage shapes in the editor.
*/ */
shapes?: Record<string, ShapeInfo> shapes?: Record<string, TLShapeInfo>
/** /**
* An array of tools to use in the editor. These will be used to handle events and manage user interactions in the editor. * An array of tools to use in the editor. These will be used to handle events and manage user interactions in the editor.
*/ */
tools?: StateNodeConstructor[] tools?: TLStateNodeConstructor[]
/** /**
* A user defined externally to replace the default user. * A user defined externally to replace the default user.
*/ */
@ -183,11 +180,6 @@ export interface AppOptions {
getContainer: () => HTMLElement getContainer: () => HTMLElement
} }
/** @public */
export function isShapeWithHandles(shape: TLShape) {
return shape.type === 'arrow' || shape.type === 'line' || shape.type === 'draw'
}
/** @public */ /** @public */
export class Editor extends EventEmitter<TLEventMap> { export class Editor extends EventEmitter<TLEventMap> {
constructor({ constructor({
@ -196,7 +188,7 @@ export class Editor extends EventEmitter<TLEventMap> {
tools = defaultTools, tools = defaultTools,
shapes = defaultShapes, shapes = defaultShapes,
getContainer, getContainer,
}: AppOptions) { }: TLEditorOptions) {
super() super()
this.store = store this.store = store
@ -276,7 +268,7 @@ export class Editor extends EventEmitter<TLEventMap> {
this._updateDepth-- this._updateDepth--
} }
this.store.onAfterCreate = (record) => { this.store.onAfterCreate = (record) => {
if (record.typeName === 'shape' && this.isShapeOfType(record, TLArrowUtil)) { if (record.typeName === 'shape' && this.isShapeOfType(record, ArrowShapeUtil)) {
this._arrowDidUpdate(record) this._arrowDidUpdate(record)
} }
} }
@ -286,7 +278,7 @@ export class Editor extends EventEmitter<TLEventMap> {
this.disposables.add( this.disposables.add(
this.store.listen((changes) => { this.store.listen((changes) => {
this.emit('change', changes as TLChange) this.emit('change', changes)
}) })
) )
@ -948,7 +940,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* *
* @public * @public
*/ */
shapeUtils: { readonly [K in string]?: TLShapeUtil<TLUnknownShape> } shapeUtils: { readonly [K in string]?: ShapeUtil<TLUnknownShape> }
/** /**
* Get a shape util by its definition. * Get a shape util by its definition.
@ -956,13 +948,13 @@ export class Editor extends EventEmitter<TLEventMap> {
* @example * @example
* *
* ```ts * ```ts
* editor.getShapeUtil(TLArrowUtil) * editor.getShapeUtil(ArrowShapeUtil)
* ``` * ```
* *
* @param util - The shape util. * @param util - The shape util.
* @public * @public
*/ */
getShapeUtil<C extends { new (...args: any[]): TLShapeUtil<any>; type: string }>( getShapeUtil<C extends { new (...args: any[]): ShapeUtil<any>; type: string }>(
util: C util: C
): InstanceType<C> ): InstanceType<C>
/** /**
@ -972,18 +964,18 @@ export class Editor extends EventEmitter<TLEventMap> {
* *
* ```ts * ```ts
* const util = editor.getShapeUtil(myShape) * const util = editor.getShapeUtil(myShape)
* const util = editor.getShapeUtil<TLArrowUtil>(myShape) * const util = editor.getShapeUtil<ArrowShapeUtil>(myShape)
* const util = editor.getShapeUtil(TLArrowUtil) * const util = editor.getShapeUtil(ArrowShapeUtil)
* ``` * ```
* *
* @param shape - A shape or shape partial. * @param shape - A shape or shape partial.
* @public * @public
*/ */
getShapeUtil<S extends TLUnknownShape>(shape: S | TLShapePartial<S>): TLShapeUtil<S> getShapeUtil<S extends TLUnknownShape>(shape: S | TLShapePartial<S>): ShapeUtil<S>
getShapeUtil<T extends TLShapeUtil>({ getShapeUtil<T extends ShapeUtil>({
type, type,
}: { }: {
type: T extends TLShapeUtil<infer R> ? R['type'] : string type: T extends ShapeUtil<infer R> ? R['type'] : string
}): T { }): T {
return this.shapeUtils[type] as T return this.shapeUtils[type] as T
} }
@ -1422,7 +1414,7 @@ export class Editor extends EventEmitter<TLEventMap> {
/** @internal */ /** @internal */
private _shapeDidChange(prev: TLShape, next: TLShape) { private _shapeDidChange(prev: TLShape, next: TLShape) {
if (this.isShapeOfType(next, TLArrowUtil)) { if (this.isShapeOfType(next, ArrowShapeUtil)) {
this._arrowDidUpdate(next) this._arrowDidUpdate(next)
} }
@ -3204,7 +3196,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @example * @example
* *
* ```ts * ```ts
* const isArrowShape = isShapeOfType(someShape, TLArrowUtil) * const isArrowShape = isShapeOfType(someShape, ArrowShapeUtil)
* ``` * ```
* *
* @param util - the TLShapeUtil constructor to test against * @param util - the TLShapeUtil constructor to test against
@ -3214,7 +3206,7 @@ export class Editor extends EventEmitter<TLEventMap> {
*/ */
isShapeOfType<T extends TLUnknownShape>( isShapeOfType<T extends TLUnknownShape>(
shape: TLUnknownShape, shape: TLUnknownShape,
util: { new (...args: any): TLShapeUtil<T>; type: string } util: { new (...args: any): ShapeUtil<T>; type: string }
): shape is T { ): shape is T {
return shape.type === util.type return shape.type === util.type
} }
@ -4145,7 +4137,7 @@ export class Editor extends EventEmitter<TLEventMap> {
}) })
} }
getContent(ids: TLShapeId[] = this.selectedIds): TLClipboardModel | undefined { getContent(ids: TLShapeId[] = this.selectedIds): TLContent | undefined {
if (!ids) return if (!ids) return
if (ids.length === 0) return if (ids.length === 0) return
@ -4169,14 +4161,14 @@ export class Editor extends EventEmitter<TLEventMap> {
shape = structuredClone(shape) as typeof shape shape = structuredClone(shape) as typeof shape
if (this.isShapeOfType(shape, TLArrowUtil)) { if (this.isShapeOfType(shape, ArrowShapeUtil)) {
const startBindingId = const startBindingId =
shape.props.start.type === 'binding' ? shape.props.start.boundShapeId : undefined shape.props.start.type === 'binding' ? shape.props.start.boundShapeId : undefined
const endBindingId = const endBindingId =
shape.props.end.type === 'binding' ? shape.props.end.boundShapeId : undefined shape.props.end.type === 'binding' ? shape.props.end.boundShapeId : undefined
const info = this.getShapeUtil(TLArrowUtil).getArrowInfo(shape) const info = this.getShapeUtil(ArrowShapeUtil).getArrowInfo(shape)
if (shape.props.start.type === 'binding') { if (shape.props.start.type === 'binding') {
if (!shapes.some((s) => s.id === startBindingId)) { if (!shapes.some((s) => s.id === startBindingId)) {
@ -4281,7 +4273,7 @@ export class Editor extends EventEmitter<TLEventMap> {
/* --------------------- Commands --------------------- */ /* --------------------- Commands --------------------- */
putContent( putContent(
content: TLClipboardModel, content: TLContent,
options: { options: {
point?: VecLike point?: VecLike
select?: boolean select?: boolean
@ -4352,8 +4344,8 @@ export class Editor extends EventEmitter<TLEventMap> {
if (rootShapeIds.length === 1) { if (rootShapeIds.length === 1) {
const rootShape = shapes.find((s) => s.id === rootShapeIds[0])! const rootShape = shapes.find((s) => s.id === rootShapeIds[0])!
if ( if (
this.isShapeOfType(parent, TLFrameUtil) && this.isShapeOfType(parent, FrameShapeUtil) &&
this.isShapeOfType(rootShape, TLFrameUtil) && this.isShapeOfType(rootShape, FrameShapeUtil) &&
rootShape.props.w === parent?.props.w && rootShape.props.w === parent?.props.w &&
rootShape.props.h === parent?.props.h rootShape.props.h === parent?.props.h
) { ) {
@ -4409,7 +4401,7 @@ export class Editor extends EventEmitter<TLEventMap> {
index = getIndexAbove(index) index = getIndexAbove(index)
} }
if (this.isShapeOfType(newShape, TLArrowUtil)) { if (this.isShapeOfType(newShape, ArrowShapeUtil)) {
if (newShape.props.start.type === 'binding') { if (newShape.props.start.type === 'binding') {
const mappedId = idMap.get(newShape.props.start.boundShapeId) const mappedId = idMap.get(newShape.props.start.boundShapeId)
newShape.props.start = mappedId newShape.props.start = mappedId
@ -4564,7 +4556,7 @@ export class Editor extends EventEmitter<TLEventMap> {
while ( while (
this.getShapesAtPoint(point).some( this.getShapesAtPoint(point).some(
(shape) => (shape) =>
this.isShapeOfType(shape, TLFrameUtil) && this.isShapeOfType(shape, FrameShapeUtil) &&
shape.props.w === onlyRoot.props.w && shape.props.w === onlyRoot.props.w &&
shape.props.h === onlyRoot.props.h shape.props.h === onlyRoot.props.h
) )
@ -6382,7 +6374,7 @@ export class Editor extends EventEmitter<TLEventMap> {
const shapes = compact(ids.map((id) => this.getShapeById(id))).filter((shape) => { const shapes = compact(ids.map((id) => this.getShapeById(id))).filter((shape) => {
if (!shape) return false if (!shape) return false
if (this.isShapeOfType(shape, TLArrowUtil)) { if (this.isShapeOfType(shape, ArrowShapeUtil)) {
if (shape.props.start.type === 'binding' || shape.props.end.type === 'binding') { if (shape.props.start.type === 'binding' || shape.props.end.type === 'binding') {
return false return false
} }
@ -6514,7 +6506,7 @@ export class Editor extends EventEmitter<TLEventMap> {
.filter((shape) => { .filter((shape) => {
if (!shape) return false if (!shape) return false
if (this.isShapeOfType(shape, TLArrowUtil)) { if (this.isShapeOfType(shape, ArrowShapeUtil)) {
if (shape.props.start.type === 'binding' || shape.props.end.type === 'binding') { if (shape.props.start.type === 'binding' || shape.props.end.type === 'binding') {
return false return false
} }
@ -7340,7 +7332,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @param options - Options for setting the current page. * @param options - Options for setting the current page.
* @public * @public
*/ */
setCurrentPageId(pageId: TLPageId, { stopFollowing = true }: ViewportOptions = {}): this { setCurrentPageId(pageId: TLPageId, { stopFollowing = true }: TLViewportOptions = {}): this {
this._setCurrentPageId(pageId, { stopFollowing }) this._setCurrentPageId(pageId, { stopFollowing })
return this return this
} }
@ -7348,7 +7340,7 @@ export class Editor extends EventEmitter<TLEventMap> {
/** @internal */ /** @internal */
private _setCurrentPageId = this.history.createCommand( private _setCurrentPageId = this.history.createCommand(
'setCurrentPage', 'setCurrentPage',
(pageId: TLPageId, { stopFollowing = true }: ViewportOptions = {}) => { (pageId: TLPageId, { stopFollowing = true }: TLViewportOptions = {}) => {
if (!this.store.has(pageId)) { if (!this.store.has(pageId)) {
console.error("Tried to set the current page id to a page that doesn't exist.") console.error("Tried to set the current page id to a page that doesn't exist.")
return return
@ -7721,8 +7713,11 @@ export class Editor extends EventEmitter<TLEventMap> {
let newShape: TLShape = deepCopy(shape) let newShape: TLShape = deepCopy(shape)
if (this.isShapeOfType(shape, TLArrowUtil) && this.isShapeOfType(newShape, TLArrowUtil)) { if (
const info = this.getShapeUtil(TLArrowUtil).getArrowInfo(shape) this.isShapeOfType(shape, ArrowShapeUtil) &&
this.isShapeOfType(newShape, ArrowShapeUtil)
) {
const info = this.getShapeUtil(ArrowShapeUtil).getArrowInfo(shape)
let newStartShapeId: TLShapeId | undefined = undefined let newStartShapeId: TLShapeId | undefined = undefined
let newEndShapeId: TLShapeId | undefined = undefined let newEndShapeId: TLShapeId | undefined = undefined
@ -7944,7 +7939,7 @@ export class Editor extends EventEmitter<TLEventMap> {
if (boundsA.width !== boundsB.width) { if (boundsA.width !== boundsB.width) {
didChange = true didChange = true
if (this.isShapeOfType(shape, TLTextUtil)) { if (this.isShapeOfType(shape, TextShapeUtil)) {
switch (shape.props.align) { switch (shape.props.align) {
case 'middle': { case 'middle': {
change.x = currentShape.x + (boundsA.width - boundsB.width) / 2 change.x = currentShape.x + (boundsA.width - boundsB.width) / 2
@ -8044,7 +8039,7 @@ export class Editor extends EventEmitter<TLEventMap> {
x: number, x: number,
y: number, y: number,
z = this.camera.z, z = this.camera.z,
{ stopFollowing = true }: ViewportOptions = {} { stopFollowing = true }: TLViewportOptions = {}
) { ) {
this.stopCameraAnimation() this.stopCameraAnimation()
if (stopFollowing && this.instanceState.followingUserId) { if (stopFollowing && this.instanceState.followingUserId) {
@ -8080,7 +8075,7 @@ export class Editor extends EventEmitter<TLEventMap> {
x: number, x: number,
y: number, y: number,
z = this.camera.z, z = this.camera.z,
opts: AnimationOptions = DEFAULT_ANIMATION_OPTIONS opts: TLAnimationOptions = DEFAULT_ANIMATION_OPTIONS
) { ) {
x = Number.isNaN(x) ? 0 : x x = Number.isNaN(x) ? 0 : x
y = Number.isNaN(y) ? 0 : y y = Number.isNaN(y) ? 0 : y
@ -8112,7 +8107,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @param opts - The options for an animation. * @param opts - The options for an animation.
* @public * @public
*/ */
centerOnPoint(x: number, y: number, opts?: AnimationOptions): this { centerOnPoint(x: number, y: number, opts?: TLAnimationOptions): this {
if (!this.canMoveCamera) return this if (!this.canMoveCamera) return this
const { const {
@ -8161,7 +8156,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* *
* @public * @public
*/ */
zoomToFit(opts?: AnimationOptions): this { zoomToFit(opts?: TLAnimationOptions): this {
if (!this.canMoveCamera) return this if (!this.canMoveCamera) return this
const ids = [...this.shapeIds] const ids = [...this.shapeIds]
@ -8191,7 +8186,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @param opts - The options for an animation. * @param opts - The options for an animation.
* @public * @public
*/ */
resetZoom(point = this.viewportScreenCenter, opts?: AnimationOptions): this { resetZoom(point = this.viewportScreenCenter, opts?: TLAnimationOptions): this {
if (!this.canMoveCamera) return this if (!this.canMoveCamera) return this
const { x: cx, y: cy, z: cz } = this.camera const { x: cx, y: cy, z: cz } = this.camera
@ -8219,7 +8214,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @param opts - The options for an animation. * @param opts - The options for an animation.
* @public * @public
*/ */
zoomIn(point = this.viewportScreenCenter, opts?: AnimationOptions): this { zoomIn(point = this.viewportScreenCenter, opts?: TLAnimationOptions): this {
if (!this.canMoveCamera) return this if (!this.canMoveCamera) return this
const { x: cx, y: cy, z: cz } = this.camera const { x: cx, y: cy, z: cz } = this.camera
@ -8263,7 +8258,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @param opts - The options for an animation. * @param opts - The options for an animation.
* @public * @public
*/ */
zoomOut(point = this.viewportScreenCenter, opts?: AnimationOptions): this { zoomOut(point = this.viewportScreenCenter, opts?: TLAnimationOptions): this {
if (!this.canMoveCamera) return this if (!this.canMoveCamera) return this
const { x: cx, y: cy, z: cz } = this.camera const { x: cx, y: cy, z: cz } = this.camera
@ -8306,7 +8301,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @param opts - The options for an animation. * @param opts - The options for an animation.
* @public * @public
*/ */
zoomToSelection(opts?: AnimationOptions): this { zoomToSelection(opts?: TLAnimationOptions): this {
if (!this.canMoveCamera) return this if (!this.canMoveCamera) return this
const ids = this.selectedIds const ids = this.selectedIds
@ -8334,7 +8329,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @param opts - The options for an animation. * @param opts - The options for an animation.
* @public * @public
*/ */
panZoomIntoView(ids: TLShapeId[], opts?: AnimationOptions): this { panZoomIntoView(ids: TLShapeId[], opts?: TLAnimationOptions): this {
if (!this.canMoveCamera) return this if (!this.canMoveCamera) return this
if (ids.length <= 0) return this if (ids.length <= 0) return this
@ -8423,7 +8418,7 @@ export class Editor extends EventEmitter<TLEventMap> {
width: number, width: number,
height: number, height: number,
targetZoom?: number, targetZoom?: number,
opts?: AnimationOptions opts?: TLAnimationOptions
): this { ): this {
if (!this.canMoveCamera) return this if (!this.canMoveCamera) return this
@ -8476,7 +8471,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @param dy - The amount to pan on the y axis. * @param dy - The amount to pan on the y axis.
* @param opts - The animation options * @param opts - The animation options
*/ */
pan(dx: number, dy: number, opts?: AnimationOptions): this { pan(dx: number, dy: number, opts?: TLAnimationOptions): this {
if (!this.canMoveCamera) return this if (!this.canMoveCamera) return this
const { camera } = this const { camera } = this
@ -8556,7 +8551,7 @@ export class Editor extends EventEmitter<TLEventMap> {
} }
/** @internal */ /** @internal */
private _animateToViewport(targetViewportPage: Box2d, opts = {} as AnimationOptions) { private _animateToViewport(targetViewportPage: Box2d, opts = {} as TLAnimationOptions) {
const { duration = 0, easing = EASINGS.easeInOutCubic } = opts const { duration = 0, easing = EASINGS.easeInOutCubic } = opts
const { animationSpeed, viewportPageBounds } = this const { animationSpeed, viewportPageBounds } = this
@ -8776,7 +8771,7 @@ export class Editor extends EventEmitter<TLEventMap> {
return this return this
} }
animateToShape(shapeId: TLShapeId, opts: AnimationOptions = DEFAULT_ANIMATION_OPTIONS): this { animateToShape(shapeId: TLShapeId, opts: TLAnimationOptions = DEFAULT_ANIMATION_OPTIONS): this {
if (!this.canMoveCamera) return this if (!this.canMoveCamera) return this
const activeArea = getActiveAreaScreenSpace(this) const activeArea = getActiveAreaScreenSpace(this)
@ -9026,7 +9021,7 @@ export class Editor extends EventEmitter<TLEventMap> {
const groups: TLGroupShape[] = [] const groups: TLGroupShape[] = []
shapes.forEach((shape) => { shapes.forEach((shape) => {
if (this.isShapeOfType(shape, TLGroupUtil)) { if (this.isShapeOfType(shape, GroupShapeUtil)) {
groups.push(shape) groups.push(shape)
} else { } else {
idsToSelect.add(shape.id) idsToSelect.add(shape.id)

View file

@ -17,7 +17,7 @@ import { compact, dedupe, deepCopy } from '@tldraw/utils'
import { atom, computed, EMPTY_ARRAY } from 'signia' import { atom, computed, EMPTY_ARRAY } from 'signia'
import { uniqueId } from '../../utils/data' import { uniqueId } from '../../utils/data'
import type { Editor } from '../Editor' import type { Editor } from '../Editor'
import { getSplineForLineShape, TLLineUtil } from '../shapeutils/TLLineUtil/TLLineUtil' import { getSplineForLineShape, LineShapeUtil } from '../shapeutils/LineShapeUtil/LineShapeUtil'
export type PointsSnapLine = { export type PointsSnapLine = {
id: string id: string
@ -495,7 +495,7 @@ export class SnapManager {
// and then pass them to the snap function as 'additionalOutlines' // and then pass them to the snap function as 'additionalOutlines'
// First, let's find which handle we're dragging // First, let's find which handle we're dragging
const util = this.editor.getShapeUtil(TLLineUtil) const util = this.editor.getShapeUtil(LineShapeUtil)
const handles = util.handles(line).sort(sortByIndex) const handles = util.handles(line).sort(sortByIndex)
if (handles.length < 3) return { nudge: new Vec2d(0, 0) } if (handles.length < 3) return { nudge: new Vec2d(0, 0) }

View file

@ -1,7 +1,7 @@
import { Box2dModel, TLAlignType } from '@tldraw/tlschema' import { Box2dModel, TLAlignType } from '@tldraw/tlschema'
import { uniqueId } from '../../utils/data' import { uniqueId } from '../../utils/data'
import { Editor } from '../Editor' import { Editor } from '../Editor'
import { TextHelpers } from '../shapeutils/TLTextUtil/TextHelpers' import { TextHelpers } from '../shapeutils/TextShapeUtil/TextHelpers'
const textAlignmentsForLtr = { const textAlignmentsForLtr = {
start: 'left', start: 'left',
@ -12,9 +12,9 @@ const textAlignmentsForLtr = {
'end-legacy': 'right', 'end-legacy': 'right',
} }
type OverflowMode = 'wrap' | 'truncate-ellipsis' | 'truncate-clip' type TLOverflowMode = 'wrap' | 'truncate-ellipsis' | 'truncate-clip'
type MeasureTextSpanOpts = { type TLMeasureTextSpanOpts = {
overflow: OverflowMode overflow: TLOverflowMode
width: number width: number
height: number height: number
padding: number padding: number
@ -183,7 +183,7 @@ export class TextManager {
*/ */
measureTextSpans( measureTextSpans(
textToMeasure: string, textToMeasure: string,
opts: MeasureTextSpanOpts opts: TLMeasureTextSpanOpts
): { text: string; box: Box2dModel }[] { ): { text: string; box: Box2dModel }[] {
const shouldTruncateToFirstLine = const shouldTruncateToFirstLine =
opts.overflow === 'truncate-ellipsis' || opts.overflow === 'truncate-clip' opts.overflow === 'truncate-ellipsis' || opts.overflow === 'truncate-clip'

View file

@ -2,7 +2,7 @@ import { TAU } from '@tldraw/primitives'
import { TLArrowShape, TLArrowTerminal, TLShapeId, createShapeId } from '@tldraw/tlschema' import { TLArrowShape, TLArrowTerminal, TLShapeId, createShapeId } from '@tldraw/tlschema'
import { assert } from '@tldraw/utils' import { assert } from '@tldraw/utils'
import { TestEditor } from '../../../test/TestEditor' import { TestEditor } from '../../../test/TestEditor'
import { TLArrowUtil } from './TLArrowUtil' import { ArrowShapeUtil } from './ArrowShapeUtil'
let editor: TestEditor let editor: TestEditor
@ -299,7 +299,7 @@ describe('Other cases when arrow are moved', () => {
editor.setSelectedTool('arrow').pointerDown(1000, 1000).pointerMove(50, 350).pointerUp(50, 350) editor.setSelectedTool('arrow').pointerDown(1000, 1000).pointerMove(50, 350).pointerUp(50, 350)
let arrow = editor.shapesArray[editor.shapesArray.length - 1] let arrow = editor.shapesArray[editor.shapesArray.length - 1]
assert(editor.isShapeOfType(arrow, TLArrowUtil)) assert(editor.isShapeOfType(arrow, ArrowShapeUtil))
assert(arrow.props.end.type === 'binding') assert(arrow.props.end.type === 'binding')
expect(arrow.props.end.boundShapeId).toBe(ids.box3) expect(arrow.props.end.boundShapeId).toBe(ids.box3)
@ -308,7 +308,7 @@ describe('Other cases when arrow are moved', () => {
// arrow should still be bound to box3 // arrow should still be bound to box3
arrow = editor.getShapeById(arrow.id)! arrow = editor.getShapeById(arrow.id)!
assert(editor.isShapeOfType(arrow, TLArrowUtil)) assert(editor.isShapeOfType(arrow, ArrowShapeUtil))
assert(arrow.props.end.type === 'binding') assert(arrow.props.end.type === 'binding')
expect(arrow.props.end.boundShapeId).toBe(ids.box3) expect(arrow.props.end.boundShapeId).toBe(ids.box3)
}) })

View file

@ -26,18 +26,18 @@ import * as React from 'react'
import { computed, EMPTY_ARRAY } from 'signia' import { computed, EMPTY_ARRAY } from 'signia'
import { SVGContainer } from '../../../components/SVGContainer' import { SVGContainer } from '../../../components/SVGContainer'
import { ARROW_LABEL_FONT_SIZES, FONT_FAMILIES, TEXT_PROPS } from '../../../constants' import { ARROW_LABEL_FONT_SIZES, FONT_FAMILIES, TEXT_PROPS } from '../../../constants'
import {
ShapeUtil,
TLOnEditEndHandler,
TLOnHandleChangeHandler,
TLOnResizeHandler,
TLOnTranslateStartHandler,
TLShapeUtilFlag,
} from '../ShapeUtil'
import { createTextSvgElementFromSpans } from '../shared/createTextSvgElementFromSpans' import { createTextSvgElementFromSpans } from '../shared/createTextSvgElementFromSpans'
import { getPerfectDashProps } from '../shared/getPerfectDashProps' import { getPerfectDashProps } from '../shared/getPerfectDashProps'
import { getShapeFillSvg, ShapeFill } from '../shared/ShapeFill' import { getShapeFillSvg, ShapeFill } from '../shared/ShapeFill'
import { TLExportColors } from '../shared/TLExportColors' import { TLExportColors } from '../shared/TLExportColors'
import {
OnEditEndHandler,
OnHandleChangeHandler,
OnResizeHandler,
OnTranslateStartHandler,
TLShapeUtil,
TLShapeUtilFlag,
} from '../TLShapeUtil'
import { ArrowInfo } from './arrow/arrow-types' import { ArrowInfo } from './arrow/arrow-types'
import { getArrowheadPathForType } from './arrow/arrowheads' import { getArrowheadPathForType } from './arrow/arrowheads'
import { import {
@ -56,7 +56,7 @@ import { ArrowTextLabel } from './components/ArrowTextLabel'
let globalRenderIndex = 0 let globalRenderIndex = 0
/** @public */ /** @public */
export class TLArrowUtil extends TLShapeUtil<TLArrowShape> { export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
static override type = 'arrow' static override type = 'arrow'
override canEdit = () => true override canEdit = () => true
@ -245,7 +245,7 @@ export class TLArrowUtil extends TLShapeUtil<TLArrowShape> {
] ]
} }
onHandleChange: OnHandleChangeHandler<TLArrowShape> = (shape, { handle, isPrecise }) => { onHandleChange: TLOnHandleChangeHandler<TLArrowShape> = (shape, { handle, isPrecise }) => {
const next = deepCopy(shape) const next = deepCopy(shape)
switch (handle.id) { switch (handle.id) {
@ -376,7 +376,7 @@ export class TLArrowUtil extends TLShapeUtil<TLArrowShape> {
return next return next
} }
onTranslateStart: OnTranslateStartHandler<TLArrowShape> = (shape) => { onTranslateStart: TLOnTranslateStartHandler<TLArrowShape> = (shape) => {
let startBinding: TLShapeId | null = let startBinding: TLShapeId | null =
shape.props.start.type === 'binding' ? shape.props.start.boundShapeId : null shape.props.start.type === 'binding' ? shape.props.start.boundShapeId : null
let endBinding: TLShapeId | null = let endBinding: TLShapeId | null =
@ -416,7 +416,7 @@ export class TLArrowUtil extends TLShapeUtil<TLArrowShape> {
} }
} }
onResize: OnResizeHandler<TLArrowShape> = (shape, info) => { onResize: TLOnResizeHandler<TLArrowShape> = (shape, info) => {
const { scaleX, scaleY } = info const { scaleX, scaleY } = info
const terminals = getArrowTerminalsInArrowSpace(this.editor, shape) const terminals = getArrowTerminalsInArrowSpace(this.editor, shape)
@ -900,7 +900,7 @@ export class TLArrowUtil extends TLShapeUtil<TLArrowShape> {
return this.getLabelBounds(shape) ?? new Box2d() return this.getLabelBounds(shape) ?? new Box2d()
} }
onEditEnd: OnEditEndHandler<TLArrowShape> = (shape) => { onEditEnd: TLOnEditEndHandler<TLArrowShape> = (shape) => {
const { const {
id, id,
type, type,

View file

@ -1,7 +1,7 @@
import { Matrix2d, Vec2d } from '@tldraw/primitives' import { Matrix2d, Vec2d } from '@tldraw/primitives'
import { TLArrowShape, TLArrowTerminal, TLShape } from '@tldraw/tlschema' import { TLArrowShape, TLArrowTerminal, TLShape } from '@tldraw/tlschema'
import { Editor } from '../../../Editor' import { Editor } from '../../../Editor'
import { TLShapeUtil } from '../../TLShapeUtil' import { ShapeUtil } from '../../ShapeUtil'
export function getIsArrowStraight(shape: TLArrowShape) { export function getIsArrowStraight(shape: TLArrowShape) {
return Math.abs(shape.props.bend) < 8 // snap to +-8px return Math.abs(shape.props.bend) < 8 // snap to +-8px
@ -9,7 +9,7 @@ export function getIsArrowStraight(shape: TLArrowShape) {
export type BoundShapeInfo<T extends TLShape = TLShape> = { export type BoundShapeInfo<T extends TLShape = TLShape> = {
shape: T shape: T
util: TLShapeUtil<T> util: ShapeUtil<T>
didIntersect: boolean didIntersect: boolean
isExact: boolean isExact: boolean
transform: Matrix2d transform: Matrix2d

View file

@ -3,7 +3,7 @@ import { TLArrowShape, TLShapeId } from '@tldraw/tlschema'
import * as React from 'react' import * as React from 'react'
import { ARROW_LABEL_FONT_SIZES, TEXT_PROPS } from '../../../../constants' import { ARROW_LABEL_FONT_SIZES, TEXT_PROPS } from '../../../../constants'
import { stopEventPropagation } from '../../../../utils/dom' import { stopEventPropagation } from '../../../../utils/dom'
import { TextHelpers } from '../../TLTextUtil/TextHelpers' import { TextHelpers } from '../../TextShapeUtil/TextHelpers'
import { useEditableText } from '../../shared/useEditableText' import { useEditableText } from '../../shared/useEditableText'
export const ArrowTextLabel = React.memo(function ArrowTextLabel({ export const ArrowTextLabel = React.memo(function ArrowTextLabel({

View file

@ -1,10 +1,13 @@
import { Box2d, linesIntersect, pointInPolygon, Vec2d, VecLike } from '@tldraw/primitives' import { Box2d, linesIntersect, pointInPolygon, Vec2d, VecLike } from '@tldraw/primitives'
import { TLBoxLike } from '../statechart/TLBoxTool/TLBoxTool' import { TLBaseShape } from '@tldraw/tlschema'
import { ShapeUtil, TLOnResizeHandler } from './ShapeUtil'
import { resizeBox } from './shared/resizeBox' import { resizeBox } from './shared/resizeBox'
import { OnResizeHandler, TLShapeUtil } from './TLShapeUtil'
/** @public */ /** @public */
export abstract class TLBoxUtil<Shape extends TLBoxLike> extends TLShapeUtil<Shape> { export type TLBaseBoxShape = TLBaseShape<string, { w: number; h: number }>
/** @public */
export abstract class BaseBoxShapeUtil<Shape extends TLBaseBoxShape> extends ShapeUtil<Shape> {
override getBounds(shape: Shape) { override getBounds(shape: Shape) {
return new Box2d(0, 0, shape.props.w, shape.props.h) return new Box2d(0, 0, shape.props.w, shape.props.h)
} }
@ -33,7 +36,7 @@ export abstract class TLBoxUtil<Shape extends TLBoxLike> extends TLShapeUtil<Sha
return false return false
} }
onResize: OnResizeHandler<any> = (shape, info) => { onResize: TLOnResizeHandler<any> = (shape, info) => {
return resizeBox(shape, info) return resizeBox(shape, info)
} }
} }

View file

@ -12,12 +12,12 @@ import {
stopEventPropagation, stopEventPropagation,
truncateStringWithEllipsis, truncateStringWithEllipsis,
} from '../../../utils/dom' } from '../../../utils/dom'
import { TLBoxUtil } from '../TLBoxUtil' import { BaseBoxShapeUtil } from '../BaseBoxShapeUtil'
import { OnBeforeCreateHandler, OnBeforeUpdateHandler } from '../TLShapeUtil' import { TLOnBeforeCreateHandler, TLOnBeforeUpdateHandler } from '../ShapeUtil'
import { HyperlinkButton } from '../shared/HyperlinkButton' import { HyperlinkButton } from '../shared/HyperlinkButton'
/** @public */ /** @public */
export class TLBookmarkUtil extends TLBoxUtil<TLBookmarkShape> { export class BookmarkShapeUtil extends BaseBoxShapeUtil<TLBookmarkShape> {
static override type = 'bookmark' static override type = 'bookmark'
override canResize = () => false override canResize = () => false
@ -104,11 +104,11 @@ export class TLBookmarkUtil extends TLBoxUtil<TLBookmarkShape> {
) )
} }
override onBeforeCreate?: OnBeforeCreateHandler<TLBookmarkShape> = (shape) => { override onBeforeCreate?: TLOnBeforeCreateHandler<TLBookmarkShape> = (shape) => {
this.updateBookmarkAsset(shape) this.updateBookmarkAsset(shape)
} }
override onBeforeUpdate?: OnBeforeUpdateHandler<TLBookmarkShape> = (prev, shape) => { override onBeforeUpdate?: TLOnBeforeUpdateHandler<TLBookmarkShape> = (prev, shape) => {
if (prev.props.url !== shape.props.url) { if (prev.props.url !== shape.props.url) {
this.updateBookmarkAsset(shape) this.updateBookmarkAsset(shape)
} }

View file

@ -13,14 +13,14 @@ import { TLDrawShape, TLDrawShapeSegment } from '@tldraw/tlschema'
import { last, rng } from '@tldraw/utils' import { last, rng } from '@tldraw/utils'
import { SVGContainer } from '../../../components/SVGContainer' import { SVGContainer } from '../../../components/SVGContainer'
import { getSvgPathFromStroke, getSvgPathFromStrokePoints } from '../../../utils/svg' import { getSvgPathFromStroke, getSvgPathFromStrokePoints } from '../../../utils/svg'
import { ShapeUtil, TLOnResizeHandler } from '../ShapeUtil'
import { getShapeFillSvg, ShapeFill } from '../shared/ShapeFill' import { getShapeFillSvg, ShapeFill } from '../shared/ShapeFill'
import { TLExportColors } from '../shared/TLExportColors' import { TLExportColors } from '../shared/TLExportColors'
import { useForceSolid } from '../shared/useForceSolid' import { useForceSolid } from '../shared/useForceSolid'
import { OnResizeHandler, TLShapeUtil } from '../TLShapeUtil'
import { getDrawShapeStrokeDashArray, getFreehandOptions, getPointsFromSegments } from './getPath' import { getDrawShapeStrokeDashArray, getFreehandOptions, getPointsFromSegments } from './getPath'
/** @public */ /** @public */
export class TLDrawUtil extends TLShapeUtil<TLDrawShape> { export class DrawShapeUtil extends ShapeUtil<TLDrawShape> {
static override type = 'draw' static override type = 'draw'
hideResizeHandles = (shape: TLDrawShape) => getIsDot(shape) hideResizeHandles = (shape: TLDrawShape) => getIsDot(shape)
@ -269,7 +269,7 @@ export class TLDrawUtil extends TLShapeUtil<TLDrawShape> {
return foregroundPath return foregroundPath
} }
override onResize: OnResizeHandler<TLDrawShape> = (shape, info) => { override onResize: TLOnResizeHandler<TLDrawShape> = (shape, info) => {
const { scaleX, scaleY } = info const { scaleX, scaleY } = info
const newSegments: TLDrawShapeSegment[] = [] const newSegments: TLDrawShapeSegment[] = []

View file

@ -14,8 +14,8 @@ import { ROTATING_SHADOWS } from '../../../constants'
import { useIsEditing } from '../../../hooks/useIsEditing' import { useIsEditing } from '../../../hooks/useIsEditing'
import { rotateBoxShadow } from '../../../utils/dom' import { rotateBoxShadow } from '../../../utils/dom'
import { getEmbedInfo, getEmbedInfoUnsafely } from '../../../utils/embeds' import { getEmbedInfo, getEmbedInfoUnsafely } from '../../../utils/embeds'
import { TLBoxUtil } from '../TLBoxUtil' import { BaseBoxShapeUtil } from '../BaseBoxShapeUtil'
import { OnResizeHandler, TLShapeUtilFlag } from '../TLShapeUtil' import { TLOnResizeHandler, TLShapeUtilFlag } from '../ShapeUtil'
import { resizeBox } from '../shared/resizeBox' import { resizeBox } from '../shared/resizeBox'
const getSandboxPermissions = (permissions: TLEmbedShapePermissions) => { const getSandboxPermissions = (permissions: TLEmbedShapePermissions) => {
@ -26,7 +26,7 @@ const getSandboxPermissions = (permissions: TLEmbedShapePermissions) => {
} }
/** @public */ /** @public */
export class TLEmbedUtil extends TLBoxUtil<TLEmbedShape> { export class EmbedShapeUtil extends BaseBoxShapeUtil<TLEmbedShape> {
static override type = 'embed' static override type = 'embed'
override canUnmount: TLShapeUtilFlag<TLEmbedShape> = () => false override canUnmount: TLShapeUtilFlag<TLEmbedShape> = () => false
@ -55,7 +55,7 @@ export class TLEmbedUtil extends TLBoxUtil<TLEmbedShape> {
return embedInfo?.definition.isAspectRatioLocked ?? false return embedInfo?.definition.isAspectRatioLocked ?? false
} }
onResize: OnResizeHandler<TLEmbedShape> = (shape, info) => { onResize: TLOnResizeHandler<TLEmbedShape> = (shape, info) => {
const isAspectRatioLocked = this.isAspectRatioLocked(shape) const isAspectRatioLocked = this.isAspectRatioLocked(shape)
const embedInfo = getEmbedInfo(shape.props.url) const embedInfo = getEmbedInfo(shape.props.url)
let minWidth = embedInfo?.definition.minWidth ?? 200 let minWidth = embedInfo?.definition.minWidth ?? 200

View file

@ -3,14 +3,14 @@ import { TLFrameShape, TLShape, TLShapeId } from '@tldraw/tlschema'
import { last } from '@tldraw/utils' import { last } from '@tldraw/utils'
import { SVGContainer } from '../../../components/SVGContainer' import { SVGContainer } from '../../../components/SVGContainer'
import { defaultEmptyAs } from '../../../utils/string' import { defaultEmptyAs } from '../../../utils/string'
import { BaseBoxShapeUtil } from '../BaseBoxShapeUtil'
import { TLOnResizeEndHandler } from '../ShapeUtil'
import { createTextSvgElementFromSpans } from '../shared/createTextSvgElementFromSpans' import { createTextSvgElementFromSpans } from '../shared/createTextSvgElementFromSpans'
import { TLExportColors } from '../shared/TLExportColors' import { TLExportColors } from '../shared/TLExportColors'
import { TLBoxUtil } from '../TLBoxUtil'
import { OnResizeEndHandler } from '../TLShapeUtil'
import { FrameHeading } from './components/FrameHeading' import { FrameHeading } from './components/FrameHeading'
/** @public */ /** @public */
export class TLFrameUtil extends TLBoxUtil<TLFrameShape> { export class FrameShapeUtil extends BaseBoxShapeUtil<TLFrameShape> {
static override type = 'frame' static override type = 'frame'
override canBind = () => true override canBind = () => true
@ -190,7 +190,7 @@ export class TLFrameUtil extends TLBoxUtil<TLFrameShape> {
} }
} }
override onResizeEnd: OnResizeEndHandler<TLFrameShape> = (shape) => { override onResizeEnd: TLOnResizeEndHandler<TLFrameShape> = (shape) => {
const bounds = this.editor.getPageBounds(shape)! const bounds = this.editor.getPageBounds(shape)!
const children = this.editor.getSortedChildIds(shape.id) const children = this.editor.getSortedChildIds(shape.id)

View file

@ -17,13 +17,13 @@ import { SVGContainer } from '../../../components/SVGContainer'
import { FONT_FAMILIES, LABEL_FONT_SIZES, TEXT_PROPS } from '../../../constants' import { FONT_FAMILIES, LABEL_FONT_SIZES, TEXT_PROPS } from '../../../constants'
import { getLegacyOffsetX } from '../../../utils/legacy' import { getLegacyOffsetX } from '../../../utils/legacy'
import { Editor } from '../../Editor' import { Editor } from '../../Editor'
import { BaseBoxShapeUtil } from '../BaseBoxShapeUtil'
import { TLOnEditEndHandler, TLOnResizeHandler } from '../ShapeUtil'
import { createTextSvgElementFromSpans } from '../shared/createTextSvgElementFromSpans' import { createTextSvgElementFromSpans } from '../shared/createTextSvgElementFromSpans'
import { HyperlinkButton } from '../shared/HyperlinkButton' import { HyperlinkButton } from '../shared/HyperlinkButton'
import { TextLabel } from '../shared/TextLabel' import { TextLabel } from '../shared/TextLabel'
import { TLExportColors } from '../shared/TLExportColors' import { TLExportColors } from '../shared/TLExportColors'
import { useForceSolid } from '../shared/useForceSolid' import { useForceSolid } from '../shared/useForceSolid'
import { TLBoxUtil } from '../TLBoxUtil'
import { OnEditEndHandler, OnResizeHandler } from '../TLShapeUtil'
import { DashStyleEllipse, DashStyleEllipseSvg } from './components/DashStyleEllipse' import { DashStyleEllipse, DashStyleEllipseSvg } from './components/DashStyleEllipse'
import { DashStyleOval, DashStyleOvalSvg } from './components/DashStyleOval' import { DashStyleOval, DashStyleOvalSvg } from './components/DashStyleOval'
import { DashStylePolygon, DashStylePolygonSvg } from './components/DashStylePolygon' import { DashStylePolygon, DashStylePolygonSvg } from './components/DashStylePolygon'
@ -41,7 +41,7 @@ const LABEL_PADDING = 16
const MIN_SIZE_WITH_LABEL = 17 * 3 const MIN_SIZE_WITH_LABEL = 17 * 3
/** @public */ /** @public */
export class TLGeoUtil extends TLBoxUtil<TLGeoShape> { export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
static override type = 'geo' static override type = 'geo'
canEdit = () => true canEdit = () => true
@ -312,7 +312,7 @@ export class TLGeoUtil extends TLBoxUtil<TLGeoShape> {
} }
} }
onEditEnd: OnEditEndHandler<TLGeoShape> = (shape) => { onEditEnd: TLOnEditEndHandler<TLGeoShape> = (shape) => {
const { const {
id, id,
type, type,
@ -686,7 +686,7 @@ export class TLGeoUtil extends TLBoxUtil<TLGeoShape> {
return svgElm return svgElm
} }
onResize: OnResizeHandler<TLGeoShape> = ( onResize: TLOnResizeHandler<TLGeoShape> = (
shape, shape,
{ initialBounds, handle, newPoint, scaleX, scaleY } { initialBounds, handle, newPoint, scaleX, scaleY }
) => { ) => {

View file

@ -1,11 +1,11 @@
import { Box2d, Matrix2d } from '@tldraw/primitives' import { Box2d, Matrix2d } from '@tldraw/primitives'
import { TLGroupShape, Vec2dModel } from '@tldraw/tlschema' import { TLGroupShape, Vec2dModel } from '@tldraw/tlschema'
import { SVGContainer } from '../../../components/SVGContainer' import { SVGContainer } from '../../../components/SVGContainer'
import { OnChildrenChangeHandler, TLShapeUtil } from '../TLShapeUtil' import { ShapeUtil, TLOnChildrenChangeHandler } from '../ShapeUtil'
import { DashedOutlineBox } from '../shared/DashedOutlineBox' import { DashedOutlineBox } from '../shared/DashedOutlineBox'
/** @public */ /** @public */
export class TLGroupUtil extends TLShapeUtil<TLGroupShape> { export class GroupShapeUtil extends ShapeUtil<TLGroupShape> {
static override type = 'group' static override type = 'group'
type = 'group' as const type = 'group' as const
@ -87,7 +87,7 @@ export class TLGroupUtil extends TLShapeUtil<TLGroupShape> {
return <DashedOutlineBox className="" bounds={bounds} zoomLevel={zoomLevel} /> return <DashedOutlineBox className="" bounds={bounds} zoomLevel={zoomLevel} />
} }
onChildrenChange: OnChildrenChangeHandler<TLGroupShape> = (group) => { onChildrenChange: TLOnChildrenChangeHandler<TLGroupShape> = (group) => {
const children = this.editor.getSortedChildIds(group.id) const children = this.editor.getSortedChildIds(group.id)
if (children.length === 0) { if (children.length === 0) {
if (this.editor.pageState.focusLayerId === group.id) { if (this.editor.pageState.focusLayerId === group.id) {

View file

@ -13,17 +13,17 @@ import { last, rng } from '@tldraw/utils'
import { SVGContainer } from '../../../components/SVGContainer' import { SVGContainer } from '../../../components/SVGContainer'
import { FONT_SIZES } from '../../../constants' import { FONT_SIZES } from '../../../constants'
import { getSvgPathFromStroke, getSvgPathFromStrokePoints } from '../../../utils/svg' import { getSvgPathFromStroke, getSvgPathFromStrokePoints } from '../../../utils/svg'
import { getHighlightFreehandSettings, getPointsFromSegments } from '../DrawShapeUtil/getPath'
import { ShapeUtil, TLOnResizeHandler } from '../ShapeUtil'
import { ShapeFill } from '../shared/ShapeFill' import { ShapeFill } from '../shared/ShapeFill'
import { TLExportColors } from '../shared/TLExportColors' import { TLExportColors } from '../shared/TLExportColors'
import { useForceSolid } from '../shared/useForceSolid' import { useForceSolid } from '../shared/useForceSolid'
import { getHighlightFreehandSettings, getPointsFromSegments } from '../TLDrawUtil/getPath'
import { OnResizeHandler, TLShapeUtil } from '../TLShapeUtil'
const OVERLAY_OPACITY = 0.35 const OVERLAY_OPACITY = 0.35
const UNDERLAY_OPACITY = 0.82 const UNDERLAY_OPACITY = 0.82
/** @public */ /** @public */
export class TLHighlightUtil extends TLShapeUtil<TLHighlightShape> { export class HighlightShapeUtil extends ShapeUtil<TLHighlightShape> {
static type = 'highlight' static type = 'highlight'
hideResizeHandles = (shape: TLHighlightShape) => getIsDot(shape) hideResizeHandles = (shape: TLHighlightShape) => getIsDot(shape)
@ -162,7 +162,7 @@ export class TLHighlightUtil extends TLShapeUtil<TLHighlightShape> {
return highlighterToSvg(getStrokeWidth(shape), shape, UNDERLAY_OPACITY, colors) return highlighterToSvg(getStrokeWidth(shape), shape, UNDERLAY_OPACITY, colors)
} }
override onResize: OnResizeHandler<TLHighlightShape> = (shape, info) => { override onResize: TLOnResizeHandler<TLHighlightShape> = (shape, info) => {
const { scaleX, scaleY } = info const { scaleX, scaleY } = info
const newSegments: TLDrawShapeSegment[] = [] const newSegments: TLDrawShapeSegment[] = []

View file

@ -0,0 +1 @@
export {}

View file

@ -8,8 +8,8 @@ import { DefaultSpinner } from '../../../components/DefaultSpinner'
import { HTMLContainer } from '../../../components/HTMLContainer' import { HTMLContainer } from '../../../components/HTMLContainer'
import { useIsCropping } from '../../../hooks/useIsCropping' import { useIsCropping } from '../../../hooks/useIsCropping'
import { usePrefersReducedMotion } from '../../../utils/dom' import { usePrefersReducedMotion } from '../../../utils/dom'
import { TLBoxUtil } from '../TLBoxUtil' import { BaseBoxShapeUtil } from '../BaseBoxShapeUtil'
import { OnDoubleClickHandler } from '../TLShapeUtil' import { TLOnDoubleClickHandler } from '../ShapeUtil'
import { HyperlinkButton } from '../shared/HyperlinkButton' import { HyperlinkButton } from '../shared/HyperlinkButton'
const loadImage = async (url: string): Promise<HTMLImageElement> => { const loadImage = async (url: string): Promise<HTMLImageElement> => {
@ -48,7 +48,7 @@ async function getDataURIFromURL(url: string): Promise<string> {
} }
/** @public */ /** @public */
export class TLImageUtil extends TLBoxUtil<TLImageShape> { export class ImageShapeUtil extends BaseBoxShapeUtil<TLImageShape> {
static override type = 'image' static override type = 'image'
override isAspectRatioLocked = () => true override isAspectRatioLocked = () => true
@ -225,7 +225,7 @@ export class TLImageUtil extends TLBoxUtil<TLImageShape> {
]) ])
} }
onDoubleClickEdge: OnDoubleClickHandler<TLImageShape> = (shape) => { onDoubleClickEdge: TLOnDoubleClickHandler<TLImageShape> = (shape) => {
const props = shape.props const props = shape.props
if (!props) return if (!props) return

View file

@ -13,7 +13,7 @@ import { TLHandle, TLLineShape } from '@tldraw/tlschema'
import { deepCopy } from '@tldraw/utils' import { deepCopy } from '@tldraw/utils'
import { SVGContainer } from '../../../components/SVGContainer' import { SVGContainer } from '../../../components/SVGContainer'
import { WeakMapCache } from '../../../utils/WeakMapCache' import { WeakMapCache } from '../../../utils/WeakMapCache'
import { OnHandleChangeHandler, OnResizeHandler, TLShapeUtil } from '../TLShapeUtil' import { ShapeUtil, TLOnHandleChangeHandler, TLOnResizeHandler } from '../ShapeUtil'
import { ShapeFill } from '../shared/ShapeFill' import { ShapeFill } from '../shared/ShapeFill'
import { TLExportColors } from '../shared/TLExportColors' import { TLExportColors } from '../shared/TLExportColors'
import { getPerfectDashProps } from '../shared/getPerfectDashProps' import { getPerfectDashProps } from '../shared/getPerfectDashProps'
@ -25,7 +25,7 @@ const splinesCache = new WeakMapCache<TLLineShape['props'], CubicSpline2d | Poly
const handlesCache = new WeakMapCache<TLLineShape['props'], TLHandle[]>() const handlesCache = new WeakMapCache<TLLineShape['props'], TLHandle[]>()
/** @public */ /** @public */
export class TLLineUtil extends TLShapeUtil<TLLineShape> { export class LineShapeUtil extends ShapeUtil<TLLineShape> {
static override type = 'line' static override type = 'line'
override hideResizeHandles = () => true override hideResizeHandles = () => true
@ -107,7 +107,7 @@ export class TLLineUtil extends TLShapeUtil<TLLineShape> {
// Events // Events
onResize: OnResizeHandler<TLLineShape> = (shape, info) => { onResize: TLOnResizeHandler<TLLineShape> = (shape, info) => {
const { scaleX, scaleY } = info const { scaleX, scaleY } = info
const handles = deepCopy(shape.props.handles) const handles = deepCopy(shape.props.handles)
@ -124,7 +124,7 @@ export class TLLineUtil extends TLShapeUtil<TLLineShape> {
} }
} }
onHandleChange: OnHandleChangeHandler<TLLineShape> = (shape, { handle }) => { onHandleChange: TLOnHandleChangeHandler<TLLineShape> = (shape, { handle }) => {
const next = deepCopy(shape) const next = deepCopy(shape)
switch (handle.id) { switch (handle.id) {

View file

@ -3,16 +3,16 @@ import { TLNoteShape } from '@tldraw/tlschema'
import { FONT_FAMILIES, LABEL_FONT_SIZES, TEXT_PROPS } from '../../../constants' import { FONT_FAMILIES, LABEL_FONT_SIZES, TEXT_PROPS } from '../../../constants'
import { getLegacyOffsetX } from '../../../utils/legacy' import { getLegacyOffsetX } from '../../../utils/legacy'
import { Editor } from '../../Editor' import { Editor } from '../../Editor'
import { ShapeUtil, TLOnEditEndHandler } from '../ShapeUtil'
import { createTextSvgElementFromSpans } from '../shared/createTextSvgElementFromSpans' import { createTextSvgElementFromSpans } from '../shared/createTextSvgElementFromSpans'
import { HyperlinkButton } from '../shared/HyperlinkButton' import { HyperlinkButton } from '../shared/HyperlinkButton'
import { TextLabel } from '../shared/TextLabel' import { TextLabel } from '../shared/TextLabel'
import { TLExportColors } from '../shared/TLExportColors' import { TLExportColors } from '../shared/TLExportColors'
import { OnEditEndHandler, TLShapeUtil } from '../TLShapeUtil'
const NOTE_SIZE = 200 const NOTE_SIZE = 200
/** @public */ /** @public */
export class TLNoteUtil extends TLShapeUtil<TLNoteShape> { export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
static override type = 'note' static override type = 'note'
canEdit = () => true canEdit = () => true
@ -181,7 +181,7 @@ export class TLNoteUtil extends TLShapeUtil<TLNoteShape> {
return getGrowY(this.editor, next, prev.props.growY) return getGrowY(this.editor, next, prev.props.growY)
} }
onEditEnd: OnEditEndHandler<TLNoteShape> = (shape) => { onEditEnd: TLOnEditEndHandler<TLNoteShape> = (shape) => {
const { const {
id, id,
type, type,

View file

@ -21,9 +21,9 @@ const transforms = new WeakMapCache<TLShape, Matrix2d>()
/** @public */ /** @public */
export interface TLShapeUtilConstructor< export interface TLShapeUtilConstructor<
T extends TLUnknownShape, T extends TLUnknownShape,
ShapeUtil extends TLShapeUtil<T> = TLShapeUtil<T> U extends ShapeUtil<T> = ShapeUtil<T>
> { > {
new (editor: Editor, type: T['type']): ShapeUtil new (editor: Editor, type: T['type']): U
type: T['type'] type: T['type']
} }
@ -31,7 +31,7 @@ export interface TLShapeUtilConstructor<
export type TLShapeUtilFlag<T> = (shape: T) => boolean export type TLShapeUtilFlag<T> = (shape: T) => boolean
/** @public */ /** @public */
export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> { export abstract class ShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
constructor(public editor: Editor, public readonly type: T['type']) {} constructor(public editor: Editor, public readonly type: T['type']) {}
static type: string static type: string
@ -430,7 +430,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @returns The next shape or void. * @returns The next shape or void.
* @public * @public
*/ */
onBeforeCreate?: OnBeforeCreateHandler<T> onBeforeCreate?: TLOnBeforeCreateHandler<T>
/** /**
* A callback called just before a shape is updated. This method provides a last chance to modify * A callback called just before a shape is updated. This method provides a last chance to modify
@ -451,7 +451,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @returns The next shape or void. * @returns The next shape or void.
* @public * @public
*/ */
onBeforeUpdate?: OnBeforeUpdateHandler<T> onBeforeUpdate?: TLOnBeforeUpdateHandler<T>
/** /**
* A callback called when some other shapes are dragged over this one. * A callback called when some other shapes are dragged over this one.
@ -469,7 +469,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @returns An object specifying whether the shape should hint that it can receive the dragged shapes. * @returns An object specifying whether the shape should hint that it can receive the dragged shapes.
* @public * @public
*/ */
onDragShapesOver?: OnDragHandler<T, { shouldHint: boolean }> onDragShapesOver?: TLOnDragHandler<T, { shouldHint: boolean }>
/** /**
* A callback called when some other shapes are dragged out of this one. * A callback called when some other shapes are dragged out of this one.
@ -478,7 +478,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @param shapes - The shapes that are being dragged out. * @param shapes - The shapes that are being dragged out.
* @public * @public
*/ */
onDragShapesOut?: OnDragHandler<T> onDragShapesOut?: TLOnDragHandler<T>
/** /**
* A callback called when some other shapes are dropped over this one. * A callback called when some other shapes are dropped over this one.
@ -487,7 +487,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @param shapes - The shapes that are being dropped over this one. * @param shapes - The shapes that are being dropped over this one.
* @public * @public
*/ */
onDropShapesOver?: OnDragHandler<T> onDropShapesOver?: TLOnDragHandler<T>
/** /**
* A callback called when a shape starts being resized. * A callback called when a shape starts being resized.
@ -496,7 +496,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @returns A change to apply to the shape, or void. * @returns A change to apply to the shape, or void.
* @public * @public
*/ */
onResizeStart?: OnResizeStartHandler<T> onResizeStart?: TLOnResizeStartHandler<T>
/** /**
* A callback called when a shape changes from a resize. * A callback called when a shape changes from a resize.
@ -506,7 +506,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @returns A change to apply to the shape, or void. * @returns A change to apply to the shape, or void.
* @public * @public
*/ */
onResize?: OnResizeHandler<T> onResize?: TLOnResizeHandler<T>
/** /**
* A callback called when a shape finishes resizing. * A callback called when a shape finishes resizing.
@ -516,7 +516,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @returns A change to apply to the shape, or void. * @returns A change to apply to the shape, or void.
* @public * @public
*/ */
onResizeEnd?: OnResizeEndHandler<T> onResizeEnd?: TLOnResizeEndHandler<T>
/** /**
* A callback called when a shape starts being translated. * A callback called when a shape starts being translated.
@ -525,7 +525,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @returns A change to apply to the shape, or void. * @returns A change to apply to the shape, or void.
* @public * @public
*/ */
onTranslateStart?: OnTranslateStartHandler<T> onTranslateStart?: TLOnTranslateStartHandler<T>
/** /**
* A callback called when a shape changes from a translation. * A callback called when a shape changes from a translation.
@ -535,7 +535,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @returns A change to apply to the shape, or void. * @returns A change to apply to the shape, or void.
* @public * @public
*/ */
onTranslate?: OnTranslateHandler<T> onTranslate?: TLOnTranslateHandler<T>
/** /**
* A callback called when a shape finishes translating. * A callback called when a shape finishes translating.
@ -545,7 +545,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @returns A change to apply to the shape, or void. * @returns A change to apply to the shape, or void.
* @public * @public
*/ */
onTranslateEnd?: OnTranslateEndHandler<T> onTranslateEnd?: TLOnTranslateEndHandler<T>
/** /**
* A callback called when a shape starts being rotated. * A callback called when a shape starts being rotated.
@ -554,7 +554,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @returns A change to apply to the shape, or void. * @returns A change to apply to the shape, or void.
* @public * @public
*/ */
onRotateStart?: OnRotateStartHandler<T> onRotateStart?: TLOnRotateStartHandler<T>
/** /**
* A callback called when a shape changes from a rotation. * A callback called when a shape changes from a rotation.
@ -564,7 +564,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @returns A change to apply to the shape, or void. * @returns A change to apply to the shape, or void.
* @public * @public
*/ */
onRotate?: OnRotateHandler<T> onRotate?: TLOnRotateHandler<T>
/** /**
* A callback called when a shape finishes rotating. * A callback called when a shape finishes rotating.
@ -574,7 +574,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @returns A change to apply to the shape, or void. * @returns A change to apply to the shape, or void.
* @public * @public
*/ */
onRotateEnd?: OnRotateEndHandler<T> onRotateEnd?: TLOnRotateEndHandler<T>
/** /**
* A callback called when a shape's handle changes. * A callback called when a shape's handle changes.
@ -584,14 +584,14 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @returns A change to apply to the shape, or void. * @returns A change to apply to the shape, or void.
* @public * @public
*/ */
onHandleChange?: OnHandleChangeHandler<T> onHandleChange?: TLOnHandleChangeHandler<T>
/** /**
* Not currently used. * Not currently used.
* *
* @internal * @internal
*/ */
onBindingChange?: OnBindingChangeHandler<T> onBindingChange?: TLOnBindingChangeHandler<T>
/** /**
* A callback called when a shape's children change. * A callback called when a shape's children change.
@ -600,7 +600,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @returns An array of shape updates, or void. * @returns An array of shape updates, or void.
* @public * @public
*/ */
onChildrenChange?: OnChildrenChangeHandler<T> onChildrenChange?: TLOnChildrenChangeHandler<T>
/** /**
* A callback called when a shape's handle is double clicked. * A callback called when a shape's handle is double clicked.
@ -610,7 +610,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @returns A change to apply to the shape, or void. * @returns A change to apply to the shape, or void.
* @public * @public
*/ */
onDoubleClickHandle?: OnDoubleClickHandleHandler<T> onDoubleClickHandle?: TLOnDoubleClickHandleHandler<T>
/** /**
* A callback called when a shape's edge is double clicked. * A callback called when a shape's edge is double clicked.
@ -619,7 +619,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @returns A change to apply to the shape, or void. * @returns A change to apply to the shape, or void.
* @public * @public
*/ */
onDoubleClickEdge?: OnDoubleClickHandler<T> onDoubleClickEdge?: TLOnDoubleClickHandler<T>
/** /**
* A callback called when a shape is double clicked. * A callback called when a shape is double clicked.
@ -628,7 +628,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @returns A change to apply to the shape, or void. * @returns A change to apply to the shape, or void.
* @public * @public
*/ */
onDoubleClick?: OnDoubleClickHandler<T> onDoubleClick?: TLOnDoubleClickHandler<T>
/** /**
* A callback called when a shape is clicked. * A callback called when a shape is clicked.
@ -637,7 +637,7 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @returns A change to apply to the shape, or void. * @returns A change to apply to the shape, or void.
* @public * @public
*/ */
onClick?: OnClickHandler<T> onClick?: TLOnClickHandler<T>
/** /**
* A callback called when a shape finishes being editing. * A callback called when a shape finishes being editing.
@ -645,25 +645,25 @@ export abstract class TLShapeUtil<T extends TLUnknownShape = TLUnknownShape> {
* @param shape - The shape. * @param shape - The shape.
* @public * @public
*/ */
onEditEnd?: OnEditEndHandler<T> onEditEnd?: TLOnEditEndHandler<T>
} }
/** @public */ /** @public */
export type OnBeforeCreateHandler<T extends TLShape> = (next: T) => T | void export type TLOnBeforeCreateHandler<T extends TLShape> = (next: T) => T | void
/** @public */ /** @public */
export type OnBeforeUpdateHandler<T extends TLShape> = (prev: T, next: T) => T | void export type TLOnBeforeUpdateHandler<T extends TLShape> = (prev: T, next: T) => T | void
/** @public */ /** @public */
export type OnTranslateStartHandler<T extends TLShape> = EventStartHandler<T> export type TLOnTranslateStartHandler<T extends TLShape> = TLEventStartHandler<T>
/** @public */ /** @public */
export type OnTranslateHandler<T extends TLShape> = EventChangeHandler<T> export type TLOnTranslateHandler<T extends TLShape> = TLEventChangeHandler<T>
/** @public */ /** @public */
export type OnTranslateEndHandler<T extends TLShape> = EventChangeHandler<T> export type TLOnTranslateEndHandler<T extends TLShape> = TLEventChangeHandler<T>
/** @public */ /** @public */
export type OnRotateStartHandler<T extends TLShape> = EventStartHandler<T> export type TLOnRotateStartHandler<T extends TLShape> = TLEventStartHandler<T>
/** @public */ /** @public */
export type OnRotateHandler<T extends TLShape> = EventChangeHandler<T> export type TLOnRotateHandler<T extends TLShape> = TLEventChangeHandler<T>
/** @public */ /** @public */
export type OnRotateEndHandler<T extends TLShape> = EventChangeHandler<T> export type TLOnRotateEndHandler<T extends TLShape> = TLEventChangeHandler<T>
/** /**
* The type of resize. * The type of resize.
@ -700,30 +700,30 @@ export type TLResizeInfo<T extends TLShape> = {
} }
/** @public */ /** @public */
export type OnResizeHandler<T extends TLShape> = ( export type TLOnResizeHandler<T extends TLShape> = (
shape: T, shape: T,
info: TLResizeInfo<T> info: TLResizeInfo<T>
) => Partial<TLShapePartial<T>> | undefined | void ) => Partial<TLShapePartial<T>> | undefined | void
/** @public */ /** @public */
export type OnResizeStartHandler<T extends TLShape> = EventStartHandler<T> export type TLOnResizeStartHandler<T extends TLShape> = TLEventStartHandler<T>
/** @public */ /** @public */
export type OnResizeEndHandler<T extends TLShape> = EventChangeHandler<T> export type TLOnResizeEndHandler<T extends TLShape> = TLEventChangeHandler<T>
/* -------------------- Dragging -------------------- */ /* -------------------- Dragging -------------------- */
/** @public */ /** @public */
export type OnDragHandler<T extends TLShape, R = void> = (shape: T, shapes: TLShape[]) => R export type TLOnDragHandler<T extends TLShape, R = void> = (shape: T, shapes: TLShape[]) => R
/** @internal */ /** @internal */
export type OnBindingChangeHandler<T extends TLShape> = (shape: T) => TLShapePartial<T> | void export type TLOnBindingChangeHandler<T extends TLShape> = (shape: T) => TLShapePartial<T> | void
/** @public */ /** @public */
export type OnChildrenChangeHandler<T extends TLShape> = (shape: T) => TLShapePartial[] | void export type TLOnChildrenChangeHandler<T extends TLShape> = (shape: T) => TLShapePartial[] | void
/** @public */ /** @public */
export type OnHandleChangeHandler<T extends TLShape> = ( export type TLOnHandleChangeHandler<T extends TLShape> = (
shape: T, shape: T,
info: { info: {
handle: TLHandle handle: TLHandle
@ -732,16 +732,16 @@ export type OnHandleChangeHandler<T extends TLShape> = (
) => TLShapePartial<T> | void ) => TLShapePartial<T> | void
/** @public */ /** @public */
export type OnClickHandler<T extends TLShape> = (shape: T) => TLShapePartial<T> | void export type TLOnClickHandler<T extends TLShape> = (shape: T) => TLShapePartial<T> | void
/** @public */ /** @public */
export type OnEditEndHandler<T extends TLShape> = (shape: T) => void export type TLOnEditEndHandler<T extends TLShape> = (shape: T) => void
/** @public */ /** @public */
export type OnDoubleClickHandler<T extends TLShape> = (shape: T) => TLShapePartial<T> | void export type TLOnDoubleClickHandler<T extends TLShape> = (shape: T) => TLShapePartial<T> | void
/** @public */ /** @public */
export type OnDoubleClickHandleHandler<T extends TLShape> = ( export type TLOnDoubleClickHandleHandler<T extends TLShape> = (
shape: T, shape: T,
handle: TLHandle handle: TLHandle
) => TLShapePartial<T> | void ) => TLShapePartial<T> | void
type EventStartHandler<T extends TLShape> = (shape: T) => TLShapePartial<T> | void type TLEventStartHandler<T extends TLShape> = (shape: T) => TLShapePartial<T> | void
type EventChangeHandler<T extends TLShape> = (initial: T, current: T) => TLShapePartial<T> | void type TLEventChangeHandler<T extends TLShape> = (initial: T, current: T) => TLShapePartial<T> | void

View file

@ -6,18 +6,18 @@ import { FONT_FAMILIES, FONT_SIZES, TEXT_PROPS } from '../../../constants'
import { stopEventPropagation } from '../../../utils/dom' import { stopEventPropagation } from '../../../utils/dom'
import { WeakMapCache } from '../../../utils/WeakMapCache' import { WeakMapCache } from '../../../utils/WeakMapCache'
import { Editor } from '../../Editor' import { Editor } from '../../Editor'
import { ShapeUtil, TLOnEditEndHandler, TLOnResizeHandler, TLShapeUtilFlag } from '../ShapeUtil'
import { createTextSvgElementFromSpans } from '../shared/createTextSvgElementFromSpans' import { createTextSvgElementFromSpans } from '../shared/createTextSvgElementFromSpans'
import { resizeScaled } from '../shared/resizeScaled' import { resizeScaled } from '../shared/resizeScaled'
import { TLExportColors } from '../shared/TLExportColors' import { TLExportColors } from '../shared/TLExportColors'
import { useEditableText } from '../shared/useEditableText' import { useEditableText } from '../shared/useEditableText'
import { OnEditEndHandler, OnResizeHandler, TLShapeUtil, TLShapeUtilFlag } from '../TLShapeUtil'
export { INDENT } from './TextHelpers' export { INDENT } from './TextHelpers'
const sizeCache = new WeakMapCache<TLTextShape['props'], { height: number; width: number }>() const sizeCache = new WeakMapCache<TLTextShape['props'], { height: number; width: number }>()
/** @public */ /** @public */
export class TLTextUtil extends TLShapeUtil<TLTextShape> { export class TextShapeUtil extends ShapeUtil<TLTextShape> {
static override type = 'text' static override type = 'text'
canEdit = () => true canEdit = () => true
@ -201,7 +201,7 @@ export class TLTextUtil extends TLShapeUtil<TLTextShape> {
return groupEl return groupEl
} }
onResize: OnResizeHandler<TLTextShape> = (shape, info) => { onResize: TLOnResizeHandler<TLTextShape> = (shape, info) => {
const { initialBounds, initialShape, scaleX, handle } = info const { initialBounds, initialShape, scaleX, handle } = info
if (info.mode === 'scale_shape' || (handle !== 'right' && handle !== 'left')) { if (info.mode === 'scale_shape' || (handle !== 'right' && handle !== 'left')) {
@ -256,7 +256,7 @@ export class TLTextUtil extends TLShapeUtil<TLTextShape> {
} }
} }
onEditEnd: OnEditEndHandler<TLTextShape> = (shape) => { onEditEnd: TLOnEditEndHandler<TLTextShape> = (shape) => {
const { const {
id, id,
type, type,

View file

@ -6,11 +6,11 @@ import { DefaultSpinner } from '../../../components/DefaultSpinner'
import { HTMLContainer } from '../../../components/HTMLContainer' import { HTMLContainer } from '../../../components/HTMLContainer'
import { useIsEditing } from '../../../hooks/useIsEditing' import { useIsEditing } from '../../../hooks/useIsEditing'
import { usePrefersReducedMotion } from '../../../utils/dom' import { usePrefersReducedMotion } from '../../../utils/dom'
import { TLBoxUtil } from '../TLBoxUtil' import { BaseBoxShapeUtil } from '../BaseBoxShapeUtil'
import { HyperlinkButton } from '../shared/HyperlinkButton' import { HyperlinkButton } from '../shared/HyperlinkButton'
/** @public */ /** @public */
export class TLVideoUtil extends TLBoxUtil<TLVideoShape> { export class VideoShapeUtil extends BaseBoxShapeUtil<TLVideoShape> {
static override type = 'video' static override type = 'video'
override canEdit = () => true override canEdit = () => true
@ -63,7 +63,7 @@ function serializeVideo(id: string): string {
const TLVideoUtilComponent = track(function TLVideoUtilComponent(props: { const TLVideoUtilComponent = track(function TLVideoUtilComponent(props: {
shape: TLVideoShape shape: TLVideoShape
videoUtil: TLVideoUtil videoUtil: VideoShapeUtil
}) { }) {
const { shape, videoUtil } = props const { shape, videoUtil } = props
const showControls = videoUtil.editor.getBounds(shape).w * videoUtil.editor.zoomLevel >= 110 const showControls = videoUtil.editor.getBounds(shape).w * videoUtil.editor.zoomLevel >= 110

View file

@ -10,7 +10,7 @@ import React from 'react'
import { LABEL_FONT_SIZES, TEXT_PROPS } from '../../../constants' import { LABEL_FONT_SIZES, TEXT_PROPS } from '../../../constants'
import { stopEventPropagation } from '../../../utils/dom' import { stopEventPropagation } from '../../../utils/dom'
import { isLegacyAlign } from '../../../utils/legacy' import { isLegacyAlign } from '../../../utils/legacy'
import { TextHelpers } from '../TLTextUtil/TextHelpers' import { TextHelpers } from '../TextShapeUtil/TextHelpers'
import { useEditableText } from './useEditableText' import { useEditableText } from './useEditableText'
export const TextLabel = React.memo(function TextLabel< export const TextLabel = React.memo(function TextLabel<

View file

@ -1,8 +1,8 @@
import { Box2d, Vec2d } from '@tldraw/primitives' import { Box2d, Vec2d } from '@tldraw/primitives'
import { Vec2dModel } from '@tldraw/tlschema' import { Vec2dModel } from '@tldraw/tlschema'
import { TLBoxLike } from '../../statechart/TLBoxTool/TLBoxTool'
import { TLResizeHandle } from '../../types/selection-types' import { TLResizeHandle } from '../../types/selection-types'
import { TLResizeMode } from '../TLShapeUtil' import { TLBaseBoxShape } from '../BaseBoxShapeUtil'
import { TLResizeMode } from '../ShapeUtil'
export type ResizeBoxOptions = Partial<{ export type ResizeBoxOptions = Partial<{
minWidth: number minWidth: number
@ -12,7 +12,7 @@ export type ResizeBoxOptions = Partial<{
}> }>
export const resizeBox = ( export const resizeBox = (
shape: TLBoxLike, shape: TLBaseBoxShape,
info: { info: {
newPoint: Vec2dModel newPoint: Vec2dModel
handle: TLResizeHandle handle: TLResizeHandle
@ -20,7 +20,7 @@ export const resizeBox = (
scaleX: number scaleX: number
scaleY: number scaleY: number
initialBounds: Box2d initialBounds: Box2d
initialShape: TLBoxLike initialShape: TLBaseBoxShape
}, },
opts = {} as ResizeBoxOptions opts = {} as ResizeBoxOptions
) => { ) => {

View file

@ -4,7 +4,7 @@ import React, { useCallback, useEffect, useRef } from 'react'
import { useValue } from 'signia-react' import { useValue } from 'signia-react'
import { useEditor } from '../../../hooks/useEditor' import { useEditor } from '../../../hooks/useEditor'
import { preventDefault, stopEventPropagation } from '../../../utils/dom' import { preventDefault, stopEventPropagation } from '../../../utils/dom'
import { INDENT, TextHelpers } from '../TLTextUtil/TextHelpers' import { INDENT, TextHelpers } from '../TextShapeUtil/TextHelpers'
export function useEditableText<T extends Extract<TLShape, { props: { text: string } }>>( export function useEditableText<T extends Extract<TLShape, { props: { text: string } }>>(
id: T['id'], id: T['id'],

View file

@ -3,7 +3,7 @@ import { StateNode } from '../StateNode'
import { Idle } from './children/Idle' import { Idle } from './children/Idle'
import { Pointing } from './children/Pointing' import { Pointing } from './children/Pointing'
export class TLArrowTool extends StateNode { export class ArrowShapeTool extends StateNode {
static override id = 'arrow' static override id = 'arrow'
static initial = 'idle' static initial = 'idle'
static children = () => [Idle, Pointing] static children = () => [Idle, Pointing]

View file

@ -1,8 +1,8 @@
import { createShapeId, TLArrowShape } from '@tldraw/tlschema' import { createShapeId, TLArrowShape } from '@tldraw/tlschema'
import { TLArrowUtil } from '../../../shapeutils/TLArrowUtil/TLArrowUtil' import { ArrowShapeUtil } from '../../../shapeutils/ArrowShapeUtil/ArrowShapeUtil'
import { TLEventHandlers } from '../../../types/event-types' import { TLEventHandlers } from '../../../types/event-types'
import { StateNode } from '../../StateNode' import { StateNode } from '../../StateNode'
import { TLArrowTool } from '../TLArrowTool' import { ArrowShapeTool } from '../ArrowShapeTool'
export class Pointing extends StateNode { export class Pointing extends StateNode {
static override id = 'pointing' static override id = 'pointing'
@ -31,7 +31,7 @@ export class Pointing extends StateNode {
this.didTimeout = false this.didTimeout = false
const shapeType = (this.parent as TLArrowTool).shapeType const shapeType = (this.parent as ArrowShapeTool).shapeType
this.editor.mark('creating') this.editor.mark('creating')
@ -46,7 +46,7 @@ export class Pointing extends StateNode {
}, },
]) ])
const util = this.editor.getShapeUtil(TLArrowUtil) const util = this.editor.getShapeUtil(ArrowShapeUtil)
const shape = this.editor.getShapeById<TLArrowShape>(id) const shape = this.editor.getShapeById<TLArrowShape>(id)
if (!shape) return if (!shape) return
@ -94,7 +94,7 @@ export class Pointing extends StateNode {
} }
if (!this.didTimeout) { if (!this.didTimeout) {
const util = this.editor.getShapeUtil(TLArrowUtil) const util = this.editor.getShapeUtil(ArrowShapeUtil)
const shape = this.editor.getShapeById<TLArrowShape>(this.shape.id) const shape = this.editor.getShapeById<TLArrowShape>(this.shape.id)
if (!shape) return if (!shape) return

View file

@ -1,14 +1,10 @@
import { TLStyleType } from '@tldraw/tlschema'
import { StateNode } from '../StateNode' import { StateNode } from '../StateNode'
import { TLBaseShape, TLStyleType } from '@tldraw/tlschema'
import { Idle } from './children/Idle' import { Idle } from './children/Idle'
import { Pointing } from './children/Pointing' import { Pointing } from './children/Pointing'
/** @public */ /** @public */
export type TLBoxLike = TLBaseShape<string, { w: number; h: number }> export abstract class BaseBoxShapeTool extends StateNode {
/** @public */
export abstract class TLBoxTool extends StateNode {
static override id = 'box' static override id = 'box'
static initial = 'idle' static initial = 'idle'
static children = () => [Idle, Pointing] static children = () => [Idle, Pointing]

View file

@ -1,8 +1,9 @@
import { Vec2d } from '@tldraw/primitives' import { Vec2d } from '@tldraw/primitives'
import { createShapeId } from '@tldraw/tlschema' import { createShapeId } from '@tldraw/tlschema'
import { TLBaseBoxShape } from '../../../shapeutils/BaseBoxShapeUtil'
import { TLEventHandlers } from '../../../types/event-types' import { TLEventHandlers } from '../../../types/event-types'
import { StateNode } from '../../StateNode' import { StateNode } from '../../StateNode'
import { TLBoxLike, TLBoxTool } from '../TLBoxTool' import { BaseBoxShapeTool } from '../BaseBoxShapeTool'
export class Pointing extends StateNode { export class Pointing extends StateNode {
static override id = 'pointing' static override id = 'pointing'
@ -20,7 +21,7 @@ export class Pointing extends StateNode {
if (this.editor.inputs.isDragging) { if (this.editor.inputs.isDragging) {
const { originPagePoint } = this.editor.inputs const { originPagePoint } = this.editor.inputs
const shapeType = (this.parent as TLBoxTool)!.shapeType as TLBoxLike['type'] const shapeType = (this.parent as BaseBoxShapeTool)!.shapeType as TLBaseBoxShape['type']
const id = createShapeId() const id = createShapeId()
@ -76,7 +77,7 @@ export class Pointing extends StateNode {
this.editor.mark(this.markId) this.editor.mark(this.markId)
const shapeType = (this.parent as TLBoxTool)!.shapeType as TLBoxLike['type'] const shapeType = (this.parent as BaseBoxShapeTool)!.shapeType as TLBaseBoxShape['type']
const id = createShapeId() const id = createShapeId()
@ -91,8 +92,8 @@ export class Pointing extends StateNode {
}, },
]) ])
const shape = this.editor.getShapeById<TLBoxLike>(id)! const shape = this.editor.getShapeById<TLBaseBoxShape>(id)!
const { w, h } = this.editor.getShapeUtil(shape).defaultProps() as TLBoxLike['props'] const { w, h } = this.editor.getShapeUtil(shape).defaultProps() as TLBaseBoxShape['props']
const delta = this.editor.getDeltaInParentSpace(shape, new Vec2d(w / 2, h / 2)) const delta = this.editor.getDeltaInParentSpace(shape, new Vec2d(w / 2, h / 2))
this.editor.updateShapes([ this.editor.updateShapes([

View file

@ -4,7 +4,7 @@ import { StateNode } from '../StateNode'
import { Drawing } from './children/Drawing' import { Drawing } from './children/Drawing'
import { Idle } from './children/Idle' import { Idle } from './children/Idle'
export class TLDrawTool extends StateNode { export class DrawShapeTool extends StateNode {
static override id = 'draw' static override id = 'draw'
static initial = 'idle' static initial = 'idle'
static children = () => [Idle, Drawing] static children = () => [Idle, Drawing]

View file

@ -10,10 +10,10 @@ import {
import { last, structuredClone } from '@tldraw/utils' import { last, structuredClone } from '@tldraw/utils'
import { DRAG_DISTANCE } from '../../../../constants' import { DRAG_DISTANCE } from '../../../../constants'
import { uniqueId } from '../../../../utils/data' import { uniqueId } from '../../../../utils/data'
import { TLDrawUtil } from '../../../shapeutils/TLDrawUtil/TLDrawUtil' import { DrawShapeUtil } from '../../../shapeutils/DrawShapeUtil/DrawShapeUtil'
import { TLEventHandlers, TLPointerEventInfo } from '../../../types/event-types' import { TLEventHandlers, TLPointerEventInfo } from '../../../types/event-types'
import { TLHighlightUtil } from '../../../shapeutils/TLHighlightUtil/TLHighlightUtil' import { HighlightShapeUtil } from '../../../shapeutils/HighlightShapeUtil/HighlightShapeUtil'
import { StateNode } from '../../StateNode' import { StateNode } from '../../StateNode'
type DrawableShape = TLDrawShape | TLHighlightShape type DrawableShape = TLDrawShape | TLHighlightShape
@ -29,8 +29,8 @@ export class Drawing extends StateNode {
util = util =
this.shapeType === 'highlight' this.shapeType === 'highlight'
? this.editor.getShapeUtil(TLHighlightUtil) ? this.editor.getShapeUtil(HighlightShapeUtil)
: this.editor.getShapeUtil(TLDrawUtil) : this.editor.getShapeUtil(DrawShapeUtil)
isPen = false isPen = false

View file

@ -4,7 +4,7 @@ import { Erasing } from './children/Erasing'
import { Idle } from './children/Idle' import { Idle } from './children/Idle'
import { Pointing } from './children/Pointing' import { Pointing } from './children/Pointing'
export class TLEraserTool extends StateNode { export class EraserShapeTool extends StateNode {
static override id = 'eraser' static override id = 'eraser'
static initial = 'idle' static initial = 'idle'
static children = () => [Idle, Pointing, Erasing] static children = () => [Idle, Pointing, Erasing]

View file

@ -1,7 +1,7 @@
import { TLStyleType } from '@tldraw/tlschema' import { TLStyleType } from '@tldraw/tlschema'
import { TLBoxTool } from '../TLBoxTool/TLBoxTool' import { BaseBoxShapeTool } from '../BaseBoxShapeTool/BaseBoxShapeTool'
export class TLFrameTool extends TLBoxTool { export class FrameShapeTool extends BaseBoxShapeTool {
static override id = 'frame' static override id = 'frame'
static initial = 'idle' static initial = 'idle'

View file

@ -4,7 +4,7 @@ import { TLStyleType } from '@tldraw/tlschema'
import { Idle } from './children/Idle' import { Idle } from './children/Idle'
import { Pointing } from './children/Pointing' import { Pointing } from './children/Pointing'
export class TLGeoTool extends StateNode { export class GeoShapeTool extends StateNode {
static override id = 'geo' static override id = 'geo'
static initial = 'idle' static initial = 'idle'
static children = () => [Idle, Pointing] static children = () => [Idle, Pointing]

View file

@ -6,7 +6,7 @@ import { Dragging } from './children/Dragging'
import { Idle } from './children/Idle' import { Idle } from './children/Idle'
import { Pointing } from './children/Pointing' import { Pointing } from './children/Pointing'
export class TLHandTool extends StateNode { export class HandTool extends StateNode {
static override id = 'hand' static override id = 'hand'
static initial = 'idle' static initial = 'idle'
static children = () => [Idle, Pointing, Dragging] static children = () => [Idle, Pointing, Dragging]

View file

@ -2,10 +2,10 @@ import { TLStyleType } from '@tldraw/tlschema'
import { StateNode } from '../StateNode' import { StateNode } from '../StateNode'
// shared custody // shared custody
import { Drawing } from '../TLDrawTool/children/Drawing' import { Drawing } from '../DrawShapeTool/children/Drawing'
import { Idle } from '../TLDrawTool/children/Idle' import { Idle } from '../DrawShapeTool/children/Idle'
export class TLHighlightTool extends StateNode { export class HighlightShapeTool extends StateNode {
static override id = 'highlight' static override id = 'highlight'
static initial = 'idle' static initial = 'idle'
static children = () => [Idle, Drawing] static children = () => [Idle, Drawing]

View file

@ -3,7 +3,7 @@ import { StateNode } from '../StateNode'
import { Idle } from './children/Idle' import { Idle } from './children/Idle'
import { Lasering } from './children/Lasering' import { Lasering } from './children/Lasering'
export class TLLaserTool extends StateNode { export class LaserTool extends StateNode {
static override id = 'laser' static override id = 'laser'
static initial = 'idle' static initial = 'idle'

View file

@ -4,7 +4,7 @@ import { StateNode } from '../StateNode'
import { Idle } from './children/Idle' import { Idle } from './children/Idle'
import { Pointing } from './children/Pointing' import { Pointing } from './children/Pointing'
export class TLLineTool extends StateNode { export class LineShapeTool extends StateNode {
static override id = 'line' static override id = 'line'
static initial = 'idle' static initial = 'idle'
static children = () => [Idle, Pointing] static children = () => [Idle, Pointing]

View file

@ -4,7 +4,7 @@ import { TLHandle, TLLineShape, TLShapeId, createShapeId } from '@tldraw/tlschem
import { last, structuredClone } from '@tldraw/utils' import { last, structuredClone } from '@tldraw/utils'
import { TLEventHandlers, TLInterruptEvent } from '../../../types/event-types' import { TLEventHandlers, TLInterruptEvent } from '../../../types/event-types'
import { StateNode } from '../../StateNode' import { StateNode } from '../../StateNode'
import { TLLineTool } from '../TLLineTool' import { LineShapeTool } from '../LineShapeTool'
export class Pointing extends StateNode { export class Pointing extends StateNode {
static override id = 'pointing' static override id = 'pointing'
@ -82,7 +82,7 @@ export class Pointing extends StateNode {
this.editor.createShapes([ this.editor.createShapes([
{ {
id, id,
type: (this.parent as TLLineTool).shapeType, type: (this.parent as LineShapeTool).shapeType,
x: currentPagePoint.x, x: currentPagePoint.x,
y: currentPagePoint.y, y: currentPagePoint.y,
}, },

View file

@ -3,7 +3,7 @@ import { StateNode } from '../StateNode'
import { Idle } from './children/Idle' import { Idle } from './children/Idle'
import { Pointing } from './children/Pointing' import { Pointing } from './children/Pointing'
export class TLNoteTool extends StateNode { export class NoteShapeTool extends StateNode {
static override id = 'note' static override id = 'note'
static initial = 'idle' static initial = 'idle'
static children = () => [Idle, Pointing] static children = () => [Idle, Pointing]

View file

@ -1,5 +1,5 @@
import { TLNoteShape, createShapeId } from '@tldraw/tlschema' import { TLNoteShape, createShapeId } from '@tldraw/tlschema'
import { TLNoteUtil } from '../../../shapeutils/TLNoteUtil/TLNoteUtil' import { NoteShapeUtil } from '../../../shapeutils/NoteShapeUtil/NoteShapeUtil'
import { TLEventHandlers, TLInterruptEvent, TLPointerEventInfo } from '../../../types/event-types' import { TLEventHandlers, TLInterruptEvent, TLPointerEventInfo } from '../../../types/event-types'
import { StateNode } from '../../StateNode' import { StateNode } from '../../StateNode'
@ -97,7 +97,7 @@ export class Pointing extends StateNode {
true true
) )
const util = this.editor.getShapeUtil(TLNoteUtil) const util = this.editor.getShapeUtil(NoteShapeUtil)
const shape = this.editor.getShapeById<TLNoteShape>(id)! const shape = this.editor.getShapeById<TLNoteShape>(id)!
const bounds = util.bounds(shape) const bounds = util.bounds(shape)

View file

@ -1,12 +1,12 @@
import { TLEventHandlers } from '../types/event-types' import { TLEventHandlers } from '../types/event-types'
import { SelectTool } from './SelectTool/SelectTool'
import { StateNode } from './StateNode' import { StateNode } from './StateNode'
import { TLSelectTool } from './TLSelectTool/TLSelectTool' import { ZoomTool } from './ZoomTool/ZoomTool'
import { TLZoomTool } from './TLZoomTool/TLZoomTool'
export class RootState extends StateNode { export class RootState extends StateNode {
static override id = 'root' static override id = 'root'
static initial = 'select' static initial = 'select'
static children = () => [TLSelectTool, TLZoomTool] static children = () => [SelectTool, ZoomTool]
onKeyDown: TLEventHandlers['onKeyDown'] = (info) => { onKeyDown: TLEventHandlers['onKeyDown'] = (info) => {
switch (info.code) { switch (info.code) {

View file

@ -19,7 +19,7 @@ import { Rotating } from './children/Rotating'
import { ScribbleBrushing } from './children/ScribbleBrushing' import { ScribbleBrushing } from './children/ScribbleBrushing'
import { Translating } from './children/Translating' import { Translating } from './children/Translating'
export class TLSelectTool extends StateNode { export class SelectTool extends StateNode {
static override id = 'select' static override id = 'select'
static initial = 'idle' static initial = 'idle'
static children = () => [ static children = () => [

View file

@ -7,7 +7,7 @@ import {
VecLike, VecLike,
} from '@tldraw/primitives' } from '@tldraw/primitives'
import { TLPageId, TLShape, TLShapeId } from '@tldraw/tlschema' import { TLPageId, TLShape, TLShapeId } from '@tldraw/tlschema'
import { TLShapeUtil } from '../../../shapeutils/TLShapeUtil' import { ShapeUtil } from '../../../shapeutils/ShapeUtil'
import { import {
TLCancelEvent, TLCancelEvent,
TLEventHandlers, TLEventHandlers,
@ -103,7 +103,7 @@ export class Brushing extends StateNode {
let A: VecLike, let A: VecLike,
B: VecLike, B: VecLike,
shape: TLShape, shape: TLShape,
util: TLShapeUtil<TLShape>, util: ShapeUtil<TLShape>,
pageBounds: Box2d | undefined, pageBounds: Box2d | undefined,
pageTransform: Matrix2d | undefined, pageTransform: Matrix2d | undefined,
localCorners: VecLike[] localCorners: VecLike[]

View file

@ -1,5 +1,5 @@
import { Vec2d } from '@tldraw/primitives' import { Vec2d } from '@tldraw/primitives'
import { TLEventHandlers, UiExitHandler } from '../../../../../types/event-types' import { TLEventHandlers, TLExitEventHandler } from '../../../../../types/event-types'
import { StateNode } from '../../../../StateNode' import { StateNode } from '../../../../StateNode'
import { ShapeWithCrop, getTranslateCroppedImageChange } from './crop_helpers' import { ShapeWithCrop, getTranslateCroppedImageChange } from './crop_helpers'
@ -23,7 +23,7 @@ export class Idle extends StateNode {
} }
} }
onExit: UiExitHandler = () => { onExit: TLExitEventHandler = () => {
this.editor.setCursor({ type: 'default' }) this.editor.setCursor({ type: 'default' })
this.editor.off('change-history', this.cleanupCroppingState) this.editor.off('change-history', this.cleanupCroppingState)

View file

@ -8,8 +8,12 @@ import {
} from '@tldraw/tlschema' } from '@tldraw/tlschema'
import { deepCopy } from '@tldraw/utils' import { deepCopy } from '@tldraw/utils'
import { MIN_CROP_SIZE } from '../../../../constants' import { MIN_CROP_SIZE } from '../../../../constants'
import { TLImageUtil } from '../../../shapeutils/TLImageUtil/TLImageUtil' import { ImageShapeUtil } from '../../../shapeutils/ImageShapeUtil/ImageShapeUtil'
import { TLEventHandlers, TLPointerEventInfo, UiEnterHandler } from '../../../types/event-types' import {
TLEnterEventHandler,
TLEventHandlers,
TLPointerEventInfo,
} from '../../../types/event-types'
import { StateNode } from '../../StateNode' import { StateNode } from '../../StateNode'
import { CursorTypeMap } from './PointingResizeHandle' import { CursorTypeMap } from './PointingResizeHandle'
@ -28,7 +32,7 @@ export class Cropping extends StateNode {
private snapshot = {} as any as Snapshot private snapshot = {} as any as Snapshot
onEnter: UiEnterHandler = ( onEnter: TLEnterEventHandler = (
info: TLPointerEventInfo & { info: TLPointerEventInfo & {
target: 'selection' target: 'selection'
handle: SelectionHandle handle: SelectionHandle
@ -77,7 +81,7 @@ export class Cropping extends StateNode {
const { shape, cursorHandleOffset } = this.snapshot const { shape, cursorHandleOffset } = this.snapshot
if (!shape) return if (!shape) return
const util = this.editor.getShapeUtil(TLImageUtil) const util = this.editor.getShapeUtil(ImageShapeUtil)
if (!util) return if (!util) return
const props = shape.props as TLImageShapeProps const props = shape.props as TLImageShapeProps

Some files were not shown because too many files have changed in this diff Show more