diff --git a/apps/docs/components/mdx-components/api-docs.tsx b/apps/docs/components/mdx-components/api-docs.tsx
index 6ddd2867f..fd3b0e1c5 100644
--- a/apps/docs/components/mdx-components/api-docs.tsx
+++ b/apps/docs/components/mdx-components/api-docs.tsx
@@ -1,4 +1,5 @@
import classNames from 'classnames'
+import Link from 'next/link'
import { ReactNode } from 'react'
import { Icon } from '../Icon'
@@ -35,32 +36,56 @@ export function TitleWithSourceLink({
source,
large,
tags,
+ inherited,
}: {
children: ReactNode
source?: string | null
large?: boolean
tags?: string[]
+ inherited?: { name: string; link: string }
}) {
return (
-
- {children}
-
- {tags?.map((tag) =>
{tag})}
- {source && (
-
-
-
+ <>
+
+ {children}
+
+ {tags?.map((tag) => (
+
+ {tag}
+
+ ))}
+ {source && (
+
+
+
+ )}
+
-
+ {inherited && (
+
+ from{' '}
+
+
+ {inherited.name}
+
+
+
+ )}
+ >
)
}
-export function Tag({ children }: { children: string }) {
- return
{children}
+export function Tag({ children, tag }: { children: ReactNode; tag: string }) {
+ return
{children}
}
diff --git a/apps/docs/scripts/functions/createApiMarkdown.ts b/apps/docs/scripts/functions/createApiMarkdown.ts
index 56c4575c4..e886c48b0 100644
--- a/apps/docs/scripts/functions/createApiMarkdown.ts
+++ b/apps/docs/scripts/functions/createApiMarkdown.ts
@@ -85,4 +85,6 @@ export async function createApiMarkdown() {
)
sectionsJson.push(apiInputSection)
fs.writeFileSync(sectionsJsonPath, JSON.stringify(sectionsJson, null, '\t') + '\n')
+
+ model.throwEncounteredErrors()
}
diff --git a/apps/docs/scripts/functions/getApiMarkdown.ts b/apps/docs/scripts/functions/getApiMarkdown.ts
index d5c2038b4..271fad133 100644
--- a/apps/docs/scripts/functions/getApiMarkdown.ts
+++ b/apps/docs/scripts/functions/getApiMarkdown.ts
@@ -22,6 +22,7 @@ import {
ApiTypeAlias,
ApiVariable,
Excerpt,
+ HeritageType,
} from '@microsoft/api-extractor-model'
import { MarkdownWriter, formatWithPrettier, getPath, getSlug } from '../utils'
@@ -36,6 +37,11 @@ const date = new Intl.DateTimeFormat('en-US', {
day: '2-digit',
}).format(new Date())
+interface Member {
+ item: ApiItem
+ inheritedFrom: ApiItem | null
+}
+
export async function getApiMarkdown(
model: TldrawApiModel,
categoryName: string,
@@ -49,100 +55,59 @@ export async function getApiMarkdown(
const isComponent = model.isComponent(item)
const componentProps = isComponent ? model.getReactPropsItem(item) : null
- const members = componentProps?.members ?? item.members
- if (members) {
- const constructors = []
- const properties = []
- const methods = []
- for (const member of members) {
- switch (member.kind) {
- case ApiItemKind.Constructor:
- case ApiItemKind.ConstructSignature:
- constructors.push(member)
- break
- case ApiItemKind.Variable:
- case ApiItemKind.Property:
- case ApiItemKind.PropertySignature:
- properties.push(member)
- break
- case ApiItemKind.Method:
- case ApiItemKind.Function:
- case ApiItemKind.MethodSignature:
- if (isComponent) {
- properties.push(member)
- } else {
- methods.push(member)
- }
-
- break
- case ApiItemKind.EnumMember:
- case ApiItemKind.Class:
- case ApiItemKind.TypeAlias:
- case ApiItemKind.Interface:
- // TODO: document these
- break
- default:
- throw new Error(`Unknown member kind: ${member.kind} ${member.displayName}`)
- }
- }
+ const contents = collectMembersAndExtends(model, item)
+ if (contents.constructors.length) {
const constructorResult: Result = { markdown: '', keywords: [] }
+ for (const member of contents.constructors) {
+ await addMarkdownForMember(model, constructorResult, member)
+ addHorizontalRule(constructorResult)
+ }
+ addMarkdown(membersResult, constructorResult.markdown)
+ }
+
+ if (contents.properties.length || isComponent) {
const propertiesResult: Result = { markdown: '', keywords: [] }
+ if (componentProps) addExtends(propertiesResult, item, contents.heritage)
+ for (const member of contents.properties) {
+ const slug = getSlug(member.item)
+ addMarkdown(toc, ` - [${member.item.displayName}](#${slug})\n`)
+ await addMarkdownForMember(model, propertiesResult, member, {
+ isComponentProp: isComponent,
+ })
+ addHorizontalRule(propertiesResult)
+ }
+ if (
+ componentProps &&
+ componentProps instanceof ApiDeclaredItem &&
+ componentProps?.kind !== 'Interface'
+ ) {
+ propertiesResult.markdown += await excerptToMarkdown(componentProps, componentProps.excerpt, {
+ kind: componentProps.kind,
+ })
+ }
+
+ if (propertiesResult.markdown.trim()) {
+ addMarkdown(toc, `- [Properties](#properties)\n`)
+ addMarkdown(membersResult, `## Properties\n\n`)
+ addMarkdown(membersResult, propertiesResult.markdown)
+ } else if (isComponent && !componentProps) {
+ addMarkdown(membersResult, `## Properties\n\n`)
+ addMarkdown(membersResult, `This component does not take any props.\n\n`)
+ }
+ }
+
+ if (contents.methods.length) {
const methodsResult: Result = { markdown: '', keywords: [] }
-
- if (constructors.length) {
- for (const member of constructors) {
- await addMarkdownForMember(model, constructorResult, member)
- addHorizontalRule(constructorResult)
- }
- addMarkdown(membersResult, constructorResult.markdown)
- }
-
- if (properties.length || isComponent) {
- if (componentProps) addExtends(propertiesResult, componentProps)
- for (const member of properties) {
- const slug = getSlug(member)
- addMarkdown(toc, ` - [${member.displayName}](#${slug})\n`)
- await addMarkdownForMember(model, propertiesResult, member, {
- isComponentProp: isComponent,
- })
- addHorizontalRule(propertiesResult)
- }
- if (
- componentProps &&
- componentProps instanceof ApiDeclaredItem &&
- componentProps?.kind !== 'Interface'
- ) {
- propertiesResult.markdown += await excerptToMarkdown(
- componentProps,
- componentProps.excerpt,
- {
- kind: componentProps.kind,
- }
- )
- }
-
- if (propertiesResult.markdown.trim()) {
- addMarkdown(toc, `- [Properties](#properties)\n`)
- addMarkdown(membersResult, `## Properties\n\n`)
- addMarkdown(membersResult, propertiesResult.markdown)
- } else if (isComponent && !componentProps) {
- addMarkdown(membersResult, `## Properties\n\n`)
- addMarkdown(membersResult, `This component does not take any props.\n\n`)
- }
- }
-
- if (methods.length) {
- addMarkdown(toc, `- [Methods](#methods)\n`)
- addMarkdown(methodsResult, `## Methods\n\n`)
- for (const member of methods) {
- const slug = getSlug(member)
- addMarkdown(toc, ` - [${member.displayName}](#${slug})\n`)
- await addMarkdownForMember(model, methodsResult, member)
- addHorizontalRule(methodsResult)
- }
- addMarkdown(membersResult, methodsResult.markdown)
+ addMarkdown(toc, `- [Methods](#methods)\n`)
+ addMarkdown(methodsResult, `## Methods\n\n`)
+ for (const member of contents.methods) {
+ const slug = getSlug(member.item)
+ addMarkdown(toc, ` - [${member.item.displayName}](#${slug})\n`)
+ await addMarkdownForMember(model, methodsResult, member)
+ addHorizontalRule(methodsResult)
}
+ addMarkdown(membersResult, methodsResult.markdown)
}
await addFrontmatter(model, result, item, categoryName, j)
@@ -153,6 +118,10 @@ export async function getApiMarkdown(
result.markdown += `\n\n`
}
+ if (!isComponent) {
+ addExtends(result, item, contents.heritage)
+ }
+
await addDocComment(model, result, item)
if (membersResult.markdown.length) {
@@ -165,6 +134,108 @@ export async function getApiMarkdown(
/* --------------------- Helpers -------------------- */
+function sortMembers(members: Member[]) {
+ return members.sort((a, b) => {
+ const aIsStatic = ApiStaticMixin.isBaseClassOf(a.item) && a.item.isStatic
+ const bIsStatic = ApiStaticMixin.isBaseClassOf(b.item) && b.item.isStatic
+ if (aIsStatic && !bIsStatic) return -1
+ if (!aIsStatic && bIsStatic) return 1
+ return a.item.displayName.localeCompare(b.item.displayName)
+ })
+}
+
+function collectMembersAndExtends(model: TldrawApiModel, item: ApiItem) {
+ const isComponent = model.isComponent(item)
+
+ const constructors: Member[] = []
+ const properties: Member[] = []
+ const methods: Member[] = []
+ const heritage: HeritageType[] = []
+
+ function visit(item: ApiItem, inheritedFrom: ApiItem | null) {
+ if (item.members) {
+ for (const member of item.members) {
+ switch (member.kind) {
+ case ApiItemKind.Constructor:
+ case ApiItemKind.ConstructSignature:
+ addMember(constructors, member, inheritedFrom)
+ break
+ case ApiItemKind.Variable:
+ case ApiItemKind.Property:
+ case ApiItemKind.PropertySignature:
+ addMember(properties, member, inheritedFrom)
+ break
+ case ApiItemKind.Method:
+ case ApiItemKind.Function:
+ case ApiItemKind.MethodSignature:
+ if (isComponent) {
+ addMember(properties, member, inheritedFrom)
+ } else {
+ addMember(methods, member, inheritedFrom)
+ }
+
+ break
+ case ApiItemKind.EnumMember:
+ case ApiItemKind.Class:
+ case ApiItemKind.TypeAlias:
+ case ApiItemKind.Interface:
+ // TODO: document these
+ break
+ default:
+ model.nonBlockingError(
+ member,
+ `Unknown member kind: ${member.kind} in ${item.displayName}`
+ )
+ break
+ }
+ }
+ }
+
+ if (model.isComponent(item)) {
+ const componentProps = model.getReactPropsItem(item)
+ if (componentProps) {
+ visit(componentProps, null)
+ }
+ }
+
+ const extendsTypes =
+ item instanceof ApiClass && item.extendsType
+ ? [item.extendsType]
+ : item instanceof ApiInterface
+ ? item.extendsTypes
+ : []
+
+ for (const extendsType of extendsTypes) {
+ if (!inheritedFrom) {
+ heritage.push(extendsType)
+ }
+
+ const tokens = extendsType.excerpt.spannedTokens
+ let extendedItem = null
+ if (tokens[0].kind === 'Reference') {
+ extendedItem = model.tryResolveToken(item, tokens[0])
+ }
+
+ if (extendedItem) {
+ visit(extendedItem, extendedItem)
+ }
+ }
+ }
+
+ visit(item, null)
+
+ sortMembers(constructors)
+ sortMembers(properties)
+ sortMembers(methods)
+
+ return { constructors, properties, methods, heritage }
+}
+
+function addMember(members: Member[], item: ApiItem, inheritedFrom: ApiItem | null) {
+ if (members.some((m) => m.item.displayName === item.displayName)) return
+ members.push({ item, inheritedFrom })
+}
+
function addMarkdown(result: Result, markdown: string) {
result.markdown += markdown
}
@@ -172,12 +243,12 @@ function addMarkdown(result: Result, markdown: string) {
async function addMarkdownForMember(
model: TldrawApiModel,
result: Result,
- member: ApiItem,
+ { item, inheritedFrom }: Member,
{ isComponentProp = false } = {}
) {
- if (member.displayName.startsWith('_')) return
- addMemberNameAndMeta(result, model, member, { isComponentProp })
- await addDocComment(model, result, member)
+ if (item.displayName.startsWith('_')) return
+ addMemberNameAndMeta(result, model, item, { isComponentProp, inheritedFrom })
+ await addDocComment(model, result, item)
}
async function addFrontmatter(
@@ -251,24 +322,27 @@ function addMemberNameAndMeta(
result: Result,
model: TldrawApiModel,
item: ApiItem,
- { level = 3, isComponentProp = false } = {}
+ {
+ level = 3,
+ isComponentProp = false,
+ inheritedFrom = null,
+ }: { level?: number; isComponentProp?: boolean; inheritedFrom?: ApiItem | null } = {}
) {
const heading = `${'#'.repeat(level)} ${getItemTitle(item)}`
- if (item instanceof ApiDeclaredItem && item.sourceLocation.fileUrl) {
- const source = item.sourceLocation.fileUrl
- const tags = getTags(model, item, { isComponentProp, includeKind: false })
- result.markdown += [
- `
`,
- '',
- heading,
- '',
- '',
- '',
- ].join('\n')
- } else {
- result.markdown += `${heading}\n\n`
- }
+ const inherited = inheritedFrom
+ ? { name: inheritedFrom.displayName, link: `/reference/${getPath(inheritedFrom)}` }
+ : null
+
+ const tags = getTags(model, item, { isComponentProp, includeKind: false })
+ result.markdown += [
+ `
`,
+ '',
+ heading,
+ '',
+ '',
+ '',
+ ].join('\n')
}
async function addDocComment(model: TldrawApiModel, result: Result, member: ApiItem) {
@@ -395,7 +469,7 @@ async function addDocComment(model: TldrawApiModel, result: Result, member: ApiI
result.markdown += '\n\n'
}
} else {
- model.error(member, `Unknown member kind: ${member.kind}`)
+ model.nonBlockingError(member, `Unknown member kind: ${member.kind}`)
}
}
@@ -514,18 +588,11 @@ function getTags(
return tags
}
-function addExtends(result: Result, item: ApiItem) {
- const extendsTypes =
- item instanceof ApiClass && item.extendsType
- ? [item.extendsType]
- : item instanceof ApiInterface
- ? item.extendsTypes
- : []
-
- if (!extendsTypes.length) return
+function addExtends(result: Result, item: ApiItem, heritage: HeritageType[]) {
+ if (!heritage.length) return
const links = {} as Record
- for (const type of extendsTypes) {
+ for (const type of heritage) {
for (const token of type.excerpt.spannedTokens) {
if (!token.canonicalReference) continue
@@ -543,7 +610,7 @@ function addExtends(result: Result, item: ApiItem) {
result.markdown += [
``,
'',
- `Extends \`${extendsTypes.map((type) => type.excerpt.text).join(', ')}\`.`,
+ `Extends \`${heritage.map((type) => type.excerpt.text).join(', ')}\`.`,
'',
'',
'',
diff --git a/apps/docs/styles/globals.css b/apps/docs/styles/globals.css
index 6a454d535..a89635e78 100644
--- a/apps/docs/styles/globals.css
+++ b/apps/docs/styles/globals.css
@@ -309,7 +309,7 @@ body {
margin-left: auto;
padding-left: 16px;
}
-.article__title-with-source-link__meta > a {
+.article__title-with-source-link__source {
display: block;
width: 32px;
height: 32px;
@@ -318,11 +318,11 @@ body {
align-items: center;
justify-content: center;
}
-.article__title-with-source-link .article__title-with-source-link__meta > a {
- color: var(--color-text);
+.article__title-with-source-link__source {
+ color: var(--color-text) !important;
}
-.article__title-with-source-link > a:hover {
+.article__title-with-source-link__source:hover {
background-color: var(--color-tint-1);
}
.article__title-with-source-link .icon {
@@ -330,7 +330,7 @@ body {
width: 20px;
height: 20px;
}
-.article__title-with-source-link--large .article__title-with-source-link__meta > a {
+.article__title-with-source-link--large .article__title-with-source-link__source {
width: 42px;
height: 42px;
}
@@ -342,15 +342,21 @@ body {
display: inline-block;
padding: 4px 6px;
border-radius: var(--border-radius-menu);
- background-color: var(--color-tint-0);
+ background-color: var(--color-tint-1);
color: var(--color-tint-5);
font-size: 12px;
margin-right: 8px;
line-height: 1;
+ white-space: nowrap;
}
.article__title-with-source-link--large .article__tag {
font-size: 14px;
}
+.article__title-with-source-link__from {
+ font-size: 12px;
+ line-height: 1.5;
+ color: var(--color-tint-5);
+}
/* Prev / Next Links */
diff --git a/apps/docs/styles/hljs.css b/apps/docs/styles/hljs.css
index 57582910c..dc7a5d64a 100644
--- a/apps/docs/styles/hljs.css
+++ b/apps/docs/styles/hljs.css
@@ -83,8 +83,8 @@
}
a.code-link::before {
content: '';
- width: 15px;
- height: 15px;
+ width: 1.0714em; /* 15px when font-size is 14px */
+ height: 1.0714em;
display: inline-block;
background: currentColor;
-webkit-mask: url('data:image/svg+xml;utf8,')
@@ -93,6 +93,6 @@ a.code-link::before {
no-repeat 50% 50%;
-webkit-mask-size: cover;
mask-size: cover;
- vertical-align: -2px;
- margin-right: 2px;
+ vertical-align: -0.142em; /* 2px when font-size is 14px */
+ margin-right: 0.142em;
}
diff --git a/apps/docs/utils/TldrawApiModel.ts b/apps/docs/utils/TldrawApiModel.ts
index 2e3a4c6eb..08b8c7b47 100644
--- a/apps/docs/utils/TldrawApiModel.ts
+++ b/apps/docs/utils/TldrawApiModel.ts
@@ -15,8 +15,9 @@ export class TldrawApiModel extends ApiModel {
private reactComponents = new Set()
private reactComponentProps = new Set()
+ nonBlockingErrors: Error[] = []
+
async preprocessReactComponents() {
- const errors = []
for (const packageModel of this.members) {
assert(packageModel instanceof ApiPackage)
if (packageModel.name !== 'tldraw') continue
@@ -37,22 +38,18 @@ export class TldrawApiModel extends ApiModel {
props.tsdocComment.summarySection
)
if (markdown.trim()) {
- this.error(
+ this.nonBlockingError(
props,
"Component props should not contain documentation as it won't be included in the docs site. Add it to the component instead."
)
}
}
if (props) this.reactComponentProps.add(props)
- } catch (e) {
- errors.push(e)
+ } catch (e: any) {
+ this.nonBlockingErrors.push(e)
}
}
}
-
- if (errors.length > 0) {
- throw new Error(errors.map((e) => (e as any).message).join('\n\n'))
- }
}
resolveToken(origin: ApiItem, token: ExcerptToken) {
@@ -63,6 +60,14 @@ export class TldrawApiModel extends ApiModel {
return apiItemResult.resolvedApiItem!
}
+ tryResolveToken(origin: ApiItem, token: ExcerptToken) {
+ const apiItemResult = this.resolveDeclarationReference(token.canonicalReference!, origin)
+ if (apiItemResult.errorMessage) {
+ return null
+ }
+ return apiItemResult.resolvedApiItem!
+ }
+
getReactPropsItem(component: ApiItem): ApiItem | null {
if (component instanceof ApiFunction) {
if (component.parameters.length === 0) return null
@@ -84,10 +89,11 @@ export class TldrawApiModel extends ApiModel {
return this.resolveToken(component, tokens[0])
}
- this.error(
+ this.nonBlockingError(
component,
`Expected props parameter to be a simple reference. Rewrite this to use a \`${component.displayName}Props\` interface.\nFound: ${propsParam.parameterTypeExcerpt.text}`
)
+ return null
} else if (component instanceof ApiVariable) {
const tokens = component.variableTypeExcerpt.spannedTokens
if (
@@ -130,12 +136,16 @@ export class TldrawApiModel extends ApiModel {
return null
}
- this.error(
+ this.nonBlockingError(
component,
`Expected a simple props interface for react component. Got: ${component.variableTypeExcerpt.text}`
)
+
+ return null
} else {
- this.error(component, `Unknown item kind for @react component: ${component.kind}`)
+ this.nonBlockingError(component, `Unknown item kind for @react component: ${component.kind}`)
+
+ return null
}
}
@@ -147,12 +157,26 @@ export class TldrawApiModel extends ApiModel {
return this.reactComponentProps.has(item)
}
- error(item: ApiItem, message: string): never {
+ private createError(item: ApiItem, message: string) {
const suffix =
'_fileUrlPath' in item && typeof item._fileUrlPath === 'string'
? `\nin ${item._fileUrlPath}`
: ''
- throw new Error(`${item.displayName}: ${message}${suffix}`)
+ return new Error(`${item.displayName}: ${message}${suffix}`)
+ }
+
+ nonBlockingError(item: ApiItem, message: string) {
+ this.nonBlockingErrors.push(this.createError(item, message))
+ }
+
+ throwEncounteredErrors() {
+ if (this.nonBlockingErrors.length > 0) {
+ throw new Error(this.nonBlockingErrors.map((e) => (e as any).message).join('\n\n'))
+ }
+ }
+
+ error(item: ApiItem, message: string): never {
+ throw this.createError(item, message)
}
assert(item: ApiItem, condition: unknown, message: string): asserts condition {
diff --git a/packages/editor/api-report.md b/packages/editor/api-report.md
index fd91dcc13..5043255dd 100644
--- a/packages/editor/api-report.md
+++ b/packages/editor/api-report.md
@@ -1045,13 +1045,13 @@ export class Editor extends EventEmitter {
getStateDescendant(path: string): T | undefined;
getStyleForNextShape(style: StyleProp): T;
// @deprecated (undocumented)
- getSvg(shapes: TLShape[] | TLShapeId[], opts?: Partial): Promise;
- getSvgElement(shapes: TLShape[] | TLShapeId[], opts?: Partial): Promise<{
+ getSvg(shapes: TLShape[] | TLShapeId[], opts?: TLSvgOptions): Promise;
+ getSvgElement(shapes: TLShape[] | TLShapeId[], opts?: TLSvgOptions): Promise<{
height: number;
svg: SVGSVGElement;
width: number;
} | undefined>;
- getSvgString(shapes: TLShape[] | TLShapeId[], opts?: Partial): Promise<{
+ getSvgString(shapes: TLShape[] | TLShapeId[], opts?: TLSvgOptions): Promise<{
height: number;
svg: string;
width: number;
@@ -3299,17 +3299,17 @@ export type TLStoreWithStatus = {
// @public (undocumented)
export interface TLSvgOptions {
// (undocumented)
- background: boolean;
+ background?: boolean;
// (undocumented)
- bounds: Box;
+ bounds?: Box;
// (undocumented)
darkMode?: boolean;
// (undocumented)
- padding: number;
+ padding?: number;
// (undocumented)
- preserveAspectRatio: React.SVGAttributes['preserveAspectRatio'];
+ preserveAspectRatio?: React.SVGAttributes['preserveAspectRatio'];
// (undocumented)
- scale: number;
+ scale?: number;
}
// @public (undocumented)
diff --git a/packages/editor/src/lib/editor/Editor.ts b/packages/editor/src/lib/editor/Editor.ts
index d95f7949e..f3a9ac374 100644
--- a/packages/editor/src/lib/editor/Editor.ts
+++ b/packages/editor/src/lib/editor/Editor.ts
@@ -8224,7 +8224,7 @@ export class Editor extends EventEmitter {
*
* @public
*/
- async getSvgElement(shapes: TLShapeId[] | TLShape[], opts = {} as Partial) {
+ async getSvgElement(shapes: TLShapeId[] | TLShape[], opts: TLSvgOptions = {}) {
const result = await getSvgJsx(this, shapes, opts)
if (!result) return undefined
@@ -8251,7 +8251,7 @@ export class Editor extends EventEmitter {
*
* @public
*/
- async getSvgString(shapes: TLShapeId[] | TLShape[], opts = {} as Partial) {
+ async getSvgString(shapes: TLShapeId[] | TLShape[], opts: TLSvgOptions = {}) {
const result = await this.getSvgElement(shapes, opts)
if (!result) return undefined
@@ -8264,7 +8264,7 @@ export class Editor extends EventEmitter {
}
/** @deprecated Use {@link Editor.getSvgString} or {@link Editor.getSvgElement} instead. */
- async getSvg(shapes: TLShapeId[] | TLShape[], opts = {} as Partial) {
+ async getSvg(shapes: TLShapeId[] | TLShape[], opts: TLSvgOptions = {}) {
const result = await this.getSvgElement(shapes, opts)
if (!result) return undefined
return result.svg
diff --git a/packages/editor/src/lib/editor/getSvgJsx.tsx b/packages/editor/src/lib/editor/getSvgJsx.tsx
index b0f2bb2c9..70b708538 100644
--- a/packages/editor/src/lib/editor/getSvgJsx.tsx
+++ b/packages/editor/src/lib/editor/getSvgJsx.tsx
@@ -13,7 +13,7 @@ import { TLSvgOptions } from './types/misc-types'
export async function getSvgJsx(
editor: Editor,
shapes: TLShapeId[] | TLShape[],
- opts = {} as Partial
+ opts: TLSvgOptions = {}
) {
const ids =
typeof shapes[0] === 'string' ? (shapes as TLShapeId[]) : (shapes as TLShape[]).map((s) => s.id)
diff --git a/packages/editor/src/lib/editor/types/misc-types.ts b/packages/editor/src/lib/editor/types/misc-types.ts
index 6692b1eaf..51fe4fbf1 100644
--- a/packages/editor/src/lib/editor/types/misc-types.ts
+++ b/packages/editor/src/lib/editor/types/misc-types.ts
@@ -9,12 +9,12 @@ export type OptionalKeys = Omit & Partial
/** @public */
export interface TLSvgOptions {
- bounds: Box
- scale: number
- background: boolean
- padding: number
+ bounds?: Box
+ scale?: number
+ background?: boolean
+ padding?: number
darkMode?: boolean
- preserveAspectRatio: React.SVGAttributes['preserveAspectRatio']
+ preserveAspectRatio?: React.SVGAttributes['preserveAspectRatio']
}
/** @public */
diff --git a/packages/tldraw/api-report.md b/packages/tldraw/api-report.md
index be13525c5..ac45f7c81 100644
--- a/packages/tldraw/api-report.md
+++ b/packages/tldraw/api-report.md
@@ -359,7 +359,7 @@ export function ConvertToBookmarkMenuItem(): JSX_2.Element | null;
export function ConvertToEmbedMenuItem(): JSX_2.Element | null;
// @public
-export function copyAs(editor: Editor, ids: TLShapeId[], format?: TLCopyType, opts?: Partial): Promise;
+export function copyAs(editor: Editor, ids: TLShapeId[], format?: TLCopyType, opts?: TLSvgOptions): Promise;
// @public (undocumented)
export function CopyAsMenuGroup(): JSX_2.Element;
@@ -634,7 +634,7 @@ export interface ExampleDialogProps {
}
// @public
-export function exportAs(editor: Editor, ids: TLShapeId[], format: TLExportType | undefined, name: string | undefined, opts?: Partial): Promise;
+export function exportAs(editor: Editor, ids: TLShapeId[], format: TLExportType | undefined, name: string | undefined, opts?: TLSvgOptions): Promise;
// @public (undocumented)
export function ExportFileContentSubMenu(): JSX_2.Element;
@@ -644,7 +644,7 @@ export function exportToBlob({ editor, ids, format, opts, }: {
editor: Editor;
format: TLExportType;
ids: TLShapeId[];
- opts?: Partial;
+ opts?: TLSvgOptions;
}): Promise;
// @public (undocumented)
@@ -1660,7 +1660,7 @@ export function TldrawHandles({ children }: TLHandlesProps): JSX_2.Element | nul
export const TldrawImage: NamedExoticComponent;
// @public (undocumented)
-export interface TldrawImageProps extends Partial {
+export interface TldrawImageProps extends TLSvgOptions {
bindingUtils?: readonly TLAnyBindingUtilConstructor[];
format?: 'png' | 'svg';
pageId?: TLPageId;
diff --git a/packages/tldraw/src/lib/TldrawImage.tsx b/packages/tldraw/src/lib/TldrawImage.tsx
index e6a65a190..b2e79b146 100644
--- a/packages/tldraw/src/lib/TldrawImage.tsx
+++ b/packages/tldraw/src/lib/TldrawImage.tsx
@@ -20,7 +20,7 @@ import { getSvgAsImage } from './utils/export/export'
import { useDefaultEditorAssetsWithOverrides } from './utils/static-assets/assetUrls'
/** @public */
-export interface TldrawImageProps extends Partial {
+export interface TldrawImageProps extends TLSvgOptions {
/**
* The snapshot to display.
*/
diff --git a/packages/tldraw/src/lib/utils/export/copyAs.ts b/packages/tldraw/src/lib/utils/export/copyAs.ts
index 22a4184fc..1b7ebc86a 100644
--- a/packages/tldraw/src/lib/utils/export/copyAs.ts
+++ b/packages/tldraw/src/lib/utils/export/copyAs.ts
@@ -18,7 +18,7 @@ export function copyAs(
editor: Editor,
ids: TLShapeId[],
format: TLCopyType = 'svg',
- opts = {} as Partial
+ opts: TLSvgOptions = {}
): Promise {
// Note: it's important that this function itself isn't async and doesn't really use promises -
// we need to create the relevant `ClipboardItem`s and call window.navigator.clipboard.write
diff --git a/packages/tldraw/src/lib/utils/export/export.ts b/packages/tldraw/src/lib/utils/export/export.ts
index 4020db8fb..781336bca 100644
--- a/packages/tldraw/src/lib/utils/export/export.ts
+++ b/packages/tldraw/src/lib/utils/export/export.ts
@@ -96,7 +96,7 @@ export async function getSvgAsImage(
}
}
-async function getSvgString(editor: Editor, ids: TLShapeId[], opts: Partial) {
+async function getSvgString(editor: Editor, ids: TLShapeId[], opts: TLSvgOptions) {
const svg = await editor.getSvgString(ids?.length ? ids : [...editor.getCurrentPageShapeIds()], {
scale: 1,
background: editor.getInstanceState().exportBackground,
@@ -112,7 +112,7 @@ export async function exportToString(
editor: Editor,
ids: TLShapeId[],
format: 'svg' | 'json',
- opts = {} as Partial
+ opts: TLSvgOptions = {}
) {
switch (format) {
case 'svg': {
@@ -141,12 +141,12 @@ export async function exportToBlob({
editor,
ids,
format,
- opts = {} as Partial,
+ opts = {},
}: {
editor: Editor
ids: TLShapeId[]
format: TLExportType
- opts?: Partial
+ opts?: TLSvgOptions
}): Promise {
switch (format) {
case 'svg':
@@ -188,7 +188,7 @@ export function exportToBlobPromise(
editor: Editor,
ids: TLShapeId[],
format: TLExportType,
- opts = {} as Partial
+ opts: TLSvgOptions = {}
): { blobPromise: Promise; mimeType: string } {
return {
blobPromise: exportToBlob({ editor, ids, format, opts }),
diff --git a/packages/tldraw/src/lib/utils/export/exportAs.ts b/packages/tldraw/src/lib/utils/export/exportAs.ts
index aebd3c4a1..d463244cb 100644
--- a/packages/tldraw/src/lib/utils/export/exportAs.ts
+++ b/packages/tldraw/src/lib/utils/export/exportAs.ts
@@ -20,7 +20,7 @@ export async function exportAs(
ids: TLShapeId[],
format: TLExportType = 'png',
name: string | undefined,
- opts = {} as Partial
+ opts: TLSvgOptions = {}
) {
// If we don't get name then use a predefined one
if (!name) {