annotate custom config example (#2404)
Annotates the custom config example with more detail ### Change Type - [ ] `patch` — Bug fix - [ ] `minor` — New feature - [ ] `major` — Breaking change - [ ] `dependencies` — Changes to package dependencies[^1] - [x] `documentation` — Changes to the documentation only[^2] - [ ] `tests` — Changes to any test code only[^2] - [ ] `internal` — Any other changes that don't affect the published package[^2] - [ ] I don't know [^1]: publishes a `patch` release, for devDependencies use `internal` [^2]: will not publish a new version ### Test Plan 1. Add a step-by-step description of how to test your PR here. 2. - [ ] Unit Tests - [ ] End to end tests ### Release Notes - Adds annotation to the custom config example with a bit more detail
This commit is contained in:
parent
85c5210cd1
commit
c198283c78
7 changed files with 103 additions and 31 deletions
|
@ -1,7 +1,4 @@
|
|||
import { BaseBoxShapeTool, TLClickEvent } from '@tldraw/tldraw'
|
||||
|
||||
// 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'
|
||||
|
@ -12,3 +9,12 @@ export class CardShapeTool extends BaseBoxShapeTool {
|
|||
// check the BaseBoxShapeTool source as an example
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
This file contains our custom tool. The tool is a StateNode with the `id` "card".
|
||||
|
||||
We get a lot of functionality for free by extending the BaseBoxShapeTool. but we can
|
||||
handle events in out own way by overriding methods like onDoubleClick. For an example
|
||||
of a tool with more custom functionality, check out the screenshot-tool example.
|
||||
|
||||
*/
|
||||
|
|
|
@ -11,31 +11,30 @@ import { cardShapeMigrations } from './card-shape-migrations'
|
|||
import { cardShapeProps } from './card-shape-props'
|
||||
import { ICardShape } from './card-shape-types'
|
||||
|
||||
// A utility class for the card shape. This is where you define
|
||||
// the shape's behavior, how it renders (its component and
|
||||
// indicator), and how it handles different events.
|
||||
// There's a guide at the bottom of this file!
|
||||
|
||||
export class CardShapeUtil extends ShapeUtil<ICardShape> {
|
||||
static override type = 'card' as const
|
||||
// A validation schema for the shape's props (optional)
|
||||
// [1]
|
||||
static override props = cardShapeProps
|
||||
// Migrations for upgrading shapes (optional)
|
||||
// [2]
|
||||
static override migrations = cardShapeMigrations
|
||||
|
||||
// Flags
|
||||
// [3]
|
||||
override isAspectRatioLocked = (_shape: ICardShape) => false
|
||||
override canResize = (_shape: ICardShape) => true
|
||||
override canBind = (_shape: ICardShape) => true
|
||||
|
||||
// [4]
|
||||
getDefaultProps(): ICardShape['props'] {
|
||||
return {
|
||||
w: 300,
|
||||
h: 300,
|
||||
color: 'black',
|
||||
weight: 'regular',
|
||||
}
|
||||
}
|
||||
|
||||
// [5]
|
||||
getGeometry(shape: ICardShape) {
|
||||
return new Rectangle2d({
|
||||
width: shape.props.w,
|
||||
|
@ -44,12 +43,12 @@ export class CardShapeUtil extends ShapeUtil<ICardShape> {
|
|||
})
|
||||
}
|
||||
|
||||
// Render method — the React component that will be rendered for the shape
|
||||
// [6]
|
||||
component(shape: ICardShape) {
|
||||
const bounds = this.editor.getShapeGeometry(shape).bounds
|
||||
const theme = getDefaultColorTheme({ isDarkMode: this.editor.user.getIsDarkMode() })
|
||||
|
||||
// Unfortunately eslint will think this is a class components
|
||||
//[a]
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const [count, setCount] = useState(0)
|
||||
|
||||
|
@ -64,15 +63,13 @@ export class CardShapeUtil extends ShapeUtil<ICardShape> {
|
|||
justifyContent: 'center',
|
||||
pointerEvents: 'all',
|
||||
backgroundColor: theme[shape.props.color].semi,
|
||||
fontWeight: shape.props.weight,
|
||||
color: theme[shape.props.color].solid,
|
||||
}}
|
||||
>
|
||||
<h2>Clicks: {count}</h2>
|
||||
<button
|
||||
// [b]
|
||||
onClick={() => setCount((count) => count + 1)}
|
||||
// You need to stop the pointer down event on buttons
|
||||
// that should prevent shape selection or click and drag
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
{bounds.w.toFixed()}x{bounds.h.toFixed()}
|
||||
|
@ -81,13 +78,54 @@ export class CardShapeUtil extends ShapeUtil<ICardShape> {
|
|||
)
|
||||
}
|
||||
|
||||
// Indicator — used when hovering over a shape or when it's selected; must return only SVG elements here
|
||||
// [7]
|
||||
indicator(shape: ICardShape) {
|
||||
return <rect width={shape.props.w} height={shape.props.h} />
|
||||
}
|
||||
|
||||
// Events
|
||||
// [8]
|
||||
override onResize: TLOnResizeHandler<ICardShape> = (shape, info) => {
|
||||
return resizeBox(shape, info)
|
||||
}
|
||||
}
|
||||
/*
|
||||
A utility class for the card shape. This is where you define the shape's behavior,
|
||||
how it renders (its component and indicator), and how it handles different events.
|
||||
|
||||
[1]
|
||||
A validation schema for the shape's props (optional)
|
||||
Check out card-shape-props.ts for more info.
|
||||
|
||||
[2]
|
||||
Migrations for upgrading shapes (optional)
|
||||
Check out card-shape-migrations.ts for more info.
|
||||
|
||||
[3]
|
||||
Letting the editor know if the shape's aspect ratio is locked, and whether it
|
||||
can be resized or bound to other shapes.
|
||||
|
||||
[4]
|
||||
The default props the shape will be rendered with when click-creating one.
|
||||
|
||||
[5]
|
||||
We use this to calculate the shape's geometry for hit-testing, bindings and
|
||||
doing other geometric calculations.
|
||||
|
||||
[6]
|
||||
Render method — the React component that will be rendered for the shape. It takes the
|
||||
shape as an argument. HTMLContainer is just a div that's being used to wrap our text
|
||||
and button. We can get the shape's bounds using our own getGeometry method.
|
||||
|
||||
- [a] Check it out! We can do normal React stuff here like using setState.
|
||||
Annoying: eslint sometimes thinks this is a class component, but it's not.
|
||||
|
||||
- [b] You need to stop the pointer down event on buttons, otherwise the editor will
|
||||
think you're trying to select drag the shape.
|
||||
|
||||
[7]
|
||||
Indicator — used when hovering over a shape or when it's selected; must return only SVG elements here
|
||||
|
||||
[8]
|
||||
Resize handler — called when the shape is resized. Sometimes you'll want to do some
|
||||
custom logic here, but for our purposes, this is fine.
|
||||
*/
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
import { DefaultColorStyle, ShapeProps, StyleProp, T } from '@tldraw/tldraw'
|
||||
import { DefaultColorStyle, ShapeProps, T } from '@tldraw/tldraw'
|
||||
import { ICardShape } from './card-shape-types'
|
||||
|
||||
export const WeightStyle = StyleProp.defineEnum('myApp:weight', {
|
||||
defaultValue: 'regular',
|
||||
values: ['regular', 'bold'],
|
||||
})
|
||||
|
||||
// Validation for our custom card shape's props, using our custom style + one of tldraw's default styles
|
||||
// Validation for our custom card shape's props, using one of tldraw's default styles
|
||||
export const cardShapeProps: ShapeProps<ICardShape> = {
|
||||
w: T.number,
|
||||
h: T.number,
|
||||
color: DefaultColorStyle,
|
||||
weight: WeightStyle,
|
||||
}
|
||||
|
||||
// To generate your own custom styles, check out the custom styles example.
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
import { TLBaseShape, TLDefaultColorStyle } from '@tldraw/tldraw'
|
||||
|
||||
// We'll have a custom style called weight
|
||||
export type IWeightStyle = 'regular' | 'bold'
|
||||
|
||||
// A type for our custom card shape
|
||||
export type ICardShape = TLBaseShape<
|
||||
'card',
|
||||
|
@ -10,6 +7,5 @@ export type ICardShape = TLBaseShape<
|
|||
w: number
|
||||
h: number
|
||||
color: TLDefaultColorStyle
|
||||
weight: IWeightStyle
|
||||
}
|
||||
>
|
||||
|
|
|
@ -4,9 +4,13 @@ import { CardShapeTool } from './CardShape/CardShapeTool'
|
|||
import { CardShapeUtil } from './CardShape/CardShapeUtil'
|
||||
import { uiOverrides } from './ui-overrides'
|
||||
|
||||
// There's a guide at the bottom of this file!
|
||||
|
||||
// [1]
|
||||
const customShapeUtils = [CardShapeUtil]
|
||||
const customTools = [CardShapeTool]
|
||||
|
||||
// [2]
|
||||
export default function CustomConfigExample() {
|
||||
return (
|
||||
<div className="tldraw__editor">
|
||||
|
@ -21,3 +25,20 @@ export default function CustomConfigExample() {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
Introduction:
|
||||
|
||||
This example shows how to create a custom shape, and add your own icon for it to the toolbar.
|
||||
Check out CardShapeUtil.tsx and CardShapeTool.tsx to see how we define the shape util and tool.
|
||||
Check out ui-overrides.ts for more info on how to add your icon to the toolbar.
|
||||
|
||||
[1]
|
||||
We define an array to hold the custom shape util and custom tool. It's important to do this outside of
|
||||
any React component so that this array doesn't get redefined on every render.
|
||||
|
||||
[2]
|
||||
Now we'll pass these arrays into the Tldraw component's props, along with our ui overrides.
|
||||
|
||||
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { TLUiMenuGroup, TLUiOverrides, menuItem, toolbarItem } from '@tldraw/tldraw'
|
||||
|
||||
// In order to see select our custom shape tool, we need to add it to the ui.
|
||||
// There's a guide at the bottom of this file!
|
||||
|
||||
export const uiOverrides: TLUiOverrides = {
|
||||
tools(editor, tools) {
|
||||
|
@ -31,3 +31,18 @@ export const uiOverrides: TLUiOverrides = {
|
|||
return keyboardShortcutsMenu
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
This file contains overrides for the Tldraw UI. These overrides are used to add your custom tools
|
||||
to the toolbar and the keyboard shortcuts menu.
|
||||
|
||||
We do this by providing a custom toolbar override to the Tldraw component. This override is a
|
||||
function that takes the current editor, the default toolbar items, and the default tools.
|
||||
It returns the new toolbar items. We use the toolbarItem helper to create a new toolbar item
|
||||
for our custom tool. We then splice it into the toolbar items array at the 4th index. This puts
|
||||
it after the eraser tool. We'll pass our overrides object into the Tldraw component's `overrides`
|
||||
prop.
|
||||
|
||||
|
||||
*/
|
||||
|
|
|
@ -7,7 +7,7 @@ export class SpeechBubbleTool extends BaseBoxShapeTool {
|
|||
}
|
||||
|
||||
/*
|
||||
This file contains our speech bubble tool. The tool is a StateNode with the `id` "screenshot".
|
||||
This file contains our speech bubble tool. The tool is a StateNode with the `id` "speech-bubble".
|
||||
|
||||
We get a lot of functionality for free by extending the BaseBoxShapeTool. For an example of a tool
|
||||
with more custom functionality, check out the screenshot-tool example.
|
||||
|
|
Loading…
Reference in a new issue