Better generated docs for react components (#3930)

Before:
![Screenshot 2024-06-12 at 12 57
26](https://github.com/tldraw/tldraw/assets/1489520/2a9f6098-ef2a-4f52-88f5-d6e4311c067d)

After:
![Screenshot 2024-06-12 at 12 59
16](https://github.com/tldraw/tldraw/assets/1489520/51733c2a-a2b4-4084-a89a-85bce5b47672)

React components in docs now list their props, and appear under a new
"Component" section instead of randomly under either `Function` or
`Variable`. In order to have our docs generate this, a few criteria need
to be met:
1. They need to be tagged with the `@react` tsdoc tag
2. Their props need to be a simple type alias, typically to an
interface.

Both of these rules are enforced with a new lint rule - any component
tagged as `@public` will have these rules enforced.

### Change Type

- [x] `docs` — Changes to the documentation, examples, or templates.
- [x] `improvement` — Improving existing features
This commit is contained in:
alex 2024-06-13 14:09:27 +01:00 committed by GitHub
parent 69e6dbc407
commit 6cb797a074
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
97 changed files with 998 additions and 485 deletions

View file

@ -56,6 +56,7 @@ module.exports = {
],
'local/no-export-star': 'error',
'local/no-internal-imports': 'error',
'local/tagged-components': 'error',
'no-only-tests/no-only-tests': 'error',
'no-restricted-syntax': [
'error',

View file

@ -1,8 +1,7 @@
import { SearchResult } from '@/types/search-types'
import { getDb } from '@/utils/ContentDatabase'
import { SEARCH_RESULTS, searchBucket, sectionTypeBucket } from '@/utils/search-api'
import { structuredClone } from '@tldraw/utils'
import assert from 'assert'
import { assert, structuredClone } from '@tldraw/utils'
import { NextRequest } from 'next/server'
type Data = {

View file

@ -17,6 +17,154 @@ order: 3
{/* START AUTO-GENERATED CHANGELOG */}
### [v2.2.0](/releases/v2.2.0)
#### Bindings
Bindings allow you to create relationships between shapes. Our default arrow shapes are a great example of this: each end of the arrow can **bind** to the shape it's pointing to. When that shape moves, so does the arrow. Before this change, it wasn't possible to build things like arrows on top of the tldraw sdk - arrows were hard-coded into the library. Now, with bindings, you can create arrows, constraint systems, visual programming environments, and much more.
Check out the [bindings guide](https://tldraw.dev/docs/editor#Bindings) for more information. ([#3326](https://github.com/tldraw/tldraw/pull/3326), [#3780](https://github.com/tldraw/tldraw/pull/3780), [#3797](https://github.com/tldraw/tldraw/pull/3797), [#3800](https://github.com/tldraw/tldraw/pull/3800), [#3871](https://github.com/tldraw/tldraw/pull/3871))
#### Camera constraints
You can now limit the camera in tldraw to a certain fixed area of the canvas. This is useful for creating experiences that don't quite fit the "infinite canvas" paradigm: document annotators, image editor, slideshow creators, etc.
See the [camera constraints guide](https://tldraw.dev/docs/editor#Camera-options) for more information. ([#3282](https://github.com/tldraw/tldraw/pull/3282), [#3747](https://github.com/tldraw/tldraw/pull/3747), [#3814](https://github.com/tldraw/tldraw/pull/3814), [#3828](https://github.com/tldraw/tldraw/pull/3828), [#3844](https://github.com/tldraw/tldraw/pull/3844), [#3863](https://github.com/tldraw/tldraw/pull/3863))
#### Configurable options prop
You can now override many options which were previously hard-coded constants. Pass an `options` prop into the tldraw component to change the maximum number of pages, grid steps, or other previously hard-coded values.
See [`TldrawOptions`](https://tldraw.dev/reference/editor/TldrawOptions) for details. ([#3799](https://github.com/tldraw/tldraw/pull/3799), [#3900](https://github.com/tldraw/tldraw/pull/3900))
#### Breaking changes
- The `canBind` flag now accepts an options object instead of just the shape in question. If you're relying on its arguments, check out [`TLShapeUtilCanBindOpts`](https://tldraw.dev/reference/editor/TLShapeUtilCanBindOpts) for its replacement.
- `editor.sideEffects.registerBatchCompleteHandler` has been replaced with `editor.sideEffects.registerOperationCompleteHandler` ([#3748](https://github.com/tldraw/tldraw/pull/3748))
- `editor.getArrowInfo(shape)` has been replaced with `getArrowInfo(editor, shape)`
- `editor.getArrowsBoundTo(shape)` has been removed. Instead, use `editor.getBindingsToShape(shape, 'arrow')` and follow the `fromId` of each binding to the corresponding arrow shape
- These types have moved from `@tldraw/editor` to `tldraw`:
- `TLArcInfo`
- `TLArrowInfo`
- `TLArrowPoint`
- The `start` and `end` properties on `TLArrowShape` no longer have `type: point | binding`. Instead, they're always a point, which may be out of date if a binding exists. To check for & retrieve arrow bindings, use `getArrowBindings(editor, shape)` instead.
- `getArrowTerminalsInArrowSpace` must be passed a `TLArrowBindings` as a third argument: `getArrowTerminalsInArrowSpace(editor, shape, getArrowBindings(editor, shape))`
- The following types have been renamed:
- `ShapeProps` -> `RecordProps`
- `ShapePropsType` -> `RecordPropsType`
- `TLShapePropsMigrations` -> `TLPropsMigrations`
- `SchemaShapeInfo` -> `SchemaPropsInfo`
##### Undo/redo
###### 1. History Options
Previously, some (not all!) commands accepted a history options object with `squashing`, `ephemeral`, and `preserveRedoStack` flags. Squashing enabled/disabled a memory optimisation (storing individual commands vs squashing them together). Ephemeral stopped a command from affecting the undo/redo stack at all. Preserve redo stack stopped commands from wiping the redo stack. These flags were never available consistently - some commands had them and others didn't.
In this version, most of these flags have been removed. `squashing` is gone entirely (everything squashes & does so much faster than before). There were a couple of commands that had a special default - for example, `updateInstanceState` used to default to being `ephemeral`. Those maintain the defaults, but the options look a little different now - `{ephemeral: true}` is now `{history: 'ignore'}` and `{preserveRedoStack: true}` is now `{history: 'record-preserveRedoStack'}`.
If you were previously using these options in places where they've now been removed, you can use wrap them with `editor.history.ignore(fn)` or `editor.history.batch(fn, {history: 'record-preserveRedoStack'})`. For example,
```ts
editor.nudgeShapes(..., { ephemeral: true })
```
can now be written as
```ts
editor.history.ignore(() => {
editor.nudgeShapes(...)
})
```
###### 2. Automatic recording
Previously, only commands (e.g. `editor.updateShapes` and things that use it) were added to the undo/redo stack. Everything else (e.g. `editor.store.put`) wasn't. Now, _everything_ that touches the store is recorded in the undo/redo stack (unless it's part of `mergeRemoteChanges`). You can use `editor.history.ignore(fn)` as above if you want to make other changes to the store that aren't recorded - this is short for `editor.history.batch(fn, {history: 'ignore'})`
When upgrading to this version of tldraw, you shouldn't need to change anything unless you're using `store.put`, `store.remove`, or `store.applyDiff` outside of `store.mergeRemoteChanges`. If you are, you can preserve the functionality of those not being recorded by wrapping them either in `mergeRemoteChanges` (if they're multiplayer-related) or `history.ignore` as appropriate.
###### 3. Side effects
Before this diff, any changes in side-effects weren't captured by the undo-redo stack. This was actually the motivation for this change in the first place! But it's a pretty big change, and if you're using side effects we recommend you double-check how they interact with undo/redo before/after this change. To get the old behaviour back, wrap your side effects in `editor.history.ignore`.
###### 4. Mark options
Previously, `editor.mark(id)` accepted two additional boolean parameters: `onUndo` and `onRedo`. If these were set to false, then when undoing or redoing we'd skip over that mark and keep going until we found one with those values set to true. We've removed those options - if you're using them, let us know and we'll figure out an alternative!
#### Improvements
- Nicer rendering for bookmarks without preview images. ([#3856](https://github.com/tldraw/tldraw/pull/3856))
- Improve undo/redo UX around image cropping. ([#3891](https://github.com/tldraw/tldraw/pull/3891))
- Disable toolbar items that don't work when not in select mode. ([#3819](https://github.com/tldraw/tldraw/pull/3819))
- ❤️ We've added a heart shape to the geo shape set. ([#3787](https://github.com/tldraw/tldraw/pull/3787))
- Detect coarse pointers (ie touch) more reliably. ([#3572](https://github.com/tldraw/tldraw/pull/3572), [#3656](https://github.com/tldraw/tldraw/pull/3656), [#3795](https://github.com/tldraw/tldraw/pull/3795))
- Reduce padding when zooming to fit. ([#3798](https://github.com/tldraw/tldraw/pull/3798))
- Increase the default limit of shapes per page from 2000 to 4000 ([#3716](https://github.com/tldraw/tldraw/pull/3716))
- Unify list of accepted image types and expand to include webp, webm, apng, & avif. ([#3730](https://github.com/tldraw/tldraw/pull/3730))
- Prunes unused assets when loading a `.tldr` file. ([#3689](https://github.com/tldraw/tldraw/pull/3689))
- Improve handling of mouse-type devices that support pressure, e.g. wacom tablets. They now use the same freehand options as true pen-type inputs. ([#3639](https://github.com/tldraw/tldraw/pull/3639))
- Separate the text align property for text shapes and labels. Text shapes are now left-aligned by default. ([#3627](https://github.com/tldraw/tldraw/pull/3627))
- Add desmos graph embed type. ([#3608](https://github.com/tldraw/tldraw/pull/3608))
- Tweak default gap value to be consistent with sticky note gaps. ([#3606](https://github.com/tldraw/tldraw/pull/3606))
#### API changes
- Add `editor.blur` method. ([#3875](https://github.com/tldraw/tldraw/pull/3875))
- Better defaults for `createTLStore`. ([#3886](https://github.com/tldraw/tldraw/pull/3886))
- Add `getSnapshot` and `loadSnapshot` for easier loading/saving of tldraw documents. Read more [here](https://tldraw.dev/docs/persistence#State-Snapshots). ([#3811](https://github.com/tldraw/tldraw/pull/3811))
- Add `select` option to `Editor.groupShapes` and `Editor.ungroupShapes`. ([#3690](https://github.com/tldraw/tldraw/pull/3690))
- `InFrontOfTheCanvas` now has access to the editor's UI context ([#3782](https://github.com/tldraw/tldraw/pull/3782))
- `useEditor` and other context-based hooks will now throw an error when used out-of-context, instead of returning a fake value. ([#3750](https://github.com/tldraw/tldraw/pull/3750))
- Expose migrations, validators, and versions from tlschema. Previously, we weren't exporting migrations & validators for our default shapes. This meant that it wasn't possible to make your own tlschema with both our default shapes and some of your own (e.g. for custom multiplayer). This fixes that by exposing all the migrations, validators, and versions from tlschema, plus `defaultShapeSchemas` which can be passed directly to `createTLSchema`.([#3613](https://github.com/tldraw/tldraw/pull/3613))
#### Bug fixes
- Fix 'insert media' undo removing other changes. ([#3910](https://github.com/tldraw/tldraw/pull/3910))
- Fix referrer being sent for bookmarks and images. ([#3881](https://github.com/tldraw/tldraw/pull/3881))
- Prevent stale shape data sometimes being used in render. ([#3882](https://github.com/tldraw/tldraw/pull/3882))
- Fix an issue with pen pressure. ([#3877](https://github.com/tldraw/tldraw/pull/3877))
- Fixed a bug where the minimum distance for a drag was wrong when zoomed in or out. ([#3873](https://github.com/tldraw/tldraw/pull/3873))
- Make sure timers/animation frames are disposed along with the editor. ([#3852](https://github.com/tldraw/tldraw/pull/3852))
- Fix some inconsistencies with text label rendering. ([#3830](https://github.com/tldraw/tldraw/pull/3830))
- Fixed cropped images not exporting properly. ([#3837](https://github.com/tldraw/tldraw/pull/3837))
- Fix bug with spacebar & middle mousenbutton panning. ([#3791](https://github.com/tldraw/tldraw/pull/3791), [#3792](https://github.com/tldraw/tldraw/pull/3792))
- Make sure any in-progress interactions are cancelled when switching page/ ([#3771](https://github.com/tldraw/tldraw/pull/3771)
- Fixes a bug that caused the cursor & shapes to wiggle around when following someone else's viewport. ([#3695](https://github.com/tldraw/tldraw/pull/3695))
- Fix some long-stanging cross-browser issues with focus management. ([#3718](https://github.com/tldraw/tldraw/pull/3718))
- Fix bug preventing imports in Astro. ([#3742](https://github.com/tldraw/tldraw/pull/3742))
- Fixes an issue with copy pasting shapes as svg and png not correctly working for patterned shapes. ([#3708](https://github.com/tldraw/tldraw/pull/3708))
- Fix RTL text layout for SVG exports. ([#3680](https://github.com/tldraw/tldraw/pull/3680))
- Fixes a rare crash effecting text shapes on mobile. ([#3672](https://github.com/tldraw/tldraw/pull/3672))
- Fix textbox direction when it contains both RTL and LTR languages /([#3188](https://github.com/tldraw/tldraw/pull/3188))
- Fix an links in embeds that open the embedded site (e.g. YouTube). ([#3609](https://github.com/tldraw/tldraw/pull/3609))
- Fix pasting not working from Edit menu. ([#3623](https://github.com/tldraw/tldraw/pull/3623))
- Fixed a bug with resizing text shapes from the left and right while holding alt. ([#3632](https://github.com/tldraw/tldraw/pull/3632))
- Fix a bug where locked shapes could still be hovered. ([#3575](https://github.com/tldraw/tldraw/pull/3575))
- Fix clicking on the minimap sometimes not changing the viewport. ([#3617](https://github.com/tldraw/tldraw/pull/3617))
- Fix an issue with the minimap bugging out after you change the window's height. ([#3621](https://github.com/tldraw/tldraw/pull/3621))
#### Translations
- Update French, Hungarian, & Korean translations.
- Add Bahasa Indonesia translation. ([#3649](https://github.com/tldraw/tldraw/pull/3649))
#### Authors: 14
- alex ([@SomeHats](https://github.com/SomeHats))
- CodeTorso ([@CodeTorso](https://github.com/CodeTorso))
- David Sheldrick ([@ds300](https://github.com/ds300))
- Eric Mika ([@kitschpatrol](https://github.com/kitschpatrol))
- Eswar Prasad Clinton. A ([@eswarclynn](https://github.com/eswarclynn))
- fakerr ([@not-first](https://github.com/not-first))
- Lorenzo Lewis ([@lorenzolewis](https://github.com/lorenzolewis))
- Lu Wilson ([@TodePond](https://github.com/TodePond))
- Mime Čuvalo ([@mimecuvalo](https://github.com/mimecuvalo))
- Mitja Bezenšek ([@MitjaBezensek](https://github.com/MitjaBezensek))
- Mohammad Kazemi ([@mokazemi](https://github.com/mokazemi))
- Steve Ruiz ([@steveruizok](https://github.com/steveruizok))
- Taha ([@Taha-Hassan-Git](https://github.com/Taha-Hassan-Git))
- Trevor Dobbertin ([@Trevato](https://github.com/Trevato))
### [v2.1.4](/releases/v2.1.4)
### Release Notes

View file

@ -64,6 +64,10 @@
{
"id": "Namespace",
"path": null
},
{
"id": "Component",
"path": null
}
],
"hero": null
@ -100,6 +104,10 @@
{
"id": "Namespace",
"path": null
},
{
"id": "Component",
"path": null
}
],
"hero": null
@ -136,6 +144,10 @@
{
"id": "Namespace",
"path": null
},
{
"id": "Component",
"path": null
}
],
"hero": null
@ -172,6 +184,10 @@
{
"id": "Namespace",
"path": null
},
{
"id": "Component",
"path": null
}
],
"hero": null
@ -208,6 +224,10 @@
{
"id": "Namespace",
"path": null
},
{
"id": "Component",
"path": null
}
],
"hero": null

View file

@ -1,6 +1,6 @@
import { APIGroup, InputSection } from '@/types/content-types'
import { TldrawApiModel } from '@/utils/TldrawApiModel'
import { nicelog } from '@/utils/nicelog'
import { ApiModel } from '@microsoft/api-extractor-model'
import fs from 'fs'
import path from 'path'
import { CONTENT_DIR, getSlug } from '../utils'
@ -27,7 +27,7 @@ export async function createApiMarkdown() {
fs.mkdirSync(OUTPUT_DIR)
const model = new ApiModel()
const model = new TldrawApiModel()
const packageModels = []
// get all files in the INPUT_DIR
@ -43,6 +43,8 @@ export async function createApiMarkdown() {
packageModels.push(apiModel)
}
await model.preprocessReactComponents()
for (const packageModel of packageModels) {
const categoryName = packageModel.name.replace(`@tldraw/`, '')
@ -65,8 +67,9 @@ export async function createApiMarkdown() {
for (let j = 0; j < entrypoint.members.length; j++) {
const item = entrypoint.members[j]
const result = await getApiMarkdown(categoryName, item, j)
const outputFileName = `${getSlug(item)}.mdx`
const result = await getApiMarkdown(model, categoryName, item, j)
nicelog(`${outputFileName}`)
fs.writeFileSync(path.join(OUTPUT_DIR, outputFileName), result.markdown)
}

View file

@ -1,4 +1,4 @@
import { assert } from '@/utils/assert'
import { assert } from '@tldraw/utils'
import fs from 'fs'
import { Octokit } from 'octokit'
import path from 'path'

View file

@ -1,3 +1,5 @@
import { APIGroup } from '@/types/content-types'
import { TldrawApiModel } from '@/utils/TldrawApiModel'
import {
ApiClass,
ApiConstructSignature,
@ -20,6 +22,7 @@ import {
ApiTypeAlias,
ApiVariable,
Excerpt,
ExcerptToken,
ReleaseTag,
} from '@microsoft/api-extractor-model'
import { MarkdownWriter, formatWithPrettier, getPath, getSlug } from '../utils'
@ -37,16 +40,25 @@ const date = new Intl.DateTimeFormat('en-US', {
day: '2-digit',
}).format(new Date())
export async function getApiMarkdown(categoryName: string, item: ApiItem, j: number) {
export async function getApiMarkdown(
model: TldrawApiModel,
categoryName: string,
item: ApiItem,
j: number
) {
const result: Result = { markdown: '', keywords: [] }
const toc: Result = { markdown: '', keywords: [] }
const membersResult: Result = { markdown: '', keywords: [] }
if (item.members) {
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 item.members) {
for (const member of members) {
switch (member.kind) {
case ApiItemKind.Constructor:
case ApiItemKind.ConstructSignature:
@ -60,7 +72,11 @@ export async function getApiMarkdown(categoryName: string, item: ApiItem, j: num
case ApiItemKind.Method:
case ApiItemKind.Function:
case ApiItemKind.MethodSignature:
methods.push(member)
if (isComponent) {
properties.push(member)
} else {
methods.push(member)
}
break
case ApiItemKind.EnumMember:
@ -80,22 +96,36 @@ export async function getApiMarkdown(categoryName: string, item: ApiItem, j: num
if (constructors.length) {
for (const member of constructors) {
await addMarkdownForMember(constructorResult, member)
await addMarkdownForMember(model, constructorResult, member)
addHorizontalRule(constructorResult)
}
addMarkdown(membersResult, constructorResult.markdown)
}
if (properties.length) {
addMarkdown(toc, `- [Properties](#properties)\n`)
if (properties.length || componentProps) {
addMarkdown(propertiesResult, `## Properties\n\n`)
if (componentProps) addExtends(propertiesResult, componentProps)
for (const member of properties) {
const slug = getSlug(member)
addMarkdown(toc, ` - [${member.displayName}](#${slug})\n`)
await addMarkdownForMember(propertiesResult, member)
await addMarkdownForMember(model, propertiesResult, member, {
isComponentProp: isComponent,
})
addHorizontalRule(propertiesResult)
}
addMarkdown(membersResult, propertiesResult.markdown)
if (
componentProps &&
componentProps instanceof ApiDeclaredItem &&
componentProps?.kind !== 'Interface'
) {
propertiesResult.markdown += await typeExcerptToMarkdown(componentProps.excerpt, {
kind: componentProps.kind,
})
}
if (propertiesResult.markdown.trim()) {
addMarkdown(toc, `- [Properties](#properties)\n`)
addMarkdown(membersResult, propertiesResult.markdown)
}
}
if (methods.length) {
@ -104,14 +134,14 @@ export async function getApiMarkdown(categoryName: string, item: ApiItem, j: num
for (const member of methods) {
const slug = getSlug(member)
addMarkdown(toc, ` - [${member.displayName}](#${slug})\n`)
await addMarkdownForMember(methodsResult, member)
await addMarkdownForMember(model, methodsResult, member)
addHorizontalRule(methodsResult)
}
addMarkdown(membersResult, methodsResult.markdown)
}
}
await addFrontmatter(result, item, categoryName, j)
await addFrontmatter(model, result, item, categoryName, j)
if (toc.markdown.length) {
result.markdown += `<details className="article__table-of-contents">\n\t<summary>Table of contents</summary>\n`
@ -119,11 +149,11 @@ export async function getApiMarkdown(categoryName: string, item: ApiItem, j: num
result.markdown += `</details>\n\n`
}
addTags(result, item)
addTags(model, result, item)
await addDocComment(result, item)
await addDocComment(model, result, item)
addReferences(result, item)
addReferences(model, result, item)
addLinkToSource(result, item)
if (membersResult.markdown.length) {
@ -140,16 +170,21 @@ function addMarkdown(result: Result, markdown: string) {
result.markdown += markdown
}
async function addMarkdownForMember(result: Result, member: ApiItem) {
async function addMarkdownForMember(
model: TldrawApiModel,
result: Result,
member: ApiItem,
{ isComponentProp = false } = {}
) {
if (member.displayName.startsWith('_')) return
addMemberName(result, member)
addTags(result, member)
await addDocComment(result, member)
addReferences(result, member)
addLinkToSource(result, member)
addTags(model, result, member, { isComponentProp })
await addDocComment(model, result, member, { isComponentProp })
addReferences(model, result, member)
}
async function addFrontmatter(
model: TldrawApiModel,
result: Result,
member: ApiItem,
categoryName: string,
@ -180,7 +215,7 @@ title: ${member.displayName}
status: published
description: ${description}
category: ${categoryName}
group: ${member.kind}
group: ${model.isComponent(member) ? APIGroup.Component : member.kind}
author: api
date: ${date}
order: ${order}
@ -203,11 +238,18 @@ function addMemberName(result: Result, member: ApiItem) {
result.markdown += `### \`${member.displayName}${member.kind === 'Method' ? '()' : ''}\`\n\n`
}
async function addDocComment(result: Result, member: ApiItem) {
async function addDocComment(
model: TldrawApiModel,
result: Result,
member: ApiItem,
{ isComponentProp = false } = {}
) {
if (!(member instanceof ApiDocumentedItem)) {
return
}
const isComponent = model.isComponent(member)
if (member.tsdocComment) {
result.markdown += await MarkdownWriter.docNodeToMarkdown(
member,
@ -239,13 +281,15 @@ async function addDocComment(result: Result, member: ApiItem) {
member instanceof ApiNamespace ||
member instanceof ApiMethod
) {
result.markdown += `<ApiHeading>Signature</ApiHeading>\n\n`
if (!isComponentProp) result.markdown += `<ApiHeading>Signature</ApiHeading>\n\n`
result.markdown += await typeExcerptToMarkdown(member.excerpt, {
kind: member.kind,
})
result.markdown += `\n\n`
}
if (isComponent) return
if (
member instanceof ApiMethod ||
member instanceof ApiMethodSignature ||
@ -321,7 +365,7 @@ async function addDocComment(result: Result, member: ApiItem) {
result.markdown += '</ParametersTable>\n\n'
}
} else {
throw new Error('unknown member kind: ' + member.kind)
model.error(member, `Unknown member kind: ${member.kind}`)
}
}
@ -384,44 +428,81 @@ async function typeExcerptToMarkdown(
return ['```ts', code, '```'].join('\n')
}
function addTags(result: Result, member: ApiItem) {
function addTags(
model: TldrawApiModel,
result: Result,
member: ApiItem,
{ isComponentProp = false } = {}
) {
const tags = []
if (ApiReleaseTagMixin.isBaseClassOf(member)) {
tags.push(ReleaseTag[member.releaseTag])
if (!isComponentProp) {
if (ApiReleaseTagMixin.isBaseClassOf(member)) {
tags.push(ReleaseTag[member.releaseTag])
}
if (ApiStaticMixin.isBaseClassOf(member) && member.isStatic) {
tags.push('static')
}
if (ApiReadonlyMixin.isBaseClassOf(member) && member.isReadonly) {
tags.push('readonly')
}
}
if (ApiStaticMixin.isBaseClassOf(member) && member.isStatic) {
tags.push('static')
if (member instanceof ApiPropertySignature && member.isOptional) {
tags.push('optional')
}
if (ApiReadonlyMixin.isBaseClassOf(member) && member.isReadonly) {
tags.push('readonly')
if (!isComponentProp) {
const kind = model.isComponent(member) ? 'component' : member.kind.toLowerCase()
tags.push(kind)
}
tags.push(member.kind.toLowerCase())
result.markdown += `<Small>${tags.filter((t) => t.toLowerCase() !== 'none').join(' ')}</Small>\n\n`
}
function addReferences(result: Result, member: ApiItem) {
function addReferences(model: TldrawApiModel, result: Result, member: ApiItem) {
if (!(member instanceof ApiDeclaredItem)) return
const references = new Set<string>()
member.excerptTokens.forEach((token) => {
function addToken(item: ApiDeclaredItem, token: ExcerptToken) {
if (token.kind !== 'Reference') return
const apiItemResult = member
const apiItemResult = item
.getAssociatedModel()!
.resolveDeclarationReference(token.canonicalReference!, member)
.resolveDeclarationReference(token.canonicalReference!, item)
if (apiItemResult.errorMessage) {
return
}
const apiItem = apiItemResult.resolvedApiItem!
const url = `/reference/${getPath(apiItem)}`
references.add(`[${token.text}](${url})`)
}
member.excerptTokens.forEach((token) => {
addToken(member, token)
})
const componentProps = model.isComponent(member) ? model.getReactPropsItem(member) : null
if (componentProps && componentProps instanceof ApiDeclaredItem) {
componentProps.excerptTokens.forEach((token) => {
addToken(componentProps, token)
})
}
if (references.size) {
result.markdown += `<ApiHeading>References</ApiHeading>\n\n`
result.markdown += Array.from(references).join(', ') + '\n\n'
}
}
function addExtends(result: Result, item: ApiItem) {
const extendsTypes =
item instanceof ApiClass && item.extendsType
? [item.extendsType]
: item instanceof ApiInterface
? item.extendsTypes
: []
if (!extendsTypes.length) return
result.markdown += `Extends \`${extendsTypes.map((type) => type.excerpt.text).join(', ')}\`.\n\n`
}
function addLinkToSource(result: Result, member: ApiItem) {
if ('_fileUrlPath' in member && member._fileUrlPath) {
result.markdown += `<ApiHeading>Source</ApiHeading>\n\n`

View file

@ -10,7 +10,7 @@ import {
DocSection,
DocSoftBreak,
} from '@microsoft/tsdoc'
import assert from 'assert'
import { assert } from '@tldraw/utils'
import { slug as githubSlug } from 'github-slugger'
import path from 'path'
@ -98,6 +98,12 @@ export async function formatWithPrettier(
console.warn(`☢️ Could not format code: ${code}`)
}
// sometimes prettier adds a semicolon to the start of the code when formatting expressions (JSX
// in particular), so strip it if we see it
if (formattedCode.startsWith(';')) {
formattedCode = formattedCode.slice(1)
}
return formattedCode.trimEnd()
}

View file

@ -130,6 +130,7 @@ export enum APIGroup {
Interface = 'Interface',
TypeAlias = 'TypeAlias',
Namespace = 'Namespace',
Component = 'Component',
}
/* ---------------- Article Headings ---------------- */

View file

@ -1,4 +1,5 @@
import { connect } from '@/scripts/functions/connect'
import { assert } from '@tldraw/utils'
import { Database } from 'sqlite'
import sqlite3 from 'sqlite3'
import {
@ -12,7 +13,6 @@ import {
SidebarContentLink,
SidebarContentList,
} from '../types/content-types'
import { assert } from './assert'
export class ContentDatabase {
constructor(public db: Database<sqlite3.Database, sqlite3.Statement>) {}

View file

@ -0,0 +1,163 @@
import { MarkdownWriter } from '@/scripts/utils'
import {
ApiDocumentedItem,
ApiFunction,
ApiItem,
ApiModel,
ApiPackage,
ApiVariable,
ExcerptToken,
ExcerptTokenKind,
} from '@microsoft/api-extractor-model'
import { assert } from '@tldraw/utils'
export class TldrawApiModel extends ApiModel {
private reactComponents = new Set<ApiItem>()
private reactComponentProps = new Set<ApiItem>()
async preprocessReactComponents() {
const errors = []
for (const packageModel of this.members) {
assert(packageModel instanceof ApiPackage)
if (packageModel.name !== 'tldraw') continue
const entrypoint = packageModel.entryPoints[0]
for (const member of entrypoint.members) {
assert(member instanceof ApiDocumentedItem)
if (!member.tsdocComment) continue
if (!member.tsdocComment.modifierTagSet.hasTagName('@react')) continue
this.reactComponents.add(member)
try {
const props = this.getReactPropsItem(member)
if (props instanceof ApiDocumentedItem && props.tsdocComment) {
const markdown = await MarkdownWriter.docNodeToMarkdown(
props,
props.tsdocComment.summarySection
)
if (markdown.trim()) {
this.error(
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)
}
}
}
if (errors.length > 0) {
throw new Error(errors.map((e) => (e as any).message).join('\n\n'))
}
}
resolveToken(origin: ApiItem, token: ExcerptToken) {
const apiItemResult = this.resolveDeclarationReference(token.canonicalReference!, origin)
if (apiItemResult.errorMessage) {
this.error(origin, apiItemResult.errorMessage)
}
return apiItemResult.resolvedApiItem!
}
getReactPropsItem(component: ApiItem): ApiItem | null {
if (component instanceof ApiFunction) {
if (component.parameters.length === 0) return null
this.assert(
component,
component.parameters.length === 1,
`Expected 1 parameter for @react component`
)
const propsParam = component.parameters[0]
const tokens = propsParam.parameterTypeExcerpt.spannedTokens
if (tokens.length === 1 && tokens[0].kind === 'Reference') {
return this.resolveToken(component, tokens[0])
} else if (
tokens.length === 2 &&
tokens[0].kind === 'Reference' &&
tokens[1].text.startsWith('<')
) {
return this.resolveToken(component, tokens[0])
}
this.error(
component,
`Expected props parameter to be a simple reference. Rewrite this to use a \`${component.displayName}Props\` interface.\nFound: ${propsParam.parameterTypeExcerpt.text}`
)
} else if (component instanceof ApiVariable) {
const tokens = component.variableTypeExcerpt.spannedTokens
if (
tokens.length === 5 &&
tokens[0].text === 'import("react").' &&
tokens[1].text === 'NamedExoticComponent' &&
tokens[2].text === '<' &&
tokens[3].kind === ExcerptTokenKind.Reference &&
tokens[4].text === '>'
) {
return this.resolveToken(component, tokens[3])
}
if (
tokens.length === 4 &&
tokens[0].text === 'React.NamedExoticComponent' &&
tokens[1].text === '<' &&
tokens[2].kind === ExcerptTokenKind.Reference &&
tokens[3].text === '>'
) {
return this.resolveToken(component, tokens[2])
}
if (
tokens.length === 8 &&
tokens[0].text === 'React.ForwardRefExoticComponent' &&
tokens[1].text === '<' &&
tokens[2].kind === ExcerptTokenKind.Reference &&
tokens[3].text === ' & ' &&
tokens[4].text === 'React.RefAttributes' &&
tokens[5].text === '<' &&
tokens[6].kind === ExcerptTokenKind.Reference &&
tokens[7].text === '>>'
) {
return this.resolveToken(component, tokens[2])
}
if (component.variableTypeExcerpt.text === 'import("react").NamedExoticComponent<object>') {
// this is a `memo` component with no props
return null
}
this.error(
component,
`Expected a simple props interface for react component. Got: ${component.variableTypeExcerpt.text}`
)
} else {
this.error(component, `Unknown item kind for @react component: ${component.kind}`)
}
}
isComponent(item: ApiItem): boolean {
return this.reactComponents.has(item)
}
isComponentProps(item: ApiItem): boolean {
return this.reactComponentProps.has(item)
}
error(item: ApiItem, message: string): never {
const suffix =
'_fileUrlPath' in item && typeof item._fileUrlPath === 'string'
? `\nin ${item._fileUrlPath}`
: ''
throw new Error(`${item.displayName}: ${message}${suffix}`)
}
assert(item: ApiItem, condition: unknown, message: string): asserts condition {
if (!condition) {
this.error(item, message)
}
}
}

View file

@ -1,5 +0,0 @@
export function assert<T>(thing: T, errorMessage?: string): asserts thing is NonNullable<T> {
if (thing === null || thing === undefined) {
throw new Error(errorMessage)
}
}

View file

@ -108,7 +108,6 @@ async function getReadonlyUrl() {
return `${window.location.origin}${newPathname}${window.location.search}`
}
/** @public */
export const ShareMenu = React.memo(function ShareMenu() {
const msg = useTranslation()
const container = useContainer()

View file

@ -1,7 +1,5 @@
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"packages": [
"packages/*"
],
"packages": ["packages/*"],
"version": "2.2.0"
}

View file

@ -1282,9 +1282,7 @@ export class ErrorBoundary extends React_3.Component<React_3.PropsWithRef<React_
}
// @public (undocumented)
export function ErrorScreen({ children }: {
children: ReactNode;
}): JSX_2.Element;
export function ErrorScreen({ children }: LoadingScreenProps): JSX_2.Element;
// @public (undocumented)
export const EVENT_NAME_MAP: Record<Exclude<TLEventName, TLPinchEventName>, keyof TLEventHandlers>;
@ -1600,9 +1598,13 @@ export const isSafeFloat: (n: number) => boolean;
export function linesIntersect(A: VecLike, B: VecLike, C: VecLike, D: VecLike): boolean;
// @public (undocumented)
export function LoadingScreen({ children }: {
export function LoadingScreen({ children }: LoadingScreenProps): JSX_2.Element;
// @public (undocumented)
export interface LoadingScreenProps {
// (undocumented)
children: ReactNode;
}): JSX_2.Element;
}
// @public
export function loadSessionStateSnapshotIntoStore(store: TLStore, snapshot: TLSessionStateSnapshot): void;

View file

@ -37,6 +37,7 @@ export {
ErrorScreen,
LoadingScreen,
TldrawEditor,
type LoadingScreenProps,
type TLOnMountHandler,
type TldrawEditorBaseProps,
type TldrawEditorProps,

View file

@ -160,7 +160,7 @@ const EMPTY_SHAPE_UTILS_ARRAY = [] as const
const EMPTY_BINDING_UTILS_ARRAY = [] as const
const EMPTY_TOOLS_ARRAY = [] as const
/** @public */
/** @public @react */
export const TldrawEditor = memo(function TldrawEditor({
store,
components,
@ -415,12 +415,17 @@ function Crash({ crashingError }: { crashingError: unknown }): null {
}
/** @public */
export function LoadingScreen({ children }: { children: ReactNode }) {
export interface LoadingScreenProps {
children: ReactNode
}
/** @public @react */
export function LoadingScreen({ children }: LoadingScreenProps) {
return <div className="tl-loading">{children}</div>
}
/** @public */
export function ErrorScreen({ children }: { children: ReactNode }) {
/** @public @react */
export function ErrorScreen({ children }: LoadingScreenProps) {
return <div className="tl-loading">{children}</div>
}

View file

@ -4,7 +4,7 @@ import * as React from 'react'
/** @public */
export type HTMLContainerProps = React.HTMLAttributes<HTMLDivElement>
/** @public */
/** @public @react */
export function HTMLContainer({ children, className = '', ...rest }: HTMLContainerProps) {
return (
<div {...rest} className={classNames('tl-html-container', className)}>

View file

@ -4,7 +4,7 @@ import * as React from 'react'
/** @public */
export type SVGContainerProps = React.HTMLAttributes<SVGElement>
/** @public */
/** @public @react */
export function SVGContainer({ children, className = '', ...rest }: SVGContainerProps) {
return (
<svg {...rest} className={classNames('tl-svg-container', className)}>

View file

@ -1,4 +1,4 @@
/** @public */
/** @public @react */
export function DefaultBackground() {
return <div className="tl-background" />
}

View file

@ -11,7 +11,7 @@ export interface TLBrushProps {
className?: string
}
/** @public */
/** @public @react */
export const DefaultBrush = ({ brush, color, opacity, className }: TLBrushProps) => {
const rSvg = useRef<SVGSVGElement>(null)
useTransform(rSvg, brush.x, brush.y)

View file

@ -29,7 +29,7 @@ export interface TLCanvasComponentProps {
className?: string
}
/** @public */
/** @public @react */
export function DefaultCanvas({ className }: TLCanvasComponentProps) {
const editor = useEditor()

View file

@ -16,7 +16,7 @@ export interface TLCollaboratorHintProps {
color: string
}
/** @public */
/** @public @react */
export function DefaultCollaboratorHint({
className,
zoom,

View file

@ -13,7 +13,7 @@ export interface TLCursorProps {
chatMessage: string
}
/** @public */
/** @public @react */
export const DefaultCursor = memo(function DefaultCursor({
className,
zoom,

View file

@ -14,7 +14,7 @@ const BASE_ERROR_URL = 'https://github.com/tldraw/tldraw/issues/new'
/** @public */
export type TLErrorFallbackComponent = ComponentType<{ error: unknown; editor?: Editor }>
/** @public */
/** @public @react */
export const DefaultErrorFallback: TLErrorFallbackComponent = ({ error, editor }) => {
const containerRef = useRef<HTMLDivElement>(null)
const [shouldShowError, setShouldShowError] = useState(process.env.NODE_ENV === 'development')

View file

@ -9,7 +9,7 @@ export interface TLGridProps {
size: number
}
/** @public */
/** @public @react */
export function DefaultGrid({ x, y, z, size }: TLGridProps) {
const editor = useEditor()
const { gridSteps } = editor.options

View file

@ -12,7 +12,7 @@ export interface TLHandleProps {
className?: string
}
/** @public */
/** @public @react */
export function DefaultHandle({ handle, isCoarse, className, zoom }: TLHandleProps) {
const editor = useEditor()
const br = (isCoarse ? editor.options.coarseHandleRadius : editor.options.handleRadius) / zoom

View file

@ -5,7 +5,7 @@ export interface TLHandlesProps {
children: ReactNode
}
/** @public */
/** @public @react */
export const DefaultHandles = ({ children }: TLHandlesProps) => {
return <svg className="tl-user-handles tl-overlays__item">{children}</svg>
}

View file

@ -1,7 +1,7 @@
import { LoadingScreen } from '../../TldrawEditor'
import { useEditorComponents } from '../../hooks/useEditorComponents'
/** @public */
/** @public @react */
export const DefaultLoadingScreen = () => {
const { Spinner } = useEditorComponents()
return <LoadingScreen>{Spinner ? <Spinner /> : null}</LoadingScreen>

View file

@ -11,7 +11,7 @@ export interface TLScribbleProps {
className?: string
}
/** @public */
/** @public @react */
export function DefaultScribble({ scribble, zoom, color, opacity, className }: TLScribbleProps) {
if (!scribble.points.length) return null

View file

@ -9,7 +9,7 @@ export interface TLSelectionBackgroundProps {
rotation: number
}
/** @public */
/** @public @react */
export function DefaultSelectionBackground({ bounds, rotation }: TLSelectionBackgroundProps) {
const rDiv = React.useRef<HTMLDivElement>(null)
useTransform(rDiv, bounds.x, bounds.y, 1, rotation)

View file

@ -12,7 +12,7 @@ export interface TLSelectionForegroundProps {
rotation: number
}
/** @public */
/** @public @react */
export function DefaultSelectionForeground({ bounds, rotation }: TLSelectionForegroundProps) {
const editor = useEditor()
const rSvg = useRef<SVGSVGElement>(null)

View file

@ -45,7 +45,7 @@ export interface TLShapeIndicatorProps {
hidden?: boolean
}
/** @public */
/** @public @react */
export const DefaultShapeIndicator = memo(function DefaultShapeIndicator({
shapeId,
className,

View file

@ -160,7 +160,7 @@ export interface TLSnapIndicatorProps {
zoom: number
}
/** @public */
/** @public @react */
export function DefaultSnapIndicator({ className, line, zoom }: TLSnapIndicatorProps) {
return (
<svg className={classNames('tl-overlays__item', className)}>

View file

@ -1,4 +1,4 @@
/** @public */
/** @public @react */
export function DefaultSpinner() {
return (
<svg width={16} height={16} viewBox="0 0 16 16">

View file

@ -1,4 +1,4 @@
/** @public */
/** @public @react */
export const DefaultSvgDefs = () => {
return null
}

View file

@ -32,8 +32,8 @@ import { MigrationSequence } from '@tldraw/editor';
import { NamedExoticComponent } from 'react';
import { Polygon2d } from '@tldraw/editor';
import { Polyline2d } from '@tldraw/editor';
import { default as React_2 } from 'react';
import * as React_3 from 'react';
import * as React_2 from 'react';
import { default as React_3 } from 'react';
import { ReactElement } from 'react';
import { ReactNode } from 'react';
import { ReadonlySharedStyleMap } from '@tldraw/editor';
@ -116,7 +116,7 @@ import { VecModel } from '@tldraw/editor';
// @public (undocumented)
export interface ActionsProviderProps {
// (undocumented)
children: React_3.ReactNode;
children: React_2.ReactNode;
// (undocumented)
overrides?: (editor: Editor, actions: TLUiActionsContextType, helpers: undefined) => TLUiActionsContextType;
}
@ -134,9 +134,7 @@ export function ArrangeMenuSubmenu(): JSX_2.Element | null;
export function ArrowDownToolbarItem(): JSX_2.Element;
// @public (undocumented)
export function ArrowheadStylePickerSet({ styles }: {
styles: ReadonlySharedStyleMap;
}): JSX_2.Element | null;
export function ArrowheadStylePickerSet({ styles }: StylePickerSetProps): JSX_2.Element | null;
// @public (undocumented)
export function ArrowLeftToolbarItem(): JSX_2.Element;
@ -293,10 +291,15 @@ export interface BoxWidthHeight {
}
// @public (undocumented)
export function BreakPointProvider({ forceMobile, children, }: {
export function BreakPointProvider({ forceMobile, children }: BreakPointProviderProps): JSX_2.Element;
// @public (undocumented)
export interface BreakPointProviderProps {
// (undocumented)
children: ReactNode;
// (undocumented)
forceMobile?: boolean;
}): JSX_2.Element;
}
// @internal (undocumented)
export function buildFromV1Document(editor: Editor, _document: unknown): void;
@ -311,10 +314,7 @@ export function ClipboardMenuGroup(): JSX_2.Element;
export function CloudToolbarItem(): JSX_2.Element;
// @public (undocumented)
export function CommonStylePickerSet({ styles, theme, }: {
styles: ReadonlySharedStyleMap;
theme: TLDefaultColorTheme;
}): JSX_2.Element;
export function CommonStylePickerSet({ styles, theme }: ThemeStylePickerSetProps): JSX_2.Element;
// @public
export function containBoxSize(originalSize: BoxWidthHeight, containBoxSize: BoxWidthHeight): BoxWidthHeight;
@ -421,13 +421,17 @@ export const DefaultStylePanel: NamedExoticComponent<TLUiStylePanelProps>;
export function DefaultStylePanelContent({ styles }: TLUiStylePanelContentProps): JSX_2.Element | null;
// @public
export const DefaultToolbar: NamedExoticComponent< {
children?: ReactNode;
}>;
export const DefaultToolbar: NamedExoticComponent<DefaultToolbarProps>;
// @public (undocumented)
export function DefaultToolbarContent(): JSX_2.Element;
// @public (undocumented)
export interface DefaultToolbarProps {
// (undocumented)
children?: ReactNode;
}
// @public (undocumented)
export const defaultTools: (typeof EraserTool | typeof HandTool | typeof ZoomTool)[];
@ -572,21 +576,31 @@ export function EraserToolbarItem(): JSX_2.Element;
// @public (undocumented)
export interface EventsProviderProps {
// (undocumented)
children: React_3.ReactNode;
children: React_2.ReactNode;
// (undocumented)
onEvent?: TLUiEventHandler;
}
// @public (undocumented)
export function ExampleDialog({ title, body, cancel, confirm, displayDontShowAgain, onCancel, onContinue, }: {
export function ExampleDialog({ title, body, cancel, confirm, displayDontShowAgain, onCancel, onContinue, }: ExampleDialogProps): JSX_2.Element;
// @public (undocumented)
export interface ExampleDialogProps {
// (undocumented)
body?: string;
// (undocumented)
cancel?: string;
// (undocumented)
confirm?: string;
// (undocumented)
displayDontShowAgain?: boolean;
// (undocumented)
onCancel: () => void;
// (undocumented)
onContinue: () => void;
// (undocumented)
title?: string;
}): JSX_2.Element;
}
// @public
export function exportAs(editor: Editor, ids: TLShapeId[], format: TLExportType | undefined, name: string | undefined, opts?: Partial<TLSvgOptions>): Promise<void>;
@ -822,9 +836,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
}
// @public (undocumented)
export function GeoStylePickerSet({ styles }: {
styles: ReadonlySharedStyleMap;
}): JSX_2.Element | null;
export function GeoStylePickerSet({ styles }: StylePickerSetProps): JSX_2.Element | null;
// @public (undocumented)
export function getArrowBindings(editor: Editor, shape: TLArrowShape): TLArrowBindings;
@ -1194,11 +1206,17 @@ export function OpacitySlider(): JSX_2.Element | null;
export function OvalToolbarItem(): JSX_2.Element;
// @public (undocumented)
export const PageItemInput: ({ name, id, isCurrentPage, }: {
export const PageItemInput: ({ name, id, isCurrentPage, }: PageItemInputProps) => JSX_2.Element;
// @public (undocumented)
export interface PageItemInputProps {
// (undocumented)
id: TLPageId;
// (undocumented)
isCurrentPage: boolean;
// (undocumented)
name: string;
}) => JSX_2.Element;
}
// @public (undocumented)
export const PageItemSubmenu: MemoExoticComponent<({ index, listSize, item, onRename, }: PageItemSubmenuProps) => JSX_2.Element>;
@ -1315,12 +1333,10 @@ export function setDefaultEditorAssetUrls(assetUrls: TLEditorAssetUrls): void;
export function setDefaultUiAssetUrls(urls: TLUiAssetUrls): void;
// @internal (undocumented)
export function Spinner(props: React_2.SVGProps<SVGSVGElement>): JSX_2.Element;
export function Spinner(props: React_3.SVGProps<SVGSVGElement>): JSX_2.Element;
// @public (undocumented)
export function SplineStylePickerSet({ styles }: {
styles: ReadonlySharedStyleMap;
}): JSX_2.Element | null;
export function SplineStylePickerSet({ styles }: StylePickerSetProps): JSX_2.Element | null;
// @public (undocumented)
export function StackMenuItems(): JSX_2.Element;
@ -1328,6 +1344,12 @@ export function StackMenuItems(): JSX_2.Element;
// @public (undocumented)
export function StarToolbarItem(): JSX_2.Element;
// @public (undocumented)
export interface StylePickerSetProps {
// (undocumented)
styles: ReadonlySharedStyleMap;
}
// @public (undocumented)
export type StyleValuesForUi<T> = readonly {
readonly icon: string;
@ -1344,7 +1366,7 @@ export const TEXT_PROPS: {
};
// @public (undocumented)
export const TextLabel: React_2.NamedExoticComponent<TextLabelProps>;
export const TextLabel: React_3.NamedExoticComponent<TextLabelProps>;
// @public (undocumented)
export interface TextLabelProps {
@ -1371,9 +1393,9 @@ export interface TextLabelProps {
// (undocumented)
lineHeight: number;
// (undocumented)
onKeyDown?: (e: React_2.KeyboardEvent<HTMLTextAreaElement>) => void;
onKeyDown?: (e: React_3.KeyboardEvent<HTMLTextAreaElement>) => void;
// (undocumented)
style?: React_2.CSSProperties;
style?: React_3.CSSProperties;
// (undocumented)
text: string;
// (undocumented)
@ -1507,14 +1529,19 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
}
// @public (undocumented)
export function TextStylePickerSet({ theme, styles, }: {
styles: ReadonlySharedStyleMap;
theme: TLDefaultColorTheme;
}): JSX_2.Element | null;
export function TextStylePickerSet({ theme, styles }: ThemeStylePickerSetProps): JSX_2.Element | null;
// @public (undocumented)
export function TextToolbarItem(): JSX_2.Element;
// @public (undocumented)
export interface ThemeStylePickerSetProps {
// (undocumented)
styles: ReadonlySharedStyleMap;
// (undocumented)
theme: TLDefaultColorTheme;
}
// @public (undocumented)
export interface TLArcInfo {
// (undocumented)
@ -1613,28 +1640,16 @@ export type TldrawFileParseError = {
export function TldrawHandles({ children }: TLHandlesProps): JSX_2.Element | null;
// @public
export const TldrawImage: NamedExoticComponent< {
background?: boolean | undefined;
bindingUtils?: readonly TLAnyBindingUtilConstructor[] | undefined;
bounds?: Box | undefined;
darkMode?: boolean | undefined;
format?: "png" | "svg" | undefined;
padding?: number | undefined;
pageId?: TLPageId | undefined;
preserveAspectRatio?: string | undefined;
scale?: number | undefined;
shapeUtils?: readonly TLAnyShapeUtilConstructor[] | undefined;
snapshot: TLEditorSnapshot | TLStoreSnapshot;
}>;
export const TldrawImage: NamedExoticComponent<TldrawImageProps>;
// @public
export type TldrawImageProps = Expand<{
// @public (undocumented)
export interface TldrawImageProps extends Partial<TLSvgOptions> {
bindingUtils?: readonly TLAnyBindingUtilConstructor[];
shapeUtils?: readonly TLAnyShapeUtilConstructor[];
format?: 'png' | 'svg';
pageId?: TLPageId;
shapeUtils?: readonly TLAnyShapeUtilConstructor[];
snapshot: TLEditorSnapshot | TLStoreSnapshot;
} & Partial<TLSvgOptions>>;
}
// @public (undocumented)
export type TldrawProps = Expand<(Omit<TldrawUiProps, 'components'> & Omit<TldrawEditorBaseProps, 'components'> & {
@ -1660,96 +1675,10 @@ export const TldrawSelectionBackground: ({ bounds, rotation }: TLSelectionBackgr
export const TldrawSelectionForeground: MemoExoticComponent<({ bounds, rotation, }: TLSelectionForegroundProps) => JSX_2.Element | null>;
// @public (undocumented)
export const TldrawUi: React_2.NamedExoticComponent<{
assetUrls?: (RecursivePartial<TLUiAssetUrls> & RecursivePartial<TLUiAssetUrls>) | undefined;
children?: ReactNode;
components?: TLUiComponents | undefined;
forceMobile?: boolean | undefined;
hideUi?: boolean | undefined;
onUiEvent?: TLUiEventHandler | undefined;
overrides?: Partial<{
actions: (editor: Editor, actions: TLUiActionsContextType, helpers: {
addDialog: (dialog: Omit<TLUiDialog, "id"> & {
id?: string | undefined;
}) => string;
addToast: (toast: Omit<TLUiToast, "id"> & {
id?: string | undefined;
}) => string;
clearDialogs: () => void;
clearToasts: () => void;
isMobile: boolean;
msg: (id?: string | undefined) => string;
removeDialog: (id: string) => string;
removeToast: (id: string) => string;
updateDialog: (id: string, newDialogData: Partial<TLUiDialog>) => string;
}) => TLUiActionsContextType;
tools: (editor: Editor, tools: TLUiToolsContextType, helpers: {
insertMedia: () => void;
} & {
addDialog: (dialog: Omit<TLUiDialog, "id"> & {
id?: string | undefined;
}) => string;
addToast: (toast: Omit<TLUiToast, "id"> & {
id?: string | undefined;
}) => string;
clearDialogs: () => void;
clearToasts: () => void;
isMobile: boolean;
msg: (id?: string | undefined) => string;
removeDialog: (id: string) => string;
removeToast: (id: string) => string;
updateDialog: (id: string, newDialogData: Partial<TLUiDialog>) => string;
}) => TLUiToolsContextType;
translations: Record<string, Record<string, string>> | undefined;
}> | Partial<{
actions: (editor: Editor, actions: TLUiActionsContextType, helpers: {
addDialog: (dialog: Omit<TLUiDialog, "id"> & {
id?: string | undefined;
}) => string;
addToast: (toast: Omit<TLUiToast, "id"> & {
id?: string | undefined;
}) => string;
clearDialogs: () => void;
clearToasts: () => void;
isMobile: boolean;
msg: (id?: string | undefined) => string;
removeDialog: (id: string) => string;
removeToast: (id: string) => string;
updateDialog: (id: string, newDialogData: Partial<TLUiDialog>) => string;
}) => TLUiActionsContextType;
tools: (editor: Editor, tools: TLUiToolsContextType, helpers: {
insertMedia: () => void;
} & {
addDialog: (dialog: Omit<TLUiDialog, "id"> & {
id?: string | undefined;
}) => string;
addToast: (toast: Omit<TLUiToast, "id"> & {
id?: string | undefined;
}) => string;
clearDialogs: () => void;
clearToasts: () => void;
isMobile: boolean;
msg: (id?: string | undefined) => string;
removeDialog: (id: string) => string;
removeToast: (id: string) => string;
updateDialog: (id: string, newDialogData: Partial<TLUiDialog>) => string;
}) => TLUiToolsContextType;
translations: Record<string, Record<string, string>> | undefined;
}>[] | undefined;
renderDebugMenuItems?: (() => React_2.ReactNode) | undefined;
}>;
// @public
export interface TldrawUiBaseProps {
assetUrls?: TLUiAssetUrlOverrides;
children?: ReactNode;
components?: TLUiComponents;
hideUi?: boolean;
renderDebugMenuItems?: () => React_2.ReactNode;
}
export const TldrawUi: React_3.NamedExoticComponent<TldrawUiProps>;
// @public (undocumented)
export const TldrawUiButton: React_3.ForwardRefExoticComponent<TLUiButtonProps & React_3.RefAttributes<HTMLButtonElement>>;
export const TldrawUiButton: React_2.ForwardRefExoticComponent<TLUiButtonProps & React_2.RefAttributes<HTMLButtonElement>>;
// @public (undocumented)
export function TldrawUiButtonCheck({ checked }: TLUiButtonCheckProps): JSX_2.Element;
@ -1769,7 +1698,7 @@ export function TldrawUiComponentsProvider({ overrides, children, }: TLUiCompone
// @public (undocumented)
export function TldrawUiContextProvider({ overrides, components, assetUrls, onUiEvent, forceMobile, children, }: TldrawUiContextProviderProps): JSX_2.Element;
// @public
// @public (undocumented)
export interface TldrawUiContextProviderProps {
assetUrls?: RecursivePartial<TLUiAssetUrls>;
children?: ReactNode;
@ -1825,7 +1754,7 @@ export function TldrawUiDropdownMenuTrigger({ children, ...rest }: TLUiDropdownM
export const TldrawUiIcon: NamedExoticComponent<TLUiIconProps>;
// @public (undocumented)
export const TldrawUiInput: React_3.ForwardRefExoticComponent<TLUiInputProps & React_3.RefAttributes<HTMLInputElement>>;
export const TldrawUiInput: React_2.ForwardRefExoticComponent<TLUiInputProps & React_2.RefAttributes<HTMLInputElement>>;
// @public (undocumented)
export function TldrawUiKbd({ children, visibleOnMobileLayout }: TLUiKbdProps): JSX_2.Element | null;
@ -1857,8 +1786,14 @@ export function TldrawUiPopoverContent({ side, children, align, sideOffset, alig
// @public (undocumented)
export function TldrawUiPopoverTrigger({ children }: TLUiPopoverTriggerProps): JSX_2.Element;
// @public
export type TldrawUiProps = Expand<TldrawUiBaseProps & TldrawUiContextProviderProps>;
// @public (undocumented)
export interface TldrawUiProps extends TldrawUiContextProviderProps {
assetUrls?: TLUiAssetUrlOverrides;
children?: ReactNode;
components?: TLUiComponents;
hideUi?: boolean;
renderDebugMenuItems?: () => React_3.ReactNode;
}
// @internal (undocumented)
export const TldrawUiSlider: NamedExoticComponent<TLUiSliderProps>;
@ -1998,7 +1933,7 @@ export interface TLUiButtonPickerProps<T extends string> {
}
// @public (undocumented)
export interface TLUiButtonProps extends React_3.HTMLAttributes<HTMLButtonElement> {
export interface TLUiButtonProps extends React_2.HTMLAttributes<HTMLButtonElement> {
// (undocumented)
disabled?: boolean;
// (undocumented)
@ -2432,7 +2367,7 @@ export interface TLUiInputProps {
// (undocumented)
autoSelect?: boolean;
// (undocumented)
children?: React_3.ReactNode;
children?: React_2.ReactNode;
// (undocumented)
className?: string;
// (undocumented)
@ -2580,7 +2515,7 @@ export interface TLUiPopoverContentProps {
// (undocumented)
alignOffset?: number;
// (undocumented)
children: React_2.ReactNode;
children: React_3.ReactNode;
// (undocumented)
side: 'bottom' | 'left' | 'right' | 'top';
// (undocumented)
@ -2590,7 +2525,7 @@ export interface TLUiPopoverContentProps {
// @public (undocumented)
export interface TLUiPopoverProps {
// (undocumented)
children: React_2.ReactNode;
children: React_3.ReactNode;
// (undocumented)
id: string;
// (undocumented)
@ -2602,7 +2537,7 @@ export interface TLUiPopoverProps {
// @public (undocumented)
export interface TLUiPopoverTriggerProps {
// (undocumented)
children?: React_2.ReactNode;
children?: React_3.ReactNode;
}
// @public (undocumented)
@ -2713,7 +2648,7 @@ export type TLUiToolsContextType = Record<string, TLUiToolItem>;
// @public (undocumented)
export interface TLUiToolsProviderProps {
// (undocumented)
children: React_3.ReactNode;
children: React_2.ReactNode;
// (undocumented)
overrides?: (editor: Editor, tools: TLUiToolsContextType, helpers: {
insertMedia: () => void;
@ -2741,7 +2676,7 @@ export type TLUiTranslationKey = 'action.align-bottom' | 'action.align-center-ho
// @public (undocumented)
export interface TLUiTranslationProviderProps {
// (undocumented)
children: React_3.ReactNode;
children: React_2.ReactNode;
overrides?: Record<string, Record<string, string>>;
}
@ -3224,9 +3159,13 @@ export function ToggleTransparentBgMenuItem(): JSX_2.Element;
export function ToggleWrapModeItem(): JSX_2.Element;
// @public (undocumented)
export function ToolbarItem({ tool }: {
export function ToolbarItem({ tool }: ToolbarItemProps): JSX_2.Element;
// @public (undocumented)
export interface ToolbarItemProps {
// (undocumented)
tool: string;
}): JSX_2.Element;
}
// @public (undocumented)
export function TrapezoidToolbarItem(): JSX_2.Element;
@ -3317,15 +3256,15 @@ export function useDialogs(): TLUiDialogsContextType;
// @public (undocumented)
export function useEditableText(id: TLShapeId, type: string, text: string): {
handleBlur: () => void;
handleChange: (e: React_2.ChangeEvent<HTMLTextAreaElement>) => void;
handleChange: (e: React_3.ChangeEvent<HTMLTextAreaElement>) => void;
handleDoubleClick: (e: any) => any;
handleFocus: () => void;
handleInputPointerDown: (e: React_2.PointerEvent) => void;
handleKeyDown: (e: React_2.KeyboardEvent<HTMLTextAreaElement>) => void;
handleInputPointerDown: (e: React_3.PointerEvent) => void;
handleKeyDown: (e: React_3.KeyboardEvent<HTMLTextAreaElement>) => void;
isEditing: boolean;
isEditingAnything: boolean;
isEmpty: boolean;
rInput: React_2.RefObject<HTMLTextAreaElement>;
rInput: React_3.RefObject<HTMLTextAreaElement>;
};
// @public (undocumented)

View file

@ -56,7 +56,7 @@ export { LaserTool } from './lib/tools/LaserTool/LaserTool'
export { SelectTool } from './lib/tools/SelectTool/SelectTool'
export { getOccludedChildren, kickoutOccludedShapes } from './lib/tools/SelectTool/selectHelpers'
export { ZoomTool } from './lib/tools/ZoomTool/ZoomTool'
export { TldrawUi, type TldrawUiBaseProps, type TldrawUiProps } from './lib/ui/TldrawUi'
export { TldrawUi, type TldrawUiProps } from './lib/ui/TldrawUi'
export {
setDefaultUiAssetUrls,
type TLUiAssetUrlOverrides,
@ -91,6 +91,7 @@ export {
DefaultDebugMenuContent,
ExampleDialog,
FeatureFlags,
type ExampleDialogProps,
} from './lib/ui/components/DebugMenu/DefaultDebugMenuContent'
export {
DefaultHelpMenu,
@ -129,7 +130,7 @@ export { DefaultMinimap } from './lib/ui/components/Minimap/DefaultMinimap'
export { DefaultNavigationPanel } from './lib/ui/components/NavigationPanel/DefaultNavigationPanel'
export { OfflineIndicator } from './lib/ui/components/OfflineIndicator/OfflineIndicator'
export { DefaultPageMenu } from './lib/ui/components/PageMenu/DefaultPageMenu'
export { PageItemInput } from './lib/ui/components/PageMenu/PageItemInput'
export { PageItemInput, type PageItemInputProps } from './lib/ui/components/PageMenu/PageItemInput'
export {
PageItemSubmenu,
type PageItemSubmenuProps,
@ -152,9 +153,14 @@ export {
OpacitySlider,
SplineStylePickerSet,
TextStylePickerSet,
type StylePickerSetProps,
type TLUiStylePanelContentProps,
type ThemeStylePickerSetProps,
} from './lib/ui/components/StylePanel/DefaultStylePanelContent'
export { DefaultToolbar } from './lib/ui/components/Toolbar/DefaultToolbar'
export {
DefaultToolbar,
type DefaultToolbarProps,
} from './lib/ui/components/Toolbar/DefaultToolbar'
export {
ArrowDownToolbarItem,
ArrowLeftToolbarItem,
@ -187,6 +193,7 @@ export {
TriangleToolbarItem,
XBoxToolbarItem,
useIsToolSelected,
type ToolbarItemProps,
} from './lib/ui/components/Toolbar/DefaultToolbarContent'
export {
DefaultZoomMenu,
@ -327,7 +334,11 @@ export {
type TLUiActionsContextType,
} from './lib/ui/context/actions'
export { AssetUrlsProvider, useAssetUrls } from './lib/ui/context/asset-urls'
export { BreakPointProvider, useBreakpoint } from './lib/ui/context/breakpoints'
export {
BreakPointProvider,
useBreakpoint,
type BreakPointProviderProps,
} from './lib/ui/context/breakpoints'
export {
TldrawUiComponentsProvider,
useTldrawUiComponents,

View file

@ -71,7 +71,7 @@ export type TldrawProps = Expand<
)
>
/** @public */
/** @public @react */
export function Tldraw(props: TldrawProps) {
const {
children,

View file

@ -1,7 +1,6 @@
import {
Editor,
ErrorScreen,
Expand,
LoadingScreen,
TLAnyBindingUtilConstructor,
TLAnyShapeUtilConstructor,
@ -19,38 +18,32 @@ import { usePreloadAssets } from './ui/hooks/usePreloadAssets'
import { getSvgAsImage } from './utils/export/export'
import { useDefaultEditorAssetsWithOverrides } from './utils/static-assets/assetUrls'
/**
* Props for the {@link tldraw#TldrawImage} component.
*
* @public
**/
export type TldrawImageProps = Expand<
{
/**
* The snapshot to display.
*/
snapshot: TLEditorSnapshot | TLStoreSnapshot
/** @public */
export interface TldrawImageProps extends Partial<TLSvgOptions> {
/**
* The snapshot to display.
*/
snapshot: TLEditorSnapshot | TLStoreSnapshot
/**
* The image format to use. Defaults to 'svg'.
*/
format?: 'svg' | 'png'
/**
* The image format to use. Defaults to 'svg'.
*/
format?: 'svg' | 'png'
/**
* The page to display. Defaults to the first page.
*/
pageId?: TLPageId
/**
* The page to display. Defaults to the first page.
*/
pageId?: TLPageId
/**
* Additional shape utils to use.
*/
shapeUtils?: readonly TLAnyShapeUtilConstructor[]
/**
* Additional binding utils to use.
*/
bindingUtils?: readonly TLAnyBindingUtilConstructor[]
} & Partial<TLSvgOptions>
>
/**
* Additional shape utils to use.
*/
shapeUtils?: readonly TLAnyShapeUtilConstructor[]
/**
* Additional binding utils to use.
*/
bindingUtils?: readonly TLAnyBindingUtilConstructor[]
}
/**
* A renderered SVG image of a Tldraw snapshot.
@ -68,6 +61,7 @@ export type TldrawImageProps = Expand<
* ```
*
* @public
* @react
*/
export const TldrawImage = memo(function TldrawImage(props: TldrawImageProps) {
const [url, setUrl] = useState<string | null>(null)

View file

@ -1,6 +1,6 @@
import { TLHandlesProps, useEditor, useValue } from '@tldraw/editor'
/** @public */
/** @public @react */
export function TldrawHandles({ children }: TLHandlesProps) {
const editor = useEditor()

View file

@ -2,7 +2,7 @@ import { EASINGS, TLScribbleProps, getSvgPathFromPoints } from '@tldraw/editor'
import classNames from 'classnames'
import { getStroke } from '../shapes/shared/freehand/getStroke'
/** @public */
/** @public @react */
export function TldrawScribble({ scribble, zoom, color, opacity, className }: TLScribbleProps) {
if (!scribble.points.length) return null

View file

@ -5,7 +5,7 @@ import {
useValue,
} from '@tldraw/editor'
/** @public */
/** @public @react */
export const TldrawSelectionBackground = ({ bounds, rotation }: TLSelectionBackgroundProps) => {
const editor = useEditor()

View file

@ -35,7 +35,7 @@ export interface TextLabelProps {
textHeight?: number
}
/** @public */
/** @public @react */
export const TextLabel = React.memo(function TextLabel({
id,
type,

View file

@ -1,4 +1,4 @@
import { Expand, useEditor, useEditorComponents, useValue } from '@tldraw/editor'
import { useEditor, useEditorComponents, useValue } from '@tldraw/editor'
import classNames from 'classnames'
import React, { ReactNode } from 'react'
import { TLUiAssetUrlOverrides } from './assetUrls'
@ -21,12 +21,8 @@ import { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts'
import { useReadonly } from './hooks/useReadonly'
import { useTranslation } from './hooks/useTranslation/useTranslation'
/**
* Base props for the {@link tldraw#Tldraw} and {@link TldrawUi} components.
*
* @public
*/
export interface TldrawUiBaseProps {
/** @public */
export interface TldrawUiProps extends TldrawUiContextProviderProps {
/**
* The component's children.
*/
@ -51,15 +47,9 @@ export interface TldrawUiBaseProps {
assetUrls?: TLUiAssetUrlOverrides
}
/**
* Props for the {@link tldraw#Tldraw} and {@link TldrawUi} components.
*
* @public
*/
export type TldrawUiProps = Expand<TldrawUiBaseProps & TldrawUiContextProviderProps>
/**
* @public
* @react
*/
export const TldrawUi = React.memo(function TldrawUi({
renderDebugMenuItems,

View file

@ -19,7 +19,7 @@ export interface TLUiActionsMenuProps {
children?: ReactNode
}
/** @public */
/** @public @react */
export const DefaultActionsMenu = memo(function DefaultActionsMenu({
children,
}: TLUiActionsMenuProps) {

View file

@ -12,7 +12,7 @@ import {
} from '../../hooks/menu-hooks'
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
/** @public */
/** @public @react */
export function DefaultActionsMenuContent() {
return (
<>
@ -28,7 +28,7 @@ export function DefaultActionsMenuContent() {
)
}
/** @public */
/** @public @react */
export function AlignMenuItems() {
const actions = useActions()
const twoSelected = useUnlockedSelectedShapesCount(2)
@ -49,7 +49,7 @@ export function AlignMenuItems() {
)
}
/** @public */
/** @public @react */
export function DistributeMenuItems() {
const actions = useActions()
const threeSelected = useUnlockedSelectedShapesCount(3)
@ -64,7 +64,7 @@ export function DistributeMenuItems() {
)
}
/** @public */
/** @public @react */
export function StackMenuItems() {
const actions = useActions()
const threeStackableItems = useThreeStackableItems()
@ -79,7 +79,7 @@ export function StackMenuItems() {
)
}
/** @public */
/** @public @react */
export function ReorderMenuItems() {
const actions = useActions()
const oneSelected = useUnlockedSelectedShapesCount(1)
@ -96,13 +96,13 @@ export function ReorderMenuItems() {
)
}
/** @public */
/** @public @react */
export function ZoomOrRotateMenuItem() {
const breakpoint = useBreakpoint()
return breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM ? <ZoomTo100MenuItem /> : <RotateCCWMenuItem />
}
/** @public */
/** @public @react */
export function ZoomTo100MenuItem() {
const actions = useActions()
@ -111,7 +111,7 @@ export function ZoomTo100MenuItem() {
return <TldrawUiMenuItem {...actions['zoom-to-100']} disabled={isZoomedTo100} />
}
/** @public */
/** @public @react */
export function RotateCCWMenuItem() {
const actions = useActions()
@ -121,7 +121,7 @@ export function RotateCCWMenuItem() {
return <TldrawUiMenuItem {...actions['rotate-ccw']} disabled={!enabled} />
}
/** @public */
/** @public @react */
export function RotateCWMenuItem() {
const actions = useActions()
@ -131,7 +131,7 @@ export function RotateCWMenuItem() {
return <TldrawUiMenuItem {...actions['rotate-cw']} disabled={!enabled} />
}
/** @public */
/** @public @react */
export function EditLinkMenuItem() {
const actions = useActions()
@ -141,14 +141,14 @@ export function EditLinkMenuItem() {
return <TldrawUiMenuItem {...actions['edit-link']} disabled={!enabled} />
}
/** @public */
/** @public @react */
export function GroupOrUngroupMenuItem() {
const allowGroup = useAllowGroup()
const allowUngroup = useAllowUngroup()
return allowGroup ? <GroupMenuItem /> : allowUngroup ? <UngroupMenuItem /> : <GroupMenuItem />
}
/** @public */
/** @public @react */
export function GroupMenuItem() {
const actions = useActions()
@ -158,7 +158,7 @@ export function GroupMenuItem() {
return <TldrawUiMenuItem {...actions['group']} disabled={!enabled} />
}
/** @public */
/** @public @react */
export function UngroupMenuItem() {
const actions = useActions()

View file

@ -10,7 +10,7 @@ export interface TLUiContextMenuProps {
children?: ReactNode
}
/** @public */
/** @public @react */
export const DefaultContextMenu = memo(function DefaultContextMenu({
children,
}: TLUiContextMenuProps) {

View file

@ -18,7 +18,7 @@ import {
} from '../menu-items'
import { TldrawUiMenuGroup } from '../primitives/menus/TldrawUiMenuGroup'
/** @public */
/** @public @react */
export function DefaultContextMenuContent() {
const editor = useEditor()

View file

@ -14,7 +14,7 @@ export interface TLUiDebugMenuProps {
children?: ReactNode
}
/** @public */
/** @public @react */
export function DefaultDebugMenu({ children }: TLUiDebugMenuProps) {
const content = children ?? <DefaultDebugMenuContent />

View file

@ -29,7 +29,7 @@ import { TldrawUiMenuGroup } from '../primitives/menus/TldrawUiMenuGroup'
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
import { TldrawUiMenuSubmenu } from '../primitives/menus/TldrawUiMenuSubmenu'
/** @public */
/** @public @react */
export function DefaultDebugMenuContent() {
const editor = useEditor()
const { addToast } = useToasts()
@ -177,7 +177,7 @@ export function DefaultDebugMenuContent() {
</>
)
}
/** @public */
/** @public @react */
export function DebugFlags() {
const items = Object.values(debugFlags)
if (!items.length) return null
@ -191,7 +191,7 @@ export function DebugFlags() {
</TldrawUiMenuSubmenu>
)
}
/** @public */
/** @public @react */
export function FeatureFlags() {
const items = Object.values(featureFlags)
if (!items.length) return null
@ -205,7 +205,19 @@ export function FeatureFlags() {
</TldrawUiMenuSubmenu>
)
}
/** @public */
export interface ExampleDialogProps {
title?: string
body?: string
cancel?: string
confirm?: string
displayDontShowAgain?: boolean
onCancel: () => void
onContinue: () => void
}
/** @public @react */
export function ExampleDialog({
title = 'title',
body = 'hello hello hello',
@ -214,15 +226,7 @@ export function ExampleDialog({
displayDontShowAgain = false,
onCancel,
onContinue,
}: {
title?: string
body?: string
cancel?: string
confirm?: string
displayDontShowAgain?: boolean
onCancel: () => void
onContinue: () => void
}) {
}: ExampleDialogProps) {
const [dontShowAgain, setDontShowAgain] = React.useState(false)
return (

View file

@ -17,7 +17,7 @@ export interface TLUiHelpMenuProps {
children?: ReactNode
}
/** @public */
/** @public @react */
export const DefaultHelpMenu = memo(function DefaultHelpMenu({ children }: TLUiHelpMenuProps) {
const msg = useTranslation()
const breakpoint = useBreakpoint()

View file

@ -3,7 +3,7 @@ import { useDialogs } from '../../context/dialogs'
import { LanguageMenu } from '../LanguageMenu'
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
/** @public */
/** @public @react */
export function DefaultHelpMenuContent() {
return (
<>
@ -12,7 +12,7 @@ export function DefaultHelpMenuContent() {
</>
)
}
/** @public */
/** @public @react */
export function KeyboardShortcutsMenuItem() {
const { KeyboardShortcutsDialog } = useTldrawUiComponents()
const { addDialog } = useDialogs()

View file

@ -7,7 +7,7 @@ export interface TLUiHelperButtonsProps {
children?: ReactNode
}
/** @public */
/** @public @react */
export function DefaultHelperButtons({ children }: TLUiHelperButtonsProps) {
const content = children ?? <DefaultHelperButtonsContent />
return (

View file

@ -2,7 +2,7 @@ import { BackToContent } from './BackToContent'
import { ExitPenMode } from './ExitPenMode'
import { StopFollowing } from './StopFollowing'
/** @public */
/** @public @react */
export function DefaultHelperButtonsContent() {
return (
<>

View file

@ -18,7 +18,7 @@ export type TLUiKeyboardShortcutsDialogProps = TLUiDialogProps & {
children?: ReactNode
}
/** @public */
/** @public @react */
export const DefaultKeyboardShortcutsDialog = memo(function DefaultKeyboardShortcutsDialog({
children,
}: TLUiKeyboardShortcutsDialogProps) {

View file

@ -3,7 +3,7 @@ import { useTools } from '../../hooks/useTools'
import { TldrawUiMenuGroup } from '../primitives/menus/TldrawUiMenuGroup'
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
/** @public */
/** @public @react */
export function DefaultKeyboardShortcutsDialogContent() {
const actions = useActions()
const tools = useTools()

View file

@ -4,7 +4,7 @@ import { TldrawUiMenuCheckboxItem } from './primitives/menus/TldrawUiMenuCheckbo
import { TldrawUiMenuGroup } from './primitives/menus/TldrawUiMenuGroup'
import { TldrawUiMenuSubmenu } from './primitives/menus/TldrawUiMenuSubmenu'
/** @public */
/** @public @react */
export function LanguageMenu() {
const editor = useEditor()
const trackEvent = useUiEvents()

View file

@ -13,7 +13,7 @@ export interface TLUiMainMenuProps {
children?: ReactNode
}
/** @public */
/** @public @react */
export const DefaultMainMenu = memo(function DefaultMainMenu({ children }: TLUiMainMenuProps) {
const container = useContainer()
const [isOpen, onOpenChange] = useMenuIsOpen('main menu')

View file

@ -34,7 +34,7 @@ import { TldrawUiMenuGroup } from '../primitives/menus/TldrawUiMenuGroup'
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
import { TldrawUiMenuSubmenu } from '../primitives/menus/TldrawUiMenuSubmenu'
/** @public */
/** @public @react */
export function DefaultMainMenuContent() {
return (
<>
@ -47,7 +47,7 @@ export function DefaultMainMenuContent() {
)
}
/** @public */
/** @public @react */
export function ExportFileContentSubMenu() {
const actions = useActions()
@ -65,7 +65,7 @@ export function ExportFileContentSubMenu() {
)
}
/** @public */
/** @public @react */
export function EditSubmenu() {
const editor = useEditor()
@ -89,7 +89,7 @@ export function EditSubmenu() {
)
}
/** @public */
/** @public @react */
export function MiscMenuGroup() {
return (
<TldrawUiMenuGroup id="misc">
@ -105,7 +105,7 @@ export function MiscMenuGroup() {
)
}
/** @public */
/** @public @react */
export function LockGroup() {
return (
<TldrawUiMenuGroup id="lock">
@ -115,7 +115,7 @@ export function LockGroup() {
)
}
/** @public */
/** @public @react */
export function UndoRedoGroup() {
const actions = useActions()
const canUndo = useCanUndo()
@ -128,7 +128,7 @@ export function UndoRedoGroup() {
)
}
/** @public */
/** @public @react */
export function ViewSubmenu() {
const actions = useActions()
return (
@ -144,7 +144,7 @@ export function ViewSubmenu() {
)
}
/** @public */
/** @public @react */
export function ExtrasGroup() {
const actions = useActions()
return (
@ -157,7 +157,7 @@ export function ExtrasGroup() {
/* ------------------- Preferences ------------------ */
/** @public */
/** @public @react */
export function PreferencesGroup() {
return (
<TldrawUiMenuGroup id="preferences">

View file

@ -2,7 +2,7 @@ import { memo } from 'react'
import { useBreakpoint } from '../context/breakpoints'
import { useTldrawUiComponents } from '../context/components'
/** @public */
/** @public @react */
export const DefaultMenuPanel = memo(function MenuPanel() {
const breakpoint = useBreakpoint()

View file

@ -13,7 +13,7 @@ import {
import * as React from 'react'
import { MinimapManager } from './MinimapManager'
/** @public */
/** @public @react */
export function DefaultMinimap() {
const editor = useEditor()
const container = useContainer()

View file

@ -9,7 +9,7 @@ import { kbdStr } from '../../kbd-utils'
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
/** @public */
/** @public @react */
export const DefaultNavigationPanel = memo(function DefaultNavigationPanel() {
const actions = useActions()
const msg = useTranslation()

View file

@ -3,7 +3,7 @@ import { useRef } from 'react'
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
import { TldrawUiIcon } from '../primitives/TldrawUiIcon'
/** @public */
/** @public @react */
export function OfflineIndicator() {
const msg = useTranslation()
const rContainer = useRef<HTMLDivElement>(null)

View file

@ -25,7 +25,7 @@ import { PageItemInput } from './PageItemInput'
import { PageItemSubmenu } from './PageItemSubmenu'
import { onMovePage } from './edit-pages-shared'
/** @public */
/** @public @react */
export const DefaultPageMenu = memo(function DefaultPageMenu() {
const editor = useEditor()
const msg = useTranslation()

View file

@ -1,16 +1,20 @@
import { TLPageId, useEditor } from '@tldraw/editor'
import { useCallback, useRef } from 'react'
import { TldrawUiInput } from '../primitives/TldrawUiInput'
/** @public */
export interface PageItemInputProps {
name: string
id: TLPageId
isCurrentPage: boolean
}
/** @public @react */
export const PageItemInput = function PageItemInput({
name,
id,
isCurrentPage,
}: {
name: string
id: TLPageId
isCurrentPage: boolean
}) {
}: PageItemInputProps) {
const editor = useEditor()
const rInput = useRef<HTMLInputElement | null>(null)

View file

@ -7,7 +7,7 @@ export interface TLUiQuickActionsProps {
children?: ReactNode
}
/** @public */
/** @public @react */
export const DefaultQuickActions = memo(function DefaultQuickActions({
children,
}: TLUiQuickActionsProps) {

View file

@ -9,7 +9,7 @@ import {
import { useReadonly } from '../../hooks/useReadonly'
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
/** @public */
/** @public @react */
export function DefaultQuickActionsContent() {
const actions = useActions()

View file

@ -10,7 +10,7 @@ export interface TLUiStylePanelProps {
children?: ReactNode
}
/** @public */
/** @public @react */
export const DefaultStylePanel = memo(function DefaultStylePanel({
isMobile,
children,

View file

@ -39,7 +39,7 @@ export interface TLUiStylePanelContentProps {
styles: ReturnType<typeof useRelevantStyles>
}
/** @public */
/** @public @react */
export function DefaultStylePanelContent({ styles }: TLUiStylePanelContentProps) {
const isDarkMode = useIsDarkMode()
@ -95,13 +95,18 @@ function useStyleChangeCallback() {
}
/** @public */
export function CommonStylePickerSet({
styles,
theme,
}: {
export interface ThemeStylePickerSetProps {
styles: ReadonlySharedStyleMap
theme: TLDefaultColorTheme
}) {
}
/** @public */
export interface StylePickerSetProps {
styles: ReadonlySharedStyleMap
}
/** @public @react */
export function CommonStylePickerSet({ styles, theme }: ThemeStylePickerSetProps) {
const msg = useTranslation()
const editor = useEditor()
@ -182,14 +187,8 @@ export function CommonStylePickerSet({
)
}
/** @public */
export function TextStylePickerSet({
theme,
styles,
}: {
theme: TLDefaultColorTheme
styles: ReadonlySharedStyleMap
}) {
/** @public @react */
export function TextStylePickerSet({ theme, styles }: ThemeStylePickerSetProps) {
const msg = useTranslation()
const handleValueChange = useStyleChangeCallback()
@ -277,8 +276,8 @@ export function TextStylePickerSet({
</div>
)
}
/** @public */
export function GeoStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap }) {
/** @public @react */
export function GeoStylePickerSet({ styles }: StylePickerSetProps) {
const handleValueChange = useStyleChangeCallback()
const geo = styles.get(GeoShapeGeoStyle)
@ -299,8 +298,8 @@ export function GeoStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap }
/>
)
}
/** @public */
export function SplineStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap }) {
/** @public @react */
export function SplineStylePickerSet({ styles }: StylePickerSetProps) {
const handleValueChange = useStyleChangeCallback()
const spline = styles.get(LineShapeSplineStyle)
@ -322,8 +321,8 @@ export function SplineStylePickerSet({ styles }: { styles: ReadonlySharedStyleMa
)
}
/** @public */
export function ArrowheadStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap }) {
/** @public @react */
export function ArrowheadStylePickerSet({ styles }: StylePickerSetProps) {
const handleValueChange = useStyleChangeCallback()
const arrowheadEnd = styles.get(ArrowShapeArrowheadEndStyle)
@ -351,7 +350,7 @@ export function ArrowheadStylePickerSet({ styles }: { styles: ReadonlySharedStyl
}
const tldrawSupportedOpacities = [0.1, 0.25, 0.5, 0.75, 1] as const
/** @public */
/** @public @react */
export function OpacitySlider() {
const editor = useEditor()
const opacity = useValue('opacity', () => editor.getSharedOpacity(), [editor])

View file

@ -20,8 +20,9 @@ export interface DefaultToolbarProps {
* recently active item from the overflow being shown in the main toolbar.
*
* @public
* @react
*/
export const DefaultToolbar = memo(function DefaultToolbar({ children }: { children?: ReactNode }) {
export const DefaultToolbar = memo(function DefaultToolbar({ children }: DefaultToolbarProps) {
const editor = useEditor()
const breakpoint = useBreakpoint()
const isReadonlyMode = useReadonly()

View file

@ -2,7 +2,7 @@ import { GeoShapeGeoStyle, useEditor, useValue } from '@tldraw/editor'
import { TLUiToolItem, useTools } from '../../hooks/useTools'
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
/** @public */
/** @public @react */
export function DefaultToolbarContent() {
return (
<>
@ -54,159 +54,164 @@ export function useIsToolSelected(tool: TLUiToolItem) {
}
/** @public */
export function ToolbarItem({ tool }: { tool: string }) {
export interface ToolbarItemProps {
tool: string
}
/** @public @react */
export function ToolbarItem({ tool }: ToolbarItemProps) {
const tools = useTools()
const isSelected = useIsToolSelected(tools[tool])
return <TldrawUiMenuItem {...tools[tool]} isSelected={isSelected} />
}
/** @public */
/** @public @react */
export function SelectToolbarItem() {
return <ToolbarItem tool="select" />
}
/** @public */
/** @public @react */
export function HandToolbarItem() {
return <ToolbarItem tool="hand" />
}
/** @public */
/** @public @react */
export function DrawToolbarItem() {
return <ToolbarItem tool="draw" />
}
/** @public */
/** @public @react */
export function EraserToolbarItem() {
return <ToolbarItem tool="eraser" />
}
/** @public */
/** @public @react */
export function ArrowToolbarItem() {
return <ToolbarItem tool="arrow" />
}
/** @public */
/** @public @react */
export function TextToolbarItem() {
return <ToolbarItem tool="text" />
}
/** @public */
/** @public @react */
export function NoteToolbarItem() {
return <ToolbarItem tool="note" />
}
/** @public */
/** @public @react */
export function AssetToolbarItem() {
const tools = useTools()
return <TldrawUiMenuItem {...tools['asset']} />
}
/** @public */
/** @public @react */
export function RectangleToolbarItem() {
return <ToolbarItem tool="rectangle" />
}
/** @public */
/** @public @react */
export function EllipseToolbarItem() {
return <ToolbarItem tool="ellipse" />
}
/** @public */
/** @public @react */
export function DiamondToolbarItem() {
return <ToolbarItem tool="diamond" />
}
/** @public */
/** @public @react */
export function TriangleToolbarItem() {
return <ToolbarItem tool="triangle" />
}
/** @public */
/** @public @react */
export function TrapezoidToolbarItem() {
return <ToolbarItem tool="trapezoid" />
}
/** @public */
/** @public @react */
export function RhombusToolbarItem() {
return <ToolbarItem tool="rhombus" />
}
/** @public */
/** @public @react */
export function PentagonToolbarItem() {
return <ToolbarItem tool="pentagon" />
}
/** @public */
/** @public @react */
export function HeartToolbarItem() {
return <ToolbarItem tool="heart" />
}
/** @public */
/** @public @react */
export function HexagonToolbarItem() {
return <ToolbarItem tool="hexagon" />
}
/** @public */
/** @public @react */
export function CloudToolbarItem() {
return <ToolbarItem tool="cloud" />
}
/** @public */
/** @public @react */
export function StarToolbarItem() {
return <ToolbarItem tool="star" />
}
/** @public */
/** @public @react */
export function OvalToolbarItem() {
return <ToolbarItem tool="oval" />
}
/** @public */
/** @public @react */
export function XBoxToolbarItem() {
return <ToolbarItem tool="x-box" />
}
/** @public */
/** @public @react */
export function CheckBoxToolbarItem() {
return <ToolbarItem tool="check-box" />
}
/** @public */
/** @public @react */
export function ArrowLeftToolbarItem() {
return <ToolbarItem tool="arrow-left" />
}
/** @public */
/** @public @react */
export function ArrowUpToolbarItem() {
return <ToolbarItem tool="arrow-up" />
}
/** @public */
/** @public @react */
export function ArrowDownToolbarItem() {
return <ToolbarItem tool="arrow-down" />
}
/** @public */
/** @public @react */
export function ArrowRightToolbarItem() {
return <ToolbarItem tool="arrow-right" />
}
/** @public */
/** @public @react */
export function LineToolbarItem() {
return <ToolbarItem tool="line" />
}
/** @public */
/** @public @react */
export function HighlightToolbarItem() {
return <ToolbarItem tool="highlight" />
}
/** @public */
/** @public @react */
export function FrameToolbarItem() {
return <ToolbarItem tool="frame" />
}
/** @public */
/** @public @react */
export function LaserToolbarItem() {
return <ToolbarItem tool="laser" />
}

View file

@ -14,7 +14,7 @@ export interface TLUiZoomMenuProps {
children?: ReactNode
}
/** @public */
/** @public @react */
export const DefaultZoomMenu = memo(function DefaultZoomMenu({ children }: TLUiZoomMenuProps) {
const container = useContainer()
const [isOpen, onOpenChange] = useMenuIsOpen('zoom menu')

View file

@ -2,7 +2,7 @@ import { useActions } from '../../context/actions'
import { ZoomTo100MenuItem, ZoomToFitMenuItem, ZoomToSelectionMenuItem } from '../menu-items'
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
/** @public */
/** @public @react */
export function DefaultZoomMenuContent() {
const actions = useActions()
return (

View file

@ -27,14 +27,14 @@ import { TldrawUiMenuItem } from './primitives/menus/TldrawUiMenuItem'
import { TldrawUiMenuSubmenu } from './primitives/menus/TldrawUiMenuSubmenu'
/* -------------------- Selection ------------------- */
/** @public */
/** @public @react */
export function ToggleAutoSizeMenuItem() {
const actions = useActions()
const shouldDisplay = useShowAutoSizeToggle()
if (!shouldDisplay) return null
return <TldrawUiMenuItem {...actions['toggle-auto-size']} />
}
/** @public */
/** @public @react */
export function EditLinkMenuItem() {
const actions = useActions()
const shouldDisplay = useHasLinkShapeSelected()
@ -42,7 +42,7 @@ export function EditLinkMenuItem() {
return <TldrawUiMenuItem {...actions['edit-link']} />
}
/** @public */
/** @public @react */
export function DuplicateMenuItem() {
const actions = useActions()
const shouldDisplay = useUnlockedSelectedShapesCount(1)
@ -50,7 +50,7 @@ export function DuplicateMenuItem() {
return <TldrawUiMenuItem {...actions['duplicate']} />
}
/** @public */
/** @public @react */
export function GroupMenuItem() {
const actions = useActions()
const shouldDisplay = useAllowGroup()
@ -58,7 +58,7 @@ export function GroupMenuItem() {
return <TldrawUiMenuItem {...actions['group']} />
}
/** @public */
/** @public @react */
export function UngroupMenuItem() {
const actions = useActions()
const shouldDisplay = useAllowUngroup()
@ -66,7 +66,7 @@ export function UngroupMenuItem() {
return <TldrawUiMenuItem {...actions['ungroup']} />
}
/** @public */
/** @public @react */
export function RemoveFrameMenuItem() {
const editor = useEditor()
const actions = useActions()
@ -83,7 +83,7 @@ export function RemoveFrameMenuItem() {
return <TldrawUiMenuItem {...actions['remove-frame']} />
}
/** @public */
/** @public @react */
export function FitFrameToContentMenuItem() {
const editor = useEditor()
const actions = useActions()
@ -103,7 +103,7 @@ export function FitFrameToContentMenuItem() {
return <TldrawUiMenuItem {...actions['fit-frame-to-content']} />
}
/** @public */
/** @public @react */
export function ToggleLockMenuItem() {
const editor = useEditor()
const actions = useActions()
@ -114,7 +114,7 @@ export function ToggleLockMenuItem() {
return <TldrawUiMenuItem {...actions['toggle-lock']} />
}
/** @public */
/** @public @react */
export function ToggleTransparentBgMenuItem() {
const actions = useActions()
const editor = useEditor()
@ -127,7 +127,7 @@ export function ToggleTransparentBgMenuItem() {
<TldrawUiMenuCheckboxItem {...actions['toggle-transparent']} checked={isTransparentBg} toggle />
)
}
/** @public */
/** @public @react */
export function UnlockAllMenuItem() {
const editor = useEditor()
const actions = useActions()
@ -139,7 +139,7 @@ export function UnlockAllMenuItem() {
}
/* ---------------------- Zoom ---------------------- */
/** @public */
/** @public @react */
export function ZoomTo100MenuItem() {
const editor = useEditor()
const isZoomedTo100 = useValue('zoomed to 100', () => editor.getZoomLevel() === 1, [editor])
@ -147,7 +147,7 @@ export function ZoomTo100MenuItem() {
return <TldrawUiMenuItem {...actions['zoom-to-100']} noClose disabled={isZoomedTo100} />
}
/** @public */
/** @public @react */
export function ZoomToFitMenuItem() {
const editor = useEditor()
const hasShapes = useValue('has shapes', () => editor.getCurrentPageShapeIds().size > 0, [editor])
@ -162,7 +162,7 @@ export function ZoomToFitMenuItem() {
/>
)
}
/** @public */
/** @public @react */
export function ZoomToSelectionMenuItem() {
const editor = useEditor()
const hasSelected = useValue('has shapes', () => editor.getSelectedShapeIds().length > 0, [
@ -182,7 +182,7 @@ export function ZoomToSelectionMenuItem() {
/* -------------------- Clipboard ------------------- */
/** @public */
/** @public @react */
export function ClipboardMenuGroup() {
return (
<TldrawUiMenuGroup id="clipboard">
@ -195,7 +195,7 @@ export function ClipboardMenuGroup() {
)
}
/** @public */
/** @public @react */
export function CopyAsMenuGroup() {
const editor = useEditor()
const actions = useActions()
@ -226,7 +226,7 @@ export function CopyAsMenuGroup() {
)
}
/** @public */
/** @public @react */
export function CutMenuItem() {
const actions = useActions()
const shouldDisplay = useUnlockedSelectedShapesCount(1)
@ -234,7 +234,7 @@ export function CutMenuItem() {
return <TldrawUiMenuItem {...actions['cut']} disabled={!shouldDisplay} />
}
/** @public */
/** @public @react */
export function CopyMenuItem() {
const actions = useActions()
const shouldDisplay = useAnySelectedShapesCount(1)
@ -242,7 +242,7 @@ export function CopyMenuItem() {
return <TldrawUiMenuItem {...actions['copy']} disabled={!shouldDisplay} />
}
/** @public */
/** @public @react */
export function PasteMenuItem() {
const actions = useActions()
const shouldDisplay = showMenuPaste
@ -252,7 +252,7 @@ export function PasteMenuItem() {
/* ------------------- Conversions ------------------ */
/** @public */
/** @public @react */
export function ConversionsMenuGroup() {
const editor = useEditor()
const actions = useActions()
@ -282,7 +282,7 @@ export function ConversionsMenuGroup() {
}
/* ------------------ Set Selection ----------------- */
/** @public */
/** @public @react */
export function SelectAllMenuItem() {
const actions = useActions()
const editor = useEditor()
@ -296,7 +296,7 @@ export function SelectAllMenuItem() {
}
/* ------------------ Delete Group ------------------ */
/** @public */
/** @public @react */
export function DeleteMenuItem() {
const actions = useActions()
const oneSelected = useUnlockedSelectedShapesCount(1)
@ -305,7 +305,7 @@ export function DeleteMenuItem() {
}
/* --------------------- Modify --------------------- */
/** @public */
/** @public @react */
export function ArrangeMenuSubmenu() {
const twoSelected = useUnlockedSelectedShapesCount(2)
const onlyFlippableShapeSelected = useOnlyFlippableShape()
@ -370,7 +370,7 @@ function OrderMenuGroup() {
</TldrawUiMenuGroup>
)
}
/** @public */
/** @public @react */
export function ReorderMenuSubmenu() {
const actions = useActions()
const oneSelected = useUnlockedSelectedShapesCount(1)
@ -387,7 +387,7 @@ export function ReorderMenuSubmenu() {
</TldrawUiMenuSubmenu>
)
}
/** @public */
/** @public @react */
export function MoveToPageMenu() {
const editor = useEditor()
const pages = useValue('pages', () => editor.getPages(), [editor])
@ -442,7 +442,7 @@ export function MoveToPageMenu() {
)
}
/** @public */
/** @public @react */
export function ConvertToBookmarkMenuItem() {
const editor = useEditor()
const actions = useActions()
@ -465,7 +465,7 @@ export function ConvertToBookmarkMenuItem() {
return <TldrawUiMenuItem {...actions['convert-to-bookmark']} />
}
/** @public */
/** @public @react */
export function ConvertToEmbedMenuItem() {
const editor = useEditor()
const actions = useActions()
@ -491,21 +491,21 @@ export function ConvertToEmbedMenuItem() {
}
/* ------------------- Preferences ------------------ */
/** @public */
/** @public @react */
export function ToggleSnapModeItem() {
const actions = useActions()
const editor = useEditor()
const isSnapMode = useValue('isSnapMode', () => editor.user.getIsSnapMode(), [editor])
return <TldrawUiMenuCheckboxItem {...actions['toggle-snap-mode']} checked={isSnapMode} />
}
/** @public */
/** @public @react */
export function ToggleToolLockItem() {
const actions = useActions()
const editor = useEditor()
const isToolLock = useValue('isToolLock', () => editor.getInstanceState().isToolLocked, [editor])
return <TldrawUiMenuCheckboxItem {...actions['toggle-tool-lock']} checked={isToolLock} />
}
/** @public */
/** @public @react */
export function ToggleGridItem() {
const actions = useActions()
const editor = useEditor()
@ -513,7 +513,7 @@ export function ToggleGridItem() {
return <TldrawUiMenuCheckboxItem {...actions['toggle-grid']} checked={isGridMode} />
}
/** @public */
/** @public @react */
export function ToggleWrapModeItem() {
const actions = useActions()
const editor = useEditor()
@ -521,21 +521,21 @@ export function ToggleWrapModeItem() {
return <TldrawUiMenuCheckboxItem {...actions['toggle-wrap-mode']} checked={isWrapMode} />
}
/** @public */
/** @public @react */
export function ToggleDarkModeItem() {
const actions = useActions()
const editor = useEditor()
const isDarkMode = useValue('isDarkMode', () => editor.user.getIsDarkMode(), [editor])
return <TldrawUiMenuCheckboxItem {...actions['toggle-dark-mode']} checked={isDarkMode} />
}
/** @public */
/** @public @react */
export function ToggleFocusModeItem() {
const actions = useActions()
const editor = useEditor()
const isFocusMode = useValue('isFocusMode', () => editor.getInstanceState().isFocusMode, [editor])
return <TldrawUiMenuCheckboxItem {...actions['toggle-focus-mode']} checked={isFocusMode} />
}
/** @public */
/** @public @react */
export function ToggleEdgeScrollingItem() {
const actions = useActions()
const editor = useEditor()
@ -549,7 +549,7 @@ export function ToggleEdgeScrollingItem() {
/>
)
}
/** @public */
/** @public @react */
export function ToggleReduceMotionItem() {
const actions = useActions()
const editor = useEditor()
@ -558,7 +558,7 @@ export function ToggleReduceMotionItem() {
<TldrawUiMenuCheckboxItem {...actions['toggle-reduce-motion']} checked={animationSpeed === 0} />
)
}
/** @public */
/** @public @react */
export function ToggleDebugModeItem() {
const actions = useActions()
const editor = useEditor()
@ -567,7 +567,7 @@ export function ToggleDebugModeItem() {
}
/* ---------------------- Print --------------------- */
/** @public */
/** @public @react */
export function PrintItem() {
const editor = useEditor()
const actions = useActions()

View file

@ -7,7 +7,7 @@ export interface TLUiButtonProps extends React.HTMLAttributes<HTMLButtonElement>
type: 'normal' | 'primary' | 'danger' | 'low' | 'icon' | 'tool' | 'menu' | 'help'
}
/** @public */
/** @public @react */
export const TldrawUiButton = React.forwardRef<HTMLButtonElement, TLUiButtonProps>(
function TldrawUiButton({ children, disabled, type, ...props }, ref) {
return (

View file

@ -5,7 +5,7 @@ export interface TLUiButtonCheckProps {
checked: boolean
}
/** @public */
/** @public @react */
export function TldrawUiButtonCheck({ checked }: TLUiButtonCheckProps) {
return <TldrawUiIcon icon={checked ? 'check' : 'none'} className="tlui-button__icon" small />
}

View file

@ -7,7 +7,7 @@ export interface TLUiButtonIconProps {
invertIcon?: boolean
}
/** @public */
/** @public @react */
export function TldrawUiButtonIcon({ icon, small, invertIcon }: TLUiButtonIconProps) {
return (
<TldrawUiIcon className="tlui-button__icon" icon={icon} small={small} invertIcon={invertIcon} />

View file

@ -5,7 +5,7 @@ export interface TLUiButtonLabelProps {
children?: ReactNode
}
/** @public */
/** @public @react */
export function TldrawUiButtonLabel({ children }: TLUiButtonLabelProps) {
return <span className="tlui-button__label">{children}</span>
}

View file

@ -1,6 +1,6 @@
import { Spinner } from '../../Spinner'
/** @public */
/** @public @react */
export function TldrawUiButtonSpinner() {
return <Spinner />
}

View file

@ -10,7 +10,7 @@ export interface TLUiDialogHeaderProps {
children: ReactNode
}
/** @public */
/** @public @react */
export function TldrawUiDialogHeader({ className, children }: TLUiDialogHeaderProps) {
return <div className={classNames('tlui-dialog__header', className)}>{children}</div>
}
@ -21,7 +21,7 @@ export interface TLUiDialogTitleProps {
children: ReactNode
}
/** @public */
/** @public @react */
export function TldrawUiDialogTitle({ className, children }: TLUiDialogTitleProps) {
return (
<_Dialog.DialogTitle dir="ltr" className={classNames('tlui-dialog__header__title', className)}>
@ -30,7 +30,7 @@ export function TldrawUiDialogTitle({ className, children }: TLUiDialogTitleProp
)
}
/** @public */
/** @public @react */
export function TldrawUiDialogCloseButton() {
return (
<div className="tlui-dialog__header__close">
@ -54,7 +54,7 @@ export interface TLUiDialogBodyProps {
style?: React.CSSProperties
}
/** @public */
/** @public @react */
export function TldrawUiDialogBody({ className, children, style }: TLUiDialogBodyProps) {
return (
<div className={classNames('tlui-dialog__body', className)} style={style}>
@ -69,7 +69,7 @@ export interface TLUiDialogFooterProps {
children: ReactNode
}
/** @public */
/** @public @react */
export function TldrawUiDialogFooter({ className, children }: TLUiDialogFooterProps) {
return <div className={classNames('tlui-dialog__footer', className)}>{children}</div>
}

View file

@ -15,7 +15,7 @@ export interface TLUiDropdownMenuRootProps {
debugOpen?: boolean
}
/** @public */
/** @public @react */
export function TldrawUiDropdownMenuRoot({
id,
children,
@ -41,7 +41,7 @@ export interface TLUiDropdownMenuTriggerProps {
children?: ReactNode
}
/** @public */
/** @public @react */
export function TldrawUiDropdownMenuTrigger({ children, ...rest }: TLUiDropdownMenuTriggerProps) {
return (
<_DropdownMenu.Trigger
@ -66,7 +66,7 @@ export interface TLUiDropdownMenuContentProps {
side?: 'bottom' | 'top' | 'right' | 'left'
}
/** @public */
/** @public @react */
export function TldrawUiDropdownMenuContent({
side = 'bottom',
align = 'start',
@ -98,7 +98,7 @@ export interface TLUiDropdownMenuSubProps {
children: ReactNode
}
/** @public */
/** @public @react */
export function TldrawUiDropdownMenuSub({ id, children }: TLUiDropdownMenuSubProps) {
const [open, onOpenChange] = useMenuIsOpen(id)
@ -117,7 +117,7 @@ export interface TLUiDropdownMenuSubTriggerProps {
disabled?: boolean
}
/** @public */
/** @public @react */
export function TldrawUiDropdownMenuSubTrigger({
id,
label,
@ -149,7 +149,7 @@ export interface TLUiDropdownMenuSubContentProps {
children: ReactNode
}
/** @public */
/** @public @react */
export function TldrawUiDropdownMenuSubContent({
id,
alignOffset = -1,
@ -179,7 +179,7 @@ export interface TLUiDropdownMenuGroupProps {
children: ReactNode
}
/** @public */
/** @public @react */
export function TldrawUiDropdownMenuGroup({ children }: TLUiDropdownMenuGroupProps) {
return (
<_DropdownMenu.Group dir="ltr" className="tlui-menu__group">
@ -188,7 +188,7 @@ export function TldrawUiDropdownMenuGroup({ children }: TLUiDropdownMenuGroupPro
)
}
/** @public */
/** @public @react */
export function TldrawUiDropdownMenuIndicator() {
return (
<_DropdownMenu.ItemIndicator dir="ltr" asChild>
@ -203,7 +203,7 @@ export interface TLUiDropdownMenuItemProps {
children: ReactNode
}
/** @public */
/** @public @react */
export function TldrawUiDropdownMenuItem({ noClose, children }: TLUiDropdownMenuItemProps) {
return (
<_DropdownMenu.Item dir="ltr" asChild onClick={noClose ? preventDefault : undefined}>
@ -221,7 +221,7 @@ export interface TLUiDropdownMenuCheckboxItemProps {
children: ReactNode
}
/** @public */
/** @public @react */
export function TldrawUiDropdownMenuCheckboxItem({
children,
onSelect,

View file

@ -13,7 +13,7 @@ export interface TLUiIconProps extends React.HTMLProps<HTMLDivElement> {
crossOrigin?: 'anonymous' | 'use-credentials'
}
/** @public */
/** @public @react */
export const TldrawUiIcon = memo(function TldrawUiIcon({
small,
invertIcon,

View file

@ -35,7 +35,7 @@ export interface TLUiInputProps {
value?: string
}
/** @public */
/** @public @react */
export const TldrawUiInput = React.forwardRef<HTMLInputElement, TLUiInputProps>(
function TldrawUiInput(
{

View file

@ -8,7 +8,7 @@ export interface TLUiKbdProps {
visibleOnMobileLayout?: boolean
}
/** @public */
/** @public @react */
export function TldrawUiKbd({ children, visibleOnMobileLayout = false }: TLUiKbdProps) {
const breakpoint = useBreakpoint()
if (!visibleOnMobileLayout && breakpoint < PORTRAIT_BREAKPOINT.MOBILE) return null

View file

@ -11,7 +11,7 @@ export interface TLUiPopoverProps {
onOpenChange?: (isOpen: boolean) => void
}
/** @public */
/** @public @react */
export function TldrawUiPopover({ id, children, onOpenChange, open }: TLUiPopoverProps) {
const [isOpen, handleOpenChange] = useMenuIsOpen(id, onOpenChange)
@ -30,7 +30,7 @@ export interface TLUiPopoverTriggerProps {
children?: React.ReactNode
}
/** @public */
/** @public @react */
export function TldrawUiPopoverTrigger({ children }: TLUiPopoverTriggerProps) {
return (
<PopoverPrimitive.Trigger asChild dir="ltr">
@ -48,7 +48,7 @@ export interface TLUiPopoverContentProps {
sideOffset?: number
}
/** @public */
/** @public @react */
export function TldrawUiPopoverContent({
side,
children,

View file

@ -27,7 +27,7 @@ export interface TLUiMenuCheckboxItemProps<
disabled?: boolean
}
/** @public */
/** @public @react */
export function TldrawUiMenuCheckboxItem<
TranslationKey extends string = string,
IconType extends string = string,

View file

@ -34,7 +34,7 @@ export interface TLUiMenuContextProviderProps {
children: React.ReactNode
}
/** @public */
/** @public @react */
export function TldrawUiMenuContextProvider({
type,
sourceId,

View file

@ -16,7 +16,7 @@ export interface TLUiMenuGroupProps<TranslationKey extends string = string> {
children?: ReactNode
}
/** @public */
/** @public @react */
export function TldrawUiMenuGroup({ id, label, children }: TLUiMenuGroupProps) {
const { type: menuType, sourceId } = useTldrawUiMenuContext()
const msg = useTranslation()

View file

@ -59,7 +59,7 @@ export interface TLUiMenuItemProps<
isSelected?: boolean
}
/** @public */
/** @public @react */
export function TldrawUiMenuItem<
TranslationKey extends string = string,
IconType extends string = string,

View file

@ -28,7 +28,7 @@ export interface TLUiMenuSubmenuProps<Translation extends string = string> {
size?: 'tiny' | 'small' | 'medium' | 'wide'
}
/** @public */
/** @public @react */
export function TldrawUiMenuSubmenu<Translation extends string = string>({
id,
disabled = false,

View file

@ -12,11 +12,7 @@ import { DialogsProvider } from './dialogs'
import { TLUiEventHandler, UiEventsProvider } from './events'
import { ToastsProvider } from './toasts'
/**
* Props for the {@link tldraw#Tldraw} and {@link TldrawUi} components.
*
* @public
**/
/** @public */
export interface TldrawUiContextProviderProps {
/**
* Urls for where to find fonts and other assets for the UI.
@ -49,7 +45,7 @@ export interface TldrawUiContextProviderProps {
children?: ReactNode
}
/** @public */
/** @public @react */
export function TldrawUiContextProvider({
overrides,
components,

View file

@ -5,13 +5,13 @@ import { PORTRAIT_BREAKPOINT, PORTRAIT_BREAKPOINTS } from '../constants'
const BreakpointContext = React.createContext<number | null>(null)
/** @public */
export function BreakPointProvider({
forceMobile = false,
children,
}: {
export interface BreakPointProviderProps {
forceMobile?: boolean
children: ReactNode
}) {
}
/** @public @react */
export function BreakPointProvider({ forceMobile = false, children }: BreakPointProviderProps) {
const editor = useEditor()
const breakpoint = useValue(

View file

@ -62,7 +62,7 @@ export interface TLUiComponentsProviderProps {
children: ReactNode
}
/** @public */
/** @public @react */
export function TldrawUiComponentsProvider({
overrides = {},
children,

View file

@ -124,7 +124,7 @@ export interface EventsProviderProps {
children: React.ReactNode
}
/** @public */
/** @public @react */
export function UiEventsProvider({ onEvent, children }: EventsProviderProps) {
return (
<EventsContext.Provider value={onEvent ?? defaultEventHandler}>

View file

@ -295,4 +295,137 @@ exports.rules = {
},
defaultOptions: [],
}),
'tagged-components': ESLintUtils.RuleCreator.withoutDocs({
create(context) {
function isComponentName(node: TSESTree.Node) {
return node.type === 'Identifier' && /^[A-Z]/.test(node.name)
}
function checkComponentDeclaration(
services: utils.ParserServices,
node: TSESTree.VariableDeclarator | TSESTree.FunctionDeclaration,
propsType: ts.TypeNode | undefined
) {
const declaration = findTopLevelParent(node)
const comments = context.getSourceCode().getCommentsBefore(declaration)
// we only care about components tagged as public
const publicComment = comments.find((comment) => comment.value.includes('@public'))
if (!publicComment) return
// if it's not tagged as a react component, it should be:
if (!publicComment.value.includes('@react')) {
context.report({
messageId: 'untagged',
node: publicComment,
fix: (fixer) => {
const hasLines = publicComment.value.includes('\n')
let replacement
if (hasLines) {
const lines = publicComment.value.split('\n')
const publicLineIdx = lines.findIndex((line) => line.includes('@public'))
if (!publicLineIdx) throw new Error('Could not find @public line')
const indent = lines[publicLineIdx].match(/^\s*/)![0]
lines.splice(publicLineIdx + 1, 0, `${indent}* @react`)
replacement = lines.join('\n')
} else {
replacement = publicComment.value.replace('@public', '@public @react')
}
return fixer.replaceText(publicComment, `/*${replacement}*/`)
},
})
return
}
// if it is tagged as a react component, the props should be a named export:
if (!propsType) return
if (propsType.kind !== ts.SyntaxKind.TypeReference) {
context.report({
messageId: 'nonNamedProps',
node: services.tsNodeToESTreeNodeMap.get(propsType)!,
})
}
}
function findTopLevelParent(node: TSESTree.Node): TSESTree.Node {
let current: TSESTree.Node = node
while (current.parent && current.parent.type !== 'Program') {
current = current.parent
}
return current
}
function checkFunctionExpression(
node: TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression
) {
const services = ESLintUtils.getParserServices(context)
const parent = node.parent!
if (parent.type === utils.AST_NODE_TYPES.VariableDeclarator && isComponentName(parent.id)) {
const propsType = services.esTreeNodeToTSNodeMap.get(node).parameters[0]?.type
checkComponentDeclaration(services, parent, propsType)
}
if (parent.type === utils.AST_NODE_TYPES.CallExpression) {
const callee = parent.callee
const grandparent = parent.parent!
const isMemoFn =
(callee.type === utils.AST_NODE_TYPES.Identifier && callee.name === 'memo') ||
(callee.type === utils.AST_NODE_TYPES.MemberExpression &&
callee.property.type === utils.AST_NODE_TYPES.Identifier &&
callee.property.name === 'memo')
const isForwardRefFn =
(callee.type === utils.AST_NODE_TYPES.Identifier && callee.name === 'forwardRef') ||
(callee.type === utils.AST_NODE_TYPES.MemberExpression &&
callee.property.type === utils.AST_NODE_TYPES.Identifier &&
callee.property.name === 'forwardRef')
const isComponenty =
grandparent.type === utils.AST_NODE_TYPES.VariableDeclarator &&
isComponentName(grandparent.id)
if (isMemoFn && isComponenty) {
const propsType = services.esTreeNodeToTSNodeMap.get(node).parameters[0]?.type
checkComponentDeclaration(services, grandparent, propsType)
}
if (isForwardRefFn && isComponenty) {
const propsType =
services.esTreeNodeToTSNodeMap.get(node).parameters[1]?.type ||
services.esTreeNodeToTSNodeMap.get(parent).typeArguments?.[1]
checkComponentDeclaration(services, grandparent, propsType)
}
}
}
return {
FunctionDeclaration(node) {
if (node.id && isComponentName(node.id)) {
const services = ESLintUtils.getParserServices(context)
const propsType = services.esTreeNodeToTSNodeMap.get(node).parameters[0]?.type
checkComponentDeclaration(services, node, propsType)
}
},
FunctionExpression(node) {
checkFunctionExpression(node)
},
ArrowFunctionExpression(node) {
checkFunctionExpression(node)
},
}
},
meta: {
messages: {
untagged: 'This react component should be tagged with @react',
nonNamedProps: 'Props should be a separate named & public exported type/interface.',
},
type: 'problem',
schema: [],
fixable: 'code',
},
defaultOptions: [],
}),
}

15
tsdoc.json Normal file
View file

@ -0,0 +1,15 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
"extends": ["@microsoft/api-extractor/extends/tsdoc-base.json"],
"tagDefinitions": [
{
"tagName": "@react",
"syntaxKind": "modifier"
}
],
"supportForTags": {
"@react": true
}
}