diff --git a/apps/examples/src/examples/toSvg-method-example/CustomShapeToSvgExample.tsx b/apps/examples/src/examples/toSvg-method-example/CustomShapeToSvgExample.tsx new file mode 100644 index 000000000..89ef4c4ae --- /dev/null +++ b/apps/examples/src/examples/toSvg-method-example/CustomShapeToSvgExample.tsx @@ -0,0 +1,144 @@ +import { ReactElement } from 'react' +import { + Geometry2d, + HTMLContainer, + RecordProps, + Rectangle2d, + ShapeUtil, + SvgExportContext, + T, + TLBaseShape, + Tldraw, +} from 'tldraw' + +import 'tldraw/tldraw.css' + +// There's a guide at the bottom of this file! + +type ICustomShape = TLBaseShape< + 'my-custom-shape', + { + w: number + h: number + } +> + +const LIGHT_FILL = '#ff8888' +const DARK_FILL = '#ffcccc' + +export class MyShapeUtil extends ShapeUtil { + static override type = 'my-custom-shape' as const + static override props: RecordProps = { + w: T.number, + h: T.number, + } + + getDefaultProps(): ICustomShape['props'] { + return { + w: 200, + h: 200, + } + } + + override canEdit = () => false + override canResize = () => false + override isAspectRatioLocked = () => false + + getGeometry(shape: ICustomShape): Geometry2d { + return new Rectangle2d({ + width: shape.props.w, + height: shape.props.h, + isFilled: true, + }) + } + + component(_shape: ICustomShape) { + const isDarkmode = this.editor.user.getIsDarkMode() + return + } + + indicator(shape: ICustomShape) { + return this.getSvgRect(shape) + } + + // [1] + override toSvg( + shape: ICustomShape, + ctx: SvgExportContext + ): ReactElement | null | Promise { + // ctx.addExportDef(getFontDef(shape)) + const isDarkmode = ctx.isDarkMode + const fill = isDarkmode ? DARK_FILL : LIGHT_FILL + return this.getSvgRect(shape, { fill }) + } + + getSvgRect(shape: ICustomShape, props?: { fill: string }) { + return + } + + // [2] + + // override toBackgroundSvg( + // shape: ICustomShape, + // ctx: SvgExportContext + // ): ReactElement | null | Promise { + // const isDarkmode = ctx.isDarkMode + // const fill = isDarkmode ? '#333' : '#efefef' + // return + // } +} + +// [3] + +// function getFontDef(shape: ICustomShape): SvgExportDef { +// // +// return { +// some unique key, +// key: 'my-custom-shape-font', +// getElement: async () => { +// return element +// check out the defaultStyleDefs.tsx file for an example of how +// we do this for tldraw fonts +// }, +// } +// } + +const customShape = [MyShapeUtil] +export default function CustomShapeToSvgExample() { + return ( +
+ { + editor.createShape({ type: 'my-custom-shape', x: 100, y: 100 }) + }} + /> +
+ ) +} +/* + The "export as SVG/PNG" and "copy as SVG/PNG" actions use the `toSvg` or `toBackgroundSvg` + methods of a shape util. If a shape does not have a `toSvg` or `toBackgroundSvg` method + defined, it will default to an empty box. + + For more information on creating a custom shape, check out the custom shape example. + + [1] + This method should return a React element that represents the shape as an SVG element. + If your shape is HTML, then you will need to convert it to an SVG representation. In this + example we've used a `rect` element to represent the shape. Other shapes may require more + complex work to render them as SVGs, especially if they contain text. Check out [3] for more + info. + +[2] + The `toBackgroundSvg` method is used to render a layer behind the shape when exporting as SVG. + We use this in the tldraw codebase to make the highlighter shape. It's commented out here as + we don't need it for this example. + +[3] + If your shape contains text, you may need to add a font definition to the SVG. This is done + using the `addExportDef` method of the `SvgExportContext`. Your font def must contain a unique + key and a function that returns a React element. Check out the `getFontDefForExport` function + in the `defaultStyleDefs.tsx` file for an example of how this is done for tldraw fonts. + + */ diff --git a/apps/examples/src/examples/toSvg-method-example/README.md b/apps/examples/src/examples/toSvg-method-example/README.md new file mode 100644 index 000000000..2ab08bb4f --- /dev/null +++ b/apps/examples/src/examples/toSvg-method-example/README.md @@ -0,0 +1,13 @@ +--- +title: Shape toSvg method +component: ./CustomShapeToSvgExample.tsx +category: shapes/tools +priority: 3 +keywords: [basic, svg, custom, export, copy] +--- + +How to use the toSvg method of a shape util to determine how your custom shapes look when copied/exported as an image. + +--- + +The "export as SVG/PNG" and "copy as SVG/PNG" actions use the `toSvg` or `toBackgroundSvg` methods of a shape util. If a shape does not have a `toSvg` or `toBackgroundSvg` method defined, it will default to an empty box.