[refactor] reduce dependencies on shape utils in editor (#1693)
We'd like to make the @tldraw/editor layer more independent of specific shapes. Unfortunately there are many places where shape types and certain shape behavior is deeply embedded in the Editor. This PR begins to refactor out dependencies between the editor library and shape utils. It does this in two ways: - removing shape utils from the arguments of `isShapeOfType`, replacing with a generic - removing shape utils from the arguments of `getShapeUtil`, replacing with a generic - moving custom arrow info cache out of the util and into the editor class - changing the a tool's `shapeType` to be a string instead of a shape util We're here trading type safety based on inferred types—"hey editor, give me your instance of this shape util class"—for knowledge at the point of call—"hey editor, give me a shape util class of this type; and trust me it'll be an instance this shape util class". Likewise for shapes. ### A note on style We haven't really established our conventions or style when it comes to types, but I'm increasingly of the opinion that we should defer to the point of call to narrow a type based on generics (keeping the types in typescript land) rather than using arguments, which blur into JavaScript land. ### Change Type - [x] `major` — Breaking change ### Test Plan - [x] Unit Tests ### Release Notes - removes shape utils from the arguments of `isShapeOfType`, replacing with a generic - removes shape utils from the arguments of `getShapeUtil`, replacing with a generic - moves custom arrow info cache out of the util and into the editor class - changes the a tool's `shapeType` to be a string instead of a shape util
This commit is contained in:
parent
d99c4a0e9c
commit
910be6073f
55 changed files with 281 additions and 361 deletions
|
@ -86,8 +86,7 @@ export class CardShapeUtil extends BaseBoxShapeUtil<CardShape> {
|
|||
export class CardShapeTool extends BaseBoxShapeTool {
|
||||
static override id = 'card'
|
||||
static override initial = 'idle'
|
||||
|
||||
override shapeType = CardShapeUtil
|
||||
override shapeType = 'card'
|
||||
}
|
||||
|
||||
export const CardShape = defineShape('card', {
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import { BaseBoxShapeTool, TLClickEvent } from '@tldraw/tldraw'
|
||||
import { CardShapeUtil } from './CardShapeUtil'
|
||||
|
||||
// A tool used to create our custom card shapes. 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 = CardShapeUtil
|
||||
override shapeType = 'card'
|
||||
|
||||
override onDoubleClick: TLClickEvent = (_info) => {
|
||||
// you can handle events in handlers like this one;
|
||||
|
|
|
@ -121,8 +121,6 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|||
// (undocumented)
|
||||
component(shape: TLArrowShape): JSX.Element | null;
|
||||
// (undocumented)
|
||||
getArrowInfo(shape: TLArrowShape): ArrowInfo | undefined;
|
||||
// (undocumented)
|
||||
getBounds(shape: TLArrowShape): Box2d;
|
||||
// (undocumented)
|
||||
getCanvasSvgDefs(): TLShapeUtilCanvasSvgDef[];
|
||||
|
@ -185,7 +183,7 @@ export abstract class BaseBoxShapeTool extends StateNode {
|
|||
// (undocumented)
|
||||
static initial: string;
|
||||
// (undocumented)
|
||||
abstract shapeType: TLShapeUtilConstructor<any>;
|
||||
abstract shapeType: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
|
@ -462,6 +460,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
getAncestorPageId(shape?: TLShape): TLPageId | undefined;
|
||||
getAncestors(shape: TLShape, acc?: TLShape[]): TLShape[];
|
||||
getAncestorsById(id: TLShapeId, acc?: TLShape[]): TLShape[];
|
||||
// (undocumented)
|
||||
getArrowInfo(shape: TLArrowShape): ArrowInfo | undefined;
|
||||
getArrowsBoundTo(shapeId: TLShapeId): {
|
||||
arrowId: TLShapeId;
|
||||
handleId: "end" | "start";
|
||||
|
@ -512,11 +512,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
getShapesAtPoint(point: VecLike): TLShape[];
|
||||
// (undocumented)
|
||||
getShapeStyleIfExists<T>(shape: TLShape, style: StyleProp<T>): T | undefined;
|
||||
getShapeUtil<C extends {
|
||||
new (...args: any[]): ShapeUtil<any>;
|
||||
type: string;
|
||||
}>(util: C): InstanceType<C>;
|
||||
getShapeUtil<S extends TLUnknownShape>(shape: S | TLShapePartial<S>): ShapeUtil<S>;
|
||||
// (undocumented)
|
||||
getShapeUtil<S extends TLUnknownShape>(type: S['type']): ShapeUtil<S>;
|
||||
// (undocumented)
|
||||
getShapeUtil<T extends ShapeUtil>(type: T extends ShapeUtil<infer R> ? R['type'] : string): T;
|
||||
getSortedChildIds(parentId: TLParentId): TLShapeId[];
|
||||
getStateDescendant(path: string): StateNode | undefined;
|
||||
// @internal (undocumented)
|
||||
|
@ -579,10 +579,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
readonly isSafari: boolean;
|
||||
isSelected(id: TLShapeId): boolean;
|
||||
isShapeInPage(shape: TLShape, pageId?: TLPageId): boolean;
|
||||
isShapeOfType<T extends TLUnknownShape>(shape: TLUnknownShape, util: {
|
||||
new (...args: any): ShapeUtil<T>;
|
||||
type: string;
|
||||
}): shape is T;
|
||||
isShapeOfType<T extends TLUnknownShape>(shape: TLUnknownShape, type: T['type']): shape is T;
|
||||
isShapeOrAncestorLocked(shape?: TLShape): boolean;
|
||||
get isSnapMode(): boolean;
|
||||
get isToolLocked(): boolean;
|
||||
|
@ -2083,7 +2080,7 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
|
|||
// (undocumented)
|
||||
path: Computed<string>;
|
||||
// (undocumented)
|
||||
shapeType?: TLShapeUtilConstructor<TLBaseShape<any, any>>;
|
||||
shapeType?: string;
|
||||
// (undocumented)
|
||||
transition(id: string, info: any): this;
|
||||
// (undocumented)
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { RotateCorner, toDomPrecision } from '@tldraw/primitives'
|
||||
import { track } from '@tldraw/state'
|
||||
import { TLEmbedShape, TLTextShape } from '@tldraw/tlschema'
|
||||
import classNames from 'classnames'
|
||||
import { useRef } from 'react'
|
||||
import { EmbedShapeUtil } from '../editor/shapes/embed/EmbedShapeUtil'
|
||||
import { TextShapeUtil } from '../editor/shapes/text/TextShapeUtil'
|
||||
import { getCursor } from '../hooks/useCursor'
|
||||
import { useEditor } from '../hooks/useEditor'
|
||||
import { useSelectionEvents } from '../hooks/useSelectionEvents'
|
||||
|
@ -94,11 +93,11 @@ export const SelectionFg = track(function SelectionFg() {
|
|||
(showSelectionBounds &&
|
||||
editor.isIn('select.resizing') &&
|
||||
onlyShape &&
|
||||
editor.isShapeOfType(onlyShape, TextShapeUtil))
|
||||
editor.isShapeOfType<TLTextShape>(onlyShape, 'text'))
|
||||
|
||||
if (
|
||||
onlyShape &&
|
||||
editor.isShapeOfType(onlyShape, EmbedShapeUtil) &&
|
||||
editor.isShapeOfType<TLEmbedShape>(onlyShape, 'embed') &&
|
||||
shouldDisplayBox &&
|
||||
IS_FIREFOX
|
||||
) {
|
||||
|
@ -186,7 +185,7 @@ export const SelectionFg = track(function SelectionFg() {
|
|||
shouldDisplayControls &&
|
||||
isCoarsePointer &&
|
||||
onlyShape &&
|
||||
editor.isShapeOfType(onlyShape, TextShapeUtil) &&
|
||||
editor.isShapeOfType<TLTextShape>(onlyShape, 'text') &&
|
||||
textHandleHeight * zoom >= 4
|
||||
|
||||
return (
|
||||
|
|
|
@ -125,12 +125,10 @@ import { TextManager } from './managers/TextManager'
|
|||
import { TickManager } from './managers/TickManager'
|
||||
import { UserPreferencesManager } from './managers/UserPreferencesManager'
|
||||
import { ShapeUtil, TLResizeMode } from './shapes/ShapeUtil'
|
||||
import { ArrowShapeUtil } from './shapes/arrow/ArrowShapeUtil'
|
||||
import { ArrowInfo } from './shapes/arrow/arrow/arrow-types'
|
||||
import { getCurvedArrowInfo } from './shapes/arrow/arrow/curved-arrow'
|
||||
import { getArrowTerminalsInArrowSpace, getIsArrowStraight } from './shapes/arrow/arrow/shared'
|
||||
import { getStraightArrowInfo } from './shapes/arrow/arrow/straight-arrow'
|
||||
import { FrameShapeUtil } from './shapes/frame/FrameShapeUtil'
|
||||
import { GroupShapeUtil } from './shapes/group/GroupShapeUtil'
|
||||
import { SvgExportContext, SvgExportDef } from './shapes/shared/SvgExportContext'
|
||||
import { RootState } from './tools/RootState'
|
||||
import { StateNode, TLStateNodeConstructor } from './tools/StateNode'
|
||||
|
@ -283,7 +281,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
this._updateDepth--
|
||||
}
|
||||
this.store.onAfterCreate = (record) => {
|
||||
if (record.typeName === 'shape' && this.isShapeOfType(record, ArrowShapeUtil)) {
|
||||
if (record.typeName === 'shape' && this.isShapeOfType<TLArrowShape>(record, 'arrow')) {
|
||||
this._arrowDidUpdate(record)
|
||||
}
|
||||
if (record.typeName === 'page') {
|
||||
|
@ -603,54 +601,29 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
*/
|
||||
shapeUtils: { readonly [K in string]?: ShapeUtil<TLUnknownShape> }
|
||||
|
||||
/**
|
||||
* Get a shape util by its definition.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* editor.getShapeUtil(ArrowShapeUtil)
|
||||
* ```
|
||||
*
|
||||
* @param util - The shape util.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getShapeUtil<C extends { new (...args: any[]): ShapeUtil<any>; type: string }>(
|
||||
util: C
|
||||
): InstanceType<C>
|
||||
/**
|
||||
* Get a shape util from a shape itself.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const util = editor.getShapeUtil(myShape)
|
||||
* const util = editor.getShapeUtil<ArrowShapeUtil>(myShape)
|
||||
* const util = editor.getShapeUtil(ArrowShapeUtil)
|
||||
* const util = editor.getShapeUtil(myArrowShape)
|
||||
* const util = editor.getShapeUtil('arrow')
|
||||
* const util = editor.getShapeUtil<TLArrowShape>(myArrowShape)
|
||||
* const util = editor.getShapeUtil(TLArrowShape)('arrow')
|
||||
* ```
|
||||
*
|
||||
* @param shape - A shape or shape partial.
|
||||
* @param shape - A shape, shape partial, or shape type.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getShapeUtil<S extends TLUnknownShape>(shape: S | TLShapePartial<S>): ShapeUtil<S>
|
||||
getShapeUtil<T extends ShapeUtil>(shapeUtilConstructor: {
|
||||
type: T extends ShapeUtil<infer R> ? R['type'] : string
|
||||
}): T {
|
||||
const shapeUtil = getOwnProperty(this.shapeUtils, shapeUtilConstructor.type) as T | undefined
|
||||
assert(shapeUtil, `No shape util found for type "${shapeUtilConstructor.type}"`)
|
||||
|
||||
// does shapeUtilConstructor extends ShapeUtil?
|
||||
if (
|
||||
'prototype' in shapeUtilConstructor &&
|
||||
shapeUtilConstructor.prototype instanceof ShapeUtil
|
||||
) {
|
||||
assert(
|
||||
shapeUtil instanceof (shapeUtilConstructor as any),
|
||||
`Shape util found for type "${shapeUtilConstructor.type}" is not an instance of the provided constructor`
|
||||
)
|
||||
}
|
||||
|
||||
return shapeUtil as T
|
||||
getShapeUtil<S extends TLUnknownShape>(type: S['type']): ShapeUtil<S>
|
||||
getShapeUtil<T extends ShapeUtil>(type: T extends ShapeUtil<infer R> ? R['type'] : string): T
|
||||
getShapeUtil(arg: string | { type: string }) {
|
||||
const type = typeof arg === 'string' ? arg : arg.type
|
||||
const shapeUtil = getOwnProperty(this.shapeUtils, type)
|
||||
assert(shapeUtil, `No shape util found for type "${type}"`)
|
||||
return shapeUtil
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
@ -759,6 +732,19 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
this.store.put([{ ...arrow, props: { ...arrow.props, [handleId]: { type: 'point', x, y } } }])
|
||||
}
|
||||
|
||||
@computed
|
||||
private get arrowInfoCache() {
|
||||
return this.store.createComputedCache<ArrowInfo, TLArrowShape>('arrow infoCache', (shape) => {
|
||||
return getIsArrowStraight(shape)
|
||||
? getStraightArrowInfo(this, shape)
|
||||
: getCurvedArrowInfo(this, shape)
|
||||
})
|
||||
}
|
||||
|
||||
getArrowInfo(shape: TLArrowShape) {
|
||||
return this.arrowInfoCache.get(shape.id)
|
||||
}
|
||||
|
||||
// private _shapeWillUpdate = (prev: TLShape, next: TLShape) => {
|
||||
// const update = this.getShapeUtil(next).onUpdate?.(prev, next)
|
||||
// return update ?? next
|
||||
|
@ -844,7 +830,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
|
||||
/** @internal */
|
||||
private _shapeDidChange(prev: TLShape, next: TLShape) {
|
||||
if (this.isShapeOfType(next, ArrowShapeUtil)) {
|
||||
if (this.isShapeOfType<TLArrowShape>(next, 'arrow')) {
|
||||
this._arrowDidUpdate(next)
|
||||
}
|
||||
|
||||
|
@ -907,7 +893,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
filtered.length === 0
|
||||
? next?.focusLayerId
|
||||
: this.findCommonAncestor(compact(filtered.map((id) => this.getShapeById(id))), (shape) =>
|
||||
this.isShapeOfType(shape, GroupShapeUtil)
|
||||
this.isShapeOfType<TLGroupShape>(shape, 'group')
|
||||
)
|
||||
|
||||
if (filtered.length !== next.selectedIds.length || nextFocusLayerId != next.focusLayerId) {
|
||||
|
@ -2181,7 +2167,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
if (focusedShape) {
|
||||
// If we have a focused layer, look for an ancestor of the focused shape that is a group
|
||||
const match = this.findAncestor(focusedShape, (shape) =>
|
||||
this.isShapeOfType(shape, GroupShapeUtil)
|
||||
this.isShapeOfType<TLGroupShape>(shape, 'group')
|
||||
)
|
||||
// If we have an ancestor that can become a focused layer, set it as the focused layer
|
||||
this.setFocusLayer(match?.id ?? null)
|
||||
|
@ -4740,7 +4726,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
}
|
||||
|
||||
const frameAncestors = this.getAncestorsById(shape.id).filter((shape) =>
|
||||
this.isShapeOfType(shape, FrameShapeUtil)
|
||||
this.isShapeOfType<TLFrameShape>(shape, 'frame')
|
||||
)
|
||||
|
||||
if (frameAncestors.length === 0) return undefined
|
||||
|
@ -5203,7 +5189,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const isArrowShape = isShapeOfType(someShape, ArrowShapeUtil)
|
||||
* const isArrowShape = isShapeOfType<TLArrowShape>(someShape, 'arrow')
|
||||
* ```
|
||||
*
|
||||
* @param util - the TLShapeUtil constructor to test against
|
||||
|
@ -5211,11 +5197,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
*
|
||||
* @public
|
||||
*/
|
||||
isShapeOfType<T extends TLUnknownShape>(
|
||||
shape: TLUnknownShape,
|
||||
util: { new (...args: any): ShapeUtil<T>; type: string }
|
||||
): shape is T {
|
||||
return shape.type === util.type
|
||||
isShapeOfType<T extends TLUnknownShape>(shape: TLUnknownShape, type: T['type']): shape is T {
|
||||
return shape.type === type
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5595,7 +5578,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
let node = shape as TLShape | undefined
|
||||
while (node) {
|
||||
if (
|
||||
this.isShapeOfType(node, GroupShapeUtil) &&
|
||||
this.isShapeOfType<TLGroupShape>(node, 'group') &&
|
||||
this.focusLayerId !== node.id &&
|
||||
!this.hasAncestor(this.focusLayerShape, node.id) &&
|
||||
(filter?.(node) ?? true)
|
||||
|
@ -5761,10 +5744,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
let newShape: TLShape = deepCopy(shape)
|
||||
|
||||
if (
|
||||
this.isShapeOfType(shape, ArrowShapeUtil) &&
|
||||
this.isShapeOfType(newShape, ArrowShapeUtil)
|
||||
this.isShapeOfType<TLArrowShape>(shape, 'arrow') &&
|
||||
this.isShapeOfType<TLArrowShape>(newShape, 'arrow')
|
||||
) {
|
||||
const info = this.getShapeUtil(ArrowShapeUtil).getArrowInfo(shape)
|
||||
const info = this.getArrowInfo(shape)
|
||||
let newStartShapeId: TLShapeId | undefined = undefined
|
||||
let newEndShapeId: TLShapeId | undefined = undefined
|
||||
|
||||
|
@ -6084,7 +6067,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
shapes = compact(
|
||||
shapes
|
||||
.map((shape) => {
|
||||
if (this.isShapeOfType(shape, GroupShapeUtil)) {
|
||||
if (this.isShapeOfType<TLGroupShape>(shape, 'group')) {
|
||||
return this.getSortedChildIds(shape.id).map((id) => this.getShapeById(id))
|
||||
}
|
||||
|
||||
|
@ -6144,7 +6127,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
const shapes = compact(ids.map((id) => this.getShapeById(id))).filter((shape) => {
|
||||
if (!shape) return false
|
||||
|
||||
if (this.isShapeOfType(shape, ArrowShapeUtil)) {
|
||||
if (this.isShapeOfType<TLArrowShape>(shape, 'arrow')) {
|
||||
if (shape.props.start.type === 'binding' || shape.props.end.type === 'binding') {
|
||||
return false
|
||||
}
|
||||
|
@ -6275,7 +6258,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
.filter((shape) => {
|
||||
if (!shape) return false
|
||||
|
||||
if (this.isShapeOfType(shape, ArrowShapeUtil)) {
|
||||
if (this.isShapeOfType<TLArrowShape>(shape, 'arrow')) {
|
||||
if (shape.props.start.type === 'binding' || shape.props.end.type === 'binding') {
|
||||
return false
|
||||
}
|
||||
|
@ -7319,7 +7302,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
const groups: TLGroupShape[] = []
|
||||
|
||||
shapes.forEach((shape) => {
|
||||
if (this.isShapeOfType(shape, GroupShapeUtil)) {
|
||||
if (this.isShapeOfType<TLGroupShape>(shape, 'group')) {
|
||||
groups.push(shape)
|
||||
} else {
|
||||
idsToSelect.add(shape.id)
|
||||
|
@ -7568,7 +7551,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
* @internal
|
||||
*/
|
||||
private _extractSharedStyles(shape: TLShape, sharedStyleMap: SharedStyleMap) {
|
||||
if (this.isShapeOfType(shape, GroupShapeUtil)) {
|
||||
if (this.isShapeOfType<TLGroupShape>(shape, 'group')) {
|
||||
// For groups, ignore the styles of the group shape and instead include the styles of the
|
||||
// group's children. These are the shapes that would have their styles changed if the
|
||||
// user called `setStyle` on the current selection.
|
||||
|
@ -7673,7 +7656,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
// For groups, ignore the opacity of the group shape and instead include
|
||||
// the opacity of the group's children. These are the shapes that would have
|
||||
// their opacity changed if the user called `setOpacity` on the current selection.
|
||||
if (this.isShapeOfType(shape, GroupShapeUtil)) {
|
||||
if (this.isShapeOfType<TLGroupShape>(shape, 'group')) {
|
||||
for (const childId of this.getSortedChildIds(shape.id)) {
|
||||
addShape(childId)
|
||||
}
|
||||
|
@ -7727,7 +7710,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
const addShapeById = (id: TLShape['id']) => {
|
||||
const shape = this.getShapeById(id)
|
||||
if (!shape) return
|
||||
if (this.isShapeOfType(shape, GroupShapeUtil)) {
|
||||
if (this.isShapeOfType<TLGroupShape>(shape, 'group')) {
|
||||
const childIds = this.getSortedChildIds(id)
|
||||
for (const childId of childIds) {
|
||||
addShapeById(childId)
|
||||
|
@ -7799,7 +7782,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
const addShapeById = (id: TLShape['id']) => {
|
||||
const shape = this.getShapeById(id)
|
||||
if (!shape) return
|
||||
if (this.isShapeOfType(shape, GroupShapeUtil)) {
|
||||
if (this.isShapeOfType<TLGroupShape>(shape, 'group')) {
|
||||
const childIds = this.getSortedChildIds(id)
|
||||
for (const childId of childIds) {
|
||||
addShapeById(childId)
|
||||
|
@ -7883,14 +7866,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
|
||||
shape = structuredClone(shape) as typeof shape
|
||||
|
||||
if (this.isShapeOfType(shape, ArrowShapeUtil)) {
|
||||
if (this.isShapeOfType<TLArrowShape>(shape, 'arrow')) {
|
||||
const startBindingId =
|
||||
shape.props.start.type === 'binding' ? shape.props.start.boundShapeId : undefined
|
||||
|
||||
const endBindingId =
|
||||
shape.props.end.type === 'binding' ? shape.props.end.boundShapeId : undefined
|
||||
|
||||
const info = this.getShapeUtil(ArrowShapeUtil).getArrowInfo(shape)
|
||||
const info = this.getArrowInfo(shape)
|
||||
|
||||
if (shape.props.start.type === 'binding') {
|
||||
if (!shapes.some((s) => s.id === startBindingId)) {
|
||||
|
@ -8034,7 +8017,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
for (const shape of this.selectedShapes) {
|
||||
if (lowestDepth === 0) break
|
||||
|
||||
const isFrame = this.isShapeOfType(shape, FrameShapeUtil)
|
||||
const isFrame = this.isShapeOfType<TLFrameShape>(shape, 'frame')
|
||||
const ancestors = this.getAncestors(shape)
|
||||
if (isFrame) ancestors.push(shape)
|
||||
|
||||
|
@ -8073,8 +8056,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
if (rootShapeIds.length === 1) {
|
||||
const rootShape = shapes.find((s) => s.id === rootShapeIds[0])!
|
||||
if (
|
||||
this.isShapeOfType(parent, FrameShapeUtil) &&
|
||||
this.isShapeOfType(rootShape, FrameShapeUtil) &&
|
||||
this.isShapeOfType<TLFrameShape>(parent, 'frame') &&
|
||||
this.isShapeOfType<TLFrameShape>(rootShape, 'frame') &&
|
||||
rootShape.props.w === parent?.props.w &&
|
||||
rootShape.props.h === parent?.props.h
|
||||
) {
|
||||
|
@ -8130,7 +8113,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
index = getIndexAbove(index)
|
||||
}
|
||||
|
||||
if (this.isShapeOfType(newShape, ArrowShapeUtil)) {
|
||||
if (this.isShapeOfType<TLArrowShape>(newShape, 'arrow')) {
|
||||
if (newShape.props.start.type === 'binding') {
|
||||
const mappedId = idMap.get(newShape.props.start.boundShapeId)
|
||||
newShape.props.start = mappedId
|
||||
|
@ -8281,11 +8264,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
if (rootShapes.length === 1) {
|
||||
const onlyRoot = rootShapes[0] as TLFrameShape
|
||||
// If the old bounds are in the viewport...
|
||||
if (this.isShapeOfType(onlyRoot, FrameShapeUtil)) {
|
||||
if (this.isShapeOfType<TLFrameShape>(onlyRoot, 'frame')) {
|
||||
while (
|
||||
this.getShapesAtPoint(point).some(
|
||||
(shape) =>
|
||||
this.isShapeOfType(shape, FrameShapeUtil) &&
|
||||
this.isShapeOfType<TLFrameShape>(shape, 'frame') &&
|
||||
shape.props.w === onlyRoot.props.w &&
|
||||
shape.props.h === onlyRoot.props.h
|
||||
)
|
||||
|
@ -8398,7 +8381,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
if (!bbox) return
|
||||
|
||||
const singleFrameShapeId =
|
||||
ids.length === 1 && this.isShapeOfType(this.getShapeById(ids[0])!, FrameShapeUtil)
|
||||
ids.length === 1 && this.isShapeOfType<TLFrameShape>(this.getShapeById(ids[0])!, 'frame')
|
||||
? ids[0]
|
||||
: null
|
||||
if (!singleFrameShapeId) {
|
||||
|
@ -8474,7 +8457,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
|
||||
const shape = this.getShapeById(id)!
|
||||
|
||||
if (this.isShapeOfType(shape, GroupShapeUtil)) return []
|
||||
if (this.isShapeOfType<TLGroupShape>(shape, 'group')) return []
|
||||
|
||||
const util = this.getShapeUtil(shape)
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { TLArrowShape, TLShapeId } from '@tldraw/tlschema'
|
||||
import { TLArrowShape, TLGeoShape, TLShapeId } from '@tldraw/tlschema'
|
||||
import { TestEditor } from '../../test/TestEditor'
|
||||
import { TL } from '../../test/jsx'
|
||||
import { GeoShapeUtil } from '../shapes/geo/GeoShapeUtil'
|
||||
|
||||
let editor: TestEditor
|
||||
|
||||
|
@ -185,7 +184,7 @@ describe('arrowBindingsIndex', () => {
|
|||
editor.duplicateShapes()
|
||||
|
||||
const [box1Clone, box2Clone] = editor.selectedShapes
|
||||
.filter((shape) => editor.isShapeOfType(shape, GeoShapeUtil))
|
||||
.filter((shape) => editor.isShapeOfType<TLGeoShape>(shape, 'geo'))
|
||||
.sort((a, b) => a.x - b.x)
|
||||
|
||||
expect(editor.getArrowsBoundTo(box2Clone.id)).toHaveLength(3)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { Computed, RESET_VALUE, computed, isUninitialized } from '@tldraw/state'
|
||||
import { TLArrowShape, TLShape, TLShapeId } from '@tldraw/tlschema'
|
||||
import { Editor } from '../Editor'
|
||||
import { ArrowShapeUtil } from '../shapes/arrow/ArrowShapeUtil'
|
||||
|
||||
export type TLArrowBindingsIndex = Record<
|
||||
TLShapeId,
|
||||
|
@ -83,7 +82,7 @@ export const arrowBindingsIndex = (editor: Editor): Computed<TLArrowBindingsInde
|
|||
|
||||
for (const changes of diff) {
|
||||
for (const newShape of Object.values(changes.added)) {
|
||||
if (editor.isShapeOfType(newShape, ArrowShapeUtil)) {
|
||||
if (editor.isShapeOfType<TLArrowShape>(newShape, 'arrow')) {
|
||||
const { start, end } = newShape.props
|
||||
if (start.type === 'binding') {
|
||||
addBinding(start.boundShapeId, newShape.id, 'start')
|
||||
|
@ -96,8 +95,8 @@ export const arrowBindingsIndex = (editor: Editor): Computed<TLArrowBindingsInde
|
|||
|
||||
for (const [prev, next] of Object.values(changes.updated) as [TLShape, TLShape][]) {
|
||||
if (
|
||||
!editor.isShapeOfType(prev, ArrowShapeUtil) ||
|
||||
!editor.isShapeOfType(next, ArrowShapeUtil)
|
||||
!editor.isShapeOfType<TLArrowShape>(prev, 'arrow') ||
|
||||
!editor.isShapeOfType<TLArrowShape>(next, 'arrow')
|
||||
)
|
||||
continue
|
||||
|
||||
|
@ -124,7 +123,7 @@ export const arrowBindingsIndex = (editor: Editor): Computed<TLArrowBindingsInde
|
|||
}
|
||||
|
||||
for (const prev of Object.values(changes.removed)) {
|
||||
if (editor.isShapeOfType(prev, ArrowShapeUtil)) {
|
||||
if (editor.isShapeOfType<TLArrowShape>(prev, 'arrow')) {
|
||||
const { start, end } = prev.props
|
||||
if (start.type === 'binding') {
|
||||
removingBinding(start.boundShapeId, prev.id, 'start')
|
||||
|
|
|
@ -27,7 +27,6 @@ import { getEmbedInfo } from '../../utils/embeds'
|
|||
import { Editor } from '../Editor'
|
||||
import { FONT_FAMILIES, FONT_SIZES, TEXT_PROPS } from '../shapes/shared/default-shape-constants'
|
||||
import { INDENT } from '../shapes/text/TextHelpers'
|
||||
import { TextShapeUtil } from '../shapes/text/TextShapeUtil'
|
||||
|
||||
/** @public */
|
||||
export type TLExternalContent =
|
||||
|
@ -235,7 +234,7 @@ export class ExternalContentManager {
|
|||
const p =
|
||||
point ?? (editor.inputs.shiftKey ? editor.inputs.currentPagePoint : editor.viewportPageCenter)
|
||||
|
||||
const defaultProps = editor.getShapeUtil(TextShapeUtil).getDefaultProps()
|
||||
const defaultProps = editor.getShapeUtil<TLTextShape>('text').getDefaultProps()
|
||||
|
||||
const textToPaste = stripTrailingWhitespace(
|
||||
stripCommonMinimumIndentation(replaceTabsWithSpaces(text))
|
||||
|
|
|
@ -12,11 +12,10 @@ import {
|
|||
VecLike,
|
||||
} from '@tldraw/primitives'
|
||||
import { atom, computed, EMPTY_ARRAY } from '@tldraw/state'
|
||||
import { TLParentId, TLShape, TLShapeId, Vec2dModel } from '@tldraw/tlschema'
|
||||
import { TLGroupShape, TLParentId, TLShape, TLShapeId, Vec2dModel } from '@tldraw/tlschema'
|
||||
import { dedupe, deepCopy } from '@tldraw/utils'
|
||||
import { uniqueId } from '../../utils/data'
|
||||
import type { Editor } from '../Editor'
|
||||
import { GroupShapeUtil } from '../shapes/group/GroupShapeUtil'
|
||||
|
||||
export type PointsSnapLine = {
|
||||
id: string
|
||||
|
@ -266,7 +265,7 @@ export class SnapManager {
|
|||
const pageBounds = editor.getPageBoundsById(childId)
|
||||
if (!(pageBounds && renderingBounds.includes(pageBounds))) continue
|
||||
// Snap to children of groups but not group itself
|
||||
if (editor.isShapeOfType(childShape, GroupShapeUtil)) {
|
||||
if (editor.isShapeOfType<TLGroupShape>(childShape, 'group')) {
|
||||
collectSnappableShapesFromParent(childId)
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { StateNode } from '../../tools/StateNode'
|
||||
import { ArrowShapeUtil } from './ArrowShapeUtil'
|
||||
import { Idle } from './toolStates/Idle'
|
||||
import { Pointing } from './toolStates/Pointing'
|
||||
|
||||
|
@ -8,5 +7,5 @@ export class ArrowShapeTool extends StateNode {
|
|||
static initial = 'idle'
|
||||
static children = () => [Idle, Pointing]
|
||||
|
||||
shapeType = ArrowShapeUtil
|
||||
shapeType = 'arrow'
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import { TAU } from '@tldraw/primitives'
|
|||
import { TLArrowShape, TLArrowShapeTerminal, TLShapeId, createShapeId } from '@tldraw/tlschema'
|
||||
import { assert } from '@tldraw/utils'
|
||||
import { TestEditor } from '../../../test/TestEditor'
|
||||
import { ArrowShapeUtil } from './ArrowShapeUtil'
|
||||
|
||||
let editor: TestEditor
|
||||
|
||||
|
@ -299,7 +298,7 @@ describe('Other cases when arrow are moved', () => {
|
|||
|
||||
editor.setSelectedTool('arrow').pointerDown(1000, 1000).pointerMove(50, 350).pointerUp(50, 350)
|
||||
let arrow = editor.shapesArray[editor.shapesArray.length - 1]
|
||||
assert(editor.isShapeOfType(arrow, ArrowShapeUtil))
|
||||
assert(editor.isShapeOfType<TLArrowShape>(arrow, 'arrow'))
|
||||
assert(arrow.props.end.type === 'binding')
|
||||
expect(arrow.props.end.boundShapeId).toBe(ids.box3)
|
||||
|
||||
|
@ -308,7 +307,7 @@ describe('Other cases when arrow are moved', () => {
|
|||
|
||||
// arrow should still be bound to box3
|
||||
arrow = editor.getShapeById(arrow.id)!
|
||||
assert(editor.isShapeOfType(arrow, ArrowShapeUtil))
|
||||
assert(editor.isShapeOfType<TLArrowShape>(arrow, 'arrow'))
|
||||
assert(arrow.props.end.type === 'binding')
|
||||
expect(arrow.props.end.boundShapeId).toBe(ids.box3)
|
||||
})
|
||||
|
|
|
@ -52,19 +52,10 @@ import {
|
|||
import { getPerfectDashProps } from '../shared/getPerfectDashProps'
|
||||
import { getShapeFillSvg, ShapeFill, useDefaultColorTheme } from '../shared/ShapeFill'
|
||||
import { SvgExportContext } from '../shared/SvgExportContext'
|
||||
import { ArrowInfo } from './arrow/arrow-types'
|
||||
import { getArrowheadPathForType } from './arrow/arrowheads'
|
||||
import {
|
||||
getCurvedArrowHandlePath,
|
||||
getCurvedArrowInfo,
|
||||
getSolidCurvedArrowPath,
|
||||
} from './arrow/curved-arrow'
|
||||
import { getArrowTerminalsInArrowSpace, getIsArrowStraight } from './arrow/shared'
|
||||
import {
|
||||
getSolidStraightArrowPath,
|
||||
getStraightArrowHandlePath,
|
||||
getStraightArrowInfo,
|
||||
} from './arrow/straight-arrow'
|
||||
import { getCurvedArrowHandlePath, getSolidCurvedArrowPath } from './arrow/curved-arrow'
|
||||
import { getArrowTerminalsInArrowSpace } from './arrow/shared'
|
||||
import { getSolidStraightArrowPath, getStraightArrowHandlePath } from './arrow/straight-arrow'
|
||||
import { ArrowTextLabel } from './components/ArrowTextLabel'
|
||||
|
||||
let globalRenderIndex = 0
|
||||
|
@ -108,7 +99,7 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|||
}
|
||||
|
||||
getOutlineWithoutLabel(shape: TLArrowShape): Vec2d[] {
|
||||
const info = this.getArrowInfo(shape)
|
||||
const info = this.editor.getArrowInfo(shape)
|
||||
|
||||
if (!info) {
|
||||
return []
|
||||
|
@ -213,24 +204,8 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|||
return EMPTY_ARRAY
|
||||
}
|
||||
|
||||
@computed
|
||||
private get infoCache() {
|
||||
return this.editor.store.createComputedCache<ArrowInfo, TLArrowShape>(
|
||||
'arrow infoCache',
|
||||
(shape) => {
|
||||
return getIsArrowStraight(shape)
|
||||
? getStraightArrowInfo(this.editor, shape)
|
||||
: getCurvedArrowInfo(this.editor, shape)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
getArrowInfo(shape: TLArrowShape) {
|
||||
return this.infoCache.get(shape.id)
|
||||
}
|
||||
|
||||
getHandles(shape: TLArrowShape): TLHandle[] {
|
||||
const info = this.infoCache.get(shape.id)!
|
||||
const info = this.editor.getArrowInfo(shape)!
|
||||
return [
|
||||
{
|
||||
id: 'start',
|
||||
|
@ -581,7 +556,7 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|||
'arrow.dragging'
|
||||
) && !this.editor.isReadOnly
|
||||
|
||||
const info = this.getArrowInfo(shape)
|
||||
const info = this.editor.getArrowInfo(shape)
|
||||
const bounds = this.editor.getBounds(shape)
|
||||
const labelSize = this.getLabelBounds(shape)
|
||||
|
||||
|
@ -760,7 +735,7 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|||
indicator(shape: TLArrowShape) {
|
||||
const { start, end } = getArrowTerminalsInArrowSpace(this.editor, shape)
|
||||
|
||||
const info = this.getArrowInfo(shape)
|
||||
const info = this.editor.getArrowInfo(shape)
|
||||
const bounds = this.editor.getBounds(shape)
|
||||
const labelSize = this.getLabelBounds(shape)
|
||||
|
||||
|
@ -854,7 +829,7 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|||
|
||||
@computed get labelBoundsCache(): ComputedCache<Box2d | null, TLArrowShape> {
|
||||
return this.editor.store.createComputedCache('labelBoundsCache', (shape) => {
|
||||
const info = this.getArrowInfo(shape)
|
||||
const info = this.editor.getArrowInfo(shape)
|
||||
const bounds = this.editor.getBounds(shape)
|
||||
const { text, font, size } = shape.props
|
||||
|
||||
|
@ -938,7 +913,7 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
|||
|
||||
const color = theme[shape.props.color].solid
|
||||
|
||||
const info = this.getArrowInfo(shape)
|
||||
const info = this.editor.getArrowInfo(shape)
|
||||
|
||||
const strokeWidth = STROKE_SIZES[shape.props.size]
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { createShapeId, TLArrowShape } from '@tldraw/tlschema'
|
||||
import { ArrowShapeUtil } from '../../../shapes/arrow/ArrowShapeUtil'
|
||||
import { StateNode } from '../../../tools/StateNode'
|
||||
import { TLEventHandlers } from '../../../types/event-types'
|
||||
|
||||
|
@ -43,7 +42,7 @@ export class Pointing extends StateNode {
|
|||
},
|
||||
])
|
||||
|
||||
const util = this.editor.getShapeUtil(ArrowShapeUtil)
|
||||
const util = this.editor.getShapeUtil<TLArrowShape>('arrow')
|
||||
const shape = this.editor.getShapeById<TLArrowShape>(id)
|
||||
if (!shape) return
|
||||
|
||||
|
@ -90,7 +89,7 @@ export class Pointing extends StateNode {
|
|||
}
|
||||
|
||||
if (!this.didTimeout) {
|
||||
const util = this.editor.getShapeUtil(ArrowShapeUtil)
|
||||
const util = this.editor.getShapeUtil<TLArrowShape>('arrow')
|
||||
const shape = this.editor.getShapeById<TLArrowShape>(this.shape.id)
|
||||
|
||||
if (!shape) return
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { StateNode } from '../../tools/StateNode'
|
||||
import { DrawShapeUtil } from './DrawShapeUtil'
|
||||
import { Drawing } from './toolStates/Drawing'
|
||||
import { Idle } from './toolStates/Idle'
|
||||
|
||||
|
@ -8,7 +7,7 @@ export class DrawShapeTool extends StateNode {
|
|||
static initial = 'idle'
|
||||
static children = () => [Idle, Drawing]
|
||||
|
||||
shapeType = DrawShapeUtil
|
||||
shapeType = 'draw'
|
||||
|
||||
onExit = () => {
|
||||
const drawingState = this.children!['drawing'] as Drawing
|
||||
|
|
|
@ -13,9 +13,7 @@ import { DRAG_DISTANCE } from '../../../../constants'
|
|||
import { uniqueId } from '../../../../utils/data'
|
||||
import { StateNode } from '../../../tools/StateNode'
|
||||
import { TLEventHandlers, TLPointerEventInfo } from '../../../types/event-types'
|
||||
import { HighlightShapeUtil } from '../../highlight/HighlightShapeUtil'
|
||||
import { STROKE_SIZES } from '../../shared/default-shape-constants'
|
||||
import { DrawShapeUtil } from '../DrawShapeUtil'
|
||||
|
||||
type DrawableShape = TLDrawShape | TLHighlightShape
|
||||
|
||||
|
@ -26,7 +24,7 @@ export class Drawing extends StateNode {
|
|||
|
||||
initialShape?: DrawableShape
|
||||
|
||||
shapeType = this.parent.id === 'highlight' ? HighlightShapeUtil : DrawShapeUtil
|
||||
shapeType = this.parent.id === 'highlight' ? ('highlight' as const) : ('draw' as const)
|
||||
|
||||
util = this.editor.getShapeUtil(this.shapeType)
|
||||
|
||||
|
@ -138,7 +136,7 @@ export class Drawing extends StateNode {
|
|||
}
|
||||
|
||||
canClose() {
|
||||
return this.shapeType.type !== 'highlight'
|
||||
return this.shapeType !== 'highlight'
|
||||
}
|
||||
|
||||
getIsClosed(segments: TLDrawShapeSegment[], size: TLDefaultSizeStyle) {
|
||||
|
@ -219,7 +217,7 @@ export class Drawing extends StateNode {
|
|||
|
||||
const shapePartial: TLShapePartial<DrawableShape> = {
|
||||
id: shape.id,
|
||||
type: this.shapeType.type,
|
||||
type: this.shapeType,
|
||||
props: {
|
||||
segments,
|
||||
},
|
||||
|
@ -246,7 +244,7 @@ export class Drawing extends StateNode {
|
|||
this.editor.createShapes<DrawableShape>([
|
||||
{
|
||||
id,
|
||||
type: this.shapeType.type,
|
||||
type: this.shapeType,
|
||||
x: originPagePoint.x,
|
||||
y: originPagePoint.y,
|
||||
props: {
|
||||
|
@ -349,7 +347,7 @@ export class Drawing extends StateNode {
|
|||
|
||||
const shapePartial: TLShapePartial<DrawableShape> = {
|
||||
id,
|
||||
type: this.shapeType.type,
|
||||
type: this.shapeType,
|
||||
props: {
|
||||
segments: [...segments, newSegment],
|
||||
},
|
||||
|
@ -409,7 +407,7 @@ export class Drawing extends StateNode {
|
|||
|
||||
const shapePartial: TLShapePartial<DrawableShape> = {
|
||||
id,
|
||||
type: this.shapeType.type,
|
||||
type: this.shapeType,
|
||||
props: {
|
||||
segments: finalSegments,
|
||||
},
|
||||
|
@ -551,7 +549,7 @@ export class Drawing extends StateNode {
|
|||
|
||||
const shapePartial: TLShapePartial<DrawableShape> = {
|
||||
id,
|
||||
type: this.shapeType.type,
|
||||
type: this.shapeType,
|
||||
props: {
|
||||
segments: newSegments,
|
||||
},
|
||||
|
@ -596,7 +594,7 @@ export class Drawing extends StateNode {
|
|||
|
||||
const shapePartial: TLShapePartial<DrawableShape> = {
|
||||
id,
|
||||
type: this.shapeType.type,
|
||||
type: this.shapeType,
|
||||
props: {
|
||||
segments: newSegments,
|
||||
},
|
||||
|
@ -613,7 +611,7 @@ export class Drawing extends StateNode {
|
|||
|
||||
// Set a maximum length for the lines array; after 200 points, complete the line.
|
||||
if (newPoints.length > 500) {
|
||||
this.editor.updateShapes([{ id, type: this.shapeType.type, props: { isComplete: true } }])
|
||||
this.editor.updateShapes([{ id, type: this.shapeType, props: { isComplete: true } }])
|
||||
|
||||
const { currentPagePoint } = this.editor.inputs
|
||||
|
||||
|
@ -622,7 +620,7 @@ export class Drawing extends StateNode {
|
|||
this.editor.createShapes<DrawableShape>([
|
||||
{
|
||||
id: newShapeId,
|
||||
type: this.shapeType.type,
|
||||
type: this.shapeType,
|
||||
x: toFixed(currentPagePoint.x),
|
||||
y: toFixed(currentPagePoint.y),
|
||||
props: {
|
||||
|
|
|
@ -84,7 +84,7 @@ export class EmbedShapeUtil extends BaseBoxShapeUtil<TLEmbedShape> {
|
|||
|
||||
if (editingId && hoveredId !== editingId) {
|
||||
const editingShape = this.editor.getShapeById(editingId)
|
||||
if (editingShape && this.editor.isShapeOfType(editingShape, EmbedShapeUtil)) {
|
||||
if (editingShape && this.editor.isShapeOfType<TLEmbedShape>(editingShape, 'embed')) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { BaseBoxShapeTool } from '../../tools/BaseBoxShapeTool/BaseBoxShapeTool'
|
||||
import { FrameShapeUtil } from './FrameShapeUtil'
|
||||
|
||||
export class FrameShapeTool extends BaseBoxShapeTool {
|
||||
static override id = 'frame'
|
||||
static initial = 'idle'
|
||||
|
||||
shapeType = FrameShapeUtil
|
||||
shapeType = 'frame'
|
||||
}
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
import { canolicalizeRotation, SelectionEdge, toDomPrecision } from '@tldraw/primitives'
|
||||
import { getDefaultColorTheme, TLFrameShape, TLShape, TLShapeId } from '@tldraw/tlschema'
|
||||
import {
|
||||
getDefaultColorTheme,
|
||||
TLFrameShape,
|
||||
TLGroupShape,
|
||||
TLShape,
|
||||
TLShapeId,
|
||||
} from '@tldraw/tlschema'
|
||||
import { last } from '@tldraw/utils'
|
||||
import { SVGContainer } from '../../../components/SVGContainer'
|
||||
import { defaultEmptyAs } from '../../../utils/string'
|
||||
import { BaseBoxShapeUtil } from '../BaseBoxShapeUtil'
|
||||
import { GroupShapeUtil } from '../group/GroupShapeUtil'
|
||||
import { TLOnResizeEndHandler } from '../ShapeUtil'
|
||||
import { createTextSvgElementFromSpans } from '../shared/createTextSvgElementFromSpans'
|
||||
import { useDefaultColorTheme } from '../shared/ShapeFill'
|
||||
|
@ -173,7 +178,7 @@ export class FrameShapeUtil extends BaseBoxShapeUtil<TLFrameShape> {
|
|||
|
||||
onDragShapesOut = (_shape: TLFrameShape, shapes: TLShape[]): void => {
|
||||
const parent = this.editor.getShapeById(_shape.parentId)
|
||||
const isInGroup = parent && this.editor.isShapeOfType(parent, GroupShapeUtil)
|
||||
const isInGroup = parent && this.editor.isShapeOfType<TLGroupShape>(parent, 'group')
|
||||
|
||||
// If frame is in a group, keep the shape
|
||||
// moved out in that group
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { StateNode } from '../../tools/StateNode'
|
||||
import { GeoShapeUtil } from './GeoShapeUtil'
|
||||
import { Idle } from './toolStates/Idle'
|
||||
import { Pointing } from './toolStates/Pointing'
|
||||
|
||||
|
@ -8,5 +7,5 @@ export class GeoShapeTool extends StateNode {
|
|||
static initial = 'idle'
|
||||
static children = () => [Idle, Pointing]
|
||||
|
||||
shapeType = GeoShapeUtil
|
||||
shapeType = 'geo'
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { TLGeoShape } from '@tldraw/tlschema'
|
||||
import { StateNode } from '../../../tools/StateNode'
|
||||
import { TLEventHandlers } from '../../../types/event-types'
|
||||
import { GeoShapeUtil } from '../GeoShapeUtil'
|
||||
|
||||
export class Idle extends StateNode {
|
||||
static override id = 'idle'
|
||||
|
@ -16,7 +16,7 @@ export class Idle extends StateNode {
|
|||
onKeyUp: TLEventHandlers['onKeyUp'] = (info) => {
|
||||
if (info.key === 'Enter') {
|
||||
const shape = this.editor.onlySelectedShape
|
||||
if (shape && this.editor.isShapeOfType(shape, GeoShapeUtil)) {
|
||||
if (shape && this.editor.isShapeOfType<TLGeoShape>(shape, 'geo')) {
|
||||
// todo: ensure that this only works with the most recently created shape, not just any geo shape that happens to be selected at the time
|
||||
this.editor.mark('editing shape')
|
||||
this.editor.setEditingId(shape.id)
|
||||
|
|
|
@ -58,7 +58,7 @@ export class GroupShapeUtil extends ShapeUtil<TLGroupShape> {
|
|||
hintingIds.some(
|
||||
(id) =>
|
||||
id !== shape.id &&
|
||||
this.editor.isShapeOfType(this.editor.getShapeById(id)!, GroupShapeUtil)
|
||||
this.editor.isShapeOfType<TLGroupShape>(this.editor.getShapeById(id)!, 'group')
|
||||
)
|
||||
|
||||
if (
|
||||
|
|
|
@ -2,14 +2,13 @@ import { StateNode } from '../../tools/StateNode'
|
|||
// shared custody
|
||||
import { Drawing } from '../draw/toolStates/Drawing'
|
||||
import { Idle } from '../draw/toolStates/Idle'
|
||||
import { HighlightShapeUtil } from './HighlightShapeUtil'
|
||||
|
||||
export class HighlightShapeTool extends StateNode {
|
||||
static override id = 'highlight'
|
||||
static initial = 'idle'
|
||||
static children = () => [Idle, Drawing]
|
||||
|
||||
shapeType = HighlightShapeUtil
|
||||
shapeType = 'highlight'
|
||||
|
||||
onExit = () => {
|
||||
const drawingState = this.children!['drawing'] as Drawing
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { TLLineShape } from '@tldraw/tlschema'
|
||||
import { assert } from '@tldraw/utils'
|
||||
import { TestEditor } from '../../../test/TestEditor'
|
||||
import { LineShapeUtil } from '../../shapes/line/LineShapeUtil'
|
||||
|
||||
let editor: TestEditor
|
||||
|
||||
|
@ -128,7 +128,7 @@ describe('When extending the line with the shift-key in tool-lock mode', () => {
|
|||
.pointerUp(20, 10)
|
||||
|
||||
const line = editor.shapesArray[editor.shapesArray.length - 1]
|
||||
assert(editor.isShapeOfType(line, LineShapeUtil))
|
||||
assert(editor.isShapeOfType<TLLineShape>(line, 'line'))
|
||||
const handles = Object.values(line.props.handles)
|
||||
expect(handles.length).toBe(3)
|
||||
})
|
||||
|
@ -145,7 +145,7 @@ describe('When extending the line with the shift-key in tool-lock mode', () => {
|
|||
.pointerUp(30, 10)
|
||||
|
||||
const line = editor.shapesArray[editor.shapesArray.length - 1]
|
||||
assert(editor.isShapeOfType(line, LineShapeUtil))
|
||||
assert(editor.isShapeOfType<TLLineShape>(line, 'line'))
|
||||
const handles = Object.values(line.props.handles)
|
||||
expect(handles.length).toBe(3)
|
||||
})
|
||||
|
@ -163,7 +163,7 @@ describe('When extending the line with the shift-key in tool-lock mode', () => {
|
|||
.pointerUp(30, 10)
|
||||
|
||||
const line = editor.shapesArray[editor.shapesArray.length - 1]
|
||||
assert(editor.isShapeOfType(line, LineShapeUtil))
|
||||
assert(editor.isShapeOfType<TLLineShape>(line, 'line'))
|
||||
const handles = Object.values(line.props.handles)
|
||||
expect(handles.length).toBe(3)
|
||||
})
|
||||
|
@ -183,7 +183,7 @@ describe('When extending the line with the shift-key in tool-lock mode', () => {
|
|||
.pointerUp(30, 10)
|
||||
|
||||
const line = editor.shapesArray[editor.shapesArray.length - 1]
|
||||
assert(editor.isShapeOfType(line, LineShapeUtil))
|
||||
assert(editor.isShapeOfType<TLLineShape>(line, 'line'))
|
||||
const handles = Object.values(line.props.handles)
|
||||
expect(handles.length).toBe(3)
|
||||
})
|
||||
|
@ -205,7 +205,7 @@ describe('When extending the line with the shift-key in tool-lock mode', () => {
|
|||
.pointerUp(40, 10)
|
||||
|
||||
const line = editor.shapesArray[editor.shapesArray.length - 1]
|
||||
assert(editor.isShapeOfType(line, LineShapeUtil))
|
||||
assert(editor.isShapeOfType<TLLineShape>(line, 'line'))
|
||||
const handles = Object.values(line.props.handles)
|
||||
expect(handles.length).toBe(3)
|
||||
})
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { StateNode } from '../../tools/StateNode'
|
||||
import { LineShapeUtil } from './LineShapeUtil'
|
||||
import { Idle } from './toolStates/Idle'
|
||||
import { Pointing } from './toolStates/Pointing'
|
||||
|
||||
|
@ -8,5 +7,5 @@ export class LineShapeTool extends StateNode {
|
|||
static initial = 'idle'
|
||||
static children = () => [Idle, Pointing]
|
||||
|
||||
shapeType = LineShapeUtil
|
||||
shapeType = 'line'
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { StateNode } from '../../tools/StateNode'
|
||||
import { NoteShapeUtil } from './NoteShapeUtil'
|
||||
import { Idle } from './toolStates/Idle'
|
||||
import { Pointing } from './toolStates/Pointing'
|
||||
|
||||
|
@ -8,5 +7,5 @@ export class NoteShapeTool extends StateNode {
|
|||
static initial = 'idle'
|
||||
static children = () => [Idle, Pointing]
|
||||
|
||||
shapeType = NoteShapeUtil
|
||||
shapeType = 'note'
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { StateNode } from '../../tools/StateNode'
|
||||
import { TextShapeUtil } from './TextShapeUtil'
|
||||
import { Idle } from './toolStates/Idle'
|
||||
import { Pointing } from './toolStates/Pointing'
|
||||
|
||||
|
@ -9,5 +8,5 @@ export class TextShapeTool extends StateNode {
|
|||
|
||||
static children = () => [Idle, Pointing]
|
||||
|
||||
shapeType = TextShapeUtil
|
||||
shapeType = 'text'
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { TLGeoShape, TLTextShape } from '@tldraw/tlschema'
|
||||
import { StateNode } from '../../../tools/StateNode'
|
||||
import { TLEventHandlers } from '../../../types/event-types'
|
||||
import { GeoShapeUtil } from '../../geo/GeoShapeUtil'
|
||||
import { TextShapeUtil } from '../TextShapeUtil'
|
||||
|
||||
export class Idle extends StateNode {
|
||||
static override id = 'idle'
|
||||
|
@ -19,7 +18,7 @@ export class Idle extends StateNode {
|
|||
(parent) => !selectedIds.includes(parent.id)
|
||||
)
|
||||
if (hoveringShape.id !== focusLayerId) {
|
||||
if (this.editor.isShapeOfType(hoveringShape, TextShapeUtil)) {
|
||||
if (this.editor.isShapeOfType<TLTextShape>(hoveringShape, 'text')) {
|
||||
this.editor.setHoveredId(hoveringShape.id)
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +40,7 @@ export class Idle extends StateNode {
|
|||
const { hoveredId } = this.editor
|
||||
if (hoveredId) {
|
||||
const shape = this.editor.getShapeById(hoveredId)!
|
||||
if (this.editor.isShapeOfType(shape, TextShapeUtil)) {
|
||||
if (this.editor.isShapeOfType<TLTextShape>(shape, 'text')) {
|
||||
requestAnimationFrame(() => {
|
||||
this.editor.setSelectedIds([shape.id])
|
||||
this.editor.setEditingId(shape.id)
|
||||
|
@ -65,7 +64,7 @@ export class Idle extends StateNode {
|
|||
onKeyDown: TLEventHandlers['onKeyDown'] = (info) => {
|
||||
if (info.key === 'Enter') {
|
||||
const shape = this.editor.selectedShapes[0]
|
||||
if (shape && this.editor.isShapeOfType(shape, GeoShapeUtil)) {
|
||||
if (shape && this.editor.isShapeOfType<TLGeoShape>(shape, 'geo')) {
|
||||
this.editor.setSelectedTool('select')
|
||||
this.editor.setEditingId(shape.id)
|
||||
this.editor.root.current.value!.transition('editing_shape', {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { TLShapeUtilConstructor } from '../../shapes/ShapeUtil'
|
||||
import { StateNode } from '../StateNode'
|
||||
import { Idle } from './children/Idle'
|
||||
import { Pointing } from './children/Pointing'
|
||||
|
@ -9,5 +8,5 @@ export abstract class BaseBoxShapeTool extends StateNode {
|
|||
static initial = 'idle'
|
||||
static children = () => [Idle, Pointing]
|
||||
|
||||
abstract shapeType: TLShapeUtilConstructor<any>
|
||||
abstract shapeType: string
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ export class Pointing extends StateNode {
|
|||
if (this.editor.inputs.isDragging) {
|
||||
const { originPagePoint } = this.editor.inputs
|
||||
|
||||
const shapeType = (this.parent as BaseBoxShapeTool)!.shapeType.type as TLBaseBoxShape['type']
|
||||
const shapeType = (this.parent as BaseBoxShapeTool)!.shapeType
|
||||
|
||||
const id = createShapeId()
|
||||
|
||||
|
@ -78,7 +78,7 @@ export class Pointing extends StateNode {
|
|||
|
||||
this.editor.mark(this.markId)
|
||||
|
||||
const shapeType = (this.parent as BaseBoxShapeTool)!.shapeType.type as TLBaseBoxShape['type']
|
||||
const shapeType = (this.parent as BaseBoxShapeTool)!.shapeType as TLBaseBoxShape['type']
|
||||
|
||||
const id = createShapeId()
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { pointInPolygon } from '@tldraw/primitives'
|
||||
import { TLScribble, TLShapeId } from '@tldraw/tlschema'
|
||||
import { TLFrameShape, TLGroupShape, TLScribble, TLShapeId } from '@tldraw/tlschema'
|
||||
import { ScribbleManager } from '../../../managers/ScribbleManager'
|
||||
import { FrameShapeUtil } from '../../../shapes/frame/FrameShapeUtil'
|
||||
import { GroupShapeUtil } from '../../../shapes/group/GroupShapeUtil'
|
||||
import { TLEventHandlers, TLPointerEventInfo } from '../../../types/event-types'
|
||||
import { StateNode } from '../../StateNode'
|
||||
|
||||
|
@ -24,8 +22,8 @@ export class Erasing extends StateNode {
|
|||
.filter(
|
||||
(shape) =>
|
||||
this.editor.isShapeOrAncestorLocked(shape) ||
|
||||
((this.editor.isShapeOfType(shape, GroupShapeUtil) ||
|
||||
this.editor.isShapeOfType(shape, FrameShapeUtil)) &&
|
||||
((this.editor.isShapeOfType<TLGroupShape>(shape, 'group') ||
|
||||
this.editor.isShapeOfType<TLFrameShape>(shape, 'frame')) &&
|
||||
this.editor.isPointInShape(originPagePoint, shape))
|
||||
)
|
||||
.map((shape) => shape.id)
|
||||
|
@ -98,7 +96,7 @@ export class Erasing extends StateNode {
|
|||
const erasing = new Set<TLShapeId>(erasingIdsSet)
|
||||
|
||||
for (const shape of shapesArray) {
|
||||
if (this.editor.isShapeOfType(shape, GroupShapeUtil)) continue
|
||||
if (this.editor.isShapeOfType<TLGroupShape>(shape, 'group')) continue
|
||||
|
||||
// Avoid testing masked shapes, unless the pointer is inside the mask
|
||||
const pageMask = this.editor.getPageMaskById(shape.id)
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import { TLShapeId } from '@tldraw/tlschema'
|
||||
import { FrameShapeUtil } from '../../../shapes/frame/FrameShapeUtil'
|
||||
import { GroupShapeUtil } from '../../../shapes/group/GroupShapeUtil'
|
||||
import { TLFrameShape, TLGroupShape, TLShapeId } from '@tldraw/tlschema'
|
||||
import { TLEventHandlers } from '../../../types/event-types'
|
||||
import { StateNode } from '../../StateNode'
|
||||
|
||||
|
@ -17,12 +15,16 @@ export class Pointing extends StateNode {
|
|||
for (const shape of [...this.editor.sortedShapesArray].reverse()) {
|
||||
if (this.editor.isPointInShape(inputs.currentPagePoint, shape)) {
|
||||
// Skip groups
|
||||
if (this.editor.isShapeOfType(shape, GroupShapeUtil)) continue
|
||||
if (this.editor.isShapeOfType<TLGroupShape>(shape, 'group')) continue
|
||||
|
||||
const hitShape = this.editor.getOutermostSelectableShape(shape)
|
||||
|
||||
// If we've hit a frame after hitting any other shape, stop here
|
||||
if (this.editor.isShapeOfType(hitShape, FrameShapeUtil) && erasing.size > initialSize) break
|
||||
if (
|
||||
this.editor.isShapeOfType<TLFrameShape>(hitShape, 'frame') &&
|
||||
erasing.size > initialSize
|
||||
)
|
||||
break
|
||||
|
||||
erasing.add(hitShape.id)
|
||||
}
|
||||
|
|
|
@ -6,9 +6,7 @@ import {
|
|||
Vec2d,
|
||||
VecLike,
|
||||
} from '@tldraw/primitives'
|
||||
import { TLPageId, TLShape, TLShapeId } from '@tldraw/tlschema'
|
||||
import { FrameShapeUtil } from '../../../shapes/frame/FrameShapeUtil'
|
||||
import { GroupShapeUtil } from '../../../shapes/group/GroupShapeUtil'
|
||||
import { TLFrameShape, TLGroupShape, TLPageId, TLShape, TLShapeId } from '@tldraw/tlschema'
|
||||
import { ShapeUtil } from '../../../shapes/ShapeUtil'
|
||||
import {
|
||||
TLCancelEvent,
|
||||
|
@ -43,7 +41,7 @@ export class Brushing extends StateNode {
|
|||
this.editor.shapesArray
|
||||
.filter(
|
||||
(shape) =>
|
||||
this.editor.isShapeOfType(shape, GroupShapeUtil) ||
|
||||
this.editor.isShapeOfType<TLGroupShape>(shape, 'group') ||
|
||||
this.editor.isShapeOrAncestorLocked(shape)
|
||||
)
|
||||
.map((shape) => shape.id)
|
||||
|
@ -136,7 +134,7 @@ export class Brushing extends StateNode {
|
|||
// Should we even test for a single segment intersections? Only if
|
||||
// we're not holding the ctrl key for alternate selection mode
|
||||
// (only wraps count!), or if the shape is a frame.
|
||||
if (ctrlKey || this.editor.isShapeOfType(shape, FrameShapeUtil)) {
|
||||
if (ctrlKey || this.editor.isShapeOfType<TLFrameShape>(shape, 'frame')) {
|
||||
continue testAllShapes
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
import { SelectionHandle, Vec2d } from '@tldraw/primitives'
|
||||
import {
|
||||
TLBaseShape,
|
||||
TLImageShapeCrop,
|
||||
TLImageShapeProps,
|
||||
TLShape,
|
||||
TLShapePartial,
|
||||
} from '@tldraw/tlschema'
|
||||
import { TLBaseShape, TLImageShape, TLImageShapeCrop, TLShapePartial } from '@tldraw/tlschema'
|
||||
import { deepCopy } from '@tldraw/utils'
|
||||
import { MIN_CROP_SIZE } from '../../../../constants'
|
||||
import { ImageShapeUtil } from '../../../shapes/image/ImageShapeUtil'
|
||||
import {
|
||||
TLEnterEventHandler,
|
||||
TLEventHandlers,
|
||||
|
@ -81,10 +74,10 @@ export class Cropping extends StateNode {
|
|||
const { shape, cursorHandleOffset } = this.snapshot
|
||||
|
||||
if (!shape) return
|
||||
const util = this.editor.getShapeUtil(ImageShapeUtil)
|
||||
const util = this.editor.getShapeUtil<TLImageShape>('image')
|
||||
if (!util) return
|
||||
|
||||
const props = shape.props as TLImageShapeProps
|
||||
const props = shape.props
|
||||
|
||||
const currentPagePoint = this.editor.inputs.currentPagePoint.clone().sub(cursorHandleOffset)
|
||||
const originPagePoint = this.editor.inputs.originPagePoint.clone().sub(cursorHandleOffset)
|
||||
|
@ -229,7 +222,7 @@ export class Cropping extends StateNode {
|
|||
inputs: { originPagePoint },
|
||||
} = this.editor
|
||||
|
||||
const shape = this.editor.onlySelectedShape as TLShape
|
||||
const shape = this.editor.onlySelectedShape as TLImageShape
|
||||
|
||||
const selectionBounds = this.editor.selectionBounds!
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { Vec2d } from '@tldraw/primitives'
|
||||
import { TLGeoShape, TLShape, TLTextShape, createShapeId } from '@tldraw/tlschema'
|
||||
import { TLGeoShape, TLGroupShape, TLShape, TLTextShape, createShapeId } from '@tldraw/tlschema'
|
||||
import { debugFlags } from '../../../../utils/debug-flags'
|
||||
import { GroupShapeUtil } from '../../../shapes/group/GroupShapeUtil'
|
||||
import {
|
||||
TLClickEventInfo,
|
||||
TLEventHandlers,
|
||||
|
@ -319,7 +318,9 @@ export class Idle extends StateNode {
|
|||
case 'Enter': {
|
||||
const { selectedShapes } = this.editor
|
||||
|
||||
if (selectedShapes.every((shape) => this.editor.isShapeOfType(shape, GroupShapeUtil))) {
|
||||
if (
|
||||
selectedShapes.every((shape) => this.editor.isShapeOfType<TLGroupShape>(shape, 'group'))
|
||||
) {
|
||||
this.editor.setSelectedIds(
|
||||
selectedShapes.flatMap((shape) => this.editor.getSortedChildIds(shape.id))
|
||||
)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { TLShape } from '@tldraw/tlschema'
|
||||
import { GroupShapeUtil } from '../../../shapes/group/GroupShapeUtil'
|
||||
import { TLGroupShape, TLShape } from '@tldraw/tlschema'
|
||||
import { TLEventHandlers, TLPointerEventInfo } from '../../../types/event-types'
|
||||
import { StateNode } from '../../StateNode'
|
||||
|
||||
|
@ -36,7 +35,7 @@ export class PointingShape extends StateNode {
|
|||
|
||||
const parent = this.editor.getParentShape(info.shape)
|
||||
|
||||
if (parent && this.editor.isShapeOfType(parent, GroupShapeUtil)) {
|
||||
if (parent && this.editor.isShapeOfType<TLGroupShape>(parent, 'group')) {
|
||||
this.editor.cancelDoubleClick()
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,7 @@ import {
|
|||
Vec2d,
|
||||
VecLike,
|
||||
} from '@tldraw/primitives'
|
||||
import { TLShape, TLShapeId, TLShapePartial } from '@tldraw/tlschema'
|
||||
import { FrameShapeUtil } from '../../../shapes/frame/FrameShapeUtil'
|
||||
import { TLFrameShape, TLShape, TLShapeId, TLShapePartial } from '@tldraw/tlschema'
|
||||
import {
|
||||
TLEnterEventHandler,
|
||||
TLEventHandlers,
|
||||
|
@ -371,12 +370,13 @@ export class Resizing extends StateNode {
|
|||
const shape = this.editor.getShapeById(id)
|
||||
if (shape) {
|
||||
shapeSnapshots.set(shape.id, this._createShapeSnapshot(shape))
|
||||
if (this.editor.isShapeOfType(shape, FrameShapeUtil) && selectedIds.length === 1) return
|
||||
if (this.editor.isShapeOfType<TLFrameShape>(shape, 'frame') && selectedIds.length === 1)
|
||||
return
|
||||
this.editor.visitDescendants(shape.id, (descendantId) => {
|
||||
const descendent = this.editor.getShapeById(descendantId)
|
||||
if (descendent) {
|
||||
shapeSnapshots.set(descendent.id, this._createShapeSnapshot(descendent))
|
||||
if (this.editor.isShapeOfType(descendent, FrameShapeUtil)) {
|
||||
if (this.editor.isShapeOfType<TLFrameShape>(descendent, 'frame')) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { intersectLineSegmentPolyline, pointInPolygon } from '@tldraw/primitives'
|
||||
import { TLScribble, TLShape, TLShapeId } from '@tldraw/tlschema'
|
||||
import { TLFrameShape, TLGroupShape, TLScribble, TLShape, TLShapeId } from '@tldraw/tlschema'
|
||||
import { ScribbleManager } from '../../../managers/ScribbleManager'
|
||||
import { ShapeUtil } from '../../../shapes/ShapeUtil'
|
||||
import { FrameShapeUtil } from '../../../shapes/frame/FrameShapeUtil'
|
||||
import { GroupShapeUtil } from '../../../shapes/group/GroupShapeUtil'
|
||||
import { TLEventHandlers } from '../../../types/event-types'
|
||||
import { StateNode } from '../../StateNode'
|
||||
|
||||
|
@ -106,9 +104,9 @@ export class ScribbleBrushing extends StateNode {
|
|||
util = this.editor.getShapeUtil(shape)
|
||||
|
||||
if (
|
||||
this.editor.isShapeOfType(shape, GroupShapeUtil) ||
|
||||
this.editor.isShapeOfType<TLGroupShape>(shape, 'group') ||
|
||||
this.newlySelectedIds.has(shape.id) ||
|
||||
(this.editor.isShapeOfType(shape, FrameShapeUtil) &&
|
||||
(this.editor.isShapeOfType<TLFrameShape>(shape, 'frame') &&
|
||||
util.hitTestPoint(shape, this.editor.getPointInShapeSpace(shape, originPagePoint))) ||
|
||||
this.editor.isShapeOrAncestorLocked(shape)
|
||||
) {
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import { Atom, Computed, atom, computed } from '@tldraw/state'
|
||||
import { TLBaseShape } from '@tldraw/tlschema'
|
||||
import type { Editor } from '../Editor'
|
||||
import { TLShapeUtilConstructor } from '../shapes/ShapeUtil'
|
||||
import {
|
||||
EVENT_NAME_MAP,
|
||||
TLEnterEventHandler,
|
||||
|
@ -69,7 +67,7 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
|
|||
id: string
|
||||
current: Atom<StateNode | undefined>
|
||||
type: TLStateNodeType
|
||||
shapeType?: TLShapeUtilConstructor<TLBaseShape<any, any>>
|
||||
shapeType?: string
|
||||
initial?: string
|
||||
children?: Record<string, StateNode>
|
||||
parent: StateNode
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { PageRecordType, TLShape, createShapeId } from '@tldraw/tlschema'
|
||||
import { defaultShapes } from '../config/defaultShapes'
|
||||
import { defineShape } from '../config/defineShape'
|
||||
import { BaseBoxShapeUtil } from '../editor/shapes/BaseBoxShapeUtil'
|
||||
import { GeoShapeUtil } from '../editor/shapes/geo/GeoShapeUtil'
|
||||
import { TestEditor } from './TestEditor'
|
||||
import { TL } from './jsx'
|
||||
|
||||
|
@ -435,58 +436,66 @@ describe('isFocused', () => {
|
|||
})
|
||||
|
||||
describe('getShapeUtil', () => {
|
||||
it('accepts shapes', () => {
|
||||
const geoShape = editor.getShapeById(ids.box1)!
|
||||
const geoUtil = editor.getShapeUtil(geoShape)
|
||||
expect(geoUtil).toBeInstanceOf(GeoShapeUtil)
|
||||
let myUtil: any
|
||||
|
||||
beforeEach(() => {
|
||||
class _MyFakeShapeUtil extends BaseBoxShapeUtil<any> {
|
||||
static type = 'blorg'
|
||||
type = 'blorg'
|
||||
|
||||
getDefaultProps() {
|
||||
return {
|
||||
w: 100,
|
||||
h: 100,
|
||||
}
|
||||
}
|
||||
component() {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
indicator() {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
}
|
||||
|
||||
myUtil = _MyFakeShapeUtil
|
||||
|
||||
const myShapeDef = defineShape('blorg', {
|
||||
util: _MyFakeShapeUtil,
|
||||
})
|
||||
|
||||
editor = new TestEditor({
|
||||
shapes: [...defaultShapes, myShapeDef],
|
||||
})
|
||||
|
||||
editor.createShapes([
|
||||
{ id: ids.box1, type: 'blorg', x: 100, y: 100, props: { w: 100, h: 100 } },
|
||||
])
|
||||
const page1 = editor.currentPageId
|
||||
editor.createPage('page 2', ids.page2)
|
||||
editor.setCurrentPageId(page1)
|
||||
})
|
||||
|
||||
it('accepts shape utils', () => {
|
||||
const geoUtil = editor.getShapeUtil(GeoShapeUtil)
|
||||
expect(geoUtil).toBeInstanceOf(GeoShapeUtil)
|
||||
it('accepts shapes', () => {
|
||||
const shape = editor.getShapeById(ids.box1)!
|
||||
const util = editor.getShapeUtil(shape)
|
||||
expect(util).toBeInstanceOf(myUtil)
|
||||
})
|
||||
|
||||
it('accepts shape types', () => {
|
||||
const util = editor.getShapeUtil('blorg')
|
||||
expect(util).toBeInstanceOf(myUtil)
|
||||
})
|
||||
|
||||
it('throws if that shape type isnt registered', () => {
|
||||
const myFakeShape = { type: 'fake' } as TLShape
|
||||
expect(() => editor.getShapeUtil(myFakeShape)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"No shape util found for type \\"fake\\""`
|
||||
)
|
||||
|
||||
class MyFakeShapeUtil extends BaseBoxShapeUtil<any> {
|
||||
static type = 'fake'
|
||||
|
||||
getDefaultProps() {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
component() {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
indicator() {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
}
|
||||
|
||||
expect(() => editor.getShapeUtil(MyFakeShapeUtil)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"No shape util found for type \\"fake\\""`
|
||||
const myMissingShape = { type: 'missing' } as TLShape
|
||||
expect(() => editor.getShapeUtil(myMissingShape)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"No shape util found for type \\"missing\\""`
|
||||
)
|
||||
})
|
||||
|
||||
it("throws if a shape util that isn't the one registered is passed in", () => {
|
||||
class MyFakeGeoShapeUtil extends BaseBoxShapeUtil<any> {
|
||||
static type = 'geo'
|
||||
|
||||
getDefaultProps() {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
component() {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
indicator() {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
}
|
||||
expect(() => editor.getShapeUtil(MyFakeGeoShapeUtil)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Shape util found for type \\"geo\\" is not an instance of the provided constructor"`
|
||||
it('throws if that type isnt registered', () => {
|
||||
expect(() => editor.getShapeUtil('missing')).toThrowErrorMatchingInlineSnapshot(
|
||||
`"No shape util found for type \\"missing\\""`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -301,7 +301,7 @@ describe('Custom shapes', () => {
|
|||
class CardTool extends BaseBoxShapeTool {
|
||||
static override id = 'card'
|
||||
static override initial = 'idle'
|
||||
override shapeType = CardUtil
|
||||
override shapeType = 'card'
|
||||
}
|
||||
|
||||
const tools = [CardTool]
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { createShapeId } from '@tldraw/tlschema'
|
||||
import { GeoShapeUtil } from '../../editor/shapes/geo/GeoShapeUtil'
|
||||
import { TestEditor } from '../TestEditor'
|
||||
|
||||
let editor: TestEditor
|
||||
|
@ -43,7 +42,7 @@ beforeEach(() => {
|
|||
describe('editor.rotateShapes', () => {
|
||||
it('Rotates shapes and fires events', () => {
|
||||
// Set start / change / end events on only the geo shape
|
||||
const util = editor.getShapeUtil(GeoShapeUtil)
|
||||
const util = editor.getShapeUtil('geo')
|
||||
|
||||
// Bad! who did this (did I do this)
|
||||
const fnStart = jest.fn()
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { TLArrowShape, TLShapePartial, createShapeId } from '@tldraw/tlschema'
|
||||
import { ArrowShapeUtil } from '../editor/shapes/arrow/ArrowShapeUtil'
|
||||
import { TestEditor } from './TestEditor'
|
||||
|
||||
let editor: TestEditor
|
||||
|
@ -189,7 +188,7 @@ describe('When duplicating shapes that include arrows', () => {
|
|||
.createShapes(shapes)
|
||||
.select(
|
||||
...editor.shapesArray
|
||||
.filter((s) => editor.isShapeOfType(s, ArrowShapeUtil))
|
||||
.filter((s) => editor.isShapeOfType<TLArrowShape>(s, 'arrow'))
|
||||
.map((s) => s.id)
|
||||
)
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import { createShapeId } from '@tldraw/tlschema'
|
||||
import { FrameShapeUtil } from '../editor/shapes/frame/FrameShapeUtil'
|
||||
import { GeoShapeUtil } from '../editor/shapes/geo/GeoShapeUtil'
|
||||
import { TLFrameShape, TLGeoShape, createShapeId } from '@tldraw/tlschema'
|
||||
import { TestEditor } from './TestEditor'
|
||||
|
||||
let editor: TestEditor
|
||||
|
@ -56,7 +54,7 @@ beforeEach(() => {
|
|||
describe('When interacting with a shape...', () => {
|
||||
it('fires rotate events', () => {
|
||||
// Set start / change / end events on only the geo shape
|
||||
const util = editor.getShapeUtil(FrameShapeUtil)
|
||||
const util = editor.getShapeUtil<TLFrameShape>('frame')
|
||||
|
||||
const fnStart = jest.fn()
|
||||
util.onRotateStart = fnStart
|
||||
|
@ -89,12 +87,12 @@ describe('When interacting with a shape...', () => {
|
|||
})
|
||||
|
||||
it('cleans up events', () => {
|
||||
const util = editor.getShapeUtil(GeoShapeUtil)
|
||||
const util = editor.getShapeUtil<TLGeoShape>('geo')
|
||||
expect(util.onRotateStart).toBeUndefined()
|
||||
})
|
||||
|
||||
it('fires double click handler event', () => {
|
||||
const util = editor.getShapeUtil(GeoShapeUtil)
|
||||
const util = editor.getShapeUtil<TLGeoShape>('geo')
|
||||
|
||||
const fnStart = jest.fn()
|
||||
util.onDoubleClick = fnStart
|
||||
|
@ -105,7 +103,7 @@ describe('When interacting with a shape...', () => {
|
|||
})
|
||||
|
||||
it('Fires resisizing events', () => {
|
||||
const util = editor.getShapeUtil(FrameShapeUtil)
|
||||
const util = editor.getShapeUtil<TLFrameShape>('frame')
|
||||
|
||||
const fnStart = jest.fn()
|
||||
util.onResizeStart = fnStart
|
||||
|
@ -142,7 +140,7 @@ describe('When interacting with a shape...', () => {
|
|||
})
|
||||
|
||||
it('Fires translating events', () => {
|
||||
const util = editor.getShapeUtil(FrameShapeUtil)
|
||||
const util = editor.getShapeUtil<TLFrameShape>('frame')
|
||||
|
||||
const fnStart = jest.fn()
|
||||
util.onTranslateStart = fnStart
|
||||
|
@ -170,7 +168,7 @@ describe('When interacting with a shape...', () => {
|
|||
})
|
||||
|
||||
it('Uses the shape utils onClick handler', () => {
|
||||
const util = editor.getShapeUtil(FrameShapeUtil)
|
||||
const util = editor.getShapeUtil<TLFrameShape>('frame')
|
||||
|
||||
const fnClick = jest.fn()
|
||||
util.onClick = fnClick
|
||||
|
@ -184,7 +182,7 @@ describe('When interacting with a shape...', () => {
|
|||
})
|
||||
|
||||
it('Uses the shape utils onClick handler', () => {
|
||||
const util = editor.getShapeUtil(FrameShapeUtil)
|
||||
const util = editor.getShapeUtil<TLFrameShape>('frame')
|
||||
|
||||
const fnClick = jest.fn((shape: any) => {
|
||||
return {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { DefaultFillStyle, TLArrowShape, createShapeId } from '@tldraw/tlschema'
|
||||
import { FrameShapeUtil } from '../../editor/shapes/frame/FrameShapeUtil'
|
||||
import { DefaultFillStyle, TLArrowShape, TLFrameShape, createShapeId } from '@tldraw/tlschema'
|
||||
import { TestEditor } from '../TestEditor'
|
||||
|
||||
let editor: TestEditor
|
||||
|
@ -33,7 +32,7 @@ describe('creating frames', () => {
|
|||
editor.setSelectedTool('frame')
|
||||
editor.pointerDown(100, 100).pointerUp(100, 100)
|
||||
expect(editor.onlySelectedShape?.type).toBe('frame')
|
||||
const { w, h } = editor.getShapeUtil(FrameShapeUtil).getDefaultProps()
|
||||
const { w, h } = editor.getShapeUtil<TLFrameShape>('frame').getDefaultProps()
|
||||
expect(editor.getPageBounds(editor.onlySelectedShape!)).toMatchObject({
|
||||
x: 100 - w / 2,
|
||||
y: 100 - h / 2,
|
||||
|
|
|
@ -1895,7 +1895,7 @@ describe('Group opacity', () => {
|
|||
editor.setOpacity(0.5)
|
||||
editor.groupShapes()
|
||||
const group = editor.getShapeById(onlySelectedId())!
|
||||
assert(editor.isShapeOfType(group, GroupShapeUtil))
|
||||
assert(editor.isShapeOfType<TLGroupShape>(group, 'group'))
|
||||
expect(group.opacity).toBe(1)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import { Box2d, Vec2d } from '@tldraw/primitives'
|
||||
import { TLShapeId, TLShapePartial, createShapeId } from '@tldraw/tlschema'
|
||||
import { TLArrowShape, TLShapeId, TLShapePartial, createShapeId } from '@tldraw/tlschema'
|
||||
import { GapsSnapLine, PointsSnapLine, SnapLine } from '../../editor/managers/SnapManager'
|
||||
import { ShapeUtil } from '../../editor/shapes/ShapeUtil'
|
||||
import { TestEditor } from '../TestEditor'
|
||||
|
||||
import { defaultShapes } from '../../config/defaultShapes'
|
||||
import { defineShape } from '../../config/defineShape'
|
||||
import { ArrowShapeUtil } from '../../editor/shapes/arrow/ArrowShapeUtil'
|
||||
import { getSnapLines } from '../testutils/getSnapLines'
|
||||
|
||||
type __TopLeftSnapOnlyShape = any
|
||||
|
@ -1950,7 +1949,7 @@ describe('translating a shape with a bound shape', () => {
|
|||
})
|
||||
|
||||
const newArrow = editor.shapesArray.find(
|
||||
(s) => editor.isShapeOfType(s, ArrowShapeUtil) && s.id !== arrow1
|
||||
(s) => editor.isShapeOfType<TLArrowShape>(s, 'arrow') && s.id !== arrow1
|
||||
)
|
||||
expect(newArrow).toMatchObject({
|
||||
props: { start: { type: 'binding' }, end: { type: 'point' } },
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import {
|
||||
ArrowShapeUtil,
|
||||
AssetRecordType,
|
||||
Editor,
|
||||
MAX_SHAPES_PER_PAGE,
|
||||
|
@ -518,7 +517,7 @@ export function buildFromV1Document(editor: Editor, document: LegacyTldrawDocume
|
|||
}
|
||||
|
||||
const v2ShapeId = v1ShapeIdsToV2ShapeIds.get(v1Shape.id)!
|
||||
const util = editor.getShapeUtil(ArrowShapeUtil)
|
||||
const util = editor.getShapeUtil<TLArrowShape>('arrow')
|
||||
|
||||
// dumb but necessary
|
||||
editor.inputs.ctrlKey = false
|
||||
|
|
|
@ -19,6 +19,7 @@ import { TLEditorAssetUrls } from '@tldraw/editor';
|
|||
import { TLExportType } from '@tldraw/editor';
|
||||
import { TLLanguage } from '@tldraw/editor';
|
||||
import { TLShapeId } from '@tldraw/editor';
|
||||
import { TLShapeId as TLShapeId_2 } from '@tldraw/tlschema';
|
||||
import { VecLike } from '@tldraw/primitives';
|
||||
|
||||
// @internal (undocumented)
|
||||
|
@ -643,7 +644,7 @@ export function useDialogs(): TLUiDialogsContextType;
|
|||
export function useEvents(): TLUiEventContextType;
|
||||
|
||||
// @public (undocumented)
|
||||
export function useExportAs(): (ids?: TLShapeId[], format?: TLExportType) => Promise<void>;
|
||||
export function useExportAs(): (ids?: TLShapeId_2[], format?: TLExportType) => Promise<void>;
|
||||
|
||||
// @public (undocumented)
|
||||
export function useHelpMenuSchema(): TLUiMenuSchema;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ArrowShapeUtil, Editor, useEditor } from '@tldraw/editor'
|
||||
import { Editor, TLArrowShape, useEditor } from '@tldraw/editor'
|
||||
import { useValue } from '@tldraw/state'
|
||||
import { assert, exhaustiveSwitchError } from '@tldraw/utils'
|
||||
import { TLUiActionItem } from './useActions'
|
||||
|
@ -139,10 +139,13 @@ function shapesWithUnboundArrows(editor: Editor) {
|
|||
|
||||
return selectedShapes.filter((shape) => {
|
||||
if (!shape) return false
|
||||
if (editor.isShapeOfType(shape, ArrowShapeUtil) && shape.props.start.type === 'binding') {
|
||||
if (
|
||||
editor.isShapeOfType<TLArrowShape>(shape, 'arrow') &&
|
||||
shape.props.start.type === 'binding'
|
||||
) {
|
||||
return false
|
||||
}
|
||||
if (editor.isShapeOfType(shape, ArrowShapeUtil) && shape.props.end.type === 'binding') {
|
||||
if (editor.isShapeOfType<TLArrowShape>(shape, 'arrow') && shape.props.end.type === 'binding') {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
|
|
@ -1,20 +1,14 @@
|
|||
import { ANIMATION_MEDIUM_MS, Editor, getEmbedInfo, openWindow, useEditor } from '@tldraw/editor'
|
||||
import { Box2d, TAU, Vec2d, approximately } from '@tldraw/primitives'
|
||||
import {
|
||||
ANIMATION_MEDIUM_MS,
|
||||
BookmarkShapeUtil,
|
||||
Editor,
|
||||
EmbedShapeUtil,
|
||||
GroupShapeUtil,
|
||||
TLBookmarkShape,
|
||||
TLEmbedShape,
|
||||
TLGroupShape,
|
||||
TLShapeId,
|
||||
TLShapePartial,
|
||||
TLTextShape,
|
||||
TextShapeUtil,
|
||||
createShapeId,
|
||||
getEmbedInfo,
|
||||
openWindow,
|
||||
useEditor,
|
||||
} from '@tldraw/editor'
|
||||
import { Box2d, TAU, Vec2d, approximately } from '@tldraw/primitives'
|
||||
} from '@tldraw/tlschema'
|
||||
import { compact } from '@tldraw/utils'
|
||||
import * as React from 'react'
|
||||
import { EditLinkDialog } from '../components/EditLinkDialog'
|
||||
|
@ -214,7 +208,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
|
|||
editor.selectedShapes
|
||||
.filter(
|
||||
(shape): shape is TLTextShape =>
|
||||
editor.isShapeOfType(shape, TextShapeUtil) && shape.props.autoSize === false
|
||||
editor.isShapeOfType<TLTextShape>(shape, 'text') && shape.props.autoSize === false
|
||||
)
|
||||
.map((shape) => {
|
||||
return {
|
||||
|
@ -243,7 +237,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
|
|||
return
|
||||
}
|
||||
const shape = editor.getShapeById(ids[0])
|
||||
if (!shape || !editor.isShapeOfType(shape, EmbedShapeUtil)) {
|
||||
if (!shape || !editor.isShapeOfType<TLEmbedShape>(shape, 'embed')) {
|
||||
console.error(warnMsg)
|
||||
return
|
||||
}
|
||||
|
@ -262,7 +256,8 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
|
|||
const createList: TLShapePartial[] = []
|
||||
const deleteList: TLShapeId[] = []
|
||||
for (const shape of shapes) {
|
||||
if (!shape || !editor.isShapeOfType(shape, EmbedShapeUtil) || !shape.props.url) continue
|
||||
if (!shape || !editor.isShapeOfType<TLEmbedShape>(shape, 'embed') || !shape.props.url)
|
||||
continue
|
||||
|
||||
const newPos = new Vec2d(shape.x, shape.y)
|
||||
newPos.rot(-shape.rotation)
|
||||
|
@ -300,7 +295,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
|
|||
const createList: TLShapePartial[] = []
|
||||
const deleteList: TLShapeId[] = []
|
||||
for (const shape of shapes) {
|
||||
if (!editor.isShapeOfType(shape, BookmarkShapeUtil)) continue
|
||||
if (!editor.isShapeOfType<TLBookmarkShape>(shape, 'bookmark')) continue
|
||||
|
||||
const { url } = shape.props
|
||||
|
||||
|
@ -383,7 +378,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
|
|||
onSelect(source) {
|
||||
trackEvent('group-shapes', { source })
|
||||
const { onlySelectedShape } = editor
|
||||
if (onlySelectedShape && editor.isShapeOfType(onlySelectedShape, GroupShapeUtil)) {
|
||||
if (onlySelectedShape && editor.isShapeOfType<TLGroupShape>(onlySelectedShape, 'group')) {
|
||||
editor.mark('ungroup')
|
||||
editor.ungroupShapes(editor.selectedIds)
|
||||
} else {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import {
|
||||
ArrowShapeUtil,
|
||||
BookmarkShapeUtil,
|
||||
Editor,
|
||||
EmbedShapeUtil,
|
||||
GeoShapeUtil,
|
||||
TLArrowShape,
|
||||
TLBookmarkShape,
|
||||
TLContent,
|
||||
TextShapeUtil,
|
||||
TLEmbedShape,
|
||||
TLGeoShape,
|
||||
TLTextShape,
|
||||
getValidHttpURLList,
|
||||
isSvgText,
|
||||
isValidHttpURL,
|
||||
|
@ -508,15 +508,15 @@ const handleNativeOrMenuCopy = (editor: Editor) => {
|
|||
const textItems = content.shapes
|
||||
.map((shape) => {
|
||||
if (
|
||||
editor.isShapeOfType(shape, TextShapeUtil) ||
|
||||
editor.isShapeOfType(shape, GeoShapeUtil) ||
|
||||
editor.isShapeOfType(shape, ArrowShapeUtil)
|
||||
editor.isShapeOfType<TLTextShape>(shape, 'text') ||
|
||||
editor.isShapeOfType<TLGeoShape>(shape, 'geo') ||
|
||||
editor.isShapeOfType<TLArrowShape>(shape, 'arrow')
|
||||
) {
|
||||
return shape.props.text
|
||||
}
|
||||
if (
|
||||
editor.isShapeOfType(shape, BookmarkShapeUtil) ||
|
||||
editor.isShapeOfType(shape, EmbedShapeUtil)
|
||||
editor.isShapeOfType<TLBookmarkShape>(shape, 'bookmark') ||
|
||||
editor.isShapeOfType<TLEmbedShape>(shape, 'embed')
|
||||
) {
|
||||
return shape.props.url
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { BookmarkShapeUtil, Editor, EmbedShapeUtil, getEmbedInfo, useEditor } from '@tldraw/editor'
|
||||
import { Editor, TLBookmarkShape, TLEmbedShape, getEmbedInfo, useEditor } from '@tldraw/editor'
|
||||
import { track, useValue } from '@tldraw/state'
|
||||
import React, { useMemo } from 'react'
|
||||
import {
|
||||
|
@ -65,7 +65,7 @@ export const TLUiContextMenuSchemaProvider = track(function TLUiContextMenuSchem
|
|||
if (editor.selectedIds.length !== 1) return false
|
||||
return editor.selectedIds.some((selectedId) => {
|
||||
const shape = editor.getShapeById(selectedId)
|
||||
return shape && editor.isShapeOfType(shape, EmbedShapeUtil) && shape.props.url
|
||||
return shape && editor.isShapeOfType<TLEmbedShape>(shape, 'embed') && shape.props.url
|
||||
})
|
||||
},
|
||||
[]
|
||||
|
@ -78,7 +78,7 @@ export const TLUiContextMenuSchemaProvider = track(function TLUiContextMenuSchem
|
|||
const shape = editor.getShapeById(selectedId)
|
||||
return (
|
||||
shape &&
|
||||
editor.isShapeOfType(shape, BookmarkShapeUtil) &&
|
||||
editor.isShapeOfType<TLBookmarkShape>(shape, 'bookmark') &&
|
||||
shape.props.url &&
|
||||
getEmbedInfo(shape.props.url)
|
||||
)
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import {
|
||||
FrameShapeUtil,
|
||||
TLExportType,
|
||||
TLShapeId,
|
||||
downloadDataURLAsFile,
|
||||
getSvgAsDataUrl,
|
||||
getSvgAsImage,
|
||||
useEditor,
|
||||
} from '@tldraw/editor'
|
||||
import { TLFrameShape, TLShapeId } from '@tldraw/tlschema'
|
||||
import { useCallback } from 'react'
|
||||
import { useToasts } from './useToastsProvider'
|
||||
import { useTranslation } from './useTranslation/useTranslation'
|
||||
|
@ -38,7 +37,7 @@ export function useExportAs() {
|
|||
|
||||
if (ids.length === 1) {
|
||||
const first = editor.getShapeById(ids[0])!
|
||||
if (editor.isShapeOfType(first, FrameShapeUtil)) {
|
||||
if (editor.isShapeOfType<TLFrameShape>(first, 'frame')) {
|
||||
name = first.props.name ?? 'frame'
|
||||
} else {
|
||||
name = first.id.replace(/:/, '_')
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
import {
|
||||
ArrowShapeUtil,
|
||||
DrawShapeUtil,
|
||||
GroupShapeUtil,
|
||||
LineShapeUtil,
|
||||
useEditor,
|
||||
} from '@tldraw/editor'
|
||||
import { TLArrowShape, TLGroupShape, TLLineShape, useEditor } from '@tldraw/editor'
|
||||
import { useValue } from '@tldraw/state'
|
||||
import { TLDrawShape } from '@tldraw/tlschema'
|
||||
|
||||
export function useOnlyFlippableShape() {
|
||||
const editor = useEditor()
|
||||
|
@ -17,10 +12,10 @@ export function useOnlyFlippableShape() {
|
|||
selectedShapes.length === 1 &&
|
||||
selectedShapes.every(
|
||||
(shape) =>
|
||||
editor.isShapeOfType(shape, GroupShapeUtil) ||
|
||||
editor.isShapeOfType(shape, ArrowShapeUtil) ||
|
||||
editor.isShapeOfType(shape, LineShapeUtil) ||
|
||||
editor.isShapeOfType(shape, DrawShapeUtil)
|
||||
editor.isShapeOfType<TLGroupShape>(shape, 'group') ||
|
||||
editor.isShapeOfType<TLArrowShape>(shape, 'arrow') ||
|
||||
editor.isShapeOfType<TLLineShape>(shape, 'line') ||
|
||||
editor.isShapeOfType<TLDrawShape>(shape, 'draw')
|
||||
)
|
||||
)
|
||||
},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { TextShapeUtil, useEditor } from '@tldraw/editor'
|
||||
import { TLTextShape, useEditor } from '@tldraw/editor'
|
||||
import { useValue } from '@tldraw/state'
|
||||
|
||||
export function useShowAutoSizeToggle() {
|
||||
|
@ -9,7 +9,7 @@ export function useShowAutoSizeToggle() {
|
|||
const { selectedShapes } = editor
|
||||
return (
|
||||
selectedShapes.length === 1 &&
|
||||
editor.isShapeOfType(selectedShapes[0], TextShapeUtil) &&
|
||||
editor.isShapeOfType<TLTextShape>(selectedShapes[0], 'text') &&
|
||||
selectedShapes[0].props.autoSize === false
|
||||
)
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue