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) {