Document inherited members in reference (#3956)
Our reference docs don't currently include members inherited through the `extends` keyword. These extended items are barely referenced at all - you have to find them in the signature. This diff adds a clearer note to the docs saying which type has been extended, and if possible brings the extended items through onto the current documentation page (with a note saying where they're from) ![image](https://github.com/tldraw/tldraw/assets/1489520/0349252d-e8bc-406b-bf47-636da424ebe0) ### Change Type - [x] `docs` — Changes to the documentation, examples, or templates. - [x] `improvement` — Improving existing features
This commit is contained in:
parent
12aea7ed68
commit
c4b9ea30f4
15 changed files with 312 additions and 188 deletions
|
@ -1,4 +1,5 @@
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
|
import Link from 'next/link'
|
||||||
import { ReactNode } from 'react'
|
import { ReactNode } from 'react'
|
||||||
import { Icon } from '../Icon'
|
import { Icon } from '../Icon'
|
||||||
|
|
||||||
|
@ -35,32 +36,56 @@ export function TitleWithSourceLink({
|
||||||
source,
|
source,
|
||||||
large,
|
large,
|
||||||
tags,
|
tags,
|
||||||
|
inherited,
|
||||||
}: {
|
}: {
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
source?: string | null
|
source?: string | null
|
||||||
large?: boolean
|
large?: boolean
|
||||||
tags?: string[]
|
tags?: string[]
|
||||||
|
inherited?: { name: string; link: string }
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div
|
<>
|
||||||
className={classNames(
|
<div
|
||||||
'article__title-with-source-link',
|
className={classNames(
|
||||||
large && 'article__title-with-source-link--large'
|
'article__title-with-source-link',
|
||||||
)}
|
large && 'article__title-with-source-link--large'
|
||||||
>
|
|
||||||
{children}
|
|
||||||
<div className="article__title-with-source-link__meta">
|
|
||||||
{tags?.map((tag) => <Tag key={tag}>{tag}</Tag>)}
|
|
||||||
{source && (
|
|
||||||
<a href={source} target="_blank" rel="noopener noreferrer" title="Source code">
|
|
||||||
<Icon icon="code" />
|
|
||||||
</a>
|
|
||||||
)}
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<div className="article__title-with-source-link__meta">
|
||||||
|
{tags?.map((tag) => (
|
||||||
|
<Tag key={tag} tag={tag}>
|
||||||
|
{tag}
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
{source && (
|
||||||
|
<a
|
||||||
|
href={source}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
title="Source code"
|
||||||
|
className="article__title-with-source-link__source"
|
||||||
|
>
|
||||||
|
<Icon icon="code" />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{inherited && (
|
||||||
|
<div className="article__title-with-source-link__from">
|
||||||
|
from{' '}
|
||||||
|
<code className="hljs">
|
||||||
|
<Link href={inherited.link} className="code-link">
|
||||||
|
{inherited.name}
|
||||||
|
</Link>
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Tag({ children }: { children: string }) {
|
export function Tag({ children, tag }: { children: ReactNode; tag: string }) {
|
||||||
return <span className={classNames(`article__tag`, `article__tag--${children}`)}>{children}</span>
|
return <span className={classNames(`article__tag`, `article__tag--${tag}`)}>{children}</span>
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,4 +85,6 @@ export async function createApiMarkdown() {
|
||||||
)
|
)
|
||||||
sectionsJson.push(apiInputSection)
|
sectionsJson.push(apiInputSection)
|
||||||
fs.writeFileSync(sectionsJsonPath, JSON.stringify(sectionsJson, null, '\t') + '\n')
|
fs.writeFileSync(sectionsJsonPath, JSON.stringify(sectionsJson, null, '\t') + '\n')
|
||||||
|
|
||||||
|
model.throwEncounteredErrors()
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import {
|
||||||
ApiTypeAlias,
|
ApiTypeAlias,
|
||||||
ApiVariable,
|
ApiVariable,
|
||||||
Excerpt,
|
Excerpt,
|
||||||
|
HeritageType,
|
||||||
} from '@microsoft/api-extractor-model'
|
} from '@microsoft/api-extractor-model'
|
||||||
import { MarkdownWriter, formatWithPrettier, getPath, getSlug } from '../utils'
|
import { MarkdownWriter, formatWithPrettier, getPath, getSlug } from '../utils'
|
||||||
|
|
||||||
|
@ -36,6 +37,11 @@ const date = new Intl.DateTimeFormat('en-US', {
|
||||||
day: '2-digit',
|
day: '2-digit',
|
||||||
}).format(new Date())
|
}).format(new Date())
|
||||||
|
|
||||||
|
interface Member {
|
||||||
|
item: ApiItem
|
||||||
|
inheritedFrom: ApiItem | null
|
||||||
|
}
|
||||||
|
|
||||||
export async function getApiMarkdown(
|
export async function getApiMarkdown(
|
||||||
model: TldrawApiModel,
|
model: TldrawApiModel,
|
||||||
categoryName: string,
|
categoryName: string,
|
||||||
|
@ -49,100 +55,59 @@ export async function getApiMarkdown(
|
||||||
const isComponent = model.isComponent(item)
|
const isComponent = model.isComponent(item)
|
||||||
const componentProps = isComponent ? model.getReactPropsItem(item) : null
|
const componentProps = isComponent ? model.getReactPropsItem(item) : null
|
||||||
|
|
||||||
const members = componentProps?.members ?? item.members
|
const contents = collectMembersAndExtends(model, item)
|
||||||
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}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (contents.constructors.length) {
|
||||||
const constructorResult: Result = { markdown: '', keywords: [] }
|
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: [] }
|
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: [] }
|
const methodsResult: Result = { markdown: '', keywords: [] }
|
||||||
|
addMarkdown(toc, `- [Methods](#methods)\n`)
|
||||||
if (constructors.length) {
|
addMarkdown(methodsResult, `## Methods\n\n`)
|
||||||
for (const member of constructors) {
|
for (const member of contents.methods) {
|
||||||
await addMarkdownForMember(model, constructorResult, member)
|
const slug = getSlug(member.item)
|
||||||
addHorizontalRule(constructorResult)
|
addMarkdown(toc, ` - [${member.item.displayName}](#${slug})\n`)
|
||||||
}
|
await addMarkdownForMember(model, methodsResult, member)
|
||||||
addMarkdown(membersResult, constructorResult.markdown)
|
addHorizontalRule(methodsResult)
|
||||||
}
|
|
||||||
|
|
||||||
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(membersResult, methodsResult.markdown)
|
||||||
}
|
}
|
||||||
|
|
||||||
await addFrontmatter(model, result, item, categoryName, j)
|
await addFrontmatter(model, result, item, categoryName, j)
|
||||||
|
@ -153,6 +118,10 @@ export async function getApiMarkdown(
|
||||||
result.markdown += `</details>\n\n`
|
result.markdown += `</details>\n\n`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isComponent) {
|
||||||
|
addExtends(result, item, contents.heritage)
|
||||||
|
}
|
||||||
|
|
||||||
await addDocComment(model, result, item)
|
await addDocComment(model, result, item)
|
||||||
|
|
||||||
if (membersResult.markdown.length) {
|
if (membersResult.markdown.length) {
|
||||||
|
@ -165,6 +134,108 @@ export async function getApiMarkdown(
|
||||||
|
|
||||||
/* --------------------- Helpers -------------------- */
|
/* --------------------- 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) {
|
function addMarkdown(result: Result, markdown: string) {
|
||||||
result.markdown += markdown
|
result.markdown += markdown
|
||||||
}
|
}
|
||||||
|
@ -172,12 +243,12 @@ function addMarkdown(result: Result, markdown: string) {
|
||||||
async function addMarkdownForMember(
|
async function addMarkdownForMember(
|
||||||
model: TldrawApiModel,
|
model: TldrawApiModel,
|
||||||
result: Result,
|
result: Result,
|
||||||
member: ApiItem,
|
{ item, inheritedFrom }: Member,
|
||||||
{ isComponentProp = false } = {}
|
{ isComponentProp = false } = {}
|
||||||
) {
|
) {
|
||||||
if (member.displayName.startsWith('_')) return
|
if (item.displayName.startsWith('_')) return
|
||||||
addMemberNameAndMeta(result, model, member, { isComponentProp })
|
addMemberNameAndMeta(result, model, item, { isComponentProp, inheritedFrom })
|
||||||
await addDocComment(model, result, member)
|
await addDocComment(model, result, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addFrontmatter(
|
async function addFrontmatter(
|
||||||
|
@ -251,24 +322,27 @@ function addMemberNameAndMeta(
|
||||||
result: Result,
|
result: Result,
|
||||||
model: TldrawApiModel,
|
model: TldrawApiModel,
|
||||||
item: ApiItem,
|
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)}`
|
const heading = `${'#'.repeat(level)} ${getItemTitle(item)}`
|
||||||
|
|
||||||
if (item instanceof ApiDeclaredItem && item.sourceLocation.fileUrl) {
|
const inherited = inheritedFrom
|
||||||
const source = item.sourceLocation.fileUrl
|
? { name: inheritedFrom.displayName, link: `/reference/${getPath(inheritedFrom)}` }
|
||||||
const tags = getTags(model, item, { isComponentProp, includeKind: false })
|
: null
|
||||||
result.markdown += [
|
|
||||||
`<TitleWithSourceLink source={${JSON.stringify(source)}} tags={${JSON.stringify(tags)}}>`,
|
const tags = getTags(model, item, { isComponentProp, includeKind: false })
|
||||||
'',
|
result.markdown += [
|
||||||
heading,
|
`<TitleWithSourceLink tags={${JSON.stringify(tags)}} inherited={${JSON.stringify(inherited)}}>`,
|
||||||
'',
|
'',
|
||||||
'</TitleWithSourceLink>',
|
heading,
|
||||||
'',
|
'',
|
||||||
].join('\n')
|
'</TitleWithSourceLink>',
|
||||||
} else {
|
'',
|
||||||
result.markdown += `${heading}\n\n`
|
].join('\n')
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addDocComment(model: TldrawApiModel, result: Result, member: ApiItem) {
|
async function addDocComment(model: TldrawApiModel, result: Result, member: ApiItem) {
|
||||||
|
@ -395,7 +469,7 @@ async function addDocComment(model: TldrawApiModel, result: Result, member: ApiI
|
||||||
result.markdown += '</ParametersTable>\n\n'
|
result.markdown += '</ParametersTable>\n\n'
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
return tags
|
||||||
}
|
}
|
||||||
|
|
||||||
function addExtends(result: Result, item: ApiItem) {
|
function addExtends(result: Result, item: ApiItem, heritage: HeritageType[]) {
|
||||||
const extendsTypes =
|
if (!heritage.length) return
|
||||||
item instanceof ApiClass && item.extendsType
|
|
||||||
? [item.extendsType]
|
|
||||||
: item instanceof ApiInterface
|
|
||||||
? item.extendsTypes
|
|
||||||
: []
|
|
||||||
|
|
||||||
if (!extendsTypes.length) return
|
|
||||||
|
|
||||||
const links = {} as Record<string, string>
|
const links = {} as Record<string, string>
|
||||||
for (const type of extendsTypes) {
|
for (const type of heritage) {
|
||||||
for (const token of type.excerpt.spannedTokens) {
|
for (const token of type.excerpt.spannedTokens) {
|
||||||
if (!token.canonicalReference) continue
|
if (!token.canonicalReference) continue
|
||||||
|
|
||||||
|
@ -543,7 +610,7 @@ function addExtends(result: Result, item: ApiItem) {
|
||||||
result.markdown += [
|
result.markdown += [
|
||||||
`<CodeLinkProvider links={${JSON.stringify(links)}}>`,
|
`<CodeLinkProvider links={${JSON.stringify(links)}}>`,
|
||||||
'',
|
'',
|
||||||
`Extends \`${extendsTypes.map((type) => type.excerpt.text).join(', ')}\`.`,
|
`Extends \`${heritage.map((type) => type.excerpt.text).join(', ')}\`.`,
|
||||||
'',
|
'',
|
||||||
'</CodeLinkProvider>',
|
'</CodeLinkProvider>',
|
||||||
'',
|
'',
|
||||||
|
|
|
@ -309,7 +309,7 @@ body {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
padding-left: 16px;
|
padding-left: 16px;
|
||||||
}
|
}
|
||||||
.article__title-with-source-link__meta > a {
|
.article__title-with-source-link__source {
|
||||||
display: block;
|
display: block;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
@ -318,11 +318,11 @@ body {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
.article__title-with-source-link .article__title-with-source-link__meta > a {
|
.article__title-with-source-link__source {
|
||||||
color: var(--color-text);
|
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);
|
background-color: var(--color-tint-1);
|
||||||
}
|
}
|
||||||
.article__title-with-source-link .icon {
|
.article__title-with-source-link .icon {
|
||||||
|
@ -330,7 +330,7 @@ body {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 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;
|
width: 42px;
|
||||||
height: 42px;
|
height: 42px;
|
||||||
}
|
}
|
||||||
|
@ -342,15 +342,21 @@ body {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 4px 6px;
|
padding: 4px 6px;
|
||||||
border-radius: var(--border-radius-menu);
|
border-radius: var(--border-radius-menu);
|
||||||
background-color: var(--color-tint-0);
|
background-color: var(--color-tint-1);
|
||||||
color: var(--color-tint-5);
|
color: var(--color-tint-5);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.article__title-with-source-link--large .article__tag {
|
.article__title-with-source-link--large .article__tag {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
.article__title-with-source-link__from {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--color-tint-5);
|
||||||
|
}
|
||||||
|
|
||||||
/* Prev / Next Links */
|
/* Prev / Next Links */
|
||||||
|
|
||||||
|
|
|
@ -83,8 +83,8 @@
|
||||||
}
|
}
|
||||||
a.code-link::before {
|
a.code-link::before {
|
||||||
content: '';
|
content: '';
|
||||||
width: 15px;
|
width: 1.0714em; /* 15px when font-size is 14px */
|
||||||
height: 15px;
|
height: 1.0714em;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background: currentColor;
|
background: currentColor;
|
||||||
-webkit-mask: url('data:image/svg+xml;utf8,<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.5 2.5H3.5C2.94772 2.5 2.5 2.94772 2.5 3.5V11.5C2.5 12.0523 2.94772 12.5 3.5 12.5H11.5C12.0523 12.5 12.5 12.0523 12.5 11.5V8.5M9.5 2.5H12.5M12.5 2.5V5.5M12.5 2.5L6.5 8.5" stroke="black" stroke-linecap="round" stroke-linejoin="round"/></svg>')
|
-webkit-mask: url('data:image/svg+xml;utf8,<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.5 2.5H3.5C2.94772 2.5 2.5 2.94772 2.5 3.5V11.5C2.5 12.0523 2.94772 12.5 3.5 12.5H11.5C12.0523 12.5 12.5 12.0523 12.5 11.5V8.5M9.5 2.5H12.5M12.5 2.5V5.5M12.5 2.5L6.5 8.5" stroke="black" stroke-linecap="round" stroke-linejoin="round"/></svg>')
|
||||||
|
@ -93,6 +93,6 @@ a.code-link::before {
|
||||||
no-repeat 50% 50%;
|
no-repeat 50% 50%;
|
||||||
-webkit-mask-size: cover;
|
-webkit-mask-size: cover;
|
||||||
mask-size: cover;
|
mask-size: cover;
|
||||||
vertical-align: -2px;
|
vertical-align: -0.142em; /* 2px when font-size is 14px */
|
||||||
margin-right: 2px;
|
margin-right: 0.142em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,9 @@ export class TldrawApiModel extends ApiModel {
|
||||||
private reactComponents = new Set<ApiItem>()
|
private reactComponents = new Set<ApiItem>()
|
||||||
private reactComponentProps = new Set<ApiItem>()
|
private reactComponentProps = new Set<ApiItem>()
|
||||||
|
|
||||||
|
nonBlockingErrors: Error[] = []
|
||||||
|
|
||||||
async preprocessReactComponents() {
|
async preprocessReactComponents() {
|
||||||
const errors = []
|
|
||||||
for (const packageModel of this.members) {
|
for (const packageModel of this.members) {
|
||||||
assert(packageModel instanceof ApiPackage)
|
assert(packageModel instanceof ApiPackage)
|
||||||
if (packageModel.name !== 'tldraw') continue
|
if (packageModel.name !== 'tldraw') continue
|
||||||
|
@ -37,22 +38,18 @@ export class TldrawApiModel extends ApiModel {
|
||||||
props.tsdocComment.summarySection
|
props.tsdocComment.summarySection
|
||||||
)
|
)
|
||||||
if (markdown.trim()) {
|
if (markdown.trim()) {
|
||||||
this.error(
|
this.nonBlockingError(
|
||||||
props,
|
props,
|
||||||
"Component props should not contain documentation as it won't be included in the docs site. Add it to the component instead."
|
"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)
|
if (props) this.reactComponentProps.add(props)
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
errors.push(e)
|
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) {
|
resolveToken(origin: ApiItem, token: ExcerptToken) {
|
||||||
|
@ -63,6 +60,14 @@ export class TldrawApiModel extends ApiModel {
|
||||||
return apiItemResult.resolvedApiItem!
|
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 {
|
getReactPropsItem(component: ApiItem): ApiItem | null {
|
||||||
if (component instanceof ApiFunction) {
|
if (component instanceof ApiFunction) {
|
||||||
if (component.parameters.length === 0) return null
|
if (component.parameters.length === 0) return null
|
||||||
|
@ -84,10 +89,11 @@ export class TldrawApiModel extends ApiModel {
|
||||||
return this.resolveToken(component, tokens[0])
|
return this.resolveToken(component, tokens[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
this.error(
|
this.nonBlockingError(
|
||||||
component,
|
component,
|
||||||
`Expected props parameter to be a simple reference. Rewrite this to use a \`${component.displayName}Props\` interface.\nFound: ${propsParam.parameterTypeExcerpt.text}`
|
`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) {
|
} else if (component instanceof ApiVariable) {
|
||||||
const tokens = component.variableTypeExcerpt.spannedTokens
|
const tokens = component.variableTypeExcerpt.spannedTokens
|
||||||
if (
|
if (
|
||||||
|
@ -130,12 +136,16 @@ export class TldrawApiModel extends ApiModel {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
this.error(
|
this.nonBlockingError(
|
||||||
component,
|
component,
|
||||||
`Expected a simple props interface for react component. Got: ${component.variableTypeExcerpt.text}`
|
`Expected a simple props interface for react component. Got: ${component.variableTypeExcerpt.text}`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return null
|
||||||
} else {
|
} 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)
|
return this.reactComponentProps.has(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
error(item: ApiItem, message: string): never {
|
private createError(item: ApiItem, message: string) {
|
||||||
const suffix =
|
const suffix =
|
||||||
'_fileUrlPath' in item && typeof item._fileUrlPath === 'string'
|
'_fileUrlPath' in item && typeof item._fileUrlPath === 'string'
|
||||||
? `\nin ${item._fileUrlPath}`
|
? `\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 {
|
assert(item: ApiItem, condition: unknown, message: string): asserts condition {
|
||||||
|
|
|
@ -1045,13 +1045,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
getStateDescendant<T extends StateNode>(path: string): T | undefined;
|
getStateDescendant<T extends StateNode>(path: string): T | undefined;
|
||||||
getStyleForNextShape<T>(style: StyleProp<T>): T;
|
getStyleForNextShape<T>(style: StyleProp<T>): T;
|
||||||
// @deprecated (undocumented)
|
// @deprecated (undocumented)
|
||||||
getSvg(shapes: TLShape[] | TLShapeId[], opts?: Partial<TLSvgOptions>): Promise<SVGSVGElement | undefined>;
|
getSvg(shapes: TLShape[] | TLShapeId[], opts?: TLSvgOptions): Promise<SVGSVGElement | undefined>;
|
||||||
getSvgElement(shapes: TLShape[] | TLShapeId[], opts?: Partial<TLSvgOptions>): Promise<{
|
getSvgElement(shapes: TLShape[] | TLShapeId[], opts?: TLSvgOptions): Promise<{
|
||||||
height: number;
|
height: number;
|
||||||
svg: SVGSVGElement;
|
svg: SVGSVGElement;
|
||||||
width: number;
|
width: number;
|
||||||
} | undefined>;
|
} | undefined>;
|
||||||
getSvgString(shapes: TLShape[] | TLShapeId[], opts?: Partial<TLSvgOptions>): Promise<{
|
getSvgString(shapes: TLShape[] | TLShapeId[], opts?: TLSvgOptions): Promise<{
|
||||||
height: number;
|
height: number;
|
||||||
svg: string;
|
svg: string;
|
||||||
width: number;
|
width: number;
|
||||||
|
@ -3299,17 +3299,17 @@ export type TLStoreWithStatus = {
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export interface TLSvgOptions {
|
export interface TLSvgOptions {
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
background: boolean;
|
background?: boolean;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
bounds: Box;
|
bounds?: Box;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
darkMode?: boolean;
|
darkMode?: boolean;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
padding: number;
|
padding?: number;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
preserveAspectRatio: React.SVGAttributes<SVGSVGElement>['preserveAspectRatio'];
|
preserveAspectRatio?: React.SVGAttributes<SVGSVGElement>['preserveAspectRatio'];
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
scale: number;
|
scale?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
|
|
|
@ -8224,7 +8224,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
async getSvgElement(shapes: TLShapeId[] | TLShape[], opts = {} as Partial<TLSvgOptions>) {
|
async getSvgElement(shapes: TLShapeId[] | TLShape[], opts: TLSvgOptions = {}) {
|
||||||
const result = await getSvgJsx(this, shapes, opts)
|
const result = await getSvgJsx(this, shapes, opts)
|
||||||
if (!result) return undefined
|
if (!result) return undefined
|
||||||
|
|
||||||
|
@ -8251,7 +8251,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
async getSvgString(shapes: TLShapeId[] | TLShape[], opts = {} as Partial<TLSvgOptions>) {
|
async getSvgString(shapes: TLShapeId[] | TLShape[], opts: TLSvgOptions = {}) {
|
||||||
const result = await this.getSvgElement(shapes, opts)
|
const result = await this.getSvgElement(shapes, opts)
|
||||||
if (!result) return undefined
|
if (!result) return undefined
|
||||||
|
|
||||||
|
@ -8264,7 +8264,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @deprecated Use {@link Editor.getSvgString} or {@link Editor.getSvgElement} instead. */
|
/** @deprecated Use {@link Editor.getSvgString} or {@link Editor.getSvgElement} instead. */
|
||||||
async getSvg(shapes: TLShapeId[] | TLShape[], opts = {} as Partial<TLSvgOptions>) {
|
async getSvg(shapes: TLShapeId[] | TLShape[], opts: TLSvgOptions = {}) {
|
||||||
const result = await this.getSvgElement(shapes, opts)
|
const result = await this.getSvgElement(shapes, opts)
|
||||||
if (!result) return undefined
|
if (!result) return undefined
|
||||||
return result.svg
|
return result.svg
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { TLSvgOptions } from './types/misc-types'
|
||||||
export async function getSvgJsx(
|
export async function getSvgJsx(
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
shapes: TLShapeId[] | TLShape[],
|
shapes: TLShapeId[] | TLShape[],
|
||||||
opts = {} as Partial<TLSvgOptions>
|
opts: TLSvgOptions = {}
|
||||||
) {
|
) {
|
||||||
const ids =
|
const ids =
|
||||||
typeof shapes[0] === 'string' ? (shapes as TLShapeId[]) : (shapes as TLShape[]).map((s) => s.id)
|
typeof shapes[0] === 'string' ? (shapes as TLShapeId[]) : (shapes as TLShape[]).map((s) => s.id)
|
||||||
|
|
|
@ -9,12 +9,12 @@ export type OptionalKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export interface TLSvgOptions {
|
export interface TLSvgOptions {
|
||||||
bounds: Box
|
bounds?: Box
|
||||||
scale: number
|
scale?: number
|
||||||
background: boolean
|
background?: boolean
|
||||||
padding: number
|
padding?: number
|
||||||
darkMode?: boolean
|
darkMode?: boolean
|
||||||
preserveAspectRatio: React.SVGAttributes<SVGSVGElement>['preserveAspectRatio']
|
preserveAspectRatio?: React.SVGAttributes<SVGSVGElement>['preserveAspectRatio']
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
|
|
|
@ -359,7 +359,7 @@ export function ConvertToBookmarkMenuItem(): JSX_2.Element | null;
|
||||||
export function ConvertToEmbedMenuItem(): JSX_2.Element | null;
|
export function ConvertToEmbedMenuItem(): JSX_2.Element | null;
|
||||||
|
|
||||||
// @public
|
// @public
|
||||||
export function copyAs(editor: Editor, ids: TLShapeId[], format?: TLCopyType, opts?: Partial<TLSvgOptions>): Promise<void>;
|
export function copyAs(editor: Editor, ids: TLShapeId[], format?: TLCopyType, opts?: TLSvgOptions): Promise<void>;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export function CopyAsMenuGroup(): JSX_2.Element;
|
export function CopyAsMenuGroup(): JSX_2.Element;
|
||||||
|
@ -634,7 +634,7 @@ export interface ExampleDialogProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
// @public
|
// @public
|
||||||
export function exportAs(editor: Editor, ids: TLShapeId[], format: TLExportType | undefined, name: string | undefined, opts?: Partial<TLSvgOptions>): Promise<void>;
|
export function exportAs(editor: Editor, ids: TLShapeId[], format: TLExportType | undefined, name: string | undefined, opts?: TLSvgOptions): Promise<void>;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export function ExportFileContentSubMenu(): JSX_2.Element;
|
export function ExportFileContentSubMenu(): JSX_2.Element;
|
||||||
|
@ -644,7 +644,7 @@ export function exportToBlob({ editor, ids, format, opts, }: {
|
||||||
editor: Editor;
|
editor: Editor;
|
||||||
format: TLExportType;
|
format: TLExportType;
|
||||||
ids: TLShapeId[];
|
ids: TLShapeId[];
|
||||||
opts?: Partial<TLSvgOptions>;
|
opts?: TLSvgOptions;
|
||||||
}): Promise<Blob>;
|
}): Promise<Blob>;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
|
@ -1660,7 +1660,7 @@ export function TldrawHandles({ children }: TLHandlesProps): JSX_2.Element | nul
|
||||||
export const TldrawImage: NamedExoticComponent<TldrawImageProps>;
|
export const TldrawImage: NamedExoticComponent<TldrawImageProps>;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export interface TldrawImageProps extends Partial<TLSvgOptions> {
|
export interface TldrawImageProps extends TLSvgOptions {
|
||||||
bindingUtils?: readonly TLAnyBindingUtilConstructor[];
|
bindingUtils?: readonly TLAnyBindingUtilConstructor[];
|
||||||
format?: 'png' | 'svg';
|
format?: 'png' | 'svg';
|
||||||
pageId?: TLPageId;
|
pageId?: TLPageId;
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { getSvgAsImage } from './utils/export/export'
|
||||||
import { useDefaultEditorAssetsWithOverrides } from './utils/static-assets/assetUrls'
|
import { useDefaultEditorAssetsWithOverrides } from './utils/static-assets/assetUrls'
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export interface TldrawImageProps extends Partial<TLSvgOptions> {
|
export interface TldrawImageProps extends TLSvgOptions {
|
||||||
/**
|
/**
|
||||||
* The snapshot to display.
|
* The snapshot to display.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -18,7 +18,7 @@ export function copyAs(
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
ids: TLShapeId[],
|
ids: TLShapeId[],
|
||||||
format: TLCopyType = 'svg',
|
format: TLCopyType = 'svg',
|
||||||
opts = {} as Partial<TLSvgOptions>
|
opts: TLSvgOptions = {}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Note: it's important that this function itself isn't async and doesn't really use promises -
|
// 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
|
// we need to create the relevant `ClipboardItem`s and call window.navigator.clipboard.write
|
||||||
|
|
|
@ -96,7 +96,7 @@ export async function getSvgAsImage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getSvgString(editor: Editor, ids: TLShapeId[], opts: Partial<TLSvgOptions>) {
|
async function getSvgString(editor: Editor, ids: TLShapeId[], opts: TLSvgOptions) {
|
||||||
const svg = await editor.getSvgString(ids?.length ? ids : [...editor.getCurrentPageShapeIds()], {
|
const svg = await editor.getSvgString(ids?.length ? ids : [...editor.getCurrentPageShapeIds()], {
|
||||||
scale: 1,
|
scale: 1,
|
||||||
background: editor.getInstanceState().exportBackground,
|
background: editor.getInstanceState().exportBackground,
|
||||||
|
@ -112,7 +112,7 @@ export async function exportToString(
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
ids: TLShapeId[],
|
ids: TLShapeId[],
|
||||||
format: 'svg' | 'json',
|
format: 'svg' | 'json',
|
||||||
opts = {} as Partial<TLSvgOptions>
|
opts: TLSvgOptions = {}
|
||||||
) {
|
) {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case 'svg': {
|
case 'svg': {
|
||||||
|
@ -141,12 +141,12 @@ export async function exportToBlob({
|
||||||
editor,
|
editor,
|
||||||
ids,
|
ids,
|
||||||
format,
|
format,
|
||||||
opts = {} as Partial<TLSvgOptions>,
|
opts = {},
|
||||||
}: {
|
}: {
|
||||||
editor: Editor
|
editor: Editor
|
||||||
ids: TLShapeId[]
|
ids: TLShapeId[]
|
||||||
format: TLExportType
|
format: TLExportType
|
||||||
opts?: Partial<TLSvgOptions>
|
opts?: TLSvgOptions
|
||||||
}): Promise<Blob> {
|
}): Promise<Blob> {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case 'svg':
|
case 'svg':
|
||||||
|
@ -188,7 +188,7 @@ export function exportToBlobPromise(
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
ids: TLShapeId[],
|
ids: TLShapeId[],
|
||||||
format: TLExportType,
|
format: TLExportType,
|
||||||
opts = {} as Partial<TLSvgOptions>
|
opts: TLSvgOptions = {}
|
||||||
): { blobPromise: Promise<Blob>; mimeType: string } {
|
): { blobPromise: Promise<Blob>; mimeType: string } {
|
||||||
return {
|
return {
|
||||||
blobPromise: exportToBlob({ editor, ids, format, opts }),
|
blobPromise: exportToBlob({ editor, ids, format, opts }),
|
||||||
|
|
|
@ -20,7 +20,7 @@ export async function exportAs(
|
||||||
ids: TLShapeId[],
|
ids: TLShapeId[],
|
||||||
format: TLExportType = 'png',
|
format: TLExportType = 'png',
|
||||||
name: string | undefined,
|
name: string | undefined,
|
||||||
opts = {} as Partial<TLSvgOptions>
|
opts: TLSvgOptions = {}
|
||||||
) {
|
) {
|
||||||
// If we don't get name then use a predefined one
|
// If we don't get name then use a predefined one
|
||||||
if (!name) {
|
if (!name) {
|
||||||
|
|
Loading…
Reference in a new issue