
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
103 lines
2.6 KiB
TypeScript
103 lines
2.6 KiB
TypeScript
import {
|
|
BaseBoxShapeTool,
|
|
BaseBoxShapeUtil,
|
|
DefaultColorStyle,
|
|
HTMLContainer,
|
|
StyleProp,
|
|
TLBaseShape,
|
|
TLDefaultColorStyle,
|
|
defineShape,
|
|
getDefaultColorTheme,
|
|
} from '@tldraw/tldraw'
|
|
import { T } from '@tldraw/validate'
|
|
|
|
// Define a style that can be used across multiple shapes.
|
|
// The ID (myApp:filter) must be globally unique, so we recommend prefixing it with a namespace.
|
|
export const MyFilterStyle = StyleProp.defineEnum('myApp:filter', {
|
|
defaultValue: 'none',
|
|
values: ['none', 'invert', 'grayscale', 'blur'],
|
|
})
|
|
|
|
export type MyFilterStyle = T.TypeOf<typeof MyFilterStyle>
|
|
|
|
export type CardShape = TLBaseShape<
|
|
'card',
|
|
{
|
|
w: number
|
|
h: number
|
|
color: TLDefaultColorStyle
|
|
filter: MyFilterStyle
|
|
}
|
|
>
|
|
|
|
export class CardShapeUtil extends BaseBoxShapeUtil<CardShape> {
|
|
static override type = 'card' as const
|
|
|
|
override isAspectRatioLocked = (_shape: CardShape) => false
|
|
override canResize = (_shape: CardShape) => true
|
|
override canBind = (_shape: CardShape) => true
|
|
|
|
override getDefaultProps(): CardShape['props'] {
|
|
return {
|
|
w: 300,
|
|
h: 300,
|
|
color: 'black',
|
|
filter: 'none',
|
|
}
|
|
}
|
|
|
|
component(shape: CardShape) {
|
|
const bounds = this.editor.getBounds(shape)
|
|
const theme = getDefaultColorTheme(this.editor)
|
|
|
|
return (
|
|
<HTMLContainer
|
|
id={shape.id}
|
|
style={{
|
|
border: `4px solid ${theme[shape.props.color].solid}`,
|
|
borderRadius: 4,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
pointerEvents: 'all',
|
|
filter: this.filterStyleToCss(shape.props.filter),
|
|
backgroundColor: theme[shape.props.color].semi,
|
|
}}
|
|
>
|
|
🍇🫐🍏🍋🍊🍒 {bounds.w.toFixed()}x{bounds.h.toFixed()} 🍒🍊🍋🍏🫐🍇
|
|
</HTMLContainer>
|
|
)
|
|
}
|
|
|
|
// Indicator — used when hovering over a shape or when it's selected; must return only SVG elements here
|
|
indicator(shape: CardShape) {
|
|
return <rect width={shape.props.w} height={shape.props.h} />
|
|
}
|
|
|
|
filterStyleToCss(filter: MyFilterStyle) {
|
|
if (filter === 'invert') return 'invert(100%)'
|
|
if (filter === 'grayscale') return 'grayscale(100%)'
|
|
if (filter === 'blur') return 'blur(10px)'
|
|
return 'none'
|
|
}
|
|
}
|
|
|
|
// 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'
|
|
}
|
|
|
|
export const CardShape = defineShape('card', {
|
|
util: CardShapeUtil,
|
|
// to use a style prop, you need to describe all the props in your shape.
|
|
props: {
|
|
w: T.number,
|
|
h: T.number,
|
|
// You can re-use tldraw built-in styles...
|
|
color: DefaultColorStyle,
|
|
// ...or your own custom styles.
|
|
filter: MyFilterStyle,
|
|
},
|
|
})
|