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-export-star': 'error',
'local/no-internal-imports': 'error', 'local/no-internal-imports': 'error',
'local/tagged-components': 'error',
'no-only-tests/no-only-tests': 'error', 'no-only-tests/no-only-tests': 'error',
'no-restricted-syntax': [ 'no-restricted-syntax': [
'error', 'error',

View file

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

View file

@ -17,6 +17,154 @@ order: 3
{/* START AUTO-GENERATED CHANGELOG */} {/* 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) ### [v2.1.4](/releases/v2.1.4)
### Release Notes ### Release Notes

View file

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

View file

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

View file

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

View file

@ -10,7 +10,7 @@ import {
DocSection, DocSection,
DocSoftBreak, DocSoftBreak,
} from '@microsoft/tsdoc' } from '@microsoft/tsdoc'
import assert from 'assert' import { assert } from '@tldraw/utils'
import { slug as githubSlug } from 'github-slugger' import { slug as githubSlug } from 'github-slugger'
import path from 'path' import path from 'path'
@ -98,6 +98,12 @@ export async function formatWithPrettier(
console.warn(`☢️ Could not format code: ${code}`) 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() return formattedCode.trimEnd()
} }

View file

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

View file

@ -1,4 +1,5 @@
import { connect } from '@/scripts/functions/connect' import { connect } from '@/scripts/functions/connect'
import { assert } from '@tldraw/utils'
import { Database } from 'sqlite' import { Database } from 'sqlite'
import sqlite3 from 'sqlite3' import sqlite3 from 'sqlite3'
import { import {
@ -12,7 +13,6 @@ import {
SidebarContentLink, SidebarContentLink,
SidebarContentList, SidebarContentList,
} from '../types/content-types' } from '../types/content-types'
import { assert } from './assert'
export class ContentDatabase { export class ContentDatabase {
constructor(public db: Database<sqlite3.Database, sqlite3.Statement>) {} 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}` return `${window.location.origin}${newPathname}${window.location.search}`
} }
/** @public */
export const ShareMenu = React.memo(function ShareMenu() { export const ShareMenu = React.memo(function ShareMenu() {
const msg = useTranslation() const msg = useTranslation()
const container = useContainer() const container = useContainer()

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -35,7 +35,7 @@ export interface TextLabelProps {
textHeight?: number textHeight?: number
} }
/** @public */ /** @public @react */
export const TextLabel = React.memo(function TextLabel({ export const TextLabel = React.memo(function TextLabel({
id, id,
type, 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 classNames from 'classnames'
import React, { ReactNode } from 'react' import React, { ReactNode } from 'react'
import { TLUiAssetUrlOverrides } from './assetUrls' import { TLUiAssetUrlOverrides } from './assetUrls'
@ -21,12 +21,8 @@ import { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts'
import { useReadonly } from './hooks/useReadonly' import { useReadonly } from './hooks/useReadonly'
import { useTranslation } from './hooks/useTranslation/useTranslation' import { useTranslation } from './hooks/useTranslation/useTranslation'
/** /** @public */
* Base props for the {@link tldraw#Tldraw} and {@link TldrawUi} components. export interface TldrawUiProps extends TldrawUiContextProviderProps {
*
* @public
*/
export interface TldrawUiBaseProps {
/** /**
* The component's children. * The component's children.
*/ */
@ -51,15 +47,9 @@ export interface TldrawUiBaseProps {
assetUrls?: TLUiAssetUrlOverrides assetUrls?: TLUiAssetUrlOverrides
} }
/**
* Props for the {@link tldraw#Tldraw} and {@link TldrawUi} components.
*
* @public
*/
export type TldrawUiProps = Expand<TldrawUiBaseProps & TldrawUiContextProviderProps>
/** /**
* @public * @public
* @react
*/ */
export const TldrawUi = React.memo(function TldrawUi({ export const TldrawUi = React.memo(function TldrawUi({
renderDebugMenuItems, renderDebugMenuItems,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -13,7 +13,7 @@ export interface TLUiMainMenuProps {
children?: ReactNode children?: ReactNode
} }
/** @public */ /** @public @react */
export const DefaultMainMenu = memo(function DefaultMainMenu({ children }: TLUiMainMenuProps) { export const DefaultMainMenu = memo(function DefaultMainMenu({ children }: TLUiMainMenuProps) {
const container = useContainer() const container = useContainer()
const [isOpen, onOpenChange] = useMenuIsOpen('main menu') 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 { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
import { TldrawUiMenuSubmenu } from '../primitives/menus/TldrawUiMenuSubmenu' import { TldrawUiMenuSubmenu } from '../primitives/menus/TldrawUiMenuSubmenu'
/** @public */ /** @public @react */
export function DefaultMainMenuContent() { export function DefaultMainMenuContent() {
return ( return (
<> <>
@ -47,7 +47,7 @@ export function DefaultMainMenuContent() {
) )
} }
/** @public */ /** @public @react */
export function ExportFileContentSubMenu() { export function ExportFileContentSubMenu() {
const actions = useActions() const actions = useActions()
@ -65,7 +65,7 @@ export function ExportFileContentSubMenu() {
) )
} }
/** @public */ /** @public @react */
export function EditSubmenu() { export function EditSubmenu() {
const editor = useEditor() const editor = useEditor()
@ -89,7 +89,7 @@ export function EditSubmenu() {
) )
} }
/** @public */ /** @public @react */
export function MiscMenuGroup() { export function MiscMenuGroup() {
return ( return (
<TldrawUiMenuGroup id="misc"> <TldrawUiMenuGroup id="misc">
@ -105,7 +105,7 @@ export function MiscMenuGroup() {
) )
} }
/** @public */ /** @public @react */
export function LockGroup() { export function LockGroup() {
return ( return (
<TldrawUiMenuGroup id="lock"> <TldrawUiMenuGroup id="lock">
@ -115,7 +115,7 @@ export function LockGroup() {
) )
} }
/** @public */ /** @public @react */
export function UndoRedoGroup() { export function UndoRedoGroup() {
const actions = useActions() const actions = useActions()
const canUndo = useCanUndo() const canUndo = useCanUndo()
@ -128,7 +128,7 @@ export function UndoRedoGroup() {
) )
} }
/** @public */ /** @public @react */
export function ViewSubmenu() { export function ViewSubmenu() {
const actions = useActions() const actions = useActions()
return ( return (
@ -144,7 +144,7 @@ export function ViewSubmenu() {
) )
} }
/** @public */ /** @public @react */
export function ExtrasGroup() { export function ExtrasGroup() {
const actions = useActions() const actions = useActions()
return ( return (
@ -157,7 +157,7 @@ export function ExtrasGroup() {
/* ------------------- Preferences ------------------ */ /* ------------------- Preferences ------------------ */
/** @public */ /** @public @react */
export function PreferencesGroup() { export function PreferencesGroup() {
return ( return (
<TldrawUiMenuGroup id="preferences"> <TldrawUiMenuGroup id="preferences">

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -39,7 +39,7 @@ export interface TLUiStylePanelContentProps {
styles: ReturnType<typeof useRelevantStyles> styles: ReturnType<typeof useRelevantStyles>
} }
/** @public */ /** @public @react */
export function DefaultStylePanelContent({ styles }: TLUiStylePanelContentProps) { export function DefaultStylePanelContent({ styles }: TLUiStylePanelContentProps) {
const isDarkMode = useIsDarkMode() const isDarkMode = useIsDarkMode()
@ -95,13 +95,18 @@ function useStyleChangeCallback() {
} }
/** @public */ /** @public */
export function CommonStylePickerSet({ export interface ThemeStylePickerSetProps {
styles,
theme,
}: {
styles: ReadonlySharedStyleMap styles: ReadonlySharedStyleMap
theme: TLDefaultColorTheme theme: TLDefaultColorTheme
}) { }
/** @public */
export interface StylePickerSetProps {
styles: ReadonlySharedStyleMap
}
/** @public @react */
export function CommonStylePickerSet({ styles, theme }: ThemeStylePickerSetProps) {
const msg = useTranslation() const msg = useTranslation()
const editor = useEditor() const editor = useEditor()
@ -182,14 +187,8 @@ export function CommonStylePickerSet({
) )
} }
/** @public */ /** @public @react */
export function TextStylePickerSet({ export function TextStylePickerSet({ theme, styles }: ThemeStylePickerSetProps) {
theme,
styles,
}: {
theme: TLDefaultColorTheme
styles: ReadonlySharedStyleMap
}) {
const msg = useTranslation() const msg = useTranslation()
const handleValueChange = useStyleChangeCallback() const handleValueChange = useStyleChangeCallback()
@ -277,8 +276,8 @@ export function TextStylePickerSet({
</div> </div>
) )
} }
/** @public */ /** @public @react */
export function GeoStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap }) { export function GeoStylePickerSet({ styles }: StylePickerSetProps) {
const handleValueChange = useStyleChangeCallback() const handleValueChange = useStyleChangeCallback()
const geo = styles.get(GeoShapeGeoStyle) const geo = styles.get(GeoShapeGeoStyle)
@ -299,8 +298,8 @@ export function GeoStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap }
/> />
) )
} }
/** @public */ /** @public @react */
export function SplineStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap }) { export function SplineStylePickerSet({ styles }: StylePickerSetProps) {
const handleValueChange = useStyleChangeCallback() const handleValueChange = useStyleChangeCallback()
const spline = styles.get(LineShapeSplineStyle) const spline = styles.get(LineShapeSplineStyle)
@ -322,8 +321,8 @@ export function SplineStylePickerSet({ styles }: { styles: ReadonlySharedStyleMa
) )
} }
/** @public */ /** @public @react */
export function ArrowheadStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap }) { export function ArrowheadStylePickerSet({ styles }: StylePickerSetProps) {
const handleValueChange = useStyleChangeCallback() const handleValueChange = useStyleChangeCallback()
const arrowheadEnd = styles.get(ArrowShapeArrowheadEndStyle) 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 const tldrawSupportedOpacities = [0.1, 0.25, 0.5, 0.75, 1] as const
/** @public */ /** @public @react */
export function OpacitySlider() { export function OpacitySlider() {
const editor = useEditor() const editor = useEditor()
const opacity = useValue('opacity', () => editor.getSharedOpacity(), [editor]) 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. * recently active item from the overflow being shown in the main toolbar.
* *
* @public * @public
* @react
*/ */
export const DefaultToolbar = memo(function DefaultToolbar({ children }: { children?: ReactNode }) { export const DefaultToolbar = memo(function DefaultToolbar({ children }: DefaultToolbarProps) {
const editor = useEditor() const editor = useEditor()
const breakpoint = useBreakpoint() const breakpoint = useBreakpoint()
const isReadonlyMode = useReadonly() const isReadonlyMode = useReadonly()

View file

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

View file

@ -14,7 +14,7 @@ export interface TLUiZoomMenuProps {
children?: ReactNode children?: ReactNode
} }
/** @public */ /** @public @react */
export const DefaultZoomMenu = memo(function DefaultZoomMenu({ children }: TLUiZoomMenuProps) { export const DefaultZoomMenu = memo(function DefaultZoomMenu({ children }: TLUiZoomMenuProps) {
const container = useContainer() const container = useContainer()
const [isOpen, onOpenChange] = useMenuIsOpen('zoom menu') 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 { ZoomTo100MenuItem, ZoomToFitMenuItem, ZoomToSelectionMenuItem } from '../menu-items'
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem' import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
/** @public */ /** @public @react */
export function DefaultZoomMenuContent() { export function DefaultZoomMenuContent() {
const actions = useActions() const actions = useActions()
return ( return (

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -59,7 +59,7 @@ export interface TLUiMenuItemProps<
isSelected?: boolean isSelected?: boolean
} }
/** @public */ /** @public @react */
export function TldrawUiMenuItem< export function TldrawUiMenuItem<
TranslationKey extends string = string, TranslationKey extends string = string,
IconType 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' size?: 'tiny' | 'small' | 'medium' | 'wide'
} }
/** @public */ /** @public @react */
export function TldrawUiMenuSubmenu<Translation extends string = string>({ export function TldrawUiMenuSubmenu<Translation extends string = string>({
id, id,
disabled = false, disabled = false,

View file

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

View file

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

View file

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

View file

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

View file

@ -295,4 +295,137 @@ exports.rules = {
}, },
defaultOptions: [], 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
}
}