@tldraw/ui
Advanced tools
Comparing version 2.0.0-canary.08c9b63bd to 2.0.0-canary.090e148552ab
348
CHANGELOG.md
@@ -0,1 +1,349 @@ | ||
# v2.0.0-alpha.14 (Tue Jul 04 2023) | ||
### Release Notes | ||
#### Disable styles panel button on mobile when using the laser tool. ([#1704](https://github.com/tldraw/tldraw/pull/1704)) | ||
- Disable the styles panel button for laser tool on mobile. | ||
#### remove lock option from highlighter ([#1703](https://github.com/tldraw/tldraw/pull/1703)) | ||
- We no longer show the tool lock option for highlighter - it didn't do anything anyway | ||
#### [fix] Lock shortcut ([#1677](https://github.com/tldraw/tldraw/pull/1677)) | ||
- [@tldraw/editor] Fix lock tool shortcut | ||
#### [feature] add `meta` property to records ([#1627](https://github.com/tldraw/tldraw/pull/1627)) | ||
- todo | ||
--- | ||
#### 🚀 Enhancement | ||
- [feature] add `meta` property to records [#1627](https://github.com/tldraw/tldraw/pull/1627) ([@steveruizok](https://github.com/steveruizok)) | ||
#### 🐛 Bug Fix | ||
- Disable styles panel button on mobile when using the laser tool. [#1704](https://github.com/tldraw/tldraw/pull/1704) ([@MitjaBezensek](https://github.com/MitjaBezensek)) | ||
- remove lock option from highlighter [#1703](https://github.com/tldraw/tldraw/pull/1703) ([@SomeHats](https://github.com/SomeHats)) | ||
- [fix] Lock shortcut [#1677](https://github.com/tldraw/tldraw/pull/1677) ([@steveruizok](https://github.com/steveruizok)) | ||
#### Authors: 3 | ||
- alex ([@SomeHats](https://github.com/SomeHats)) | ||
- Mitja Bezenšek ([@MitjaBezensek](https://github.com/MitjaBezensek)) | ||
- Steve Ruiz ([@steveruizok](https://github.com/steveruizok)) | ||
--- | ||
# v2.0.0-alpha.13 (Wed Jun 28 2023) | ||
### Release Notes | ||
#### Styles API follow-ups ([#1636](https://github.com/tldraw/tldraw/pull/1636)) | ||
-- | ||
#### Revert "Update dependencies (#1613)" ([#1617](https://github.com/tldraw/tldraw/pull/1617)) | ||
- | ||
#### tldraw.css ([#1607](https://github.com/tldraw/tldraw/pull/1607)) | ||
- [tldraw] Removes `editor.css` and `ui.css` exports, replaces with `tldraw.css` | ||
#### Styles API ([#1580](https://github.com/tldraw/tldraw/pull/1580)) | ||
- | ||
#### (1/2) Cursor Chat - Presence ([#1487](https://github.com/tldraw/tldraw/pull/1487)) | ||
- [dev] Added support for cursor chat presence. | ||
#### Use unpkg as a default for serving assets. ([#1548](https://github.com/tldraw/tldraw/pull/1548)) | ||
- Use unpkg asset hosting as a default. | ||
#### hoist opacity out of props ([#1526](https://github.com/tldraw/tldraw/pull/1526)) | ||
[internal only for now] | ||
#### Select locked shapes on long press ([#1529](https://github.com/tldraw/tldraw/pull/1529)) | ||
#### highlighter fixes ([#1530](https://github.com/tldraw/tldraw/pull/1530)) | ||
[aq bug fixes] | ||
#### Simplify static cursors ([#1520](https://github.com/tldraw/tldraw/pull/1520)) | ||
- (editor) Simplifies the cursors in our CSS. | ||
#### Renaming types, shape utils, tools ([#1513](https://github.com/tldraw/tldraw/pull/1513)) | ||
- Renaming of types, shape utils, tools | ||
#### tlschema cleanup ([#1509](https://github.com/tldraw/tldraw/pull/1509)) | ||
- [editor] Remove `app.createShapeId` | ||
- [tlschema] Cleans up exports | ||
#### Cleanup @tldraw/ui types / exports ([#1504](https://github.com/tldraw/tldraw/pull/1504)) | ||
- [editor] clean up / unify types | ||
#### rename app to editor ([#1503](https://github.com/tldraw/tldraw/pull/1503)) | ||
- Rename `App` to `Editor` and many other things that reference `app` to `editor`. | ||
#### Add support for locking shapes ([#1447](https://github.com/tldraw/tldraw/pull/1447)) | ||
- Add support for locking shapes. | ||
#### [3/3] Highlighter styling ([#1490](https://github.com/tldraw/tldraw/pull/1490)) | ||
Highlighter pen is here! 🎉🎉🎉 | ||
#### [1/3] initial highlighter shape/tool ([#1401](https://github.com/tldraw/tldraw/pull/1401)) | ||
[internal only change layout ground work for highlighter] | ||
#### [feature] reduce motion ([#1485](https://github.com/tldraw/tldraw/pull/1485)) | ||
- [editor] Add `reduceMotion` user preference | ||
- Add reduce motion option to preferences | ||
#### Feature flags rework ([#1474](https://github.com/tldraw/tldraw/pull/1474)) | ||
[internal only change] | ||
#### [refactor] update record names ([#1473](https://github.com/tldraw/tldraw/pull/1473)) | ||
- [editor] rename record types | ||
#### remove safari special-casing for paste ([#1470](https://github.com/tldraw/tldraw/pull/1470)) | ||
[fixes a regression introduced during this release] | ||
#### Don't allow `g` keyboard shortcut in readonly mode, show laser tool in the toolbar ([#1459](https://github.com/tldraw/tldraw/pull/1459)) | ||
- Disable geo tool shortcut in readonly mode. Show laser on the toolbar. | ||
#### [mini-feature] Following indicator ([#1468](https://github.com/tldraw/tldraw/pull/1468)) | ||
- Adds viewport following indicator | ||
#### [chore] refactor user preferences ([#1435](https://github.com/tldraw/tldraw/pull/1435)) | ||
- Add a brief release note for your PR here. | ||
#### Add translations for "Leave shared project" action ([#1394](https://github.com/tldraw/tldraw/pull/1394)) | ||
- None | ||
#### Stricter ID types ([#1439](https://github.com/tldraw/tldraw/pull/1439)) | ||
[internal only, covered by #1432 changelog] | ||
#### Add SVG cursors for all cursor types ([#1416](https://github.com/tldraw/tldraw/pull/1416)) | ||
- Added consistent custom cursors. | ||
#### [refactor] Remove `TLShapeDef`, `getShapeUtilByType`. ([#1432](https://github.com/tldraw/tldraw/pull/1432)) | ||
- [tlschema] Update props of `createTLSchema` | ||
- [editor] Update props of `TldrawEditorConfig` | ||
- [editor] Remove `App.getShapeUtilByType` | ||
- [editor] Update `App.getShapeUtil` to take a type rather than a shape | ||
#### Measure individual words instead of just line breaks for text exports ([#1397](https://github.com/tldraw/tldraw/pull/1397)) | ||
- Add a brief release note for your PR here. | ||
#### [feature] Add checkbox to toolbar ([#1423](https://github.com/tldraw/tldraw/pull/1423)) | ||
- Adds missing checkbox to toolbar. | ||
#### [feature] add laser pointer ([#1412](https://github.com/tldraw/tldraw/pull/1412)) | ||
- Adds the laser pointer tool. | ||
#### Vertical text alignment for geo shapes ([#1414](https://github.com/tldraw/tldraw/pull/1414)) | ||
- This adds vertical text alignment property to geo shapes. | ||
#### [fix] page menu, drag handle css ([#1406](https://github.com/tldraw/tldraw/pull/1406)) | ||
- Fix styling in the page menu | ||
#### Switch to new collaborators component ([#1405](https://github.com/tldraw/tldraw/pull/1405)) | ||
- [Breaking] Removes the old version of LiveCollaborators, replacing it with the new one based on `TLInstancePresence` | ||
#### [improvement] refactor paste to support multi-line text ([#1398](https://github.com/tldraw/tldraw/pull/1398)) | ||
- Improves clipboard logic when pasting text | ||
- Adds support for pasting multi-line text | ||
- Adds maximum widths when pasting single-line text | ||
- Adds support for RTL languages when pasting multi-line or wrapped text | ||
- Strips leading indentation when pasting text | ||
#### Don't allow the users to use keyboard shortcuts to select tools in readonly mode. ([#1382](https://github.com/tldraw/tldraw/pull/1382)) | ||
- Disable keyboard shortcut events for tools in readonly mode. We only allow the select, hand tools, and zoom tool. | ||
#### [fix] Don't synchronize isReadOnly ([#1396](https://github.com/tldraw/tldraw/pull/1396)) | ||
- Removes the isReadOnly value from the `user_document_settings` record type. | ||
#### Add localizations for snapshots links ([#1347](https://github.com/tldraw/tldraw/pull/1347)) | ||
- Add localization for creating snapshot links. | ||
#### avoid lazy race conditions ([#1364](https://github.com/tldraw/tldraw/pull/1364)) | ||
[internal only] | ||
#### Export Events stuff ([#1360](https://github.com/tldraw/tldraw/pull/1360)) | ||
- [ui] export the `TLUiEventSource` type | ||
- [ui] export the `EventsProviderProps ` type | ||
- [ui] export the `useEvents ` hook | ||
#### [improvement] rename onEvent to onUiEvent ([#1358](https://github.com/tldraw/tldraw/pull/1358)) | ||
- [docs] Adds docs for ui events | ||
- [tldraw] Renames `onEvent` to `onUiEvent` | ||
#### [improvement] Ui events followup ([#1354](https://github.com/tldraw/tldraw/pull/1354)) | ||
- [ui] Adds source to ui events data object | ||
- [ui] Corrects source for toolbar events | ||
- [ui] Corrects source for clipboard events | ||
- [examples] Updates events example | ||
#### Fix "copy as png" in firefox when `dom.events.asyncClipboard.clipboardItem` is enabled ([#1342](https://github.com/tldraw/tldraw/pull/1342)) | ||
- Fix "copy as png" in firefox when `dom.events.asyncClipboard.clipboardItem` is enabled | ||
--- | ||
#### 💥 Breaking Change | ||
- [fix] react component runaways, error boundaries [#1625](https://github.com/tldraw/tldraw/pull/1625) ([@steveruizok](https://github.com/steveruizok)) | ||
- tldraw.css [#1607](https://github.com/tldraw/tldraw/pull/1607) ([@steveruizok](https://github.com/steveruizok)) | ||
- Tidy up [#1600](https://github.com/tldraw/tldraw/pull/1600) ([@steveruizok](https://github.com/steveruizok)) | ||
- Styles API [#1580](https://github.com/tldraw/tldraw/pull/1580) ([@SomeHats](https://github.com/SomeHats) [@steveruizok](https://github.com/steveruizok)) | ||
- Use unpkg as a default for serving assets. [#1548](https://github.com/tldraw/tldraw/pull/1548) ([@MitjaBezensek](https://github.com/MitjaBezensek)) | ||
- hoist opacity out of props [#1526](https://github.com/tldraw/tldraw/pull/1526) ([@SomeHats](https://github.com/SomeHats)) | ||
- Independent instance state persistence [#1493](https://github.com/tldraw/tldraw/pull/1493) ([@ds300](https://github.com/ds300)) | ||
- Renaming types, shape utils, tools [#1513](https://github.com/tldraw/tldraw/pull/1513) ([@steveruizok](https://github.com/steveruizok)) | ||
- tlschema cleanup [#1509](https://github.com/tldraw/tldraw/pull/1509) ([@steveruizok](https://github.com/steveruizok)) | ||
- Cleanup @tldraw/ui types / exports [#1504](https://github.com/tldraw/tldraw/pull/1504) ([@steveruizok](https://github.com/steveruizok)) | ||
- rename app to editor [#1503](https://github.com/tldraw/tldraw/pull/1503) ([@steveruizok](https://github.com/steveruizok)) | ||
- Add support for project names [#1340](https://github.com/tldraw/tldraw/pull/1340) ([@TodePond](https://github.com/TodePond) [@steveruizok](https://github.com/steveruizok)) | ||
- [refactor] User-facing APIs [#1478](https://github.com/tldraw/tldraw/pull/1478) ([@steveruizok](https://github.com/steveruizok)) | ||
- [refactor] update record names [#1473](https://github.com/tldraw/tldraw/pull/1473) ([@steveruizok](https://github.com/steveruizok)) | ||
- [chore] refactor user preferences [#1435](https://github.com/tldraw/tldraw/pull/1435) ([@ds300](https://github.com/ds300)) | ||
- [refactor] Remove `TLShapeDef`, `getShapeUtilByType`. [#1432](https://github.com/tldraw/tldraw/pull/1432) ([@steveruizok](https://github.com/steveruizok) [@SomeHats](https://github.com/SomeHats)) | ||
- Switch to new collaborators component [#1405](https://github.com/tldraw/tldraw/pull/1405) ([@ds300](https://github.com/ds300)) | ||
- [fix] Don't synchronize isReadOnly [#1396](https://github.com/tldraw/tldraw/pull/1396) ([@ds300](https://github.com/ds300)) | ||
- [improvement] rename onEvent to onUiEvent [#1358](https://github.com/tldraw/tldraw/pull/1358) ([@steveruizok](https://github.com/steveruizok)) | ||
- [improvement] Ui events followup [#1354](https://github.com/tldraw/tldraw/pull/1354) ([@steveruizok](https://github.com/steveruizok)) | ||
- [feature] ui events [#1326](https://github.com/tldraw/tldraw/pull/1326) ([@orangemug](https://github.com/orangemug) [@steveruizok](https://github.com/steveruizok)) | ||
#### 🚀 Enhancement | ||
- Styles API follow-ups [#1636](https://github.com/tldraw/tldraw/pull/1636) ([@SomeHats](https://github.com/SomeHats) [@steveruizok](https://github.com/steveruizok)) | ||
- (1/2) Cursor Chat - Presence [#1487](https://github.com/tldraw/tldraw/pull/1487) ([@TodePond](https://github.com/TodePond) [@steveruizok](https://github.com/steveruizok)) | ||
- Add support for locking shapes [#1447](https://github.com/tldraw/tldraw/pull/1447) ([@MitjaBezensek](https://github.com/MitjaBezensek) [@steveruizok](https://github.com/steveruizok)) | ||
- [3/3] Highlighter styling [#1490](https://github.com/tldraw/tldraw/pull/1490) ([@SomeHats](https://github.com/SomeHats) [@steveruizok](https://github.com/steveruizok)) | ||
- [1/3] initial highlighter shape/tool [#1401](https://github.com/tldraw/tldraw/pull/1401) ([@SomeHats](https://github.com/SomeHats)) | ||
- [feature] reduce motion [#1485](https://github.com/tldraw/tldraw/pull/1485) ([@steveruizok](https://github.com/steveruizok)) | ||
- [mini-feature] Following indicator [#1468](https://github.com/tldraw/tldraw/pull/1468) ([@steveruizok](https://github.com/steveruizok)) | ||
- Add SVG cursors for all cursor types [#1416](https://github.com/tldraw/tldraw/pull/1416) ([@TodePond](https://github.com/TodePond) [@steveruizok](https://github.com/steveruizok)) | ||
- [feature] Add checkbox to toolbar [#1423](https://github.com/tldraw/tldraw/pull/1423) ([@steveruizok](https://github.com/steveruizok)) | ||
- [feature] add laser pointer [#1412](https://github.com/tldraw/tldraw/pull/1412) ([@steveruizok](https://github.com/steveruizok)) | ||
- Vertical text alignment for geo shapes [#1414](https://github.com/tldraw/tldraw/pull/1414) ([@MitjaBezensek](https://github.com/MitjaBezensek) [@steveruizok](https://github.com/steveruizok)) | ||
- [improvement] refactor paste to support multi-line text [#1398](https://github.com/tldraw/tldraw/pull/1398) ([@steveruizok](https://github.com/steveruizok)) | ||
- Add stuff for new 'share project' flow [#1403](https://github.com/tldraw/tldraw/pull/1403) ([@ds300](https://github.com/ds300)) | ||
- open menus refactor [#1400](https://github.com/tldraw/tldraw/pull/1400) ([@steveruizok](https://github.com/steveruizok)) | ||
- Snapshot link menu translations [#1399](https://github.com/tldraw/tldraw/pull/1399) ([@steveruizok](https://github.com/steveruizok)) | ||
#### 🐛 Bug Fix | ||
- 3/2 Cursor chat [#1623](https://github.com/tldraw/tldraw/pull/1623) ([@steveruizok](https://github.com/steveruizok)) | ||
- [fix] embeds [#1578](https://github.com/tldraw/tldraw/pull/1578) ([@steveruizok](https://github.com/steveruizok)) | ||
- Asset improvements [#1557](https://github.com/tldraw/tldraw/pull/1557) ([@MitjaBezensek](https://github.com/MitjaBezensek)) | ||
- Use `"Toggle locked"` [#1538](https://github.com/tldraw/tldraw/pull/1538) ([@steveruizok](https://github.com/steveruizok)) | ||
- Select locked shapes on long press [#1529](https://github.com/tldraw/tldraw/pull/1529) ([@steveruizok](https://github.com/steveruizok)) | ||
- highlighter fixes [#1530](https://github.com/tldraw/tldraw/pull/1530) ([@SomeHats](https://github.com/SomeHats)) | ||
- Feature flags rework [#1474](https://github.com/tldraw/tldraw/pull/1474) ([@SomeHats](https://github.com/SomeHats)) | ||
- remove safari special-casing for paste [#1470](https://github.com/tldraw/tldraw/pull/1470) ([@SomeHats](https://github.com/SomeHats)) | ||
- Don't allow `g` keyboard shortcut in readonly mode, show laser tool in the toolbar [#1459](https://github.com/tldraw/tldraw/pull/1459) ([@MitjaBezensek](https://github.com/MitjaBezensek) [@steveruizok](https://github.com/steveruizok)) | ||
- Fix people menu button border on android [#1471](https://github.com/tldraw/tldraw/pull/1471) ([@TodePond](https://github.com/TodePond)) | ||
- [fix] lock option for laser tool [#1460](https://github.com/tldraw/tldraw/pull/1460) ([@steveruizok](https://github.com/steveruizok)) | ||
- Add laser keyboard shortcut. [#1467](https://github.com/tldraw/tldraw/pull/1467) ([@MitjaBezensek](https://github.com/MitjaBezensek)) | ||
- [fix] make follow icon visible on iPad [#1462](https://github.com/tldraw/tldraw/pull/1462) ([@steveruizok](https://github.com/steveruizok)) | ||
- [fix] page item submenu [#1461](https://github.com/tldraw/tldraw/pull/1461) ([@steveruizok](https://github.com/steveruizok)) | ||
- Add translations for "Leave shared project" action [#1394](https://github.com/tldraw/tldraw/pull/1394) ([@TodePond](https://github.com/TodePond) [@steveruizok](https://github.com/steveruizok)) | ||
- Stricter ID types [#1439](https://github.com/tldraw/tldraw/pull/1439) ([@SomeHats](https://github.com/SomeHats) [@steveruizok](https://github.com/steveruizok)) | ||
- Measure individual words instead of just line breaks for text exports [#1397](https://github.com/tldraw/tldraw/pull/1397) ([@SomeHats](https://github.com/SomeHats)) | ||
- [fix] page menu, drag handle css [#1406](https://github.com/tldraw/tldraw/pull/1406) ([@steveruizok](https://github.com/steveruizok)) | ||
- Don't allow the users to use keyboard shortcuts to select tools in readonly mode. [#1382](https://github.com/tldraw/tldraw/pull/1382) ([@MitjaBezensek](https://github.com/MitjaBezensek)) | ||
- Disabling middle click paste in favour of panning [#1335](https://github.com/tldraw/tldraw/pull/1335) ([@orangemug](https://github.com/orangemug) [@steveruizok](https://github.com/steveruizok)) | ||
- Export Events stuff [#1360](https://github.com/tldraw/tldraw/pull/1360) ([@steveruizok](https://github.com/steveruizok)) | ||
- Fix "copy as png" in firefox when `dom.events.asyncClipboard.clipboardItem` is enabled [#1342](https://github.com/tldraw/tldraw/pull/1342) ([@orangemug](https://github.com/orangemug)) | ||
- [feature] `check-box` geo shape [#1330](https://github.com/tldraw/tldraw/pull/1330) ([@steveruizok](https://github.com/steveruizok)) | ||
- [tiny] rename show menu paste [#1332](https://github.com/tldraw/tldraw/pull/1332) ([@steveruizok](https://github.com/steveruizok)) | ||
- remove svg layer, html all the things, rs to tl [#1227](https://github.com/tldraw/tldraw/pull/1227) ([@TodePond](https://github.com/TodePond) [@steveruizok](https://github.com/steveruizok)) | ||
- New vite-based examples app [#1226](https://github.com/tldraw/tldraw/pull/1226) ([@SomeHats](https://github.com/SomeHats)) | ||
- readmes [#1195](https://github.com/tldraw/tldraw/pull/1195) ([@steveruizok](https://github.com/steveruizok)) | ||
- update @radix-ui/react-popover to 1.0.6-rc.5 [#1206](https://github.com/tldraw/tldraw/pull/1206) ([@SomeHats](https://github.com/SomeHats)) | ||
- [chore] update lazyrepo [#1211](https://github.com/tldraw/tldraw/pull/1211) ([@ds300](https://github.com/ds300)) | ||
- [fix] pick a better default language [#1201](https://github.com/tldraw/tldraw/pull/1201) ([@steveruizok](https://github.com/steveruizok) [@TodePond](https://github.com/TodePond)) | ||
- Added `pHYs` to import/export of png images [#1200](https://github.com/tldraw/tldraw/pull/1200) ([@orangemug](https://github.com/orangemug) [@steveruizok](https://github.com/steveruizok)) | ||
- derived presence state [#1204](https://github.com/tldraw/tldraw/pull/1204) ([@ds300](https://github.com/ds300)) | ||
- [lite] upgrade lazyrepo [#1198](https://github.com/tldraw/tldraw/pull/1198) ([@ds300](https://github.com/ds300)) | ||
- transfer-out: transfer out [#1195](https://github.com/tldraw/tldraw/pull/1195) ([@SomeHats](https://github.com/SomeHats)) | ||
#### ⚠️ Pushed to `main` | ||
- update lazyrepo ([@ds300](https://github.com/ds300)) | ||
#### 🏠 Internal | ||
- Explicit shape type checks [#1594](https://github.com/tldraw/tldraw/pull/1594) ([@steveruizok](https://github.com/steveruizok)) | ||
- move some kbds into actions and tools [#1585](https://github.com/tldraw/tldraw/pull/1585) ([@BrianHung](https://github.com/BrianHung) [@steveruizok](https://github.com/steveruizok)) | ||
- [improvement] bookmark shape logic [#1568](https://github.com/tldraw/tldraw/pull/1568) ([@steveruizok](https://github.com/steveruizok)) | ||
- Simplify static cursors [#1520](https://github.com/tldraw/tldraw/pull/1520) ([@steveruizok](https://github.com/steveruizok)) | ||
- [chore] remove benchmark [#1489](https://github.com/tldraw/tldraw/pull/1489) ([@steveruizok](https://github.com/steveruizok)) | ||
- Add localizations for snapshots links [#1347](https://github.com/tldraw/tldraw/pull/1347) ([@MitjaBezensek](https://github.com/MitjaBezensek)) | ||
- avoid lazy race conditions [#1364](https://github.com/tldraw/tldraw/pull/1364) ([@SomeHats](https://github.com/SomeHats)) | ||
#### 🧪 Tests | ||
- Add playwright tests [#1484](https://github.com/tldraw/tldraw/pull/1484) ([@steveruizok](https://github.com/steveruizok)) | ||
#### 🔩 Dependency Updates | ||
- Incorporate signia as @tldraw/state [#1620](https://github.com/tldraw/tldraw/pull/1620) ([@steveruizok](https://github.com/steveruizok) [@ds300](https://github.com/ds300)) | ||
- Revert "Update dependencies (#1613)" [#1617](https://github.com/tldraw/tldraw/pull/1617) ([@SomeHats](https://github.com/SomeHats)) | ||
- Update dependencies [#1613](https://github.com/tldraw/tldraw/pull/1613) ([@steveruizok](https://github.com/steveruizok)) | ||
#### Authors: 7 | ||
- alex ([@SomeHats](https://github.com/SomeHats)) | ||
- Brian Hung ([@BrianHung](https://github.com/BrianHung)) | ||
- David Sheldrick ([@ds300](https://github.com/ds300)) | ||
- Lu Wilson ([@TodePond](https://github.com/TodePond)) | ||
- Mitja Bezenšek ([@MitjaBezensek](https://github.com/MitjaBezensek)) | ||
- Orange Mug ([@orangemug](https://github.com/orangemug)) | ||
- Steve Ruiz ([@steveruizok](https://github.com/steveruizok)) | ||
--- | ||
# v2.0.0-alpha.12 (Mon Apr 03 2023) | ||
@@ -2,0 +350,0 @@ |
/// <reference types="react" /> | ||
import { App } from '@tldraw/editor'; | ||
import { Context } from 'react'; | ||
import { EditorAssetUrls } from '@tldraw/editor'; | ||
import { Editor } from '@tldraw/editor'; | ||
import { EMBED_DEFINITIONS } from '@tldraw/editor'; | ||
import { MemoExoticComponent } from 'react'; | ||
import { LANGUAGES } from '@tldraw/editor'; | ||
import { NamedExoticComponent } from 'react'; | ||
@@ -12,42 +10,13 @@ import { default as React_2 } from 'react'; | ||
import { ReactNode } from 'react'; | ||
import { RecursivePartial } from '@tldraw/utils'; | ||
import { TLCopyType } from '@tldraw/editor'; | ||
import { TLEditorAssetUrls } from '@tldraw/editor'; | ||
import { TLExportType } from '@tldraw/editor'; | ||
import { TLLanguage } from '@tldraw/editor'; | ||
import { TLShapeId } from '@tldraw/editor'; | ||
import { TLStyleItem } from '@tldraw/editor'; | ||
import { TLStyleType } from '@tldraw/editor'; | ||
import { VecLike } from '@tldraw/primitives'; | ||
/** @public */ | ||
export declare interface ActionItem { | ||
icon?: TLUiIconType; | ||
id: string; | ||
kbd?: string; | ||
title?: string; | ||
label?: TLTranslationKey; | ||
menuLabel?: TLTranslationKey; | ||
shortcutsLabel?: TLTranslationKey; | ||
contextMenuLabel?: TLTranslationKey; | ||
readonlyOk: boolean; | ||
checkbox?: boolean; | ||
onSelect: () => Promise<void> | void; | ||
} | ||
/** @public */ | ||
export declare const ActionsContext: React_3.Context<ActionsContextType>; | ||
/** @public */ | ||
export declare type ActionsContextType = Record<string, ActionItem>; | ||
/** @public */ | ||
export declare const ActionsMenuSchemaContext: React_2.Context<MenuSchema>; | ||
/** @public */ | ||
export declare type ActionsMenuSchemaContextType = MenuSchema; | ||
/** @public */ | ||
export declare const ActionsMenuSchemaProvider: MemoExoticComponent<({ overrides, children, }: ActionsMenuSchemaProviderProps) => JSX.Element>; | ||
/** @public */ | ||
export declare type ActionsMenuSchemaProviderProps = { | ||
overrides?: (app: App, schema: ActionsMenuSchemaContextType, helpers: { | ||
declare type ActionsMenuSchemaProviderProps = { | ||
overrides?: (editor: Editor, schema: TLUiActionsMenuSchemaContextType, helpers: { | ||
actions: ReturnType<typeof useActions>; | ||
@@ -57,3 +26,3 @@ oneSelected: boolean; | ||
threeSelected: boolean; | ||
}) => ActionsMenuSchemaContextType; | ||
}) => TLUiActionsMenuSchemaContextType; | ||
children: any; | ||
@@ -63,20 +32,10 @@ }; | ||
/** @public */ | ||
export declare function ActionsProvider({ overrides, children }: ActionsProviderProps): JSX.Element; | ||
/** @public */ | ||
export declare type ActionsProviderProps = { | ||
overrides?: (app: App, actions: ActionsContextType, helpers: undefined) => ActionsContextType; | ||
declare type ActionsProviderProps = { | ||
overrides?: (editor: Editor, actions: TLUiActionsContextType, helpers: undefined) => TLUiActionsContextType; | ||
children: any; | ||
}; | ||
/** @public */ | ||
export declare function AssetUrlsProvider({ assetUrls, children, }: { | ||
assetUrls: UiAssetUrls; | ||
children: React.ReactNode; | ||
}): JSX.Element; | ||
/* Excluded from this release type: AssetUrlsProvider */ | ||
/** @public */ | ||
export declare const BASE_URL: string; | ||
/** @public */ | ||
declare function Body_2({ className, children, style, }: { | ||
@@ -94,36 +53,5 @@ className?: string; | ||
/** @public */ | ||
export declare const Button: React_3.ForwardRefExoticComponent<ButtonProps & React_3.RefAttributes<HTMLButtonElement>>; | ||
export declare const Button: React_3.ForwardRefExoticComponent<TLUiButtonProps & React_3.RefAttributes<HTMLButtonElement>>; | ||
/** @public */ | ||
export declare const ButtonPicker: React_3.MemoExoticComponent<typeof _ButtonPicker>; | ||
declare function _ButtonPicker<T extends TLStyleItem>(props: ButtonPickerProps<T>): JSX.Element; | ||
/** @public */ | ||
export declare interface ButtonPickerProps<T extends TLStyleItem> { | ||
title: string; | ||
items: T[]; | ||
styleType: TLStyleType; | ||
value?: null | number | string; | ||
columns?: 2 | 3 | 4; | ||
'data-wd'?: string; | ||
onValueChange: (item: T, squashing: boolean) => void; | ||
} | ||
/** @public */ | ||
export declare interface ButtonProps extends React_3.HTMLAttributes<HTMLButtonElement> { | ||
loading?: boolean; | ||
disabled?: boolean; | ||
label?: TLTranslationKey; | ||
icon?: TLUiIconType; | ||
spinner?: boolean; | ||
iconLeft?: TLUiIconType; | ||
smallIcon?: boolean; | ||
kbd?: string; | ||
isChecked?: boolean; | ||
invertIcon?: boolean; | ||
type?: 'danger' | 'normal' | 'primary'; | ||
} | ||
/** @public */ | ||
declare function CheckboxItem({ children, onSelect, ...rest }: DropdownMenuCheckboxItemProps): JSX.Element; | ||
@@ -151,43 +79,2 @@ | ||
/** @public */ | ||
export declare interface ContextMenuProps { | ||
children: any; | ||
} | ||
/** @public */ | ||
export declare const ContextMenuSchemaContext: React_2.Context<MenuSchema>; | ||
/** @public */ | ||
export declare type ContextMenuSchemaContextType = MenuSchema; | ||
/** @public */ | ||
export declare const ContextMenuSchemaProvider: MemoExoticComponent<({ overrides, children, }: ContextMenuSchemaProviderProps) => JSX.Element>; | ||
/** @public */ | ||
export declare type ContextMenuSchemaProviderProps = { | ||
overrides?: (app: App, schema: ContextMenuSchemaContextType, helpers: { | ||
actions: ReturnType<typeof useActions>; | ||
oneSelected: boolean; | ||
twoSelected: boolean; | ||
threeSelected: boolean; | ||
showAutoSizeToggle: boolean; | ||
showUngroup: boolean; | ||
onlyFlippableShapeSelected: boolean; | ||
}) => ContextMenuSchemaContextType; | ||
children: any; | ||
}; | ||
/** @public */ | ||
export declare type CustomMenuItem = { | ||
id: string; | ||
type: 'custom'; | ||
disabled: boolean; | ||
readonlyOk: boolean; | ||
}; | ||
/** @public */ | ||
export declare const DebugPanel: React_3.NamedExoticComponent<{ | ||
renderDebugMenuItems: (() => React_3.ReactNode) | null; | ||
}>; | ||
declare type DefaultHelpers = ReturnType<typeof useDefaultHelpers>; | ||
@@ -206,30 +93,2 @@ | ||
/** @public */ | ||
export declare interface DialogProps { | ||
onClose: () => void; | ||
} | ||
/** @public */ | ||
export declare const DialogsContext: Context<DialogsContextType>; | ||
/** @public */ | ||
export declare type DialogsContextType = { | ||
addDialog: (dialog: Omit<TLDialog, 'id'> & { | ||
id?: string; | ||
}) => string; | ||
removeDialog: (id: string) => string; | ||
updateDialog: (id: string, newDialogData: Partial<TLDialog>) => string; | ||
clearDialogs: () => void; | ||
dialogs: TLDialog[]; | ||
}; | ||
/** @public */ | ||
export declare function DialogsProvider({ children }: DialogsProviderProps): JSX.Element; | ||
/** @public */ | ||
export declare type DialogsProviderProps = { | ||
overrides?: (app: App) => DialogsContextType; | ||
children: any; | ||
}; | ||
declare namespace DropdownMenu { | ||
@@ -264,3 +123,3 @@ export { | ||
/** @public */ | ||
declare interface DropdownMenuItemProps extends ButtonProps { | ||
declare interface DropdownMenuItemProps extends TLUiButtonProps { | ||
noClose?: boolean; | ||
@@ -270,20 +129,5 @@ } | ||
/** @public */ | ||
export declare type EmbedInfo = { | ||
width: number; | ||
height: number; | ||
doesResize: boolean; | ||
isEmbedUrl: (url: string) => boolean; | ||
toEmbed: (url: string) => string; | ||
}; | ||
export declare function findMenuItem(menu: TLUiMenuSchema, path: string[]): TLUiMenuChild; | ||
/** @public */ | ||
export declare const EN_TRANSLATION: TLTranslation; | ||
/** @public */ | ||
export declare function fetchTranslation(locale: TLTranslationLocale, assetUrls: UiAssetUrls): Promise<TLTranslation>; | ||
/** @public */ | ||
export declare function findMenuItem(menu: MenuSchema, path: string[]): MenuChild; | ||
/** @public */ | ||
declare function Footer({ className, children }: { | ||
@@ -295,8 +139,2 @@ className?: string; | ||
/** @public */ | ||
export declare const getBaseUrl: () => string; | ||
/** @public */ | ||
export declare function getTranslation(locale: TLTranslationLocale, assetUrls: UiAssetUrls): Promise<TLTranslation>; | ||
/** @public */ | ||
declare function Group({ children, size, }: { | ||
@@ -314,118 +152,190 @@ children: any; | ||
/** @public */ | ||
export declare const HelpMenu: React_3.NamedExoticComponent<object>; | ||
export declare const Icon: NamedExoticComponent<TLUiIconProps>; | ||
declare interface HelpMenuLink { | ||
label: TLTranslationKey; | ||
icon: TLUiIconType; | ||
url: string; | ||
} | ||
/** @public */ | ||
declare function Indicator(): JSX.Element; | ||
/** @public */ | ||
export declare interface HelpMenuProps { | ||
links?: HelpMenuLink[]; | ||
} | ||
export declare const Input: React_3.ForwardRefExoticComponent<TLUiInputProps & React_3.RefAttributes<HTMLInputElement>>; | ||
/* Excluded from this release type: HelpMenuSchemaContext */ | ||
/** @public */ | ||
export declare const HelpMenuSchemaProvider: MemoExoticComponent<({ overrides, children, }: HelpMenuSchemaProviderProps) => JSX.Element>; | ||
declare function Item({ noClose, ...props }: DropdownMenuItemProps): JSX.Element; | ||
/** @public */ | ||
export declare type HelpMenuSchemaProviderProps = { | ||
overrides?: (app: App, schema: HelpMenuSchemaProviderType, helpers: { | ||
actions: ReturnType<typeof useActions>; | ||
languages: TLListedTranslations; | ||
currentLanguage: string; | ||
oneSelected: boolean; | ||
twoSelected: boolean; | ||
threeSelected: boolean; | ||
}) => HelpMenuSchemaProviderType; | ||
children: any; | ||
declare type Join<T, K> = K extends null ? { | ||
[R in keyof T]: T[R]; | ||
} : { | ||
[R in keyof T]: T[R]; | ||
} & { | ||
[R in keyof K]: K[R]; | ||
}; | ||
/** @public */ | ||
export declare type HelpMenuSchemaProviderType = MenuSchema; | ||
export declare function menuCustom(id: string, opts?: Partial<{ | ||
readonlyOk: boolean; | ||
disabled: boolean; | ||
}>): { | ||
id: string; | ||
type: "custom"; | ||
disabled: boolean; | ||
readonlyOk: boolean; | ||
}; | ||
/** @public */ | ||
export declare const HTMLCanvas: MemoExoticComponent<() => JSX.Element>; | ||
export declare function menuGroup(id: string, ...children: (false | null | TLUiMenuChild)[]): null | TLUiMenuGroup; | ||
/** @public */ | ||
export declare const Icon: NamedExoticComponent<IconProps>; | ||
export declare function menuItem(actionItem: TLUiActionItem | TLUiToolItem, opts?: Partial<{ | ||
checked: boolean; | ||
disabled: boolean; | ||
}>): TLUiMenuItem; | ||
/** @public */ | ||
export declare interface IconProps extends React.HTMLProps<HTMLDivElement> { | ||
icon: TLUiIconType; | ||
small?: boolean; | ||
children?: undefined; | ||
invertIcon?: boolean; | ||
crossOrigin?: 'anonymous' | 'use-credentials'; | ||
} | ||
export declare function menuSubmenu(id: string, label: TLUiTranslationKey, ...children: (false | null | TLUiMenuChild)[]): null | TLUiSubMenu; | ||
/** @public */ | ||
declare function Indicator(): JSX.Element; | ||
declare function RadioItem({ children, onSelect, ...rest }: DropdownMenuCheckboxItemProps): JSX.Element; | ||
/** @public */ | ||
export declare const Input: React_3.ForwardRefExoticComponent<InputProps & React_3.RefAttributes<HTMLInputElement>>; | ||
declare function Root({ id, children, modal, }: { | ||
id: string; | ||
children: any; | ||
modal?: boolean; | ||
}): JSX.Element; | ||
/* Excluded from this release type: setDefaultUiAssetUrls */ | ||
/** @public */ | ||
export declare interface InputProps { | ||
disabled?: boolean; | ||
label?: TLTranslationKey; | ||
icon?: TLUiIconType; | ||
iconLeft?: TLUiIconType; | ||
autofocus?: boolean; | ||
autoselect?: boolean; | ||
children?: any; | ||
defaultValue?: string; | ||
placeholder?: string; | ||
onComplete?: (value: string) => void; | ||
onValueChange?: (value: string) => void; | ||
onCancel?: (value: string) => void; | ||
className?: string; | ||
/** | ||
* Usually on iOS when you focus an input, the browser will adjust the viewport to bring the input | ||
* into view. Sometimes this doesn't work properly though - for example, if the input is newly | ||
* created, iOS seems to have a hard time adjusting the viewport for it. This prop allows you to | ||
* opt-in to some extra code to manually bring the input into view when the visual viewport of the | ||
* browser changes, but we don't want to use it everywhere because generally the native behavior | ||
* looks nicer in scenarios where it's sufficient. | ||
*/ | ||
shouldManuallyMaintainScrollPositionWhenFocused?: boolean; | ||
} | ||
declare function Sub({ id, children }: { | ||
id: string; | ||
children: any; | ||
}): JSX.Element; | ||
/** @public */ | ||
export declare const isActiveToolItem: (item: ToolItem, activeToolId: string | undefined, geoState: null | string | undefined) => boolean; | ||
declare function SubContent({ alignOffset, sideOffset, children, }: { | ||
alignOffset?: number; | ||
sideOffset?: number; | ||
children: any; | ||
}): JSX.Element; | ||
/** @public */ | ||
declare function Item({ noClose, ...props }: DropdownMenuItemProps): JSX.Element; | ||
declare function SubTrigger({ label, 'data-testid': testId, 'data-direction': dataDirection, }: { | ||
label: TLUiTranslationKey; | ||
'data-testid'?: string; | ||
'data-direction'?: 'left' | 'right'; | ||
}): JSX.Element; | ||
/** @public */ | ||
export declare function Kbd({ children }: KbdProps): JSX.Element; | ||
declare function Title({ className, children }: { | ||
className?: string; | ||
children: any; | ||
}): JSX.Element; | ||
/** | ||
* @public | ||
*/ | ||
export declare const TldrawUi: React_2.NamedExoticComponent<{ | ||
children?: ReactNode; | ||
hideUi?: boolean | undefined; | ||
shareZone?: ReactNode; | ||
topZone?: ReactNode; | ||
renderDebugMenuItems?: (() => React_2.ReactNode) | undefined; | ||
} & TldrawUiContextProviderProps>; | ||
/** @public */ | ||
export declare function kbd(str: string): string[]; | ||
export declare function TldrawUiContextProvider({ overrides, assetUrls, onUiEvent, children, }: TldrawUiContextProviderProps): JSX.Element; | ||
/** | ||
* Attributes for the {@link @tldraw/tldraw#Tldraw} and {@link TldrawUi} components. | ||
* | ||
* @param assetUrls - Urls for where to find fonts and other assets for the UI. | ||
* @param overrides - Overrides for the UI. | ||
* @param onUiEvent - Callback for when UI events occur. | ||
* @param children - The component's children. | ||
* | ||
* @public | ||
**/ | ||
export declare interface TldrawUiContextProviderProps { | ||
assetUrls?: RecursivePartial<TLUiAssetUrls>; | ||
overrides?: TLUiOverrides | TLUiOverrides[]; | ||
onUiEvent?: TLUiEventHandler; | ||
children?: any; | ||
} | ||
/** | ||
* Attributes for the {@link @tldraw/tldraw#Tldraw} and {@link TldrawUi} components. | ||
* | ||
* @param children - The component's children. | ||
* @param hideUi - Whether to hide the interface and only display the canvas. | ||
* @param shareZone - A component to use for the share zone (will be deprecated) | ||
* @param topZone - A component to use for the top zone (will be deprecated) | ||
* @param renderDebugMenuItems - Additional items to add to the debug menu (will be deprecated) | ||
* | ||
* @public | ||
*/ | ||
export declare type TldrawUiProps = { | ||
children?: ReactNode; | ||
hideUi?: boolean; | ||
shareZone?: ReactNode; | ||
topZone?: ReactNode; | ||
renderDebugMenuItems?: () => React_2.ReactNode; | ||
} & TldrawUiContextProviderProps; | ||
/** @public */ | ||
export declare interface KbdProps { | ||
children: string; | ||
export declare interface TLUiActionItem { | ||
icon?: TLUiIconType; | ||
id: string; | ||
kbd?: string; | ||
title?: string; | ||
label?: TLUiTranslationKey; | ||
menuLabel?: TLUiTranslationKey; | ||
shortcutsLabel?: TLUiTranslationKey; | ||
contextMenuLabel?: TLUiTranslationKey; | ||
readonlyOk: boolean; | ||
checkbox?: boolean; | ||
onSelect: (source: TLUiEventSource) => Promise<void> | void; | ||
} | ||
/** @public */ | ||
export declare function kbdStr(str: string): string; | ||
export declare type TLUiActionsContextType = Record<string, TLUiActionItem>; | ||
/** @public */ | ||
export declare const KeyboardShortcutsSchemaContext: React_2.Context<MenuSchema>; | ||
export declare type TLUiActionsMenuSchemaContextType = TLUiMenuSchema; | ||
declare type TLUiAssetUrls = TLEditorAssetUrls & { | ||
icons: Record<TLUiIconType, string>; | ||
translations: Record<(typeof LANGUAGES)[number]['locale'], string>; | ||
embedIcons: Record<(typeof EMBED_DEFINITIONS)[number]['type'], string>; | ||
}; | ||
/** @public */ | ||
export declare type KeyboardShortcutsSchemaContextType = MenuSchema; | ||
export declare interface TLUiButtonProps extends React_3.HTMLAttributes<HTMLButtonElement> { | ||
loading?: boolean; | ||
disabled?: boolean; | ||
label?: TLUiTranslationKey; | ||
icon?: TLUiIconType; | ||
spinner?: boolean; | ||
iconLeft?: TLUiIconType; | ||
smallIcon?: boolean; | ||
kbd?: string; | ||
isChecked?: boolean; | ||
invertIcon?: boolean; | ||
type?: 'danger' | 'normal' | 'primary'; | ||
} | ||
/** @public */ | ||
export declare const KeyboardShortcutsSchemaProvider: MemoExoticComponent<({ overrides, children, }: KeyboardShortcutsSchemaProviderProps) => JSX.Element>; | ||
export declare interface TLUiContextMenuProps { | ||
children: any; | ||
} | ||
/** @public */ | ||
export declare type KeyboardShortcutsSchemaProviderProps = { | ||
overrides?: (app: App, schema: KeyboardShortcutsSchemaContextType, more: { | ||
tools: ToolsContextType; | ||
actions: ActionsContextType; | ||
}) => KeyboardShortcutsSchemaContextType; | ||
declare type TLUiContextMenuSchemaProviderProps = { | ||
overrides?: (editor: Editor, schema: TLUiContextTTLUiMenuSchemaContextType, helpers: { | ||
actions: ReturnType<typeof useActions>; | ||
oneSelected: boolean; | ||
twoSelected: boolean; | ||
threeSelected: boolean; | ||
showAutoSizeToggle: boolean; | ||
showUngroup: boolean; | ||
onlyFlippableShapeSelected: boolean; | ||
}) => TLUiContextTTLUiMenuSchemaContextType; | ||
children: any; | ||
@@ -435,113 +345,8 @@ }; | ||
/** @public */ | ||
declare const LANGUAGES: readonly [{ | ||
readonly locale: "ar"; | ||
readonly label: "عربي"; | ||
}, { | ||
readonly locale: "ca"; | ||
readonly label: "Català"; | ||
}, { | ||
readonly locale: "da"; | ||
readonly label: "Danish"; | ||
}, { | ||
readonly locale: "de"; | ||
readonly label: "Deutsch"; | ||
}, { | ||
readonly locale: "en"; | ||
readonly label: "English"; | ||
}, { | ||
readonly locale: "es"; | ||
readonly label: "Español"; | ||
}, { | ||
readonly locale: "fa"; | ||
readonly label: "فارسی"; | ||
}, { | ||
readonly locale: "fi"; | ||
readonly label: "Suomi"; | ||
}, { | ||
readonly locale: "fr"; | ||
readonly label: "Français"; | ||
}, { | ||
readonly locale: "gl"; | ||
readonly label: "Galego"; | ||
}, { | ||
readonly locale: "he"; | ||
readonly label: "עברית"; | ||
}, { | ||
readonly locale: "it"; | ||
readonly label: "Italiano"; | ||
}, { | ||
readonly locale: "ja"; | ||
readonly label: "日本語"; | ||
}, { | ||
readonly locale: "ko-kr"; | ||
readonly label: "한국어"; | ||
}, { | ||
readonly locale: "ku"; | ||
readonly label: "کوردی"; | ||
}, { | ||
readonly locale: "hi-in"; | ||
readonly label: "हिन्दी"; | ||
}, { | ||
readonly locale: "hu"; | ||
readonly label: "Magyar"; | ||
}, { | ||
readonly locale: "my"; | ||
readonly label: "မြန်မာစာ"; | ||
}, { | ||
readonly locale: "ne"; | ||
readonly label: "नेपाली"; | ||
}, { | ||
readonly locale: "no"; | ||
readonly label: "Norwegian"; | ||
}, { | ||
readonly locale: "pl"; | ||
readonly label: "Polski"; | ||
}, { | ||
readonly locale: "pt-br"; | ||
readonly label: "Português - Brasil"; | ||
}, { | ||
readonly locale: "pt-pt"; | ||
readonly label: "Português - Europeu"; | ||
}, { | ||
readonly locale: "ro"; | ||
readonly label: "Română"; | ||
}, { | ||
readonly locale: "ru"; | ||
readonly label: "Russian"; | ||
}, { | ||
readonly locale: "sv"; | ||
readonly label: "Svenska"; | ||
}, { | ||
readonly locale: "te"; | ||
readonly label: "తెలుగు"; | ||
}, { | ||
readonly locale: "th"; | ||
readonly label: "ภาษาไทย"; | ||
}, { | ||
readonly locale: "tr"; | ||
readonly label: "Türkçe"; | ||
}, { | ||
readonly locale: "uk"; | ||
readonly label: "Ukrainian"; | ||
}, { | ||
readonly locale: "vi"; | ||
readonly label: "Tiếng Việt"; | ||
}, { | ||
readonly locale: "zh-cn"; | ||
readonly label: "Chinese - Simplified"; | ||
}, { | ||
readonly locale: "zh-tw"; | ||
readonly label: "繁體中文 (台灣)"; | ||
}]; | ||
export declare type TLUiContextTTLUiMenuSchemaContextType = TLUiMenuSchema; | ||
/** @public */ | ||
export declare type MenuChild = CustomMenuItem | MenuGroup | MenuItem | SubMenu; | ||
/** @public */ | ||
export declare function menuCustom(id: string, opts?: Partial<{ | ||
readonlyOk: boolean; | ||
disabled: boolean; | ||
}>): { | ||
export declare type TLUiCustomMenuItem = { | ||
id: string; | ||
type: "custom"; | ||
type: 'custom'; | ||
disabled: boolean; | ||
@@ -552,51 +357,129 @@ readonlyOk: boolean; | ||
/** @public */ | ||
export declare type MenuGroup = { | ||
export declare interface TLUiDialog { | ||
id: string; | ||
type: 'group'; | ||
checkbox: boolean; | ||
disabled: boolean; | ||
readonlyOk: boolean; | ||
children: MenuChild[]; | ||
}; | ||
onClose?: () => void; | ||
component: (props: TLUiDialogProps) => any; | ||
} | ||
/** @public */ | ||
export declare function menuGroup(id: string, ...children: (false | MenuChild | null)[]): MenuGroup | null; | ||
export declare interface TLUiDialogProps { | ||
onClose: () => void; | ||
} | ||
/** @public */ | ||
export declare type MenuItem = { | ||
id: string; | ||
type: 'item'; | ||
readonlyOk: boolean; | ||
actionItem: ActionItem; | ||
disabled: boolean; | ||
checked: boolean; | ||
export declare type TLUiDialogsContextType = { | ||
addDialog: (dialog: Omit<TLUiDialog, 'id'> & { | ||
id?: string; | ||
}) => string; | ||
removeDialog: (id: string) => string; | ||
updateDialog: (id: string, newDialogData: Partial<TLUiDialog>) => string; | ||
clearDialogs: () => void; | ||
dialogs: TLUiDialog[]; | ||
}; | ||
/** @public */ | ||
export declare function menuItem(actionItem: ActionItem | ToolItem, opts?: Partial<{ | ||
checked: boolean; | ||
disabled: boolean; | ||
}>): MenuItem; | ||
export declare type TLUiEventContextType = TLUiEventHandler<keyof TLUiEventMap>; | ||
/** @public */ | ||
export declare type MenuSchema = (CustomMenuItem | MenuGroup | MenuItem)[]; | ||
export declare type TLUiEventHandler<T extends keyof TLUiEventMap = keyof TLUiEventMap> = (name: T, data: Join<{ | ||
source: TLUiEventSource; | ||
}, TLUiEventMap[T]>) => void; | ||
/** @public */ | ||
export declare const MenuSchemaContext: React_2.Context<MenuSchema>; | ||
declare interface TLUiEventMap { | ||
undo: null; | ||
redo: null; | ||
'group-shapes': null; | ||
'ungroup-shapes': null; | ||
'convert-to-embed': null; | ||
'convert-to-bookmark': null; | ||
'open-embed-link': null; | ||
'toggle-auto-size': null; | ||
'copy-as': { | ||
format: 'json' | 'png' | 'svg'; | ||
}; | ||
'export-as': { | ||
format: 'json' | 'png' | 'svg'; | ||
}; | ||
'edit-link': null; | ||
'insert-embed': null; | ||
'insert-media': null; | ||
'align-shapes': { | ||
operation: 'bottom' | 'center-horizontal' | 'center-vertical' | 'left' | 'right' | 'top'; | ||
}; | ||
'duplicate-shapes': null; | ||
'pack-shapes': null; | ||
'stack-shapes': { | ||
operation: 'horizontal' | 'vertical'; | ||
}; | ||
'flip-shapes': { | ||
operation: 'horizontal' | 'vertical'; | ||
}; | ||
'distribute-shapes': { | ||
operation: 'horizontal' | 'vertical'; | ||
}; | ||
'stretch-shapes': { | ||
operation: 'horizontal' | 'vertical'; | ||
}; | ||
'reorder-shapes': { | ||
operation: 'backward' | 'forward' | 'toBack' | 'toFront'; | ||
}; | ||
'delete-shapes': null; | ||
'select-all-shapes': null; | ||
'select-none-shapes': null; | ||
'rotate-ccw': null; | ||
'rotate-cw': null; | ||
'zoom-in': null; | ||
'zoom-out': null; | ||
'zoom-to-fit': null; | ||
'zoom-to-selection': null; | ||
'reset-zoom': null; | ||
'zoom-into-view': null; | ||
'zoom-to-content': null; | ||
'open-menu': { | ||
id: string; | ||
}; | ||
'close-menu': { | ||
id: string; | ||
}; | ||
'create-new-project': null; | ||
'save-project-to-file': null; | ||
'open-file': null; | ||
'select-tool': { | ||
id: string; | ||
}; | ||
print: null; | ||
copy: null; | ||
paste: null; | ||
cut: null; | ||
'toggle-transparent': null; | ||
'toggle-snap-mode': null; | ||
'toggle-tool-lock': null; | ||
'toggle-grid-mode': null; | ||
'toggle-dark-mode': null; | ||
'toggle-focus-mode': null; | ||
'toggle-debug-mode': null; | ||
'toggle-lock': null; | ||
'toggle-reduce-motion': null; | ||
'exit-pen-mode': null; | ||
'stop-following': null; | ||
'open-cursor-chat': null; | ||
} | ||
/** @public */ | ||
export declare type MenuSchemaContextType = MenuSchema; | ||
export declare type TLUiEventSource = 'actions-menu' | 'context-menu' | 'debug-panel' | 'dialog' | 'export-menu' | 'help-menu' | 'helper-buttons' | 'kbd' | 'menu' | 'navigation-zone' | 'page-menu' | 'people-menu' | 'quick-actions' | 'share-menu' | 'toolbar' | 'unknown' | 'zoom-menu'; | ||
/** @public */ | ||
export declare function MenuSchemaProvider({ overrides, children }: MenuSchemaProviderProps): JSX.Element; | ||
export declare type TLUiHelpMenuSchemaContextType = TLUiMenuSchema; | ||
/** @public */ | ||
export declare type MenuSchemaProviderProps = { | ||
overrides?: (app: App, schema: MenuSchemaContextType, helpers: { | ||
declare type TLUiHelpMenuSchemaProviderProps = { | ||
overrides?: (editor: Editor, schema: TLUiHelpMenuSchemaContextType, helpers: { | ||
actions: ReturnType<typeof useActions>; | ||
noneSelected: boolean; | ||
languages: readonly TLLanguage[]; | ||
currentLanguage: string; | ||
oneSelected: boolean; | ||
twoSelected: boolean; | ||
threeSelected: boolean; | ||
}) => MenuSchemaContextType; | ||
}) => TLUiHelpMenuSchemaContextType; | ||
children: any; | ||
@@ -606,141 +489,122 @@ }; | ||
/** @public */ | ||
export declare function menuSubmenu(id: string, label: TLTranslationKey, ...children: (false | MenuChild | null)[]): null | SubMenu; | ||
export declare interface TLUiIconProps extends React.HTMLProps<HTMLDivElement> { | ||
icon: TLUiIconType; | ||
small?: boolean; | ||
color?: string; | ||
children?: undefined; | ||
invertIcon?: boolean; | ||
crossOrigin?: 'anonymous' | 'use-credentials'; | ||
} | ||
/** @public */ | ||
export declare const NavigationZone: NamedExoticComponent<object>; | ||
export declare type TLUiIconType = 'align-bottom-center' | 'align-bottom-left' | 'align-bottom-right' | 'align-bottom' | 'align-center-center' | 'align-center-horizontal' | 'align-center-left' | 'align-center-right' | 'align-center-vertical' | 'align-left' | 'align-right' | 'align-top-center' | 'align-top-left' | 'align-top-right' | 'align-top' | 'arrow-left' | 'arrowhead-arrow' | 'arrowhead-bar' | 'arrowhead-diamond' | 'arrowhead-dot' | 'arrowhead-none' | 'arrowhead-square' | 'arrowhead-triangle-inverted' | 'arrowhead-triangle' | 'aspect-ratio' | 'avatar' | 'blob' | 'bring-forward' | 'bring-to-front' | 'check' | 'checkbox-checked' | 'checkbox-empty' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'chevrons-ne' | 'chevrons-sw' | 'clipboard-copied' | 'clipboard-copy' | 'code' | 'collab' | 'color' | 'comment' | 'cross-2' | 'cross' | 'dash-dashed' | 'dash-dotted' | 'dash-draw' | 'dash-solid' | 'discord' | 'distribute-horizontal' | 'distribute-vertical' | 'dot' | 'dots-horizontal' | 'dots-vertical' | 'drag-handle-dots' | 'duplicate' | 'edit' | 'external-link' | 'file' | 'fill-none' | 'fill-pattern' | 'fill-semi' | 'fill-solid' | 'follow' | 'following' | 'font-draw' | 'font-mono' | 'font-sans' | 'font-serif' | 'geo-arrow-down' | 'geo-arrow-left' | 'geo-arrow-right' | 'geo-arrow-up' | 'geo-check-box' | 'geo-diamond' | 'geo-ellipse' | 'geo-hexagon' | 'geo-octagon' | 'geo-oval' | 'geo-pentagon' | 'geo-rectangle' | 'geo-rhombus-2' | 'geo-rhombus' | 'geo-star' | 'geo-trapezoid' | 'geo-triangle' | 'geo-x-box' | 'github' | 'group' | 'hidden' | 'image' | 'info-circle' | 'leading' | 'link' | 'lock-small' | 'lock' | 'menu' | 'minus' | 'mixed' | 'pack' | 'page' | 'plus' | 'question-mark-circle' | 'question-mark' | 'redo' | 'reset-zoom' | 'rotate-ccw' | 'rotate-cw' | 'ruler' | 'search' | 'send-backward' | 'send-to-back' | 'settings-horizontal' | 'settings-vertical-1' | 'settings-vertical' | 'share-1' | 'share-2' | 'size-extra-large' | 'size-large' | 'size-medium' | 'size-small' | 'spline-cubic' | 'spline-line' | 'stack-horizontal' | 'stack-vertical' | 'stretch-horizontal' | 'stretch-vertical' | 'text-align-center' | 'text-align-justify' | 'text-align-left' | 'text-align-right' | 'tool-arrow' | 'tool-embed' | 'tool-eraser' | 'tool-frame' | 'tool-hand' | 'tool-highlight' | 'tool-laser' | 'tool-line' | 'tool-media' | 'tool-note' | 'tool-pencil' | 'tool-pointer' | 'tool-text' | 'trash' | 'triangle-down' | 'triangle-up' | 'twitter' | 'undo' | 'ungroup' | 'unlock-small' | 'unlock' | 'vertical-align-center' | 'vertical-align-end' | 'vertical-align-start' | 'visible' | 'warning-triangle' | 'zoom-in' | 'zoom-out'; | ||
/** @public */ | ||
declare function RadioItem({ children, onSelect, ...rest }: DropdownMenuCheckboxItemProps): JSX.Element; | ||
/** @public */ | ||
declare function Root({ id, open, children, modal, }: { | ||
id: string; | ||
children: any; | ||
open?: boolean; | ||
modal?: boolean; | ||
}): JSX.Element; | ||
/** @public */ | ||
export declare const showUiPaste: boolean; | ||
/** @public */ | ||
export declare function Slider(props: SliderProps): JSX.Element; | ||
/** @public */ | ||
export declare interface SliderProps { | ||
steps: number; | ||
value: null | number; | ||
label: string; | ||
title: string; | ||
onValueChange: (value: number, emphemeral: boolean) => void; | ||
'data-wd'?: string; | ||
export declare interface TLUiInputProps { | ||
disabled?: boolean; | ||
label?: TLUiTranslationKey; | ||
icon?: TLUiIconType; | ||
iconLeft?: TLUiIconType; | ||
autofocus?: boolean; | ||
autoselect?: boolean; | ||
children?: any; | ||
defaultValue?: string; | ||
placeholder?: string; | ||
onComplete?: (value: string) => void; | ||
onValueChange?: (value: string) => void; | ||
onCancel?: (value: string) => void; | ||
onBlur?: (value: string) => void; | ||
className?: string; | ||
/** | ||
* Usually on iOS when you focus an input, the browser will adjust the viewport to bring the input | ||
* into view. Sometimes this doesn't work properly though - for example, if the input is newly | ||
* created, iOS seems to have a hard time adjusting the viewport for it. This prop allows you to | ||
* opt-in to some extra code to manually bring the input into view when the visual viewport of the | ||
* browser changes, but we don't want to use it everywhere because generally the native behavior | ||
* looks nicer in scenarios where it's sufficient. | ||
*/ | ||
shouldManuallyMaintainScrollPositionWhenFocused?: boolean; | ||
value?: string; | ||
} | ||
/** @public */ | ||
export declare const StylePanel: MemoExoticComponent<({ isMobile }: StylePanelProps) => JSX.Element | null>; | ||
export declare type TLUiKeyboardShortcutsSchemaContextType = TLUiMenuSchema; | ||
declare interface StylePanelProps { | ||
isMobile?: boolean; | ||
} | ||
/** @public */ | ||
declare function Sub({ id, children, open }: { | ||
id: string; | ||
export declare type TLUiKeyboardShortcutsSchemaProviderProps = { | ||
overrides?: (editor: Editor, schema: TLUiKeyboardShortcutsSchemaContextType, more: { | ||
tools: TLUiToolsContextType; | ||
actions: TLUiActionsContextType; | ||
}) => TLUiKeyboardShortcutsSchemaContextType; | ||
children: any; | ||
open?: boolean; | ||
}): JSX.Element; | ||
}; | ||
/** @public */ | ||
declare function SubContent({ alignOffset, sideOffset, children, }: { | ||
alignOffset?: number; | ||
sideOffset?: number; | ||
children: any; | ||
}): JSX.Element; | ||
export declare type TLUiMenuChild = TLUiCustomMenuItem | TLUiMenuGroup | TLUiMenuItem | TLUiSubMenu; | ||
/** @public */ | ||
export declare type SubMenu = { | ||
export declare type TLUiMenuGroup = { | ||
id: string; | ||
type: 'submenu'; | ||
label: TLTranslationKey; | ||
type: 'group'; | ||
checkbox: boolean; | ||
disabled: boolean; | ||
readonlyOk: boolean; | ||
children: MenuChild[]; | ||
children: TLUiMenuChild[]; | ||
}; | ||
/** @public */ | ||
declare function SubTrigger({ label, 'data-wd': dataWd, 'data-direction': dataDirection, }: { | ||
label: TLTranslationKey; | ||
'data-wd'?: string; | ||
'data-direction'?: 'left' | 'right'; | ||
}): JSX.Element; | ||
export declare type TLUiMenuItem = { | ||
id: string; | ||
type: 'item'; | ||
readonlyOk: boolean; | ||
actionItem: TLUiActionItem; | ||
disabled: boolean; | ||
checked: boolean; | ||
}; | ||
/** @public */ | ||
declare function Title({ className, children }: { | ||
className?: string; | ||
children: any; | ||
}): JSX.Element; | ||
export declare type TLUiMenuSchema = (TLUiCustomMenuItem | TLUiMenuGroup | TLUiMenuItem)[]; | ||
/** @public */ | ||
export declare interface TLDialog { | ||
id: string; | ||
onClose?: () => void; | ||
component: (props: DialogProps) => any; | ||
} | ||
export declare type TLUiMenuSchemaContextType = TLUiMenuSchema; | ||
/** | ||
* @public | ||
*/ | ||
export declare const TldrawUi: React_2.NamedExoticComponent<{ | ||
shareZone?: ReactNode; | ||
renderDebugMenuItems?: (() => React_2.ReactNode) | undefined; | ||
children?: ReactNode; | ||
/** Whether to hide the interface and only display the canvas. */ | ||
hideUi?: boolean | undefined; | ||
} & TldrawUiContextProviderProps>; | ||
/** @public */ | ||
export declare const TldrawUiContent: React_2.NamedExoticComponent<TldrawUiContentProps>; | ||
declare type TldrawUiContentProps = { | ||
hideUi?: boolean; | ||
shareZone?: ReactNode; | ||
renderDebugMenuItems?: () => React_2.ReactNode; | ||
export declare type TLUiMenuSchemaProviderProps = { | ||
overrides?: (editor: Editor, schema: TLUiMenuSchemaContextType, helpers: { | ||
actions: ReturnType<typeof useActions>; | ||
noneSelected: boolean; | ||
oneSelected: boolean; | ||
twoSelected: boolean; | ||
threeSelected: boolean; | ||
}) => TLUiMenuSchemaContextType; | ||
children: any; | ||
}; | ||
/** @public */ | ||
export declare function TldrawUiContextProvider({ overrides, assetUrls, children, }: TldrawUiContextProviderProps): JSX.Element; | ||
declare type TLUiOverride<Type, Helpers> = (editor: Editor, schema: Type, helpers: Helpers) => Type; | ||
/** @public */ | ||
export declare interface TldrawUiContextProviderProps { | ||
assetUrls?: UiAssetUrls; | ||
overrides?: TldrawUiOverrides | TldrawUiOverrides[]; | ||
children?: any; | ||
} | ||
declare type TldrawUiOverride<Type, Helpers> = (app: App, schema: Type, helpers: Helpers) => Type; | ||
/** @public */ | ||
export declare interface TldrawUiOverrides { | ||
export declare interface TLUiOverrides { | ||
actionsMenu?: WithDefaultHelpers<NonNullable<ActionsMenuSchemaProviderProps['overrides']>>; | ||
actions?: WithDefaultHelpers<NonNullable<ActionsProviderProps['overrides']>>; | ||
contextMenu?: WithDefaultHelpers<NonNullable<ContextMenuSchemaProviderProps['overrides']>>; | ||
helpMenu?: WithDefaultHelpers<NonNullable<HelpMenuSchemaProviderProps['overrides']>>; | ||
menu?: WithDefaultHelpers<NonNullable<MenuSchemaProviderProps['overrides']>>; | ||
toolbar?: WithDefaultHelpers<NonNullable<ToolbarSchemaProviderProps['overrides']>>; | ||
keyboardShortcutsMenu?: WithDefaultHelpers<NonNullable<KeyboardShortcutsSchemaProviderProps['overrides']>>; | ||
tools?: WithDefaultHelpers<NonNullable<ToolsProviderProps['overrides']>>; | ||
translations?: TranslationProviderProps['overrides']; | ||
contextMenu?: WithDefaultHelpers<NonNullable<TLUiContextMenuSchemaProviderProps['overrides']>>; | ||
helpMenu?: WithDefaultHelpers<NonNullable<TLUiHelpMenuSchemaProviderProps['overrides']>>; | ||
menu?: WithDefaultHelpers<NonNullable<TLUiMenuSchemaProviderProps['overrides']>>; | ||
toolbar?: WithDefaultHelpers<NonNullable<TLUiToolbarSchemaProviderProps['overrides']>>; | ||
keyboardShortcutsMenu?: WithDefaultHelpers<NonNullable<TLUiKeyboardShortcutsSchemaProviderProps['overrides']>>; | ||
tools?: WithDefaultHelpers<NonNullable<TLUiToolsProviderProps['overrides']>>; | ||
translations?: TLUiTranslationProviderProps['overrides']; | ||
} | ||
/** @public */ | ||
export declare type TLListedTranslation = { | ||
readonly locale: string; | ||
readonly label: string; | ||
export declare type TLUiSubMenu = { | ||
id: string; | ||
type: 'submenu'; | ||
label: TLUiTranslationKey; | ||
disabled: boolean; | ||
readonlyOk: boolean; | ||
children: TLUiMenuChild[]; | ||
}; | ||
/** @public */ | ||
export declare type TLListedTranslations = readonly TLListedTranslation[]; | ||
/** @public */ | ||
export declare interface TLToast { | ||
export declare interface TLUiToast { | ||
id: string; | ||
@@ -750,3 +614,3 @@ icon?: string; | ||
description?: string; | ||
actions?: TLToastAction[]; | ||
actions?: TLUiToastAction[]; | ||
keepOpen?: boolean; | ||
@@ -757,3 +621,3 @@ closeLabel?: string; | ||
/** @public */ | ||
export declare interface TLToastAction { | ||
export declare interface TLUiToastAction { | ||
type: 'primary' | 'secondary' | 'warn'; | ||
@@ -765,73 +629,27 @@ label: string; | ||
/** @public */ | ||
export declare type TLTranslation = { | ||
readonly locale: string; | ||
readonly label: string; | ||
readonly messages: TLTranslationMessages; | ||
}; | ||
/** @public */ | ||
export declare type TLTranslationKey = 'action.align-bottom' | 'action.align-center-horizontal.short' | 'action.align-center-horizontal' | 'action.align-center-vertical.short' | 'action.align-center-vertical' | 'action.align-left' | 'action.align-right' | 'action.align-top' | 'action.back-to-content' | 'action.bring-forward' | 'action.bring-to-front' | 'action.convert-to-bookmark' | 'action.convert-to-embed' | 'action.copy-as-json.short' | 'action.copy-as-json' | 'action.copy-as-png.short' | 'action.copy-as-png' | 'action.copy-as-svg.short' | 'action.copy-as-svg' | 'action.copy' | 'action.cut' | 'action.delete' | 'action.distribute-horizontal.short' | 'action.distribute-horizontal' | 'action.distribute-vertical.short' | 'action.distribute-vertical' | 'action.duplicate' | 'action.edit-link' | 'action.exit-pen-mode' | 'action.export-as-json.short' | 'action.export-as-json' | 'action.export-as-png.short' | 'action.export-as-png' | 'action.export-as-svg.short' | 'action.export-as-svg' | 'action.flip-horizontal.short' | 'action.flip-horizontal' | 'action.flip-vertical.short' | 'action.flip-vertical' | 'action.group' | 'action.insert-media' | 'action.new-project' | 'action.new-shared-project' | 'action.open-embed-link' | 'action.open-file' | 'action.pack' | 'action.paste' | 'action.print' | 'action.redo' | 'action.rotate-ccw' | 'action.rotate-cw' | 'action.save-copy' | 'action.select-all' | 'action.select-none' | 'action.send-backward' | 'action.send-to-back' | 'action.share-project' | 'action.stack-horizontal.short' | 'action.stack-horizontal' | 'action.stack-vertical.short' | 'action.stack-vertical' | 'action.stop-following' | 'action.stretch-horizontal.short' | 'action.stretch-horizontal' | 'action.stretch-vertical.short' | 'action.stretch-vertical' | 'action.toggle-auto-size' | 'action.toggle-dark-mode.menu' | 'action.toggle-dark-mode' | 'action.toggle-debug-mode.menu' | 'action.toggle-debug-mode' | 'action.toggle-focus-mode.menu' | 'action.toggle-focus-mode' | 'action.toggle-grid.menu' | 'action.toggle-grid' | 'action.toggle-snap-mode.menu' | 'action.toggle-snap-mode' | 'action.toggle-tool-lock.menu' | 'action.toggle-tool-lock' | 'action.toggle-transparent.context-menu' | 'action.toggle-transparent.menu' | 'action.toggle-transparent' | 'action.undo' | 'action.ungroup' | 'action.zoom-in' | 'action.zoom-out' | 'action.zoom-to-100' | 'action.zoom-to-fit' | 'action.zoom-to-selection' | 'actions-menu.title' | 'align-style.end' | 'align-style.justify' | 'align-style.middle' | 'align-style.start' | 'arrowheadEnd-style.arrow' | 'arrowheadEnd-style.bar' | 'arrowheadEnd-style.diamond' | 'arrowheadEnd-style.dot' | 'arrowheadEnd-style.inverted' | 'arrowheadEnd-style.none' | 'arrowheadEnd-style.pipe' | 'arrowheadEnd-style.square' | 'arrowheadEnd-style.triangle' | 'arrowheadStart-style.arrow' | 'arrowheadStart-style.bar' | 'arrowheadStart-style.diamond' | 'arrowheadStart-style.dot' | 'arrowheadStart-style.inverted' | 'arrowheadStart-style.none' | 'arrowheadStart-style.pipe' | 'arrowheadStart-style.square' | 'arrowheadStart-style.triangle' | 'color-style.black' | 'color-style.blue' | 'color-style.green' | 'color-style.grey' | 'color-style.light-blue' | 'color-style.light-green' | 'color-style.light-red' | 'color-style.light-violet' | 'color-style.orange' | 'color-style.red' | 'color-style.violet' | 'color-style.yellow' | 'context-menu.arrange' | 'context-menu.copy-as' | 'context-menu.export-as' | 'context-menu.move-to-page' | 'context-menu.reorder' | 'context.pages.new-page' | 'dash-style.dashed' | 'dash-style.dotted' | 'dash-style.draw' | 'dash-style.solid' | 'debug-panel.more' | 'edit-link-dialog.cancel' | 'edit-link-dialog.clear' | 'edit-link-dialog.detail' | 'edit-link-dialog.invalid-url' | 'edit-link-dialog.save' | 'edit-link-dialog.title' | 'edit-link-dialog.url' | 'edit-pages-dialog.move-down' | 'edit-pages-dialog.move-up' | 'embed-dialog.back' | 'embed-dialog.cancel' | 'embed-dialog.create' | 'embed-dialog.instruction' | 'embed-dialog.invalid-url' | 'embed-dialog.title' | 'embed-dialog.url-title' | 'embed-dialog.url' | 'file-system.confirm-clear.cancel' | 'file-system.confirm-clear.continue' | 'file-system.confirm-clear.description' | 'file-system.confirm-clear.dont-show-again' | 'file-system.confirm-clear.title' | 'file-system.confirm-open.cancel' | 'file-system.confirm-open.description' | 'file-system.confirm-open.dont-show-again' | 'file-system.confirm-open.open' | 'file-system.confirm-open.title' | 'file-system.file-open-error.file-format-version-too-new' | 'file-system.file-open-error.generic-corrupted-file' | 'file-system.file-open-error.not-a-tldraw-file' | 'file-system.file-open-error.title' | 'file-system.shared-document-file-open-error.description' | 'file-system.shared-document-file-open-error.title' | 'fill-style.none' | 'fill-style.pattern' | 'fill-style.semi' | 'fill-style.solid' | 'focus-mode.toggle-focus-mode' | 'font-style.draw' | 'font-style.mono' | 'font-style.sans' | 'font-style.serif' | 'geo-style.arrow-down' | 'geo-style.arrow-left' | 'geo-style.arrow-right' | 'geo-style.arrow-up' | 'geo-style.diamond' | 'geo-style.ellipse' | 'geo-style.hexagon' | 'geo-style.octagon' | 'geo-style.oval' | 'geo-style.pentagon' | 'geo-style.rectangle' | 'geo-style.rhombus-2' | 'geo-style.rhombus' | 'geo-style.star' | 'geo-style.trapezoid' | 'geo-style.triangle' | 'geo-style.x-box' | 'help-menu.about' | 'help-menu.discord' | 'help-menu.github' | 'help-menu.keyboard-shortcuts' | 'help-menu.title' | 'help-menu.twitter' | 'menu.copy-as' | 'menu.edit' | 'menu.export-as' | 'menu.file' | 'menu.language' | 'menu.preferences' | 'menu.title' | 'menu.view' | 'navigation-zone.toggle-minimap' | 'navigation-zone.zoom' | 'opacity-style.0.1' | 'opacity-style.0.25' | 'opacity-style.0.5' | 'opacity-style.0.75' | 'opacity-style.1' | 'page-menu.create-new-page' | 'page-menu.edit-done' | 'page-menu.edit-start' | 'page-menu.go-to-page' | 'page-menu.max-page-count-reached' | 'page-menu.new-page-initial-name' | 'page-menu.submenu.delete' | 'page-menu.submenu.duplicate-page' | 'page-menu.submenu.move-down' | 'page-menu.submenu.move-up' | 'page-menu.submenu.rename' | 'page-menu.submenu.title' | 'page-menu.title' | 'people-menu.change-color' | 'people-menu.change-name' | 'people-menu.follow' | 'people-menu.invite' | 'people-menu.title' | 'people-menu.user' | 'share-menu.copy-link-note' | 'share-menu.copy-link' | 'share-menu.copy-readonly-link-note' | 'share-menu.copy-readonly-link' | 'share-menu.offline-note' | 'share-menu.project-too-large' | 'share-menu.readonly-link' | 'share-menu.share-project' | 'share-menu.title' | 'shortcuts-dialog.edit' | 'shortcuts-dialog.file' | 'shortcuts-dialog.preferences' | 'shortcuts-dialog.title' | 'shortcuts-dialog.tools' | 'shortcuts-dialog.transform' | 'shortcuts-dialog.view' | 'size-style.l' | 'size-style.m' | 'size-style.s' | 'size-style.xl' | 'spline-style.cubic' | 'spline-style.line' | 'style-panel.align' | 'style-panel.arrowhead-end' | 'style-panel.arrowhead-start' | 'style-panel.arrowheads' | 'style-panel.color' | 'style-panel.dash' | 'style-panel.fill' | 'style-panel.font' | 'style-panel.geo' | 'style-panel.mixed' | 'style-panel.opacity' | 'style-panel.position' | 'style-panel.size' | 'style-panel.spline' | 'style-panel.title' | 'toast.close' | 'toast.error.copy-fail.desc' | 'toast.error.copy-fail.title' | 'toast.error.export-fail.desc' | 'toast.error.export-fail.title' | 'tool-panel.drawing' | 'tool-panel.more' | 'tool-panel.shapes' | 'tool.arrow-down' | 'tool.arrow-left' | 'tool.arrow-right' | 'tool.arrow-up' | 'tool.arrow' | 'tool.asset' | 'tool.diamond' | 'tool.draw' | 'tool.ellipse' | 'tool.embed' | 'tool.eraser' | 'tool.frame' | 'tool.hand' | 'tool.hexagon' | 'tool.line' | 'tool.note' | 'tool.octagon' | 'tool.oval' | 'tool.pentagon' | 'tool.rectangle' | 'tool.rhombus' | 'tool.select' | 'tool.star' | 'tool.text' | 'tool.trapezoid' | 'tool.triangle' | 'tool.x-box' | 'vscode.file-open.backup-failed' | 'vscode.file-open.backup-saved' | 'vscode.file-open.backup' | 'vscode.file-open.desc' | 'vscode.file-open.dont-show-again' | 'vscode.file-open.open'; | ||
/** @public */ | ||
export declare type TLTranslationLocale = TLTranslations[number]['locale']; | ||
/** @public */ | ||
export declare type TLTranslationMessages = Record<TLTranslationKey, string>; | ||
/** @public */ | ||
export declare type TLTranslations = TLTranslation[]; | ||
/** @public */ | ||
export declare type TLUiIconType = 'align-bottom-center' | 'align-bottom-left' | 'align-bottom-right' | 'align-bottom' | 'align-center-center' | 'align-center-horizontal' | 'align-center-left' | 'align-center-right' | 'align-center-vertical' | 'align-left' | 'align-right' | 'align-top-center' | 'align-top-left' | 'align-top-right' | 'align-top' | 'arrow-left' | 'arrowhead-arrow' | 'arrowhead-bar' | 'arrowhead-diamond' | 'arrowhead-dot' | 'arrowhead-none' | 'arrowhead-square' | 'arrowhead-triangle-inverted' | 'arrowhead-triangle' | 'aspect-ratio' | 'avatar' | 'blob' | 'bring-forward' | 'bring-to-front' | 'check' | 'checkbox-checked' | 'checkbox-empty' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'chevrons-ne' | 'chevrons-sw' | 'clipboard-copy' | 'code' | 'collab' | 'color' | 'comment' | 'cross-2' | 'cross' | 'dash-dashed' | 'dash-dotted' | 'dash-draw' | 'dash-solid' | 'discord' | 'distribute-horizontal' | 'distribute-vertical' | 'dot' | 'dots-horizontal' | 'dots-vertical' | 'drag-handle-dots' | 'duplicate' | 'edit' | 'external-link' | 'file' | 'fill-none' | 'fill-pattern' | 'fill-semi' | 'fill-solid' | 'font-draw' | 'font-mono' | 'font-sans' | 'font-serif' | 'geo-arrow-down' | 'geo-arrow-left' | 'geo-arrow-right' | 'geo-arrow-up' | 'geo-diamond' | 'geo-ellipse' | 'geo-hexagon' | 'geo-octagon' | 'geo-oval' | 'geo-pentagon' | 'geo-rectangle' | 'geo-rhombus-2' | 'geo-rhombus' | 'geo-star' | 'geo-trapezoid' | 'geo-triangle' | 'geo-x-box' | 'github' | 'group' | 'hidden' | 'image' | 'info-circle' | 'link' | 'lock-small' | 'lock' | 'menu' | 'minus' | 'mixed' | 'pack' | 'page' | 'plus' | 'question-mark-circle' | 'question-mark' | 'redo' | 'reset-zoom' | 'rotate-ccw' | 'rotate-cw' | 'ruler' | 'search' | 'send-backward' | 'send-to-back' | 'settings-horizontal' | 'settings-vertical-1' | 'settings-vertical' | 'share-1' | 'share-2' | 'size-extra-large' | 'size-large' | 'size-medium' | 'size-small' | 'spline-cubic' | 'spline-line' | 'stack-horizontal' | 'stack-vertical' | 'stretch-horizontal' | 'stretch-vertical' | 'text-align-center' | 'text-align-justify' | 'text-align-left' | 'text-align-right' | 'tool-arrow' | 'tool-embed' | 'tool-eraser' | 'tool-frame' | 'tool-hand' | 'tool-highlighter' | 'tool-line' | 'tool-media' | 'tool-note' | 'tool-pencil' | 'tool-pointer' | 'tool-text' | 'trash' | 'triangle-down' | 'triangle-up' | 'twitter' | 'undo' | 'ungroup' | 'unlock-small' | 'unlock' | 'visible' | 'warning-triangle' | 'zoom-in' | 'zoom-out'; | ||
/** @public */ | ||
export declare const TLUiIconTypes: readonly ["align-bottom-center", "align-bottom-left", "align-bottom-right", "align-bottom", "align-center-center", "align-center-horizontal", "align-center-left", "align-center-right", "align-center-vertical", "align-left", "align-right", "align-top-center", "align-top-left", "align-top-right", "align-top", "arrow-left", "arrowhead-arrow", "arrowhead-bar", "arrowhead-diamond", "arrowhead-dot", "arrowhead-none", "arrowhead-square", "arrowhead-triangle-inverted", "arrowhead-triangle", "aspect-ratio", "avatar", "blob", "bring-forward", "bring-to-front", "check", "checkbox-checked", "checkbox-empty", "chevron-down", "chevron-left", "chevron-right", "chevron-up", "chevrons-ne", "chevrons-sw", "clipboard-copy", "code", "collab", "color", "comment", "cross-2", "cross", "dash-dashed", "dash-dotted", "dash-draw", "dash-solid", "discord", "distribute-horizontal", "distribute-vertical", "dot", "dots-horizontal", "dots-vertical", "drag-handle-dots", "duplicate", "edit", "external-link", "file", "fill-none", "fill-pattern", "fill-semi", "fill-solid", "font-draw", "font-mono", "font-sans", "font-serif", "geo-arrow-down", "geo-arrow-left", "geo-arrow-right", "geo-arrow-up", "geo-diamond", "geo-ellipse", "geo-hexagon", "geo-octagon", "geo-oval", "geo-pentagon", "geo-rectangle", "geo-rhombus-2", "geo-rhombus", "geo-star", "geo-trapezoid", "geo-triangle", "geo-x-box", "github", "group", "hidden", "image", "info-circle", "link", "lock-small", "lock", "menu", "minus", "mixed", "pack", "page", "plus", "question-mark-circle", "question-mark", "redo", "reset-zoom", "rotate-ccw", "rotate-cw", "ruler", "search", "send-backward", "send-to-back", "settings-horizontal", "settings-vertical-1", "settings-vertical", "share-1", "share-2", "size-extra-large", "size-large", "size-medium", "size-small", "spline-cubic", "spline-line", "stack-horizontal", "stack-vertical", "stretch-horizontal", "stretch-vertical", "text-align-center", "text-align-justify", "text-align-left", "text-align-right", "tool-arrow", "tool-embed", "tool-eraser", "tool-frame", "tool-hand", "tool-highlighter", "tool-line", "tool-media", "tool-note", "tool-pencil", "tool-pointer", "tool-text", "trash", "triangle-down", "triangle-up", "twitter", "undo", "ungroup", "unlock-small", "unlock", "visible", "warning-triangle", "zoom-in", "zoom-out"]; | ||
/** @public */ | ||
export declare const ToastsContext: Context<ToastsContextType>; | ||
/** @public */ | ||
export declare type ToastsContextType = { | ||
addToast: (toast: Omit<TLToast, 'id'> & { | ||
export declare type TLUiToastsContextType = { | ||
addToast: (toast: Omit<TLUiToast, 'id'> & { | ||
id?: string; | ||
}) => string; | ||
removeToast: (id: TLToast['id']) => string; | ||
removeToast: (id: TLUiToast['id']) => string; | ||
clearToasts: () => void; | ||
toasts: TLToast[]; | ||
toasts: TLUiToast[]; | ||
}; | ||
/** @public */ | ||
export declare function ToastsProvider({ children }: ToastsProviderProps): JSX.Element; | ||
/** @public */ | ||
export declare type ToastsProviderProps = { | ||
overrides?: (app: App) => ToastsContextType; | ||
children: any; | ||
}; | ||
/** @public */ | ||
export declare type ToolbarItem = { | ||
export declare type TLUiToolbarItem = { | ||
id: string; | ||
type: 'item'; | ||
readonlyOk: boolean; | ||
toolItem: ToolItem; | ||
toolItem: TLUiToolItem; | ||
}; | ||
/** @public */ | ||
export declare function toolbarItem(toolItem: ToolItem): ToolbarItem; | ||
export declare type TLUiToolbarSchemaContextType = TLUiToolbarItem[]; | ||
/** @public */ | ||
export declare const ToolbarSchemaContext: React_2.Context<ToolbarSchemaContextType>; | ||
/** @public */ | ||
export declare type ToolbarSchemaContextType = ToolbarItem[]; | ||
/** @public */ | ||
export declare function ToolbarSchemaProvider({ overrides, children }: ToolbarSchemaProviderProps): JSX.Element; | ||
/** @public */ | ||
export declare type ToolbarSchemaProviderProps = { | ||
overrides?: (app: App, schema: ToolbarSchemaContextType, more: { | ||
tools: ToolsContextType; | ||
}) => ToolbarSchemaContextType; | ||
declare type TLUiToolbarSchemaProviderProps = { | ||
overrides?: (editor: Editor, schema: TLUiToolbarSchemaContextType, more: { | ||
tools: TLUiToolsContextType; | ||
}) => TLUiToolbarSchemaContextType; | ||
children: any; | ||
@@ -841,8 +659,8 @@ }; | ||
/** @public */ | ||
export declare interface ToolItem { | ||
export declare interface TLUiToolItem { | ||
id: string; | ||
label: TLTranslationKey; | ||
shortcutsLabel?: TLTranslationKey; | ||
label: TLUiTranslationKey; | ||
shortcutsLabel?: TLUiTranslationKey; | ||
icon: TLUiIconType; | ||
onSelect: () => void; | ||
onSelect: (source: TLUiEventSource) => void; | ||
kbd?: string; | ||
@@ -856,15 +674,9 @@ readonlyOk: boolean; | ||
/** @public */ | ||
export declare const ToolsContext: React_3.Context<ToolsContextType>; | ||
export declare type TLUiToolsContextType = Record<string, TLUiToolItem>; | ||
/** @public */ | ||
export declare type ToolsContextType = Record<string, ToolItem>; | ||
/** @public */ | ||
export declare function ToolsProvider({ overrides, children }: ToolsProviderProps): JSX.Element; | ||
/** @public */ | ||
export declare type ToolsProviderProps = { | ||
overrides?: (app: App, tools: ToolsContextType, helpers: { | ||
export declare type TLUiToolsProviderProps = { | ||
overrides?: (editor: Editor, tools: TLUiToolsContextType, helpers: { | ||
insertMedia: () => void; | ||
}) => ToolsContextType; | ||
}) => TLUiToolsContextType; | ||
children: any; | ||
@@ -874,13 +686,16 @@ }; | ||
/** @public */ | ||
export declare function toStartCase(str: string): string; | ||
export declare type TLUiTranslation = { | ||
readonly locale: string; | ||
readonly label: string; | ||
readonly messages: Record<TLUiTranslationKey, string>; | ||
}; | ||
/** | ||
* Provides a translation context to the app. | ||
* | ||
* @public | ||
*/ | ||
export declare const TranslationProvider: MemoExoticComponent<({ overrides, children, }: TranslationProviderProps) => JSX.Element>; | ||
/** @public */ | ||
export declare type TLUiTranslationContextType = TLUiTranslation; | ||
/** @public */ | ||
export declare interface TranslationProviderProps { | ||
export declare type TLUiTranslationKey = 'action.align-bottom' | 'action.align-center-horizontal.short' | 'action.align-center-horizontal' | 'action.align-center-vertical.short' | 'action.align-center-vertical' | 'action.align-left' | 'action.align-right' | 'action.align-top' | 'action.back-to-content' | 'action.bring-forward' | 'action.bring-to-front' | 'action.convert-to-bookmark' | 'action.convert-to-embed' | 'action.copy-as-json.short' | 'action.copy-as-json' | 'action.copy-as-png.short' | 'action.copy-as-png' | 'action.copy-as-svg.short' | 'action.copy-as-svg' | 'action.copy' | 'action.cut' | 'action.delete' | 'action.distribute-horizontal.short' | 'action.distribute-horizontal' | 'action.distribute-vertical.short' | 'action.distribute-vertical' | 'action.duplicate' | 'action.edit-link' | 'action.exit-pen-mode' | 'action.export-as-json.short' | 'action.export-as-json' | 'action.export-as-png.short' | 'action.export-as-png' | 'action.export-as-svg.short' | 'action.export-as-svg' | 'action.flip-horizontal.short' | 'action.flip-horizontal' | 'action.flip-vertical.short' | 'action.flip-vertical' | 'action.fork-project' | 'action.group' | 'action.insert-embed' | 'action.insert-media' | 'action.leave-shared-project' | 'action.new-project' | 'action.new-shared-project' | 'action.open-cursor-chat' | 'action.open-embed-link' | 'action.open-file' | 'action.pack' | 'action.paste' | 'action.print' | 'action.redo' | 'action.rotate-ccw' | 'action.rotate-cw' | 'action.save-copy' | 'action.select-all' | 'action.select-none' | 'action.send-backward' | 'action.send-to-back' | 'action.share-project' | 'action.stack-horizontal.short' | 'action.stack-horizontal' | 'action.stack-vertical.short' | 'action.stack-vertical' | 'action.stop-following' | 'action.stretch-horizontal.short' | 'action.stretch-horizontal' | 'action.stretch-vertical.short' | 'action.stretch-vertical' | 'action.toggle-auto-size' | 'action.toggle-dark-mode.menu' | 'action.toggle-dark-mode' | 'action.toggle-debug-mode.menu' | 'action.toggle-debug-mode' | 'action.toggle-focus-mode.menu' | 'action.toggle-focus-mode' | 'action.toggle-grid.menu' | 'action.toggle-grid' | 'action.toggle-lock' | 'action.toggle-reduce-motion.menu' | 'action.toggle-reduce-motion' | 'action.toggle-snap-mode.menu' | 'action.toggle-snap-mode' | 'action.toggle-tool-lock.menu' | 'action.toggle-tool-lock' | 'action.toggle-transparent.context-menu' | 'action.toggle-transparent.menu' | 'action.toggle-transparent' | 'action.undo' | 'action.ungroup' | 'action.zoom-in' | 'action.zoom-out' | 'action.zoom-to-100' | 'action.zoom-to-fit' | 'action.zoom-to-selection' | 'actions-menu.title' | 'align-style.end' | 'align-style.justify' | 'align-style.middle' | 'align-style.start' | 'arrowheadEnd-style.arrow' | 'arrowheadEnd-style.bar' | 'arrowheadEnd-style.diamond' | 'arrowheadEnd-style.dot' | 'arrowheadEnd-style.inverted' | 'arrowheadEnd-style.none' | 'arrowheadEnd-style.pipe' | 'arrowheadEnd-style.square' | 'arrowheadEnd-style.triangle' | 'arrowheadStart-style.arrow' | 'arrowheadStart-style.bar' | 'arrowheadStart-style.diamond' | 'arrowheadStart-style.dot' | 'arrowheadStart-style.inverted' | 'arrowheadStart-style.none' | 'arrowheadStart-style.pipe' | 'arrowheadStart-style.square' | 'arrowheadStart-style.triangle' | 'color-style.black' | 'color-style.blue' | 'color-style.green' | 'color-style.grey' | 'color-style.light-blue' | 'color-style.light-green' | 'color-style.light-red' | 'color-style.light-violet' | 'color-style.orange' | 'color-style.red' | 'color-style.violet' | 'color-style.yellow' | 'context-menu.arrange' | 'context-menu.copy-as' | 'context-menu.export-as' | 'context-menu.move-to-page' | 'context-menu.reorder' | 'context.pages.new-page' | 'cursor-chat.type-to-chat' | 'dash-style.dashed' | 'dash-style.dotted' | 'dash-style.draw' | 'dash-style.solid' | 'debug-panel.more' | 'edit-link-dialog.cancel' | 'edit-link-dialog.clear' | 'edit-link-dialog.detail' | 'edit-link-dialog.invalid-url' | 'edit-link-dialog.save' | 'edit-link-dialog.title' | 'edit-link-dialog.url' | 'edit-pages-dialog.move-down' | 'edit-pages-dialog.move-up' | 'embed-dialog.back' | 'embed-dialog.cancel' | 'embed-dialog.create' | 'embed-dialog.instruction' | 'embed-dialog.invalid-url' | 'embed-dialog.title' | 'embed-dialog.url' | 'file-system.confirm-clear.cancel' | 'file-system.confirm-clear.continue' | 'file-system.confirm-clear.description' | 'file-system.confirm-clear.dont-show-again' | 'file-system.confirm-clear.title' | 'file-system.confirm-open.cancel' | 'file-system.confirm-open.description' | 'file-system.confirm-open.dont-show-again' | 'file-system.confirm-open.open' | 'file-system.confirm-open.title' | 'file-system.file-open-error.file-format-version-too-new' | 'file-system.file-open-error.generic-corrupted-file' | 'file-system.file-open-error.not-a-tldraw-file' | 'file-system.file-open-error.title' | 'file-system.shared-document-file-open-error.description' | 'file-system.shared-document-file-open-error.title' | 'fill-style.none' | 'fill-style.pattern' | 'fill-style.semi' | 'fill-style.solid' | 'focus-mode.toggle-focus-mode' | 'font-style.draw' | 'font-style.mono' | 'font-style.sans' | 'font-style.serif' | 'geo-style.arrow-down' | 'geo-style.arrow-left' | 'geo-style.arrow-right' | 'geo-style.arrow-up' | 'geo-style.check-box' | 'geo-style.diamond' | 'geo-style.ellipse' | 'geo-style.hexagon' | 'geo-style.octagon' | 'geo-style.oval' | 'geo-style.pentagon' | 'geo-style.rectangle' | 'geo-style.rhombus-2' | 'geo-style.rhombus' | 'geo-style.star' | 'geo-style.trapezoid' | 'geo-style.triangle' | 'geo-style.x-box' | 'help-menu.about' | 'help-menu.discord' | 'help-menu.github' | 'help-menu.keyboard-shortcuts' | 'help-menu.title' | 'help-menu.twitter' | 'home-project-dialog.description' | 'home-project-dialog.ok' | 'home-project-dialog.title' | 'menu.copy-as' | 'menu.edit' | 'menu.export-as' | 'menu.file' | 'menu.language' | 'menu.preferences' | 'menu.title' | 'menu.view' | 'navigation-zone.toggle-minimap' | 'navigation-zone.zoom' | 'opacity-style.0.1' | 'opacity-style.0.25' | 'opacity-style.0.5' | 'opacity-style.0.75' | 'opacity-style.1' | 'page-menu.create-new-page' | 'page-menu.edit-done' | 'page-menu.edit-start' | 'page-menu.go-to-page' | 'page-menu.max-page-count-reached' | 'page-menu.new-page-initial-name' | 'page-menu.submenu.delete' | 'page-menu.submenu.duplicate-page' | 'page-menu.submenu.move-down' | 'page-menu.submenu.move-up' | 'page-menu.submenu.rename' | 'page-menu.submenu.title' | 'page-menu.title' | 'people-menu.change-color' | 'people-menu.change-name' | 'people-menu.follow' | 'people-menu.following' | 'people-menu.invite' | 'people-menu.leading' | 'people-menu.title' | 'people-menu.user' | 'rename-project-dialog.cancel' | 'rename-project-dialog.rename' | 'rename-project-dialog.title' | 'share-menu.copy-link-note' | 'share-menu.copy-link' | 'share-menu.copy-readonly-link-note' | 'share-menu.copy-readonly-link' | 'share-menu.create-snapshot-link' | 'share-menu.default-project-name' | 'share-menu.fork-note' | 'share-menu.offline-note' | 'share-menu.project-too-large' | 'share-menu.readonly-link' | 'share-menu.save-note' | 'share-menu.share-project' | 'share-menu.snapshot-link-note' | 'share-menu.title' | 'share-menu.upload-failed' | 'sharing.confirm-leave.cancel' | 'sharing.confirm-leave.description' | 'sharing.confirm-leave.dont-show-again' | 'sharing.confirm-leave.leave' | 'sharing.confirm-leave.title' | 'shortcuts-dialog.collaboration' | 'shortcuts-dialog.edit' | 'shortcuts-dialog.file' | 'shortcuts-dialog.preferences' | 'shortcuts-dialog.title' | 'shortcuts-dialog.tools' | 'shortcuts-dialog.transform' | 'shortcuts-dialog.view' | 'size-style.l' | 'size-style.m' | 'size-style.s' | 'size-style.xl' | 'spline-style.cubic' | 'spline-style.line' | 'style-panel.align' | 'style-panel.arrowhead-end' | 'style-panel.arrowhead-start' | 'style-panel.arrowheads' | 'style-panel.color' | 'style-panel.dash' | 'style-panel.fill' | 'style-panel.font' | 'style-panel.geo' | 'style-panel.mixed' | 'style-panel.opacity' | 'style-panel.position' | 'style-panel.size' | 'style-panel.spline' | 'style-panel.title' | 'style-panel.vertical-align' | 'toast.close' | 'toast.error.copy-fail.desc' | 'toast.error.copy-fail.title' | 'toast.error.export-fail.desc' | 'toast.error.export-fail.title' | 'tool-panel.drawing' | 'tool-panel.more' | 'tool-panel.shapes' | 'tool.arrow-down' | 'tool.arrow-left' | 'tool.arrow-right' | 'tool.arrow-up' | 'tool.arrow' | 'tool.asset' | 'tool.check-box' | 'tool.diamond' | 'tool.draw' | 'tool.ellipse' | 'tool.embed' | 'tool.eraser' | 'tool.frame' | 'tool.hand' | 'tool.hexagon' | 'tool.highlight' | 'tool.laser' | 'tool.line' | 'tool.note' | 'tool.octagon' | 'tool.oval' | 'tool.pentagon' | 'tool.rectangle' | 'tool.rhombus' | 'tool.select' | 'tool.star' | 'tool.text' | 'tool.trapezoid' | 'tool.triangle' | 'tool.x-box' | 'vscode.file-open.backup-failed' | 'vscode.file-open.backup-saved' | 'vscode.file-open.backup' | 'vscode.file-open.desc' | 'vscode.file-open.dont-show-again' | 'vscode.file-open.open'; | ||
/** @public */ | ||
declare interface TLUiTranslationProviderProps { | ||
children: any; | ||
@@ -893,3 +708,3 @@ /** | ||
* ```ts | ||
* ;<TranslationProvider overrides={{ en: { 'style-panel.styles': 'Properties' } }} /> | ||
* <TranslationProvider overrides={{ en: { 'style-panel.styles': 'Properties' } }} /> | ||
* ``` | ||
@@ -901,32 +716,19 @@ */ | ||
/** @public */ | ||
declare function Trigger({ children, 'data-wd': dataWd }: { | ||
export declare function toolbarItem(toolItem: TLUiToolItem): TLUiToolbarItem; | ||
/** @public */ | ||
declare function Trigger({ children, 'data-testid': testId, }: { | ||
children: any; | ||
'data-wd'?: string; | ||
'data-testid'?: string; | ||
}): JSX.Element; | ||
declare type UiAssetUrls = EditorAssetUrls & { | ||
icons: Record<TLUiIconType, string>; | ||
translations: Record<(typeof LANGUAGES)[number]['locale'], string>; | ||
embedIcons: Record<(typeof EMBED_DEFINITIONS)[number]['type'], string>; | ||
}; | ||
/** @public */ | ||
export declare function useActions(): ActionsContextType; | ||
export declare function useActions(): TLUiActionsContextType; | ||
/** @public */ | ||
export declare function useActionsMenuSchema(): MenuSchema; | ||
export declare function useActionsMenuSchema(): TLUiMenuSchema; | ||
/** @public */ | ||
export declare const useAllowGroup: () => boolean; | ||
/* Excluded from this release type: useAssetUrls */ | ||
/** @public */ | ||
export declare const useAllowUngroup: () => boolean; | ||
/** @public */ | ||
export declare function useAppEvents(): void; | ||
/** @public */ | ||
export declare function useAssetUrls(): UiAssetUrls; | ||
/** @public */ | ||
export declare function useBreakpoint(): number; | ||
@@ -941,3 +743,3 @@ | ||
/** @public */ | ||
export declare function useContextMenuSchema(): MenuSchema; | ||
export declare function useContextMenuSchema(): TLUiMenuSchema; | ||
@@ -949,3 +751,3 @@ /** @public */ | ||
export declare function useDefaultHelpers(): { | ||
addToast: (toast: Omit<TLToast, "id"> & { | ||
addToast: (toast: Omit<TLUiToast, "id"> & { | ||
id?: string | undefined; | ||
@@ -955,3 +757,3 @@ }) => string; | ||
clearToasts: () => void; | ||
addDialog: (dialog: Omit<TLDialog, "id"> & { | ||
addDialog: (dialog: Omit<TLUiDialog, "id"> & { | ||
id?: string | undefined; | ||
@@ -961,4 +763,4 @@ }) => string; | ||
removeDialog: (id: string) => string; | ||
updateDialog: (id: string, newDialogData: Partial<TLDialog>) => string; | ||
msg: (id: TLTranslationKey) => string; | ||
updateDialog: (id: string, newDialogData: Partial<TLUiDialog>) => string; | ||
msg: (id: TLUiTranslationKey) => string; | ||
isMobile: boolean; | ||
@@ -968,12 +770,12 @@ }; | ||
/** @public */ | ||
export declare function useDialogs(): DialogsContextType; | ||
export declare function useDialogs(): TLUiDialogsContextType; | ||
/** @public */ | ||
export declare function useExportAs(): (ids?: TLShapeId[], format?: TLExportType) => Promise<void>; | ||
export declare function useEvents(): TLUiEventContextType; | ||
/** @public */ | ||
export declare function useHelpMenuSchema(): MenuSchema; | ||
export declare function useExportAs(): (ids?: TLShapeId[], format?: TLExportType) => Promise<void>; | ||
/** @public */ | ||
export declare function useHighDpiCanvas(ref: React.RefObject<HTMLCanvasElement>, dpr: number): void; | ||
export declare function useHelpMenuSchema(): TLUiMenuSchema; | ||
@@ -984,110 +786,5 @@ /** @public */ | ||
/** @public */ | ||
export declare function useKeyboardShortcutsSchema(): KeyboardShortcutsSchemaContextType; | ||
export declare function useKeyboardShortcutsSchema(): TLUiKeyboardShortcutsSchemaContextType; | ||
/** @public */ | ||
export declare function useLanguages(): { | ||
languages: readonly [{ | ||
readonly locale: "ar"; | ||
readonly label: "عربي"; | ||
}, { | ||
readonly locale: "ca"; | ||
readonly label: "Català"; | ||
}, { | ||
readonly locale: "da"; | ||
readonly label: "Danish"; | ||
}, { | ||
readonly locale: "de"; | ||
readonly label: "Deutsch"; | ||
}, { | ||
readonly locale: "en"; | ||
readonly label: "English"; | ||
}, { | ||
readonly locale: "es"; | ||
readonly label: "Español"; | ||
}, { | ||
readonly locale: "fa"; | ||
readonly label: "فارسی"; | ||
}, { | ||
readonly locale: "fi"; | ||
readonly label: "Suomi"; | ||
}, { | ||
readonly locale: "fr"; | ||
readonly label: "Français"; | ||
}, { | ||
readonly locale: "gl"; | ||
readonly label: "Galego"; | ||
}, { | ||
readonly locale: "he"; | ||
readonly label: "עברית"; | ||
}, { | ||
readonly locale: "it"; | ||
readonly label: "Italiano"; | ||
}, { | ||
readonly locale: "ja"; | ||
readonly label: "日本語"; | ||
}, { | ||
readonly locale: "ko-kr"; | ||
readonly label: "한국어"; | ||
}, { | ||
readonly locale: "ku"; | ||
readonly label: "کوردی"; | ||
}, { | ||
readonly locale: "hi-in"; | ||
readonly label: "हिन्दी"; | ||
}, { | ||
readonly locale: "hu"; | ||
readonly label: "Magyar"; | ||
}, { | ||
readonly locale: "my"; | ||
readonly label: "မြန်မာစာ"; | ||
}, { | ||
readonly locale: "ne"; | ||
readonly label: "नेपाली"; | ||
}, { | ||
readonly locale: "no"; | ||
readonly label: "Norwegian"; | ||
}, { | ||
readonly locale: "pl"; | ||
readonly label: "Polski"; | ||
}, { | ||
readonly locale: "pt-br"; | ||
readonly label: "Português - Brasil"; | ||
}, { | ||
readonly locale: "pt-pt"; | ||
readonly label: "Português - Europeu"; | ||
}, { | ||
readonly locale: "ro"; | ||
readonly label: "Română"; | ||
}, { | ||
readonly locale: "ru"; | ||
readonly label: "Russian"; | ||
}, { | ||
readonly locale: "sv"; | ||
readonly label: "Svenska"; | ||
}, { | ||
readonly locale: "te"; | ||
readonly label: "తెలుగు"; | ||
}, { | ||
readonly locale: "th"; | ||
readonly label: "ภาษาไทย"; | ||
}, { | ||
readonly locale: "tr"; | ||
readonly label: "Türkçe"; | ||
}, { | ||
readonly locale: "uk"; | ||
readonly label: "Ukrainian"; | ||
}, { | ||
readonly locale: "vi"; | ||
readonly label: "Tiếng Việt"; | ||
}, { | ||
readonly locale: "zh-cn"; | ||
readonly label: "Chinese - Simplified"; | ||
}, { | ||
readonly locale: "zh-tw"; | ||
readonly label: "繁體中文 (台灣)"; | ||
}]; | ||
currentLanguage: string; | ||
}; | ||
/** @public */ | ||
export declare function useLocalStorageState<T = any>(key: string, defaultValue: T): readonly [T, (setter: ((value: T) => T) | T) => void]; | ||
@@ -1097,12 +794,12 @@ | ||
export declare function useMenuClipboardEvents(): { | ||
copy: () => void; | ||
cut: () => void; | ||
paste: (data: ClipboardItem[] | DataTransfer, point?: VecLike) => Promise<void>; | ||
copy: (source: TLUiEventSource) => void; | ||
cut: (source: TLUiEventSource) => void; | ||
paste: (data: ClipboardItem[] | DataTransfer, source: TLUiEventSource, point?: VecLike) => Promise<void>; | ||
}; | ||
/** @public */ | ||
export declare function useMenuIsOpen(id: string, cb?: (isOpen: boolean) => void): (isOpen: boolean) => void; | ||
export declare function useMenuIsOpen(id: string, cb?: (isOpen: boolean) => void): readonly [boolean, (isOpen: boolean) => void]; | ||
/** @public */ | ||
export declare function useMenuSchema(): MenuSchema; | ||
export declare function useMenuSchema(): TLUiMenuSchema; | ||
@@ -1113,19 +810,13 @@ /** @public */ | ||
/** @public */ | ||
export declare function usePrint(): () => Promise<void>; | ||
/** @public */ | ||
export declare function useReadonly(): boolean; | ||
/** @public */ | ||
export declare const useThreeStackableItems: () => boolean; | ||
export declare function useToasts(): TLUiToastsContextType; | ||
/** @public */ | ||
export declare function useToasts(): ToastsContextType; | ||
export declare function useToolbarSchema(): TLUiToolbarSchemaContextType; | ||
/** @public */ | ||
export declare function useToolbarSchema(): ToolbarSchemaContextType; | ||
export declare function useTools(): TLUiToolsContextType; | ||
/** @public */ | ||
export declare function useTools(): ToolsContextType; | ||
/** | ||
@@ -1143,6 +834,6 @@ * Returns a function to translate a translation key into a string based on the current translation. | ||
*/ | ||
export declare function useTranslation(): (id: TLTranslationKey) => string; | ||
export declare function useTranslation(): (id: TLUiTranslationKey) => string; | ||
declare type WithDefaultHelpers<T extends TldrawUiOverride<any, any>> = T extends TldrawUiOverride<infer Type, infer Helpers> ? TldrawUiOverride<Type, Helpers extends undefined ? DefaultHelpers : Helpers & DefaultHelpers> : never; | ||
declare type WithDefaultHelpers<T extends TLUiOverride<any, any>> = T extends TLUiOverride<infer Type, infer Helpers> ? TLUiOverride<Type, Helpers extends undefined ? DefaultHelpers : Helpers & DefaultHelpers> : never; | ||
export { } |
@@ -31,53 +31,14 @@ "use strict"; | ||
__export(src_exports, { | ||
ActionsContext: () => import_useActions.ActionsContext, | ||
ActionsMenuSchemaContext: () => import_useActionsMenuSchema.ActionsMenuSchemaContext, | ||
ActionsMenuSchemaProvider: () => import_useActionsMenuSchema.ActionsMenuSchemaProvider, | ||
ActionsProvider: () => import_useActions.ActionsProvider, | ||
AssetUrlsProvider: () => import_useAssetUrls.AssetUrlsProvider, | ||
BASE_URL: () => import_shared.BASE_URL, | ||
BreakPointProvider: () => import_useBreakpoint.BreakPointProvider, | ||
Button: () => import_Button.Button, | ||
ButtonPicker: () => import_ButtonPicker.ButtonPicker, | ||
ContextMenu: () => import_ContextMenu.ContextMenu, | ||
ContextMenuSchemaContext: () => import_useContextMenuSchema.ContextMenuSchemaContext, | ||
ContextMenuSchemaProvider: () => import_useContextMenuSchema.ContextMenuSchemaProvider, | ||
DebugPanel: () => import_DebugPanel.DebugPanel, | ||
Dialog: () => Dialog, | ||
DialogsContext: () => import_useDialogsProvider.DialogsContext, | ||
DialogsProvider: () => import_useDialogsProvider.DialogsProvider, | ||
DropdownMenu: () => DropdownMenu, | ||
EN_TRANSLATION: () => import_translations.EN_TRANSLATION, | ||
HTMLCanvas: () => import_HTMLCanvas.HTMLCanvas, | ||
HelpMenu: () => import_HelpMenu.HelpMenu, | ||
HelpMenuSchemaContext: () => import_useHelpMenuSchema.HelpMenuSchemaContext, | ||
HelpMenuSchemaProvider: () => import_useHelpMenuSchema.HelpMenuSchemaProvider, | ||
Icon: () => import_Icon.Icon, | ||
Input: () => import_Input.Input, | ||
Kbd: () => import_Kbd.Kbd, | ||
KeyboardShortcutsSchemaContext: () => import_useKeyboardShortcutsSchema.KeyboardShortcutsSchemaContext, | ||
KeyboardShortcutsSchemaProvider: () => import_useKeyboardShortcutsSchema.KeyboardShortcutsSchemaProvider, | ||
MenuSchemaContext: () => import_useMenuSchema.MenuSchemaContext, | ||
MenuSchemaProvider: () => import_useMenuSchema.MenuSchemaProvider, | ||
NavigationZone: () => import_NavigationZone.NavigationZone, | ||
Slider: () => import_Slider.Slider, | ||
StylePanel: () => import_StylePanel.StylePanel, | ||
TLUiIconTypes: () => import_icon_types.TLUiIconTypes, | ||
TldrawUi: () => import_TldrawUi.TldrawUi, | ||
TldrawUiContent: () => import_TldrawUi.TldrawUiContent, | ||
TldrawUiContextProvider: () => import_TldrawUiContextProvider.TldrawUiContextProvider, | ||
ToastsContext: () => import_useToastsProvider.ToastsContext, | ||
ToastsProvider: () => import_useToastsProvider.ToastsProvider, | ||
ToolbarSchemaContext: () => import_useToolbarSchema.ToolbarSchemaContext, | ||
ToolbarSchemaProvider: () => import_useToolbarSchema.ToolbarSchemaProvider, | ||
ToolsContext: () => import_useTools.ToolsContext, | ||
ToolsProvider: () => import_useTools.ToolsProvider, | ||
TranslationProvider: () => import_useTranslation.TranslationProvider, | ||
compactMenuItems: () => import_menuHelpers.compactMenuItems, | ||
fetchTranslation: () => import_translations.fetchTranslation, | ||
findMenuItem: () => import_menuHelpers.findMenuItem, | ||
getBaseUrl: () => import_shared.getBaseUrl, | ||
getTranslation: () => import_translations.getTranslation, | ||
isActiveToolItem: () => import_menuHelpers.isActiveToolItem, | ||
kbd: () => import_shared.kbd, | ||
kbdStr: () => import_shared.kbdStr, | ||
menuCustom: () => import_menuHelpers.menuCustom, | ||
@@ -87,10 +48,6 @@ menuGroup: () => import_menuHelpers.menuGroup, | ||
menuSubmenu: () => import_menuHelpers.menuSubmenu, | ||
showUiPaste: () => import_menuHelpers.showUiPaste, | ||
toStartCase: () => import_shared.toStartCase, | ||
setDefaultUiAssetUrls: () => import_assetUrls.setDefaultUiAssetUrls, | ||
toolbarItem: () => import_useToolbarSchema.toolbarItem, | ||
useActions: () => import_useActions.useActions, | ||
useActionsMenuSchema: () => import_useActionsMenuSchema.useActionsMenuSchema, | ||
useAllowGroup: () => import_menuHelpers.useAllowGroup, | ||
useAllowUngroup: () => import_menuHelpers.useAllowUngroup, | ||
useAppEvents: () => import_useAppEvents.useAppEvents, | ||
useAssetUrls: () => import_useAssetUrls.useAssetUrls, | ||
@@ -104,8 +61,7 @@ useBreakpoint: () => import_useBreakpoint.useBreakpoint, | ||
useDialogs: () => import_useDialogsProvider.useDialogs, | ||
useEvents: () => import_useEventsProvider.useEvents, | ||
useExportAs: () => import_useExportAs.useExportAs, | ||
useHelpMenuSchema: () => import_useHelpMenuSchema.useHelpMenuSchema, | ||
useHighDpiCanvas: () => import_useHighDpiCanvas.useHighDpiCanvas, | ||
useKeyboardShortcuts: () => import_useKeyboardShortcuts.useKeyboardShortcuts, | ||
useKeyboardShortcutsSchema: () => import_useKeyboardShortcutsSchema.useKeyboardShortcutsSchema, | ||
useLanguages: () => import_useLanguages.useLanguages, | ||
useLocalStorageState: () => import_useLocalStorageState.useLocalStorageState, | ||
@@ -116,5 +72,3 @@ useMenuClipboardEvents: () => import_useClipboardEvents.useMenuClipboardEvents, | ||
useNativeClipboardEvents: () => import_useClipboardEvents.useNativeClipboardEvents, | ||
usePrint: () => import_usePrint.usePrint, | ||
useReadonly: () => import_useReadonly.useReadonly, | ||
useThreeStackableItems: () => import_menuHelpers.useThreeStackableItems, | ||
useToasts: () => import_useToastsProvider.useToasts, | ||
@@ -130,19 +84,10 @@ useToolbarSchema: () => import_useToolbarSchema.useToolbarSchema, | ||
var import_TldrawUiContextProvider = require("./lib/TldrawUiContextProvider"); | ||
var import_assetUrls = require("./lib/assetUrls"); | ||
var import_ContextMenu = require("./lib/components/ContextMenu"); | ||
var import_DebugPanel = require("./lib/components/DebugPanel"); | ||
var import_HTMLCanvas = require("./lib/components/HTMLCanvas"); | ||
var import_HelpMenu = require("./lib/components/HelpMenu"); | ||
var import_NavigationZone = require("./lib/components/NavigationZone/NavigationZone"); | ||
var import_StylePanel = require("./lib/components/StylePanel/StylePanel"); | ||
var import_Button = require("./lib/components/primitives/Button"); | ||
var import_ButtonPicker = require("./lib/components/primitives/ButtonPicker"); | ||
var import_Icon = require("./lib/components/primitives/Icon"); | ||
var import_Input = require("./lib/components/primitives/Input"); | ||
var import_Kbd = require("./lib/components/primitives/Kbd"); | ||
var import_Slider = require("./lib/components/primitives/Slider"); | ||
var import_shared = require("./lib/components/primitives/shared"); | ||
var import_menuHelpers = require("./lib/hooks/menuHelpers"); | ||
var import_useActions = require("./lib/hooks/useActions"); | ||
var import_useActionsMenuSchema = require("./lib/hooks/useActionsMenuSchema"); | ||
var import_useAppEvents = require("./lib/hooks/useAppEvents"); | ||
var import_useAssetUrls = require("./lib/hooks/useAssetUrls"); | ||
@@ -156,5 +101,5 @@ var import_useBreakpoint = require("./lib/hooks/useBreakpoint"); | ||
var import_useDialogsProvider = require("./lib/hooks/useDialogsProvider"); | ||
var import_useEventsProvider = require("./lib/hooks/useEventsProvider"); | ||
var import_useExportAs = require("./lib/hooks/useExportAs"); | ||
var import_useHelpMenuSchema = require("./lib/hooks/useHelpMenuSchema"); | ||
var import_useHighDpiCanvas = require("./lib/hooks/useHighDpiCanvas"); | ||
var import_useKeyboardShortcuts = require("./lib/hooks/useKeyboardShortcuts"); | ||
@@ -165,3 +110,2 @@ var import_useKeyboardShortcutsSchema = require("./lib/hooks/useKeyboardShortcutsSchema"); | ||
var import_useMenuSchema = require("./lib/hooks/useMenuSchema"); | ||
var import_usePrint = require("./lib/hooks/usePrint"); | ||
var import_useReadonly = require("./lib/hooks/useReadonly"); | ||
@@ -171,7 +115,4 @@ var import_useToastsProvider = require("./lib/hooks/useToastsProvider"); | ||
var import_useTools = require("./lib/hooks/useTools"); | ||
var import_translations = require("./lib/hooks/useTranslation/translations"); | ||
var import_useLanguages = require("./lib/hooks/useTranslation/useLanguages"); | ||
var import_useTranslation = require("./lib/hooks/useTranslation/useTranslation"); | ||
var import_icon_types = require("./lib/icon-types"); | ||
var import_overrides = require("./lib/overrides"); | ||
//# sourceMappingURL=index.js.map |
@@ -21,20 +21,47 @@ "use strict"; | ||
__export(assetUrls_exports, { | ||
defaultUiAssetUrls: () => defaultUiAssetUrls | ||
defaultUiAssetUrls: () => defaultUiAssetUrls, | ||
setDefaultUiAssetUrls: () => setDefaultUiAssetUrls, | ||
useDefaultUiAssetUrlsWithOverrides: () => useDefaultUiAssetUrlsWithOverrides | ||
}); | ||
module.exports = __toCommonJS(assetUrls_exports); | ||
var import_editor = require("@tldraw/editor"); | ||
var import_languages = require("./hooks/useTranslation/languages"); | ||
var import_version = require("../version"); | ||
var import_icon_types = require("./icon-types"); | ||
const defaultUiAssetUrls = { | ||
let defaultUiAssetUrls = { | ||
...import_editor.defaultEditorAssetUrls, | ||
icons: Object.fromEntries( | ||
import_icon_types.TLUiIconTypes.map((name) => [name, `/icons/icon/${name}.svg`]) | ||
import_icon_types.iconTypes.map((name) => [ | ||
name, | ||
`https://unpkg.com/@tldraw/assets@${import_version.version}/icons/icon/${name}.svg` | ||
]) | ||
), | ||
translations: Object.fromEntries( | ||
import_languages.LANGUAGES.map((lang) => [lang.locale, `/translations/${lang.locale}.json`]) | ||
import_editor.LANGUAGES.map((lang) => [ | ||
lang.locale, | ||
`https://unpkg.com/@tldraw/assets@${import_version.version}/translations/${lang.locale}.json` | ||
]) | ||
), | ||
embedIcons: Object.fromEntries( | ||
import_editor.EMBED_DEFINITIONS.map((def) => [def.type, `/embed-icons/${def.type}.png`]) | ||
import_editor.EMBED_DEFINITIONS.map((def) => [ | ||
def.type, | ||
`https://unpkg.com/@tldraw/assets@${import_version.version}/embed-icons/${def.type}.png` | ||
]) | ||
) | ||
}; | ||
function setDefaultUiAssetUrls(urls) { | ||
defaultUiAssetUrls = urls; | ||
} | ||
function useDefaultUiAssetUrlsWithOverrides(overrides) { | ||
if (!overrides) | ||
return defaultUiAssetUrls; | ||
return { | ||
fonts: Object.assign({ ...defaultUiAssetUrls.fonts }, { ...overrides?.fonts }), | ||
icons: Object.assign({ ...defaultUiAssetUrls.icons }, { ...overrides?.icons }), | ||
embedIcons: Object.assign({ ...defaultUiAssetUrls.embedIcons }, { ...overrides?.embedIcons }), | ||
translations: Object.assign( | ||
{ ...defaultUiAssetUrls.translations }, | ||
{ ...overrides?.translations } | ||
) | ||
}; | ||
} | ||
//# sourceMappingURL=assetUrls.js.map |
@@ -59,6 +59,6 @@ "use strict"; | ||
className: "tlui-button-grid__button", | ||
"data-wd": `menu-item.${item.id}`, | ||
"data-testid": `menu-item.${item.id}`, | ||
icon, | ||
title: label ? kbd ? `${msg(label)} ${(0, import_shared.kbdStr)(kbd)}` : `${msg(label)}` : kbd ? `${(0, import_shared.kbdStr)(kbd)}` : "", | ||
onClick: onSelect, | ||
onClick: () => onSelect("actions-menu"), | ||
disabled: item.disabled | ||
@@ -76,3 +76,3 @@ }, | ||
className: "tlui-menu__trigger", | ||
"data-wd": "main.action-menu", | ||
"data-testid": "main.action-menu", | ||
icon: "dots-vertical", | ||
@@ -79,0 +79,0 @@ title: msg("actions-menu.title"), |
@@ -30,3 +30,3 @@ "use strict"; | ||
function BackToContent() { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const actions = (0, import_useActions.useActions)(); | ||
@@ -38,5 +38,5 @@ const action = actions["back-to-content"]; | ||
const interval = setInterval(() => { | ||
const { renderingShapes } = app; | ||
const { renderingShapes } = editor; | ||
const visibleShapes = renderingShapes.filter((s) => s.isInViewport); | ||
const showBackToContentNow = visibleShapes.length === 0 && app.shapesArray.length > 0; | ||
const showBackToContentNow = visibleShapes.length === 0 && editor.shapesArray.length > 0; | ||
if (showBackToContentPrev !== showBackToContentNow) { | ||
@@ -50,3 +50,3 @@ setShowBackToContent(showBackToContentNow); | ||
}; | ||
}, [app]); | ||
}, [editor]); | ||
if (!showBackToContent) | ||
@@ -60,3 +60,3 @@ return null; | ||
onClick: () => { | ||
action.onSelect(); | ||
action.onSelect("helper-buttons"); | ||
setShowBackToContent(false); | ||
@@ -63,0 +63,0 @@ } |
@@ -37,7 +37,6 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_classnames = __toESM(require("classnames")); | ||
var React = __toESM(require("react")); | ||
var import_signia_react = require("signia-react"); | ||
var import_react = require("react"); | ||
var import_useBreakpoint = require("../hooks/useBreakpoint"); | ||
var import_useClipboardEvents = require("../hooks/useClipboardEvents"); | ||
var import_useContextMenuSchema = require("../hooks/useContextMenuSchema"); | ||
@@ -52,9 +51,38 @@ var import_useMenuIsOpen = require("../hooks/useMenuIsOpen"); | ||
const ContextMenu = function ContextMenu2({ children }) { | ||
const app = (0, import_editor.useApp)(); | ||
const contextMenuSchema = (0, import_useContextMenuSchema.useContextMenuSchema)(); | ||
const handleOpenChange = (0, import_useMenuIsOpen.useMenuIsOpen)("context menu"); | ||
const editor = (0, import_editor.useEditor)(); | ||
const contextTLUiMenuSchema = (0, import_useContextMenuSchema.useContextMenuSchema)(); | ||
const cb = (0, import_react.useCallback)( | ||
(isOpen) => { | ||
if (!isOpen) { | ||
const { onlySelectedShape } = editor; | ||
if (onlySelectedShape && editor.isShapeOrAncestorLocked(onlySelectedShape)) { | ||
editor.setSelectedIds([]); | ||
} | ||
} else { | ||
if (editor.isCoarsePointer) { | ||
const { | ||
selectedShapes, | ||
inputs: { currentPagePoint } | ||
} = editor; | ||
const shapesAtPoint = editor.getShapesAtPoint(currentPagePoint); | ||
if ( | ||
// if there are no selected shapes | ||
!editor.selectedShapes.length || // OR if none of the shapes at the point include the selected shape | ||
!shapesAtPoint.some((s) => selectedShapes.includes(s)) | ||
) { | ||
const lockedShapes = shapesAtPoint.filter((s) => editor.isShapeOrAncestorLocked(s)); | ||
if (lockedShapes.length) { | ||
editor.select(...lockedShapes.map((s) => s.id)); | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
[editor] | ||
); | ||
const [_, handleOpenChange] = (0, import_useMenuIsOpen.useMenuIsOpen)("context menu", cb); | ||
const isReadonly = (0, import_useReadonly.useReadonly)(); | ||
const noItemsToShow = contextMenuSchema.length === 0 || isReadonly && contextMenuSchema.every((item) => !item.readonlyOk); | ||
const selectToolActive = (0, import_signia_react.useValue)("isSelectToolActive", () => app.currentToolId === "select", [ | ||
app | ||
const noItemsToShow = contextTLUiMenuSchema.length === 0 || isReadonly && contextTLUiMenuSchema.every((item) => !item.readonlyOk); | ||
const selectToolActive = (0, import_state.useValue)("isSelectToolActive", () => editor.currentToolId === "select", [ | ||
editor | ||
]); | ||
@@ -76,12 +104,11 @@ const disabled = !selectToolActive || noItemsToShow; | ||
function ContextMenuContent() { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
const menuSchema = (0, import_useContextMenuSchema.useContextMenuSchema)(); | ||
const handleSubOpenChange = (0, import_useMenuIsOpen.useMenuIsOpen)("context menu sub"); | ||
const [_, handleSubOpenChange] = (0, import_useMenuIsOpen.useMenuIsOpen)("context menu sub"); | ||
const isReadonly = (0, import_useReadonly.useReadonly)(); | ||
const { paste } = (0, import_useClipboardEvents.useMenuClipboardEvents)(); | ||
const breakpoint = (0, import_useBreakpoint.useBreakpoint)(); | ||
const container = (0, import_editor.useContainer)(); | ||
const [disableClicks, setDisableClicks] = React.useState(false); | ||
function getContextMenuItem(app2, item, parent, depth) { | ||
const [disableClicks, setDisableClicks] = (0, import_react.useState)(false); | ||
function getContextMenuItem(editor2, item, parent, depth) { | ||
if (isReadonly && !item.readonlyOk) | ||
@@ -92,28 +119,2 @@ return null; | ||
switch (item.id) { | ||
case "MENU_PASTE": { | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(_ContextMenu.Item, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
import_Button.Button, | ||
{ | ||
className: "tlui-menu__button", | ||
"data-wd": `menu-item.${item.id}`, | ||
kbd: "$v", | ||
label: "action.paste", | ||
disabled: item.disabled, | ||
onClick: () => { | ||
if (!app2.isSafari || app2.isSafari && app2.isIos) { | ||
navigator.clipboard.read().then((clipboardItems) => { | ||
paste(clipboardItems, app2.inputs.currentPagePoint); | ||
}); | ||
} | ||
}, | ||
onMouseDown: () => { | ||
if (app2.isSafari && !app2.isIos) { | ||
navigator.clipboard.read().then((clipboardItems) => { | ||
paste(clipboardItems, app2.inputs.currentPagePoint); | ||
}); | ||
} | ||
} | ||
} | ||
) }, item.id); | ||
} | ||
case "MOVE_TO_PAGE_MENU": { | ||
@@ -133,4 +134,4 @@ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_MoveToPageMenu.MoveToPageMenu, {}, item.id); | ||
}), | ||
"data-wd": `menu-item.${item.id}`, | ||
children: item.children.map((child) => getContextMenuItem(app2, child, item, depth + 1)) | ||
"data-testid": `menu-item.${item.id}`, | ||
children: item.children.map((child) => getContextMenuItem(editor2, child, item, depth + 1)) | ||
}, | ||
@@ -147,7 +148,7 @@ item.id | ||
label: item.label, | ||
"data-wd": `menu-item.${item.id}`, | ||
"data-testid": `menu-item.${item.id}`, | ||
icon: "chevron-right" | ||
} | ||
) }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(_ContextMenu.Portal, { container, dir: "ltr", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(_ContextMenu.SubContent, { className: "tlui-menu", sideOffset: -4, collisionPadding: 4, children: item.children.map((child) => getContextMenuItem(app2, child, item, depth + 1)) }) }) | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(_ContextMenu.Portal, { container, dir: "ltr", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(_ContextMenu.SubContent, { className: "tlui-menu", sideOffset: -4, collisionPadding: 4, children: item.children.map((child) => getContextMenuItem(editor2, child, item, depth + 1)) }) }) | ||
] }, item.id); | ||
@@ -169,3 +170,3 @@ } | ||
onSelect: (e) => { | ||
onSelect(); | ||
onSelect("context-menu"); | ||
(0, import_editor.preventDefault)(e); | ||
@@ -199,3 +200,3 @@ }, | ||
className: "tlui-menu__button", | ||
"data-wd": `menu-item.${id}`, | ||
"data-testid": `menu-item.${id}`, | ||
kbd, | ||
@@ -209,3 +210,3 @@ label: labelToUse, | ||
} else { | ||
onSelect(); | ||
onSelect("context-menu"); | ||
} | ||
@@ -225,3 +226,3 @@ } | ||
onContextMenu: import_editor.preventDefault, | ||
children: menuSchema.map((item) => getContextMenuItem(app, item, null, 0)) | ||
children: menuSchema.map((item) => getContextMenuItem(editor, item, null, 0)) | ||
} | ||
@@ -228,0 +229,0 @@ ) }); |
@@ -36,4 +36,4 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var React = __toESM(require("react")); | ||
var import_signia_react = require("signia-react"); | ||
var import_useDialogsProvider = require("../hooks/useDialogsProvider"); | ||
@@ -46,3 +46,3 @@ var import_useToastsProvider = require("../hooks/useToastsProvider"); | ||
let t = 0; | ||
function createNShapes(app, n) { | ||
function createNShapes(editor, n) { | ||
const shapesToCreate = Array(n); | ||
@@ -53,3 +53,3 @@ const cols = Math.floor(Math.sqrt(n)); | ||
shapesToCreate[i] = { | ||
id: app.createShapeId("box" + t), | ||
id: (0, import_editor.createShapeId)("box" + t), | ||
type: "geo", | ||
@@ -60,4 +60,4 @@ x: i % cols * 132, | ||
} | ||
app.batch(() => { | ||
app.createShapes(shapesToCreate).setSelectedIds(shapesToCreate.map((s) => s.id)); | ||
editor.batch(() => { | ||
editor.createShapes(shapesToCreate).setSelectedIds(shapesToCreate.map((s) => s.id)); | ||
}); | ||
@@ -78,9 +78,9 @@ } | ||
}); | ||
const CurrentState = (0, import_signia_react.track)(function CurrentState2() { | ||
const app = (0, import_editor.useApp)(); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-debug-panel__current-state", children: app.root.path.value }); | ||
const CurrentState = (0, import_state.track)(function CurrentState2() { | ||
const editor = (0, import_editor.useEditor)(); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-debug-panel__current-state", children: editor.root.path.value }); | ||
}); | ||
const ShapeCount = function ShapeCount2() { | ||
const app = (0, import_editor.useApp)(); | ||
const count = (0, import_signia_react.useValue)("rendering shapes count", () => app.renderingShapes.length, [app]); | ||
const editor = (0, import_editor.useEditor)(); | ||
const count = (0, import_state.useValue)("rendering shapes count", () => editor.renderingShapes.length, [editor]); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [ | ||
@@ -91,6 +91,6 @@ count, | ||
}; | ||
function DebugMenuContent({ | ||
const DebugMenuContent = (0, import_state.track)(function DebugMenuContent2({ | ||
renderDebugMenuItems | ||
}) { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const { addToast } = (0, import_useToastsProvider.useToasts)(); | ||
@@ -112,3 +112,3 @@ const { addDialog } = (0, import_useDialogsProvider.useDialogs)(); | ||
// description?: string | ||
// actions?: TLToastAction[] | ||
// actions?: TLUiToastAction[] | ||
}); | ||
@@ -143,3 +143,3 @@ }, | ||
), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenu.Item, { onClick: () => createNShapes(app, 100), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Create 100 shapes" }) }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenu.Item, { onClick: () => createNShapes(editor, 100), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Create 100 shapes" }) }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
@@ -159,4 +159,4 @@ DropdownMenu.Item, | ||
} | ||
const { selectedShapes } = app; | ||
const shapes = selectedShapes.length === 0 ? app.renderingShapes : selectedShapes; | ||
const { selectedShapes } = editor; | ||
const shapes = selectedShapes.length === 0 ? editor.renderingShapes : selectedShapes; | ||
const elms = shapes.map( | ||
@@ -191,4 +191,3 @@ (shape) => document.getElementById(shape.id).parentElement | ||
onClick: () => { | ||
; | ||
window.__tldraw__hardReset(); | ||
(0, import_editor.hardResetEditor)(); | ||
}, | ||
@@ -201,26 +200,87 @@ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Hard reset" }) | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
DropdownMenu.Item, | ||
Toggle, | ||
{ | ||
onClick: () => { | ||
import_editor.debugFlags.peopleMenu.set(!import_editor.debugFlags.peopleMenu.value); | ||
window.location.reload(); | ||
}, | ||
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Toggle people menu" }) | ||
label: "Read-only", | ||
value: editor.isReadOnly, | ||
onChange: (r) => editor.setReadOnly(r) | ||
} | ||
), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DebugFlagToggle, { flag: import_editor.debugFlags.debugSvg }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DebugFlagToggle, { flag: import_editor.debugFlags.forceSrgb }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
DropdownMenu.Item, | ||
DebugFlagToggle, | ||
{ | ||
onClick: () => { | ||
app.store.put([ | ||
{ ...app.userDocumentSettings, isReadOnly: !app.userDocumentSettings.isReadOnly } | ||
]); | ||
}, | ||
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Toggle read-only" }) | ||
flag: import_editor.debugFlags.debugCursors, | ||
onChange: (enabled) => { | ||
if (enabled) { | ||
const MAX_COLUMNS = 5; | ||
const partials = CURSOR_NAMES.map((name, i) => { | ||
return { | ||
id: (0, import_editor.createShapeId)(), | ||
type: "geo", | ||
x: i % MAX_COLUMNS * 175, | ||
y: Math.floor(i / MAX_COLUMNS) * 175, | ||
props: { | ||
text: name, | ||
w: 150, | ||
h: 150, | ||
fill: "semi" | ||
} | ||
}; | ||
}); | ||
editor.createShapes(partials); | ||
} | ||
} | ||
} | ||
) | ||
] }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenu.Group, { children: Object.values(import_editor.featureFlags).map((flag) => { | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DebugFlagToggle, { flag }, flag.name); | ||
}) }), | ||
renderDebugMenuItems?.() | ||
] }); | ||
}); | ||
function Toggle({ | ||
label, | ||
value, | ||
onChange | ||
}) { | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenu.CheckboxItem, { title: label, checked: value, onSelect: () => onChange(!value), children: label }); | ||
} | ||
const DebugFlagToggle = (0, import_state.track)(function DebugFlagToggle2({ | ||
flag, | ||
onChange | ||
}) { | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
Toggle, | ||
{ | ||
label: flag.name.replace(/([a-z0-9])([A-Z])/g, (m) => `${m[0]} ${m[1].toLowerCase()}`).replace(/^[a-z]/, (m) => m.toUpperCase()), | ||
value: flag.value, | ||
onChange: (newValue) => { | ||
flag.set(newValue); | ||
onChange?.(newValue); | ||
} | ||
} | ||
); | ||
}); | ||
const CURSOR_NAMES = [ | ||
"none", | ||
"default", | ||
"pointer", | ||
"cross", | ||
"move", | ||
"grab", | ||
"grabbing", | ||
"text", | ||
"ew-resize", | ||
"ns-resize", | ||
"nesw-resize", | ||
"nwse-resize", | ||
"nwse-rotate", | ||
"nesw-rotate", | ||
"senw-rotate", | ||
"swne-rotate", | ||
"zoom-in", | ||
"zoom-out" | ||
]; | ||
function ExampleDialog({ | ||
@@ -227,0 +287,0 @@ title = "title", |
@@ -26,3 +26,3 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_signia_react = require("signia-react"); | ||
var import_state = require("@tldraw/state"); | ||
var import_useActions = require("../hooks/useActions"); | ||
@@ -32,8 +32,8 @@ var import_useTranslation = require("../hooks/useTranslation/useTranslation"); | ||
var import_shared = require("./primitives/shared"); | ||
const DuplicateButton = (0, import_signia_react.track)(function DuplicateButton2() { | ||
const app = (0, import_editor.useApp)(); | ||
const DuplicateButton = (0, import_state.track)(function DuplicateButton2() { | ||
const editor = (0, import_editor.useEditor)(); | ||
const actions = (0, import_useActions.useActions)(); | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
const action = actions["duplicate"]; | ||
const noSelected = app.selectedIds.length <= 0; | ||
const noSelected = editor.selectedIds.length <= 0; | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
@@ -43,3 +43,3 @@ import_Button.Button, | ||
icon: action.icon, | ||
onClick: action.onSelect, | ||
onClick: () => action.onSelect("quick-actions"), | ||
disabled: noSelected, | ||
@@ -46,0 +46,0 @@ title: `${msg(action.label)} ${(0, import_shared.kbdStr)(action.kbd)}`, |
@@ -31,3 +31,4 @@ "use strict"; | ||
__export(EditLinkDialog_exports, { | ||
EditLinkDialog: () => EditLinkDialog | ||
EditLinkDialog: () => EditLinkDialog, | ||
EditLinkDialogInner: () => EditLinkDialogInner | ||
}); | ||
@@ -37,4 +38,4 @@ module.exports = __toCommonJS(EditLinkDialog_exports); | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_react = require("react"); | ||
var import_signia_react = require("signia-react"); | ||
var import_useTranslation = require("../hooks/useTranslation/useTranslation"); | ||
@@ -44,46 +45,77 @@ var import_Button = require("./primitives/Button"); | ||
var import_Input = require("./primitives/Input"); | ||
const validUrlRegex = new RegExp( | ||
/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i | ||
); | ||
function valiateUrl(url) { | ||
if (validUrlRegex.test(url)) | ||
return true; | ||
if (validUrlRegex.test("https://" + url)) | ||
return "needs protocol"; | ||
return false; | ||
function validateUrl(url) { | ||
if ((0, import_editor.isValidUrl)(url)) { | ||
return { isValid: true, hasProtocol: true }; | ||
} | ||
if ((0, import_editor.isValidUrl)("https://" + url)) { | ||
return { isValid: true, hasProtocol: false }; | ||
} | ||
return { isValid: false, hasProtocol: false }; | ||
} | ||
const EditLinkDialog = (0, import_signia_react.track)(function EditLink({ onClose }) { | ||
const app = (0, import_editor.useApp)(); | ||
const EditLinkDialog = (0, import_state.track)(function EditLinkDialog2({ onClose }) { | ||
const editor = (0, import_editor.useEditor)(); | ||
const selectedShape = editor.onlySelectedShape; | ||
if (!(selectedShape && "url" in selectedShape.props && typeof selectedShape.props.url === "string")) { | ||
return null; | ||
} | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EditLinkDialogInner, { onClose, selectedShape }); | ||
}); | ||
const EditLinkDialogInner = (0, import_state.track)(function EditLinkDialogInner2({ | ||
onClose, | ||
selectedShape | ||
}) { | ||
const editor = (0, import_editor.useEditor)(); | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
const selectedShape = app.onlySelectedShape; | ||
const [validState, setIsValid] = (0, import_react.useState)(valiateUrl(selectedShape?.props.url)); | ||
const rInitialValue = (0, import_react.useRef)(selectedShape?.props.url); | ||
const rValue = (0, import_react.useRef)(selectedShape?.props.url); | ||
const handleChange = (0, import_react.useCallback)((value) => { | ||
const validStateUrl = valiateUrl(value.trim()); | ||
setIsValid((s) => s === validStateUrl ? s : validStateUrl); | ||
if (validStateUrl) { | ||
rValue.current = value; | ||
} | ||
const rInput = (0, import_react.useRef)(null); | ||
(0, import_react.useEffect)(() => { | ||
requestAnimationFrame(() => rInput.current?.focus()); | ||
}, []); | ||
const rInitialValue = (0, import_react.useRef)(selectedShape.props.url); | ||
const [urlInputState, setUrlInputState] = (0, import_react.useState)(() => { | ||
const urlValidResult = validateUrl(selectedShape.props.url); | ||
const initialValue = urlValidResult.isValid === true ? urlValidResult.hasProtocol ? selectedShape.props.url : "https://" + selectedShape.props.url : "https://"; | ||
return { | ||
actual: initialValue, | ||
safe: initialValue, | ||
valid: true | ||
}; | ||
}); | ||
const handleChange = (0, import_react.useCallback)((rawValue) => { | ||
const fixedRawValue = rawValue.replace(/https?:\/\/(https?:\/\/)/, (_match, arg1) => { | ||
return arg1; | ||
}); | ||
const urlValidResult = validateUrl(fixedRawValue); | ||
const safeValue = urlValidResult.isValid === true ? urlValidResult.hasProtocol ? fixedRawValue : "https://" + fixedRawValue : "https://"; | ||
setUrlInputState({ | ||
actual: fixedRawValue, | ||
safe: safeValue, | ||
valid: urlValidResult.isValid | ||
}); | ||
}, []); | ||
const handleClear = (0, import_react.useCallback)(() => { | ||
app.setProp("url", "", false); | ||
const { onlySelectedShape } = editor; | ||
if (!onlySelectedShape) | ||
return; | ||
editor.updateShapes([ | ||
{ id: onlySelectedShape.id, type: onlySelectedShape.type, props: { url: "" } } | ||
]); | ||
onClose(); | ||
}, [app, onClose]); | ||
const handleComplete = (0, import_react.useCallback)( | ||
(value) => { | ||
value = value.trim(); | ||
const validState2 = valiateUrl(value); | ||
const shape = app.selectedShapes[0]; | ||
if (shape) { | ||
const current = shape.props.url; | ||
const next = validState2 ? validState2 === "needs protocol" ? "https://" + value : value : shape.type === "bookmark" ? rInitialValue.current : ""; | ||
if (current !== void 0 && current !== next) { | ||
app.setProp("url", next, false); | ||
} | ||
}, [editor, onClose]); | ||
const handleComplete = (0, import_react.useCallback)(() => { | ||
const { onlySelectedShape } = editor; | ||
if (!onlySelectedShape) | ||
return; | ||
if (onlySelectedShape && "url" in onlySelectedShape.props) { | ||
if (onlySelectedShape.props.url !== urlInputState.safe) { | ||
editor.updateShapes([ | ||
{ | ||
id: onlySelectedShape.id, | ||
type: onlySelectedShape.type, | ||
props: { url: urlInputState.safe } | ||
} | ||
]); | ||
} | ||
onClose(); | ||
}, | ||
[app, onClose] | ||
); | ||
} | ||
onClose(); | ||
}, [editor, onClose, urlInputState]); | ||
const handleCancel = (0, import_react.useCallback)(() => { | ||
@@ -96,3 +128,3 @@ onClose(); | ||
} | ||
const isRemoving = rInitialValue.current && !validState; | ||
const isRemoving = rInitialValue.current && !urlInputState.valid; | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ | ||
@@ -107,6 +139,7 @@ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Dialog.Header, { children: [ | ||
{ | ||
ref: rInput, | ||
className: "tlui-edit-link-dialog__input", | ||
label: "edit-link-dialog.url", | ||
defaultValue: validState ? validState === "needs protocol" ? "https://" + selectedShape.props.url : selectedShape.props.url : "https://", | ||
autofocus: true, | ||
value: urlInputState.actual, | ||
onValueChange: handleChange, | ||
@@ -117,3 +150,3 @@ onComplete: handleComplete, | ||
), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: validState ? msg("edit-link-dialog.detail") : msg("edit-link-dialog.invalid-url") }) | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: urlInputState.valid ? msg("edit-link-dialog.detail") : msg("edit-link-dialog.invalid-url") }) | ||
] }) }), | ||
@@ -126,5 +159,5 @@ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Dialog.Footer, { className: "tlui-dialog__footer__actions", children: [ | ||
type: "primary", | ||
disabled: !validState, | ||
onTouchEnd: () => handleComplete(rValue.current), | ||
onClick: () => handleComplete(rValue.current), | ||
disabled: !urlInputState.valid, | ||
onTouchEnd: handleComplete, | ||
onClick: handleComplete, | ||
children: msg("edit-link-dialog.save") | ||
@@ -131,0 +164,0 @@ } |
@@ -36,5 +36,5 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_tlschema = require("@tldraw/tlschema"); | ||
var import_react = require("react"); | ||
var import_signia_react = require("signia-react"); | ||
var import_useAssetUrls = require("../hooks/useAssetUrls"); | ||
@@ -46,4 +46,4 @@ var import_useTranslation = require("../hooks/useTranslation/useTranslation"); | ||
var import_Input = require("./primitives/Input"); | ||
const EmbedDialog = (0, import_signia_react.track)(function EmbedDialog2({ onClose }) { | ||
const app = (0, import_editor.useApp)(); | ||
const EmbedDialog = (0, import_state.track)(function EmbedDialog2({ onClose }) { | ||
const editor = (0, import_editor.useEditor)(); | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
@@ -62,3 +62,3 @@ const assetUrls = (0, import_useAssetUrls.useAssetUrls)(); | ||
embedDefinition ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Dialog.Body, { className: "tlui-embed-dialog__enter", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-embed-dialog", children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Dialog.Body, { className: "tlui-embed-dialog__enter", children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
@@ -100,3 +100,3 @@ import_Input.Input, | ||
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-embed-dialog__warning", children: showError ? msg("embed-dialog.invalid-url") : "\xA0" }) | ||
] }) }), | ||
] }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Dialog.Footer, { className: "tlui-dialog__footer__actions", children: [ | ||
@@ -114,3 +114,3 @@ /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-spacer" }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-embed__spacer" }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Button.Button, { label: "embed-dialog.cancel", onClick: onClose }), | ||
@@ -126,6 +126,7 @@ /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
return; | ||
(0, import_editor.createEmbedShapeAtPoint)(app, url, app.viewportPageCenter, { | ||
width: embedInfoForUrl.definition.width, | ||
height: embedInfoForUrl.definition.height, | ||
doesResize: embedInfoForUrl.definition.doesResize | ||
editor.putExternalContent({ | ||
type: "embed", | ||
url, | ||
point: editor.viewportPageCenter, | ||
embed: embedInfoForUrl.definition | ||
}); | ||
@@ -132,0 +133,0 @@ onClose(); |
@@ -48,4 +48,4 @@ "use strict"; | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
const onOpenChange = (0, import_useMenuIsOpen.useMenuIsOpen)("help menu"); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-help-menu", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_dropdown_menu.Root, { dir: "ltr", onOpenChange, modal: false, children: [ | ||
const [isOpen, onOpenChange] = (0, import_useMenuIsOpen.useMenuIsOpen)("help menu"); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-help-menu", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_dropdown_menu.Root, { dir: "ltr", open: isOpen, onOpenChange, modal: false, children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
@@ -98,3 +98,12 @@ import_react_dropdown_menu.Trigger, | ||
const { id, kbd, label, onSelect, icon } = item.actionItem; | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(M.Item, { kbd, label, onClick: onSelect, iconLeft: icon }, id); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
M.Item, | ||
{ | ||
kbd, | ||
label, | ||
onClick: () => onSelect("help-menu"), | ||
iconLeft: icon | ||
}, | ||
id | ||
); | ||
} | ||
@@ -101,0 +110,0 @@ } |
@@ -36,9 +36,9 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var React = __toESM(require("react")); | ||
var import_signia_react = require("signia-react"); | ||
const HTMLCanvas = (0, import_signia_react.track)(function HTMLCanvas2() { | ||
const app = (0, import_editor.useApp)(); | ||
const HTMLCanvas = (0, import_state.track)(function HTMLCanvas2() { | ||
const editor = (0, import_editor.useEditor)(); | ||
const rCanvas = React.useRef(null); | ||
const camera = app.camera; | ||
const shapes = app.shapesArray; | ||
const camera = editor.camera; | ||
const shapes = editor.shapesArray; | ||
if (rCanvas.current) { | ||
@@ -52,3 +52,3 @@ const cvs = rCanvas.current; | ||
for (const shape of shapes) { | ||
const bounds = app.getPageBounds(shape); | ||
const bounds = editor.getPageBounds(shape); | ||
path.rect(bounds.minX, bounds.minY, bounds.width, bounds.height); | ||
@@ -60,3 +60,3 @@ } | ||
ctx.save(); | ||
const corners = app.getPageCorners(shape); | ||
const corners = editor.getPageCorners(shape); | ||
corners.forEach((corner) => dot(ctx, corner.x, corner.y, "red")); | ||
@@ -70,4 +70,4 @@ ctx.restore(); | ||
ref: rCanvas, | ||
width: app.viewportScreenBounds.width, | ||
height: app.viewportScreenBounds.height, | ||
width: editor.viewportScreenBounds.width, | ||
height: editor.viewportScreenBounds.height, | ||
style: { width: "100%", height: "100%" } | ||
@@ -74,0 +74,0 @@ } |
@@ -40,9 +40,7 @@ "use strict"; | ||
function LanguageMenu() { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const { languages, currentLanguage } = (0, import_useLanguages.useLanguages)(); | ||
const handleLanguageSelect = (0, import_react.useCallback)( | ||
(locale) => { | ||
app.updateUser({ locale }); | ||
}, | ||
[app] | ||
(locale) => editor.setLocale(locale), | ||
[editor] | ||
); | ||
@@ -49,0 +47,0 @@ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(D.Sub, { id: "help menu language", children: [ |
@@ -38,3 +38,2 @@ "use strict"; | ||
var import_useBreakpoint = require("../hooks/useBreakpoint"); | ||
var import_useClipboardEvents = require("../hooks/useClipboardEvents"); | ||
var import_useMenuSchema = require("../hooks/useMenuSchema"); | ||
@@ -54,3 +53,3 @@ var import_useReadonly = require("../hooks/useReadonly"); | ||
className: "tlui-menu__trigger", | ||
"data-wd": "main.menu", | ||
"data-testid": "main.menu", | ||
title: msg("menu.title"), | ||
@@ -64,3 +63,3 @@ icon: "menu" | ||
function MenuContent() { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
@@ -70,4 +69,3 @@ const menuSchema = (0, import_useMenuSchema.useMenuSchema)(); | ||
const isReadonly = (0, import_useReadonly.useReadonly)(); | ||
const { paste } = (0, import_useClipboardEvents.useMenuClipboardEvents)(); | ||
function getMenuItem(app2, item, parent, depth) { | ||
function getMenuItem(editor2, item, parent, depth) { | ||
switch (item.type) { | ||
@@ -80,30 +78,2 @@ case "custom": { | ||
} | ||
if (item.id === "MENU_PASTE") { | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
M.Item, | ||
{ | ||
"data-wd": `menu-item.${item.id}`, | ||
kbd: "$v", | ||
label: "action.paste", | ||
disabled: item.disabled, | ||
onMouseDown: () => { | ||
if (app2.isSafari && navigator.clipboard?.read) { | ||
navigator.clipboard.read().then((clipboardItems) => { | ||
paste(clipboardItems); | ||
}); | ||
} | ||
}, | ||
onClick: () => { | ||
if (app2.isSafari) { | ||
} else if (navigator.clipboard?.read) { | ||
navigator.clipboard.read().then((clipboardItems) => { | ||
paste(clipboardItems); | ||
}); | ||
} | ||
}, | ||
onPointerUp: import_editor.preventDefault | ||
}, | ||
item.id | ||
); | ||
} | ||
return null; | ||
@@ -118,3 +88,3 @@ } | ||
size: depth <= 1 ? "medium" : breakpoint < 3 || parent?.type === "submenu" && depth > 2 ? "tiny" : "medium", | ||
children: item.children.map((child) => getMenuItem(app2, child, item, depth + 1)) | ||
children: item.children.map((child) => getMenuItem(editor2, child, item, depth + 1)) | ||
}, | ||
@@ -128,4 +98,4 @@ item.id | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(M.Sub, { id: `main menu ${parent ? parent.id + " " : ""}${item.id}`, children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(M.SubTrigger, { label: item.label, "data-wd": `menu-item.${item.id}` }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(M.SubContent, { sideOffset: -4, alignOffset: -1, children: item.children.map((child) => getMenuItem(app2, child, item, depth + 1)) }) | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(M.SubTrigger, { label: item.label, "data-testid": `menu-item.${item.id}` }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(M.SubContent, { sideOffset: -4, alignOffset: -1, children: item.children.map((child) => getMenuItem(editor2, child, item, depth + 1)) }) | ||
] }, item.id); | ||
@@ -143,3 +113,3 @@ } | ||
{ | ||
onSelect, | ||
onSelect: () => onSelect("menu"), | ||
title: labelStr ? labelStr : "", | ||
@@ -159,6 +129,6 @@ checked: item.checked, | ||
{ | ||
"data-wd": `menu-item.${item.id}`, | ||
"data-testid": `menu-item.${item.id}`, | ||
kbd, | ||
label: labelToUse, | ||
onClick: onSelect, | ||
onClick: () => onSelect("menu"), | ||
disabled: item.disabled | ||
@@ -171,4 +141,4 @@ }, | ||
} | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: menuSchema.map((item) => getMenuItem(app, item, null, 0)) }); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: menuSchema.map((item) => getMenuItem(editor, item, null, 0)) }); | ||
} | ||
//# sourceMappingURL=Menu.js.map |
@@ -26,3 +26,3 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_signia_react = require("signia-react"); | ||
var import_state = require("@tldraw/state"); | ||
var import_useBreakpoint = require("../hooks/useBreakpoint"); | ||
@@ -37,7 +37,7 @@ var import_useReadonly = require("../hooks/useReadonly"); | ||
var import_UndoButton = require("./UndoButton"); | ||
const MenuZone = (0, import_signia_react.track)(function MenuZone2() { | ||
const app = (0, import_editor.useApp)(); | ||
const MenuZone = (0, import_state.track)(function MenuZone2() { | ||
const editor = (0, import_editor.useEditor)(); | ||
const breakpoint = (0, import_useBreakpoint.useBreakpoint)(); | ||
const isReadonly = (0, import_useReadonly.useReadonly)(); | ||
const showQuickActions = !isReadonly && !app.isIn("hand"); | ||
const showQuickActions = !isReadonly && !editor.isInAny("hand", "zoom", "eraser"); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-menu-zone", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-menu-zone__controls", children: [ | ||
@@ -47,3 +47,3 @@ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Menu.Menu, {}), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_PageMenu.PageMenu, {}), | ||
breakpoint >= 5 && showQuickActions && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ | ||
breakpoint >= 6 && showQuickActions && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-menu-zone__divider" }), | ||
@@ -50,0 +50,0 @@ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_UndoButton.UndoButton, {}), |
@@ -26,4 +26,4 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_react = require("react"); | ||
var import_signia_react = require("signia-react"); | ||
var import_useTranslation = require("../hooks/useTranslation/useTranslation"); | ||
@@ -35,16 +35,21 @@ var import_StylePanel = require("./StylePanel/StylePanel"); | ||
function MobileStylePanel() { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
const currentColor = (0, import_signia_react.useValue)( | ||
const currentColor = (0, import_state.useValue)( | ||
"current color", | ||
() => { | ||
const { props } = app; | ||
return props ? props.color ? app.getCssColor(props.color) : null : "var(--color-muted-1)"; | ||
const color = editor.sharedStyles.get(import_editor.DefaultColorStyle); | ||
if (!color) | ||
return "var(--color-muted-1)"; | ||
if (color.type === "mixed") | ||
return null; | ||
const theme = (0, import_editor.getDefaultColorTheme)(editor); | ||
return theme[color.value].solid; | ||
}, | ||
[app] | ||
[editor] | ||
); | ||
const disableStylePanel = (0, import_signia_react.useValue)( | ||
const disableStylePanel = (0, import_state.useValue)( | ||
"isHandOrEraserToolActive", | ||
() => app.currentToolId === "hand" || app.currentToolId === "eraser", | ||
[app] | ||
() => editor.isInAny("hand", "zoom", "eraser", "laser"), | ||
[editor] | ||
); | ||
@@ -54,6 +59,6 @@ const handleStylesOpenChange = (0, import_react.useCallback)( | ||
if (!isOpen) { | ||
app.isChangingStyle = false; | ||
editor.isChangingStyle = false; | ||
} | ||
}, | ||
[app] | ||
[editor] | ||
); | ||
@@ -64,4 +69,4 @@ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Popover.Popover, { id: "style menu", onOpenChange: handleStylesOpenChange, children: [ | ||
{ | ||
className: "tlui-toolbar__tools__button tlui-toolbar__styles__button tlui-popover__trigger", | ||
"data-wd": "mobile.styles", | ||
className: "tlui-toolbar__tools__button tlui-toolbar__styles__button", | ||
"data-testid": "mobile.styles", | ||
style: { color: currentColor ?? "var(--color-text)" }, | ||
@@ -68,0 +73,0 @@ title: msg("style-panel.title"), |
@@ -37,11 +37,13 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_signia_react = require("signia-react"); | ||
var import_state = require("@tldraw/state"); | ||
var import_useToastsProvider = require("../hooks/useToastsProvider"); | ||
var import_useTranslation = require("../hooks/useTranslation/useTranslation"); | ||
var import_Button = require("./primitives/Button"); | ||
const MoveToPageMenu = (0, import_signia_react.track)(function MoveToPageMenu2() { | ||
const app = (0, import_editor.useApp)(); | ||
const MoveToPageMenu = (0, import_state.track)(function MoveToPageMenu2() { | ||
const editor = (0, import_editor.useEditor)(); | ||
const container = (0, import_editor.useContainer)(); | ||
const pages = app.pages; | ||
const currentPageId = app.currentPageId; | ||
const pages = editor.pages; | ||
const currentPageId = editor.currentPageId; | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
const { addToast } = (0, import_useToastsProvider.useToasts)(); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(_ContextMenu.Sub, { children: [ | ||
@@ -53,3 +55,3 @@ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(_ContextMenu.SubTrigger, { dir: "ltr", asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
label: "context-menu.move-to-page", | ||
"data-wd": "menu-item.move-to-page", | ||
"data-testid": "menu-item.move-to-page", | ||
icon: "chevron-right" | ||
@@ -64,3 +66,3 @@ } | ||
className: "tlui-menu__group", | ||
"data-wd": `menu-item.pages`, | ||
"data-testid": `menu-item.pages`, | ||
children: pages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
@@ -71,4 +73,21 @@ _ContextMenu.Item, | ||
onSelect: () => { | ||
app.mark("move_shapes_to_page"); | ||
app.moveShapesToPage(app.selectedIds, page.id); | ||
editor.mark("move_shapes_to_page"); | ||
editor.moveShapesToPage(editor.selectedIds, page.id); | ||
const toPage = editor.getPageById(page.id); | ||
if (toPage) { | ||
addToast({ | ||
title: "Changed Page", | ||
description: `Moved to ${toPage.name}.`, | ||
actions: [ | ||
{ | ||
label: "Go Back", | ||
type: "primary", | ||
onClick: () => { | ||
editor.mark("change-page"); | ||
editor.setCurrentPageId(currentPageId); | ||
} | ||
} | ||
] | ||
}); | ||
} | ||
}, | ||
@@ -95,3 +114,3 @@ asChild: true, | ||
className: "tlui-menu__group", | ||
"data-wd": `menu-item.new-page`, | ||
"data-testid": `menu-item.new-page`, | ||
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
@@ -101,10 +120,10 @@ _ContextMenu.Item, | ||
onSelect: () => { | ||
app.mark("move_shapes_to_page"); | ||
const newPageId = import_editor.TLPage.createId(); | ||
const ids = app.selectedIds; | ||
const oldPageId = app.currentPageId; | ||
app.batch(() => { | ||
app.createPage("Page 1", newPageId); | ||
app.setCurrentPageId(oldPageId); | ||
app.moveShapesToPage(ids, newPageId); | ||
editor.mark("move_shapes_to_page"); | ||
const newPageId = import_editor.PageRecordType.createId(); | ||
const ids = editor.selectedIds; | ||
const oldPageId = editor.currentPageId; | ||
editor.batch(() => { | ||
editor.createPage("Page 1", newPageId); | ||
editor.setCurrentPageId(oldPageId); | ||
editor.moveShapesToPage(ids, newPageId); | ||
}); | ||
@@ -111,0 +130,0 @@ }, |
@@ -31,4 +31,3 @@ "use strict"; | ||
__export(Minimap_exports, { | ||
Minimap: () => Minimap, | ||
useActivePresences: () => useActivePresences | ||
Minimap: () => Minimap | ||
}); | ||
@@ -39,22 +38,6 @@ module.exports = __toCommonJS(Minimap_exports); | ||
var import_primitives = require("@tldraw/primitives"); | ||
var import_state = require("@tldraw/state"); | ||
var React = __toESM(require("react")); | ||
var import_signia_react = require("signia-react"); | ||
var import_MinimapManager = require("./MinimapManager"); | ||
const COLLABORATOR_INACTIVITY_TIMEOUT = 1e4; | ||
const useActivePresences = () => { | ||
const app = (0, import_editor.useApp)(); | ||
const time = (0, import_signia_react.useAtom)("time", Date.now()); | ||
React.useEffect(() => { | ||
const interval = setInterval(() => time.set(Date.now()), 1e3 * 5); | ||
return () => clearInterval(interval); | ||
}, [time]); | ||
return React.useMemo( | ||
() => app.store.query.records("user_presence", () => ({ | ||
lastActivityTimestamp: { gt: time.value - COLLABORATOR_INACTIVITY_TIMEOUT }, | ||
userId: { neq: app.userId } | ||
})), | ||
[app, time] | ||
); | ||
}; | ||
const Minimap = (0, import_signia_react.track)(function Minimap2({ | ||
const Minimap = (0, import_state.track)(function Minimap2({ | ||
shapeFill, | ||
@@ -64,8 +47,8 @@ selectFill, | ||
}) { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const rCanvas = React.useRef(null); | ||
const container = (0, import_editor.useContainer)(); | ||
const rPointing = React.useRef(false); | ||
const minimap = React.useMemo(() => new import_MinimapManager.MinimapManager(app, app.devicePixelRatio), [app]); | ||
const isDarkMode = app.userDocumentSettings.isDarkMode; | ||
const minimap = React.useMemo(() => new import_MinimapManager.MinimapManager(editor, editor.devicePixelRatio), [editor]); | ||
const isDarkMode = editor.isDarkMode; | ||
React.useEffect(() => { | ||
@@ -87,3 +70,3 @@ const raf = requestAnimationFrame(() => { | ||
(e) => { | ||
if (!app.shapeIds.size) | ||
if (!editor.currentPageShapeIds.size) | ||
return; | ||
@@ -93,6 +76,6 @@ const { x, y } = minimap.minimapScreenPointToPagePoint(e.clientX, e.clientY, false, false); | ||
minimap.originPagePoint.setTo(clampedPoint); | ||
minimap.originPageCenter.setTo(app.viewportPageBounds.center); | ||
app.centerOnPoint(x, y, { duration: import_editor.ANIMATION_MEDIUM_MS }); | ||
minimap.originPageCenter.setTo(editor.viewportPageBounds.center); | ||
editor.centerOnPoint(x, y, { duration: import_editor.ANIMATION_MEDIUM_MS }); | ||
}, | ||
[app, minimap] | ||
[editor, minimap] | ||
); | ||
@@ -102,3 +85,3 @@ const onPointerDown = React.useCallback( | ||
e.currentTarget.setPointerCapture(e.pointerId); | ||
if (!app.shapeIds.size) | ||
if (!editor.currentPageShapeIds.size) | ||
return; | ||
@@ -109,3 +92,3 @@ rPointing.current = true; | ||
const clampedPoint = minimap.minimapScreenPointToPagePoint(e.clientX, e.clientY, false, true); | ||
const _vpPageBounds = app.viewportPageBounds; | ||
const _vpPageBounds = editor.viewportPageBounds; | ||
minimap.originPagePoint.setTo(clampedPoint); | ||
@@ -115,6 +98,6 @@ minimap.originPageCenter.setTo(_vpPageBounds.center); | ||
if (!minimap.isInViewport) { | ||
app.centerOnPoint(x, y, { duration: import_editor.ANIMATION_MEDIUM_MS }); | ||
editor.centerOnPoint(x, y, { duration: import_editor.ANIMATION_MEDIUM_MS }); | ||
} | ||
}, | ||
[app, minimap] | ||
[editor, minimap] | ||
); | ||
@@ -133,9 +116,9 @@ const onPointerMove = React.useCallback( | ||
const center = import_primitives.Vec2d.Add(minimap.originPageCenter, delta); | ||
app.centerOnPoint(center.x, center.y); | ||
editor.centerOnPoint(center.x, center.y); | ||
return; | ||
} | ||
app.centerOnPoint(x, y); | ||
editor.centerOnPoint(x, y); | ||
} | ||
const pagePoint = minimap.getPagePoint(e.clientX, e.clientY); | ||
const screenPoint = app.pageToScreen(pagePoint.x, pagePoint.y); | ||
const screenPoint = editor.pageToScreen(pagePoint.x, pagePoint.y); | ||
const info = { | ||
@@ -147,7 +130,7 @@ type: "pointer", | ||
point: screenPoint, | ||
isPen: app.isPenMode | ||
isPen: editor.isPenMode | ||
}; | ||
app.dispatch(info); | ||
editor.dispatch(info); | ||
}, | ||
[app, minimap] | ||
[editor, minimap] | ||
); | ||
@@ -160,3 +143,3 @@ const onPointerUp = React.useCallback((_e) => { | ||
const offset = (0, import_editor.normalizeWheel)(e); | ||
app.dispatch({ | ||
editor.dispatch({ | ||
type: "wheel", | ||
@@ -170,3 +153,3 @@ name: "wheel", | ||
}, | ||
[app] | ||
[editor] | ||
); | ||
@@ -176,3 +159,3 @@ (0, import_editor.useQuickReactor)( | ||
() => { | ||
const { devicePixelRatio } = app; | ||
const { devicePixelRatio } = editor; | ||
minimap.setDpr(devicePixelRatio); | ||
@@ -188,9 +171,11 @@ const canvas = rCanvas.current; | ||
}, | ||
[app, minimap] | ||
[editor, minimap] | ||
); | ||
const presences = useActivePresences(); | ||
const presences = React.useMemo(() => { | ||
return editor.store.query.records("instance_presence"); | ||
}, [editor]); | ||
(0, import_editor.useQuickReactor)( | ||
"minimap render when pagebounds or collaborators changes", | ||
() => { | ||
const { devicePixelRatio, viewportPageBounds, allShapesCommonBounds } = app; | ||
const { devicePixelRatio, viewportPageBounds, allShapesCommonBounds } = editor; | ||
devicePixelRatio; | ||
@@ -200,5 +185,5 @@ minimap.contentPageBounds = allShapesCommonBounds ? import_primitives.Box2d.Expand(allShapesCommonBounds, viewportPageBounds) : viewportPageBounds; | ||
const allShapeBounds = []; | ||
app.shapeIds.forEach((id) => { | ||
let pageBounds = app.getPageBoundsById(id); | ||
const pageMask = app.getPageMaskById(id); | ||
editor.currentPageShapeIds.forEach((id) => { | ||
let pageBounds = editor.getPageBoundsById(id); | ||
const pageMask = editor.getPageMaskById(id); | ||
if (pageMask) { | ||
@@ -220,3 +205,3 @@ const intersection = (0, import_primitives.intersectPolygonPolygon)(pageMask, pageBounds.corners); | ||
}, | ||
[app, minimap] | ||
[editor, minimap] | ||
); | ||
@@ -223,0 +208,0 @@ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-minimap", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( |
@@ -27,4 +27,4 @@ "use strict"; | ||
class MinimapManager { | ||
constructor(app, dpr) { | ||
this.app = app; | ||
constructor(editor, dpr) { | ||
this.editor = editor; | ||
this.dpr = dpr; | ||
@@ -109,7 +109,7 @@ } | ||
minimapScreenPointToPagePoint = (x, y, shiftKey = false, clampToBounds = false) => { | ||
const { app } = this; | ||
const { viewportPageBounds } = app; | ||
const { editor } = this; | ||
const { viewportPageBounds } = editor; | ||
let { x: px, y: py } = this.getPagePoint(x, y); | ||
if (clampToBounds) { | ||
const shapesPageBounds = this.app.allShapesCommonBounds; | ||
const shapesPageBounds = this.editor.allShapesCommonBounds; | ||
const vpPageBounds = viewportPageBounds; | ||
@@ -156,5 +156,5 @@ const minX = (shapesPageBounds?.minX ?? 0) - vpPageBounds.width / 2; | ||
this.updateCanvasPageBounds(); | ||
const { app, canvasScreenBounds, canvasPageBounds, contentPageBounds, contentScreenBounds } = this; | ||
const { editor, canvasScreenBounds, canvasPageBounds, contentPageBounds, contentScreenBounds } = this; | ||
const { width: cw, height: ch } = canvasScreenBounds; | ||
const { viewportPageBounds, selectedIds } = app; | ||
const { viewportPageBounds, selectedIds } = editor; | ||
if (!cvs || !pageBounds) { | ||
@@ -209,3 +209,3 @@ return; | ||
{ | ||
const { brush } = app; | ||
const { brush } = editor; | ||
if (brush) { | ||
@@ -244,8 +244,7 @@ const { x, y, w, h } = brush; | ||
const py = 2.5 / sy; | ||
const { currentPageId } = app; | ||
const { currentPageId } = editor; | ||
let collaborator; | ||
for (let i = 0; i < this.collaborators.length; i++) { | ||
collaborator = this.collaborators[i]; | ||
const instance = collaborator.lastUsedInstanceId ? app.store.get(collaborator.lastUsedInstanceId) : null; | ||
if (instance?.currentPageId !== currentPageId) { | ||
if (collaborator.currentPageId !== currentPageId) { | ||
continue; | ||
@@ -252,0 +251,0 @@ } |
@@ -53,3 +53,3 @@ "use strict"; | ||
className: "tlui-navigation-zone__toggle", | ||
"data-wd": "minimap.toggle", | ||
"data-testid": "minimap.toggle", | ||
onClick: toggleMinimap, | ||
@@ -64,5 +64,5 @@ icon: collapsed ? "chevrons-ne" : "chevrons-sw" | ||
icon: "minus", | ||
"data-wd": "minimap.zoom-out", | ||
"data-testid": "minimap.zoom-out", | ||
title: `${msg(actions["zoom-out"].label)} ${(0, import_shared.kbdStr)(actions["zoom-out"].kbd)}`, | ||
onClick: actions["zoom-out"].onSelect | ||
onClick: () => actions["zoom-out"].onSelect("navigation-zone") | ||
} | ||
@@ -75,5 +75,5 @@ ), | ||
icon: "plus", | ||
"data-wd": "minimap.zoom-in", | ||
"data-testid": "minimap.zoom-in", | ||
title: `${msg(actions["zoom-in"].label)} ${(0, import_shared.kbdStr)(actions["zoom-in"].kbd)}`, | ||
onClick: actions["zoom-in"].onSelect | ||
onClick: () => actions["zoom-in"].onSelect("navigation-zone") | ||
} | ||
@@ -80,0 +80,0 @@ ), |
@@ -36,4 +36,4 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var React = __toESM(require("react")); | ||
var import_signia_react = require("signia-react"); | ||
var import_useActions = require("../../hooks/useActions"); | ||
@@ -44,13 +44,13 @@ var import_useBreakpoint = require("../../hooks/useBreakpoint"); | ||
var M = __toESM(require("../primitives/DropdownMenu")); | ||
const ZoomMenu = (0, import_signia_react.track)(function ZoomMenu2() { | ||
const app = (0, import_editor.useApp)(); | ||
const ZoomMenu = (0, import_state.track)(function ZoomMenu2() { | ||
const editor = (0, import_editor.useEditor)(); | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
const breakpoint = (0, import_useBreakpoint.useBreakpoint)(); | ||
const zoom = app.zoomLevel; | ||
const hasShapes = app.shapeIds.size > 0; | ||
const hasSelected = app.selectedIds.length > 0; | ||
const isZoomedTo100 = app.zoomLevel === 1; | ||
const zoom = editor.zoomLevel; | ||
const hasShapes = editor.currentPageShapeIds.size > 0; | ||
const hasSelected = editor.selectedIds.length > 0; | ||
const isZoomedTo100 = editor.zoomLevel === 1; | ||
const handleDoubleClick = React.useCallback(() => { | ||
app.resetZoom(app.viewportScreenCenter, { duration: import_editor.ANIMATION_MEDIUM_MS }); | ||
}, [app]); | ||
editor.resetZoom(editor.viewportScreenCenter, { duration: import_editor.ANIMATION_MEDIUM_MS }); | ||
}, [editor]); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(M.Root, { id: "zoom", children: [ | ||
@@ -61,3 +61,3 @@ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(M.Trigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
title: `${msg("navigation-zone.zoom")}`, | ||
"data-wd": "minimap.zoom-menu", | ||
"data-testid": "minimap.zoom-menu", | ||
className: breakpoint < 5 ? "tlui-zoom-menu__button" : "tlui-zoom-menu__button__pct", | ||
@@ -73,4 +73,4 @@ onDoubleClick: handleDoubleClick, | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(M.Content, { side: "top", align: "start", alignOffset: 0, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(M.Group, { children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ZoomMenuItem, { action: "zoom-in", "data-wd": "minimap.zoom-menu.zoom-in", noClose: true }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ZoomMenuItem, { action: "zoom-out", "data-wd": "minimap.zoom-menu.zoom-out", noClose: true }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ZoomMenuItem, { action: "zoom-in", "data-testid": "minimap.zoom-menu.zoom-in", noClose: true }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ZoomMenuItem, { action: "zoom-out", "data-testid": "minimap.zoom-menu.zoom-out", noClose: true }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
@@ -80,3 +80,3 @@ ZoomMenuItem, | ||
action: "zoom-to-100", | ||
"data-wd": "minimap.zoom-menu.zoom-to-100", | ||
"data-testid": "minimap.zoom-menu.zoom-to-100", | ||
noClose: true, | ||
@@ -91,3 +91,3 @@ disabled: isZoomedTo100 | ||
disabled: !hasShapes, | ||
"data-wd": "minimap.zoom-menu.zoom-to-fit", | ||
"data-testid": "minimap.zoom-menu.zoom-to-fit", | ||
noClose: true | ||
@@ -101,3 +101,3 @@ } | ||
disabled: !hasSelected, | ||
"data-wd": "minimap.zoom-menu.zoom-to-selection", | ||
"data-testid": "minimap.zoom-menu.zoom-to-selection", | ||
noClose: true | ||
@@ -117,4 +117,4 @@ } | ||
kbd: actions[action].kbd, | ||
"data-wd": props["data-wd"], | ||
onClick: actions[action].onSelect, | ||
"data-testid": props["data-testid"], | ||
onClick: () => actions[action].onSelect("zoom-menu"), | ||
noClose, | ||
@@ -121,0 +121,0 @@ disabled |
@@ -25,5 +25,5 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
const onMovePage = (app, id, from, to) => { | ||
const onMovePage = (editor, id, from, to) => { | ||
let index; | ||
const pages = app.pages; | ||
const pages = editor.pages; | ||
const below = from > to ? pages[to - 1] : pages[to]; | ||
@@ -39,4 +39,4 @@ const above = from > to ? pages[to] : pages[to + 1]; | ||
if (index !== pages[from].index) { | ||
app.mark("moving page"); | ||
app.updatePage({ | ||
editor.mark("moving page"); | ||
editor.updatePage({ | ||
id, | ||
@@ -43,0 +43,0 @@ index |
@@ -33,16 +33,16 @@ "use strict"; | ||
}) { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const rInput = (0, import_react.useRef)(null); | ||
const handleChange = (0, import_react.useCallback)( | ||
(value) => { | ||
app.renamePage(id, value ? value : "New Page", true); | ||
editor.renamePage(id, value ? value : "New Page", true); | ||
}, | ||
[app, id] | ||
[editor, id] | ||
); | ||
const handleComplete = (0, import_react.useCallback)( | ||
(value) => { | ||
app.mark("rename page"); | ||
app.renamePage(id, value || "New Page", false); | ||
editor.mark("rename page"); | ||
editor.renamePage(id, value || "New Page", false); | ||
}, | ||
[app, id] | ||
[editor, id] | ||
); | ||
@@ -49,0 +49,0 @@ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( |
@@ -37,4 +37,4 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_react = require("react"); | ||
var import_signia_react = require("signia-react"); | ||
var import_useTranslation = require("../../hooks/useTranslation/useTranslation"); | ||
@@ -44,3 +44,3 @@ var import_Button = require("../primitives/Button"); | ||
var import_edit_pages_shared = require("./edit-pages-shared"); | ||
const PageItemSubmenu = (0, import_signia_react.track)(function PageItemSubmenu2({ | ||
const PageItemSubmenu = (0, import_state.track)(function PageItemSubmenu2({ | ||
index, | ||
@@ -51,21 +51,21 @@ listSize, | ||
}) { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
const pages = app.pages; | ||
const pages = editor.pages; | ||
const onDuplicate = (0, import_react.useCallback)(() => { | ||
app.mark("creating page"); | ||
const newId = import_editor.TLPage.createId(); | ||
app.duplicatePage(item.id, newId); | ||
}, [app, item]); | ||
editor.mark("creating page"); | ||
const newId = import_editor.PageRecordType.createId(); | ||
editor.duplicatePage(item.id, newId); | ||
}, [editor, item]); | ||
const onMoveUp = (0, import_react.useCallback)(() => { | ||
(0, import_edit_pages_shared.onMovePage)(app, item.id, index, index - 1); | ||
}, [app, item, index]); | ||
(0, import_edit_pages_shared.onMovePage)(editor, item.id, index, index - 1); | ||
}, [editor, item, index]); | ||
const onMoveDown = (0, import_react.useCallback)(() => { | ||
(0, import_edit_pages_shared.onMovePage)(app, item.id, index, index + 1); | ||
}, [app, item, index]); | ||
(0, import_edit_pages_shared.onMovePage)(editor, item.id, index, index + 1); | ||
}, [editor, item, index]); | ||
const onDelete = (0, import_react.useCallback)(() => { | ||
app.mark("deleting page"); | ||
app.deletePage(item.id); | ||
}, [app, item]); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(M.Root, { id: "page item submenu", children: [ | ||
editor.mark("deleting page"); | ||
editor.deletePage(item.id); | ||
}, [editor, item]); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(M.Root, { id: `page item submenu ${index}`, children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(M.Trigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Button.Button, { title: msg("page-menu.submenu.title"), icon: "dots-vertical" }) }), | ||
@@ -72,0 +72,0 @@ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(M.Content, { alignOffset: 0, children: [ |
@@ -26,6 +26,7 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_tlschema = require("@tldraw/tlschema"); | ||
var import_react = require("react"); | ||
var import_signia_react = require("signia-react"); | ||
var import_useBreakpoint = require("../../hooks/useBreakpoint"); | ||
var import_useMenuIsOpen = require("../../hooks/useMenuIsOpen"); | ||
var import_useTranslation = require("../../hooks/useTranslation/useTranslation"); | ||
@@ -39,14 +40,18 @@ var import_Button = require("../primitives/Button"); | ||
const PageMenu = function PageMenu2() { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
const breakpoint = (0, import_useBreakpoint.useBreakpoint)(); | ||
const handleOpenChange = (0, import_react.useCallback)(() => setIsEditing(false), []); | ||
const [isOpen, onOpenChange] = (0, import_useMenuIsOpen.useMenuIsOpen)("page-menu", handleOpenChange); | ||
const ITEM_HEIGHT = breakpoint < 5 ? 36 : 40; | ||
const rSortableContainer = (0, import_react.useRef)(null); | ||
const pages = (0, import_signia_react.useValue)("pages", () => app.pages, [app]); | ||
const currentPage = (0, import_signia_react.useValue)("currentPage", () => app.currentPage, [app]); | ||
const isReadonlyMode = (0, import_signia_react.useValue)("isReadonlyMode", () => app.isReadOnly, [app]); | ||
const maxPageCountReached = (0, import_signia_react.useValue)("maxPageCountReached", () => app.pages.length >= import_editor.MAX_PAGES, [ | ||
app | ||
]); | ||
const isCoarsePointer = (0, import_signia_react.useValue)("isCoarsePointer", () => app.isCoarsePointer, [app]); | ||
const pages = (0, import_state.useValue)("pages", () => editor.pages, [editor]); | ||
const currentPage = (0, import_state.useValue)("currentPage", () => editor.currentPage, [editor]); | ||
const isReadonlyMode = (0, import_state.useValue)("isReadonlyMode", () => editor.isReadOnly, [editor]); | ||
const maxPageCountReached = (0, import_state.useValue)( | ||
"maxPageCountReached", | ||
() => editor.pages.length >= import_editor.MAX_PAGES, | ||
[editor] | ||
); | ||
const isCoarsePointer = (0, import_state.useValue)("isCoarsePointer", () => editor.isCoarsePointer, [editor]); | ||
const [isEditing, setIsEditing] = (0, import_react.useState)(false); | ||
@@ -58,7 +63,2 @@ const toggleEditing = (0, import_react.useCallback)(() => { | ||
}, [isReadonlyMode]); | ||
const [isOpen, setIsOpen] = (0, import_react.useState)(false); | ||
const handleOpenChange = (0, import_react.useCallback)((isOpen2) => { | ||
setIsOpen(isOpen2); | ||
setIsEditing(false); | ||
}, []); | ||
const rMutables = (0, import_react.useRef)({ | ||
@@ -89,3 +89,3 @@ isPointing: false, | ||
const elm = document.querySelector( | ||
`[data-wd="page-menu-item-${currentPage.id}"]` | ||
`[data-testid="page-menu-item-${currentPage.id}"]` | ||
); | ||
@@ -189,3 +189,3 @@ if (elm) { | ||
const { id, index } = mut.pointing; | ||
(0, import_edit_pages_shared.onMovePage)(app, id, index, mut.dragIndex); | ||
(0, import_edit_pages_shared.onMovePage)(editor, id, index, mut.dragIndex); | ||
} | ||
@@ -195,3 +195,3 @@ e.currentTarget.releasePointerCapture(e.pointerId); | ||
}, | ||
[app] | ||
[editor] | ||
); | ||
@@ -220,8 +220,8 @@ const handleKeyDown = (0, import_react.useCallback)( | ||
return; | ||
app.mark("creating page"); | ||
const newPageId = import_tlschema.TLPage.createId(); | ||
app.createPage(msg("page-menu.new-page-initial-name"), newPageId); | ||
editor.mark("creating page"); | ||
const newPageId = import_tlschema.PageRecordType.createId(); | ||
editor.createPage(msg("page-menu.new-page-initial-name"), newPageId); | ||
setIsEditing(true); | ||
}, [app, msg, isReadonlyMode]); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Popover.Popover, { id: "page menu", onOpenChange: handleOpenChange, open: isOpen, children: [ | ||
}, [editor, msg, isReadonlyMode]); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Popover.Popover, { id: "page menu", onOpenChange, open: isOpen, children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Popover.PopoverTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
@@ -231,3 +231,3 @@ import_Button.Button, | ||
className: "tlui-page-menu__trigger tlui-menu__trigger", | ||
"data-wd": "main.page-menu", | ||
"data-testid": "main.page-menu", | ||
icon: "chevron-down", | ||
@@ -245,3 +245,3 @@ title: currentPage.name, | ||
{ | ||
"data-wd": "page-menu.edit", | ||
"data-testid": "page-menu.edit", | ||
title: msg(isEditing ? "page-menu.edit-done" : "page-menu.edit-start"), | ||
@@ -255,3 +255,3 @@ icon: isEditing ? "check" : "edit", | ||
{ | ||
"data-wd": "page-menu.create", | ||
"data-testid": "page-menu.create", | ||
icon: "plus", | ||
@@ -281,3 +281,3 @@ title: msg( | ||
{ | ||
"data-wd": `page-menu-item-${page.id}`, | ||
"data-testid": `page-menu-item-${page.id}`, | ||
className: "tlui-page_menu__item__sortable", | ||
@@ -315,3 +315,3 @@ style: { | ||
if (name && name !== page.name) { | ||
app.renamePage(page.id, name); | ||
editor.renamePage(page.id, name); | ||
} | ||
@@ -328,3 +328,3 @@ }, | ||
id: `page-menu-item-${page.id}`, | ||
"data-wd": `page-menu-item-${page.id}`, | ||
"data-testid": `page-menu-item-${page.id}`, | ||
className: "tlui-page_menu__item__sortable__title", | ||
@@ -349,3 +349,3 @@ style: { height: ITEM_HEIGHT }, | ||
{ | ||
"data-wd": `page-menu-item-${page.id}`, | ||
"data-testid": `page-menu-item-${page.id}`, | ||
className: "tlui-page-menu__item", | ||
@@ -357,3 +357,3 @@ children: [ | ||
className: "tlui-page-menu__item__button tlui-page-menu__item__button__checkbox", | ||
onClick: () => app.setCurrentPageId(page.id), | ||
onClick: () => editor.setCurrentPageId(page.id), | ||
onDoubleClick: toggleEditing, | ||
@@ -375,10 +375,10 @@ isChecked: page.id === currentPage.id, | ||
onRename: () => { | ||
if (app.isIos) { | ||
if (editor.isIos) { | ||
const name = window.prompt("Rename page", page.name); | ||
if (name && name !== page.name) { | ||
app.renamePage(page.id, name); | ||
editor.renamePage(page.id, name); | ||
} | ||
} else { | ||
setIsEditing(true); | ||
app.setCurrentPageId(page.id); | ||
editor.setCurrentPageId(page.id); | ||
} | ||
@@ -385,0 +385,0 @@ } |
@@ -26,8 +26,8 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_signia_react = require("signia-react"); | ||
var import_state = require("@tldraw/state"); | ||
var import_useActions = require("../hooks/useActions"); | ||
var import_Button = require("./primitives/Button"); | ||
const ExitPenMode = (0, import_signia_react.track)(function ExitPenMode2() { | ||
const app = (0, import_editor.useApp)(); | ||
const isPenMode = app.isPenMode; | ||
const ExitPenMode = (0, import_state.track)(function ExitPenMode2() { | ||
const editor = (0, import_editor.useEditor)(); | ||
const isPenMode = editor.isPenMode; | ||
const actions = (0, import_useActions.useActions)(); | ||
@@ -37,4 +37,11 @@ if (!isPenMode) | ||
const action = actions["exit-pen-mode"]; | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Button.Button, { label: action.label, iconLeft: action.icon, onClick: action.onSelect }); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
import_Button.Button, | ||
{ | ||
label: action.label, | ||
iconLeft: action.icon, | ||
onClick: () => action.onSelect("helper-buttons") | ||
} | ||
); | ||
}); | ||
//# sourceMappingURL=PenModeToggle.js.map |
@@ -44,10 +44,11 @@ "use strict"; | ||
const { | ||
uiType, | ||
items, | ||
title, | ||
styleType, | ||
value = null, | ||
onValueChange, | ||
columns = (0, import_primitives.clamp)(items.length, 2, 4) | ||
style, | ||
value, | ||
columns = (0, import_primitives.clamp)(items.length, 2, 4), | ||
onValueChange | ||
} = props; | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
@@ -67,11 +68,11 @@ const rPointing = (0, import_react.useRef)(false); | ||
const { id } = e.currentTarget.dataset; | ||
if (value === id) | ||
if (value.type === "shared" && value.value === id) | ||
return; | ||
app.mark("point picker item"); | ||
onValueChange(items.find((i) => i.id === id), false); | ||
editor.mark("point picker item"); | ||
onValueChange(style, id, false); | ||
}; | ||
const handleButtonPointerDown2 = (e) => { | ||
const { id } = e.currentTarget.dataset; | ||
app.mark("point picker item"); | ||
onValueChange(items.find((i) => i.id === id), true); | ||
editor.mark("point picker item"); | ||
onValueChange(style, id, true); | ||
rPointing.current = true; | ||
@@ -84,7 +85,7 @@ window.addEventListener("pointerup", handlePointerUp); | ||
const { id } = e.currentTarget.dataset; | ||
onValueChange(items.find((i) => i.id === id), true); | ||
onValueChange(style, id, true); | ||
}; | ||
const handleButtonPointerUp2 = (e) => { | ||
const { id } = e.currentTarget.dataset; | ||
onValueChange(items.find((i) => i.id === id), false); | ||
onValueChange(style, id, false); | ||
}; | ||
@@ -97,3 +98,4 @@ return { | ||
}; | ||
}, [app, value, onValueChange, items]); | ||
}, [value, editor, onValueChange, style]); | ||
const theme = (0, import_editor.useValue)("theme", () => (0, import_editor.getDefaultColorTheme)(editor), [editor]); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
@@ -110,9 +112,9 @@ "div", | ||
{ | ||
"data-id": item.id, | ||
"data-wd": `${props["data-wd"]}.${item.id}`, | ||
"aria-label": item.id, | ||
"data-state": value === item.id ? "hinted" : void 0, | ||
title: title + " \u2014 " + msg(`${styleType}-style.${item.id}`), | ||
"data-id": item.value, | ||
"data-testid": `style.${uiType}.${item.value}`, | ||
"aria-label": item.value, | ||
"data-state": value.type === "shared" && value.value === item.value ? "hinted" : void 0, | ||
title: title + " \u2014 " + msg(`${uiType}-style.${item.value}`), | ||
className: (0, import_classnames.default)("tlui-button-grid__button"), | ||
style: item.type === "color" ? { color: `var(--palette-${item.id})` } : void 0, | ||
style: style === import_editor.DefaultColorStyle ? { color: theme[item.value].solid } : void 0, | ||
onPointerEnter: handleButtonPointerEnter, | ||
@@ -124,3 +126,3 @@ onPointerDown: handleButtonPointerDown, | ||
}, | ||
item.id | ||
item.value | ||
)) | ||
@@ -127,0 +129,0 @@ } |
@@ -50,3 +50,3 @@ "use strict"; | ||
function CloseButton() { | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-dialog__header__close", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(_Dialog.DialogClose, { "data-wd": "dialog.close", dir: "ltr", asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Button.Button, { "aria-label": "Close", onTouchEnd: (e) => e.target.click(), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Icon.Icon, { small: true, icon: "cross-2" }) }) }) }); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-dialog__header__close", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(_Dialog.DialogClose, { "data-testid": "dialog.close", dir: "ltr", asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Button.Button, { "aria-label": "Close", onTouchEnd: (e) => e.target.click(), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Icon.Icon, { small: true, icon: "cross-2" }) }) }) }); | ||
} | ||
@@ -53,0 +53,0 @@ function Body({ |
@@ -52,11 +52,13 @@ "use strict"; | ||
id, | ||
open, | ||
children, | ||
modal = false | ||
}) { | ||
const onOpenChange = (0, import_useMenuIsOpen.useMenuIsOpen)(id); | ||
const [open, onOpenChange] = (0, import_useMenuIsOpen.useMenuIsOpen)(id); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenu.Root, { open, dir: "ltr", modal, onOpenChange, children }); | ||
} | ||
function Trigger({ children, "data-wd": dataWd }) { | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenu.Trigger, { dir: "ltr", "data-wd": dataWd, asChild: true, children }); | ||
function Trigger({ | ||
children, | ||
"data-testid": testId | ||
}) { | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenu.Trigger, { dir: "ltr", "data-testid": testId, asChild: true, children }); | ||
} | ||
@@ -84,4 +86,4 @@ function Content({ | ||
} | ||
function Sub({ id, children, open }) { | ||
const onOpenChange = (0, import_useMenuIsOpen.useMenuIsOpen)(id); | ||
function Sub({ id, children }) { | ||
const [open, onOpenChange] = (0, import_useMenuIsOpen.useMenuIsOpen)(id); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenu.Sub, { open, onOpenChange, children }); | ||
@@ -91,6 +93,6 @@ } | ||
label, | ||
"data-wd": dataWd, | ||
"data-testid": testId, | ||
"data-direction": dataDirection | ||
}) { | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenu.SubTrigger, { dir: "ltr", "data-direction": dataDirection, "data-wd": dataWd, asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenu.SubTrigger, { dir: "ltr", "data-direction": dataDirection, "data-testid": testId, asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
import_Button.Button, | ||
@@ -97,0 +99,0 @@ { |
@@ -42,2 +42,3 @@ "use strict"; | ||
icon, | ||
color, | ||
className, | ||
@@ -61,2 +62,3 @@ ...props | ||
style: { | ||
color, | ||
mask: `url(${asset}) center 100% / 100% no-repeat`, | ||
@@ -63,0 +65,0 @@ transform: invertIcon ? "scale(-1, 1)" : void 0 |
@@ -51,4 +51,6 @@ "use strict"; | ||
onCancel, | ||
onBlur, | ||
shouldManuallyMaintainScrollPositionWhenFocused = false, | ||
children | ||
children, | ||
value | ||
}, ref) { | ||
@@ -76,5 +78,5 @@ const rInputRef = React.useRef(null); | ||
(e) => { | ||
const value = e.currentTarget.value; | ||
rCurrentValue.current = value; | ||
onValueChange?.(value); | ||
const value2 = e.currentTarget.value; | ||
rCurrentValue.current = value2; | ||
onValueChange?.(value2); | ||
}, | ||
@@ -103,3 +105,10 @@ [onValueChange] | ||
); | ||
const handleBlur = React.useCallback(() => setIsFocused(false), []); | ||
const handleBlur = React.useCallback( | ||
(e) => { | ||
setIsFocused(false); | ||
const value2 = e.currentTarget.value; | ||
onBlur?.(value2); | ||
}, | ||
[onBlur] | ||
); | ||
React.useEffect(() => { | ||
@@ -138,3 +147,4 @@ const visualViewport = window.visualViewport; | ||
autoFocus: autofocus, | ||
placeholder | ||
placeholder, | ||
value | ||
} | ||
@@ -141,0 +151,0 @@ ), |
@@ -40,8 +40,8 @@ "use strict"; | ||
var import_useMenuIsOpen = require("../../hooks/useMenuIsOpen"); | ||
const Popover = ({ id, open, children, onOpenChange }) => { | ||
const handleOpenChange = (0, import_useMenuIsOpen.useMenuIsOpen)(id, onOpenChange); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PopoverPrimitive.Root, { onOpenChange: handleOpenChange, open, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-popover", children }) }); | ||
const Popover = ({ id, children, onOpenChange }) => { | ||
const [isOpen, handleOpenChange] = (0, import_useMenuIsOpen.useMenuIsOpen)(id, onOpenChange); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PopoverPrimitive.Root, { onOpenChange: handleOpenChange, open: isOpen, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-popover", children }) }); | ||
}; | ||
const PopoverTrigger = ({ children, disabled, "data-wd": dataWd }) => { | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PopoverPrimitive.Trigger, { "data-wd": dataWd, disabled, asChild: true, dir: "ltr", children }); | ||
const PopoverTrigger = ({ children, disabled, "data-testid": testId }) => { | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PopoverPrimitive.Trigger, { "data-testid": testId, disabled, asChild: true, dir: "ltr", children }); | ||
}; | ||
@@ -48,0 +48,0 @@ const PopoverContent = ({ side, children, align = "center", sideOffset = 8, alignOffset = 0 }) => { |
@@ -21,4 +21,2 @@ "use strict"; | ||
__export(shared_exports, { | ||
BASE_URL: () => BASE_URL, | ||
getBaseUrl: () => getBaseUrl, | ||
kbd: () => kbd, | ||
@@ -47,18 +45,2 @@ kbdStr: () => kbdStr, | ||
} | ||
const getBaseUrl = () => { | ||
if (typeof process === "undefined") { | ||
return "http://localhost:5420"; | ||
} | ||
if (process.env.NODE_ENV === "development") { | ||
return "http://localhost:3000"; | ||
} | ||
if (process.env.NEXT_PUBLIC_VERCEL_ENV === "production") { | ||
return "https://www.tldraw.com"; | ||
} | ||
if (process.env.NEXT_PUBLIC_VERCEL_ENV === "preview") { | ||
return `https://${process.env.NEXT_PUBLIC_VERCEL_URL}`; | ||
} | ||
return "http://localhost:3000"; | ||
}; | ||
const BASE_URL = getBaseUrl(); | ||
//# sourceMappingURL=shared.js.map |
@@ -29,5 +29,5 @@ "use strict"; | ||
var import_useTranslation = require("../../hooks/useTranslation/useTranslation"); | ||
function Slider(props) { | ||
const Slider = (0, import_react.memo)(function Slider2(props) { | ||
const { title, steps, value, label, onValueChange } = props; | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
@@ -41,4 +41,4 @@ const handleValueChange = (0, import_react.useCallback)( | ||
const handlePointerDown = (0, import_react.useCallback)(() => { | ||
app.mark("click slider"); | ||
}, [app]); | ||
editor.mark("click slider"); | ||
}, [editor]); | ||
const handlePointerUp = (0, import_react.useCallback)(() => { | ||
@@ -52,3 +52,3 @@ if (!value) | ||
{ | ||
"data-wd": props["data-wd"], | ||
"data-testid": props["data-testid"], | ||
className: "tlui-slider", | ||
@@ -71,3 +71,3 @@ "area-label": "Opacity", | ||
) }); | ||
} | ||
}); | ||
//# sourceMappingURL=Slider.js.map |
@@ -39,7 +39,7 @@ "use strict"; | ||
{ | ||
"data-wd": "main.redo", | ||
"data-testid": "main.redo", | ||
icon: redo.icon, | ||
title: `${msg(redo.label)} ${(0, import_shared.kbdStr)(redo.kbd)}`, | ||
disabled: !canRedo, | ||
onClick: redo.onSelect, | ||
onClick: () => redo.onSelect("quick-actions"), | ||
smallIcon: true | ||
@@ -46,0 +46,0 @@ } |
@@ -26,14 +26,21 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_signia_react = require("signia-react"); | ||
var import_state = require("@tldraw/state"); | ||
var import_useActions = require("../hooks/useActions"); | ||
var import_Button = require("./primitives/Button"); | ||
const StopFollowing = (0, import_signia_react.track)(function ExitPenMode() { | ||
const app = (0, import_editor.useApp)(); | ||
const StopFollowing = (0, import_state.track)(function ExitPenMode() { | ||
const editor = (0, import_editor.useEditor)(); | ||
const actions = (0, import_useActions.useActions)(); | ||
if (!app.instanceState.followingUserId) { | ||
if (!editor.instanceState.followingUserId) { | ||
return null; | ||
} | ||
const action = actions["stop-following"]; | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Button.Button, { label: action.label, iconLeft: action.icon, onClick: action.onSelect }); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
import_Button.Button, | ||
{ | ||
label: action.label, | ||
iconLeft: action.icon, | ||
onClick: () => action.onSelect("people-menu") | ||
} | ||
); | ||
}); | ||
//# sourceMappingURL=StopFollowing.js.map |
@@ -42,4 +42,5 @@ "use strict"; | ||
const DoubleDropdownPicker = React.memo(function DoubleDropdownPicker2({ | ||
"data-wd": dataWd, | ||
label, | ||
uiTypeA, | ||
uiTypeB, | ||
labelA, | ||
@@ -49,4 +50,4 @@ labelB, | ||
itemsB, | ||
styleTypeA, | ||
styleTypeB, | ||
styleA, | ||
styleB, | ||
valueA, | ||
@@ -58,7 +59,7 @@ valueB, | ||
const iconA = React.useMemo( | ||
() => itemsA.find((item) => item.id === valueA)?.icon ?? "mixed", | ||
() => itemsA.find((item) => valueA.type === "shared" && valueA.value === item.value)?.icon ?? "mixed", | ||
[itemsA, valueA] | ||
); | ||
const iconB = React.useMemo( | ||
() => itemsB.find((item) => item.id === valueB)?.icon ?? "mixed", | ||
() => itemsB.find((item) => valueB.type === "shared" && valueB.value === item.value)?.icon ?? "mixed", | ||
[itemsB, valueB] | ||
@@ -68,12 +69,10 @@ ); | ||
return null; | ||
const startWdPrefix = `${dataWd}.start`; | ||
const endWdPrefix = `${dataWd}.end`; | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-style-panel__double-select-picker", children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { title: msg(label), className: "tlui-style-panel__double-select-picker-label", children: msg(label) }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenu.Root, { id: `style panel ${styleTypeA}`, children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenu.Root, { id: `style panel ${uiTypeA} A`, children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_dropdown_menu.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
import_Button.Button, | ||
{ | ||
"data-wd": startWdPrefix, | ||
title: msg(labelA) + " \u2014 " + (valueA === null ? msg("style-panel.mixed") : msg(`${styleTypeA}-style.${valueA}`)), | ||
"data-testid": `style.${uiTypeA}`, | ||
title: msg(labelA) + " \u2014 " + (valueA === null ? msg("style-panel.mixed") : msg(`${uiTypeA}-style.${valueA}`)), | ||
icon: iconA, | ||
@@ -96,9 +95,9 @@ invertIcon: true, | ||
className: "tlui-button-grid__button", | ||
title: msg(labelA) + " \u2014 " + msg(`${styleTypeA}-style.${item.id}`), | ||
"data-wd": `${startWdPrefix}.${item.id}`, | ||
title: msg(labelA) + " \u2014 " + msg(`${uiTypeA}-style.${item.value}`), | ||
"data-testid": `style.${uiTypeA}.${item.value}`, | ||
icon: item.icon, | ||
onClick: () => onValueChange(item, false), | ||
onClick: () => onValueChange(styleA, item.value, false), | ||
invertIcon: true | ||
}, | ||
item.id | ||
item.value | ||
); | ||
@@ -109,8 +108,8 @@ }) | ||
] }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenu.Root, { id: `style panel ${styleTypeB}`, children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenu.Root, { id: `style panel ${uiTypeB}`, children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_dropdown_menu.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
import_Button.Button, | ||
{ | ||
"data-wd": endWdPrefix, | ||
title: msg(labelB) + " \u2014 " + (valueB === null ? msg("style-panel.mixed") : msg(`${styleTypeB}-style.${valueB}`)), | ||
"data-testid": `style.${uiTypeB}`, | ||
title: msg(labelB) + " \u2014 " + (valueB === null ? msg("style-panel.mixed") : msg(`${uiTypeB}-style.${valueB}`)), | ||
icon: iconB, | ||
@@ -132,8 +131,8 @@ smallIcon: true | ||
className: "tlui-button-grid__button", | ||
title: msg(labelB) + " \u2014 " + msg(`${styleTypeB}-style.${item.id}`), | ||
"data-wd": `${endWdPrefix}.${item.id}`, | ||
title: msg(labelB) + " \u2014 " + msg(`${uiTypeB}-style.${item.value}`), | ||
"data-testid": `style.${uiTypeB}.${item.value}`, | ||
icon: item.icon, | ||
onClick: () => onValueChange(item, false) | ||
onClick: () => onValueChange(styleB, item.value, false) | ||
}, | ||
item.id | ||
item.value | ||
); | ||
@@ -140,0 +139,0 @@ }) |
@@ -43,11 +43,14 @@ "use strict"; | ||
id, | ||
label, | ||
uiType, | ||
style, | ||
items, | ||
styleType, | ||
label, | ||
value, | ||
onValueChange, | ||
"data-wd": dataWd | ||
onValueChange | ||
}) { | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
const icon = React.useMemo(() => items.find((item) => item.id === value)?.icon, [items, value]); | ||
const icon = React.useMemo( | ||
() => items.find((item) => value.type === "shared" && item.value === value.value)?.icon, | ||
[items, value] | ||
); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenu.Root, { id: `style panel ${id}`, children: [ | ||
@@ -57,4 +60,4 @@ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_dropdown_menu.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
{ | ||
"data-wd": dataWd, | ||
title: value === null ? msg("style-panel.mixed") : msg(`${styleType}-style.${value}`), | ||
"data-testid": `style.${uiType}`, | ||
title: value.type === "mixed" ? msg("style-panel.mixed") : msg(`${uiType}-style.${value.value}`), | ||
label, | ||
@@ -68,3 +71,4 @@ icon: icon ?? "mixed" | ||
className: (0, import_classnames.default)("tlui-button-grid", { | ||
"tlui-button-grid__two": items.length < 4, | ||
"tlui-button-grid__two": items.length < 3, | ||
"tlui-button-grid__three": items.length == 3, | ||
"tlui-button-grid__four": items.length >= 4 | ||
@@ -77,8 +81,8 @@ }), | ||
className: "tlui-button-grid__button", | ||
"data-wd": `${dataWd}.${item.id}`, | ||
title: msg(`${styleType}-style.${item.id}`), | ||
"data-testid": `style.${uiType}.${item.value}`, | ||
title: msg(`${uiType}-style.${item.value}`), | ||
icon: item.icon, | ||
onClick: () => onValueChange(item, false) | ||
onClick: () => onValueChange(style, item.value, false) | ||
}, | ||
item.id | ||
item.value | ||
); | ||
@@ -85,0 +89,0 @@ }) |
@@ -36,4 +36,5 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_utils = require("@tldraw/utils"); | ||
var import_react = __toESM(require("react")); | ||
var import_signia_react = require("signia-react"); | ||
var import_useTranslation = require("../../hooks/useTranslation/useTranslation"); | ||
@@ -45,13 +46,32 @@ var import_Button = require("../primitives/Button"); | ||
var import_DropdownPicker = require("./DropdownPicker"); | ||
const StylePanel = (0, import_signia_react.track)(function StylePanel2({ isMobile }) { | ||
const app = (0, import_editor.useApp)(); | ||
const props = app.props; | ||
var import_styles = require("./styles"); | ||
const selectToolStyles = [import_editor.DefaultColorStyle, import_editor.DefaultDashStyle, import_editor.DefaultFillStyle, import_editor.DefaultSizeStyle]; | ||
function getRelevantStyles(editor) { | ||
const styles = new import_editor.SharedStyleMap(editor.sharedStyles); | ||
const hasShape = editor.selectedIds.length > 0 || !!editor.root.current.value?.shapeType; | ||
if (styles.size === 0 && editor.isIn("select") && editor.selectedIds.length === 0) { | ||
for (const style of selectToolStyles) { | ||
styles.applyValue(style, editor.getStyleForNextShape(style)); | ||
} | ||
} | ||
if (styles.size === 0 && !hasShape) | ||
return null; | ||
return { styles, opacity: editor.sharedOpacity }; | ||
} | ||
const StylePanel = function StylePanel2({ isMobile }) { | ||
const editor = (0, import_editor.useEditor)(); | ||
const relevantStyles = (0, import_state.useValue)("getRelevantStyles", () => getRelevantStyles(editor), [editor]); | ||
const handlePointerOut = (0, import_react.useCallback)(() => { | ||
if (!isMobile) { | ||
app.isChangingStyle = false; | ||
editor.isChangingStyle = false; | ||
} | ||
}, [app, isMobile]); | ||
if (!props) | ||
}, [editor, isMobile]); | ||
if (!relevantStyles) | ||
return null; | ||
const { geo, arrowheadEnd, arrowheadStart, spline, font } = props; | ||
const { styles, opacity } = relevantStyles; | ||
const geo = styles.get(import_editor.GeoShapeGeoStyle); | ||
const arrowheadEnd = styles.get(import_editor.ArrowShapeArrowheadEndStyle); | ||
const arrowheadStart = styles.get(import_editor.ArrowShapeArrowheadStartStyle); | ||
const spline = styles.get(import_editor.LineShapeSplineStyle); | ||
const font = styles.get(import_editor.DefaultFontStyle); | ||
const hideGeo = geo === void 0; | ||
@@ -62,26 +82,26 @@ const hideArrowHeads = arrowheadEnd === void 0 && arrowheadStart === void 0; | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-style-panel", "data-ismobile": isMobile, onPointerLeave: handlePointerOut, children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommonStylePickerSet, { props }), | ||
!hideText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextStylePickerSet, { props }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommonStylePickerSet, { styles, opacity }), | ||
!hideText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextStylePickerSet, { styles }), | ||
!(hideGeo && hideArrowHeads && hideSpline) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-style-panel__section", "aria-label": "style panel styles", children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(GeoStylePickerSet, { props }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowheadStylePickerSet, { props }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SplineStylePickerSet, { props }) | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(GeoStylePickerSet, { styles }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowheadStylePickerSet, { styles }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SplineStylePickerSet, { styles }) | ||
] }) | ||
] }); | ||
}); | ||
const { styles } = import_editor.App; | ||
}; | ||
function useStyleChangeCallback() { | ||
const app = (0, import_editor.useApp)(); | ||
return import_react.default.useCallback( | ||
(item, squashing) => { | ||
app.batch(() => { | ||
app.setProp(item.type, item.id, false, squashing); | ||
app.isChangingStyle = true; | ||
}); | ||
}, | ||
[app] | ||
); | ||
const editor = (0, import_editor.useEditor)(); | ||
return import_react.default.useMemo(() => { | ||
return function(style, value, squashing) { | ||
editor.setStyle(style, value, squashing); | ||
editor.isChangingStyle = true; | ||
}; | ||
}, [editor]); | ||
} | ||
function CommonStylePickerSet({ props }) { | ||
const app = (0, import_editor.useApp)(); | ||
const tldrawSupportedOpacities = [0.1, 0.25, 0.5, 0.75, 1]; | ||
function CommonStylePickerSet({ | ||
styles, | ||
opacity | ||
}) { | ||
const editor = (0, import_editor.useEditor)(); | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
@@ -91,14 +111,19 @@ const handleValueChange = useStyleChangeCallback(); | ||
(value, ephemeral) => { | ||
const item = styles.opacity[value]; | ||
app.setProp(item.type, item.id, ephemeral); | ||
app.isChangingStyle = true; | ||
const item = tldrawSupportedOpacities[value]; | ||
editor.setOpacity(item, ephemeral); | ||
editor.isChangingStyle = true; | ||
}, | ||
[app] | ||
[editor] | ||
); | ||
const { color, fill, dash, size, opacity } = props; | ||
if (color === void 0 && fill === void 0 && dash === void 0 && size === void 0 && opacity === void 0) { | ||
return null; | ||
} | ||
const showPickers = fill || dash || size; | ||
const opacityIndex = styles.opacity.findIndex((s) => s.id === opacity); | ||
const color = styles.get(import_editor.DefaultColorStyle); | ||
const fill = styles.get(import_editor.DefaultFillStyle); | ||
const dash = styles.get(import_editor.DefaultDashStyle); | ||
const size = styles.get(import_editor.DefaultSizeStyle); | ||
const showPickers = fill !== void 0 || dash !== void 0 || size !== void 0; | ||
const opacityIndex = opacity.type === "mixed" ? -1 : tldrawSupportedOpacities.indexOf( | ||
(0, import_utils.minBy)( | ||
tldrawSupportedOpacities, | ||
(supportedOpacity) => Math.abs(supportedOpacity - opacity.value) | ||
) | ||
); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ | ||
@@ -110,5 +135,5 @@ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-style-panel__section__common", "aria-label": "style panel styles", children: [ | ||
title: msg("style-panel.color"), | ||
styleType: "color", | ||
"data-wd": "style.color", | ||
items: styles.color, | ||
uiType: "color", | ||
style: import_editor.DefaultColorStyle, | ||
items: import_styles.STYLES.color, | ||
value: color, | ||
@@ -121,7 +146,7 @@ onValueChange: handleValueChange | ||
{ | ||
"data-wd": "style.opacity", | ||
value: opacityIndex >= 0 ? opacityIndex : styles.opacity.length - 1, | ||
label: opacity ? `opacity-style.${opacity}` : "style-panel.mixed", | ||
"data-testid": "style.opacity", | ||
value: opacityIndex >= 0 ? opacityIndex : tldrawSupportedOpacities.length - 1, | ||
label: opacity.type === "mixed" ? "style-panel.mixed" : `opacity-style.${opacity}`, | ||
onValueChange: handleOpacityValueChange, | ||
steps: styles.opacity.length - 1, | ||
steps: tldrawSupportedOpacities.length - 1, | ||
title: msg("style-panel.opacity") | ||
@@ -136,5 +161,5 @@ } | ||
title: msg("style-panel.fill"), | ||
styleType: "fill", | ||
"data-wd": "style.fill", | ||
items: styles.fill, | ||
uiType: "fill", | ||
style: import_editor.DefaultFillStyle, | ||
items: import_styles.STYLES.fill, | ||
value: fill, | ||
@@ -148,5 +173,5 @@ onValueChange: handleValueChange | ||
title: msg("style-panel.dash"), | ||
styleType: "dash", | ||
"data-wd": "style.dash", | ||
items: styles.dash, | ||
uiType: "dash", | ||
style: import_editor.DefaultDashStyle, | ||
items: import_styles.STYLES.dash, | ||
value: dash, | ||
@@ -160,5 +185,5 @@ onValueChange: handleValueChange | ||
title: msg("style-panel.size"), | ||
styleType: "size", | ||
"data-wd": "style.size", | ||
items: styles.size, | ||
uiType: "size", | ||
style: import_editor.DefaultSizeStyle, | ||
items: import_styles.STYLES.size, | ||
value: size, | ||
@@ -171,6 +196,8 @@ onValueChange: handleValueChange | ||
} | ||
function TextStylePickerSet({ props }) { | ||
function TextStylePickerSet({ styles }) { | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
const handleValueChange = useStyleChangeCallback(); | ||
const { font, align } = props; | ||
const font = styles.get(import_editor.DefaultFontStyle); | ||
const align = styles.get(import_editor.DefaultHorizontalAlignStyle); | ||
const verticalAlign = styles.get(import_editor.DefaultVerticalAlignStyle); | ||
if (font === void 0 && align === void 0) { | ||
@@ -184,5 +211,5 @@ return null; | ||
title: msg("style-panel.font"), | ||
styleType: "font", | ||
"data-wd": "font", | ||
items: styles.font, | ||
uiType: "font", | ||
style: import_editor.DefaultFontStyle, | ||
items: import_styles.STYLES.font, | ||
value: font, | ||
@@ -197,5 +224,5 @@ onValueChange: handleValueChange | ||
title: msg("style-panel.align"), | ||
styleType: "align", | ||
"data-wd": "align", | ||
items: styles.align, | ||
uiType: "align", | ||
style: import_editor.DefaultHorizontalAlignStyle, | ||
items: import_styles.STYLES.horizontalAlign, | ||
value: align, | ||
@@ -205,10 +232,20 @@ onValueChange: handleValueChange | ||
), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
verticalAlign === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
import_Button.Button, | ||
{ | ||
title: msg("style-panel.position"), | ||
"data-wd": "position", | ||
icon: "align-center-center", | ||
title: msg("style-panel.vertical-align"), | ||
"data-testid": "vertical-align", | ||
icon: "vertical-align-center", | ||
disabled: true | ||
} | ||
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
import_DropdownPicker.DropdownPicker, | ||
{ | ||
id: "geo-vertical-alignment", | ||
uiType: "verticalAlign", | ||
style: import_editor.DefaultVerticalAlignStyle, | ||
items: import_styles.STYLES.verticalAlign, | ||
value: verticalAlign, | ||
onValueChange: handleValueChange | ||
} | ||
) | ||
@@ -218,5 +255,5 @@ ] }) | ||
} | ||
function GeoStylePickerSet({ props }) { | ||
function GeoStylePickerSet({ styles }) { | ||
const handleValueChange = useStyleChangeCallback(); | ||
const { geo } = props; | ||
const geo = styles.get(import_editor.GeoShapeGeoStyle); | ||
if (geo === void 0) { | ||
@@ -230,5 +267,5 @@ return null; | ||
label: "style-panel.geo", | ||
styleType: "geo", | ||
"data-wd": "style-panel.geo", | ||
items: styles.geo, | ||
uiType: "geo", | ||
style: import_editor.GeoShapeGeoStyle, | ||
items: import_styles.STYLES.geo, | ||
value: geo, | ||
@@ -239,5 +276,5 @@ onValueChange: handleValueChange | ||
} | ||
function SplineStylePickerSet({ props }) { | ||
function SplineStylePickerSet({ styles }) { | ||
const handleValueChange = useStyleChangeCallback(); | ||
const { spline } = props; | ||
const spline = styles.get(import_editor.LineShapeSplineStyle); | ||
if (spline === void 0) { | ||
@@ -251,5 +288,5 @@ return null; | ||
label: "style-panel.spline", | ||
styleType: "spline", | ||
"data-wd": "style.spline", | ||
items: styles.spline, | ||
uiType: "spline", | ||
style: import_editor.LineShapeSplineStyle, | ||
items: import_styles.STYLES.spline, | ||
value: spline, | ||
@@ -260,6 +297,7 @@ onValueChange: handleValueChange | ||
} | ||
function ArrowheadStylePickerSet({ props }) { | ||
function ArrowheadStylePickerSet({ styles }) { | ||
const handleValueChange = useStyleChangeCallback(); | ||
const { arrowheadEnd, arrowheadStart } = props; | ||
if (arrowheadEnd === void 0 && arrowheadStart === void 0) { | ||
const arrowheadEnd = styles.get(import_editor.ArrowShapeArrowheadEndStyle); | ||
const arrowheadStart = styles.get(import_editor.ArrowShapeArrowheadStartStyle); | ||
if (!arrowheadEnd || !arrowheadStart) { | ||
return null; | ||
@@ -271,8 +309,9 @@ } | ||
label: "style-panel.arrowheads", | ||
styleTypeA: "arrowheadStart", | ||
"data-wd": "style.arrowheads", | ||
itemsA: styles.arrowheadStart, | ||
uiTypeA: "arrowheadStart", | ||
styleA: import_editor.ArrowShapeArrowheadStartStyle, | ||
itemsA: import_styles.STYLES.arrowheadStart, | ||
valueA: arrowheadStart, | ||
styleTypeB: "arrowheadEnd", | ||
itemsB: styles.arrowheadEnd, | ||
uiTypeB: "arrowheadEnd", | ||
styleB: import_editor.ArrowShapeArrowheadEndStyle, | ||
itemsB: import_styles.STYLES.arrowheadEnd, | ||
valueB: arrowheadEnd, | ||
@@ -279,0 +318,0 @@ onValueChange: handleValueChange, |
@@ -36,16 +36,23 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_classnames = __toESM(require("classnames")); | ||
var import_signia_react = require("signia-react"); | ||
var import_useBreakpoint = require("../../hooks/useBreakpoint"); | ||
var import_useTranslation = require("../../hooks/useTranslation/useTranslation"); | ||
var import_Button = require("../primitives/Button"); | ||
const ToggleToolLockedButton = function ToggleToolLockedButton2({ | ||
activeToolId | ||
}) { | ||
const app = (0, import_editor.useApp)(); | ||
const NOT_LOCKABLE_TOOLS = [ | ||
"select", | ||
"hand", | ||
"draw", | ||
"eraser", | ||
"text", | ||
"zoom", | ||
"laser", | ||
"highlight" | ||
]; | ||
function ToggleToolLockedButton({ activeToolId }) { | ||
const editor = (0, import_editor.useEditor)(); | ||
const breakpoint = (0, import_useBreakpoint.useBreakpoint)(); | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
const isToolLocked = (0, import_signia_react.useValue)("is tool locked", () => app.instanceState.isToolLocked, [app]); | ||
const isLockable = !(activeToolId === "select" || activeToolId === "select.crop" || activeToolId === "hand" || activeToolId === "draw" || activeToolId === "eraser" || activeToolId === "text"); | ||
if (!isLockable) | ||
const isToolLocked = (0, import_state.useValue)("is tool locked", () => editor.instanceState.isToolLocked, [editor]); | ||
if (!activeToolId || NOT_LOCKABLE_TOOLS.includes(activeToolId)) | ||
return null; | ||
@@ -60,7 +67,7 @@ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
icon: isToolLocked ? "lock" : "unlock", | ||
onClick: () => app.updateInstanceState({ isToolLocked: !isToolLocked }), | ||
onClick: () => editor.updateInstanceState({ isToolLocked: !isToolLocked }), | ||
smallIcon: true | ||
} | ||
); | ||
}; | ||
} | ||
//# sourceMappingURL=ToggleToolLockedButton.js.map |
@@ -36,7 +36,7 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_classnames = __toESM(require("classnames")); | ||
var import_react = __toESM(require("react")); | ||
var import_signia_react = require("signia-react"); | ||
var import_menuHelpers = require("../../hooks/menuHelpers"); | ||
var import_useBreakpoint = require("../../hooks/useBreakpoint"); | ||
var import_useReadonly = require("../../hooks/useReadonly"); | ||
var import_useToolbarSchema = require("../../hooks/useToolbarSchema"); | ||
@@ -54,14 +54,20 @@ var import_useTranslation = require("../../hooks/useTranslation/useTranslation"); | ||
var import_ToggleToolLockedButton = require("./ToggleToolLockedButton"); | ||
const Toolbar = (0, import_signia_react.track)(function Toolbar2() { | ||
const app = (0, import_editor.useApp)(); | ||
const Toolbar = (0, import_react.memo)(function Toolbar2() { | ||
const editor = (0, import_editor.useEditor)(); | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
const breakpoint = (0, import_useBreakpoint.useBreakpoint)(); | ||
const rMostRecentlyActiveDropdownItem = import_react.default.useRef(void 0); | ||
const isReadonlyMode = app.isReadOnly; | ||
const isReadOnly = (0, import_useReadonly.useReadonly)(); | ||
const toolbarItems = (0, import_useToolbarSchema.useToolbarSchema)(); | ||
const activeToolId = app.currentToolId; | ||
const geoState = app.props ? app.props.geo : void 0; | ||
const laserTool = toolbarItems.find((item) => item.toolItem.id === "laser"); | ||
const activeToolId = (0, import_state.useValue)("current tool id", () => editor.currentToolId, [editor]); | ||
const isHandTool = activeToolId === "hand"; | ||
const geoState = (0, import_state.useValue)("geo", () => editor.sharedStyles.getAsKnownValue(import_editor.GeoShapeGeoStyle), [ | ||
editor | ||
]); | ||
const showEditingTools = !isReadOnly; | ||
const showExtraActions = !(isReadOnly || isHandTool); | ||
const getTitle = (item) => item.label ? `${msg(item.label)} ${item.kbd ? (0, import_shared.kbdStr)(item.kbd) : ""}` : ""; | ||
const activeToolbarItem = toolbarItems.find((item) => { | ||
return (0, import_menuHelpers.isActiveToolItem)(item.toolItem, activeToolId, geoState); | ||
const activeTLUiToolbarItem = toolbarItems.find((item) => { | ||
return isActiveTLUiToolItem(item.toolItem, activeToolId, geoState); | ||
}); | ||
@@ -78,3 +84,3 @@ const { itemsInPanel, itemsInDropdown, dropdownFirstItem } = import_react.default.useMemo(() => { | ||
} else { | ||
if (item === activeToolbarItem) { | ||
if (item === activeTLUiToolbarItem) { | ||
dropdownFirstItem2 = item; | ||
@@ -101,8 +107,6 @@ } | ||
return { itemsInPanel: itemsInPanel2, itemsInDropdown: itemsInDropdown2, dropdownFirstItem: dropdownFirstItem2 }; | ||
}, [toolbarItems, activeToolbarItem, breakpoint]); | ||
const showEditingTools = !isReadonlyMode; | ||
const showExtraActions = !(isReadonlyMode || app.isIn("hand")); | ||
}, [toolbarItems, activeTLUiToolbarItem, breakpoint]); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-toolbar", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-toolbar__inner", children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-toolbar__left", children: [ | ||
!isReadonlyMode && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( | ||
!isReadOnly && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( | ||
"div", | ||
@@ -114,3 +118,3 @@ { | ||
children: [ | ||
breakpoint < 5 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-toolbar__extras__controls", children: [ | ||
breakpoint < 6 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-toolbar__extras__controls", children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_UndoButton.UndoButton, {}), | ||
@@ -133,11 +137,22 @@ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_RedoButton.RedoButton, {}), | ||
children: [ | ||
toolbarItems.slice(0, 2).map(({ toolItem }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
toolbarItems.slice(0, 2).map(({ toolItem }) => { | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
ToolbarButton, | ||
{ | ||
item: toolItem, | ||
title: getTitle(toolItem), | ||
isSelected: isActiveTLUiToolItem(toolItem, activeToolId, geoState) | ||
}, | ||
toolItem.id | ||
); | ||
}), | ||
isReadOnly && laserTool && /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
ToolbarButton, | ||
{ | ||
item: toolItem, | ||
title: getTitle(toolItem), | ||
isSelected: (0, import_menuHelpers.isActiveToolItem)(toolItem, activeToolId, geoState) | ||
item: laserTool.toolItem, | ||
title: getTitle(laserTool.toolItem), | ||
isSelected: isActiveTLUiToolItem(laserTool.toolItem, activeToolId, geoState) | ||
}, | ||
toolItem.id | ||
)), | ||
laserTool.toolItem.id | ||
), | ||
showEditingTools && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ | ||
@@ -150,3 +165,3 @@ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-toolbar__divider" }), | ||
title: getTitle(toolItem), | ||
isSelected: (0, import_menuHelpers.isActiveToolItem)(toolItem, activeToolId, geoState) | ||
isSelected: isActiveTLUiToolItem(toolItem, activeToolId, geoState) | ||
}, | ||
@@ -161,3 +176,3 @@ toolItem.id | ||
title: getTitle(toolItem), | ||
isSelected: (0, import_menuHelpers.isActiveToolItem)(toolItem, activeToolId, geoState) | ||
isSelected: isActiveTLUiToolItem(toolItem, activeToolId, geoState) | ||
}, | ||
@@ -172,3 +187,3 @@ toolItem.id | ||
title: getTitle(dropdownFirstItem.toolItem), | ||
isSelected: (0, import_menuHelpers.isActiveToolItem)( | ||
isSelected: isActiveTLUiToolItem( | ||
dropdownFirstItem.toolItem, | ||
@@ -187,3 +202,3 @@ activeToolId, | ||
icon: "chevron-up", | ||
"data-wd": "tools.more", | ||
"data-testid": "tools.more", | ||
title: msg("tool-panel.more") | ||
@@ -200,6 +215,6 @@ } | ||
] }), | ||
breakpoint < 5 && !isReadonlyMode && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-toolbar__tools", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_MobileStylePanel.MobileStylePanel, {}) }) | ||
breakpoint < 5 && !isReadOnly && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-toolbar__tools", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_MobileStylePanel.MobileStylePanel, {}) }) | ||
] }) }); | ||
}); | ||
const OverflowToolsContent = (0, import_signia_react.track)(function OverflowToolsContent2({ | ||
const OverflowToolsContent = (0, import_state.track)(function OverflowToolsContent2({ | ||
toolbarItems | ||
@@ -213,7 +228,7 @@ }) { | ||
className: "tlui-button-grid__button", | ||
"data-wd": `tools.${id}`, | ||
"data-testid": `tools.${id}`, | ||
"data-tool": id, | ||
"data-geo": meta?.geo ?? "", | ||
"aria-label": label, | ||
onClick: onSelect, | ||
onClick: () => onSelect("toolbar"), | ||
title: label ? `${msg(label)} ${kbd ? (0, import_shared.kbdStr)(kbd) : ""}` : "", | ||
@@ -235,3 +250,3 @@ icon | ||
className: "tlui-toolbar__tools__button", | ||
"data-wd": `tools.${item.id}`, | ||
"data-testid": `tools.${item.id}`, | ||
"data-tool": item.id, | ||
@@ -243,11 +258,13 @@ "data-geo": item.meta?.geo ?? "", | ||
"data-state": isSelected ? "selected" : void 0, | ||
onClick: item.onSelect, | ||
onClick: () => item.onSelect("toolbar"), | ||
onTouchStart: (e) => { | ||
(0, import_editor.preventDefault)(e); | ||
item.onSelect(); | ||
item.onSelect("toolbar"); | ||
} | ||
}, | ||
item.id | ||
} | ||
); | ||
} | ||
const isActiveTLUiToolItem = (item, activeToolId, geoState) => { | ||
return item.meta?.geo ? activeToolId === "geo" && geoState === item.meta?.geo : activeToolId === item.id; | ||
}; | ||
//# sourceMappingURL=Toolbar.js.map |
@@ -26,3 +26,3 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_signia_react = require("signia-react"); | ||
var import_state = require("@tldraw/state"); | ||
var import_useActions = require("../hooks/useActions"); | ||
@@ -33,4 +33,4 @@ var import_useReadonly = require("../hooks/useReadonly"); | ||
var import_shared = require("./primitives/shared"); | ||
const TrashButton = (0, import_signia_react.track)(function TrashButton2() { | ||
const app = (0, import_editor.useApp)(); | ||
const TrashButton = (0, import_state.track)(function TrashButton2() { | ||
const editor = (0, import_editor.useEditor)(); | ||
const actions = (0, import_useActions.useActions)(); | ||
@@ -42,3 +42,3 @@ const msg = (0, import_useTranslation.useTranslation)(); | ||
return null; | ||
const noSelected = app.selectedIds.length <= 0; | ||
const noSelected = editor.selectedIds.length <= 0; | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
@@ -48,3 +48,3 @@ import_Button.Button, | ||
icon: action.icon, | ||
onClick: action.onSelect, | ||
onClick: () => action.onSelect("quick-actions"), | ||
disabled: noSelected, | ||
@@ -51,0 +51,0 @@ title: `${msg(action.label)} ${(0, import_shared.kbdStr)(action.kbd)}`, |
@@ -39,7 +39,7 @@ "use strict"; | ||
{ | ||
"data-wd": "main.undo", | ||
"data-testid": "main.undo", | ||
icon: undo.icon, | ||
title: `${msg(undo.label)} ${(0, import_shared.kbdStr)(undo.kbd)}`, | ||
disabled: !canUndo, | ||
onClick: undo.onSelect, | ||
onClick: () => undo.onSelect("quick-actions"), | ||
smallIcon: true | ||
@@ -46,0 +46,0 @@ } |
@@ -23,3 +23,2 @@ "use strict"; | ||
findMenuItem: () => findMenuItem, | ||
isActiveToolItem: () => isActiveToolItem, | ||
menuCustom: () => menuCustom, | ||
@@ -29,3 +28,3 @@ menuGroup: () => menuGroup, | ||
menuSubmenu: () => menuSubmenu, | ||
showUiPaste: () => showUiPaste, | ||
showMenuPaste: () => showMenuPaste, | ||
useAllowGroup: () => useAllowGroup, | ||
@@ -37,4 +36,4 @@ useAllowUngroup: () => useAllowUngroup, | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_utils = require("@tldraw/utils"); | ||
var import_signia_react = require("signia-react"); | ||
function compactMenuItems(arr) { | ||
@@ -95,6 +94,6 @@ return arr.filter((i) => i !== void 0 && i !== null && i !== false); | ||
} | ||
function shapesWithUnboundArrows(app) { | ||
const { selectedIds } = app; | ||
function shapesWithUnboundArrows(editor) { | ||
const { selectedIds } = editor; | ||
const selectedShapes = selectedIds.map((id) => { | ||
return app.getShapeById(id); | ||
return editor.getShapeById(id); | ||
}); | ||
@@ -104,6 +103,6 @@ return selectedShapes.filter((shape) => { | ||
return false; | ||
if (import_editor.TLArrowShapeDef.is(shape) && shape.props.start.type === "binding") { | ||
if (editor.isShapeOfType(shape, import_editor.ArrowShapeUtil) && shape.props.start.type === "binding") { | ||
return false; | ||
} | ||
if (import_editor.TLArrowShapeDef.is(shape) && shape.props.end.type === "binding") { | ||
if (editor.isShapeOfType(shape, import_editor.ArrowShapeUtil) && shape.props.end.type === "binding") { | ||
return false; | ||
@@ -115,21 +114,17 @@ } | ||
const useThreeStackableItems = () => { | ||
const app = (0, import_editor.useApp)(); | ||
return (0, import_signia_react.useValue)("threeStackableItems", () => shapesWithUnboundArrows(app).length > 2, [app]); | ||
const editor = (0, import_editor.useEditor)(); | ||
return (0, import_state.useValue)("threeStackableItems", () => shapesWithUnboundArrows(editor).length > 2, [editor]); | ||
}; | ||
const useAllowGroup = () => { | ||
const app = (0, import_editor.useApp)(); | ||
return (0, import_signia_react.useValue)("allowGroup", () => shapesWithUnboundArrows(app).length > 1, [app]); | ||
const editor = (0, import_editor.useEditor)(); | ||
return (0, import_state.useValue)("allowGroup", () => shapesWithUnboundArrows(editor).length > 1, [editor]); | ||
}; | ||
const useAllowUngroup = () => { | ||
const app = (0, import_editor.useApp)(); | ||
return (0, import_signia_react.useValue)( | ||
const editor = (0, import_editor.useEditor)(); | ||
return (0, import_state.useValue)( | ||
"allowUngroup", | ||
() => app.selectedIds.some((id) => app.getShapeById(id)?.type === "group"), | ||
() => editor.selectedIds.some((id) => editor.getShapeById(id)?.type === "group"), | ||
[] | ||
); | ||
}; | ||
const showUiPaste = typeof window !== "undefined" && "navigator" in window && Boolean(navigator.clipboard) && Boolean(navigator.clipboard.read); | ||
const isActiveToolItem = (item, activeToolId, geoState) => { | ||
return item.meta?.geo ? activeToolId === "geo" && geoState === item.meta?.geo : activeToolId === item.id; | ||
}; | ||
function findMenuItem(menu, path) { | ||
@@ -158,2 +153,3 @@ const item = _findMenuItem(menu, path); | ||
} | ||
const showMenuPaste = typeof window !== "undefined" && "navigator" in window && Boolean(navigator.clipboard) && Boolean(navigator.clipboard.read); | ||
//# sourceMappingURL=menuHelpers.js.map |
@@ -42,5 +42,7 @@ "use strict"; | ||
var import_EditLinkDialog = require("../components/EditLinkDialog"); | ||
var import_EmbedDialog = require("../components/EmbedDialog"); | ||
var import_useClipboardEvents = require("./useClipboardEvents"); | ||
var import_useCopyAs = require("./useCopyAs"); | ||
var import_useDialogsProvider = require("./useDialogsProvider"); | ||
var import_useEventsProvider = require("./useEventsProvider"); | ||
var import_useExportAs = require("./useExportAs"); | ||
@@ -55,3 +57,3 @@ var import_useInsertMedia = require("./useInsertMedia"); | ||
function ActionsProvider({ overrides, children }) { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const { addDialog, clearDialogs } = (0, import_useDialogsProvider.useDialogs)(); | ||
@@ -61,38 +63,8 @@ const { clearToasts } = (0, import_useToastsProvider.useToasts)(); | ||
const printSelectionOrPages = (0, import_usePrint.usePrint)(); | ||
const { cut, copy } = (0, import_useClipboardEvents.useMenuClipboardEvents)(); | ||
const { cut, copy, paste } = (0, import_useClipboardEvents.useMenuClipboardEvents)(); | ||
const copyAs = (0, import_useCopyAs.useCopyAs)(); | ||
const exportAs = (0, import_useExportAs.useExportAs)(); | ||
const trackEvent = (0, import_useEventsProvider.useEvents)(); | ||
const actions = React.useMemo(() => { | ||
const actions2 = makeActions([ | ||
// 'new-project': { | ||
// id: 'file.new', | ||
// label: 'file.new', | ||
// onSelect() { | ||
// newFile() | ||
// }, | ||
// }, | ||
// 'open-project': { | ||
// id: 'file.open', | ||
// label: 'file.open', | ||
// kbd: '$o', | ||
// onSelect() { | ||
// openFile() | ||
// }, | ||
// }, | ||
// 'save-project': { | ||
// id: 'file.save', | ||
// label: 'file.save', | ||
// kbd: '$s', | ||
// onSelect() { | ||
// saveFile() | ||
// }, | ||
// }, | ||
// 'save-project-as': { | ||
// id: 'file.save-as', | ||
// label: 'file.save-as', | ||
// kbd: '$!s', | ||
// onSelect() { | ||
// saveFileAs() | ||
// }, | ||
// }, | ||
{ | ||
@@ -103,4 +75,5 @@ id: "edit-link", | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("edit-link"); | ||
onSelect(source) { | ||
trackEvent("edit-link", { source }); | ||
editor.mark("edit-link"); | ||
addDialog({ component: import_EditLinkDialog.EditLinkDialog }); | ||
@@ -110,2 +83,12 @@ } | ||
{ | ||
id: "insert-embed", | ||
label: "action.insert-embed", | ||
readonlyOk: false, | ||
kbd: "$i", | ||
onSelect(source) { | ||
trackEvent("insert-embed", { source }); | ||
addDialog({ component: import_EmbedDialog.EmbedDialog }); | ||
} | ||
}, | ||
{ | ||
id: "insert-media", | ||
@@ -115,3 +98,4 @@ label: "action.insert-media", | ||
readonlyOk: false, | ||
onSelect() { | ||
onSelect(source) { | ||
trackEvent("insert-media", { source }); | ||
insertMedia(); | ||
@@ -126,4 +110,5 @@ } | ||
readonlyOk: false, | ||
onSelect() { | ||
app.undo(); | ||
onSelect(source) { | ||
trackEvent("undo", { source }); | ||
editor.undo(); | ||
} | ||
@@ -137,4 +122,5 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.redo(); | ||
onSelect(source) { | ||
trackEvent("redo", { source }); | ||
editor.redo(); | ||
} | ||
@@ -148,4 +134,5 @@ }, | ||
readonlyOk: true, | ||
onSelect() { | ||
exportAs(app.selectedIds, "svg"); | ||
onSelect(source) { | ||
trackEvent("export-as", { format: "svg", source }); | ||
exportAs(editor.selectedIds, "svg"); | ||
} | ||
@@ -159,4 +146,5 @@ }, | ||
readonlyOk: true, | ||
onSelect() { | ||
exportAs(app.selectedIds, "png"); | ||
onSelect(source) { | ||
trackEvent("export-as", { format: "png", source }); | ||
exportAs(editor.selectedIds, "png"); | ||
} | ||
@@ -170,4 +158,5 @@ }, | ||
readonlyOk: true, | ||
onSelect() { | ||
exportAs(app.selectedIds, "json"); | ||
onSelect(source) { | ||
trackEvent("export-as", { format: "json", source }); | ||
exportAs(editor.selectedIds, "json"); | ||
} | ||
@@ -182,4 +171,5 @@ }, | ||
readonlyOk: true, | ||
onSelect() { | ||
copyAs(app.selectedIds, "svg"); | ||
onSelect(source) { | ||
trackEvent("copy-as", { format: "svg", source }); | ||
copyAs(editor.selectedIds, "svg"); | ||
} | ||
@@ -193,4 +183,5 @@ }, | ||
readonlyOk: true, | ||
onSelect() { | ||
copyAs(app.selectedIds, "png"); | ||
onSelect(source) { | ||
trackEvent("copy-as", { format: "png", source }); | ||
copyAs(editor.selectedIds, "png"); | ||
} | ||
@@ -204,4 +195,5 @@ }, | ||
readonlyOk: true, | ||
onSelect() { | ||
copyAs(app.selectedIds, "json"); | ||
onSelect(source) { | ||
trackEvent("copy-as", { format: "json", source }); | ||
copyAs(editor.selectedIds, "json"); | ||
} | ||
@@ -213,6 +205,9 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark(); | ||
app.updateShapes( | ||
app.selectedShapes.filter((shape) => shape && shape.type === "text" && shape.props.autoSize === false).map((shape) => { | ||
onSelect(source) { | ||
trackEvent("toggle-auto-size", { source }); | ||
editor.mark(); | ||
editor.updateShapes( | ||
editor.selectedShapes.filter( | ||
(shape) => editor.isShapeOfType(shape, import_editor.TextShapeUtil) && shape.props.autoSize === false | ||
).map((shape) => { | ||
return { | ||
@@ -235,4 +230,5 @@ id: shape.id, | ||
readonlyOk: true, | ||
onSelect() { | ||
const ids = app.selectedIds; | ||
onSelect(source) { | ||
trackEvent("open-embed-link", { source }); | ||
const ids = editor.selectedIds; | ||
const warnMsg = "No embed shapes selected"; | ||
@@ -243,4 +239,4 @@ if (ids.length !== 1) { | ||
} | ||
const shape = app.getShapeById(ids[0]); | ||
if (!shape || !import_editor.TLEmbedShapeDef.is(shape)) { | ||
const shape = editor.getShapeById(ids[0]); | ||
if (!shape || !editor.isShapeOfType(shape, import_editor.EmbedShapeUtil)) { | ||
console.error(warnMsg); | ||
@@ -256,21 +252,16 @@ return; | ||
readonlyOk: false, | ||
onSelect() { | ||
const ids = app.selectedIds; | ||
const shapes = ids.map((id) => app.getShapeById(id)); | ||
onSelect(source) { | ||
trackEvent("convert-to-bookmark", { source }); | ||
const shapes = editor.selectedShapes; | ||
const createList = []; | ||
const deleteList = []; | ||
for (const shape of shapes) { | ||
if (!shape || !import_editor.TLEmbedShapeDef.is(shape) || !shape.props.url) | ||
if (!shape || !editor.isShapeOfType(shape, import_editor.EmbedShapeUtil) || !shape.props.url) | ||
continue; | ||
const newPos = new import_primitives.Vec2d(shape.x, shape.y); | ||
newPos.rot(-shape.rotation); | ||
newPos.add( | ||
new import_primitives.Vec2d( | ||
shape.props.w / 2 - import_editor.DEFAULT_BOOKMARK_WIDTH / 2, | ||
shape.props.h / 2 - import_editor.DEFAULT_BOOKMARK_HEIGHT / 2 | ||
) | ||
); | ||
newPos.add(new import_primitives.Vec2d(shape.props.w / 2 - 300 / 2, shape.props.h / 2 - 320 / 2)); | ||
newPos.rot(shape.rotation); | ||
createList.push({ | ||
id: app.createShapeId(), | ||
id: (0, import_editor.createShapeId)(), | ||
type: "bookmark", | ||
@@ -287,5 +278,5 @@ rotation: shape.rotation, | ||
} | ||
app.mark("convert shapes to bookmark"); | ||
app.deleteShapes(deleteList); | ||
app.createShapes(createList); | ||
editor.mark("convert shapes to bookmark"); | ||
editor.deleteShapes(deleteList); | ||
editor.createShapes(createList); | ||
} | ||
@@ -297,9 +288,10 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
const ids = app.selectedIds; | ||
const shapes = (0, import_utils.compact)(ids.map((id) => app.getShapeById(id))); | ||
onSelect(source) { | ||
trackEvent("convert-to-embed", { source }); | ||
const ids = editor.selectedIds; | ||
const shapes = (0, import_utils.compact)(ids.map((id) => editor.getShapeById(id))); | ||
const createList = []; | ||
const deleteList = []; | ||
for (const shape of shapes) { | ||
if (!import_editor.TLBookmarkShapeDef.is(shape)) | ||
if (!editor.isShapeOfType(shape, import_editor.BookmarkShapeUtil)) | ||
continue; | ||
@@ -312,3 +304,3 @@ const { url } = shape.props; | ||
continue; | ||
const { width, height, doesResize } = embedInfo.definition; | ||
const { width, height } = embedInfo.definition; | ||
const newPos = new import_primitives.Vec2d(shape.x, shape.y); | ||
@@ -318,4 +310,4 @@ newPos.rot(-shape.rotation); | ||
newPos.rot(shape.rotation); | ||
createList.push({ | ||
id: app.createShapeId(), | ||
const shapeToCreate = { | ||
id: (0, import_editor.createShapeId)(), | ||
type: "embed", | ||
@@ -328,11 +320,11 @@ x: newPos.x, | ||
w: width, | ||
h: height, | ||
doesResize | ||
h: height | ||
} | ||
}); | ||
}; | ||
createList.push(shapeToCreate); | ||
deleteList.push(shape.id); | ||
} | ||
app.mark("convert shapes to embed"); | ||
app.deleteShapes(deleteList); | ||
app.createShapes(createList); | ||
editor.mark("convert shapes to embed"); | ||
editor.deleteShapes(deleteList); | ||
editor.createShapes(createList); | ||
} | ||
@@ -346,16 +338,17 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
if (app.currentToolId !== "select") | ||
onSelect(source) { | ||
if (editor.currentToolId !== "select") | ||
return; | ||
const ids = app.selectedIds; | ||
const commonBounds = import_primitives.Box2d.Common((0, import_utils.compact)(ids.map((id) => app.getPageBoundsById(id)))); | ||
const offset = app.canMoveCamera ? { | ||
trackEvent("duplicate-shapes", { source }); | ||
const ids = editor.selectedIds; | ||
const commonBounds = import_primitives.Box2d.Common((0, import_utils.compact)(ids.map((id) => editor.getPageBoundsById(id)))); | ||
const offset = editor.canMoveCamera ? { | ||
x: commonBounds.width + 10, | ||
y: 0 | ||
} : { | ||
x: 16 / app.zoomLevel, | ||
y: 16 / app.zoomLevel | ||
x: 16 / editor.zoomLevel, | ||
y: 16 / editor.zoomLevel | ||
}; | ||
app.mark("duplicate shapes"); | ||
app.duplicateShapes(ids, offset); | ||
editor.mark("duplicate shapes"); | ||
editor.duplicateShapes(ids, offset); | ||
} | ||
@@ -369,5 +362,6 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("ungroup"); | ||
app.ungroupShapes(app.selectedIds); | ||
onSelect(source) { | ||
trackEvent("ungroup-shapes", { source }); | ||
editor.mark("ungroup"); | ||
editor.ungroupShapes(editor.selectedIds); | ||
} | ||
@@ -381,9 +375,11 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
if (app.selectedShapes.length === 1 && app.selectedShapes[0].type === "group") { | ||
app.mark("ungroup"); | ||
app.ungroupShapes(app.selectedIds); | ||
onSelect(source) { | ||
trackEvent("group-shapes", { source }); | ||
const { onlySelectedShape } = editor; | ||
if (onlySelectedShape && editor.isShapeOfType(onlySelectedShape, import_editor.GroupShapeUtil)) { | ||
editor.mark("ungroup"); | ||
editor.ungroupShapes(editor.selectedIds); | ||
} else { | ||
app.mark("group"); | ||
app.groupShapes(app.selectedIds); | ||
editor.mark("group"); | ||
editor.groupShapes(editor.selectedIds); | ||
} | ||
@@ -398,5 +394,6 @@ } | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("align left"); | ||
app.alignShapes("left", app.selectedIds); | ||
onSelect(source) { | ||
trackEvent("align-shapes", { operation: "left", source }); | ||
editor.mark("align left"); | ||
editor.alignShapes("left", editor.selectedIds); | ||
} | ||
@@ -411,5 +408,6 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("align center horizontal"); | ||
app.alignShapes("center-horizontal", app.selectedIds); | ||
onSelect(source) { | ||
trackEvent("align-shapes", { operation: "center-horizontal", source }); | ||
editor.mark("align center horizontal"); | ||
editor.alignShapes("center-horizontal", editor.selectedIds); | ||
} | ||
@@ -423,5 +421,6 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("align right"); | ||
app.alignShapes("right", app.selectedIds); | ||
onSelect(source) { | ||
trackEvent("align-shapes", { operation: "right", source }); | ||
editor.mark("align right"); | ||
editor.alignShapes("right", editor.selectedIds); | ||
} | ||
@@ -436,5 +435,6 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("align center vertical"); | ||
app.alignShapes("center-vertical", app.selectedIds); | ||
onSelect(source) { | ||
trackEvent("align-shapes", { operation: "center-vertical", source }); | ||
editor.mark("align center vertical"); | ||
editor.alignShapes("center-vertical", editor.selectedIds); | ||
} | ||
@@ -448,5 +448,6 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("align top"); | ||
app.alignShapes("top", app.selectedIds); | ||
onSelect(source) { | ||
trackEvent("align-shapes", { operation: "top", source }); | ||
editor.mark("align top"); | ||
editor.alignShapes("top", editor.selectedIds); | ||
} | ||
@@ -460,5 +461,6 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("align bottom"); | ||
app.alignShapes("bottom", app.selectedIds); | ||
onSelect(source) { | ||
trackEvent("align-shapes", { operation: "bottom", source }); | ||
editor.mark("align bottom"); | ||
editor.alignShapes("bottom", editor.selectedIds); | ||
} | ||
@@ -471,6 +473,8 @@ }, | ||
icon: "distribute-horizontal", | ||
kbd: "?!h", | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("distribute horizontal"); | ||
app.distributeShapes("horizontal", app.selectedIds); | ||
onSelect(source) { | ||
trackEvent("distribute-shapes", { operation: "horizontal", source }); | ||
editor.mark("distribute horizontal"); | ||
editor.distributeShapes("horizontal", editor.selectedIds); | ||
} | ||
@@ -483,6 +487,8 @@ }, | ||
icon: "distribute-vertical", | ||
kbd: "?!V", | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("distribute vertical"); | ||
app.distributeShapes("vertical", app.selectedIds); | ||
onSelect(source) { | ||
trackEvent("distribute-shapes", { operation: "vertical", source }); | ||
editor.mark("distribute vertical"); | ||
editor.distributeShapes("vertical", editor.selectedIds); | ||
} | ||
@@ -496,5 +502,6 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("stretch horizontal"); | ||
app.stretchShapes("horizontal", app.selectedIds); | ||
onSelect(source) { | ||
trackEvent("stretch-shapes", { operation: "horizontal", source }); | ||
editor.mark("stretch horizontal"); | ||
editor.stretchShapes("horizontal", editor.selectedIds); | ||
} | ||
@@ -508,5 +515,6 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("stretch vertical"); | ||
app.stretchShapes("vertical", app.selectedIds); | ||
onSelect(source) { | ||
trackEvent("stretch-shapes", { operation: "vertical", source }); | ||
editor.mark("stretch vertical"); | ||
editor.stretchShapes("vertical", editor.selectedIds); | ||
} | ||
@@ -520,5 +528,6 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("flip horizontal"); | ||
app.flipShapes("horizontal", app.selectedIds); | ||
onSelect(source) { | ||
trackEvent("flip-shapes", { operation: "horizontal", source }); | ||
editor.mark("flip horizontal"); | ||
editor.flipShapes("horizontal", editor.selectedIds); | ||
} | ||
@@ -532,5 +541,6 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("flip vertical"); | ||
app.flipShapes("vertical", app.selectedIds); | ||
onSelect(source) { | ||
trackEvent("flip-shapes", { operation: "vertical", source }); | ||
editor.mark("flip vertical"); | ||
editor.flipShapes("vertical", editor.selectedIds); | ||
} | ||
@@ -543,5 +553,6 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("pack"); | ||
app.packShapes(app.selectedIds); | ||
onSelect(source) { | ||
trackEvent("pack-shapes", { source }); | ||
editor.mark("pack"); | ||
editor.packShapes(editor.selectedIds); | ||
} | ||
@@ -555,5 +566,6 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("stack-vertical"); | ||
app.stackShapes("vertical", app.selectedIds); | ||
onSelect(source) { | ||
trackEvent("stack-shapes", { operation: "vertical", source }); | ||
editor.mark("stack-vertical"); | ||
editor.stackShapes("vertical", editor.selectedIds); | ||
} | ||
@@ -567,5 +579,6 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("stack-horizontal"); | ||
app.stackShapes("horizontal", app.selectedIds); | ||
onSelect(source) { | ||
trackEvent("stack-shapes", { operation: "horizontal", source }); | ||
editor.mark("stack-horizontal"); | ||
editor.stackShapes("horizontal", editor.selectedIds); | ||
} | ||
@@ -579,5 +592,6 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("bring to front"); | ||
app.bringToFront(); | ||
onSelect(source) { | ||
trackEvent("reorder-shapes", { operation: "toFront", source }); | ||
editor.mark("bring to front"); | ||
editor.bringToFront(); | ||
} | ||
@@ -591,5 +605,6 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("bring forward"); | ||
app.bringForward(); | ||
onSelect(source) { | ||
trackEvent("reorder-shapes", { operation: "forward", source }); | ||
editor.mark("bring forward"); | ||
editor.bringForward(); | ||
} | ||
@@ -603,5 +618,6 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("send backward"); | ||
app.sendBackward(); | ||
onSelect(source) { | ||
trackEvent("reorder-shapes", { operation: "backward", source }); | ||
editor.mark("send backward"); | ||
editor.sendBackward(); | ||
} | ||
@@ -615,5 +631,6 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("send to back"); | ||
app.sendToBack(); | ||
onSelect(source) { | ||
trackEvent("reorder-shapes", { operation: "toBack", source }); | ||
editor.mark("send to back"); | ||
editor.sendToBack(); | ||
} | ||
@@ -626,5 +643,5 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.mark("cut"); | ||
cut(); | ||
onSelect(source) { | ||
editor.mark("cut"); | ||
cut(source); | ||
} | ||
@@ -637,4 +654,4 @@ }, | ||
readonlyOk: true, | ||
onSelect() { | ||
copy(); | ||
onSelect(source) { | ||
copy(source); | ||
} | ||
@@ -647,3 +664,10 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
onSelect(source) { | ||
navigator.clipboard?.read().then((clipboardItems) => { | ||
paste( | ||
clipboardItems, | ||
source, | ||
source === "context-menu" ? editor.inputs.currentPagePoint : void 0 | ||
); | ||
}); | ||
} | ||
@@ -656,9 +680,10 @@ }, | ||
readonlyOk: true, | ||
onSelect() { | ||
if (app.currentToolId !== "select") { | ||
app.cancel(); | ||
app.setSelectedTool("select"); | ||
onSelect(source) { | ||
trackEvent("select-all-shapes", { source }); | ||
if (editor.currentToolId !== "select") { | ||
editor.cancel(); | ||
editor.setSelectedTool("select"); | ||
} | ||
app.mark("select all kbd"); | ||
app.selectAll(); | ||
editor.mark("select all kbd"); | ||
editor.selectAll(); | ||
} | ||
@@ -670,5 +695,6 @@ }, | ||
readonlyOk: true, | ||
onSelect() { | ||
app.mark("select none"); | ||
app.selectNone(); | ||
onSelect(source) { | ||
trackEvent("select-none-shapes", { source }); | ||
editor.mark("select none"); | ||
editor.selectNone(); | ||
} | ||
@@ -679,10 +705,11 @@ }, | ||
label: "action.delete", | ||
kbd: "\u232B", | ||
kbd: "\u232B,del,backspace", | ||
icon: "trash", | ||
readonlyOk: false, | ||
onSelect() { | ||
if (app.currentToolId !== "select") | ||
onSelect(source) { | ||
if (editor.currentToolId !== "select") | ||
return; | ||
app.mark("delete"); | ||
app.deleteShapes(); | ||
trackEvent("delete-shapes", { source }); | ||
editor.mark("delete"); | ||
editor.deleteShapes(); | ||
} | ||
@@ -695,9 +722,10 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
if (app.selectedIds.length === 0) | ||
onSelect(source) { | ||
if (editor.selectedIds.length === 0) | ||
return; | ||
app.mark("rotate-cw"); | ||
const offset = app.selectionRotation % (import_primitives.TAU / 2); | ||
trackEvent("rotate-cw", { source }); | ||
editor.mark("rotate-cw"); | ||
const offset = editor.selectionRotation % (import_primitives.TAU / 2); | ||
const dontUseOffset = (0, import_primitives.approximately)(offset, 0) || (0, import_primitives.approximately)(offset, import_primitives.TAU / 2); | ||
app.rotateShapesBy(app.selectedIds, import_primitives.TAU / 2 - (dontUseOffset ? 0 : offset)); | ||
editor.rotateShapesBy(editor.selectedIds, import_primitives.TAU / 2 - (dontUseOffset ? 0 : offset)); | ||
} | ||
@@ -710,9 +738,10 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
if (app.selectedIds.length === 0) | ||
onSelect(source) { | ||
if (editor.selectedIds.length === 0) | ||
return; | ||
app.mark("rotate-ccw"); | ||
const offset = app.selectionRotation % (import_primitives.TAU / 2); | ||
trackEvent("rotate-ccw", { source }); | ||
editor.mark("rotate-ccw"); | ||
const offset = editor.selectionRotation % (import_primitives.TAU / 2); | ||
const offsetCloseToZero = (0, import_primitives.approximately)(offset, 0); | ||
app.rotateShapesBy(app.selectedIds, offsetCloseToZero ? -(import_primitives.TAU / 2) : -offset); | ||
editor.rotateShapesBy(editor.selectedIds, offsetCloseToZero ? -(import_primitives.TAU / 2) : -offset); | ||
} | ||
@@ -723,6 +752,7 @@ }, | ||
label: "action.zoom-in", | ||
kbd: "$=", | ||
kbd: "$=,=", | ||
readonlyOk: true, | ||
onSelect() { | ||
app.zoomIn(app.viewportScreenCenter, { duration: import_editor.ANIMATION_MEDIUM_MS }); | ||
onSelect(source) { | ||
trackEvent("zoom-in", { source }); | ||
editor.zoomIn(editor.viewportScreenCenter, { duration: import_editor.ANIMATION_MEDIUM_MS }); | ||
} | ||
@@ -733,6 +763,7 @@ }, | ||
label: "action.zoom-out", | ||
kbd: "$-", | ||
kbd: "$-,-", | ||
readonlyOk: true, | ||
onSelect() { | ||
app.zoomOut(app.viewportScreenCenter, { duration: import_editor.ANIMATION_MEDIUM_MS }); | ||
onSelect(source) { | ||
trackEvent("zoom-out", { source }); | ||
editor.zoomOut(editor.viewportScreenCenter, { duration: import_editor.ANIMATION_MEDIUM_MS }); | ||
} | ||
@@ -746,4 +777,5 @@ }, | ||
readonlyOk: true, | ||
onSelect() { | ||
app.resetZoom(app.viewportScreenCenter, { duration: import_editor.ANIMATION_MEDIUM_MS }); | ||
onSelect(source) { | ||
trackEvent("reset-zoom", { source }); | ||
editor.resetZoom(editor.viewportScreenCenter, { duration: import_editor.ANIMATION_MEDIUM_MS }); | ||
} | ||
@@ -756,4 +788,5 @@ }, | ||
readonlyOk: true, | ||
onSelect() { | ||
app.zoomToFit({ duration: import_editor.ANIMATION_MEDIUM_MS }); | ||
onSelect(source) { | ||
trackEvent("zoom-to-fit", { source }); | ||
editor.zoomToFit({ duration: import_editor.ANIMATION_MEDIUM_MS }); | ||
} | ||
@@ -766,4 +799,5 @@ }, | ||
readonlyOk: true, | ||
onSelect() { | ||
app.zoomToSelection({ duration: import_editor.ANIMATION_MEDIUM_MS }); | ||
onSelect(source) { | ||
trackEvent("zoom-to-selection", { source }); | ||
editor.zoomToSelection({ duration: import_editor.ANIMATION_MEDIUM_MS }); | ||
} | ||
@@ -776,9 +810,5 @@ }, | ||
readonlyOk: false, | ||
onSelect() { | ||
app.updateUserDocumentSettings( | ||
{ | ||
isSnapMode: !app.userDocumentSettings.isSnapMode | ||
}, | ||
true | ||
); | ||
onSelect(source) { | ||
trackEvent("toggle-snap-mode", { source }); | ||
editor.setSnapMode(!editor.isSnapMode); | ||
}, | ||
@@ -793,9 +823,5 @@ checkbox: true | ||
readonlyOk: true, | ||
onSelect() { | ||
app.updateUserDocumentSettings( | ||
{ | ||
isDarkMode: !app.userDocumentSettings.isDarkMode | ||
}, | ||
true | ||
); | ||
onSelect(source) { | ||
trackEvent("toggle-dark-mode", { source }); | ||
editor.setDarkMode(!editor.isDarkMode); | ||
}, | ||
@@ -805,2 +831,13 @@ checkbox: true | ||
{ | ||
id: "toggle-reduce-motion", | ||
label: "action.toggle-reduce-motion", | ||
menuLabel: "action.toggle-reduce-motion.menu", | ||
readonlyOk: true, | ||
onSelect(source) { | ||
trackEvent("toggle-reduce-motion", { source }); | ||
editor.setAnimationSpeed(editor.animationSpeed === 0 ? 1 : 0); | ||
}, | ||
checkbox: true | ||
}, | ||
{ | ||
id: "toggle-transparent", | ||
@@ -811,6 +848,7 @@ label: "action.toggle-transparent", | ||
readonlyOk: true, | ||
onSelect() { | ||
app.updateInstanceState( | ||
onSelect(source) { | ||
trackEvent("toggle-transparent", { source }); | ||
editor.updateInstanceState( | ||
{ | ||
exportBackground: !app.instanceState.exportBackground | ||
exportBackground: !editor.instanceState.exportBackground | ||
}, | ||
@@ -828,9 +866,5 @@ true | ||
kbd: "q", | ||
onSelect() { | ||
app.updateInstanceState( | ||
{ | ||
isToolLocked: !app.instanceState.isToolLocked | ||
}, | ||
true | ||
); | ||
onSelect(source) { | ||
trackEvent("toggle-tool-lock", { source }); | ||
editor.setToolLocked(!editor.isToolLocked); | ||
}, | ||
@@ -846,13 +880,9 @@ checkbox: true | ||
checkbox: true, | ||
onSelect() { | ||
onSelect(source) { | ||
requestAnimationFrame(() => { | ||
app.batch(() => { | ||
editor.batch(() => { | ||
trackEvent("toggle-focus-mode", { source }); | ||
clearDialogs(); | ||
clearToasts(); | ||
app.updateInstanceState( | ||
{ | ||
isFocusMode: !app.instanceState.isFocusMode | ||
}, | ||
true | ||
); | ||
editor.setFocusMode(!editor.isFocusMode); | ||
}); | ||
@@ -868,9 +898,5 @@ }); | ||
kbd: "$'", | ||
onSelect() { | ||
app.updateUserDocumentSettings( | ||
{ | ||
isGridMode: !app.userDocumentSettings.isGridMode | ||
}, | ||
true | ||
); | ||
onSelect(source) { | ||
trackEvent("toggle-grid-mode", { source }); | ||
editor.setGridMode(!editor.isGridMode); | ||
}, | ||
@@ -884,6 +910,7 @@ checkbox: true | ||
readonlyOk: true, | ||
onSelect() { | ||
app.updateInstanceState( | ||
onSelect(source) { | ||
trackEvent("toggle-debug-mode", { source }); | ||
editor.updateInstanceState( | ||
{ | ||
isDebugMode: !app.instanceState.isDebugMode | ||
isDebugMode: !editor.instanceState.isDebugMode | ||
}, | ||
@@ -900,3 +927,4 @@ true | ||
readonlyOk: true, | ||
onSelect() { | ||
onSelect(source) { | ||
trackEvent("print", { source }); | ||
printSelectionOrPages(); | ||
@@ -910,4 +938,5 @@ } | ||
readonlyOk: true, | ||
onSelect() { | ||
app.setPenMode(false); | ||
onSelect(source) { | ||
trackEvent("exit-pen-mode", { source }); | ||
editor.setPenMode(false); | ||
} | ||
@@ -920,4 +949,5 @@ }, | ||
readonlyOk: true, | ||
onSelect() { | ||
app.stopFollowingUser(); | ||
onSelect(source) { | ||
trackEvent("stop-following", { source }); | ||
editor.stopFollowingUser(); | ||
} | ||
@@ -930,24 +960,26 @@ }, | ||
readonlyOk: true, | ||
onSelect() { | ||
const bounds = app.selectedPageBounds ?? app.allShapesCommonBounds; | ||
if (bounds) { | ||
app.zoomToBounds( | ||
bounds.minX, | ||
bounds.minY, | ||
bounds.width, | ||
bounds.height, | ||
Math.min(1, app.zoomLevel), | ||
{ duration: 220 } | ||
); | ||
} | ||
onSelect(source) { | ||
trackEvent("zoom-to-content", { source }); | ||
editor.zoomToContent(); | ||
} | ||
}, | ||
{ | ||
id: "toggle-lock", | ||
label: "action.toggle-lock", | ||
readonlyOk: false, | ||
kbd: "!l", | ||
onSelect(source) { | ||
trackEvent("toggle-lock", { source }); | ||
editor.toggleLock(); | ||
} | ||
} | ||
]); | ||
if (overrides) { | ||
return overrides(app, actions2, void 0); | ||
return overrides(editor, actions2, void 0); | ||
} | ||
return actions2; | ||
}, [ | ||
trackEvent, | ||
overrides, | ||
app, | ||
editor, | ||
addDialog, | ||
@@ -959,2 +991,3 @@ insertMedia, | ||
copy, | ||
paste, | ||
clearDialogs, | ||
@@ -961,0 +994,0 @@ clearToasts, |
@@ -38,4 +38,4 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_react = __toESM(require("react")); | ||
var import_signia_react = require("signia-react"); | ||
var import_menuHelpers = require("./menuHelpers"); | ||
@@ -46,9 +46,9 @@ var import_useActions = require("./useActions"); | ||
const ActionsMenuSchemaContext = import_react.default.createContext({}); | ||
const ActionsMenuSchemaProvider = (0, import_signia_react.track)(function ActionsMenuSchemaProvider2({ | ||
const ActionsMenuSchemaProvider = (0, import_state.track)(function ActionsMenuSchemaProvider2({ | ||
overrides, | ||
children | ||
}) { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const actions = (0, import_useActions.useActions)(); | ||
const selectedCount = app.selectedIds.length; | ||
const selectedCount = editor.selectedIds.length; | ||
const oneSelected = selectedCount > 0; | ||
@@ -62,4 +62,4 @@ const twoSelected = selectedCount > 1; | ||
const breakpoint = (0, import_useBreakpoint.useBreakpoint)(); | ||
const isZoomedTo100 = app.zoomLevel === 1; | ||
const actionMenuSchema = (0, import_react.useMemo)(() => { | ||
const isZoomedTo100 = editor.zoomLevel === 1; | ||
const actionTLUiMenuSchema = (0, import_react.useMemo)(() => { | ||
const results = [ | ||
@@ -88,7 +88,7 @@ (0, import_menuHelpers.menuItem)(actions["align-left"], { disabled: !twoSelected }), | ||
if (overrides) { | ||
return overrides(app, results, { actions, oneSelected, twoSelected, threeSelected }); | ||
return overrides(editor, results, { actions, oneSelected, twoSelected, threeSelected }); | ||
} | ||
return results; | ||
}, [ | ||
app, | ||
editor, | ||
isZoomedTo100, | ||
@@ -106,3 +106,3 @@ allowGroup, | ||
]); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ActionsMenuSchemaContext.Provider, { value: actionMenuSchema, children }); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ActionsMenuSchemaContext.Provider, { value: actionTLUiMenuSchema, children }); | ||
}); | ||
@@ -109,0 +109,0 @@ function useActionsMenuSchema() { |
@@ -37,12 +37,12 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_react = __toESM(require("react")); | ||
var import_signia_react = require("signia-react"); | ||
var import_constants = require("../constants"); | ||
const BreakpointContext = import_react.default.createContext(0); | ||
function BreakPointProvider({ children }) { | ||
const app = (0, import_editor.useApp)(); | ||
const breakpoint = (0, import_signia_react.useValue)( | ||
const editor = (0, import_editor.useEditor)(); | ||
const breakpoint = (0, import_state.useValue)( | ||
"breakpoint", | ||
() => { | ||
const { width } = app.viewportScreenBounds; | ||
const { width } = editor.viewportScreenBounds; | ||
const breakpoints = import_constants.PORTRAIT_BREAKPOINTS; | ||
@@ -56,3 +56,3 @@ for (let i = 0; i < breakpoints.length - 1; i++) { | ||
}, | ||
[app] | ||
[editor] | ||
); | ||
@@ -59,0 +59,0 @@ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BreakpointContext.Provider, { value: breakpoint, children }); |
@@ -25,7 +25,7 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_signia_react = require("signia-react"); | ||
var import_state = require("@tldraw/state"); | ||
function useCanRedo() { | ||
const app = (0, import_editor.useApp)(); | ||
return (0, import_signia_react.useValue)("useCanRedo", () => app.canRedo, [app]); | ||
const editor = (0, import_editor.useEditor)(); | ||
return (0, import_state.useValue)("useCanRedo", () => editor.canRedo, [editor]); | ||
} | ||
//# sourceMappingURL=useCanRedo.js.map |
@@ -25,7 +25,7 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_signia_react = require("signia-react"); | ||
var import_state = require("@tldraw/state"); | ||
function useCanUndo() { | ||
const app = (0, import_editor.useApp)(); | ||
return (0, import_signia_react.useValue)("useCanUndo", () => app.canUndo, [app]); | ||
const editor = (0, import_editor.useEditor)(); | ||
return (0, import_state.useValue)("useCanUndo", () => editor.canUndo, [editor]); | ||
} | ||
//# sourceMappingURL=useCanUndo.js.map |
@@ -26,7 +26,16 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_primitives = require("@tldraw/primitives"); | ||
var import_utils = require("@tldraw/utils"); | ||
var import_lz_string = require("lz-string"); | ||
var import_react = require("react"); | ||
var import_useAppIsFocused = require("./useAppIsFocused"); | ||
var import_pasteExcalidrawContent = require("./clipboard/pasteExcalidrawContent"); | ||
var import_pasteFiles = require("./clipboard/pasteFiles"); | ||
var import_pasteTldrawContent = require("./clipboard/pasteTldrawContent"); | ||
var import_pasteUrl = require("./clipboard/pasteUrl"); | ||
var import_useEditorIsFocused = require("./useEditorIsFocused"); | ||
var import_useEventsProvider = require("./useEventsProvider"); | ||
const INPUTS = ["input", "select", "textarea"]; | ||
function disallowClipboardEvents(editor) { | ||
const { activeElement } = document; | ||
return editor.isMenuOpen || activeElement && (activeElement.getAttribute("contenteditable") || INPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1); | ||
} | ||
async function blobAsString(blob) { | ||
@@ -45,707 +54,279 @@ return new Promise((resolve, reject) => { | ||
} | ||
async function dataTransferItemAsString(item) { | ||
return new Promise((resolve) => { | ||
item.getAsString((text) => { | ||
resolve(text); | ||
}); | ||
}); | ||
} | ||
const INPUTS = ["input", "select", "textarea"]; | ||
function disallowClipboardEvents(app) { | ||
const { activeElement } = document; | ||
return app.isMenuOpen || activeElement && (activeElement.getAttribute("contenteditable") || INPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1); | ||
} | ||
function stripHtml(html) { | ||
const doc = document.implementation.createHTMLDocument(""); | ||
doc.documentElement.innerHTML = html; | ||
doc.documentElement.innerHTML = html.trim(); | ||
return doc.body.textContent || doc.body.innerText || ""; | ||
} | ||
const clearPersistedClipboard = () => { | ||
window.navigator.clipboard.writeText(""); | ||
const isFile = (item) => { | ||
return item.types.find((i) => i.match(/^image\//)); | ||
}; | ||
const getStringifiedClipboard = (data, kind) => { | ||
const s = (0, import_lz_string.compressToBase64)( | ||
JSON.stringify({ | ||
type: "application/tldraw", | ||
kind, | ||
data | ||
}) | ||
); | ||
return s; | ||
}; | ||
const pasteTldrawContent = async (app, clipboard, point) => { | ||
const p = point ?? (app.inputs.shiftKey ? app.inputs.currentPagePoint : void 0); | ||
app.mark("paste"); | ||
app.putContent(clipboard, { | ||
point: p, | ||
select: true | ||
}); | ||
}; | ||
const pastePlainText = async (app, text, point) => { | ||
const p = point ?? (app.inputs.shiftKey ? app.inputs.currentPagePoint : app.viewportPageCenter); | ||
const defaultProps = app.getShapeUtilByDef(import_editor.TLTextShapeDef).defaultProps(); | ||
const { w, h } = app.textMeasure.measureText({ | ||
...import_editor.TEXT_PROPS, | ||
text: stripHtml(text), | ||
fontFamily: import_editor.FONT_FAMILIES[defaultProps.font], | ||
fontSize: import_editor.FONT_SIZES[defaultProps.size], | ||
width: "fit-content" | ||
}); | ||
app.mark("paste"); | ||
app.createShapes([ | ||
{ | ||
id: (0, import_editor.createShapeId)(), | ||
type: "text", | ||
x: p.x - w / 2, | ||
y: p.y - h / 2, | ||
props: { | ||
text: stripHtml(text), | ||
autoSize: true | ||
} | ||
} | ||
]); | ||
}; | ||
const pasteUrl = async (app, url, point) => { | ||
const p = point ?? (app.inputs.shiftKey ? app.inputs.currentPagePoint : app.viewportPageCenter); | ||
try { | ||
const resp = await fetch(url); | ||
if (resp.headers.get("content-type")?.match(/^image\//)) { | ||
app.mark("paste"); | ||
pasteFiles(app, [url]); | ||
return; | ||
} | ||
} catch (err) { | ||
if (err.message !== "Failed to fetch") { | ||
console.error(err); | ||
} | ||
} | ||
const embedInfo = (0, import_editor.getEmbedInfo)(url); | ||
if (embedInfo) { | ||
app.mark("paste"); | ||
(0, import_editor.createEmbedShapeAtPoint)(app, embedInfo.url, p, embedInfo.definition); | ||
} else { | ||
app.mark("paste"); | ||
await (0, import_editor.createBookmarkShapeAtPoint)(app, url, p); | ||
} | ||
}; | ||
const pasteSvgText = async (app, text, point) => { | ||
const p = point ?? (app.inputs.shiftKey ? app.inputs.currentPagePoint : app.viewportPageCenter); | ||
app.mark("paste"); | ||
await (0, import_editor.createAssetShapeAtPoint)(app, text, p); | ||
}; | ||
const pasteFiles = async (app, urls, point) => { | ||
const p = point ?? (app.inputs.shiftKey ? app.inputs.currentPagePoint : app.viewportPageCenter); | ||
const blobs = await Promise.all(urls.map(async (url) => await (await fetch(url)).blob())); | ||
const files = blobs.map( | ||
(blob) => new File([blob], "tldrawFile", { | ||
type: blob.type | ||
}) | ||
); | ||
app.mark("paste"); | ||
await (0, import_editor.createShapesFromFiles)(app, files, p, false); | ||
urls.forEach((url) => URL.revokeObjectURL(url)); | ||
}; | ||
const handleMenuCopy = (app) => { | ||
const content = app.getContent(); | ||
if (!content) { | ||
clearPersistedClipboard(); | ||
return; | ||
} | ||
const stringifiedClipboard = getStringifiedClipboard(content, "content"); | ||
if (typeof window?.navigator !== "undefined") { | ||
const textItems = content.shapes.map((shape) => { | ||
if (import_editor.TLTextShapeDef.is(shape) || import_editor.TLGeoShapeDef.is(shape) || import_editor.TLArrowShapeDef.is(shape)) { | ||
return shape.props.text; | ||
} | ||
if (import_editor.TLBookmarkShapeDef.is(shape) || import_editor.TLEmbedShapeDef.is(shape)) { | ||
return shape.props.url; | ||
} | ||
return null; | ||
}).filter(import_utils.isNonNull); | ||
if (navigator.clipboard?.write) { | ||
const htmlBlob = new Blob([`<tldraw>${stringifiedClipboard}</tldraw>`], { | ||
type: "text/html" | ||
}); | ||
let textContent = textItems.join(" "); | ||
if (textContent === "") { | ||
textContent = " "; | ||
} | ||
navigator.clipboard.write([ | ||
new ClipboardItem({ | ||
"text/html": htmlBlob, | ||
"text/plain": new Blob([textContent], { type: "text/plain" }) | ||
}) | ||
]); | ||
} else if (navigator.clipboard.writeText) { | ||
navigator.clipboard.writeText(`<tldraw>${stringifiedClipboard}</tldraw>`); | ||
} | ||
} | ||
}; | ||
const pasteText = (app, data, point) => { | ||
const handleText = (editor, data, point) => { | ||
const validUrlList = (0, import_editor.getValidHttpURLList)(data); | ||
if (validUrlList) { | ||
for (const url of validUrlList) { | ||
pasteUrl(app, url, point); | ||
(0, import_pasteUrl.pasteUrl)(editor, url, point); | ||
} | ||
} else if ((0, import_editor.isValidHttpURL)(data)) { | ||
pasteUrl(app, data, point); | ||
(0, import_pasteUrl.pasteUrl)(editor, data, point); | ||
} else if ((0, import_editor.isSvgText)(data)) { | ||
pasteSvgText(app, data, point); | ||
editor.mark("paste"); | ||
editor.putExternalContent({ | ||
type: "svg-text", | ||
text: data, | ||
point | ||
}); | ||
} else { | ||
pastePlainText(app, data, point); | ||
editor.mark("paste"); | ||
editor.putExternalContent({ | ||
type: "text", | ||
text: data, | ||
point | ||
}); | ||
} | ||
}; | ||
async function pasteExcalidrawContent(app, clipboard, point) { | ||
const { elements, files } = clipboard; | ||
const tldrawContent = { | ||
shapes: [], | ||
rootShapeIds: [], | ||
assets: [], | ||
schema: app.store.schema.serialize() | ||
}; | ||
const groupShapeIdToChildren = /* @__PURE__ */ new Map(); | ||
const rotatedElements = /* @__PURE__ */ new Map(); | ||
const getOpacity = (opacity) => { | ||
const t = opacity / 100; | ||
if (t < 0.2) { | ||
return "0.1"; | ||
} else if (t < 0.4) { | ||
return "0.25"; | ||
} else if (t < 0.6) { | ||
return "0.5"; | ||
} else if (t < 0.8) { | ||
return "0.75"; | ||
} | ||
return "1"; | ||
}; | ||
const strokeWidthsToSizes = { | ||
1: "s", | ||
2: "m", | ||
3: "l", | ||
4: "xl" | ||
}; | ||
const fontSizesToSizes = { | ||
16: "s", | ||
20: "m", | ||
28: "l", | ||
36: "xl" | ||
}; | ||
function getFontSizeAndScale(fontSize) { | ||
const size = fontSizesToSizes[fontSize]; | ||
if (size) { | ||
return { size, scale: 1 }; | ||
} | ||
if (fontSize < 16) { | ||
return { size: "s", scale: fontSize / 16 }; | ||
} | ||
if (fontSize > 36) { | ||
return { size: "xl", scale: fontSize / 36 }; | ||
} | ||
return { size: "m", scale: 1 }; | ||
const handlePasteFromEventClipboardData = async (editor, clipboardData, point) => { | ||
if (editor.editingId !== null) | ||
return; | ||
if (!clipboardData) { | ||
throw Error("No clipboard data"); | ||
} | ||
const fontFamilyToFontType = { | ||
1: "draw", | ||
2: "sans", | ||
3: "mono" | ||
}; | ||
const colorsToColors = { | ||
"#ffffff": "grey", | ||
// Strokes | ||
"#000000": "black", | ||
"#343a40": "grey", | ||
"#495057": "grey", | ||
"#c92a2a": "red", | ||
"#a61e4d": "light-red", | ||
"#862e9c": "violet", | ||
"#5f3dc4": "light-violet", | ||
"#364fc7": "blue", | ||
"#1864ab": "light-blue", | ||
"#0b7285": "light-green", | ||
"#087f5b": "light-green", | ||
"#2b8a3e": "green", | ||
"#5c940d": "light-green", | ||
"#e67700": "yellow", | ||
"#d9480f": "orange", | ||
// Backgrounds | ||
"#ced4da": "grey", | ||
"#868e96": "grey", | ||
"#fa5252": "light-red", | ||
"#e64980": "red", | ||
"#be4bdb": "light-violet", | ||
"#7950f2": "violet", | ||
"#4c6ef5": "blue", | ||
"#228be6": "light-blue", | ||
"#15aabf": "light-green", | ||
"#12b886": "green", | ||
"#40c057": "green", | ||
"#82c91e": "light-green", | ||
"#fab005": "yellow", | ||
"#fd7e14": "orange", | ||
"#212529": "grey" | ||
}; | ||
const strokeStylesToStrokeTypes = { | ||
solid: "draw", | ||
dashed: "dashed", | ||
dotted: "dotted" | ||
}; | ||
const fillStylesToFillType = { | ||
"cross-hatch": "pattern", | ||
hachure: "pattern", | ||
solid: "solid" | ||
}; | ||
const textAlignToAlignTypes = { | ||
left: "start", | ||
center: "middle", | ||
right: "end" | ||
}; | ||
const arrowheadsToArrowheadTypes = { | ||
arrow: "arrow", | ||
dot: "dot", | ||
triangle: "triangle", | ||
bar: "pipe" | ||
}; | ||
function getBend(element, startPoint, endPoint) { | ||
let bend = 0; | ||
if (element.points.length > 2) { | ||
const start = new import_primitives.Vec2d(startPoint[0], startPoint[1]); | ||
const end = new import_primitives.Vec2d(endPoint[0], endPoint[1]); | ||
const handle = new import_primitives.Vec2d(element.points[1][0], element.points[1][1]); | ||
const delta = import_primitives.Vec2d.Sub(end, start); | ||
const v = import_primitives.Vec2d.Per(delta); | ||
const med = import_primitives.Vec2d.Med(end, start); | ||
const A = import_primitives.Vec2d.Sub(med, v); | ||
const B = import_primitives.Vec2d.Add(med, v); | ||
const point2 = import_primitives.Vec2d.NearestPointOnLineSegment(A, B, handle, false); | ||
bend = import_primitives.Vec2d.Dist(point2, med); | ||
if (import_primitives.Vec2d.Clockwise(point2, end, med)) | ||
bend *= -1; | ||
const things = []; | ||
for (const item of Object.values(clipboardData.items)) { | ||
switch (item.kind) { | ||
case "file": { | ||
things.push({ | ||
type: "file", | ||
source: new Promise((r) => r(item.getAsFile())) | ||
}); | ||
break; | ||
} | ||
case "string": { | ||
if (item.type === "text/html") { | ||
things.push({ | ||
type: "html", | ||
source: new Promise((r) => item.getAsString(r)) | ||
}); | ||
} else if (item.type === "text/plain") { | ||
things.push({ | ||
type: "text", | ||
source: new Promise((r) => item.getAsString(r)) | ||
}); | ||
} else { | ||
things.push({ type: item.type, source: new Promise((r) => item.getAsString(r)) }); | ||
} | ||
break; | ||
} | ||
} | ||
return bend; | ||
} | ||
const getDash = (element) => { | ||
let dash = strokeStylesToStrokeTypes[element.strokeStyle] ?? "draw"; | ||
if (dash === "draw" && element.roughness === 0) { | ||
dash = "solid"; | ||
} | ||
return dash; | ||
}; | ||
const getFill = (element) => { | ||
if (element.backgroundColor === "transparent") { | ||
return "none"; | ||
} | ||
return fillStylesToFillType[element.fillStyle] ?? "solid"; | ||
}; | ||
const { currentPageId } = app; | ||
let index = "a1"; | ||
const excElementIdsToTldrawShapeIds = /* @__PURE__ */ new Map(); | ||
const rootShapeIds = []; | ||
const skipIds = /* @__PURE__ */ new Set(); | ||
elements.forEach((element) => { | ||
excElementIdsToTldrawShapeIds.set(element.id, app.createShapeId()); | ||
if (element.boundElements !== null) { | ||
for (const boundElement of element.boundElements) { | ||
if (boundElement.type === "text") { | ||
skipIds.add(boundElement.id); | ||
handleClipboardThings(editor, things, point); | ||
}; | ||
const handlePasteFromClipboardApi = async (editor, clipboardItems, point) => { | ||
const things = []; | ||
for (const item of clipboardItems) { | ||
if (isFile(item)) { | ||
for (const type of item.types) { | ||
if (type.match(/^image\//)) { | ||
things.push({ type: "blob", source: item.getType(type) }); | ||
} | ||
} | ||
} | ||
}); | ||
for (const element of elements) { | ||
if (skipIds.has(element.id)) { | ||
continue; | ||
if (item.types.includes("text/html")) { | ||
things.push({ | ||
type: "html", | ||
source: new Promise( | ||
(r) => item.getType("text/html").then((blob) => blobAsString(blob).then(r)) | ||
) | ||
}); | ||
} | ||
const id = excElementIdsToTldrawShapeIds.get(element.id); | ||
const base = { | ||
id, | ||
typeName: "shape", | ||
parentId: currentPageId, | ||
index, | ||
x: element.x, | ||
y: element.y, | ||
rotation: 0, | ||
isLocked: element.locked | ||
}; | ||
if (element.angle !== 0) { | ||
rotatedElements.set(id, element.angle); | ||
if (item.types.includes("text/uri-list")) { | ||
things.push({ | ||
type: "url", | ||
source: new Promise( | ||
(r) => item.getType("text/uri-list").then((blob) => blobAsString(blob).then(r)) | ||
) | ||
}); | ||
} | ||
if (element.groupIds && element.groupIds.length > 0) { | ||
if (groupShapeIdToChildren.has(element.groupIds[0])) { | ||
groupShapeIdToChildren.get(element.groupIds[0])?.push(id); | ||
} else { | ||
groupShapeIdToChildren.set(element.groupIds[0], [id]); | ||
} | ||
} else { | ||
rootShapeIds.push(id); | ||
if (item.types.includes("text/plain")) { | ||
things.push({ | ||
type: "text", | ||
source: new Promise( | ||
(r) => item.getType("text/plain").then((blob) => blobAsString(blob).then(r)) | ||
) | ||
}); | ||
} | ||
switch (element.type) { | ||
case "rectangle": | ||
case "ellipse": | ||
case "diamond": { | ||
let text = ""; | ||
let align = "middle"; | ||
if (element.boundElements !== null) { | ||
for (const boundElement of element.boundElements) { | ||
if (boundElement.type === "text") { | ||
const labelElement = elements.find((elm) => elm.id === boundElement.id); | ||
if (labelElement) { | ||
text = labelElement.text; | ||
align = textAlignToAlignTypes[labelElement.textAlign]; | ||
} | ||
} | ||
} | ||
} | ||
return await handleClipboardThings(editor, things, point); | ||
}; | ||
async function handleClipboardThings(editor, things, point) { | ||
const files = things.filter( | ||
(t) => (t.type === "file" || t.type === "blob") && t.source !== null | ||
); | ||
if (files.length) { | ||
const fileBlobs = await Promise.all(files.map((t) => t.source)); | ||
const urls = fileBlobs.filter(Boolean).map( | ||
(blob) => URL.createObjectURL(blob) | ||
); | ||
return await (0, import_pasteFiles.pasteFiles)(editor, urls, point); | ||
} | ||
const results = await Promise.all( | ||
things.filter((t) => t.type !== "file").map( | ||
(t) => new Promise((r) => { | ||
const thing = t; | ||
if (thing.type === "file") { | ||
r({ type: "error", data: null, reason: "unexpected file" }); | ||
return; | ||
} | ||
const colorToUse = element.backgroundColor === "transparent" ? element.strokeColor : element.backgroundColor; | ||
tldrawContent.shapes.push({ | ||
...base, | ||
type: "geo", | ||
props: { | ||
geo: element.type, | ||
opacity: getOpacity(element.opacity), | ||
url: element.link ?? "", | ||
w: element.width, | ||
h: element.height, | ||
size: strokeWidthsToSizes[element.strokeWidth] ?? "draw", | ||
color: colorsToColors[colorToUse] ?? "black", | ||
text, | ||
align, | ||
dash: getDash(element), | ||
fill: getFill(element) | ||
} | ||
}); | ||
break; | ||
} | ||
case "freedraw": { | ||
tldrawContent.shapes.push({ | ||
...base, | ||
type: "draw", | ||
props: { | ||
dash: getDash(element), | ||
size: strokeWidthsToSizes[element.strokeWidth], | ||
opacity: getOpacity(element.opacity), | ||
color: colorsToColors[element.strokeColor] ?? "black", | ||
segments: [ | ||
{ | ||
type: "free", | ||
points: element.points.map(([x, y, z = 0.5]) => ({ | ||
x, | ||
y, | ||
z | ||
})) | ||
thing.source.then((text) => { | ||
const tldrawHtmlComment = text.match(/<tldraw[^>]*>(.*)<\/tldraw>/)?.[1]; | ||
if (tldrawHtmlComment) { | ||
try { | ||
const jsonComment = (0, import_lz_string.decompressFromBase64)(tldrawHtmlComment); | ||
if (jsonComment === null) { | ||
r({ | ||
type: "error", | ||
data: jsonComment, | ||
reason: `found tldraw data comment but could not parse base64` | ||
}); | ||
return; | ||
} else { | ||
const json = JSON.parse(jsonComment); | ||
if (json.type !== "application/tldraw") { | ||
r({ | ||
type: "error", | ||
data: json, | ||
reason: `found tldraw data comment but JSON was of a different type: ${json.type}` | ||
}); | ||
} | ||
if (typeof json.data === "string") { | ||
r({ | ||
type: "error", | ||
data: json, | ||
reason: "found tldraw json but data was a string instead of a TLClipboardModel object" | ||
}); | ||
return; | ||
} | ||
r({ type: "tldraw", data: json.data }); | ||
return; | ||
} | ||
] | ||
} | ||
}); | ||
break; | ||
} | ||
case "line": { | ||
const start = element.points[0]; | ||
const end = element.points[element.points.length - 1]; | ||
const indices = (0, import_editor.getIndices)(element.points.length); | ||
tldrawContent.shapes.push({ | ||
...base, | ||
type: "line", | ||
props: { | ||
dash: getDash(element), | ||
size: strokeWidthsToSizes[element.strokeWidth], | ||
opacity: getOpacity(element.opacity), | ||
color: colorsToColors[element.strokeColor] ?? "black", | ||
spline: element.roundness ? "cubic" : "line", | ||
handles: { | ||
start: { | ||
id: "start", | ||
type: "vertex", | ||
index: indices[0], | ||
x: start[0], | ||
y: start[1] | ||
}, | ||
end: { | ||
id: "end", | ||
type: "vertex", | ||
index: indices[indices.length - 1], | ||
x: end[0], | ||
y: end[1] | ||
}, | ||
...Object.fromEntries( | ||
element.points.slice(1, -1).map(([x, y], i) => { | ||
const id2 = (0, import_editor.uniqueId)(); | ||
return [ | ||
id2, | ||
{ | ||
id: id2, | ||
type: "vertex", | ||
index: indices[i + 1], | ||
x, | ||
y | ||
} | ||
]; | ||
}) | ||
) | ||
} catch (e) { | ||
r({ | ||
type: "error", | ||
data: tldrawHtmlComment, | ||
reason: "found tldraw json but data was a string instead of a TLClipboardModel object" | ||
}); | ||
return; | ||
} | ||
} | ||
}); | ||
break; | ||
} | ||
case "arrow": { | ||
let text = ""; | ||
if (element.boundElements !== null) { | ||
for (const boundElement of element.boundElements) { | ||
if (boundElement.type === "text") { | ||
const labelElement = elements.find((elm) => elm.id === boundElement.id); | ||
if (labelElement) { | ||
text = labelElement.text; | ||
} else { | ||
if (thing.type === "html") { | ||
r({ type: "text", data: text, subtype: "html" }); | ||
return; | ||
} | ||
if (thing.type === "url") { | ||
r({ type: "text", data: text, subtype: "url" }); | ||
return; | ||
} | ||
try { | ||
const json = JSON.parse(text); | ||
if (json.type === "excalidraw/clipboard") { | ||
r({ type: "excalidraw", data: json }); | ||
return; | ||
} else { | ||
r({ type: "text", data: text, subtype: "json" }); | ||
return; | ||
} | ||
} catch (e) { | ||
r({ type: "text", data: text, subtype: "text" }); | ||
return; | ||
} | ||
} | ||
} | ||
const start = element.points[0]; | ||
const end = element.points[element.points.length - 1]; | ||
const startTargetId = excElementIdsToTldrawShapeIds.get(element.startBinding?.elementId); | ||
const endTargetId = excElementIdsToTldrawShapeIds.get(element.endBinding?.elementId); | ||
tldrawContent.shapes.push({ | ||
...base, | ||
type: "arrow", | ||
props: { | ||
text, | ||
bend: getBend(element, start, end), | ||
dash: getDash(element), | ||
opacity: getOpacity(element.opacity), | ||
size: strokeWidthsToSizes[element.strokeWidth] ?? "m", | ||
color: colorsToColors[element.strokeColor] ?? "black", | ||
start: startTargetId ? { | ||
type: "binding", | ||
boundShapeId: startTargetId, | ||
normalizedAnchor: { x: 0.5, y: 0.5 }, | ||
isExact: false | ||
} : { | ||
type: "point", | ||
x: start[0], | ||
y: start[1] | ||
}, | ||
end: endTargetId ? { | ||
type: "binding", | ||
boundShapeId: endTargetId, | ||
normalizedAnchor: { x: 0.5, y: 0.5 }, | ||
isExact: false | ||
} : { | ||
type: "point", | ||
x: end[0], | ||
y: end[1] | ||
}, | ||
arrowheadEnd: arrowheadsToArrowheadTypes[element.endArrowhead] ?? "none", | ||
arrowheadStart: arrowheadsToArrowheadTypes[element.startArrowhead] ?? "none" | ||
} | ||
r({ type: "error", data: text, reason: "unhandled case" }); | ||
}); | ||
break; | ||
} | ||
case "text": { | ||
const { size, scale } = getFontSizeAndScale(element.fontSize); | ||
tldrawContent.shapes.push({ | ||
...base, | ||
type: "text", | ||
props: { | ||
size, | ||
scale, | ||
font: fontFamilyToFontType[element.fontFamily] ?? "draw", | ||
opacity: getOpacity(element.opacity), | ||
color: colorsToColors[element.strokeColor] ?? "black", | ||
text: element.text, | ||
align: textAlignToAlignTypes[element.textAlign] | ||
} | ||
}); | ||
break; | ||
} | ||
case "image": { | ||
const file = files[element.fileId]; | ||
if (!file) | ||
break; | ||
const assetId = import_editor.TLAsset.createId(); | ||
tldrawContent.assets.push({ | ||
id: assetId, | ||
typeName: "asset", | ||
type: "image", | ||
props: { | ||
w: element.width, | ||
h: element.height, | ||
name: element.id ?? "Untitled", | ||
isAnimated: false, | ||
mimeType: file.mimeType, | ||
src: file.dataURL | ||
} | ||
}); | ||
tldrawContent.shapes.push({ | ||
...base, | ||
type: "image", | ||
props: { | ||
opacity: getOpacity(element.opacity), | ||
w: element.width, | ||
h: element.height, | ||
assetId | ||
} | ||
}); | ||
} | ||
}) | ||
) | ||
); | ||
for (const result of results) { | ||
if (result.type === "tldraw") { | ||
(0, import_pasteTldrawContent.pasteTldrawContent)(editor, result.data, point); | ||
return; | ||
} | ||
index = (0, import_editor.getIndexAbove)(index); | ||
} | ||
const p = point ?? (app.inputs.shiftKey ? app.inputs.currentPagePoint : void 0); | ||
app.mark("paste"); | ||
app.putContent(tldrawContent, { | ||
point: p, | ||
select: false, | ||
preserveIds: true | ||
}); | ||
for (const groupedShapeIds of groupShapeIdToChildren.values()) { | ||
if (groupedShapeIds.length > 1) { | ||
app.groupShapes(groupedShapeIds); | ||
const groupShape = app.getShapeById(groupedShapeIds[0]); | ||
if (groupShape?.parentId && (0, import_editor.isShapeId)(groupShape.parentId)) { | ||
rootShapeIds.push(groupShape.parentId); | ||
} | ||
for (const result of results) { | ||
if (result.type === "excalidraw") { | ||
(0, import_pasteExcalidrawContent.pasteExcalidrawContent)(editor, result.data, point); | ||
return; | ||
} | ||
} | ||
for (const [id, angle] of rotatedElements) { | ||
app.select(id); | ||
app.rotateShapesBy([id], angle); | ||
} | ||
const rootShapes = (0, import_utils.compact)(rootShapeIds.map((id) => app.getShapeById(id))); | ||
const bounds = import_primitives.Box2d.Common(rootShapes.map((s) => app.getPageBounds(s))); | ||
const viewPortCenter = app.viewportPageBounds.center; | ||
app.updateShapes( | ||
rootShapes.map((s) => { | ||
const delta = { | ||
x: (s.x ?? 0) - (bounds.x + bounds.w / 2), | ||
y: (s.y ?? 0) - (bounds.y + bounds.h / 2) | ||
}; | ||
return { | ||
id: s.id, | ||
type: s.type, | ||
x: viewPortCenter.x + delta.x, | ||
y: viewPortCenter.y + delta.y | ||
}; | ||
}) | ||
); | ||
app.setSelectedIds(rootShapeIds); | ||
} | ||
const handleFilesBlob = async (app, blobs, point) => { | ||
const urls = blobs.map((blob) => URL.createObjectURL(blob)); | ||
pasteFiles(app, urls, point); | ||
}; | ||
const handleHtmlString = async (app, html, point) => { | ||
const s = html.match(/<tldraw[^>]*>(.*)<\/tldraw>/)?.[1]; | ||
if (s) { | ||
try { | ||
const json = JSON.parse((0, import_lz_string.decompressFromBase64)(s)); | ||
if (json.type === "application/tldraw") { | ||
pasteTldrawContent(app, json.data, point); | ||
} else { | ||
pasteText(app, s, point); | ||
for (const result of results) { | ||
if (result.type === "text" && result.subtype === "html") { | ||
const rootNode = new DOMParser().parseFromString(result.data, "text/html"); | ||
const bodyNode = rootNode.querySelector("body"); | ||
const isHtmlSingleLink = bodyNode && Array.from(bodyNode.children).filter((el) => el.nodeType === 1).length === 1 && bodyNode.firstElementChild && bodyNode.firstElementChild.tagName === "A" && bodyNode.firstElementChild.hasAttribute("href") && bodyNode.firstElementChild.getAttribute("href") !== ""; | ||
if (isHtmlSingleLink) { | ||
const href = bodyNode.firstElementChild.getAttribute("href"); | ||
handleText(editor, href, point); | ||
return; | ||
} | ||
} catch (error) { | ||
pasteText(app, s, point); | ||
if (!results.some((r) => r.type === "text" && r.subtype !== "html") && result.data.trim()) { | ||
handleText(editor, stripHtml(result.data), point); | ||
return; | ||
} | ||
} | ||
} else { | ||
const rootNode = new DOMParser().parseFromString(html, "text/html"); | ||
const bodyNode = rootNode.querySelector("body"); | ||
const isHtmlSingleLink = bodyNode && Array.from(bodyNode.children).filter((el) => el.nodeType === 1).length === 1 && bodyNode.firstElementChild && bodyNode.firstElementChild.tagName === "A" && bodyNode.firstElementChild.hasAttribute("href") && bodyNode.firstElementChild.getAttribute("href") !== ""; | ||
if (isHtmlSingleLink) { | ||
const href = bodyNode.firstElementChild.getAttribute("href"); | ||
pasteText(app, href, point); | ||
} else { | ||
pasteText(app, html, point); | ||
} | ||
for (const result of results) { | ||
if (result.type === "text" && result.subtype === "url") { | ||
(0, import_pasteUrl.pasteUrl)(editor, result.data, point); | ||
return; | ||
} | ||
} | ||
}; | ||
const handleTextString = async (app, text, point) => { | ||
const s = text.trim(); | ||
const tldrawContent = text.match(/<tldraw[^>]*>(.*)<\/tldraw>/)?.[1]; | ||
if (tldrawContent) { | ||
handleHtmlString(app, text); | ||
} else if (s) { | ||
try { | ||
const json = JSON.parse(s); | ||
if (json.type === "application/tldraw") { | ||
pasteTldrawContent(app, json.data, point); | ||
} else if (json.type === "excalidraw/clipboard") { | ||
pasteExcalidrawContent(app, json, point); | ||
} else { | ||
pasteText(app, s, point); | ||
} | ||
} catch (error) { | ||
pasteText(app, s, point); | ||
for (const result of results) { | ||
if (result.type === "text" && result.subtype === "text" && result.data.trim()) { | ||
handleText(editor, result.data, point); | ||
return; | ||
} | ||
} | ||
}; | ||
const handleNativeDataTransferPaste = async (app, clipboardData, point) => { | ||
if (app.isIn("select.editing")) | ||
} | ||
const handleNativeOrMenuCopy = (editor) => { | ||
const content = editor.getContent(); | ||
if (!content) { | ||
if (navigator && navigator.clipboard) { | ||
navigator.clipboard.writeText(""); | ||
} | ||
return; | ||
if (clipboardData) { | ||
const items = Object.values(clipboardData.items); | ||
const writingFile = items.some((item) => item.kind === "file"); | ||
const writingContent = items.some((item) => item.type === "text/html"); | ||
const files = []; | ||
const text = []; | ||
items.forEach((item) => { | ||
if (item.kind === "file") { | ||
const file = item.getAsFile(); | ||
if (file) { | ||
files.push(file); | ||
} | ||
} else if (item.kind === "string") { | ||
text.push(item); | ||
} | ||
}); | ||
if (files.length > 0) { | ||
handleFilesBlob(app, files, point); | ||
} | ||
for (const item of text) { | ||
if (!writingFile && item.type === "text/html") { | ||
await handleHtmlString(app, await dataTransferItemAsString(item), point); | ||
} else if (item.type === "text/plain") { | ||
if (!writingContent) { | ||
await handleTextString(app, await dataTransferItemAsString(item), point); | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
const handleNativeClipboardPaste = async (app, clipboardItems, point) => { | ||
if (app.isIn("select.editing")) | ||
const stringifiedClipboard = (0, import_lz_string.compressToBase64)( | ||
JSON.stringify({ | ||
type: "application/tldraw", | ||
kind: "content", | ||
data: content | ||
}) | ||
); | ||
if (typeof navigator === "undefined") { | ||
return; | ||
const isFile = (item) => { | ||
return item.types.find((i) => i.match(/^image\//)); | ||
}; | ||
const writingFile = clipboardItems.some((item) => isFile(item)); | ||
const writingContent = clipboardItems.some((item) => item.types.includes("text/html")); | ||
const files = clipboardItems.filter((item) => { | ||
if (item.types.find((i) => i.match(/^image\//))) { | ||
return true; | ||
} | ||
return false; | ||
}); | ||
await Promise.all( | ||
files.map(async (item) => { | ||
const type = item.types.find((t) => t !== "text/plain" && t !== "text/html"); | ||
if (type) { | ||
const file = await item.getType(type); | ||
if (file) { | ||
await handleFilesBlob(app, [file], point); | ||
} | ||
} else { | ||
const textItems = content.shapes.map((shape) => { | ||
if (editor.isShapeOfType(shape, import_editor.TextShapeUtil) || editor.isShapeOfType(shape, import_editor.GeoShapeUtil) || editor.isShapeOfType(shape, import_editor.ArrowShapeUtil)) { | ||
return shape.props.text; | ||
} | ||
}) | ||
); | ||
for (const item of clipboardItems) { | ||
if (item.types.includes("text/html")) { | ||
if (writingFile) | ||
break; | ||
const blob = await item.getType("text/html"); | ||
await handleHtmlString(app, await blobAsString(blob), point); | ||
} else if (item.types.includes("text/uri-list")) { | ||
if (writingContent) | ||
break; | ||
const blob = await item.getType("text/uri-list"); | ||
await pasteUrl(app, await blobAsString(blob), point); | ||
} else if (item.types.includes("text/plain")) { | ||
if (writingContent) | ||
break; | ||
const blob = await item.getType("text/plain"); | ||
await handleTextString(app, await blobAsString(blob), point); | ||
if (editor.isShapeOfType(shape, import_editor.BookmarkShapeUtil) || editor.isShapeOfType(shape, import_editor.EmbedShapeUtil)) { | ||
return shape.props.url; | ||
} | ||
return null; | ||
}).filter(import_utils.isNonNull); | ||
if (navigator.clipboard?.write) { | ||
const htmlBlob = new Blob([`<tldraw>${stringifiedClipboard}</tldraw>`], { | ||
type: "text/html" | ||
}); | ||
let textContent = textItems.join(" "); | ||
if (textContent === "") { | ||
textContent = " "; | ||
} | ||
navigator.clipboard.write([ | ||
new ClipboardItem({ | ||
"text/html": htmlBlob, | ||
// What is this second blob used for? | ||
"text/plain": new Blob([textContent], { type: "text/plain" }) | ||
}) | ||
]); | ||
} else if (navigator.clipboard.writeText) { | ||
navigator.clipboard.writeText(`<tldraw>${stringifiedClipboard}</tldraw>`); | ||
} | ||
@@ -755,31 +336,37 @@ } | ||
function useMenuClipboardEvents() { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const trackEvent = (0, import_useEventsProvider.useEvents)(); | ||
const copy = (0, import_react.useCallback)( | ||
function onCopy() { | ||
if (app.selectedIds.length === 0) | ||
function onCopy(source) { | ||
if (editor.selectedIds.length === 0) | ||
return; | ||
handleMenuCopy(app); | ||
handleNativeOrMenuCopy(editor); | ||
trackEvent("copy", { source }); | ||
}, | ||
[app] | ||
[editor, trackEvent] | ||
); | ||
const cut = (0, import_react.useCallback)( | ||
function onCut() { | ||
if (app.selectedIds.length === 0) | ||
function onCut(source) { | ||
if (editor.selectedIds.length === 0) | ||
return; | ||
handleMenuCopy(app); | ||
app.deleteShapes(); | ||
handleNativeOrMenuCopy(editor); | ||
editor.deleteShapes(); | ||
trackEvent("cut", { source }); | ||
}, | ||
[app] | ||
[editor, trackEvent] | ||
); | ||
const paste = (0, import_react.useCallback)( | ||
async function onPaste(data, point) { | ||
async function onPaste(data, source, point) { | ||
if (editor.editingId !== null || disallowClipboardEvents(editor)) | ||
return; | ||
if (Array.isArray(data) && data[0] instanceof ClipboardItem) { | ||
handleNativeClipboardPaste(app, data, point); | ||
handlePasteFromClipboardApi(editor, data, point); | ||
trackEvent("paste", { source: "menu" }); | ||
} else { | ||
navigator.clipboard.read().then((clipboardItems) => { | ||
paste(clipboardItems, app.inputs.currentPagePoint); | ||
paste(clipboardItems, source, point); | ||
}); | ||
} | ||
}, | ||
[app] | ||
[editor, trackEvent] | ||
); | ||
@@ -793,4 +380,5 @@ return { | ||
function useNativeClipboardEvents() { | ||
const app = (0, import_editor.useApp)(); | ||
const appIsFocused = (0, import_useAppIsFocused.useAppIsFocused)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const trackEvent = (0, import_useEventsProvider.useEvents)(); | ||
const appIsFocused = (0, import_useEditorIsFocused.useEditorIsFocused)(); | ||
(0, import_react.useEffect)(() => { | ||
@@ -800,24 +388,40 @@ if (!appIsFocused) | ||
const copy = () => { | ||
if (app.selectedIds.length === 0 || app.editingId !== null || disallowClipboardEvents(app)) | ||
if (editor.selectedIds.length === 0 || editor.editingId !== null || disallowClipboardEvents(editor)) | ||
return; | ||
handleMenuCopy(app); | ||
handleNativeOrMenuCopy(editor); | ||
trackEvent("copy", { source: "kbd" }); | ||
}; | ||
function cut() { | ||
if (app.selectedIds.length === 0 || app.editingId !== null || disallowClipboardEvents(app)) | ||
if (editor.selectedIds.length === 0 || editor.editingId !== null || disallowClipboardEvents(editor)) | ||
return; | ||
handleMenuCopy(app); | ||
app.deleteShapes(); | ||
handleNativeOrMenuCopy(editor); | ||
editor.deleteShapes(); | ||
trackEvent("cut", { source: "kbd" }); | ||
} | ||
let disablingMiddleClickPaste = false; | ||
const pointerUpHandler = (e) => { | ||
if (e.button === 1) { | ||
disablingMiddleClickPaste = true; | ||
requestAnimationFrame(() => { | ||
disablingMiddleClickPaste = false; | ||
}); | ||
} | ||
}; | ||
const paste = (event) => { | ||
if (app.editingId !== null || disallowClipboardEvents(app)) | ||
if (disablingMiddleClickPaste) { | ||
event.stopPropagation(); | ||
return; | ||
if (event.clipboardData && !app.inputs.shiftKey) { | ||
handleNativeDataTransferPaste(app, event.clipboardData); | ||
} | ||
if (editor.editingId !== null || disallowClipboardEvents(editor)) | ||
return; | ||
if (event.clipboardData && !editor.inputs.shiftKey) { | ||
handlePasteFromEventClipboardData(editor, event.clipboardData); | ||
} else { | ||
navigator.clipboard.read().then((clipboardItems) => { | ||
if (Array.isArray(clipboardItems) && clipboardItems[0] instanceof ClipboardItem) { | ||
handleNativeClipboardPaste(app, clipboardItems, app.inputs.currentPagePoint); | ||
handlePasteFromClipboardApi(editor, clipboardItems, editor.inputs.currentPagePoint); | ||
} | ||
}); | ||
} | ||
trackEvent("paste", { source: "kbd" }); | ||
}; | ||
@@ -827,2 +431,3 @@ document.addEventListener("copy", copy); | ||
document.addEventListener("paste", paste); | ||
document.addEventListener("pointerup", pointerUpHandler); | ||
return () => { | ||
@@ -832,5 +437,6 @@ document.removeEventListener("copy", copy); | ||
document.removeEventListener("paste", paste); | ||
document.removeEventListener("pointerup", pointerUpHandler); | ||
}; | ||
}, [app, appIsFocused]); | ||
}, [editor, trackEvent, appIsFocused]); | ||
} | ||
//# sourceMappingURL=useClipboardEvents.js.map |
@@ -31,4 +31,4 @@ "use strict"; | ||
__export(useContextMenuSchema_exports, { | ||
ContextMenuSchemaContext: () => ContextMenuSchemaContext, | ||
ContextMenuSchemaProvider: () => ContextMenuSchemaProvider, | ||
TLUiContextMenuSchemaContext: () => TLUiContextMenuSchemaContext, | ||
TLUiContextMenuSchemaProvider: () => TLUiContextMenuSchemaProvider, | ||
useContextMenuSchema: () => useContextMenuSchema | ||
@@ -39,4 +39,4 @@ }); | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_react = __toESM(require("react")); | ||
var import_signia_react = require("signia-react"); | ||
var import_menuHelpers = require("./menuHelpers"); | ||
@@ -47,22 +47,23 @@ var import_useActions = require("./useActions"); | ||
var import_useShowAutoSizeToggle = require("./useShowAutoSizeToggle"); | ||
const ContextMenuSchemaContext = import_react.default.createContext({}); | ||
const showUiPaste = typeof window !== "undefined" && "navigator" in window && Boolean(navigator.clipboard) && Boolean(navigator.clipboard.read); | ||
const ContextMenuSchemaProvider = (0, import_signia_react.track)(function ContextMenuSchemaProvider2({ | ||
const TLUiContextMenuSchemaContext = import_react.default.createContext( | ||
{} | ||
); | ||
const TLUiContextMenuSchemaProvider = (0, import_state.track)(function TLUiContextMenuSchemaProvider2({ | ||
overrides, | ||
children | ||
}) { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const actions = (0, import_useActions.useActions)(); | ||
const showAutoSizeToggle = (0, import_useShowAutoSizeToggle.useShowAutoSizeToggle)(); | ||
const onlyFlippableShapeSelected = (0, import_useOnlyFlippableShape.useOnlyFlippableShape)(); | ||
const selectedCount = app.selectedIds.length; | ||
const selectedCount = editor.selectedIds.length; | ||
const oneSelected = selectedCount > 0; | ||
const oneEmbedSelected = (0, import_signia_react.useValue)( | ||
const oneEmbedSelected = (0, import_state.useValue)( | ||
"oneEmbedSelected", | ||
() => { | ||
if (app.selectedIds.length !== 1) | ||
if (editor.selectedIds.length !== 1) | ||
return false; | ||
return app.selectedIds.some((selectedId) => { | ||
const shape = app.getShapeById(selectedId); | ||
return shape && import_editor.TLEmbedShapeDef.is(shape) && shape.props.url; | ||
return editor.selectedIds.some((selectedId) => { | ||
const shape = editor.getShapeById(selectedId); | ||
return shape && editor.isShapeOfType(shape, import_editor.EmbedShapeUtil) && shape.props.url; | ||
}); | ||
@@ -72,10 +73,10 @@ }, | ||
); | ||
const oneEmbeddableBookmarkSelected = (0, import_signia_react.useValue)( | ||
const oneEmbeddableBookmarkSelected = (0, import_state.useValue)( | ||
"oneEmbeddableBookmarkSelected", | ||
() => { | ||
if (app.selectedIds.length !== 1) | ||
if (editor.selectedIds.length !== 1) | ||
return false; | ||
return app.selectedIds.some((selectedId) => { | ||
const shape = app.getShapeById(selectedId); | ||
return shape && import_editor.TLBookmarkShapeDef.is(shape) && (0, import_editor.getEmbedInfo)(shape.props.url); | ||
return editor.selectedIds.some((selectedId) => { | ||
const shape = editor.getShapeById(selectedId); | ||
return shape && editor.isShapeOfType(shape, import_editor.BookmarkShapeUtil) && shape.props.url && (0, import_editor.getEmbedInfo)(shape.props.url); | ||
}); | ||
@@ -88,4 +89,12 @@ }, | ||
const threeStackableItems = (0, import_menuHelpers.useThreeStackableItems)(); | ||
const atLeastOneShapeOnPage = (0, import_signia_react.useValue)("atLeastOneShapeOnPage", () => app.shapeIds.size > 0, []); | ||
const isTransparentBg = (0, import_signia_react.useValue)("isTransparentBg", () => app.instanceState.exportBackground, []); | ||
const atLeastOneShapeOnPage = (0, import_state.useValue)( | ||
"atLeastOneShapeOnPage", | ||
() => editor.currentPageShapeIds.size > 0, | ||
[] | ||
); | ||
const isTransparentBg = (0, import_state.useValue)( | ||
"isTransparentBg", | ||
() => editor.instanceState.exportBackground, | ||
[] | ||
); | ||
const allowGroup = (0, import_menuHelpers.useAllowGroup)(); | ||
@@ -95,14 +104,17 @@ const allowUngroup = (0, import_menuHelpers.useAllowUngroup)(); | ||
const showEditLink = (0, import_useHasLinkShapeSelected.useHasLinkShapeSelected)(); | ||
const contextMenuSchema = (0, import_react.useMemo)(() => { | ||
let contextMenuSchema2 = (0, import_menuHelpers.compactMenuItems)([ | ||
const { onlySelectedShape } = editor; | ||
const isShapeLocked = onlySelectedShape && editor.isShapeOrAncestorLocked(onlySelectedShape); | ||
const contextTLUiMenuSchema = (0, import_react.useMemo)(() => { | ||
let contextTLUiMenuSchema2 = (0, import_menuHelpers.compactMenuItems)([ | ||
(0, import_menuHelpers.menuGroup)( | ||
"selection", | ||
oneEmbedSelected && (0, import_menuHelpers.menuItem)(actions["open-embed-link"]), | ||
oneEmbedSelected && (0, import_menuHelpers.menuItem)(actions["convert-to-bookmark"]), | ||
oneEmbedSelected && !isShapeLocked && (0, import_menuHelpers.menuItem)(actions["convert-to-bookmark"]), | ||
oneEmbeddableBookmarkSelected && (0, import_menuHelpers.menuItem)(actions["convert-to-embed"]), | ||
showAutoSizeToggle && (0, import_menuHelpers.menuItem)(actions["toggle-auto-size"]), | ||
showEditLink && (0, import_menuHelpers.menuItem)(actions["edit-link"]), | ||
oneSelected && (0, import_menuHelpers.menuItem)(actions["duplicate"]), | ||
allowGroup && (0, import_menuHelpers.menuItem)(actions["group"]), | ||
allowUngroup && (0, import_menuHelpers.menuItem)(actions["ungroup"]) | ||
showEditLink && !isShapeLocked && (0, import_menuHelpers.menuItem)(actions["edit-link"]), | ||
oneSelected && !isShapeLocked && (0, import_menuHelpers.menuItem)(actions["duplicate"]), | ||
allowGroup && !isShapeLocked && (0, import_menuHelpers.menuItem)(actions["group"]), | ||
allowUngroup && !isShapeLocked && (0, import_menuHelpers.menuItem)(actions["ungroup"]), | ||
oneSelected && (0, import_menuHelpers.menuItem)(actions["toggle-lock"]) | ||
), | ||
@@ -133,3 +145,3 @@ (0, import_menuHelpers.menuGroup)( | ||
), | ||
onlyFlippableShapeSelected && (0, import_menuHelpers.menuGroup)( | ||
onlyFlippableShapeSelected && !isShapeLocked && (0, import_menuHelpers.menuGroup)( | ||
"flip", | ||
@@ -146,3 +158,3 @@ (0, import_menuHelpers.menuItem)(actions["flip-horizontal"]), | ||
), | ||
oneSelected && (0, import_menuHelpers.menuSubmenu)( | ||
oneSelected && !isShapeLocked && (0, import_menuHelpers.menuSubmenu)( | ||
"reorder", | ||
@@ -158,9 +170,9 @@ "context-menu.reorder", | ||
), | ||
oneSelected && (0, import_menuHelpers.menuCustom)("MOVE_TO_PAGE_MENU", { readonlyOk: false }) | ||
oneSelected && !isShapeLocked && (0, import_menuHelpers.menuCustom)("MOVE_TO_PAGE_MENU", { readonlyOk: false }) | ||
), | ||
(0, import_menuHelpers.menuGroup)( | ||
"clipboard-group", | ||
oneSelected && (0, import_menuHelpers.menuItem)(actions["cut"]), | ||
oneSelected && !isShapeLocked && (0, import_menuHelpers.menuItem)(actions["cut"]), | ||
oneSelected && (0, import_menuHelpers.menuItem)(actions["copy"]), | ||
showUiPaste && (0, import_menuHelpers.menuCustom)("MENU_PASTE", { readonlyOk: false }) | ||
import_menuHelpers.showMenuPaste && (0, import_menuHelpers.menuItem)(actions["paste"]) | ||
), | ||
@@ -203,6 +215,6 @@ atLeastOneShapeOnPage && (0, import_menuHelpers.menuGroup)( | ||
), | ||
oneSelected && (0, import_menuHelpers.menuGroup)("delete-group", (0, import_menuHelpers.menuItem)(actions["delete"])) | ||
oneSelected && !isShapeLocked && (0, import_menuHelpers.menuGroup)("delete-group", (0, import_menuHelpers.menuItem)(actions["delete"])) | ||
]); | ||
if (overrides) { | ||
contextMenuSchema2 = overrides(app, contextMenuSchema2, { | ||
contextTLUiMenuSchema2 = overrides(editor, contextTLUiMenuSchema2, { | ||
actions, | ||
@@ -217,5 +229,5 @@ oneSelected, | ||
} | ||
return contextMenuSchema2; | ||
return contextTLUiMenuSchema2; | ||
}, [ | ||
app, | ||
editor, | ||
overrides, | ||
@@ -236,10 +248,11 @@ actions, | ||
oneEmbeddableBookmarkSelected, | ||
isTransparentBg | ||
isTransparentBg, | ||
isShapeLocked | ||
]); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ContextMenuSchemaContext.Provider, { value: contextMenuSchema, children }); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TLUiContextMenuSchemaContext.Provider, { value: contextTLUiMenuSchema, children }); | ||
}); | ||
function useContextMenuSchema() { | ||
const ctx = import_react.default.useContext(ContextMenuSchemaContext); | ||
const ctx = import_react.default.useContext(TLUiContextMenuSchemaContext); | ||
if (!ctx) { | ||
throw new Error("useContextMenuSchema must be used inside of a ContextMenuSchemaProvider."); | ||
throw new Error("useContextMenuSchema must be used inside of a TLUiContextMenuSchemaProvider."); | ||
} | ||
@@ -246,0 +259,0 @@ return ctx; |
@@ -29,3 +29,3 @@ "use strict"; | ||
function useCopyAs() { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const { addToast } = (0, import_useToastsProvider.useToasts)(); | ||
@@ -41,5 +41,5 @@ const msg = (0, import_useTranslation.useTranslation)(); | ||
// little awkward. | ||
function copyAs(ids = app.selectedIds, format = "svg") { | ||
function copyAs(ids = editor.selectedIds, format = "svg") { | ||
if (ids.length === 0) { | ||
ids = [...app.shapeIds]; | ||
ids = [...editor.currentPageShapeIds]; | ||
} | ||
@@ -55,3 +55,3 @@ if (ids.length === 0) { | ||
new ClipboardItem({ | ||
"text/plain": getExportedSvgBlob(app, ids) | ||
"text/plain": getExportedSvgBlob(editor, ids) | ||
}) | ||
@@ -61,3 +61,3 @@ ]); | ||
fallbackWriteTextAsync( | ||
async () => (0, import_editor.getSvgAsString)(await getExportSvgElement(app, ids, format)) | ||
async () => (0, import_editor.getSvgAsString)(await getExportSvgElement(editor, ids)) | ||
); | ||
@@ -71,27 +71,40 @@ } | ||
const mimeType = format === "jpeg" ? "image/jpeg" : "image/png"; | ||
const blobPromise = getExportedImageBlob(editor, ids, format).then((blob) => { | ||
if (blob) { | ||
if (window.navigator.clipboard) { | ||
return blob; | ||
} | ||
throw new Error("Copy not supported"); | ||
} else { | ||
addToast({ | ||
id: "copy-fail", | ||
icon: "warning-triangle", | ||
title: msg("toast.error.copy-fail.title"), | ||
description: msg("toast.error.copy-fail.desc") | ||
}); | ||
throw new Error("Copy not possible"); | ||
} | ||
}); | ||
window.navigator.clipboard.write([ | ||
new ClipboardItem({ | ||
// Note: This needs to use the promise based approach for safari/ios to not bail on a permissions error. | ||
[mimeType]: getExportedImageBlob(app, ids, format).then((blob) => { | ||
if (blob) { | ||
if (window.navigator.clipboard) { | ||
return blob; | ||
} | ||
throw new Error("Copy not supported"); | ||
} else { | ||
addToast({ | ||
id: "copy-fail", | ||
icon: "warning-triangle", | ||
title: msg("toast.error.copy-fail.title"), | ||
description: msg("toast.error.copy-fail.desc") | ||
}); | ||
throw new Error("Copy not possible"); | ||
} | ||
}) | ||
[mimeType]: blobPromise | ||
}) | ||
]); | ||
]).catch((err) => { | ||
if (!err.toString().match(/^TypeError: DOMString not supported/)) { | ||
console.error(err); | ||
} | ||
blobPromise.then((blob) => { | ||
window.navigator.clipboard.write([ | ||
new ClipboardItem({ | ||
// Note: This needs to use the promise based approach for safari/ios to not bail on a permissions error. | ||
[mimeType]: blob | ||
}) | ||
]); | ||
}); | ||
}); | ||
break; | ||
} | ||
case "json": { | ||
const data = app.getContent(ids); | ||
const data = editor.getContent(ids); | ||
if (window.navigator.clipboard) { | ||
@@ -115,9 +128,9 @@ const jsonStr = JSON.stringify(data); | ||
}, | ||
[app, addToast, msg] | ||
[editor, addToast, msg] | ||
); | ||
} | ||
async function getExportSvgElement(app, ids, format) { | ||
const svg = await app.getSvg(ids, { | ||
scale: format === "svg" ? 1 : 2, | ||
background: app.instanceState.exportBackground | ||
async function getExportSvgElement(editor, ids) { | ||
const svg = await editor.getSvg(ids, { | ||
scale: 1, | ||
background: editor.instanceState.exportBackground | ||
}); | ||
@@ -128,17 +141,19 @@ if (!svg) | ||
} | ||
async function getExportedSvgBlob(app, ids) { | ||
return new Blob([(0, import_editor.getSvgAsString)(await getExportSvgElement(app, ids, "svg"))], { | ||
async function getExportedSvgBlob(editor, ids) { | ||
return new Blob([(0, import_editor.getSvgAsString)(await getExportSvgElement(editor, ids))], { | ||
type: "text/plain" | ||
}); | ||
} | ||
async function getExportedImageBlob(app, ids, format) { | ||
return await (0, import_editor.getSvgAsImage)(await getExportSvgElement(app, ids, format), { | ||
async function getExportedImageBlob(editor, ids, format) { | ||
return await (0, import_editor.getSvgAsImage)(await getExportSvgElement(editor, ids), { | ||
type: format, | ||
quality: 1, | ||
scale: 1 | ||
scale: 2 | ||
}); | ||
} | ||
async function fallbackWriteTextAsync(getText) { | ||
if (!(navigator && navigator.clipboard)) | ||
return; | ||
navigator.clipboard.writeText(await getText()); | ||
} | ||
//# sourceMappingURL=useCopyAs.js.map |
@@ -29,5 +29,7 @@ "use strict"; | ||
var import_react = require("react"); | ||
var import_useEventsProvider = require("./useEventsProvider"); | ||
const DialogsContext = (0, import_react.createContext)({}); | ||
function DialogsProvider({ children }) { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const trackEvent = (0, import_useEventsProvider.useEvents)(); | ||
const [dialogs, setDialogs] = (0, import_react.useState)([]); | ||
@@ -40,6 +42,7 @@ const addDialog = (0, import_react.useCallback)( | ||
}); | ||
app.openMenus.add(id); | ||
trackEvent("open-menu", { source: "dialog", id }); | ||
editor.addOpenMenu(id); | ||
return id; | ||
}, | ||
[app] | ||
[editor, trackEvent] | ||
); | ||
@@ -59,6 +62,7 @@ const updateDialog = (0, import_react.useCallback)( | ||
); | ||
app.openMenus.add(id); | ||
trackEvent("open-menu", { source: "dialog", id }); | ||
editor.addOpenMenu(id); | ||
return id; | ||
}, | ||
[app] | ||
[editor, trackEvent] | ||
); | ||
@@ -76,6 +80,7 @@ const removeDialog = (0, import_react.useCallback)( | ||
); | ||
app.openMenus.delete(id); | ||
trackEvent("close-menu", { source: "dialog", id }); | ||
editor.deleteOpenMenu(id); | ||
return id; | ||
}, | ||
[app] | ||
[editor, trackEvent] | ||
); | ||
@@ -86,7 +91,8 @@ const clearDialogs = (0, import_react.useCallback)(() => { | ||
m.onClose?.(); | ||
app.openMenus.delete(m.id); | ||
trackEvent("close-menu", { source: "dialog", id: m.id }); | ||
editor.deleteOpenMenu(m.id); | ||
}); | ||
return []; | ||
}); | ||
}, [app]); | ||
}, [editor, trackEvent]); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
@@ -93,0 +99,0 @@ DialogsContext.Provider, |
@@ -29,9 +29,9 @@ "use strict"; | ||
function useExportAs() { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const { addToast } = (0, import_useToastsProvider.useToasts)(); | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
return (0, import_react.useCallback)( | ||
async function exportAs(ids = app.selectedIds, format = "png") { | ||
async function exportAs(ids = editor.selectedIds, format = "png") { | ||
if (ids.length === 0) { | ||
ids = [...app.shapeIds]; | ||
ids = [...editor.currentPageShapeIds]; | ||
} | ||
@@ -41,9 +41,17 @@ if (ids.length === 0) { | ||
} | ||
const svg = await app.getSvg(ids, { | ||
scale: format === "svg" ? 1 : 2, | ||
background: app.instanceState.exportBackground | ||
const svg = await editor.getSvg(ids, { | ||
scale: 1, | ||
background: editor.instanceState.exportBackground | ||
}); | ||
if (!svg) | ||
throw new Error("Could not construct SVG."); | ||
const name = ids.length === 1 ? app.getShapeById(ids[0])?.id.replace(/:/, "_") : "shapes"; | ||
let name = "shapes"; | ||
if (ids.length === 1) { | ||
const first = editor.getShapeById(ids[0]); | ||
if (editor.isShapeOfType(first, import_editor.FrameShapeUtil)) { | ||
name = first.props.name ?? "frame"; | ||
} else { | ||
name = first.id.replace(/:/, "_"); | ||
} | ||
} | ||
switch (format) { | ||
@@ -60,3 +68,3 @@ case "svg": { | ||
quality: 1, | ||
scale: 1 | ||
scale: 2 | ||
}); | ||
@@ -78,3 +86,3 @@ if (!image) { | ||
case "json": { | ||
const data = app.getContent(ids); | ||
const data = editor.getContent(ids); | ||
const dataURL = URL.createObjectURL( | ||
@@ -91,5 +99,5 @@ new Blob([JSON.stringify(data, null, 4)], { type: "application/json" }) | ||
}, | ||
[app, addToast, msg] | ||
[editor, addToast, msg] | ||
); | ||
} | ||
//# sourceMappingURL=useExportAs.js.map |
@@ -25,14 +25,14 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_signia_react = require("signia-react"); | ||
var import_state = require("@tldraw/state"); | ||
function useHasLinkShapeSelected() { | ||
const app = (0, import_editor.useApp)(); | ||
return (0, import_signia_react.useValue)( | ||
const editor = (0, import_editor.useEditor)(); | ||
return (0, import_state.useValue)( | ||
"hasLinkShapeSelected", | ||
() => { | ||
const { selectedShapes } = app; | ||
const { selectedShapes } = editor; | ||
return selectedShapes.length === 1 && "url" in selectedShapes[0].props && selectedShapes[0].type !== "embed"; | ||
}, | ||
[app] | ||
[editor] | ||
); | ||
} | ||
//# sourceMappingURL=useHasLinkShapeSelected.js.map |
@@ -38,5 +38,5 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_utils = require("@tldraw/utils"); | ||
var import_react = __toESM(require("react")); | ||
var import_signia_react = require("signia-react"); | ||
var import_KeyboardShortcutsDialog = require("../components/KeyboardShortcutsDialog"); | ||
@@ -48,9 +48,9 @@ var import_menuHelpers = require("./menuHelpers"); | ||
const HelpMenuSchemaContext = import_react.default.createContext({}); | ||
const HelpMenuSchemaProvider = (0, import_signia_react.track)(function HelpMenuSchemaProvider2({ | ||
const HelpMenuSchemaProvider = (0, import_state.track)(function HelpMenuSchemaProvider2({ | ||
overrides, | ||
children | ||
}) { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const actions = (0, import_useActions.useActions)(); | ||
const selectedCount = app.selectedIds.length; | ||
const selectedCount = editor.selectedIds.length; | ||
const oneSelected = selectedCount > 0; | ||
@@ -61,4 +61,4 @@ const twoSelected = selectedCount > 1; | ||
const { addDialog } = (0, import_useDialogsProvider.useDialogs)(); | ||
const helpMenuSchema = (0, import_react.useMemo)(() => { | ||
const helpMenuSchema2 = (0, import_utils.compact)([ | ||
const helpTLUiMenuSchema = (0, import_react.useMemo)(() => { | ||
const helpTLUiMenuSchema2 = (0, import_utils.compact)([ | ||
(0, import_menuHelpers.menuGroup)( | ||
@@ -78,3 +78,3 @@ "top", | ||
if (overrides) { | ||
return overrides(app, helpMenuSchema2, { | ||
return overrides(editor, helpTLUiMenuSchema2, { | ||
actions, | ||
@@ -88,5 +88,5 @@ currentLanguage, | ||
} | ||
return helpMenuSchema2; | ||
return helpTLUiMenuSchema2; | ||
}, [ | ||
app, | ||
editor, | ||
overrides, | ||
@@ -101,3 +101,3 @@ languages, | ||
]); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpMenuSchemaContext.Provider, { value: helpMenuSchema, children }); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpMenuSchemaContext.Provider, { value: helpTLUiMenuSchema, children }); | ||
}); | ||
@@ -107,3 +107,3 @@ function useHelpMenuSchema() { | ||
if (!ctx) { | ||
throw new Error("useHelpMenuSchema must be used inside of a helpMenuSchemaProvider."); | ||
throw new Error("useHelpMenuSchema must be used inside of a helpTLUiMenuSchemaProvider."); | ||
} | ||
@@ -110,0 +110,0 @@ return ctx; |
@@ -27,3 +27,3 @@ "use strict"; | ||
function useInsertMedia() { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const inputRef = (0, import_react.useRef)(); | ||
@@ -40,3 +40,8 @@ (0, import_react.useEffect)(() => { | ||
return; | ||
await (0, import_editor.createShapesFromFiles)(app, Array.from(fileList), app.viewportPageBounds.center, false); | ||
await editor.putExternalContent({ | ||
type: "files", | ||
files: Array.from(fileList), | ||
point: editor.viewportPageBounds.center, | ||
ignoreParent: false | ||
}); | ||
input.value = ""; | ||
@@ -49,3 +54,3 @@ } | ||
}; | ||
}, [app]); | ||
}, [editor]); | ||
return (0, import_react.useCallback)(() => { | ||
@@ -52,0 +57,0 @@ inputRef.current?.click(); |
@@ -38,3 +38,3 @@ "use strict"; | ||
var import_useActions = require("./useActions"); | ||
var import_useAppIsFocused = require("./useAppIsFocused"); | ||
var import_useEditorIsFocused = require("./useEditorIsFocused"); | ||
var import_useReadonly = require("./useReadonly"); | ||
@@ -51,4 +51,4 @@ var import_useTools = require("./useTools"); | ||
function useKeyboardShortcuts() { | ||
const app = (0, import_editor.useApp)(); | ||
const appIsFocused = (0, import_useAppIsFocused.useAppIsFocused)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const appIsFocused = (0, import_useEditorIsFocused.useEditorIsFocused)(); | ||
const isReadonly = (0, import_useReadonly.useReadonly)(); | ||
@@ -60,7 +60,7 @@ const actions = (0, import_useActions.useActions)(); | ||
return; | ||
const container = app.getContainer(); | ||
const container = editor.getContainer(); | ||
const hot = (keys, callback) => { | ||
(0, import_hotkeys_js.default)(keys, { element: container, scope: app.instanceId }, callback); | ||
(0, import_hotkeys_js.default)(keys, { element: container, scope: editor.store.id }, callback); | ||
}; | ||
const areShortcutsDisabled = () => app.isMenuOpen || app.editingId !== null || app.crashingError; | ||
const areShortcutsDisabled = () => editor.isMenuOpen || editor.editingId !== null || editor.crashingError; | ||
for (const action of Object.values(actions)) { | ||
@@ -77,7 +77,7 @@ if (!action.kbd) | ||
(0, import_editor.preventDefault)(event); | ||
action.onSelect(); | ||
action.onSelect("kbd"); | ||
}); | ||
} | ||
for (const tool of Object.values(tools)) { | ||
if (!tool.kbd) | ||
if (!tool.kbd || !tool.readonlyOk && editor.isReadOnly) | ||
continue; | ||
@@ -90,55 +90,50 @@ if (SKIP_KBDS.includes(tool.id)) | ||
(0, import_editor.preventDefault)(event); | ||
tool.onSelect(); | ||
tool.onSelect("kbd"); | ||
}); | ||
} | ||
hot("g", () => { | ||
if (areShortcutsDisabled()) | ||
return; | ||
app.setSelectedTool("geo"); | ||
}); | ||
hot("backspace,del", () => { | ||
if (areShortcutsDisabled()) | ||
return; | ||
actions["delete"].onSelect(); | ||
}); | ||
hot("=", () => { | ||
if (areShortcutsDisabled()) | ||
return; | ||
actions["zoom-in"].onSelect(); | ||
}); | ||
hot("-", () => { | ||
if (areShortcutsDisabled()) | ||
return; | ||
actions["zoom-out"].onSelect(); | ||
}); | ||
import_hotkeys_js.default.setScope(app.instanceId); | ||
import_hotkeys_js.default.setScope(editor.store.id); | ||
return () => { | ||
import_hotkeys_js.default.deleteScope(app.instanceId); | ||
import_hotkeys_js.default.deleteScope(editor.store.id); | ||
}; | ||
}, [actions, tools, isReadonly, app, appIsFocused]); | ||
}, [actions, tools, isReadonly, editor, appIsFocused]); | ||
} | ||
function getHotkeysStringFromKbd(kbd) { | ||
let str = ""; | ||
const chars = kbd.split(""); | ||
if (chars.length === 1) { | ||
str = chars[0]; | ||
} else { | ||
if (chars[0] === "!") { | ||
str = `shift+${chars[1]}`; | ||
} else if (chars[0] === "?") { | ||
str = `alt+${chars[1]}`; | ||
} else if (chars[0] === "$") { | ||
if (chars[1] === "!") { | ||
str = `cmd+shift+${chars[2]},ctrl+shift+${chars[2]}`; | ||
} else if (chars[1] === "?") { | ||
str = `cmd+\u2325+${chars[2]},ctrl+alt+${chars[2]}`; | ||
return getKeys(kbd).map((kbd2) => { | ||
let str = ""; | ||
const chars = kbd2.split(""); | ||
if (chars.length === 1) { | ||
str = chars[0]; | ||
} else { | ||
if (chars[0] === "!") { | ||
str = `shift+${chars[1]}`; | ||
} else if (chars[0] === "?") { | ||
str = `alt+${chars[1]}`; | ||
} else if (chars[0] === "$") { | ||
if (chars[1] === "!") { | ||
str = `cmd+shift+${chars[2]},ctrl+shift+${chars[2]}`; | ||
} else if (chars[1] === "?") { | ||
str = `cmd+\u2325+${chars[2]},ctrl+alt+${chars[2]}`; | ||
} else { | ||
str = `cmd+${chars[1]},ctrl+${chars[1]}`; | ||
} | ||
} else { | ||
str = `cmd+${chars[1]},ctrl+${chars[1]}`; | ||
str = kbd2; | ||
} | ||
} else { | ||
str = kbd; | ||
} | ||
return str; | ||
}).join(","); | ||
} | ||
function getKeys(key) { | ||
if (typeof key !== "string") | ||
key = ""; | ||
key = key.replace(/\s/g, ""); | ||
const keys = key.split(","); | ||
let index = keys.lastIndexOf(""); | ||
for (; index >= 0; ) { | ||
keys[index - 1] += ","; | ||
keys.splice(index, 1); | ||
index = keys.lastIndexOf(""); | ||
} | ||
return str; | ||
return keys; | ||
} | ||
//# sourceMappingURL=useKeyboardShortcuts.js.map |
@@ -38,5 +38,5 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_utils = require("@tldraw/utils"); | ||
var import_react = __toESM(require("react")); | ||
var import_signia_react = require("signia-react"); | ||
var import_menuHelpers = require("./menuHelpers"); | ||
@@ -48,7 +48,7 @@ var import_useActions = require("./useActions"); | ||
); | ||
const KeyboardShortcutsSchemaProvider = (0, import_signia_react.track)(function KeyboardShortcutsSchemaProvider2({ | ||
const KeyboardShortcutsSchemaProvider = (0, import_state.track)(function KeyboardShortcutsSchemaProvider2({ | ||
overrides, | ||
children | ||
}) { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const tools = (0, import_useTools.useTools)(); | ||
@@ -71,3 +71,4 @@ const actions = (0, import_useActions.useActions)(); | ||
(0, import_menuHelpers.menuItem)(tools["frame"]), | ||
(0, import_menuHelpers.menuItem)(tools["note"]) | ||
(0, import_menuHelpers.menuItem)(tools["note"]), | ||
(0, import_menuHelpers.menuItem)(tools["laser"]) | ||
), | ||
@@ -125,6 +126,6 @@ (0, import_menuHelpers.menuGroup)( | ||
if (overrides) { | ||
return overrides(app, keyboardShortcutsSchema2, { tools, actions }); | ||
return overrides(editor, keyboardShortcutsSchema2, { tools, actions }); | ||
} | ||
return keyboardShortcutsSchema2; | ||
}, [app, overrides, actions, tools]); | ||
}, [editor, overrides, actions, tools]); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(KeyboardShortcutsSchemaContext.Provider, { value: keyboardShortcutsSchema, children }); | ||
@@ -131,0 +132,0 @@ }); |
@@ -25,34 +25,41 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_react = require("react"); | ||
var import_useEventsProvider = require("./useEventsProvider"); | ||
function useMenuIsOpen(id, cb) { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const rIsOpen = (0, import_react.useRef)(false); | ||
const trackEvent = (0, import_useEventsProvider.useEvents)(); | ||
const onOpenChange = (0, import_react.useCallback)( | ||
(isOpen) => { | ||
rIsOpen.current = isOpen; | ||
if (isOpen) { | ||
app.complete(); | ||
app.openMenus.add(id); | ||
} else { | ||
app.openMenus.delete(id); | ||
app.openMenus.forEach((menuId) => { | ||
if (menuId.startsWith(id)) { | ||
app.openMenus.delete(menuId); | ||
} | ||
}); | ||
} | ||
cb?.(isOpen); | ||
(isOpen2) => { | ||
rIsOpen.current = isOpen2; | ||
editor.batch(() => { | ||
if (isOpen2) { | ||
editor.complete(); | ||
editor.addOpenMenu(id); | ||
} else { | ||
editor.deleteOpenMenu(id); | ||
editor.openMenus.forEach((menuId) => { | ||
if (menuId.startsWith(id)) { | ||
editor.deleteOpenMenu(menuId); | ||
} | ||
}); | ||
} | ||
cb?.(isOpen2); | ||
}); | ||
}, | ||
[app, id, cb] | ||
[editor, id, cb] | ||
); | ||
(0, import_react.useEffect)(() => { | ||
if (rIsOpen.current) { | ||
app.openMenus.add(id); | ||
trackEvent("open-menu", { source: "unknown", id }); | ||
editor.addOpenMenu(id); | ||
} | ||
return () => { | ||
if (rIsOpen.current) { | ||
app.openMenus.delete(id); | ||
app.openMenus.forEach((menuId) => { | ||
editor.deleteOpenMenu(id); | ||
editor.openMenus.forEach((menuId) => { | ||
if (menuId.startsWith(id)) { | ||
app.openMenus.delete(menuId); | ||
trackEvent("close-menu", { source: "unknown", id }); | ||
editor.deleteOpenMenu(menuId); | ||
} | ||
@@ -63,5 +70,6 @@ }); | ||
}; | ||
}, [app, id]); | ||
return onOpenChange; | ||
}, [editor, id, trackEvent]); | ||
const isOpen = (0, import_state.useValue)("is menu open", () => editor.openMenus.includes(id), [editor, id]); | ||
return [isOpen, onOpenChange]; | ||
} | ||
//# sourceMappingURL=useMenuIsOpen.js.map |
@@ -31,4 +31,4 @@ "use strict"; | ||
__export(useMenuSchema_exports, { | ||
MenuSchemaContext: () => MenuSchemaContext, | ||
MenuSchemaProvider: () => MenuSchemaProvider, | ||
TLUiMenuSchemaContext: () => TLUiMenuSchemaContext, | ||
TLUiMenuSchemaProvider: () => TLUiMenuSchemaProvider, | ||
useMenuSchema: () => useMenuSchema | ||
@@ -39,5 +39,5 @@ }); | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_utils = require("@tldraw/utils"); | ||
var import_react = __toESM(require("react")); | ||
var import_signia_react = require("signia-react"); | ||
var import_menuHelpers = require("./menuHelpers"); | ||
@@ -50,19 +50,22 @@ var import_useActions = require("./useActions"); | ||
var import_useShowAutoSizeToggle = require("./useShowAutoSizeToggle"); | ||
const MenuSchemaContext = import_react.default.createContext({}); | ||
function MenuSchemaProvider({ overrides, children }) { | ||
const app = (0, import_editor.useApp)(); | ||
const TLUiMenuSchemaContext = import_react.default.createContext({}); | ||
function TLUiMenuSchemaProvider({ overrides, children }) { | ||
const editor = (0, import_editor.useEditor)(); | ||
const actions = (0, import_useActions.useActions)(); | ||
const breakpoint = (0, import_useBreakpoint.useBreakpoint)(); | ||
const isMobile = breakpoint < 5; | ||
const isDarkMode = (0, import_signia_react.useValue)("isDarkMode", () => app.userDocumentSettings.isDarkMode, [app]); | ||
const isGridMode = (0, import_signia_react.useValue)("isGridMode", () => app.userDocumentSettings.isGridMode, [app]); | ||
const isSnapMode = (0, import_signia_react.useValue)("isSnapMode", () => app.userDocumentSettings.isSnapMode, [app]); | ||
const isToolLock = (0, import_signia_react.useValue)("isToolLock", () => app.instanceState.isToolLocked, [app]); | ||
const isFocusMode = (0, import_signia_react.useValue)("isFocusMode", () => app.instanceState.isFocusMode, [app]); | ||
const isDebugMode = (0, import_signia_react.useValue)("isDebugMode", () => app.instanceState.isDebugMode, [app]); | ||
const exportBackground = (0, import_signia_react.useValue)("exportBackground", () => app.instanceState.exportBackground, [ | ||
app | ||
]); | ||
const emptyPage = (0, import_signia_react.useValue)("emptyPage", () => app.shapeIds.size === 0, [app]); | ||
const selectedCount = (0, import_signia_react.useValue)("selectedCount", () => app.selectedIds.length, [app]); | ||
const isDarkMode = (0, import_state.useValue)("isDarkMode", () => editor.isDarkMode, [editor]); | ||
const animationSpeed = (0, import_state.useValue)("animationSpeed", () => editor.animationSpeed, [editor]); | ||
const isGridMode = (0, import_state.useValue)("isGridMode", () => editor.isGridMode, [editor]); | ||
const isSnapMode = (0, import_state.useValue)("isSnapMode", () => editor.isSnapMode, [editor]); | ||
const isToolLock = (0, import_state.useValue)("isToolLock", () => editor.instanceState.isToolLocked, [editor]); | ||
const isFocusMode = (0, import_state.useValue)("isFocusMode", () => editor.instanceState.isFocusMode, [editor]); | ||
const isDebugMode = (0, import_state.useValue)("isDebugMode", () => editor.instanceState.isDebugMode, [editor]); | ||
const exportBackground = (0, import_state.useValue)( | ||
"exportBackground", | ||
() => editor.instanceState.exportBackground, | ||
[editor] | ||
); | ||
const emptyPage = (0, import_state.useValue)("emptyPage", () => editor.currentPageShapeIds.size === 0, [editor]); | ||
const selectedCount = (0, import_state.useValue)("selectedCount", () => editor.selectedIds.length, [editor]); | ||
const noneSelected = selectedCount === 0; | ||
@@ -79,3 +82,3 @@ const oneSelected = selectedCount > 0; | ||
const canRedo = (0, import_useCanRedo.useCanRedo)(); | ||
const isZoomedTo100 = (0, import_signia_react.useValue)("isZoomedTo100", () => app.zoomLevel === 1, [app]); | ||
const isZoomedTo100 = (0, import_state.useValue)("isZoomedTo100", () => editor.zoomLevel === 1, [editor]); | ||
const menuSchema = (0, import_react.useMemo)(() => { | ||
@@ -102,8 +105,3 @@ const menuSchema2 = (0, import_utils.compact)([ | ||
(0, import_menuHelpers.menuItem)(actions["copy"], { disabled: noneSelected }), | ||
{ | ||
id: "MENU_PASTE", | ||
type: "custom", | ||
disabled: !import_menuHelpers.showUiPaste, | ||
readonlyOk: false | ||
} | ||
(0, import_menuHelpers.menuItem)(actions["paste"], { disabled: !import_menuHelpers.showMenuPaste }) | ||
), | ||
@@ -167,5 +165,5 @@ (0, import_menuHelpers.menuGroup)( | ||
) | ||
), | ||
(0, import_menuHelpers.menuItem)(actions["insert-media"]) | ||
) | ||
), | ||
(0, import_menuHelpers.menuGroup)("extras", (0, import_menuHelpers.menuItem)(actions["insert-embed"]), (0, import_menuHelpers.menuItem)(actions["insert-media"])), | ||
(0, import_menuHelpers.menuGroup)( | ||
@@ -183,2 +181,3 @@ "preferences", | ||
(0, import_menuHelpers.menuItem)(actions["toggle-focus-mode"], { checked: isFocusMode }), | ||
(0, import_menuHelpers.menuItem)(actions["toggle-reduce-motion"], { checked: animationSpeed === 0 }), | ||
(0, import_menuHelpers.menuItem)(actions["toggle-debug-mode"], { checked: isDebugMode }) | ||
@@ -191,3 +190,3 @@ ) | ||
if (overrides) { | ||
return overrides(app, menuSchema2, { | ||
return overrides(editor, menuSchema2, { | ||
actions, | ||
@@ -202,3 +201,3 @@ noneSelected, | ||
}, [ | ||
app, | ||
editor, | ||
overrides, | ||
@@ -219,2 +218,3 @@ actions, | ||
canRedo, | ||
animationSpeed, | ||
isDarkMode, | ||
@@ -229,8 +229,8 @@ isGridMode, | ||
]); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MenuSchemaContext.Provider, { value: menuSchema, children }); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TLUiMenuSchemaContext.Provider, { value: menuSchema, children }); | ||
} | ||
function useMenuSchema() { | ||
const ctx = import_react.default.useContext(MenuSchemaContext); | ||
const ctx = import_react.default.useContext(TLUiMenuSchemaContext); | ||
if (!ctx) { | ||
throw new Error("useMenuSchema must be used inside of a MenuSchemaProvider."); | ||
throw new Error("useMenuSchema must be used inside of a TLUiMenuSchemaProvider."); | ||
} | ||
@@ -237,0 +237,0 @@ return ctx; |
@@ -25,14 +25,16 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_signia_react = require("signia-react"); | ||
var import_state = require("@tldraw/state"); | ||
function useOnlyFlippableShape() { | ||
const app = (0, import_editor.useApp)(); | ||
return (0, import_signia_react.useValue)( | ||
const editor = (0, import_editor.useEditor)(); | ||
return (0, import_state.useValue)( | ||
"onlyFlippableShape", | ||
() => { | ||
const { selectedShapes } = app; | ||
return selectedShapes.length === 1 && selectedShapes.every((shape) => shape.type === "group" || (0, import_editor.isShapeWithHandles)(shape)); | ||
const { selectedShapes } = editor; | ||
return selectedShapes.length === 1 && selectedShapes.every( | ||
(shape) => editor.isShapeOfType(shape, import_editor.GroupShapeUtil) || editor.isShapeOfType(shape, import_editor.ArrowShapeUtil) || editor.isShapeOfType(shape, import_editor.LineShapeUtil) || editor.isShapeOfType(shape, import_editor.DrawShapeUtil) | ||
); | ||
}, | ||
[app] | ||
[editor] | ||
); | ||
} | ||
//# sourceMappingURL=useOnlyFlippableShape.js.map |
@@ -34,3 +34,3 @@ "use strict"; | ||
await Promise.allSettled( | ||
import_icon_types.TLUiIconTypes.map((icon) => { | ||
import_icon_types.iconTypes.map((icon) => { | ||
const image = new Image(); | ||
@@ -37,0 +37,0 @@ image.src = assetUrls.icons[icon]; |
@@ -27,3 +27,3 @@ "use strict"; | ||
function usePrint() { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const prevPrintEl = (0, import_react.useRef)(null); | ||
@@ -47,3 +47,3 @@ const prevStyleEl = (0, import_react.useRef)(null); | ||
prevStyleEl.current = style; | ||
const className = `rs-print-surface-${(0, import_editor.uniqueId)()}`; | ||
const className = `tl-print-surface-${(0, import_editor.uniqueId)()}`; | ||
el.className = className; | ||
@@ -142,3 +142,3 @@ const enableMargins = false; | ||
const afterPrintHandler = () => { | ||
app.once("change-camera", () => { | ||
editor.once("change-history", () => { | ||
clearElements(el, style); | ||
@@ -167,6 +167,6 @@ }); | ||
function triggerPrint() { | ||
if (app.isChromeForIos) { | ||
if (editor.isChromeForIos) { | ||
beforePrintHandler(); | ||
window.print(); | ||
} else if (app.isSafari) { | ||
} else if (editor.isSafari) { | ||
beforePrintHandler(); | ||
@@ -178,3 +178,3 @@ document.execCommand("print", false); | ||
} | ||
const { pages, currentPageId, selectedIds } = app; | ||
const { pages, currentPageId, selectedIds } = editor; | ||
const preserveAspectRatio = "xMidYMid meet"; | ||
@@ -187,4 +187,4 @@ const svgOpts = { | ||
}; | ||
if (app.selectedIds.length > 0) { | ||
const svg = await app.getSvg(selectedIds, svgOpts); | ||
if (editor.selectedIds.length > 0) { | ||
const svg = await editor.getSvg(selectedIds, svgOpts); | ||
if (svg) { | ||
@@ -199,3 +199,3 @@ const page = pages.find((p) => p.id === currentPageId); | ||
const page = pages[i]; | ||
const svg = await app.getSvg(app.getSortedChildIds(page.id), svgOpts); | ||
const svg = await editor.getSvg(editor.getSortedChildIds(page.id), svgOpts); | ||
if (svg) { | ||
@@ -207,4 +207,4 @@ addPageToPrint(`tldraw \u2014 ${page.name}`, `${i}/${pages.length}`, svg); | ||
} else { | ||
const page = app.currentPage; | ||
const svg = await app.getSvg(app.getSortedChildIds(page.id), svgOpts); | ||
const page = editor.currentPage; | ||
const svg = await editor.getSvg(editor.getSortedChildIds(page.id), svgOpts); | ||
if (svg) { | ||
@@ -219,5 +219,5 @@ addPageToPrint(`tldraw \u2014 ${page.name}`, null, svg); | ||
}, | ||
[app] | ||
[editor] | ||
); | ||
} | ||
//# sourceMappingURL=usePrint.js.map |
@@ -25,7 +25,7 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_signia_react = require("signia-react"); | ||
var import_state = require("@tldraw/state"); | ||
function useReadonly() { | ||
const app = (0, import_editor.useApp)(); | ||
return (0, import_signia_react.useValue)("isReadOnlyMode", () => app.isReadOnly, [app]); | ||
const editor = (0, import_editor.useEditor)(); | ||
return (0, import_state.useValue)("isReadOnlyMode", () => editor.isReadOnly, [editor]); | ||
} | ||
//# sourceMappingURL=useReadonly.js.map |
@@ -25,14 +25,14 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_signia_react = require("signia-react"); | ||
var import_state = require("@tldraw/state"); | ||
function useShowAutoSizeToggle() { | ||
const app = (0, import_editor.useApp)(); | ||
return (0, import_signia_react.useValue)( | ||
const editor = (0, import_editor.useEditor)(); | ||
return (0, import_state.useValue)( | ||
"showAutoSizeToggle", | ||
() => { | ||
const { selectedShapes } = app; | ||
return selectedShapes.length === 1 && selectedShapes[0].type === "text" && selectedShapes[0].props.autoSize === false; | ||
const { selectedShapes } = editor; | ||
return selectedShapes.length === 1 && editor.isShapeOfType(selectedShapes[0], import_editor.TextShapeUtil) && selectedShapes[0].props.autoSize === false; | ||
}, | ||
[app] | ||
[editor] | ||
); | ||
} | ||
//# sourceMappingURL=useShowAutoSizeToggle.js.map |
@@ -39,2 +39,4 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_utils = require("@tldraw/utils"); | ||
var import_react = __toESM(require("react")); | ||
@@ -52,6 +54,7 @@ var import_useTools = require("./useTools"); | ||
function ToolbarSchemaProvider({ overrides, children }) { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const tools = (0, import_useTools.useTools)(); | ||
const highlighterEnabled = (0, import_state.useValue)(import_editor.featureFlags.highlighterTool); | ||
const toolbarSchema = import_react.default.useMemo(() => { | ||
const schema = [ | ||
const schema = (0, import_utils.compact)([ | ||
toolbarItem(tools.select), | ||
@@ -73,19 +76,21 @@ toolbarItem(tools.hand), | ||
toolbarItem(tools["hexagon"]), | ||
toolbarItem(tools["octagon"]), | ||
// toolbarItem(tools['octagon']), | ||
toolbarItem(tools["star"]), | ||
toolbarItem(tools["oval"]), | ||
toolbarItem(tools["x-box"]), | ||
toolbarItem(tools["arrow-right"]), | ||
toolbarItem(tools["check-box"]), | ||
toolbarItem(tools["arrow-left"]), | ||
toolbarItem(tools["arrow-up"]), | ||
toolbarItem(tools["arrow-down"]), | ||
toolbarItem(tools["arrow-right"]), | ||
toolbarItem(tools.frame), | ||
toolbarItem(tools.line), | ||
toolbarItem(tools.frame), | ||
toolbarItem(tools.embed) | ||
]; | ||
highlighterEnabled ? toolbarItem(tools.highlight) : null, | ||
toolbarItem(tools.laser) | ||
]); | ||
if (overrides) { | ||
return overrides(app, schema, { tools }); | ||
return overrides(editor, schema, { tools }); | ||
} | ||
return schema; | ||
}, [app, overrides, tools]); | ||
}, [editor, highlighterEnabled, overrides, tools]); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ToolbarSchemaContext.Provider, { value: toolbarSchema, children }); | ||
@@ -92,0 +97,0 @@ } |
@@ -38,13 +38,17 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var React = __toESM(require("react")); | ||
var import_EmbedDialog = require("../components/EmbedDialog"); | ||
var import_useDialogsProvider = require("./useDialogsProvider"); | ||
var import_useEventsProvider = require("./useEventsProvider"); | ||
var import_useInsertMedia = require("./useInsertMedia"); | ||
const ToolsContext = React.createContext({}); | ||
function ToolsProvider({ overrides, children }) { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const trackEvent = (0, import_useEventsProvider.useEvents)(); | ||
const { addDialog } = (0, import_useDialogsProvider.useDialogs)(); | ||
const insertMedia = (0, import_useInsertMedia.useInsertMedia)(); | ||
const highlighterEnabled = (0, import_state.useValue)(import_editor.featureFlags.highlighterTool); | ||
const tools = React.useMemo(() => { | ||
const tools2 = makeTools([ | ||
const toolsArray = [ | ||
{ | ||
@@ -56,4 +60,5 @@ id: "select", | ||
readonlyOk: true, | ||
onSelect() { | ||
app.setSelectedTool("select"); | ||
onSelect(source) { | ||
editor.setSelectedTool("select"); | ||
trackEvent("select-tool", { source, id: "select" }); | ||
} | ||
@@ -67,4 +72,5 @@ }, | ||
readonlyOk: true, | ||
onSelect() { | ||
app.setSelectedTool("hand"); | ||
onSelect(source) { | ||
editor.setSelectedTool("hand"); | ||
trackEvent("select-tool", { source, id: "hand" }); | ||
} | ||
@@ -77,5 +83,6 @@ }, | ||
kbd: "e", | ||
readonlyOk: true, | ||
onSelect() { | ||
app.setSelectedTool("eraser"); | ||
readonlyOk: false, | ||
onSelect(source) { | ||
editor.setSelectedTool("eraser"); | ||
trackEvent("select-tool", { source, id: "eraser" }); | ||
} | ||
@@ -86,13 +93,14 @@ }, | ||
label: "tool.draw", | ||
readonlyOk: true, | ||
readonlyOk: false, | ||
icon: "tool-pencil", | ||
kbd: "d,b,x", | ||
onSelect() { | ||
app.setSelectedTool("draw"); | ||
onSelect(source) { | ||
editor.setSelectedTool("draw"); | ||
trackEvent("select-tool", { source, id: "draw" }); | ||
} | ||
}, | ||
...[...import_editor.TL_GEO_TYPES].map((id) => ({ | ||
...[...import_editor.GeoShapeGeoStyle.values].map((id) => ({ | ||
id, | ||
label: `tool.${id}`, | ||
readonlyOk: true, | ||
readonlyOk: false, | ||
meta: { | ||
@@ -103,9 +111,15 @@ geo: id | ||
icon: "geo-" + id, | ||
onSelect() { | ||
app.batch(() => { | ||
app.updateInstanceState( | ||
{ propsForNextShape: { ...app.instanceState.propsForNextShape, geo: id } }, | ||
onSelect(source) { | ||
editor.batch(() => { | ||
editor.updateInstanceState( | ||
{ | ||
stylesForNextShape: { | ||
...editor.instanceState.stylesForNextShape, | ||
[import_editor.GeoShapeGeoStyle.id]: id | ||
} | ||
}, | ||
true | ||
); | ||
app.setSelectedTool("geo"); | ||
editor.setSelectedTool("geo"); | ||
trackEvent("select-tool", { source, id: `geo-${id}` }); | ||
}); | ||
@@ -117,7 +131,8 @@ } | ||
label: "tool.arrow", | ||
readonlyOk: true, | ||
readonlyOk: false, | ||
icon: "tool-arrow", | ||
kbd: "a", | ||
onSelect() { | ||
app.setSelectedTool("arrow"); | ||
onSelect(source) { | ||
editor.setSelectedTool("arrow"); | ||
trackEvent("select-tool", { source, id: "arrow" }); | ||
} | ||
@@ -128,7 +143,8 @@ }, | ||
label: "tool.line", | ||
readonlyOk: true, | ||
readonlyOk: false, | ||
icon: "tool-line", | ||
kbd: "l", | ||
onSelect() { | ||
app.setSelectedTool("line"); | ||
onSelect(source) { | ||
editor.setSelectedTool("line"); | ||
trackEvent("select-tool", { source, id: "line" }); | ||
} | ||
@@ -139,7 +155,8 @@ }, | ||
label: "tool.frame", | ||
readonlyOk: true, | ||
readonlyOk: false, | ||
icon: "tool-frame", | ||
kbd: "f", | ||
onSelect() { | ||
app.setSelectedTool("frame"); | ||
onSelect(source) { | ||
editor.setSelectedTool("frame"); | ||
trackEvent("select-tool", { source, id: "frame" }); | ||
} | ||
@@ -150,7 +167,8 @@ }, | ||
label: "tool.text", | ||
readonlyOk: true, | ||
readonlyOk: false, | ||
icon: "tool-text", | ||
kbd: "t", | ||
onSelect() { | ||
app.setSelectedTool("text"); | ||
onSelect(source) { | ||
editor.setSelectedTool("text"); | ||
trackEvent("select-tool", { source, id: "text" }); | ||
} | ||
@@ -161,7 +179,8 @@ }, | ||
label: "tool.asset", | ||
readonlyOk: true, | ||
readonlyOk: false, | ||
icon: "tool-media", | ||
kbd: "$u", | ||
onSelect() { | ||
onSelect(source) { | ||
insertMedia(); | ||
trackEvent("select-tool", { source, id: "media" }); | ||
} | ||
@@ -172,29 +191,54 @@ }, | ||
label: "tool.note", | ||
readonlyOk: true, | ||
readonlyOk: false, | ||
icon: "tool-note", | ||
kbd: "n", | ||
onSelect() { | ||
app.setSelectedTool("note"); | ||
onSelect(source) { | ||
editor.setSelectedTool("note"); | ||
trackEvent("select-tool", { source, id: "note" }); | ||
} | ||
}, | ||
{ | ||
id: "laser", | ||
label: "tool.laser", | ||
readonlyOk: true, | ||
icon: "tool-laser", | ||
kbd: "k", | ||
onSelect(source) { | ||
editor.setSelectedTool("laser"); | ||
trackEvent("select-tool", { source, id: "laser" }); | ||
} | ||
}, | ||
{ | ||
id: "embed", | ||
label: "tool.embed", | ||
readonlyOk: true, | ||
readonlyOk: false, | ||
icon: "tool-embed", | ||
onSelect() { | ||
onSelect(source) { | ||
addDialog({ component: import_EmbedDialog.EmbedDialog }); | ||
trackEvent("select-tool", { source, id: "embed" }); | ||
} | ||
} | ||
]); | ||
]; | ||
if (highlighterEnabled) { | ||
toolsArray.push({ | ||
id: "highlight", | ||
label: "tool.highlight", | ||
readonlyOk: true, | ||
icon: "tool-highlight", | ||
// TODO: pick a better shortcut | ||
kbd: "!d", | ||
onSelect(source) { | ||
editor.setSelectedTool("highlight"); | ||
trackEvent("select-tool", { source, id: "highlight" }); | ||
} | ||
}); | ||
} | ||
const tools2 = Object.fromEntries(toolsArray.map((t) => [t.id, t])); | ||
if (overrides) { | ||
return overrides(app, tools2, { insertMedia }); | ||
return overrides(editor, tools2, { insertMedia }); | ||
} | ||
return tools2; | ||
}, [app, overrides, insertMedia, addDialog]); | ||
}, [highlighterEnabled, overrides, editor, trackEvent, insertMedia, addDialog]); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ToolsContext.Provider, { value: tools, children }); | ||
} | ||
function makeTools(tools) { | ||
return Object.fromEntries(tools.map((t) => [t.id, t])); | ||
} | ||
function useTools() { | ||
@@ -201,0 +245,0 @@ const ctx = React.useContext(ToolsContext); |
@@ -65,6 +65,10 @@ "use strict"; | ||
"action.flip-vertical.short": "Flip V", | ||
"action.fork-project": "Fork this project", | ||
"action.group": "Group", | ||
"action.insert-embed": "Insert embed", | ||
"action.insert-media": "Upload media", | ||
"action.leave-shared-project": "Leave shared project", | ||
"action.new-project": "New project", | ||
"action.new-shared-project": "New shared project", | ||
"action.open-cursor-chat": "Cursor chat", | ||
"action.open-file": "Open file", | ||
@@ -95,2 +99,4 @@ "action.pack": "Pack", | ||
"action.toggle-dark-mode": "Toggle dark mode", | ||
"action.toggle-reduce-motion.menu": "Reduce motion", | ||
"action.toggle-reduce-motion": "Toggle reduce motion", | ||
"action.toggle-debug-mode.menu": "Debug mode", | ||
@@ -102,2 +108,3 @@ "action.toggle-debug-mode": "Toggle debug mode", | ||
"action.toggle-grid": "Toggle grid", | ||
"action.toggle-lock": "Toggle locked", | ||
"action.toggle-snap-mode.menu": "Always snap", | ||
@@ -170,3 +177,4 @@ "action.toggle-snap-mode": "Toggle always snap", | ||
"geo-style.triangle": "Triangle", | ||
"geo-style.x-box": "X Box", | ||
"geo-style.x-box": "X box", | ||
"geo-style.check-box": "Check box", | ||
"arrowheadStart-style.none": "None", | ||
@@ -204,2 +212,3 @@ "arrowheadStart-style.arrow": "Arrow", | ||
"tool.hexagon": "Hexagon", | ||
"tool.highlight": "Highlight", | ||
"tool.line": "Line", | ||
@@ -215,5 +224,7 @@ "tool.octagon": "Octagon", | ||
"tool.x-box": "X box", | ||
"tool.check-box": "Check box", | ||
"tool.asset": "Asset", | ||
"tool.frame": "Frame", | ||
"tool.note": "Note", | ||
"tool.laser": "Laser", | ||
"tool.embed": "Embed", | ||
@@ -248,14 +259,22 @@ "tool.text": "Text", | ||
"share-menu.title": "Share", | ||
"share-menu.save-note": "Download this project to your computer as a .tldr file.", | ||
"share-menu.fork-note": "Create a new shared project based on this snapshot.", | ||
"share-menu.share-project": "Share this project", | ||
"share-menu.copy-link": "Copy link", | ||
"share-menu.default-project-name": "Shared Project", | ||
"share-menu.copy-link": "Copy share link", | ||
"share-menu.readonly-link": "Read-only", | ||
"share-menu.create-snapshot-link": "Copy snapshot link", | ||
"share-menu.snapshot-link-note": "Capture and share this project as a read-only snapshot link.", | ||
"share-menu.copy-readonly-link": "Copy read-only link", | ||
"share-menu.offline-note": "Sharing this project will create a hosted live copy at a new URL. You can share the URL with up to thirty other people to view and edit the project together.", | ||
"share-menu.offline-note": "Create a new shared project based on your current project.", | ||
"share-menu.copy-link-note": "Anyone with the link will be able to view and edit this project.", | ||
"share-menu.copy-readonly-link-note": "Anyone with the link will be able to view (but not edit) this project.", | ||
"share-menu.project-too-large": "Sorry, this project can't be shared because it's too large. We're working on it!", | ||
"share-menu.upload-failed": "Sorry, we couldn't upload your project at the moment. Please try again or let us know if the problem persists.", | ||
"people-menu.title": "People", | ||
"people-menu.change-name": "Change name", | ||
"people-menu.change-color": "Change color", | ||
"people-menu.follow": "Follow", | ||
"people-menu.follow": "Following", | ||
"people-menu.following": "Following", | ||
"people-menu.leading": "Following You", | ||
"people-menu.user": "(You)", | ||
@@ -277,4 +296,3 @@ "people-menu.invite": "Invite others", | ||
"edit-link-dialog.cancel": "Cancel", | ||
"embed-dialog.title": "Create embed", | ||
"embed-dialog.url-title": "Create %s embed", | ||
"embed-dialog.title": "Insert embed", | ||
"embed-dialog.back": "Back", | ||
@@ -295,4 +313,12 @@ "embed-dialog.create": "Create", | ||
"shortcuts-dialog.view": "View", | ||
"shortcuts-dialog.collaboration": "Collaboration", | ||
"home-project-dialog.title": "Home project", | ||
"home-project-dialog.description": "This is your local home project. It's just for you!", | ||
"rename-project-dialog.title": "Rename project", | ||
"rename-project-dialog.cancel": "Cancel", | ||
"rename-project-dialog.rename": "Rename", | ||
"home-project-dialog.ok": "Ok", | ||
"style-panel.title": "Styles", | ||
"style-panel.align": "Align", | ||
"style-panel.vertical-align": "Vertical align", | ||
"style-panel.position": "Position", | ||
@@ -335,2 +361,7 @@ "style-panel.arrowheads": "Arrowheads", | ||
"file-system.shared-document-file-open-error.description": "Opening files from shared projects is not supported.", | ||
"sharing.confirm-leave.title": "Leave current project?", | ||
"sharing.confirm-leave.description": "Are you sure you want to leave this shared project? You can return to it by navigating to its URL.", | ||
"sharing.confirm-leave.cancel": "Cancel", | ||
"sharing.confirm-leave.leave": "Leave", | ||
"sharing.confirm-leave.dont-show-again": "Don't ask again", | ||
"toast.error.export-fail.title": "Failed export", | ||
@@ -346,4 +377,5 @@ "toast.error.export-fail.desc": "Failed to export image", | ||
"vscode.file-open.backup-failed": "Backup failed: this is not a .tldr file.", | ||
"vscode.file-open.dont-show-again": "Don't ask again" | ||
"vscode.file-open.dont-show-again": "Don't ask again", | ||
"cursor-chat.type-to-chat": "Type to chat..." | ||
}; | ||
//# sourceMappingURL=defaultTranslation.js.map |
@@ -21,9 +21,7 @@ "use strict"; | ||
__export(translations_exports, { | ||
EN_TRANSLATION: () => EN_TRANSLATION, | ||
fetchTranslation: () => fetchTranslation, | ||
getTranslation: () => getTranslation | ||
fetchTranslation: () => fetchTranslation | ||
}); | ||
module.exports = __toCommonJS(translations_exports); | ||
var import_tlschema = require("@tldraw/tlschema"); | ||
var import_defaultTranslation = require("./defaultTranslation"); | ||
var import_languages = require("./languages"); | ||
const EN_TRANSLATION = { | ||
@@ -43,3 +41,3 @@ locale: "en", | ||
} | ||
const language = import_languages.LANGUAGES.find((t) => t.locale === locale); | ||
const language = import_tlschema.LANGUAGES.find((t) => t.locale === locale); | ||
if (!language) { | ||
@@ -71,5 +69,2 @@ console.warn(`No translation found for locale ${locale}`); | ||
} | ||
async function getTranslation(locale, assetUrls) { | ||
return await fetchTranslation(locale, assetUrls); | ||
} | ||
//# sourceMappingURL=translations.js.map |
@@ -25,7 +25,9 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_languages = require("./languages"); | ||
function useLanguages() { | ||
const app = (0, import_editor.useApp)(); | ||
return { languages: import_languages.LANGUAGES, currentLanguage: app.user.locale }; | ||
const editor = (0, import_editor.useEditor)(); | ||
return { | ||
languages: import_editor.LANGUAGES, | ||
currentLanguage: editor.locale | ||
}; | ||
} | ||
//# sourceMappingURL=useLanguages.js.map |
@@ -37,15 +37,17 @@ "use strict"; | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var React = __toESM(require("react")); | ||
var import_signia_react = require("signia-react"); | ||
var import_useAssetUrls = require("../useAssetUrls"); | ||
var import_defaultTranslation = require("./defaultTranslation"); | ||
var import_translations = require("./translations"); | ||
const TranslationsContext = React.createContext({}); | ||
const TranslationsContext = React.createContext( | ||
{} | ||
); | ||
const useCurrentTranslation = () => React.useContext(TranslationsContext); | ||
const TranslationProvider = (0, import_signia_react.track)(function TranslationProvider2({ | ||
const TranslationProvider = (0, import_state.track)(function TranslationProvider2({ | ||
overrides, | ||
children | ||
}) { | ||
const app = (0, import_editor.useApp)(); | ||
const locale = app.userSettings.locale; | ||
const editor = (0, import_editor.useEditor)(); | ||
const locale = editor.locale; | ||
const getAssetUrl = (0, import_useAssetUrls.useAssetUrls)(); | ||
@@ -69,9 +71,8 @@ const [currentTranslation, setCurrentTranslation] = React.useState(() => { | ||
async function loadTranslation() { | ||
const localeString = locale ?? navigator.language.split(/[-_]/)[0]; | ||
const translation = await (0, import_translations.getTranslation)(localeString, getAssetUrl); | ||
const translation = await (0, import_translations.fetchTranslation)(locale, getAssetUrl); | ||
if (translation && !isCancelled) { | ||
if (overrides && overrides[localeString]) { | ||
if (overrides && overrides[locale]) { | ||
setCurrentTranslation({ | ||
...translation, | ||
messages: { ...translation.messages, ...overrides[localeString] } | ||
messages: { ...translation.messages, ...overrides[locale] } | ||
}); | ||
@@ -78,0 +79,0 @@ } else { |
@@ -21,6 +21,6 @@ "use strict"; | ||
__export(icon_types_exports, { | ||
TLUiIconTypes: () => TLUiIconTypes | ||
iconTypes: () => iconTypes | ||
}); | ||
module.exports = __toCommonJS(icon_types_exports); | ||
const TLUiIconTypes = [ | ||
const iconTypes = [ | ||
"align-bottom-center", | ||
@@ -64,2 +64,3 @@ "align-bottom-left", | ||
"chevrons-sw", | ||
"clipboard-copied", | ||
"clipboard-copy", | ||
@@ -91,2 +92,4 @@ "code", | ||
"fill-solid", | ||
"follow", | ||
"following", | ||
"font-draw", | ||
@@ -100,2 +103,3 @@ "font-mono", | ||
"geo-arrow-up", | ||
"geo-check-box", | ||
"geo-diamond", | ||
@@ -119,2 +123,3 @@ "geo-ellipse", | ||
"info-circle", | ||
"leading", | ||
"link", | ||
@@ -163,3 +168,4 @@ "lock-small", | ||
"tool-hand", | ||
"tool-highlighter", | ||
"tool-highlight", | ||
"tool-laser", | ||
"tool-line", | ||
@@ -179,2 +185,5 @@ "tool-media", | ||
"unlock", | ||
"vertical-align-center", | ||
"vertical-align-end", | ||
"vertical-align-start", | ||
"visible", | ||
@@ -181,0 +190,0 @@ "warning-triangle", |
@@ -78,6 +78,6 @@ "use strict"; | ||
return { | ||
actionsMenu: (app, schema, helpers) => { | ||
actionsMenu: (editor, schema, helpers) => { | ||
for (const override of overrides) { | ||
if (override.actionsMenu) { | ||
schema = override.actionsMenu(app, schema, { ...defaultHelpers, ...helpers }); | ||
schema = override.actionsMenu(editor, schema, { ...defaultHelpers, ...helpers }); | ||
} | ||
@@ -87,6 +87,6 @@ } | ||
}, | ||
actions: (app, schema) => { | ||
actions: (editor, schema) => { | ||
for (const override of overrides) { | ||
if (override.actions) { | ||
schema = override.actions(app, schema, defaultHelpers); | ||
schema = override.actions(editor, schema, defaultHelpers); | ||
} | ||
@@ -96,6 +96,6 @@ } | ||
}, | ||
contextMenu: (app, schema, helpers) => { | ||
contextMenu: (editor, schema, helpers) => { | ||
for (const override of overrides) { | ||
if (override.contextMenu) { | ||
schema = override.contextMenu(app, schema, { ...defaultHelpers, ...helpers }); | ||
schema = override.contextMenu(editor, schema, { ...defaultHelpers, ...helpers }); | ||
} | ||
@@ -105,6 +105,6 @@ } | ||
}, | ||
helpMenu: (app, schema, helpers) => { | ||
helpMenu: (editor, schema, helpers) => { | ||
for (const override of overrides) { | ||
if (override.helpMenu) { | ||
schema = override.helpMenu(app, schema, { ...defaultHelpers, ...helpers }); | ||
schema = override.helpMenu(editor, schema, { ...defaultHelpers, ...helpers }); | ||
} | ||
@@ -114,6 +114,6 @@ } | ||
}, | ||
menu: (app, schema, helpers) => { | ||
menu: (editor, schema, helpers) => { | ||
for (const override of overrides) { | ||
if (override.menu) { | ||
schema = override.menu(app, schema, { ...defaultHelpers, ...helpers }); | ||
schema = override.menu(editor, schema, { ...defaultHelpers, ...helpers }); | ||
} | ||
@@ -123,6 +123,6 @@ } | ||
}, | ||
toolbar: (app, schema, helpers) => { | ||
toolbar: (editor, schema, helpers) => { | ||
for (const override of overrides) { | ||
if (override.toolbar) { | ||
schema = override.toolbar(app, schema, { ...defaultHelpers, ...helpers }); | ||
schema = override.toolbar(editor, schema, { ...defaultHelpers, ...helpers }); | ||
} | ||
@@ -132,6 +132,6 @@ } | ||
}, | ||
keyboardShortcutsMenu: (app, schema, helpers) => { | ||
keyboardShortcutsMenu: (editor, schema, helpers) => { | ||
for (const override of overrides) { | ||
if (override.keyboardShortcutsMenu) { | ||
schema = override.keyboardShortcutsMenu(app, schema, { ...defaultHelpers, ...helpers }); | ||
schema = override.keyboardShortcutsMenu(editor, schema, { ...defaultHelpers, ...helpers }); | ||
} | ||
@@ -141,6 +141,6 @@ } | ||
}, | ||
tools: (app, schema, helpers) => { | ||
tools: (editor, schema, helpers) => { | ||
for (const override of overrides) { | ||
if (override.tools) { | ||
schema = override.tools(app, schema, { ...defaultHelpers, ...helpers }); | ||
schema = override.tools(editor, schema, { ...defaultHelpers, ...helpers }); | ||
} | ||
@@ -147,0 +147,0 @@ } |
@@ -31,4 +31,3 @@ "use strict"; | ||
__export(TldrawUi_exports, { | ||
TldrawUi: () => TldrawUi, | ||
TldrawUiContent: () => TldrawUiContent | ||
TldrawUi: () => TldrawUi | ||
}); | ||
@@ -39,5 +38,5 @@ module.exports = __toCommonJS(TldrawUi_exports); | ||
var import_editor = require("@tldraw/editor"); | ||
var import_state = require("@tldraw/state"); | ||
var import_classnames = __toESM(require("classnames")); | ||
var import_react = __toESM(require("react")); | ||
var import_signia_react = require("signia-react"); | ||
var import_TldrawUiContextProvider = require("./TldrawUiContextProvider"); | ||
@@ -47,2 +46,3 @@ var import_BackToContent = require("./components/BackToContent"); | ||
var import_Dialogs = require("./components/Dialogs"); | ||
var import_FollowingIndicator = require("./components/FollowingIndicator"); | ||
var import_HelpMenu = require("./components/HelpMenu"); | ||
@@ -58,10 +58,10 @@ var import_MenuZone = require("./components/MenuZone"); | ||
var import_useActions = require("./hooks/useActions"); | ||
var import_useAppEvents = require("./hooks/useAppEvents"); | ||
var import_useBreakpoint = require("./hooks/useBreakpoint"); | ||
var import_useClipboardEvents = require("./hooks/useClipboardEvents"); | ||
var import_useEditorEvents = require("./hooks/useEditorEvents"); | ||
var import_useKeyboardShortcuts = require("./hooks/useKeyboardShortcuts"); | ||
var import_usePreloadIcons = require("./hooks/usePreloadIcons"); | ||
var import_useTranslation = require("./hooks/useTranslation/useTranslation"); | ||
const TldrawUi = import_react.default.memo(function TldrawUi2({ | ||
shareZone, | ||
topZone, | ||
renderDebugMenuItems, | ||
@@ -77,2 +77,3 @@ children, | ||
shareZone, | ||
topZone, | ||
renderDebugMenuItems, | ||
@@ -88,6 +89,2 @@ children | ||
}) { | ||
const isLoaded = (0, import_usePreloadIcons.usePreloadIcons)(); | ||
if (!isLoaded) { | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_editor.LoadingScreen, { children: "Loading assets..." }); | ||
} | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ | ||
@@ -100,42 +97,34 @@ children, | ||
shareZone, | ||
topZone, | ||
renderDebugMenuItems | ||
}) { | ||
const app = (0, import_editor.useApp)(); | ||
const editor = (0, import_editor.useEditor)(); | ||
const msg = (0, import_useTranslation.useTranslation)(); | ||
const breakpoint = (0, import_useBreakpoint.useBreakpoint)(); | ||
const isReadonlyMode = (0, import_signia_react.useValue)("isReadOnlyMode", () => app.isReadOnly, []); | ||
const isFocusMode = (0, import_signia_react.useValue)("isFocusMode", () => app.instanceState.isFocusMode, []); | ||
const isDebugMode = (0, import_signia_react.useValue)("isDebugMode", () => app.instanceState.isDebugMode, []); | ||
const isReadonlyMode = (0, import_state.useValue)("isReadOnlyMode", () => editor.isReadOnly, [editor]); | ||
const isFocusMode = (0, import_state.useValue)("focus", () => editor.instanceState.isFocusMode, [editor]); | ||
const isDebugMode = (0, import_state.useValue)("debug", () => editor.instanceState.isDebugMode, [editor]); | ||
(0, import_useKeyboardShortcuts.useKeyboardShortcuts)(); | ||
(0, import_useClipboardEvents.useNativeClipboardEvents)(); | ||
(0, import_useAppEvents.useAppEvents)(); | ||
(0, import_useEditorEvents.useEditorEvents)(); | ||
const { "toggle-focus-mode": toggleFocus } = (0, import_useActions.useActions)(); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_toast.ToastProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_toast.ToastProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( | ||
"main", | ||
{ | ||
"data-wd": "desktop-ui", | ||
className: (0, import_classnames.default)("tlui-layout", { | ||
"tlui-layout__mobile": breakpoint < 5 | ||
}), | ||
children: isFocusMode ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-layout__top", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
import_Button.Button, | ||
{ | ||
className: "tlui-focus-button", | ||
title: `${msg("focus-mode.toggle-focus-mode")}`, | ||
icon: "dot", | ||
onClick: toggleFocus.onSelect | ||
} | ||
) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-layout__top", children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_MenuZone.MenuZone, {}), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-spacer" }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-share-zone", draggable: false, children: shareZone }) | ||
] }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)( | ||
"div", | ||
children: [ | ||
isFocusMode ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-layout__top", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
import_Button.Button, | ||
{ | ||
className: "tlui-layout__middle", | ||
"data-wd": "active-area", | ||
"data-tldraw-area": "active-drawing", | ||
children: [ | ||
className: "tlui-focus-button", | ||
title: `${msg("focus-mode.toggle-focus-mode")}`, | ||
icon: "dot", | ||
onClick: () => toggleFocus.onSelect("menu") | ||
} | ||
) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-layout__top", children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-layout__top__left", children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_MenuZone.MenuZone, {}), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-helper-buttons", children: [ | ||
@@ -145,19 +134,24 @@ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_PenModeToggle.ExitPenMode, {}), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_StopFollowing.StopFollowing, {}) | ||
] }), | ||
] }) | ||
] }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-layout__top__center", children: topZone }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-layout__top__right", children: [ | ||
shareZone, | ||
breakpoint >= 5 && !isReadonlyMode && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tlui-style-panel__wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_StylePanel.StylePanel, {}) }) | ||
] | ||
} | ||
), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-layout__bottom", children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-layout__bottom__main", children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_NavigationZone.NavigationZone, {}), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Toolbar.Toolbar, {}), | ||
breakpoint >= 4 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_HelpMenu.HelpMenu, {}) | ||
] }) | ||
] }), | ||
isDebugMode && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_DebugPanel.DebugPanel, { renderDebugMenuItems: renderDebugMenuItems ?? null }) | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-layout__bottom", children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tlui-layout__bottom__main", children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_NavigationZone.NavigationZone, {}), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Toolbar.Toolbar, {}), | ||
breakpoint >= 4 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_HelpMenu.HelpMenu, {}) | ||
] }), | ||
isDebugMode && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_DebugPanel.DebugPanel, { renderDebugMenuItems: renderDebugMenuItems ?? null }) | ||
] }) | ||
] }), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Toasts.Toasts, {}), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dialogs.Dialogs, {}), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Toasts.ToastViewport, {}) | ||
] }) | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Toasts.ToastViewport, {}), | ||
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_FollowingIndicator.FollowingIndicator, {}) | ||
] | ||
} | ||
@@ -164,0 +158,0 @@ ) }); |
@@ -32,2 +32,3 @@ "use strict"; | ||
var import_useDialogsProvider = require("./hooks/useDialogsProvider"); | ||
var import_useEventsProvider = require("./hooks/useEventsProvider"); | ||
var import_useHelpMenuSchema = require("./hooks/useHelpMenuSchema"); | ||
@@ -44,5 +45,6 @@ var import_useKeyboardShortcutsSchema = require("./hooks/useKeyboardShortcutsSchema"); | ||
assetUrls, | ||
onUiEvent, | ||
children | ||
}) { | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useAssetUrls.AssetUrlsProvider, { assetUrls: assetUrls ?? import_assetUrls.defaultUiAssetUrls, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useTranslation.TranslationProvider, { overrides: (0, import_overrides.useMergedTranslationOverrides)(overrides), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useToastsProvider.ToastsProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useDialogsProvider.DialogsProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useBreakpoint.BreakPointProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InternalProviders, { overrides, children }) }) }) }) }) }); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useAssetUrls.AssetUrlsProvider, { assetUrls: (0, import_assetUrls.useDefaultUiAssetUrlsWithOverrides)(assetUrls), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useTranslation.TranslationProvider, { overrides: (0, import_overrides.useMergedTranslationOverrides)(overrides), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useEventsProvider.EventsProvider, { onEvent: onUiEvent, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useToastsProvider.ToastsProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useDialogsProvider.DialogsProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useBreakpoint.BreakPointProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InternalProviders, { overrides, children }) }) }) }) }) }) }); | ||
} | ||
@@ -54,4 +56,4 @@ function InternalProviders({ | ||
const mergedOverrides = (0, import_overrides.useMergedOverrides)(overrides); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useActions.ActionsProvider, { overrides: mergedOverrides.actions, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useTools.ToolsProvider, { overrides: mergedOverrides.tools, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useToolbarSchema.ToolbarSchemaProvider, { overrides: mergedOverrides.toolbar, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useActionsMenuSchema.ActionsMenuSchemaProvider, { overrides: mergedOverrides.actionsMenu, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useKeyboardShortcutsSchema.KeyboardShortcutsSchemaProvider, { overrides: mergedOverrides.keyboardShortcutsMenu, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useContextMenuSchema.ContextMenuSchemaProvider, { overrides: mergedOverrides.contextMenu, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useHelpMenuSchema.HelpMenuSchemaProvider, { overrides: mergedOverrides.helpMenu, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useMenuSchema.MenuSchemaProvider, { overrides: mergedOverrides.menu, children }) }) }) }) }) }) }) }); | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useActions.ActionsProvider, { overrides: mergedOverrides.actions, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useTools.ToolsProvider, { overrides: mergedOverrides.tools, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useToolbarSchema.ToolbarSchemaProvider, { overrides: mergedOverrides.toolbar, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useActionsMenuSchema.ActionsMenuSchemaProvider, { overrides: mergedOverrides.actionsMenu, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useKeyboardShortcutsSchema.KeyboardShortcutsSchemaProvider, { overrides: mergedOverrides.keyboardShortcutsMenu, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useContextMenuSchema.TLUiContextMenuSchemaProvider, { overrides: mergedOverrides.contextMenu, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useHelpMenuSchema.HelpMenuSchemaProvider, { overrides: mergedOverrides.helpMenu, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_useMenuSchema.TLUiMenuSchemaProvider, { overrides: mergedOverrides.menu, children }) }) }) }) }) }) }) }); | ||
} | ||
//# sourceMappingURL=TldrawUiContextProvider.js.map |
{ | ||
"name": "@tldraw/ui", | ||
"description": "A user interface for tldraw.", | ||
"version": "2.0.0-canary.08c9b63bd", | ||
"version": "2.0.0-canary.090e148552ab", | ||
"packageManager": "yarn@3.5.0", | ||
"author": { | ||
@@ -37,9 +38,9 @@ "name": "tldraw GB Ltd.", | ||
"test": "lazy inherit --passWithNoTests", | ||
"test:coverage": "yarn run -T jest --passWithNoTests --coverage", | ||
"build:package": "yarn run -T tsx ../../../scripts/build-package.ts", | ||
"build:api": "yarn run -T tsx ../../../scripts/build-api.ts", | ||
"prepack": "yarn run -T tsx ../../../scripts/prepack.ts", | ||
"postpack": "../../../scripts/postpack.sh", | ||
"test-coverage": "yarn run -T jest --passWithNoTests --coverage", | ||
"build": "yarn run -T tsx ../../scripts/build-package.ts", | ||
"build-api": "yarn run -T tsx ../../scripts/build-api.ts", | ||
"prepack": "yarn run -T tsx ../../scripts/prepack.ts", | ||
"postpack": "../../scripts/postpack.sh", | ||
"pack-tarball": "yarn pack", | ||
"lint": "yarn run -T tsx ../../../scripts/lint.ts" | ||
"lint": "yarn run -T tsx ../../scripts/lint.ts" | ||
}, | ||
@@ -51,11 +52,11 @@ "dependencies": { | ||
"@radix-ui/react-dropdown-menu": "^2.0.1", | ||
"@radix-ui/react-popover": "^1.0.2", | ||
"@radix-ui/react-popover": "1.0.6-rc.5", | ||
"@radix-ui/react-select": "^1.2.0", | ||
"@radix-ui/react-slider": "^1.1.0", | ||
"@radix-ui/react-toast": "^1.1.1", | ||
"@tldraw/editor": "2.0.0-canary.08c9b63bd", | ||
"@tldraw/primitives": "2.0.0-canary.08c9b63bd", | ||
"@tldraw/tlschema": "2.0.0-canary.08c9b63bd", | ||
"@tldraw/tlsync-client": "2.0.0-canary.08c9b63bd", | ||
"@tldraw/utils": "2.0.0-canary.08c9b63bd", | ||
"@tldraw/editor": "2.0.0-canary.090e148552ab", | ||
"@tldraw/primitives": "2.0.0-canary.090e148552ab", | ||
"@tldraw/state": "2.0.0-canary.090e148552ab", | ||
"@tldraw/tlschema": "2.0.0-canary.090e148552ab", | ||
"@tldraw/utils": "2.0.0-canary.090e148552ab", | ||
"browser-fs-access": "^0.31.0", | ||
@@ -69,5 +70,3 @@ "classnames": "^2.3.2", | ||
"react": "^18", | ||
"react-dom": "^18", | ||
"signia": "*", | ||
"signia-react": "*" | ||
"react-dom": "^18" | ||
}, | ||
@@ -79,6 +78,5 @@ "devDependencies": { | ||
"@types/lz-string": "^1.3.34", | ||
"gzip-size": "^7.0.0", | ||
"jest-canvas-mock": "^2.4.0", | ||
"jest-environment-jsdom": "^28.1.2", | ||
"lazyrepo": "0.0.0-alpha.12", | ||
"lazyrepo": "0.0.0-alpha.27", | ||
"resize-observer-polyfill": "^1.5.1" | ||
@@ -85,0 +83,0 @@ }, |
@@ -1,1 +0,5 @@ | ||
# tldraw/ui | ||
# @tldraw/ui | ||
## License | ||
The source code in this repository (as well as our 2.0+ distributions and releases) are currently licensed under Apache-2.0. These licenses are subject to change in our upcoming 2.0 release. If you are planning to use tldraw in a commercial product, please reach out at [hello@tldraw.com](mailto://hello@tldraw.com). |
143
src/index.ts
import * as Dialog from './lib/components/primitives/Dialog' | ||
import * as DropdownMenu from './lib/components/primitives/DropdownMenu' | ||
export { TldrawUi, TldrawUiContent } from './lib/TldrawUi' | ||
export { TldrawUi, type TldrawUiProps } from './lib/TldrawUi' | ||
export { | ||
@@ -9,19 +9,10 @@ TldrawUiContextProvider, | ||
} from './lib/TldrawUiContextProvider' | ||
export { ContextMenu, type ContextMenuProps } from './lib/components/ContextMenu' | ||
export { DebugPanel } from './lib/components/DebugPanel' | ||
export { HTMLCanvas } from './lib/components/HTMLCanvas' | ||
export { HelpMenu, type HelpMenuProps } from './lib/components/HelpMenu' | ||
export { NavigationZone } from './lib/components/NavigationZone/NavigationZone' | ||
export { StylePanel } from './lib/components/StylePanel/StylePanel' | ||
export { Button, type ButtonProps } from './lib/components/primitives/Button' | ||
export { ButtonPicker, type ButtonPickerProps } from './lib/components/primitives/ButtonPicker' | ||
export { Icon, type IconProps } from './lib/components/primitives/Icon' | ||
export { Input, type InputProps } from './lib/components/primitives/Input' | ||
export { Kbd, type KbdProps } from './lib/components/primitives/Kbd' | ||
export { Slider, type SliderProps } from './lib/components/primitives/Slider' | ||
export { BASE_URL, getBaseUrl, kbd, kbdStr, toStartCase } from './lib/components/primitives/shared' | ||
export { setDefaultUiAssetUrls } from './lib/assetUrls' | ||
export { ContextMenu, type TLUiContextMenuProps } from './lib/components/ContextMenu' | ||
export { Button, type TLUiButtonProps } from './lib/components/primitives/Button' | ||
export { Icon, type TLUiIconProps } from './lib/components/primitives/Icon' | ||
export { Input, type TLUiInputProps } from './lib/components/primitives/Input' | ||
export { | ||
compactMenuItems, | ||
findMenuItem, | ||
isActiveToolItem, | ||
menuCustom, | ||
@@ -31,29 +22,18 @@ menuGroup, | ||
menuSubmenu, | ||
showUiPaste, | ||
useAllowGroup, | ||
useAllowUngroup, | ||
useThreeStackableItems, | ||
type CustomMenuItem, | ||
type MenuChild, | ||
type MenuGroup, | ||
type MenuItem, | ||
type MenuSchema, | ||
type SubMenu, | ||
type TLUiCustomMenuItem, | ||
type TLUiMenuChild, | ||
type TLUiMenuGroup, | ||
type TLUiMenuItem, | ||
type TLUiMenuSchema, | ||
type TLUiSubMenu, | ||
} from './lib/hooks/menuHelpers' | ||
export { | ||
ActionsContext, | ||
ActionsProvider, | ||
useActions, | ||
type ActionItem, | ||
type ActionsContextType, | ||
type ActionsProviderProps, | ||
type TLUiActionItem, | ||
type TLUiActionsContextType, | ||
} from './lib/hooks/useActions' | ||
export { | ||
ActionsMenuSchemaContext, | ||
ActionsMenuSchemaProvider, | ||
useActionsMenuSchema, | ||
type ActionsMenuSchemaContextType, | ||
type ActionsMenuSchemaProviderProps, | ||
type TLUiActionsMenuSchemaContextType, | ||
} from './lib/hooks/useActionsMenuSchema' | ||
export { useAppEvents } from './lib/hooks/useAppEvents' | ||
export { AssetUrlsProvider, useAssetUrls } from './lib/hooks/useAssetUrls' | ||
@@ -63,40 +43,30 @@ export { BreakPointProvider, useBreakpoint } from './lib/hooks/useBreakpoint' | ||
export { useCanUndo } from './lib/hooks/useCanUndo' | ||
export { useMenuClipboardEvents, useNativeClipboardEvents } from './lib/hooks/useClipboardEvents' | ||
export { | ||
useMenuClipboardEvents, | ||
useNativeClipboardEvents, | ||
type EmbedInfo, | ||
} from './lib/hooks/useClipboardEvents' | ||
export { | ||
ContextMenuSchemaContext, | ||
ContextMenuSchemaProvider, | ||
useContextMenuSchema, | ||
type ContextMenuSchemaContextType, | ||
type ContextMenuSchemaProviderProps, | ||
type TLUiContextTTLUiMenuSchemaContextType, | ||
} from './lib/hooks/useContextMenuSchema' | ||
export { useCopyAs } from './lib/hooks/useCopyAs' | ||
export { | ||
DialogsContext, | ||
DialogsProvider, | ||
useDialogs, | ||
type DialogProps, | ||
type DialogsContextType, | ||
type DialogsProviderProps, | ||
type TLDialog, | ||
type TLUiDialog, | ||
type TLUiDialogProps, | ||
type TLUiDialogsContextType, | ||
} from './lib/hooks/useDialogsProvider' | ||
export { | ||
useEvents, | ||
type TLUiEventContextType, | ||
type TLUiEventHandler, | ||
type TLUiEventSource, | ||
} from './lib/hooks/useEventsProvider' | ||
export { useExportAs } from './lib/hooks/useExportAs' | ||
export { | ||
HelpMenuSchemaContext, | ||
HelpMenuSchemaProvider, | ||
useHelpMenuSchema, | ||
type HelpMenuSchemaProviderProps, | ||
type HelpMenuSchemaProviderType, | ||
type TLUiHelpMenuSchemaContextType, | ||
} from './lib/hooks/useHelpMenuSchema' | ||
export { useHighDpiCanvas } from './lib/hooks/useHighDpiCanvas' | ||
export { useKeyboardShortcuts } from './lib/hooks/useKeyboardShortcuts' | ||
export { | ||
KeyboardShortcutsSchemaContext, | ||
KeyboardShortcutsSchemaProvider, | ||
useKeyboardShortcutsSchema, | ||
type KeyboardShortcutsSchemaContextType, | ||
type KeyboardShortcutsSchemaProviderProps, | ||
type TLUiKeyboardShortcutsSchemaContextType, | ||
type TLUiKeyboardShortcutsSchemaProviderProps, | ||
} from './lib/hooks/useKeyboardShortcutsSchema' | ||
@@ -106,56 +76,33 @@ export { useLocalStorageState } from './lib/hooks/useLocalStorageState' | ||
export { | ||
MenuSchemaContext, | ||
MenuSchemaProvider, | ||
useMenuSchema, | ||
type MenuSchemaContextType, | ||
type MenuSchemaProviderProps, | ||
type TLUiMenuSchemaContextType, | ||
type TLUiMenuSchemaProviderProps, | ||
} from './lib/hooks/useMenuSchema' | ||
export { usePrint } from './lib/hooks/usePrint' | ||
export { useReadonly } from './lib/hooks/useReadonly' | ||
export { | ||
ToastsContext, | ||
ToastsProvider, | ||
useToasts, | ||
type TLToast, | ||
type TLToastAction, | ||
type ToastsContextType, | ||
type ToastsProviderProps, | ||
type TLUiToast, | ||
type TLUiToastAction, | ||
type TLUiToastsContextType, | ||
} from './lib/hooks/useToastsProvider' | ||
export { | ||
ToolbarSchemaContext, | ||
ToolbarSchemaProvider, | ||
toolbarItem, | ||
useToolbarSchema, | ||
type ToolbarItem, | ||
type ToolbarSchemaContextType, | ||
type ToolbarSchemaProviderProps, | ||
type TLUiToolbarItem, | ||
type TLUiToolbarSchemaContextType, | ||
} from './lib/hooks/useToolbarSchema' | ||
export { | ||
ToolsContext, | ||
ToolsProvider, | ||
useTools, | ||
type ToolItem, | ||
type ToolsContextType, | ||
type ToolsProviderProps, | ||
type TLUiToolItem, | ||
type TLUiToolsContextType, | ||
type TLUiToolsProviderProps, | ||
} from './lib/hooks/useTools' | ||
export type { TLTranslationKey } from './lib/hooks/useTranslation/TLTranslationKey' | ||
export { type TLUiTranslationKey } from './lib/hooks/useTranslation/TLUiTranslationKey' | ||
export { type TLUiTranslation } from './lib/hooks/useTranslation/translations' | ||
export { | ||
EN_TRANSLATION, | ||
fetchTranslation, | ||
getTranslation, | ||
type TLListedTranslation, | ||
type TLListedTranslations, | ||
type TLTranslation, | ||
type TLTranslationLocale, | ||
type TLTranslationMessages, | ||
type TLTranslations, | ||
} from './lib/hooks/useTranslation/translations' | ||
export { useLanguages } from './lib/hooks/useTranslation/useLanguages' | ||
export { | ||
TranslationProvider, | ||
useTranslation, | ||
type TranslationProviderProps, | ||
useTranslation as useTranslation, | ||
type TLUiTranslationContextType, | ||
} from './lib/hooks/useTranslation/useTranslation' | ||
export { TLUiIconTypes, type TLUiIconType } from './lib/icon-types' | ||
export { useDefaultHelpers, type TldrawUiOverrides } from './lib/overrides' | ||
export { type TLUiIconType } from './lib/icon-types' | ||
export { useDefaultHelpers, type TLUiOverrides } from './lib/overrides' | ||
export { Dialog, DropdownMenu } |
@@ -1,6 +0,12 @@ | ||
import { defaultEditorAssetUrls, EditorAssetUrls, EMBED_DEFINITIONS } from '@tldraw/editor' | ||
import { LANGUAGES } from './hooks/useTranslation/languages' | ||
import { TLUiIconType, TLUiIconTypes } from './icon-types' | ||
import { | ||
EMBED_DEFINITIONS, | ||
LANGUAGES, | ||
TLEditorAssetUrls, | ||
defaultEditorAssetUrls, | ||
} from '@tldraw/editor' | ||
import { RecursivePartial } from '@tldraw/utils' | ||
import { version } from '../version' | ||
import { TLUiIconType, iconTypes } from './icon-types' | ||
export type UiAssetUrls = EditorAssetUrls & { | ||
export type TLUiAssetUrls = TLEditorAssetUrls & { | ||
icons: Record<TLUiIconType, string> | ||
@@ -11,13 +17,44 @@ translations: Record<(typeof LANGUAGES)[number]['locale'], string> | ||
export const defaultUiAssetUrls: UiAssetUrls = { | ||
export let defaultUiAssetUrls: TLUiAssetUrls = { | ||
...defaultEditorAssetUrls, | ||
icons: Object.fromEntries( | ||
TLUiIconTypes.map((name) => [name, `/icons/icon/${name}.svg`]) | ||
iconTypes.map((name) => [ | ||
name, | ||
`https://unpkg.com/@tldraw/assets@${version}/icons/icon/${name}.svg`, | ||
]) | ||
) as Record<TLUiIconType, string>, | ||
translations: Object.fromEntries( | ||
LANGUAGES.map((lang) => [lang.locale, `/translations/${lang.locale}.json`]) | ||
LANGUAGES.map((lang) => [ | ||
lang.locale, | ||
`https://unpkg.com/@tldraw/assets@${version}/translations/${lang.locale}.json`, | ||
]) | ||
) as Record<(typeof LANGUAGES)[number]['locale'], string>, | ||
embedIcons: Object.fromEntries( | ||
EMBED_DEFINITIONS.map((def) => [def.type, `/embed-icons/${def.type}.png`]) | ||
EMBED_DEFINITIONS.map((def) => [ | ||
def.type, | ||
`https://unpkg.com/@tldraw/assets@${version}/embed-icons/${def.type}.png`, | ||
]) | ||
) as Record<(typeof EMBED_DEFINITIONS)[number]['type'], string>, | ||
} | ||
/** @internal */ | ||
export function setDefaultUiAssetUrls(urls: TLUiAssetUrls) { | ||
defaultUiAssetUrls = urls | ||
} | ||
/** @internal */ | ||
export function useDefaultUiAssetUrlsWithOverrides( | ||
overrides?: RecursivePartial<TLUiAssetUrls> | ||
): TLUiAssetUrls { | ||
if (!overrides) return defaultUiAssetUrls | ||
return { | ||
fonts: Object.assign({ ...defaultUiAssetUrls.fonts }, { ...overrides?.fonts }), | ||
icons: Object.assign({ ...defaultUiAssetUrls.icons }, { ...overrides?.icons }), | ||
embedIcons: Object.assign({ ...defaultUiAssetUrls.embedIcons }, { ...overrides?.embedIcons }), | ||
translations: Object.assign( | ||
{ ...defaultUiAssetUrls.translations }, | ||
{ ...overrides?.translations } | ||
), | ||
} | ||
} |
@@ -1,6 +0,6 @@ | ||
import { App, TLShapeId, TLUserPresence, uniqueId } from '@tldraw/editor' | ||
import { Editor, TLInstancePresence, TLShapeId, uniqueId } from '@tldraw/editor' | ||
import { Box2d, PI2, Vec2d, clamp } from '@tldraw/primitives' | ||
export class MinimapManager { | ||
constructor(public app: App, private dpr: number) {} | ||
constructor(public editor: Editor, private dpr: number) {} | ||
@@ -16,3 +16,3 @@ colors = { | ||
pageBounds: (Box2d & { id: TLShapeId })[] = [] | ||
collaborators: TLUserPresence[] = [] | ||
collaborators: TLInstancePresence[] = [] | ||
@@ -111,4 +111,4 @@ canvasScreenBounds = new Box2d() | ||
) => { | ||
const { app } = this | ||
const { viewportPageBounds } = app | ||
const { editor } = this | ||
const { viewportPageBounds } = editor | ||
@@ -118,3 +118,3 @@ let { x: px, y: py } = this.getPagePoint(x, y) | ||
if (clampToBounds) { | ||
const shapesPageBounds = this.app.allShapesCommonBounds | ||
const shapesPageBounds = this.editor.allShapesCommonBounds | ||
const vpPageBounds = viewportPageBounds | ||
@@ -171,6 +171,6 @@ | ||
const { app, canvasScreenBounds, canvasPageBounds, contentPageBounds, contentScreenBounds } = | ||
const { editor, canvasScreenBounds, canvasPageBounds, contentPageBounds, contentScreenBounds } = | ||
this | ||
const { width: cw, height: ch } = canvasScreenBounds | ||
const { viewportPageBounds, selectedIds } = app | ||
const { viewportPageBounds, selectedIds } = editor | ||
@@ -252,3 +252,3 @@ if (!cvs || !pageBounds) { | ||
{ | ||
const { brush } = app | ||
const { brush } = editor | ||
if (brush) { | ||
@@ -297,11 +297,8 @@ const { x, y, w, h } = brush | ||
const { currentPageId } = app | ||
const { currentPageId } = editor | ||
let collaborator: TLUserPresence | ||
let collaborator: TLInstancePresence | ||
for (let i = 0; i < this.collaborators.length; i++) { | ||
collaborator = this.collaborators[i] | ||
const instance = collaborator.lastUsedInstanceId | ||
? app.store.get(collaborator.lastUsedInstanceId) | ||
: null | ||
if (instance?.currentPageId !== currentPageId) { | ||
if (collaborator.currentPageId !== currentPageId) { | ||
continue | ||
@@ -308,0 +305,0 @@ } |
@@ -1,7 +0,7 @@ | ||
import { App, getIndexAbove, getIndexBelow, getIndexBetween, TLPageId } from '@tldraw/editor' | ||
import { Editor, getIndexAbove, getIndexBelow, getIndexBetween, TLPageId } from '@tldraw/editor' | ||
export const onMovePage = (app: App, id: TLPageId, from: number, to: number) => { | ||
export const onMovePage = (editor: Editor, id: TLPageId, from: number, to: number) => { | ||
let index: string | ||
const pages = app.pages | ||
const pages = editor.pages | ||
@@ -20,4 +20,4 @@ const below = from > to ? pages[to - 1] : pages[to] | ||
if (index !== pages[from].index) { | ||
app.mark('moving page') | ||
app.updatePage({ | ||
editor.mark('moving page') | ||
editor.updatePage({ | ||
id: id as TLPageId, | ||
@@ -24,0 +24,0 @@ index, |
@@ -1,2 +0,2 @@ | ||
/** @public */ | ||
/** @internal */ | ||
export function toStartCase(str: string) { | ||
@@ -16,3 +16,3 @@ return str | ||
/** @public */ | ||
/** @internal */ | ||
export function kbd(str: string) { | ||
@@ -28,3 +28,3 @@ return str | ||
/** @public */ | ||
/** @internal */ | ||
export function kbdStr(str: string) { | ||
@@ -43,25 +43,1 @@ return ( | ||
} | ||
/** @public */ | ||
export const getBaseUrl = () => { | ||
if (typeof process === 'undefined') { | ||
return 'http://localhost:5420' | ||
} | ||
if (process.env.NODE_ENV === 'development') { | ||
return 'http://localhost:3000' | ||
} | ||
if (process.env.NEXT_PUBLIC_VERCEL_ENV === 'production') { | ||
return 'https://www.tldraw.com' | ||
} | ||
if (process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview') { | ||
return `https://${process.env.NEXT_PUBLIC_VERCEL_URL}` | ||
} | ||
return 'http://localhost:3000' | ||
} | ||
/** @public */ | ||
export const BASE_URL = getBaseUrl() |
@@ -1,13 +0,13 @@ | ||
import { App, TLArrowShapeDef, useApp } from '@tldraw/editor' | ||
import { ArrowShapeUtil, Editor, useEditor } from '@tldraw/editor' | ||
import { useValue } from '@tldraw/state' | ||
import { assert, exhaustiveSwitchError } from '@tldraw/utils' | ||
import { useValue } from 'signia-react' | ||
import { ActionItem } from './useActions' | ||
import { ToolItem } from './useTools' | ||
import { TLTranslationKey } from './useTranslation/TLTranslationKey' | ||
import { TLUiActionItem } from './useActions' | ||
import { TLUiToolItem } from './useTools' | ||
import { TLUiTranslationKey } from './useTranslation/TLUiTranslationKey' | ||
/** @public */ | ||
export type MenuChild = MenuItem | SubMenu | MenuGroup | CustomMenuItem | ||
export type TLUiMenuChild = TLUiMenuItem | TLUiSubMenu | TLUiMenuGroup | TLUiCustomMenuItem | ||
/** @public */ | ||
export type CustomMenuItem = { | ||
export type TLUiCustomMenuItem = { | ||
id: string | ||
@@ -20,7 +20,7 @@ type: 'custom' | ||
/** @public */ | ||
export type MenuItem = { | ||
export type TLUiMenuItem = { | ||
id: string | ||
type: 'item' | ||
readonlyOk: boolean | ||
actionItem: ActionItem | ||
actionItem: TLUiActionItem | ||
disabled: boolean | ||
@@ -31,3 +31,3 @@ checked: boolean | ||
/** @public */ | ||
export type MenuGroup = { | ||
export type TLUiMenuGroup = { | ||
id: string | ||
@@ -38,17 +38,17 @@ type: 'group' | ||
readonlyOk: boolean | ||
children: MenuChild[] | ||
children: TLUiMenuChild[] | ||
} | ||
/** @public */ | ||
export type SubMenu = { | ||
export type TLUiSubMenu = { | ||
id: string | ||
type: 'submenu' | ||
label: TLTranslationKey | ||
label: TLUiTranslationKey | ||
disabled: boolean | ||
readonlyOk: boolean | ||
children: MenuChild[] | ||
children: TLUiMenuChild[] | ||
} | ||
/** @public */ | ||
export type MenuSchema = (MenuGroup | MenuItem | CustomMenuItem)[] | ||
export type TLUiMenuSchema = (TLUiMenuGroup | TLUiMenuItem | TLUiCustomMenuItem)[] | ||
@@ -61,3 +61,6 @@ /** @public */ | ||
/** @public */ | ||
export function menuGroup(id: string, ...children: (MenuChild | null | false)[]): MenuGroup | null { | ||
export function menuGroup( | ||
id: string, | ||
...children: (TLUiMenuChild | null | false)[] | ||
): TLUiMenuGroup | null { | ||
const childItems = compactMenuItems(children) | ||
@@ -80,5 +83,5 @@ | ||
id: string, | ||
label: TLTranslationKey, | ||
...children: (MenuChild | null | false)[] | ||
): SubMenu | null { | ||
label: TLUiTranslationKey, | ||
...children: (TLUiMenuChild | null | false)[] | ||
): TLUiSubMenu | null { | ||
const childItems = compactMenuItems(children) | ||
@@ -113,5 +116,5 @@ if (childItems.length === 0) return null | ||
export function menuItem( | ||
actionItem: ActionItem | ToolItem, | ||
actionItem: TLUiActionItem | TLUiToolItem, | ||
opts = {} as Partial<{ checked: boolean; disabled: boolean }> | ||
): MenuItem { | ||
): TLUiMenuItem { | ||
if (!actionItem) { | ||
@@ -137,6 +140,6 @@ throw Error('No action item provided to menuItem') | ||
function shapesWithUnboundArrows(app: App) { | ||
const { selectedIds } = app | ||
function shapesWithUnboundArrows(editor: Editor) { | ||
const { selectedIds } = editor | ||
const selectedShapes = selectedIds.map((id) => { | ||
return app.getShapeById(id) | ||
return editor.getShapeById(id) | ||
}) | ||
@@ -146,6 +149,6 @@ | ||
if (!shape) return false | ||
if (TLArrowShapeDef.is(shape) && shape.props.start.type === 'binding') { | ||
if (editor.isShapeOfType(shape, ArrowShapeUtil) && shape.props.start.type === 'binding') { | ||
return false | ||
} | ||
if (TLArrowShapeDef.is(shape) && shape.props.end.type === 'binding') { | ||
if (editor.isShapeOfType(shape, ArrowShapeUtil) && shape.props.end.type === 'binding') { | ||
return false | ||
@@ -157,20 +160,20 @@ } | ||
/** @public */ | ||
/** @internal */ | ||
export const useThreeStackableItems = () => { | ||
const app = useApp() | ||
return useValue('threeStackableItems', () => shapesWithUnboundArrows(app).length > 2, [app]) | ||
const editor = useEditor() | ||
return useValue('threeStackableItems', () => shapesWithUnboundArrows(editor).length > 2, [editor]) | ||
} | ||
/** @public */ | ||
/** @internal */ | ||
export const useAllowGroup = () => { | ||
const app = useApp() | ||
return useValue('allowGroup', () => shapesWithUnboundArrows(app).length > 1, [app]) | ||
const editor = useEditor() | ||
return useValue('allowGroup', () => shapesWithUnboundArrows(editor).length > 1, [editor]) | ||
} | ||
/** @public */ | ||
/** @internal */ | ||
export const useAllowUngroup = () => { | ||
const app = useApp() | ||
const editor = useEditor() | ||
return useValue( | ||
'allowUngroup', | ||
() => app.selectedIds.some((id) => app.getShapeById(id)?.type === 'group'), | ||
() => editor.selectedIds.some((id) => editor.getShapeById(id)?.type === 'group'), | ||
[] | ||
@@ -181,21 +184,3 @@ ) | ||
/** @public */ | ||
export const showUiPaste = | ||
typeof window !== 'undefined' && | ||
'navigator' in window && | ||
Boolean(navigator.clipboard) && | ||
Boolean(navigator.clipboard.read) | ||
/** @public */ | ||
export const isActiveToolItem = ( | ||
item: ToolItem, | ||
activeToolId: string | undefined, | ||
geoState: string | null | undefined | ||
) => { | ||
return item.meta?.geo | ||
? activeToolId === 'geo' && geoState === item.meta?.geo | ||
: activeToolId === item.id | ||
} | ||
/** @public */ | ||
export function findMenuItem(menu: MenuSchema, path: string[]) { | ||
export function findMenuItem(menu: TLUiMenuSchema, path: string[]) { | ||
const item = _findMenuItem(menu, path) | ||
@@ -205,3 +190,7 @@ assert(item, `Menu item ${path.join(' > ')} not found`) | ||
} | ||
function _findMenuItem(menu: MenuSchema | MenuChild[], path: string[]): MenuChild | null { | ||
function _findMenuItem( | ||
menu: TLUiMenuSchema | TLUiMenuChild[], | ||
path: string[] | ||
): TLUiMenuChild | null { | ||
const [next, ...rest] = path | ||
@@ -224,1 +213,7 @@ if (!next) return null | ||
} | ||
export const showMenuPaste = | ||
typeof window !== 'undefined' && | ||
'navigator' in window && | ||
Boolean(navigator.clipboard) && | ||
Boolean(navigator.clipboard.read) |
@@ -1,8 +0,8 @@ | ||
import { useApp } from '@tldraw/editor' | ||
import { useValue } from 'signia-react' | ||
import { useEditor } from '@tldraw/editor' | ||
import { useValue } from '@tldraw/state' | ||
/** @public */ | ||
export function useCanRedo() { | ||
const app = useApp() | ||
return useValue('useCanRedo', () => app.canRedo, [app]) | ||
const editor = useEditor() | ||
return useValue('useCanRedo', () => editor.canRedo, [editor]) | ||
} |
@@ -1,8 +0,8 @@ | ||
import { useApp } from '@tldraw/editor' | ||
import { useValue } from 'signia-react' | ||
import { useEditor } from '@tldraw/editor' | ||
import { useValue } from '@tldraw/state' | ||
/** @public */ | ||
export function useCanUndo() { | ||
const app = useApp() | ||
return useValue('useCanUndo', () => app.canUndo, [app]) | ||
const editor = useEditor() | ||
return useValue('useCanUndo', () => editor.canUndo, [editor]) | ||
} |
import { | ||
App, | ||
createAssetShapeAtPoint, | ||
createBookmarkShapeAtPoint, | ||
createEmbedShapeAtPoint, | ||
createShapeId, | ||
createShapesFromFiles, | ||
FONT_FAMILIES, | ||
FONT_SIZES, | ||
getEmbedInfo, | ||
getIndexAbove, | ||
getIndices, | ||
ArrowShapeUtil, | ||
BookmarkShapeUtil, | ||
Editor, | ||
EmbedShapeUtil, | ||
GeoShapeUtil, | ||
TLContent, | ||
TextShapeUtil, | ||
getValidHttpURLList, | ||
isShapeId, | ||
isSvgText, | ||
isValidHttpURL, | ||
TEXT_PROPS, | ||
TLAlignType, | ||
TLArrowheadType, | ||
TLArrowShapeDef, | ||
TLAsset, | ||
TLAssetId, | ||
TLBookmarkShapeDef, | ||
TLClipboardModel, | ||
TLColorType, | ||
TLDashType, | ||
TLEmbedShapeDef, | ||
TLFillType, | ||
TLFontType, | ||
TLGeoShapeDef, | ||
TLOpacityType, | ||
TLShapeId, | ||
TLSizeType, | ||
TLTextShapeDef, | ||
uniqueId, | ||
useApp, | ||
useEditor, | ||
} from '@tldraw/editor' | ||
import { Box2d, Vec2d, VecLike } from '@tldraw/primitives' | ||
import { compact, isNonNull } from '@tldraw/utils' | ||
import { VecLike } from '@tldraw/primitives' | ||
import { isNonNull } from '@tldraw/utils' | ||
import { compressToBase64, decompressFromBase64 } from 'lz-string' | ||
import { useCallback, useEffect } from 'react' | ||
import { useAppIsFocused } from './useAppIsFocused' | ||
import { pasteExcalidrawContent } from './clipboard/pasteExcalidrawContent' | ||
import { pasteFiles } from './clipboard/pasteFiles' | ||
import { pasteTldrawContent } from './clipboard/pasteTldrawContent' | ||
import { pasteUrl } from './clipboard/pasteUrl' | ||
import { useEditorIsFocused } from './useEditorIsFocused' | ||
import { TLUiEventSource, useEvents } from './useEventsProvider' | ||
/** @public */ | ||
export type EmbedInfo = { | ||
width: number | ||
height: number | ||
doesResize: boolean | ||
isEmbedUrl: (url: string) => boolean | ||
toEmbed: (url: string) => string | ||
const INPUTS = ['input', 'select', 'textarea'] | ||
/** | ||
* Get whether to disallow clipboard events. | ||
* | ||
* @param editor - The editor instance. | ||
* @internal | ||
*/ | ||
function disallowClipboardEvents(editor: Editor) { | ||
const { activeElement } = document | ||
return ( | ||
editor.isMenuOpen || | ||
(activeElement && | ||
(activeElement.getAttribute('contenteditable') || | ||
INPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1)) | ||
) | ||
} | ||
/** | ||
* Get a blob as a string. | ||
* | ||
* @param blob - The blob to get as a string. | ||
* @internal | ||
*/ | ||
async function blobAsString(blob: Blob) { | ||
@@ -67,900 +63,489 @@ return new Promise<string>((resolve, reject) => { | ||
async function dataTransferItemAsString(item: DataTransferItem) { | ||
return new Promise<string>((resolve) => { | ||
item.getAsString((text) => { | ||
resolve(text) | ||
}) | ||
}) | ||
} | ||
const INPUTS = ['input', 'select', 'textarea'] | ||
function disallowClipboardEvents(app: App) { | ||
const { activeElement } = document | ||
return ( | ||
app.isMenuOpen || | ||
(activeElement && | ||
(activeElement.getAttribute('contenteditable') || | ||
INPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1)) | ||
) | ||
} | ||
/** | ||
* Strip HTML tags from a string. | ||
* @param html - The HTML to strip. | ||
* @internal | ||
*/ | ||
function stripHtml(html: string) { | ||
// See <https://github.com/developit/preact-markup/blob/4788b8d61b4e24f83688710746ee36e7464f7bbc/src/parse-markup.js#L60-L69> | ||
const doc = document.implementation.createHTMLDocument('') | ||
doc.documentElement.innerHTML = html | ||
doc.documentElement.innerHTML = html.trim() | ||
return doc.body.textContent || doc.body.innerText || '' | ||
} | ||
// Clear the clipboard when the user copies nothing | ||
const clearPersistedClipboard = () => { | ||
window.navigator.clipboard.writeText('') | ||
/** | ||
* Whether a ClipboardItem is a file. | ||
* @param item - The ClipboardItem to check. | ||
* @internal | ||
*/ | ||
const isFile = (item: ClipboardItem) => { | ||
return item.types.find((i) => i.match(/^image\//)) | ||
} | ||
/** | ||
* Write serialized data to the local storage. | ||
* | ||
* @param data - The string to write. | ||
* @param kind - The kind of data to write. | ||
* Handle text pasted into the editor. | ||
* @param editor - The editor instance. | ||
* @param data - The text to paste. | ||
* @param point - (optional) The point at which to paste the text. | ||
* @internal | ||
*/ | ||
const getStringifiedClipboard = (data: any, kind: 'text' | 'file' | 'content') => { | ||
const s = compressToBase64( | ||
JSON.stringify({ | ||
type: 'application/tldraw', | ||
kind, | ||
data, | ||
const handleText = (editor: Editor, data: string, point?: VecLike) => { | ||
const validUrlList = getValidHttpURLList(data) | ||
if (validUrlList) { | ||
for (const url of validUrlList) { | ||
pasteUrl(editor, url, point) | ||
} | ||
} else if (isValidHttpURL(data)) { | ||
pasteUrl(editor, data, point) | ||
} else if (isSvgText(data)) { | ||
editor.mark('paste') | ||
editor.putExternalContent({ | ||
type: 'svg-text', | ||
text: data, | ||
point, | ||
}) | ||
) | ||
return s | ||
} else { | ||
editor.mark('paste') | ||
editor.putExternalContent({ | ||
type: 'text', | ||
text: data, | ||
point, | ||
}) | ||
} | ||
} | ||
/** | ||
* When the clipboard has tldraw content, paste it into the scene. | ||
* | ||
* @param clipboard - The clipboard model. | ||
* @param point - The center point at which to paste the content. | ||
* Something found on the clipboard, either through the event's clipboard data or the browser's clipboard API. | ||
* @internal | ||
*/ | ||
const pasteTldrawContent = async (app: App, clipboard: TLClipboardModel, point?: VecLike) => { | ||
const p = point ?? (app.inputs.shiftKey ? app.inputs.currentPagePoint : undefined) | ||
type ClipboardThing = | ||
| { | ||
type: 'file' | ||
source: Promise<File | null> | ||
} | ||
| { | ||
type: 'blob' | ||
source: Promise<Blob | null> | ||
} | ||
| { | ||
type: 'url' | ||
source: Promise<string> | ||
} | ||
| { | ||
type: 'html' | ||
source: Promise<string> | ||
} | ||
| { | ||
type: 'text' | ||
source: Promise<string> | ||
} | ||
| { | ||
type: string | ||
source: Promise<string> | ||
} | ||
app.mark('paste') | ||
app.putContent(clipboard, { | ||
point: p, | ||
select: true, | ||
}) | ||
} | ||
/** | ||
* When the clipboard has plain text, create a text shape and insert it into the scene | ||
* | ||
* @param text - The text to paste. | ||
* @param point - The point at which to paste the text. | ||
* The result of processing a `ClipboardThing`. | ||
* @internal | ||
*/ | ||
const pastePlainText = async (app: App, text: string, point?: VecLike) => { | ||
const p = point ?? (app.inputs.shiftKey ? app.inputs.currentPagePoint : app.viewportPageCenter) | ||
const defaultProps = app.getShapeUtilByDef(TLTextShapeDef).defaultProps() | ||
type ClipboardResult = | ||
| { | ||
type: 'tldraw' | ||
data: TLContent | ||
} | ||
| { | ||
type: 'excalidraw' | ||
data: any | ||
} | ||
| { | ||
type: 'text' | ||
data: string | ||
subtype: 'json' | 'html' | 'text' | 'url' | ||
} | ||
| { | ||
type: 'error' | ||
data: string | null | ||
reason: string | ||
} | ||
// Measure the text with default values | ||
const { w, h } = app.textMeasure.measureText({ | ||
...TEXT_PROPS, | ||
text: stripHtml(text), | ||
fontFamily: FONT_FAMILIES[defaultProps.font], | ||
fontSize: FONT_SIZES[defaultProps.size], | ||
width: 'fit-content', | ||
}) | ||
app.mark('paste') | ||
app.createShapes([ | ||
{ | ||
id: createShapeId(), | ||
type: 'text', | ||
x: p.x - w / 2, | ||
y: p.y - h / 2, | ||
props: { | ||
text: stripHtml(text), | ||
autoSize: true, | ||
}, | ||
}, | ||
]) | ||
} | ||
/** | ||
* When the clipboard has plain text that is a valid URL, create a bookmark shape and insert it into | ||
* the scene | ||
* Handle a paste using event clipboard data. This is the "original" | ||
* paste method that uses the clipboard data from the paste event. | ||
* https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent/clipboardData | ||
* | ||
* @param url - The URL to paste. | ||
* @param point - The point at which to paste the file. | ||
* @param editor - The editor | ||
* @param clipboardData - The clipboard data | ||
* @param point - (optional) The point to paste at | ||
* @internal | ||
*/ | ||
const pasteUrl = async (app: App, url: string, point?: VecLike) => { | ||
const p = point ?? (app.inputs.shiftKey ? app.inputs.currentPagePoint : app.viewportPageCenter) | ||
const handlePasteFromEventClipboardData = async ( | ||
editor: Editor, | ||
clipboardData: DataTransfer, | ||
point?: VecLike | ||
) => { | ||
// Do not paste while in any editing state | ||
if (editor.editingId !== null) return | ||
// Lets see if its an image and we have CORs | ||
try { | ||
const resp = await fetch(url) | ||
if (resp.headers.get('content-type')?.match(/^image\//)) { | ||
app.mark('paste') | ||
pasteFiles(app, [url]) | ||
return | ||
} | ||
} catch (err: any) { | ||
if (err.message !== 'Failed to fetch') { | ||
console.error(err) | ||
} | ||
if (!clipboardData) { | ||
throw Error('No clipboard data') | ||
} | ||
const embedInfo = getEmbedInfo(url) | ||
const things: ClipboardThing[] = [] | ||
if (embedInfo) { | ||
app.mark('paste') | ||
createEmbedShapeAtPoint(app, embedInfo.url, p, embedInfo.definition) | ||
} else { | ||
app.mark('paste') | ||
await createBookmarkShapeAtPoint(app, url, p) | ||
for (const item of Object.values(clipboardData.items)) { | ||
switch (item.kind) { | ||
case 'file': { | ||
// files are always blobs | ||
things.push({ | ||
type: 'file', | ||
source: new Promise((r) => r(item.getAsFile())) as Promise<File | null>, | ||
}) | ||
break | ||
} | ||
case 'string': { | ||
// strings can be text or html | ||
if (item.type === 'text/html') { | ||
things.push({ | ||
type: 'html', | ||
source: new Promise((r) => item.getAsString(r)) as Promise<string>, | ||
}) | ||
} else if (item.type === 'text/plain') { | ||
things.push({ | ||
type: 'text', | ||
source: new Promise((r) => item.getAsString(r)) as Promise<string>, | ||
}) | ||
} else { | ||
things.push({ type: item.type, source: new Promise((r) => item.getAsString(r)) }) | ||
} | ||
break | ||
} | ||
} | ||
} | ||
} | ||
const pasteSvgText = async (app: App, text: string, point?: VecLike) => { | ||
const p = point ?? (app.inputs.shiftKey ? app.inputs.currentPagePoint : app.viewportPageCenter) | ||
app.mark('paste') | ||
await createAssetShapeAtPoint(app, text, p) | ||
handleClipboardThings(editor, things, point) | ||
} | ||
/** | ||
* When the clipboard has a file, create an image shape from the file and paste it into the scene | ||
* Handle a paste using items retrieved from the Clipboard API. | ||
* https://developer.mozilla.org/en-US/docs/Web/API/ClipboardItem | ||
* | ||
* @param url - The file's url. | ||
* @param point - The point at which to paste the file. | ||
* @param editor - The editor | ||
* @param clipboardItems - The clipboard items to handle | ||
* @param point - (optional) The point to paste at | ||
* @internal | ||
*/ | ||
const pasteFiles = async (app: App, urls: string[], point?: VecLike) => { | ||
const p = point ?? (app.inputs.shiftKey ? app.inputs.currentPagePoint : app.viewportPageCenter) | ||
const handlePasteFromClipboardApi = async ( | ||
editor: Editor, | ||
clipboardItems: ClipboardItem[], | ||
point?: VecLike | ||
) => { | ||
// We need to populate the array of clipboard things | ||
// based on the ClipboardItems from the Clipboard API. | ||
// This is done in a different way than when using | ||
// the clipboard data from the paste event. | ||
const blobs = await Promise.all(urls.map(async (url) => await (await fetch(url)).blob())) | ||
const things: ClipboardThing[] = [] | ||
const files = blobs.map( | ||
(blob) => | ||
new File([blob], 'tldrawFile', { | ||
type: blob.type, | ||
for (const item of clipboardItems) { | ||
if (isFile(item)) { | ||
for (const type of item.types) { | ||
if (type.match(/^image\//)) { | ||
things.push({ type: 'blob', source: item.getType(type) }) | ||
} | ||
} | ||
} | ||
if (item.types.includes('text/html')) { | ||
things.push({ | ||
type: 'html', | ||
source: new Promise<string>((r) => | ||
item.getType('text/html').then((blob) => blobAsString(blob).then(r)) | ||
), | ||
}) | ||
) | ||
} | ||
app.mark('paste') | ||
await createShapesFromFiles(app, files, p, false) | ||
urls.forEach((url) => URL.revokeObjectURL(url)) | ||
} | ||
/** | ||
* When the user copies, write the contents to local storage and to the clipboard | ||
* | ||
* @param app - App | ||
* @public | ||
*/ | ||
const handleMenuCopy = (app: App) => { | ||
const content = app.getContent() | ||
if (!content) { | ||
clearPersistedClipboard() | ||
return | ||
} | ||
const stringifiedClipboard = getStringifiedClipboard(content, 'content') | ||
if (typeof window?.navigator !== 'undefined') { | ||
// Extract the text from the clipboard | ||
const textItems = content.shapes | ||
.map((shape) => { | ||
if (TLTextShapeDef.is(shape) || TLGeoShapeDef.is(shape) || TLArrowShapeDef.is(shape)) { | ||
return shape.props.text | ||
} | ||
if (TLBookmarkShapeDef.is(shape) || TLEmbedShapeDef.is(shape)) { | ||
return shape.props.url | ||
} | ||
return null | ||
if (item.types.includes('text/uri-list')) { | ||
things.push({ | ||
type: 'url', | ||
source: new Promise<string>((r) => | ||
item.getType('text/uri-list').then((blob) => blobAsString(blob).then(r)) | ||
), | ||
}) | ||
.filter(isNonNull) | ||
} | ||
if (navigator.clipboard?.write) { | ||
const htmlBlob = new Blob([`<tldraw>${stringifiedClipboard}</tldraw>`], { | ||
type: 'text/html', | ||
if (item.types.includes('text/plain')) { | ||
things.push({ | ||
type: 'text', | ||
source: new Promise<string>((r) => | ||
item.getType('text/plain').then((blob) => blobAsString(blob).then(r)) | ||
), | ||
}) | ||
let textContent = textItems.join(' ') | ||
// This is a bug in chrome android where it won't paste content if | ||
// the text/plain content is "" so we need to always add an empty | ||
// space 🤬 | ||
if (textContent === '') { | ||
textContent = ' ' | ||
} | ||
navigator.clipboard.write([ | ||
new ClipboardItem({ | ||
'text/html': htmlBlob, | ||
'text/plain': new Blob([textContent], { type: 'text/plain' }), | ||
}), | ||
]) | ||
} else if (navigator.clipboard.writeText) { | ||
navigator.clipboard.writeText(`<tldraw>${stringifiedClipboard}</tldraw>`) | ||
} | ||
} | ||
} | ||
const pasteText = (app: App, data: string, point?: VecLike) => { | ||
const validUrlList = getValidHttpURLList(data) | ||
if (validUrlList) { | ||
for (const url of validUrlList) { | ||
pasteUrl(app, url, point) | ||
} | ||
} else if (isValidHttpURL(data)) { | ||
pasteUrl(app, data, point) | ||
} else if (isSvgText(data)) { | ||
pasteSvgText(app, data, point) | ||
} else { | ||
pastePlainText(app, data, point) | ||
} | ||
return await handleClipboardThings(editor, things, point) | ||
} | ||
async function pasteExcalidrawContent(app: App, clipboard: any, point?: VecLike) { | ||
const { elements, files } = clipboard | ||
async function handleClipboardThings(editor: Editor, things: ClipboardThing[], point?: VecLike) { | ||
// 1. Handle files | ||
// | ||
// We need to handle files separately because if we want them to | ||
// be placed next to each other, we need to create them all at once. | ||
const tldrawContent: TLClipboardModel = { | ||
shapes: [], | ||
rootShapeIds: [], | ||
assets: [], | ||
schema: app.store.schema.serialize(), | ||
} | ||
const files = things.filter( | ||
(t) => (t.type === 'file' || t.type === 'blob') && t.source !== null | ||
) as Extract<ClipboardThing, { type: 'file' } | { type: 'blob' }>[] | ||
const groupShapeIdToChildren = new Map<string, TLShapeId[]>() | ||
const rotatedElements = new Map<TLShapeId, number>() | ||
const getOpacity = (opacity: number): TLOpacityType => { | ||
const t = opacity / 100 | ||
if (t < 0.2) { | ||
return '0.1' | ||
} else if (t < 0.4) { | ||
return '0.25' | ||
} else if (t < 0.6) { | ||
return '0.5' | ||
} else if (t < 0.8) { | ||
return '0.75' | ||
} | ||
return '1' | ||
// Just paste the files, nothing else | ||
if (files.length) { | ||
const fileBlobs = await Promise.all(files.map((t) => t.source!)) | ||
const urls = (fileBlobs.filter(Boolean) as (File | Blob)[]).map((blob) => | ||
URL.createObjectURL(blob) | ||
) | ||
return await pasteFiles(editor, urls, point) | ||
} | ||
const strokeWidthsToSizes: Record<number, TLSizeType> = { | ||
1: 's', | ||
2: 'm', | ||
3: 'l', | ||
4: 'xl', | ||
} | ||
// 2. Generate clipboard results for non-file things | ||
// | ||
// Getting the source from the items is async, however they must be accessed syncronously; | ||
// we can't await them in a loop. So we'll map them to promises and await them all at once, | ||
// then make decisions based on what we find. | ||
const fontSizesToSizes: Record<number, TLSizeType> = { | ||
16: 's', | ||
20: 'm', | ||
28: 'l', | ||
36: 'xl', | ||
} | ||
const results = await Promise.all<ClipboardResult>( | ||
things | ||
.filter((t) => t.type !== 'file') | ||
.map( | ||
(t) => | ||
new Promise((r) => { | ||
const thing = t as Exclude<ClipboardThing, { type: 'file' } | { type: 'blob' }> | ||
function getFontSizeAndScale(fontSize: number): { size: TLSizeType; scale: number } { | ||
const size = fontSizesToSizes[fontSize] | ||
if (size) { | ||
return { size, scale: 1 } | ||
} | ||
if (fontSize < 16) { | ||
return { size: 's', scale: fontSize / 16 } | ||
} | ||
if (fontSize > 36) { | ||
return { size: 'xl', scale: fontSize / 36 } | ||
} | ||
return { size: 'm', scale: 1 } | ||
} | ||
if (thing.type === 'file') { | ||
r({ type: 'error', data: null, reason: 'unexpected file' }) | ||
return | ||
} | ||
const fontFamilyToFontType: Record<number, TLFontType> = { | ||
1: 'draw', | ||
2: 'sans', | ||
3: 'mono', | ||
} | ||
thing.source.then((text) => { | ||
// first, see if we can find tldraw content, which is JSON inside of an html comment | ||
const tldrawHtmlComment = text.match(/<tldraw[^>]*>(.*)<\/tldraw>/)?.[1] | ||
const colorsToColors: Record<string, TLColorType> = { | ||
'#ffffff': 'grey', | ||
// Strokes | ||
'#000000': 'black', | ||
'#343a40': 'grey', | ||
'#495057': 'grey', | ||
'#c92a2a': 'red', | ||
'#a61e4d': 'light-red', | ||
'#862e9c': 'violet', | ||
'#5f3dc4': 'light-violet', | ||
'#364fc7': 'blue', | ||
'#1864ab': 'light-blue', | ||
'#0b7285': 'light-green', | ||
'#087f5b': 'light-green', | ||
'#2b8a3e': 'green', | ||
'#5c940d': 'light-green', | ||
'#e67700': 'yellow', | ||
'#d9480f': 'orange', | ||
// Backgrounds | ||
'#ced4da': 'grey', | ||
'#868e96': 'grey', | ||
'#fa5252': 'light-red', | ||
'#e64980': 'red', | ||
'#be4bdb': 'light-violet', | ||
'#7950f2': 'violet', | ||
'#4c6ef5': 'blue', | ||
'#228be6': 'light-blue', | ||
'#15aabf': 'light-green', | ||
'#12b886': 'green', | ||
'#40c057': 'green', | ||
'#82c91e': 'light-green', | ||
'#fab005': 'yellow', | ||
'#fd7e14': 'orange', | ||
'#212529': 'grey', | ||
} | ||
if (tldrawHtmlComment) { | ||
try { | ||
// If we've found tldraw content in the html string, use that as JSON | ||
const jsonComment = decompressFromBase64(tldrawHtmlComment) | ||
if (jsonComment === null) { | ||
r({ | ||
type: 'error', | ||
data: jsonComment, | ||
reason: `found tldraw data comment but could not parse base64`, | ||
}) | ||
return | ||
} else { | ||
const json = JSON.parse(jsonComment) | ||
if (json.type !== 'application/tldraw') { | ||
r({ | ||
type: 'error', | ||
data: json, | ||
reason: `found tldraw data comment but JSON was of a different type: ${json.type}`, | ||
}) | ||
} | ||
const strokeStylesToStrokeTypes: Record<string, TLDashType> = { | ||
solid: 'draw', | ||
dashed: 'dashed', | ||
dotted: 'dotted', | ||
} | ||
if (typeof json.data === 'string') { | ||
r({ | ||
type: 'error', | ||
data: json, | ||
reason: | ||
'found tldraw json but data was a string instead of a TLClipboardModel object', | ||
}) | ||
return | ||
} | ||
const fillStylesToFillType: Record<string, TLFillType> = { | ||
'cross-hatch': 'pattern', | ||
hachure: 'pattern', | ||
solid: 'solid', | ||
} | ||
r({ type: 'tldraw', data: json.data }) | ||
return | ||
} | ||
} catch (e: any) { | ||
r({ | ||
type: 'error', | ||
data: tldrawHtmlComment, | ||
reason: | ||
'found tldraw json but data was a string instead of a TLClipboardModel object', | ||
}) | ||
return | ||
} | ||
} else { | ||
if (thing.type === 'html') { | ||
r({ type: 'text', data: text, subtype: 'html' }) | ||
return | ||
} | ||
const textAlignToAlignTypes: Record<string, TLAlignType> = { | ||
left: 'start', | ||
center: 'middle', | ||
right: 'end', | ||
} | ||
if (thing.type === 'url') { | ||
r({ type: 'text', data: text, subtype: 'url' }) | ||
return | ||
} | ||
const arrowheadsToArrowheadTypes: Record<string, TLArrowheadType> = { | ||
arrow: 'arrow', | ||
dot: 'dot', | ||
triangle: 'triangle', | ||
bar: 'pipe', | ||
} | ||
// if we have not found a tldraw comment, Otherwise, try to parse the text as JSON directly. | ||
try { | ||
const json = JSON.parse(text) | ||
if (json.type === 'excalidraw/clipboard') { | ||
// If the clipboard contains content copied from excalidraw, then paste that | ||
r({ type: 'excalidraw', data: json }) | ||
return | ||
} else { | ||
r({ type: 'text', data: text, subtype: 'json' }) | ||
return | ||
} | ||
} catch (e) { | ||
// If we could not parse the text as JSON, then it's just text | ||
r({ type: 'text', data: text, subtype: 'text' }) | ||
return | ||
} | ||
} | ||
function getBend(element: any, startPoint: any, endPoint: any) { | ||
let bend = 0 | ||
if (element.points.length > 2) { | ||
const start = new Vec2d(startPoint[0], startPoint[1]) | ||
const end = new Vec2d(endPoint[0], endPoint[1]) | ||
const handle = new Vec2d(element.points[1][0], element.points[1][1]) | ||
const delta = Vec2d.Sub(end, start) | ||
const v = Vec2d.Per(delta) | ||
r({ type: 'error', data: text, reason: 'unhandled case' }) | ||
}) | ||
}) | ||
) | ||
) | ||
const med = Vec2d.Med(end, start) | ||
const A = Vec2d.Sub(med, v) | ||
const B = Vec2d.Add(med, v) | ||
// 3. | ||
// | ||
// Now that we know what kind of stuff we're dealing with, we can actual create some content. | ||
// There are priorities here, so order matters: we've already handled images and files, which | ||
// take first priority; then we want to handle tldraw content, then excalidraw content, then | ||
// html content, then links, and finally text content. | ||
const point = Vec2d.NearestPointOnLineSegment(A, B, handle, false) | ||
bend = Vec2d.Dist(point, med) | ||
if (Vec2d.Clockwise(point, end, med)) bend *= -1 | ||
// Try to paste tldraw content | ||
for (const result of results) { | ||
if (result.type === 'tldraw') { | ||
pasteTldrawContent(editor, result.data, point) | ||
return | ||
} | ||
return bend | ||
} | ||
const getDash = (element: any): TLDashType => { | ||
let dash: TLDashType = strokeStylesToStrokeTypes[element.strokeStyle] ?? 'draw' | ||
if (dash === 'draw' && element.roughness === 0) { | ||
dash = 'solid' | ||
// Try to paste excalidraw content | ||
for (const result of results) { | ||
if (result.type === 'excalidraw') { | ||
pasteExcalidrawContent(editor, result.data, point) | ||
return | ||
} | ||
return dash | ||
} | ||
const getFill = (element: any): TLFillType => { | ||
if (element.backgroundColor === 'transparent') { | ||
return 'none' | ||
} | ||
return fillStylesToFillType[element.fillStyle] ?? 'solid' | ||
} | ||
// Try to paste html content | ||
for (const result of results) { | ||
if (result.type === 'text' && result.subtype === 'html') { | ||
// try to find a link | ||
const rootNode = new DOMParser().parseFromString(result.data, 'text/html') | ||
const bodyNode = rootNode.querySelector('body') | ||
const { currentPageId } = app | ||
// Edge on Windows 11 home appears to paste a link as a single <a/> in | ||
// the HTML document. If we're pasting a single like tag we'll just | ||
// assume the user meant to paste the URL. | ||
const isHtmlSingleLink = | ||
bodyNode && | ||
Array.from(bodyNode.children).filter((el) => el.nodeType === 1).length === 1 && | ||
bodyNode.firstElementChild && | ||
bodyNode.firstElementChild.tagName === 'A' && | ||
bodyNode.firstElementChild.hasAttribute('href') && | ||
bodyNode.firstElementChild.getAttribute('href') !== '' | ||
let index = 'a1' | ||
const excElementIdsToTldrawShapeIds = new Map<string, TLShapeId>() | ||
const rootShapeIds: TLShapeId[] = [] | ||
const skipIds = new Set<string>() | ||
elements.forEach((element: any) => { | ||
excElementIdsToTldrawShapeIds.set(element.id, app.createShapeId()) | ||
if (element.boundElements !== null) { | ||
for (const boundElement of element.boundElements) { | ||
if (boundElement.type === 'text') { | ||
skipIds.add(boundElement.id) | ||
} | ||
if (isHtmlSingleLink) { | ||
const href = bodyNode.firstElementChild.getAttribute('href')! | ||
handleText(editor, href, point) | ||
return | ||
} | ||
} | ||
}) | ||
for (const element of elements) { | ||
if (skipIds.has(element.id)) { | ||
continue | ||
} | ||
const id = excElementIdsToTldrawShapeIds.get(element.id)! | ||
const base = { | ||
id, | ||
typeName: 'shape', | ||
parentId: currentPageId, | ||
index, | ||
x: element.x, | ||
y: element.y, | ||
rotation: 0, | ||
isLocked: element.locked, | ||
} as const | ||
if (element.angle !== 0) { | ||
rotatedElements.set(id, element.angle) | ||
} | ||
if (element.groupIds && element.groupIds.length > 0) { | ||
if (groupShapeIdToChildren.has(element.groupIds[0])) { | ||
groupShapeIdToChildren.get(element.groupIds[0])?.push(id) | ||
} else { | ||
groupShapeIdToChildren.set(element.groupIds[0], [id]) | ||
// If the html is NOT a link, and we have NO OTHER texty content, then paste the html as text | ||
if (!results.some((r) => r.type === 'text' && r.subtype !== 'html') && result.data.trim()) { | ||
handleText(editor, stripHtml(result.data), point) | ||
return | ||
} | ||
} else { | ||
rootShapeIds.push(id) | ||
} | ||
switch (element.type) { | ||
case 'rectangle': | ||
case 'ellipse': | ||
case 'diamond': { | ||
let text = '' | ||
let align: TLAlignType = 'middle' | ||
if (element.boundElements !== null) { | ||
for (const boundElement of element.boundElements) { | ||
if (boundElement.type === 'text') { | ||
const labelElement = elements.find((elm: any) => elm.id === boundElement.id) | ||
if (labelElement) { | ||
text = labelElement.text | ||
align = textAlignToAlignTypes[labelElement.textAlign] | ||
} | ||
} | ||
} | ||
} | ||
const colorToUse = | ||
element.backgroundColor === 'transparent' ? element.strokeColor : element.backgroundColor | ||
tldrawContent.shapes.push({ | ||
...base, | ||
type: 'geo', | ||
props: { | ||
geo: element.type, | ||
opacity: getOpacity(element.opacity), | ||
url: element.link ?? '', | ||
w: element.width, | ||
h: element.height, | ||
size: strokeWidthsToSizes[element.strokeWidth] ?? 'draw', | ||
color: colorsToColors[colorToUse] ?? 'black', | ||
text, | ||
align, | ||
dash: getDash(element), | ||
fill: getFill(element), | ||
}, | ||
}) | ||
break | ||
} | ||
case 'freedraw': { | ||
tldrawContent.shapes.push({ | ||
...base, | ||
type: 'draw', | ||
props: { | ||
dash: getDash(element), | ||
size: strokeWidthsToSizes[element.strokeWidth], | ||
opacity: getOpacity(element.opacity), | ||
color: colorsToColors[element.strokeColor] ?? 'black', | ||
segments: [ | ||
{ | ||
type: 'free', | ||
points: element.points.map(([x, y, z = 0.5]: number[]) => ({ | ||
x, | ||
y, | ||
z, | ||
})), | ||
}, | ||
], | ||
}, | ||
}) | ||
break | ||
} | ||
case 'line': { | ||
const start = element.points[0] | ||
const end = element.points[element.points.length - 1] | ||
const indices = getIndices(element.points.length) | ||
tldrawContent.shapes.push({ | ||
...base, | ||
type: 'line', | ||
props: { | ||
dash: getDash(element), | ||
size: strokeWidthsToSizes[element.strokeWidth], | ||
opacity: getOpacity(element.opacity), | ||
color: colorsToColors[element.strokeColor] ?? 'black', | ||
spline: element.roundness ? 'cubic' : 'line', | ||
handles: { | ||
start: { | ||
id: 'start', | ||
type: 'vertex', | ||
index: indices[0], | ||
x: start[0], | ||
y: start[1], | ||
}, | ||
end: { | ||
id: 'end', | ||
type: 'vertex', | ||
index: indices[indices.length - 1], | ||
x: end[0], | ||
y: end[1], | ||
}, | ||
...Object.fromEntries( | ||
element.points.slice(1, -1).map(([x, y]: number[], i: number) => { | ||
const id = uniqueId() | ||
return [ | ||
id, | ||
{ | ||
id, | ||
type: 'vertex', | ||
index: indices[i + 1], | ||
x, | ||
y, | ||
}, | ||
] | ||
}) | ||
), | ||
}, | ||
}, | ||
}) | ||
break | ||
} | ||
case 'arrow': { | ||
let text = '' | ||
if (element.boundElements !== null) { | ||
for (const boundElement of element.boundElements) { | ||
if (boundElement.type === 'text') { | ||
const labelElement = elements.find((elm: any) => elm.id === boundElement.id) | ||
if (labelElement) { | ||
text = labelElement.text | ||
} | ||
} | ||
} | ||
} | ||
const start = element.points[0] | ||
const end = element.points[element.points.length - 1] | ||
const startTargetId = excElementIdsToTldrawShapeIds.get(element.startBinding?.elementId) | ||
const endTargetId = excElementIdsToTldrawShapeIds.get(element.endBinding?.elementId) | ||
tldrawContent.shapes.push({ | ||
...base, | ||
type: 'arrow', | ||
props: { | ||
text, | ||
bend: getBend(element, start, end), | ||
dash: getDash(element), | ||
opacity: getOpacity(element.opacity), | ||
size: strokeWidthsToSizes[element.strokeWidth] ?? 'm', | ||
color: colorsToColors[element.strokeColor] ?? 'black', | ||
start: startTargetId | ||
? { | ||
type: 'binding', | ||
boundShapeId: startTargetId, | ||
normalizedAnchor: { x: 0.5, y: 0.5 }, | ||
isExact: false, | ||
} | ||
: { | ||
type: 'point', | ||
x: start[0], | ||
y: start[1], | ||
}, | ||
end: endTargetId | ||
? { | ||
type: 'binding', | ||
boundShapeId: endTargetId, | ||
normalizedAnchor: { x: 0.5, y: 0.5 }, | ||
isExact: false, | ||
} | ||
: { | ||
type: 'point', | ||
x: end[0], | ||
y: end[1], | ||
}, | ||
arrowheadEnd: arrowheadsToArrowheadTypes[element.endArrowhead] ?? 'none', | ||
arrowheadStart: arrowheadsToArrowheadTypes[element.startArrowhead] ?? 'none', | ||
}, | ||
}) | ||
break | ||
} | ||
case 'text': { | ||
const { size, scale } = getFontSizeAndScale(element.fontSize) | ||
tldrawContent.shapes.push({ | ||
...base, | ||
type: 'text', | ||
props: { | ||
size, | ||
scale, | ||
font: fontFamilyToFontType[element.fontFamily] ?? 'draw', | ||
opacity: getOpacity(element.opacity), | ||
color: colorsToColors[element.strokeColor] ?? 'black', | ||
text: element.text, | ||
align: textAlignToAlignTypes[element.textAlign], | ||
}, | ||
}) | ||
break | ||
} | ||
case 'image': { | ||
const file = files[element.fileId] | ||
if (!file) break | ||
const assetId: TLAssetId = TLAsset.createId() | ||
tldrawContent.assets.push({ | ||
id: assetId, | ||
typeName: 'asset', | ||
type: 'image', | ||
props: { | ||
w: element.width, | ||
h: element.height, | ||
name: element.id ?? 'Untitled', | ||
isAnimated: false, | ||
mimeType: file.mimeType, | ||
src: file.dataURL, | ||
}, | ||
}) | ||
tldrawContent.shapes.push({ | ||
...base, | ||
type: 'image', | ||
props: { | ||
opacity: getOpacity(element.opacity), | ||
w: element.width, | ||
h: element.height, | ||
assetId, | ||
}, | ||
}) | ||
} | ||
} | ||
index = getIndexAbove(index) | ||
} | ||
const p = point ?? (app.inputs.shiftKey ? app.inputs.currentPagePoint : undefined) | ||
app.mark('paste') | ||
app.putContent(tldrawContent, { | ||
point: p, | ||
select: false, | ||
preserveIds: true, | ||
}) | ||
for (const groupedShapeIds of groupShapeIdToChildren.values()) { | ||
if (groupedShapeIds.length > 1) { | ||
app.groupShapes(groupedShapeIds) | ||
const groupShape = app.getShapeById(groupedShapeIds[0]) | ||
if (groupShape?.parentId && isShapeId(groupShape.parentId)) { | ||
rootShapeIds.push(groupShape.parentId) | ||
} | ||
// Try to paste a link | ||
for (const result of results) { | ||
if (result.type === 'text' && result.subtype === 'url') { | ||
pasteUrl(editor, result.data, point) | ||
return | ||
} | ||
} | ||
for (const [id, angle] of rotatedElements) { | ||
app.select(id) | ||
app.rotateShapesBy([id], angle) | ||
} | ||
const rootShapes = compact(rootShapeIds.map((id) => app.getShapeById(id))) | ||
const bounds = Box2d.Common(rootShapes.map((s) => app.getPageBounds(s)!)) | ||
const viewPortCenter = app.viewportPageBounds.center | ||
app.updateShapes( | ||
rootShapes.map((s) => { | ||
const delta = { | ||
x: (s.x ?? 0) - (bounds.x + bounds.w / 2), | ||
y: (s.y ?? 0) - (bounds.y + bounds.h / 2), | ||
} | ||
return { | ||
id: s.id, | ||
type: s.type, | ||
x: viewPortCenter.x + delta.x, | ||
y: viewPortCenter.y + delta.y, | ||
} | ||
}) | ||
) | ||
app.setSelectedIds(rootShapeIds) | ||
} | ||
const handleFilesBlob = async (app: App, blobs: Blob[], point?: VecLike) => { | ||
const urls = blobs.map((blob) => URL.createObjectURL(blob)) | ||
pasteFiles(app, urls, point) | ||
} | ||
const handleHtmlString = async (app: App, html: string, point?: VecLike) => { | ||
const s = html.match(/<tldraw[^>]*>(.*)<\/tldraw>/)?.[1] | ||
if (s) { | ||
try { | ||
const json = JSON.parse(decompressFromBase64(s)!) | ||
if (json.type === 'application/tldraw') { | ||
pasteTldrawContent(app, json.data, point) | ||
} else { | ||
pasteText(app, s, point) | ||
} | ||
} catch (error) { | ||
pasteText(app, s, point) | ||
// Finally, if we haven't bailed on anything yet, we can paste text content | ||
for (const result of results) { | ||
if (result.type === 'text' && result.subtype === 'text' && result.data.trim()) { | ||
// The clipboard may include multiple text items, but we only want to paste the first one | ||
handleText(editor, result.data, point) | ||
return | ||
} | ||
} else { | ||
const rootNode = new DOMParser().parseFromString(html, 'text/html') | ||
const bodyNode = rootNode.querySelector('body') | ||
// Edge on Windows 11 home appears to paste a link as a single <a/> in | ||
// the HTML document. If we're pasting a single like tag we'll just | ||
// assume the user meant to paste the URL. | ||
const isHtmlSingleLink = | ||
bodyNode && | ||
Array.from(bodyNode.children).filter((el) => el.nodeType === 1).length === 1 && | ||
bodyNode.firstElementChild && | ||
bodyNode.firstElementChild.tagName === 'A' && | ||
bodyNode.firstElementChild.hasAttribute('href') && | ||
bodyNode.firstElementChild.getAttribute('href') !== '' | ||
if (isHtmlSingleLink) { | ||
const href = bodyNode.firstElementChild.getAttribute('href')! | ||
pasteText(app, href, point) | ||
} else { | ||
pasteText(app, html, point) | ||
} | ||
} | ||
} | ||
const handleTextString = async (app: App, text: string, point?: VecLike) => { | ||
const s = text.trim() | ||
const tldrawContent = text.match(/<tldraw[^>]*>(.*)<\/tldraw>/)?.[1] | ||
if (tldrawContent) { | ||
handleHtmlString(app, text) | ||
} else if (s) { | ||
try { | ||
const json = JSON.parse(s) | ||
if (json.type === 'application/tldraw') { | ||
pasteTldrawContent(app, json.data, point) | ||
} else if (json.type === 'excalidraw/clipboard') { | ||
pasteExcalidrawContent(app, json, point) | ||
} else { | ||
pasteText(app, s, point) | ||
} | ||
} catch (error) { | ||
pasteText(app, s, point) | ||
/** | ||
* When the user copies, write the contents to local storage and to the clipboard | ||
* | ||
* @param editor - The editor instance. | ||
* @public | ||
*/ | ||
const handleNativeOrMenuCopy = (editor: Editor) => { | ||
const content = editor.getContent() | ||
if (!content) { | ||
if (navigator && navigator.clipboard) { | ||
navigator.clipboard.writeText('') | ||
} | ||
return | ||
} | ||
} | ||
const handleNativeDataTransferPaste = async ( | ||
app: App, | ||
clipboardData: DataTransfer, | ||
point?: VecLike | ||
) => { | ||
// Do not paste while in any editing state | ||
if (app.isIn('select.editing')) return | ||
if (clipboardData) { | ||
const items = Object.values(clipboardData.items) | ||
// In some cases, the clipboard will contain both the name of a file and the file itself | ||
// we need to avoid writing a text shape for the name AND an image or video shape for the file | ||
const writingFile = items.some((item) => item.kind === 'file') | ||
// If we're pasting in tldraw content (shapes, etc) then the clipboard may | ||
// contain both text content. We'll only paste the content. | ||
const writingContent = items.some((item) => item.type === 'text/html') | ||
// We need to handle files separately because if we want them to | ||
// be placed next to each other, we need to create them all at once | ||
const files: Blob[] = [] | ||
const text: DataTransferItem[] = [] | ||
items.forEach((item) => { | ||
if (item.kind === 'file') { | ||
const file = item.getAsFile() | ||
if (file) { | ||
files.push(file) | ||
} | ||
} else if (item.kind === 'string') { | ||
text.push(item) | ||
} | ||
const stringifiedClipboard = compressToBase64( | ||
JSON.stringify({ | ||
type: 'application/tldraw', | ||
kind: 'content', | ||
data: content, | ||
}) | ||
) | ||
if (files.length > 0) { | ||
handleFilesBlob(app, files, point) | ||
} | ||
for (const item of text) { | ||
if (!writingFile && item.type === 'text/html') { | ||
await handleHtmlString(app, await dataTransferItemAsString(item), point) | ||
} else if (item.type === 'text/plain') { | ||
if (!writingContent) { | ||
await handleTextString(app, await dataTransferItemAsString(item), point) | ||
if (typeof navigator === 'undefined') { | ||
return | ||
} else { | ||
// Extract the text from the clipboard | ||
const textItems = content.shapes | ||
.map((shape) => { | ||
if ( | ||
editor.isShapeOfType(shape, TextShapeUtil) || | ||
editor.isShapeOfType(shape, GeoShapeUtil) || | ||
editor.isShapeOfType(shape, ArrowShapeUtil) | ||
) { | ||
return shape.props.text | ||
} | ||
} | ||
} | ||
} | ||
} | ||
if ( | ||
editor.isShapeOfType(shape, BookmarkShapeUtil) || | ||
editor.isShapeOfType(shape, EmbedShapeUtil) | ||
) { | ||
return shape.props.url | ||
} | ||
return null | ||
}) | ||
.filter(isNonNull) | ||
const handleNativeClipboardPaste = async ( | ||
app: App, | ||
clipboardItems: ClipboardItem[], | ||
point?: VecLike | ||
) => { | ||
// Do not paste while in any editing state | ||
if (app.isIn('select.editing')) return | ||
if (navigator.clipboard?.write) { | ||
const htmlBlob = new Blob([`<tldraw>${stringifiedClipboard}</tldraw>`], { | ||
type: 'text/html', | ||
}) | ||
const isFile = (item: ClipboardItem) => { | ||
return item.types.find((i) => i.match(/^image\//)) | ||
} | ||
let textContent = textItems.join(' ') | ||
// In some cases, the clipboard will contain both the name of a file and the file itself | ||
// we need to avoid writing a text shape for the name AND an image or video shape for the file | ||
const writingFile = clipboardItems.some((item) => isFile(item)) | ||
// If we're pasting in tldraw content (shapes, etc) then the clipboard may | ||
// contain both text content. We'll only paste the content. | ||
const writingContent = clipboardItems.some((item) => item.types.includes('text/html')) | ||
// We need to handle files separately because if we want them to | ||
// be placed next to each other, we need to create them all at once | ||
const files: ClipboardItem[] = clipboardItems.filter((item) => { | ||
if (item.types.find((i) => i.match(/^image\//))) { | ||
return true | ||
} | ||
return false | ||
}) | ||
await Promise.all( | ||
files.map(async (item) => { | ||
const type = item.types.find((t) => t !== 'text/plain' && t !== 'text/html') | ||
if (type) { | ||
const file = await item.getType(type) | ||
if (file) { | ||
await handleFilesBlob(app, [file], point) | ||
} | ||
// This is a bug in chrome android where it won't paste content if | ||
// the text/plain content is "" so we need to always add an empty | ||
// space 🤬 | ||
if (textContent === '') { | ||
textContent = ' ' | ||
} | ||
}) | ||
) | ||
for (const item of clipboardItems) { | ||
if (item.types.includes('text/html')) { | ||
if (writingFile) break | ||
const blob = await item.getType('text/html') | ||
await handleHtmlString(app, await blobAsString(blob), point) | ||
} else if (item.types.includes('text/uri-list')) { | ||
if (writingContent) break | ||
const blob = await item.getType('text/uri-list') | ||
await pasteUrl(app, await blobAsString(blob), point) | ||
} else if (item.types.includes('text/plain')) { | ||
if (writingContent) break | ||
const blob = await item.getType('text/plain') | ||
await handleTextString(app, await blobAsString(blob), point) | ||
navigator.clipboard.write([ | ||
new ClipboardItem({ | ||
'text/html': htmlBlob, | ||
// What is this second blob used for? | ||
'text/plain': new Blob([textContent], { type: 'text/plain' }), | ||
}), | ||
]) | ||
} else if (navigator.clipboard.writeText) { | ||
navigator.clipboard.writeText(`<tldraw>${stringifiedClipboard}</tldraw>`) | ||
} | ||
@@ -972,36 +557,48 @@ } | ||
export function useMenuClipboardEvents() { | ||
const app = useApp() | ||
const editor = useEditor() | ||
const trackEvent = useEvents() | ||
const copy = useCallback( | ||
function onCopy() { | ||
if (app.selectedIds.length === 0) return | ||
handleMenuCopy(app) | ||
function onCopy(source: TLUiEventSource) { | ||
if (editor.selectedIds.length === 0) return | ||
handleNativeOrMenuCopy(editor) | ||
trackEvent('copy', { source }) | ||
}, | ||
[app] | ||
[editor, trackEvent] | ||
) | ||
const cut = useCallback( | ||
function onCut() { | ||
if (app.selectedIds.length === 0) return | ||
handleMenuCopy(app) | ||
app.deleteShapes() | ||
function onCut(source: TLUiEventSource) { | ||
if (editor.selectedIds.length === 0) return | ||
handleNativeOrMenuCopy(editor) | ||
editor.deleteShapes() | ||
trackEvent('cut', { source }) | ||
}, | ||
[app] | ||
[editor, trackEvent] | ||
) | ||
const paste = useCallback( | ||
async function onPaste(data: DataTransfer | ClipboardItem[], point?: VecLike) { | ||
async function onPaste( | ||
data: DataTransfer | ClipboardItem[], | ||
source: TLUiEventSource, | ||
point?: VecLike | ||
) { | ||
// If we're editing a shape, or we are focusing an editable input, then | ||
// we would want the user's paste interaction to go to that element or | ||
// input instead; e.g. when pasting text into a text shape's content | ||
if (editor.editingId !== null || disallowClipboardEvents(editor)) return | ||
if (Array.isArray(data) && data[0] instanceof ClipboardItem) { | ||
handleNativeClipboardPaste(app, data, point) | ||
handlePasteFromClipboardApi(editor, data, point) | ||
trackEvent('paste', { source: 'menu' }) | ||
} else { | ||
// Read it first and then recurse, kind of weird | ||
navigator.clipboard.read().then((clipboardItems) => { | ||
paste(clipboardItems, app.inputs.currentPagePoint) | ||
paste(clipboardItems, source, point) | ||
}) | ||
} | ||
// else { | ||
// handleScenePaste(app, point) | ||
// } | ||
}, | ||
[app] | ||
[editor, trackEvent] | ||
) | ||
@@ -1018,5 +615,6 @@ | ||
export function useNativeClipboardEvents() { | ||
const app = useApp() | ||
const editor = useEditor() | ||
const trackEvent = useEvents() | ||
const appIsFocused = useAppIsFocused() | ||
const appIsFocused = useEditorIsFocused() | ||
@@ -1026,25 +624,58 @@ useEffect(() => { | ||
const copy = () => { | ||
if (app.selectedIds.length === 0 || app.editingId !== null || disallowClipboardEvents(app)) | ||
if ( | ||
editor.selectedIds.length === 0 || | ||
editor.editingId !== null || | ||
disallowClipboardEvents(editor) | ||
) | ||
return | ||
handleMenuCopy(app) | ||
handleNativeOrMenuCopy(editor) | ||
trackEvent('copy', { source: 'kbd' }) | ||
} | ||
function cut() { | ||
if (app.selectedIds.length === 0 || app.editingId !== null || disallowClipboardEvents(app)) | ||
if ( | ||
editor.selectedIds.length === 0 || | ||
editor.editingId !== null || | ||
disallowClipboardEvents(editor) | ||
) | ||
return | ||
handleMenuCopy(app) | ||
app.deleteShapes() | ||
handleNativeOrMenuCopy(editor) | ||
editor.deleteShapes() | ||
trackEvent('cut', { source: 'kbd' }) | ||
} | ||
let disablingMiddleClickPaste = false | ||
const pointerUpHandler = (e: PointerEvent) => { | ||
if (e.button === 1) { | ||
disablingMiddleClickPaste = true | ||
requestAnimationFrame(() => { | ||
disablingMiddleClickPaste = false | ||
}) | ||
} | ||
} | ||
const paste = (event: ClipboardEvent) => { | ||
if (app.editingId !== null || disallowClipboardEvents(app)) return | ||
if (event.clipboardData && !app.inputs.shiftKey) { | ||
handleNativeDataTransferPaste(app, event.clipboardData) | ||
if (disablingMiddleClickPaste) { | ||
event.stopPropagation() | ||
return | ||
} | ||
// If we're editing a shape, or we are focusing an editable input, then | ||
// we would want the user's paste interaction to go to that element or | ||
// input instead; e.g. when pasting text into a text shape's content | ||
if (editor.editingId !== null || disallowClipboardEvents(editor)) return | ||
// First try to use the clipboard data on the event | ||
if (event.clipboardData && !editor.inputs.shiftKey) { | ||
handlePasteFromEventClipboardData(editor, event.clipboardData) | ||
} else { | ||
// Or else use the clipboard API | ||
navigator.clipboard.read().then((clipboardItems) => { | ||
if (Array.isArray(clipboardItems) && clipboardItems[0] instanceof ClipboardItem) { | ||
handleNativeClipboardPaste(app, clipboardItems, app.inputs.currentPagePoint) | ||
handlePasteFromClipboardApi(editor, clipboardItems, editor.inputs.currentPagePoint) | ||
} | ||
}) | ||
} | ||
trackEvent('paste', { source: 'kbd' }) | ||
} | ||
@@ -1055,2 +686,3 @@ | ||
document.addEventListener('paste', paste) | ||
document.addEventListener('pointerup', pointerUpHandler) | ||
@@ -1061,4 +693,5 @@ return () => { | ||
document.removeEventListener('paste', paste) | ||
document.removeEventListener('pointerup', pointerUpHandler) | ||
} | ||
}, [app, appIsFocused]) | ||
}, [editor, trackEvent, appIsFocused]) | ||
} |
@@ -1,2 +0,9 @@ | ||
import { App, getSvgAsImage, getSvgAsString, TLCopyType, TLShapeId, useApp } from '@tldraw/editor' | ||
import { | ||
Editor, | ||
getSvgAsImage, | ||
getSvgAsString, | ||
TLCopyType, | ||
TLShapeId, | ||
useEditor, | ||
} from '@tldraw/editor' | ||
import { useCallback } from 'react' | ||
@@ -8,3 +15,3 @@ import { useToasts } from './useToastsProvider' | ||
export function useCopyAs() { | ||
const app = useApp() | ||
const editor = useEditor() | ||
const { addToast } = useToasts() | ||
@@ -21,5 +28,5 @@ const msg = useTranslation() | ||
// little awkward. | ||
function copyAs(ids: TLShapeId[] = app.selectedIds, format: TLCopyType = 'svg') { | ||
function copyAs(ids: TLShapeId[] = editor.selectedIds, format: TLCopyType = 'svg') { | ||
if (ids.length === 0) { | ||
ids = [...app.shapeIds] | ||
ids = [...editor.currentPageShapeIds] | ||
} | ||
@@ -37,3 +44,3 @@ | ||
new ClipboardItem({ | ||
'text/plain': getExportedSvgBlob(app, ids), | ||
'text/plain': getExportedSvgBlob(editor, ids), | ||
}), | ||
@@ -43,3 +50,3 @@ ]) | ||
fallbackWriteTextAsync(async () => | ||
getSvgAsString(await getExportSvgElement(app, ids, format)) | ||
getSvgAsString(await getExportSvgElement(editor, ids)) | ||
) | ||
@@ -54,25 +61,43 @@ } | ||
const mimeType = format === 'jpeg' ? 'image/jpeg' : 'image/png' | ||
const blobPromise = getExportedImageBlob(editor, ids, format).then((blob) => { | ||
if (blob) { | ||
if (window.navigator.clipboard) { | ||
return blob | ||
} | ||
throw new Error('Copy not supported') | ||
} else { | ||
addToast({ | ||
id: 'copy-fail', | ||
icon: 'warning-triangle', | ||
title: msg('toast.error.copy-fail.title'), | ||
description: msg('toast.error.copy-fail.desc'), | ||
}) | ||
throw new Error('Copy not possible') | ||
} | ||
}) | ||
window.navigator.clipboard.write([ | ||
new ClipboardItem({ | ||
// Note: This needs to use the promise based approach for safari/ios to not bail on a permissions error. | ||
[mimeType]: getExportedImageBlob(app, ids, format).then((blob) => { | ||
if (blob) { | ||
if (window.navigator.clipboard) { | ||
return blob | ||
} | ||
throw new Error('Copy not supported') | ||
} else { | ||
addToast({ | ||
id: 'copy-fail', | ||
icon: 'warning-triangle', | ||
title: msg('toast.error.copy-fail.title'), | ||
description: msg('toast.error.copy-fail.desc'), | ||
}) | ||
throw new Error('Copy not possible') | ||
} | ||
window.navigator.clipboard | ||
.write([ | ||
new ClipboardItem({ | ||
// Note: This needs to use the promise based approach for safari/ios to not bail on a permissions error. | ||
[mimeType]: blobPromise, | ||
}), | ||
}), | ||
]) | ||
]) | ||
.catch((err: any) => { | ||
// Firefox will fail with the above if `dom.events.asyncClipboard.clipboardItem` is enabled. | ||
// See <https://github.com/tldraw/tldraw/issues/1325> | ||
if (!err.toString().match(/^TypeError: DOMString not supported/)) { | ||
console.error(err) | ||
} | ||
blobPromise.then((blob) => { | ||
window.navigator.clipboard.write([ | ||
new ClipboardItem({ | ||
// Note: This needs to use the promise based approach for safari/ios to not bail on a permissions error. | ||
[mimeType]: blob, | ||
}), | ||
]) | ||
}) | ||
}) | ||
break | ||
@@ -82,3 +107,3 @@ } | ||
case 'json': { | ||
const data = app.getContent(ids) | ||
const data = editor.getContent(ids) | ||
@@ -105,10 +130,10 @@ if (window.navigator.clipboard) { | ||
}, | ||
[app, addToast, msg] | ||
[editor, addToast, msg] | ||
) | ||
} | ||
async function getExportSvgElement(app: App, ids: TLShapeId[], format: TLCopyType) { | ||
const svg = await app.getSvg(ids, { | ||
scale: format === 'svg' ? 1 : 2, | ||
background: app.instanceState.exportBackground, | ||
async function getExportSvgElement(editor: Editor, ids: TLShapeId[]) { | ||
const svg = await editor.getSvg(ids, { | ||
scale: 1, | ||
background: editor.instanceState.exportBackground, | ||
}) | ||
@@ -121,4 +146,4 @@ | ||
async function getExportedSvgBlob(app: App, ids: TLShapeId[]) { | ||
return new Blob([getSvgAsString(await getExportSvgElement(app, ids, 'svg'))], { | ||
async function getExportedSvgBlob(editor: Editor, ids: TLShapeId[]) { | ||
return new Blob([getSvgAsString(await getExportSvgElement(editor, ids))], { | ||
type: 'text/plain', | ||
@@ -128,7 +153,7 @@ }) | ||
async function getExportedImageBlob(app: App, ids: TLShapeId[], format: 'png' | 'jpeg') { | ||
return await getSvgAsImage(await getExportSvgElement(app, ids, format), { | ||
async function getExportedImageBlob(editor: Editor, ids: TLShapeId[], format: 'png' | 'jpeg') { | ||
return await getSvgAsImage(await getExportSvgElement(editor, ids), { | ||
type: format, | ||
quality: 1, | ||
scale: 1, | ||
scale: 2, | ||
}) | ||
@@ -138,3 +163,4 @@ } | ||
async function fallbackWriteTextAsync(getText: () => Promise<string>) { | ||
if (!(navigator && navigator.clipboard)) return | ||
navigator.clipboard.writeText(await getText()) | ||
} |
import { | ||
FrameShapeUtil, | ||
TLExportType, | ||
TLShapeId, | ||
downloadDataURLAsFile, | ||
getSvgAsDataUrl, | ||
getSvgAsImage, | ||
TLExportType, | ||
TLShapeId, | ||
useApp, | ||
useEditor, | ||
} from '@tldraw/editor' | ||
@@ -15,3 +16,3 @@ import { useCallback } from 'react' | ||
export function useExportAs() { | ||
const app = useApp() | ||
const editor = useEditor() | ||
const { addToast } = useToasts() | ||
@@ -21,5 +22,5 @@ const msg = useTranslation() | ||
return useCallback( | ||
async function exportAs(ids: TLShapeId[] = app.selectedIds, format: TLExportType = 'png') { | ||
async function exportAs(ids: TLShapeId[] = editor.selectedIds, format: TLExportType = 'png') { | ||
if (ids.length === 0) { | ||
ids = [...app.shapeIds] | ||
ids = [...editor.currentPageShapeIds] | ||
} | ||
@@ -31,5 +32,5 @@ | ||
const svg = await app.getSvg(ids, { | ||
scale: format === 'svg' ? 1 : 2, | ||
background: app.instanceState.exportBackground, | ||
const svg = await editor.getSvg(ids, { | ||
scale: 1, | ||
background: editor.instanceState.exportBackground, | ||
}) | ||
@@ -39,4 +40,13 @@ | ||
const name = ids.length === 1 ? app.getShapeById(ids[0])?.id.replace(/:/, '_') : 'shapes' | ||
let name = 'shapes' | ||
if (ids.length === 1) { | ||
const first = editor.getShapeById(ids[0])! | ||
if (editor.isShapeOfType(first, FrameShapeUtil)) { | ||
name = first.props.name ?? 'frame' | ||
} else { | ||
name = first.id.replace(/:/, '_') | ||
} | ||
} | ||
switch (format) { | ||
@@ -53,3 +63,3 @@ case 'svg': { | ||
quality: 1, | ||
scale: 1, | ||
scale: 2, | ||
}) | ||
@@ -70,2 +80,3 @@ | ||
downloadDataURLAsFile(dataURL, `${name || 'shapes'}.${format}`) | ||
URL.revokeObjectURL(dataURL) | ||
@@ -76,3 +87,3 @@ return | ||
case 'json': { | ||
const data = app.getContent(ids) | ||
const data = editor.getContent(ids) | ||
const dataURL = URL.createObjectURL( | ||
@@ -83,2 +94,3 @@ new Blob([JSON.stringify(data, null, 4)], { type: 'application/json' }) | ||
downloadDataURLAsFile(dataURL, `${name || 'shapes'}.json`) | ||
URL.revokeObjectURL(dataURL) | ||
@@ -92,4 +104,4 @@ return | ||
}, | ||
[app, addToast, msg] | ||
[editor, addToast, msg] | ||
) | ||
} |
@@ -1,10 +0,10 @@ | ||
import { useApp } from '@tldraw/editor' | ||
import { useValue } from 'signia-react' | ||
import { useEditor } from '@tldraw/editor' | ||
import { useValue } from '@tldraw/state' | ||
export function useHasLinkShapeSelected() { | ||
const app = useApp() | ||
const editor = useEditor() | ||
return useValue( | ||
'hasLinkShapeSelected', | ||
() => { | ||
const { selectedShapes } = app | ||
const { selectedShapes } = editor | ||
return ( | ||
@@ -16,4 +16,4 @@ selectedShapes.length === 1 && | ||
}, | ||
[app] | ||
[editor] | ||
) | ||
} |
import { useLayoutEffect } from 'react' | ||
/** @public */ | ||
/** @internal */ | ||
export function useHighDpiCanvas(ref: React.RefObject<HTMLCanvasElement>, dpr: number) { | ||
@@ -5,0 +5,0 @@ // Match the resolution of the client |
@@ -1,6 +0,6 @@ | ||
import { ACCEPTED_ASSET_TYPE, createShapesFromFiles, useApp } from '@tldraw/editor' | ||
import { ACCEPTED_ASSET_TYPE, useEditor } from '@tldraw/editor' | ||
import { useCallback, useEffect, useRef } from 'react' | ||
export function useInsertMedia() { | ||
const app = useApp() | ||
const editor = useEditor() | ||
const inputRef = useRef<HTMLInputElement>() | ||
@@ -17,3 +17,8 @@ | ||
if (!fileList || fileList.length === 0) return | ||
await createShapesFromFiles(app, Array.from(fileList), app.viewportPageBounds.center, false) | ||
await editor.putExternalContent({ | ||
type: 'files', | ||
files: Array.from(fileList), | ||
point: editor.viewportPageBounds.center, | ||
ignoreParent: false, | ||
}) | ||
input.value = '' | ||
@@ -26,3 +31,3 @@ } | ||
} | ||
}, [app]) | ||
}, [editor]) | ||
@@ -29,0 +34,0 @@ return useCallback(() => { |
@@ -1,6 +0,6 @@ | ||
import { preventDefault, useApp } from '@tldraw/editor' | ||
import { preventDefault, useEditor } from '@tldraw/editor' | ||
import hotkeys from 'hotkeys-js' | ||
import { useEffect } from 'react' | ||
import { useActions } from './useActions' | ||
import { useAppIsFocused } from './useAppIsFocused' | ||
import { useEditorIsFocused } from './useEditorIsFocused' | ||
import { useReadonly } from './useReadonly' | ||
@@ -20,5 +20,5 @@ import { useTools } from './useTools' | ||
export function useKeyboardShortcuts() { | ||
const app = useApp() | ||
const editor = useEditor() | ||
const appIsFocused = useAppIsFocused() | ||
const appIsFocused = useEditorIsFocused() | ||
const isReadonly = useReadonly() | ||
@@ -31,6 +31,6 @@ const actions = useActions() | ||
const container = app.getContainer() | ||
const container = editor.getContainer() | ||
const hot = (keys: string, callback: (event: KeyboardEvent) => void) => { | ||
hotkeys(keys, { element: container, scope: app.instanceId }, callback) | ||
hotkeys(keys, { element: container, scope: editor.store.id }, callback) | ||
} | ||
@@ -40,3 +40,4 @@ | ||
// Except those that in SKIP_KBDS! | ||
const areShortcutsDisabled = () => app.isMenuOpen || app.editingId !== null || app.crashingError | ||
const areShortcutsDisabled = () => | ||
editor.isMenuOpen || editor.editingId !== null || editor.crashingError | ||
@@ -51,3 +52,3 @@ for (const action of Object.values(actions)) { | ||
preventDefault(event) | ||
action.onSelect() | ||
action.onSelect('kbd') | ||
}) | ||
@@ -57,3 +58,3 @@ } | ||
for (const tool of Object.values(tools)) { | ||
if (!tool.kbd) continue | ||
if (!tool.kbd || (!tool.readonlyOk && editor.isReadOnly)) continue | ||
@@ -65,63 +66,57 @@ if (SKIP_KBDS.includes(tool.id)) continue | ||
preventDefault(event) | ||
tool.onSelect() | ||
tool.onSelect('kbd') | ||
}) | ||
} | ||
// Manually add in a few shortcuts that have "extra" kbds | ||
// todo: move these into the actions themselves and make the UI only display the first one | ||
hotkeys.setScope(editor.store.id) | ||
hot('g', () => { | ||
if (areShortcutsDisabled()) return | ||
app.setSelectedTool('geo') | ||
}) | ||
hot('backspace,del', () => { | ||
if (areShortcutsDisabled()) return | ||
actions['delete'].onSelect() | ||
}) | ||
hot('=', () => { | ||
if (areShortcutsDisabled()) return | ||
actions['zoom-in'].onSelect() | ||
}) | ||
hot('-', () => { | ||
if (areShortcutsDisabled()) return | ||
actions['zoom-out'].onSelect() | ||
}) | ||
hotkeys.setScope(app.instanceId) | ||
return () => { | ||
hotkeys.deleteScope(app.instanceId) | ||
hotkeys.deleteScope(editor.store.id) | ||
} | ||
}, [actions, tools, isReadonly, app, appIsFocused]) | ||
}, [actions, tools, isReadonly, editor, appIsFocused]) | ||
} | ||
function getHotkeysStringFromKbd(kbd: string) { | ||
let str = '' | ||
return getKeys(kbd) | ||
.map((kbd) => { | ||
let str = '' | ||
const chars = kbd.split('') | ||
if (chars.length === 1) { | ||
str = chars[0] | ||
} else { | ||
if (chars[0] === '!') { | ||
str = `shift+${chars[1]}` | ||
} else if (chars[0] === '?') { | ||
str = `alt+${chars[1]}` | ||
} else if (chars[0] === '$') { | ||
if (chars[1] === '!') { | ||
str = `cmd+shift+${chars[2]},ctrl+shift+${chars[2]}` | ||
} else if (chars[1] === '?') { | ||
str = `cmd+⌥+${chars[2]},ctrl+alt+${chars[2]}` | ||
} else { | ||
str = `cmd+${chars[1]},ctrl+${chars[1]}` | ||
} | ||
} else { | ||
str = kbd | ||
} | ||
} | ||
return str | ||
}) | ||
.join(',') | ||
} | ||
const chars = kbd.split('') | ||
// Logic to split kbd string from hotkeys-js util. | ||
function getKeys(key: string) { | ||
if (typeof key !== 'string') key = '' | ||
key = key.replace(/\s/g, '') | ||
const keys = key.split(',') | ||
let index = keys.lastIndexOf('') | ||
if (chars.length === 1) { | ||
str = chars[0] | ||
} else { | ||
if (chars[0] === '!') { | ||
str = `shift+${chars[1]}` | ||
} else if (chars[0] === '?') { | ||
str = `alt+${chars[1]}` | ||
} else if (chars[0] === '$') { | ||
if (chars[1] === '!') { | ||
str = `cmd+shift+${chars[2]},ctrl+shift+${chars[2]}` | ||
} else if (chars[1] === '?') { | ||
str = `cmd+⌥+${chars[2]},ctrl+alt+${chars[2]}` | ||
} else { | ||
str = `cmd+${chars[1]},ctrl+${chars[1]}` | ||
} | ||
} else { | ||
str = kbd | ||
} | ||
for (; index >= 0; ) { | ||
keys[index - 1] += ',' | ||
keys.splice(index, 1) | ||
index = keys.lastIndexOf('') | ||
} | ||
return str | ||
return keys | ||
} |
@@ -1,8 +0,11 @@ | ||
import { useApp } from '@tldraw/editor' | ||
import { useEditor } from '@tldraw/editor' | ||
import { useValue } from '@tldraw/state' | ||
import { useCallback, useEffect, useRef } from 'react' | ||
import { useEvents } from './useEventsProvider' | ||
/** @public */ | ||
export function useMenuIsOpen(id: string, cb?: (isOpen: boolean) => void) { | ||
const app = useApp() | ||
const editor = useEditor() | ||
const rIsOpen = useRef(false) | ||
const trackEvent = useEvents() | ||
@@ -12,17 +15,19 @@ const onOpenChange = useCallback( | ||
rIsOpen.current = isOpen | ||
if (isOpen) { | ||
app.complete() | ||
app.openMenus.add(id) | ||
} else { | ||
app.openMenus.delete(id) | ||
app.openMenus.forEach((menuId) => { | ||
if (menuId.startsWith(id)) { | ||
app.openMenus.delete(menuId) | ||
} | ||
}) | ||
} | ||
editor.batch(() => { | ||
if (isOpen) { | ||
editor.complete() | ||
editor.addOpenMenu(id) | ||
} else { | ||
editor.deleteOpenMenu(id) | ||
editor.openMenus.forEach((menuId) => { | ||
if (menuId.startsWith(id)) { | ||
editor.deleteOpenMenu(menuId) | ||
} | ||
}) | ||
} | ||
cb?.(isOpen) | ||
cb?.(isOpen) | ||
}) | ||
}, | ||
[app, id, cb] | ||
[editor, id, cb] | ||
) | ||
@@ -40,3 +45,4 @@ | ||
if (rIsOpen.current) { | ||
app.openMenus.add(id) | ||
trackEvent('open-menu', { source: 'unknown', id }) | ||
editor.addOpenMenu(id) | ||
} | ||
@@ -47,8 +53,9 @@ | ||
// Close menu on unmount | ||
app.openMenus.delete(id) | ||
editor.deleteOpenMenu(id) | ||
// Close menu and all submenus when the parent is closed | ||
app.openMenus.forEach((menuId) => { | ||
editor.openMenus.forEach((menuId) => { | ||
if (menuId.startsWith(id)) { | ||
app.openMenus.delete(menuId) | ||
trackEvent('close-menu', { source: 'unknown', id }) | ||
editor.deleteOpenMenu(menuId) | ||
} | ||
@@ -60,5 +67,7 @@ }) | ||
} | ||
}, [app, id]) | ||
}, [editor, id, trackEvent]) | ||
return onOpenChange | ||
const isOpen = useValue('is menu open', () => editor.openMenus.includes(id), [editor, id]) | ||
return [isOpen, onOpenChange] as const | ||
} |
@@ -1,17 +0,29 @@ | ||
import { isShapeWithHandles, useApp } from '@tldraw/editor' | ||
import { useValue } from 'signia-react' | ||
import { | ||
ArrowShapeUtil, | ||
DrawShapeUtil, | ||
GroupShapeUtil, | ||
LineShapeUtil, | ||
useEditor, | ||
} from '@tldraw/editor' | ||
import { useValue } from '@tldraw/state' | ||
export function useOnlyFlippableShape() { | ||
const app = useApp() | ||
const editor = useEditor() | ||
return useValue( | ||
'onlyFlippableShape', | ||
() => { | ||
const { selectedShapes } = app | ||
const { selectedShapes } = editor | ||
return ( | ||
selectedShapes.length === 1 && | ||
selectedShapes.every((shape) => shape.type === 'group' || isShapeWithHandles(shape)) | ||
selectedShapes.every( | ||
(shape) => | ||
editor.isShapeOfType(shape, GroupShapeUtil) || | ||
editor.isShapeOfType(shape, ArrowShapeUtil) || | ||
editor.isShapeOfType(shape, LineShapeUtil) || | ||
editor.isShapeOfType(shape, DrawShapeUtil) | ||
) | ||
) | ||
}, | ||
[app] | ||
[editor] | ||
) | ||
} |
import { useEffect, useState } from 'react' | ||
import { TLUiIconTypes } from '../icon-types' | ||
import { iconTypes } from '../icon-types' | ||
import { useAssetUrls } from './useAssetUrls' | ||
@@ -19,3 +19,3 @@ | ||
await Promise.allSettled( | ||
TLUiIconTypes.map((icon) => { | ||
iconTypes.map((icon) => { | ||
const image = new Image() | ||
@@ -22,0 +22,0 @@ image.src = assetUrls.icons[icon] |
@@ -1,7 +0,7 @@ | ||
import { uniqueId, useApp } from '@tldraw/editor' | ||
import { uniqueId, useEditor } from '@tldraw/editor' | ||
import { useCallback, useRef } from 'react' | ||
/** @public */ | ||
/** @internal */ | ||
export function usePrint() { | ||
const app = useApp() | ||
const editor = useEditor() | ||
const prevPrintEl = useRef<HTMLDivElement | null>(null) | ||
@@ -29,3 +29,3 @@ const prevStyleEl = useRef<HTMLStyleElement | null>(null) | ||
// Random because this isn't for end users | ||
const className = `rs-print-surface-${uniqueId()}` | ||
const className = `tl-print-surface-${uniqueId()}` | ||
@@ -133,4 +133,3 @@ el.className = className | ||
const afterPrintHandler = () => { | ||
// TODO: This is kind of lazy at the moment. I guess we need an event for 'something-happens-on-canvas' | ||
app.once('change-camera', () => { | ||
editor.once('change-history', () => { | ||
clearElements(el, style) | ||
@@ -162,6 +161,6 @@ }) | ||
function triggerPrint() { | ||
if (app.isChromeForIos) { | ||
if (editor.isChromeForIos) { | ||
beforePrintHandler() | ||
window.print() | ||
} else if (app.isSafari) { | ||
} else if (editor.isSafari) { | ||
beforePrintHandler() | ||
@@ -174,3 +173,3 @@ document.execCommand('print', false) | ||
const { pages, currentPageId, selectedIds } = app | ||
const { pages, currentPageId, selectedIds } = editor | ||
@@ -186,5 +185,5 @@ const preserveAspectRatio = 'xMidYMid meet' | ||
if (app.selectedIds.length > 0) { | ||
if (editor.selectedIds.length > 0) { | ||
// Print the selected ids from the current page | ||
const svg = await app.getSvg(selectedIds, svgOpts) | ||
const svg = await editor.getSvg(selectedIds, svgOpts) | ||
@@ -201,3 +200,3 @@ if (svg) { | ||
const page = pages[i] | ||
const svg = await app.getSvg(app.getSortedChildIds(page.id), svgOpts) | ||
const svg = await editor.getSvg(editor.getSortedChildIds(page.id), svgOpts) | ||
if (svg) { | ||
@@ -209,4 +208,4 @@ addPageToPrint(`tldraw — ${page.name}`, `${i}/${pages.length}`, svg) | ||
} else { | ||
const page = app.currentPage | ||
const svg = await app.getSvg(app.getSortedChildIds(page.id), svgOpts) | ||
const page = editor.currentPage | ||
const svg = await editor.getSvg(editor.getSortedChildIds(page.id), svgOpts) | ||
if (svg) { | ||
@@ -222,4 +221,4 @@ addPageToPrint(`tldraw — ${page.name}`, null, svg) | ||
}, | ||
[app] | ||
[editor] | ||
) | ||
} |
@@ -1,8 +0,8 @@ | ||
import { useApp } from '@tldraw/editor' | ||
import { useValue } from 'signia-react' | ||
import { useEditor } from '@tldraw/editor' | ||
import { useValue } from '@tldraw/state' | ||
/** @public */ | ||
export function useReadonly() { | ||
const app = useApp() | ||
return useValue('isReadOnlyMode', () => app.isReadOnly, [app]) | ||
const editor = useEditor() | ||
return useValue('isReadOnlyMode', () => editor.isReadOnly, [editor]) | ||
} |
@@ -1,18 +0,18 @@ | ||
import { useApp } from '@tldraw/editor' | ||
import { useValue } from 'signia-react' | ||
import { TextShapeUtil, useEditor } from '@tldraw/editor' | ||
import { useValue } from '@tldraw/state' | ||
export function useShowAutoSizeToggle() { | ||
const app = useApp() | ||
const editor = useEditor() | ||
return useValue( | ||
'showAutoSizeToggle', | ||
() => { | ||
const { selectedShapes } = app | ||
const { selectedShapes } = editor | ||
return ( | ||
selectedShapes.length === 1 && | ||
selectedShapes[0].type === 'text' && | ||
editor.isShapeOfType(selectedShapes[0], TextShapeUtil) && | ||
selectedShapes[0].props.autoSize === false | ||
) | ||
}, | ||
[app] | ||
[editor] | ||
) | ||
} |
@@ -46,6 +46,10 @@ // This file is automatically generated by scripts/refresh-assets.ts. | ||
'action.flip-vertical.short': 'Flip V', | ||
'action.fork-project': 'Fork this project', | ||
'action.group': 'Group', | ||
'action.insert-embed': 'Insert embed', | ||
'action.insert-media': 'Upload media', | ||
'action.leave-shared-project': 'Leave shared project', | ||
'action.new-project': 'New project', | ||
'action.new-shared-project': 'New shared project', | ||
'action.open-cursor-chat': 'Cursor chat', | ||
'action.open-file': 'Open file', | ||
@@ -76,2 +80,4 @@ 'action.pack': 'Pack', | ||
'action.toggle-dark-mode': 'Toggle dark mode', | ||
'action.toggle-reduce-motion.menu': 'Reduce motion', | ||
'action.toggle-reduce-motion': 'Toggle reduce motion', | ||
'action.toggle-debug-mode.menu': 'Debug mode', | ||
@@ -83,2 +89,3 @@ 'action.toggle-debug-mode': 'Toggle debug mode', | ||
'action.toggle-grid': 'Toggle grid', | ||
'action.toggle-lock': 'Toggle locked', | ||
'action.toggle-snap-mode.menu': 'Always snap', | ||
@@ -151,3 +158,4 @@ 'action.toggle-snap-mode': 'Toggle always snap', | ||
'geo-style.triangle': 'Triangle', | ||
'geo-style.x-box': 'X Box', | ||
'geo-style.x-box': 'X box', | ||
'geo-style.check-box': 'Check box', | ||
'arrowheadStart-style.none': 'None', | ||
@@ -185,2 +193,3 @@ 'arrowheadStart-style.arrow': 'Arrow', | ||
'tool.hexagon': 'Hexagon', | ||
'tool.highlight': 'Highlight', | ||
'tool.line': 'Line', | ||
@@ -196,5 +205,7 @@ 'tool.octagon': 'Octagon', | ||
'tool.x-box': 'X box', | ||
'tool.check-box': 'Check box', | ||
'tool.asset': 'Asset', | ||
'tool.frame': 'Frame', | ||
'tool.note': 'Note', | ||
'tool.laser': 'Laser', | ||
'tool.embed': 'Embed', | ||
@@ -229,8 +240,12 @@ 'tool.text': 'Text', | ||
'share-menu.title': 'Share', | ||
'share-menu.save-note': 'Download this project to your computer as a .tldr file.', | ||
'share-menu.fork-note': 'Create a new shared project based on this snapshot.', | ||
'share-menu.share-project': 'Share this project', | ||
'share-menu.copy-link': 'Copy link', | ||
'share-menu.default-project-name': 'Shared Project', | ||
'share-menu.copy-link': 'Copy share link', | ||
'share-menu.readonly-link': 'Read-only', | ||
'share-menu.create-snapshot-link': 'Copy snapshot link', | ||
'share-menu.snapshot-link-note': 'Capture and share this project as a read-only snapshot link.', | ||
'share-menu.copy-readonly-link': 'Copy read-only link', | ||
'share-menu.offline-note': | ||
'Sharing this project will create a hosted live copy at a new URL. You can share the URL with up to thirty other people to view and edit the project together.', | ||
'share-menu.offline-note': 'Create a new shared project based on your current project.', | ||
'share-menu.copy-link-note': 'Anyone with the link will be able to view and edit this project.', | ||
@@ -241,6 +256,10 @@ 'share-menu.copy-readonly-link-note': | ||
"Sorry, this project can't be shared because it's too large. We're working on it!", | ||
'share-menu.upload-failed': | ||
"Sorry, we couldn't upload your project at the moment. Please try again or let us know if the problem persists.", | ||
'people-menu.title': 'People', | ||
'people-menu.change-name': 'Change name', | ||
'people-menu.change-color': 'Change color', | ||
'people-menu.follow': 'Follow', | ||
'people-menu.follow': 'Following', | ||
'people-menu.following': 'Following', | ||
'people-menu.leading': 'Following You', | ||
'people-menu.user': '(You)', | ||
@@ -262,4 +281,3 @@ 'people-menu.invite': 'Invite others', | ||
'edit-link-dialog.cancel': 'Cancel', | ||
'embed-dialog.title': 'Create embed', | ||
'embed-dialog.url-title': 'Create %s embed', | ||
'embed-dialog.title': 'Insert embed', | ||
'embed-dialog.back': 'Back', | ||
@@ -280,4 +298,12 @@ 'embed-dialog.create': 'Create', | ||
'shortcuts-dialog.view': 'View', | ||
'shortcuts-dialog.collaboration': 'Collaboration', | ||
'home-project-dialog.title': 'Home project', | ||
'home-project-dialog.description': "This is your local home project. It's just for you!", | ||
'rename-project-dialog.title': 'Rename project', | ||
'rename-project-dialog.cancel': 'Cancel', | ||
'rename-project-dialog.rename': 'Rename', | ||
'home-project-dialog.ok': 'Ok', | ||
'style-panel.title': 'Styles', | ||
'style-panel.align': 'Align', | ||
'style-panel.vertical-align': 'Vertical align', | ||
'style-panel.position': 'Position', | ||
@@ -325,2 +351,8 @@ 'style-panel.arrowheads': 'Arrowheads', | ||
'Opening files from shared projects is not supported.', | ||
'sharing.confirm-leave.title': 'Leave current project?', | ||
'sharing.confirm-leave.description': | ||
'Are you sure you want to leave this shared project? You can return to it by navigating to its URL.', | ||
'sharing.confirm-leave.cancel': 'Cancel', | ||
'sharing.confirm-leave.leave': 'Leave', | ||
'sharing.confirm-leave.dont-show-again': "Don't ask again", | ||
'toast.error.export-fail.title': 'Failed export', | ||
@@ -338,2 +370,3 @@ 'toast.error.export-fail.desc': 'Failed to export image', | ||
'vscode.file-open.dont-show-again': "Don't ask again", | ||
'cursor-chat.type-to-chat': 'Type to chat...', | ||
} |
@@ -1,5 +0,5 @@ | ||
import { UiAssetUrls } from '../../assetUrls' | ||
import { LANGUAGES } from '@tldraw/tlschema' | ||
import { TLUiAssetUrls } from '../../assetUrls' | ||
import { TLUiTranslationKey } from './TLUiTranslationKey' | ||
import { DEFAULT_TRANSLATION } from './defaultTranslation' | ||
import { LANGUAGES } from './languages' | ||
import { TLTranslationKey } from './TLTranslationKey' | ||
@@ -14,38 +14,19 @@ // The default language (english) must have a value for every message. | ||
/** @public */ | ||
export type TLListedTranslation = { | ||
export type TLUiTranslation = { | ||
readonly locale: string | ||
readonly label: string | ||
readonly messages: Record<TLUiTranslationKey, string> | ||
} | ||
/** @public */ | ||
export type TLListedTranslations = readonly TLListedTranslation[] | ||
/** @public */ | ||
export type TLTranslationMessages = Record<TLTranslationKey, string> | ||
/** @public */ | ||
export type TLTranslation = { | ||
readonly locale: string | ||
readonly label: string | ||
readonly messages: TLTranslationMessages | ||
} | ||
/** @public */ | ||
export type TLTranslations = TLTranslation[] | ||
/** @public */ | ||
export type TLTranslationLocale = TLTranslations[number]['locale'] | ||
/** @public */ | ||
export const EN_TRANSLATION: TLTranslation = { | ||
const EN_TRANSLATION: TLUiTranslation = { | ||
locale: 'en', | ||
label: 'English', | ||
messages: DEFAULT_TRANSLATION as TLTranslationMessages, | ||
messages: DEFAULT_TRANSLATION as TLUiTranslation['messages'], | ||
} | ||
/** @public */ | ||
/** @internal */ | ||
export async function fetchTranslation( | ||
locale: TLTranslationLocale, | ||
assetUrls: UiAssetUrls | ||
): Promise<TLTranslation> { | ||
locale: TLUiTranslation['locale'], | ||
assetUrls: TLUiAssetUrls | ||
): Promise<TLUiTranslation> { | ||
const mainRes = await fetch(assetUrls.translations.en) | ||
@@ -70,3 +51,3 @@ | ||
const res = await fetch(assetUrls.translations[language.locale]) | ||
const messages: TLTranslationMessages = await res.json() | ||
const messages: TLUiTranslation['messages'] = await res.json() | ||
@@ -81,3 +62,3 @@ if (!messages) { | ||
for (const key in EN_TRANSLATION) { | ||
if (!messages[key as TLTranslationKey]) { | ||
if (!messages[key as TLUiTranslationKey]) { | ||
missing.push(key) | ||
@@ -97,9 +78,1 @@ } | ||
} | ||
/** @public */ | ||
export async function getTranslation( | ||
locale: TLTranslationLocale, | ||
assetUrls: UiAssetUrls | ||
): Promise<TLTranslation> { | ||
return await fetchTranslation(locale, assetUrls) | ||
} |
@@ -44,2 +44,3 @@ // This file is automatically generated by scripts/refresh-assets.ts. | ||
| 'chevrons-sw' | ||
| 'clipboard-copied' | ||
| 'clipboard-copy' | ||
@@ -71,2 +72,4 @@ | 'code' | ||
| 'fill-solid' | ||
| 'follow' | ||
| 'following' | ||
| 'font-draw' | ||
@@ -80,2 +83,3 @@ | 'font-mono' | ||
| 'geo-arrow-up' | ||
| 'geo-check-box' | ||
| 'geo-diamond' | ||
@@ -99,2 +103,3 @@ | 'geo-ellipse' | ||
| 'info-circle' | ||
| 'leading' | ||
| 'link' | ||
@@ -143,3 +148,4 @@ | 'lock-small' | ||
| 'tool-hand' | ||
| 'tool-highlighter' | ||
| 'tool-highlight' | ||
| 'tool-laser' | ||
| 'tool-line' | ||
@@ -159,2 +165,5 @@ | 'tool-media' | ||
| 'unlock' | ||
| 'vertical-align-center' | ||
| 'vertical-align-end' | ||
| 'vertical-align-start' | ||
| 'visible' | ||
@@ -166,3 +175,3 @@ | 'warning-triangle' | ||
/** @public */ | ||
export const TLUiIconTypes = [ | ||
export const iconTypes = [ | ||
'align-bottom-center', | ||
@@ -206,2 +215,3 @@ 'align-bottom-left', | ||
'chevrons-sw', | ||
'clipboard-copied', | ||
'clipboard-copy', | ||
@@ -233,2 +243,4 @@ 'code', | ||
'fill-solid', | ||
'follow', | ||
'following', | ||
'font-draw', | ||
@@ -242,2 +254,3 @@ 'font-mono', | ||
'geo-arrow-up', | ||
'geo-check-box', | ||
'geo-diamond', | ||
@@ -261,2 +274,3 @@ 'geo-ellipse', | ||
'info-circle', | ||
'leading', | ||
'link', | ||
@@ -305,3 +319,4 @@ 'lock-small', | ||
'tool-hand', | ||
'tool-highlighter', | ||
'tool-highlight', | ||
'tool-laser', | ||
'tool-line', | ||
@@ -321,2 +336,5 @@ 'tool-media', | ||
'unlock', | ||
'vertical-align-center', | ||
'vertical-align-end', | ||
'vertical-align-start', | ||
'visible', | ||
@@ -323,0 +341,0 @@ 'warning-triangle', |
@@ -1,2 +0,2 @@ | ||
import { App } from '@tldraw/editor' | ||
import { Editor } from '@tldraw/editor' | ||
import { objectMapEntries } from '@tldraw/utils' | ||
@@ -7,11 +7,11 @@ import { useMemo } from 'react' | ||
import { useBreakpoint } from './hooks/useBreakpoint' | ||
import { ContextMenuSchemaProviderProps } from './hooks/useContextMenuSchema' | ||
import { TLUiContextMenuSchemaProviderProps } from './hooks/useContextMenuSchema' | ||
import { useDialogs } from './hooks/useDialogsProvider' | ||
import { HelpMenuSchemaProviderProps } from './hooks/useHelpMenuSchema' | ||
import { KeyboardShortcutsSchemaProviderProps } from './hooks/useKeyboardShortcutsSchema' | ||
import { MenuSchemaProviderProps } from './hooks/useMenuSchema' | ||
import { TLUiHelpMenuSchemaProviderProps } from './hooks/useHelpMenuSchema' | ||
import { TLUiKeyboardShortcutsSchemaProviderProps } from './hooks/useKeyboardShortcutsSchema' | ||
import { TLUiMenuSchemaProviderProps } from './hooks/useMenuSchema' | ||
import { useToasts } from './hooks/useToastsProvider' | ||
import { ToolbarSchemaProviderProps } from './hooks/useToolbarSchema' | ||
import { ToolsProviderProps } from './hooks/useTools' | ||
import { TranslationProviderProps, useTranslation } from './hooks/useTranslation/useTranslation' | ||
import { TLUiToolbarSchemaProviderProps } from './hooks/useToolbarSchema' | ||
import { TLUiToolsProviderProps } from './hooks/useTools' | ||
import { TLUiTranslationProviderProps, useTranslation } from './hooks/useTranslation/useTranslation' | ||
@@ -53,43 +53,43 @@ /** @public */ | ||
export type TldrawUiOverride<Type, Helpers> = (app: App, schema: Type, helpers: Helpers) => Type | ||
export type TLUiOverride<Type, Helpers> = (editor: Editor, schema: Type, helpers: Helpers) => Type | ||
type WithDefaultHelpers<T extends TldrawUiOverride<any, any>> = T extends TldrawUiOverride< | ||
type WithDefaultHelpers<T extends TLUiOverride<any, any>> = T extends TLUiOverride< | ||
infer Type, | ||
infer Helpers | ||
> | ||
? TldrawUiOverride<Type, Helpers extends undefined ? DefaultHelpers : Helpers & DefaultHelpers> | ||
? TLUiOverride<Type, Helpers extends undefined ? DefaultHelpers : Helpers & DefaultHelpers> | ||
: never | ||
/** @public */ | ||
export interface TldrawUiOverrides { | ||
export interface TLUiOverrides { | ||
actionsMenu?: WithDefaultHelpers<NonNullable<ActionsMenuSchemaProviderProps['overrides']>> | ||
actions?: WithDefaultHelpers<NonNullable<ActionsProviderProps['overrides']>> | ||
contextMenu?: WithDefaultHelpers<NonNullable<ContextMenuSchemaProviderProps['overrides']>> | ||
helpMenu?: WithDefaultHelpers<NonNullable<HelpMenuSchemaProviderProps['overrides']>> | ||
menu?: WithDefaultHelpers<NonNullable<MenuSchemaProviderProps['overrides']>> | ||
toolbar?: WithDefaultHelpers<NonNullable<ToolbarSchemaProviderProps['overrides']>> | ||
contextMenu?: WithDefaultHelpers<NonNullable<TLUiContextMenuSchemaProviderProps['overrides']>> | ||
helpMenu?: WithDefaultHelpers<NonNullable<TLUiHelpMenuSchemaProviderProps['overrides']>> | ||
menu?: WithDefaultHelpers<NonNullable<TLUiMenuSchemaProviderProps['overrides']>> | ||
toolbar?: WithDefaultHelpers<NonNullable<TLUiToolbarSchemaProviderProps['overrides']>> | ||
keyboardShortcutsMenu?: WithDefaultHelpers< | ||
NonNullable<KeyboardShortcutsSchemaProviderProps['overrides']> | ||
NonNullable<TLUiKeyboardShortcutsSchemaProviderProps['overrides']> | ||
> | ||
tools?: WithDefaultHelpers<NonNullable<ToolsProviderProps['overrides']>> | ||
translations?: TranslationProviderProps['overrides'] | ||
tools?: WithDefaultHelpers<NonNullable<TLUiToolsProviderProps['overrides']>> | ||
translations?: TLUiTranslationProviderProps['overrides'] | ||
} | ||
export interface TldrawUiOverridesWithoutDefaults { | ||
export interface TLUiOverridesWithoutDefaults { | ||
actionsMenu?: ActionsMenuSchemaProviderProps['overrides'] | ||
actions?: ActionsProviderProps['overrides'] | ||
contextMenu?: ContextMenuSchemaProviderProps['overrides'] | ||
helpMenu?: HelpMenuSchemaProviderProps['overrides'] | ||
menu?: MenuSchemaProviderProps['overrides'] | ||
toolbar?: ToolbarSchemaProviderProps['overrides'] | ||
keyboardShortcutsMenu?: KeyboardShortcutsSchemaProviderProps['overrides'] | ||
tools?: ToolsProviderProps['overrides'] | ||
translations?: TranslationProviderProps['overrides'] | ||
contextMenu?: TLUiContextMenuSchemaProviderProps['overrides'] | ||
helpMenu?: TLUiHelpMenuSchemaProviderProps['overrides'] | ||
menu?: TLUiMenuSchemaProviderProps['overrides'] | ||
toolbar?: TLUiToolbarSchemaProviderProps['overrides'] | ||
keyboardShortcutsMenu?: TLUiKeyboardShortcutsSchemaProviderProps['overrides'] | ||
tools?: TLUiToolsProviderProps['overrides'] | ||
translations?: TLUiTranslationProviderProps['overrides'] | ||
} | ||
export function mergeOverrides( | ||
overrides: TldrawUiOverrides[], | ||
overrides: TLUiOverrides[], | ||
defaultHelpers: DefaultHelpers | ||
): TldrawUiOverridesWithoutDefaults { | ||
const mergedTranslations: TranslationProviderProps['overrides'] = {} | ||
): TLUiOverridesWithoutDefaults { | ||
const mergedTranslations: TLUiTranslationProviderProps['overrides'] = {} | ||
for (const override of overrides) { | ||
@@ -107,6 +107,6 @@ if (override.translations) { | ||
return { | ||
actionsMenu: (app, schema, helpers) => { | ||
actionsMenu: (editor, schema, helpers) => { | ||
for (const override of overrides) { | ||
if (override.actionsMenu) { | ||
schema = override.actionsMenu(app, schema, { ...defaultHelpers, ...helpers }) | ||
schema = override.actionsMenu(editor, schema, { ...defaultHelpers, ...helpers }) | ||
} | ||
@@ -116,6 +116,6 @@ } | ||
}, | ||
actions: (app, schema) => { | ||
actions: (editor, schema) => { | ||
for (const override of overrides) { | ||
if (override.actions) { | ||
schema = override.actions(app, schema, defaultHelpers) | ||
schema = override.actions(editor, schema, defaultHelpers) | ||
} | ||
@@ -125,6 +125,6 @@ } | ||
}, | ||
contextMenu: (app, schema, helpers) => { | ||
contextMenu: (editor, schema, helpers) => { | ||
for (const override of overrides) { | ||
if (override.contextMenu) { | ||
schema = override.contextMenu(app, schema, { ...defaultHelpers, ...helpers }) | ||
schema = override.contextMenu(editor, schema, { ...defaultHelpers, ...helpers }) | ||
} | ||
@@ -134,6 +134,6 @@ } | ||
}, | ||
helpMenu: (app, schema, helpers) => { | ||
helpMenu: (editor, schema, helpers) => { | ||
for (const override of overrides) { | ||
if (override.helpMenu) { | ||
schema = override.helpMenu(app, schema, { ...defaultHelpers, ...helpers }) | ||
schema = override.helpMenu(editor, schema, { ...defaultHelpers, ...helpers }) | ||
} | ||
@@ -143,6 +143,6 @@ } | ||
}, | ||
menu: (app, schema, helpers) => { | ||
menu: (editor, schema, helpers) => { | ||
for (const override of overrides) { | ||
if (override.menu) { | ||
schema = override.menu(app, schema, { ...defaultHelpers, ...helpers }) | ||
schema = override.menu(editor, schema, { ...defaultHelpers, ...helpers }) | ||
} | ||
@@ -152,6 +152,6 @@ } | ||
}, | ||
toolbar: (app, schema, helpers) => { | ||
toolbar: (editor, schema, helpers) => { | ||
for (const override of overrides) { | ||
if (override.toolbar) { | ||
schema = override.toolbar(app, schema, { ...defaultHelpers, ...helpers }) | ||
schema = override.toolbar(editor, schema, { ...defaultHelpers, ...helpers }) | ||
} | ||
@@ -161,6 +161,6 @@ } | ||
}, | ||
keyboardShortcutsMenu: (app, schema, helpers) => { | ||
keyboardShortcutsMenu: (editor, schema, helpers) => { | ||
for (const override of overrides) { | ||
if (override.keyboardShortcutsMenu) { | ||
schema = override.keyboardShortcutsMenu(app, schema, { ...defaultHelpers, ...helpers }) | ||
schema = override.keyboardShortcutsMenu(editor, schema, { ...defaultHelpers, ...helpers }) | ||
} | ||
@@ -170,6 +170,6 @@ } | ||
}, | ||
tools: (app, schema, helpers) => { | ||
tools: (editor, schema, helpers) => { | ||
for (const override of overrides) { | ||
if (override.tools) { | ||
schema = override.tools(app, schema, { ...defaultHelpers, ...helpers }) | ||
schema = override.tools(editor, schema, { ...defaultHelpers, ...helpers }) | ||
} | ||
@@ -189,4 +189,4 @@ } | ||
export function useMergedTranslationOverrides( | ||
overrides?: TldrawUiOverrides[] | TldrawUiOverrides | ||
): NonNullable<TranslationProviderProps['overrides']> { | ||
overrides?: TLUiOverrides[] | TLUiOverrides | ||
): NonNullable<TLUiTranslationProviderProps['overrides']> { | ||
const overridesArray = useShallowArrayEquality( | ||
@@ -196,3 +196,3 @@ overrides == null ? [] : Array.isArray(overrides) ? overrides : [overrides] | ||
return useMemo(() => { | ||
const mergedTranslations: TranslationProviderProps['overrides'] = {} | ||
const mergedTranslations: TLUiTranslationProviderProps['overrides'] = {} | ||
for (const override of overridesArray) { | ||
@@ -214,4 +214,4 @@ if (override.translations) { | ||
export function useMergedOverrides( | ||
overrides?: TldrawUiOverrides[] | TldrawUiOverrides | ||
): TldrawUiOverridesWithoutDefaults { | ||
overrides?: TLUiOverrides[] | TLUiOverrides | ||
): TLUiOverridesWithoutDefaults { | ||
const defaultHelpers = useDefaultHelpers() | ||
@@ -218,0 +218,0 @@ const overridesArray = useShallowArrayEquality( |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
2370492
20
8
491
34277
6
+ Added@floating-ui/core@0.7.3(transitive)
+ Added@floating-ui/dom@0.5.4(transitive)
+ Added@floating-ui/react-dom@0.7.2(transitive)
+ Added@radix-ui/primitive@1.0.0(transitive)
+ Added@radix-ui/react-arrow@1.0.2(transitive)
+ Added@radix-ui/react-compose-refs@1.0.0(transitive)
+ Added@radix-ui/react-context@1.0.0(transitive)
+ Added@radix-ui/react-dismissable-layer@1.0.3(transitive)
+ Added@radix-ui/react-focus-guards@1.0.0(transitive)
+ Added@radix-ui/react-focus-scope@1.0.2(transitive)
+ Added@radix-ui/react-id@1.0.0(transitive)
+ Added@radix-ui/react-popover@1.0.6-rc.5(transitive)
+ Added@radix-ui/react-popper@1.1.2-rc.5(transitive)
+ Added@radix-ui/react-portal@1.0.2(transitive)
+ Added@radix-ui/react-presence@1.0.0(transitive)
+ Added@radix-ui/react-primitive@1.0.2(transitive)
+ Added@radix-ui/react-slot@1.0.1(transitive)
+ Added@radix-ui/react-use-callback-ref@1.0.0(transitive)
+ Added@radix-ui/react-use-controllable-state@1.0.0(transitive)
+ Added@radix-ui/react-use-escape-keydown@1.0.2(transitive)
+ Added@radix-ui/react-use-layout-effect@1.0.0(transitive)
+ Added@radix-ui/react-use-rect@1.0.0(transitive)
+ Added@radix-ui/react-use-size@1.0.0(transitive)
+ Added@radix-ui/rect@1.0.0(transitive)
+ Added@tldraw/editor@2.0.0-canary.090e148552ab(transitive)
+ Added@tldraw/indices@2.0.0-canary.090e148552ab(transitive)
+ Added@tldraw/primitives@2.0.0-canary.090e148552ab(transitive)
+ Added@tldraw/state@2.0.0-canary.090e148552ab(transitive)
+ Added@tldraw/store@2.0.0-canary.090e148552ab(transitive)
+ Added@tldraw/tlschema@2.0.0-canary.090e148552ab(transitive)
+ Added@tldraw/utils@2.0.0-canary.090e148552ab(transitive)
+ Added@tldraw/validate@2.0.0-canary.090e148552ab(transitive)
+ Added@types/canvas-size@1.2.2(transitive)
+ Addedcanvas-size@1.2.6(transitive)
+ Addednanoid@4.0.2(transitive)
+ Addeduse-isomorphic-layout-effect@1.2.0(transitive)
- Removed@radix-ui/react-popover@1.1.5(transitive)
- Removed@tldraw/editor@2.0.0-canary.08c9b63bd(transitive)
- Removed@tldraw/primitives@2.0.0-canary.08c9b63bd(transitive)
- Removed@tldraw/tlschema@2.0.0-canary.08c9b63bd(transitive)
- Removed@tldraw/tlstore@2.0.0-canary.08c9b63bd(transitive)
- Removed@tldraw/tlsync-client@2.0.0-canary.08c9b63bd(transitive)
- Removed@tldraw/tlvalidate@2.0.0-canary.08c9b63bd(transitive)
- Removed@tldraw/utils@2.0.0-canary.08c9b63bd(transitive)
- Removednanoid@3.3.8(transitive)
- Removedsignia@0.1.5(transitive)
- Removedsignia-react@0.1.5(transitive)