diff --git a/.ignore b/.ignore index 7f42387d4..dd142cc32 100644 --- a/.ignore +++ b/.ignore @@ -18,4 +18,11 @@ apps/example/www/index.css *.cjs apps/docs/.next -packages/tldraw/tldraw.css \ No newline at end of file +packages/tldraw/tldraw.css + +**/dist-cjs/**/* +**/dist-esm/**/* +**/*.js.map +**/*.api.json +apps/docs/utils/vector-db +packages/**/api \ No newline at end of file diff --git a/apps/docs/content/docs/user-interface.mdx b/apps/docs/content/docs/user-interface.mdx index 3000c3f84..8cbabcb20 100644 --- a/apps/docs/content/docs/user-interface.mdx +++ b/apps/docs/content/docs/user-interface.mdx @@ -87,7 +87,6 @@ const myOverrides: TLUiOverrides = { icon: 'color', label: 'tools.card', kbd: 'c', - readonlyOk: false, onSelect: () => { // Whatever you want to happen when the tool is selected editor.setCurrentTool('card') diff --git a/apps/docs/content/getting-started/installation.mdx b/apps/docs/content/getting-started/installation.mdx index 2f8a99083..29232701c 100644 --- a/apps/docs/content/getting-started/installation.mdx +++ b/apps/docs/content/getting-started/installation.mdx @@ -155,33 +155,35 @@ The [Tldraw](?) component combines two lower-level components: [TldrawEditor](?) ### Customize the default components -You can customize the appearance of the tldraw editor using the [Tldraw](?) (or [TldrawEditor](?) component's `components` prop. +You can customize the appearance of the tldraw editor and ui using the [Tldraw](?) (or [TldrawEditor](?)) component's `components` prop. ```tsx - + +const components: TLComponents = { + Background: YourCustomBackground, + SvgDefs: YourCustomSvgDefs, + Brush: YourCustomBrush, + ZoomBrush: YourCustomBrush, + CollaboratorBrush: YourCustomBrush, + Cursor: YourCustomCursor, + CollaboratorCursor: YourCustomCursor, + CollaboratorHint: YourCustomCollaboratorHint, + CollaboratorShapeIndicator: YourCustomdicator, + Grid: YourCustomGrid, + Scribble: YourCustomScribble, + SnapLine: YourCustomSnapLine, + Handles: YourCustomHandles, + Handle: YourCustomHandle, + CollaboratorScribble: YourCustomScribble, + ErrorFallback: YourCustomErrorFallback, + ShapeErrorFallback: YourCustomShapeErrorFallback, + ShapeIndicatorErrorFallback: YourCustomShapeIndicatorErrorFallback, + Spinner: YourCustomSpinner, + SelectionBackground: YourCustomSelectionBackground, + SelectionForeground: YourCustomSelectionForeground, + HoveredShapeIndicator: YourCustomHoveredShapeIndicator, + // ... +} + + ``` diff --git a/apps/dotcom/src/components/ExportMenu.tsx b/apps/dotcom/src/components/ExportMenu.tsx index df14b53e7..102824b57 100644 --- a/apps/dotcom/src/components/ExportMenu.tsx +++ b/apps/dotcom/src/components/ExportMenu.tsx @@ -1,5 +1,14 @@ import * as Popover from '@radix-ui/react-popover' -import { Button, useActions, useContainer, useEditor, useTranslation } from '@tldraw/tldraw' +import { + TldrawUiMenuContextProvider, + TldrawUiMenuGroup, + TldrawUiMenuItem, + unwrapLabel, + useActions, + useContainer, + useEditor, + useTranslation, +} from '@tldraw/tldraw' import React, { useState } from 'react' import { useShareMenuIsOpen } from '../hooks/useShareMenuOpen' import { SHARE_PROJECT_ACTION, SHARE_SNAPSHOT_ACTION } from '../utils/sharing' @@ -33,50 +42,39 @@ export const ExportMenu = React.memo(function ExportMenu() { side="bottom" sideOffset={6} > -
-
-
-
-
-
+ + + +

+ {msg('share-menu.fork-note')} +

+
+ + { + setIsUploadingSnapshot(true) + await shareSnapshot.onSelect('share-menu') + setIsUploadingSnapshot(false) + setDidCopySnapshotLink(true) + setTimeout(() => setDidCopySnapshotLink(false), 1000) + }} + spinner={isUploadingSnapshot} + /> +

+ {msg('share-menu.snapshot-link-note')} +

+
+ + +

+ {msg('share-menu.save-note')} +

+
+
diff --git a/apps/dotcom/src/components/FileMenu.tsx b/apps/dotcom/src/components/FileMenu.tsx new file mode 100644 index 000000000..4ce0456c2 --- /dev/null +++ b/apps/dotcom/src/components/FileMenu.tsx @@ -0,0 +1,45 @@ +import { + TldrawUiMenuGroup, + TldrawUiMenuItem, + TldrawUiMenuSubmenu, + useActions, +} from '@tldraw/tldraw' +import { + FORK_PROJECT_ACTION, + LEAVE_SHARED_PROJECT_ACTION, + SHARE_PROJECT_ACTION, +} from '../utils/sharing' +import { NEW_PROJECT_ACTION, OPEN_FILE_ACTION, SAVE_FILE_COPY_ACTION } from '../utils/useFileSystem' + +export function LocalFileMenu() { + const actions = useActions() + + return ( + + + + + + + + + + + ) +} + +export function MultiplayerFileMenu() { + const actions = useActions() + + return ( + + + + + + + + + + ) +} diff --git a/apps/dotcom/src/components/Links.tsx b/apps/dotcom/src/components/Links.tsx new file mode 100644 index 000000000..d5fb8e6be --- /dev/null +++ b/apps/dotcom/src/components/Links.tsx @@ -0,0 +1,45 @@ +import { TldrawUiMenuGroup, TldrawUiMenuItem } from '@tldraw/tldraw' +import { openUrl } from '../utils/url' + +export function Links() { + return ( + + { + openUrl('https://github.com/tldraw/tldraw') + }} + /> + { + openUrl('https://twitter.com/tldraw') + }} + /> + { + openUrl('https://discord.gg/SBBEVCA4PG') + }} + /> + { + openUrl('https://tldraw.dev') + }} + /> + + ) +} diff --git a/apps/dotcom/src/components/LocalEditor.tsx b/apps/dotcom/src/components/LocalEditor.tsx index ae49b55b3..c60a79e6d 100644 --- a/apps/dotcom/src/components/LocalEditor.tsx +++ b/apps/dotcom/src/components/LocalEditor.tsx @@ -1,21 +1,77 @@ -import { Editor, Tldraw } from '@tldraw/tldraw' +import { + DefaultDebugMenu, + DefaultDebugMenuContent, + DefaultHelpMenu, + DefaultHelpMenuContent, + DefaultKeyboardShortcutsDialog, + DefaultKeyboardShortcutsDialogContent, + DefaultMainMenu, + DefaultMainMenuContent, + Editor, + TLComponents, + Tldraw, + TldrawUiMenuGroup, + TldrawUiMenuItem, + useActions, +} from '@tldraw/tldraw' import { useCallback } from 'react' import { assetUrls } from '../utils/assetUrls' import { createAssetFromUrl } from '../utils/createAssetFromUrl' -import { linksUiOverrides } from '../utils/links' import { DebugMenuItems } from '../utils/migration/DebugMenuItems' import { LocalMigration } from '../utils/migration/LocalMigration' import { SCRATCH_PERSISTENCE_KEY } from '../utils/scratch-persistence-key' import { useSharing } from '../utils/sharing' -import { useFileSystem } from '../utils/useFileSystem' +import { OPEN_FILE_ACTION, SAVE_FILE_COPY_ACTION, useFileSystem } from '../utils/useFileSystem' import { useHandleUiEvents } from '../utils/useHandleUiEvent' +import { LocalFileMenu } from './FileMenu' +import { Links } from './Links' import { ShareMenu } from './ShareMenu' import { SneakyOnDropOverride } from './SneakyOnDropOverride' import { ThemeUpdater } from './ThemeUpdater/ThemeUpdater' +const components: TLComponents = { + ErrorFallback: ({ error }) => { + throw error + }, + HelpMenu: () => ( + + + + + + + ), + MainMenu: () => ( + + + + + ), + KeyboardShortcutsDialog: (props) => { + const actions = useActions() + return ( + + + + + + + + ) + }, + DebugMenu: () => { + return ( + + + + + ) + }, +} + export function LocalEditor() { const handleUiEvent = useHandleUiEvents() - const sharingUiOverrides = useSharing({ isMultiplayer: false }) + const sharingUiOverrides = useSharing() const fileSystemUiOverrides = useFileSystem({ isMultiplayer: false }) const handleMount = useCallback((editor: Editor) => { @@ -29,19 +85,14 @@ export function LocalEditor() { persistenceKey={SCRATCH_PERSISTENCE_KEY} onMount={handleMount} autoFocus - overrides={[sharingUiOverrides, fileSystemUiOverrides, linksUiOverrides]} + overrides={[sharingUiOverrides, fileSystemUiOverrides]} onUiEvent={handleUiEvent} - components={{ - ErrorFallback: ({ error }) => { - throw error - }, - }} + components={components} shareZone={
} - renderDebugMenuItems={() => } inferDarkMode > diff --git a/apps/dotcom/src/components/MultiplayerEditor.tsx b/apps/dotcom/src/components/MultiplayerEditor.tsx index d4cc34617..ec17cd2be 100644 --- a/apps/dotcom/src/components/MultiplayerEditor.tsx +++ b/apps/dotcom/src/components/MultiplayerEditor.tsx @@ -1,25 +1,85 @@ -import { Editor, OfflineIndicator, Tldraw, lns } from '@tldraw/tldraw' +import { + DefaultContextMenu, + DefaultContextMenuContent, + DefaultHelpMenu, + DefaultHelpMenuContent, + DefaultKeyboardShortcutsDialog, + DefaultKeyboardShortcutsDialogContent, + DefaultMainMenu, + DefaultMainMenuContent, + Editor, + OfflineIndicator, + TLComponents, + Tldraw, + TldrawUiMenuGroup, + TldrawUiMenuItem, + lns, + useActions, +} from '@tldraw/tldraw' import { useCallback, useEffect } from 'react' import { useRemoteSyncClient } from '../hooks/useRemoteSyncClient' import { UrlStateParams, useUrlState } from '../hooks/useUrlState' import { assetUrls } from '../utils/assetUrls' import { MULTIPLAYER_SERVER } from '../utils/config' +import { CursorChatMenuItem } from '../utils/context-menu/CursorChatMenuItem' import { createAssetFromFile } from '../utils/createAssetFromFile' import { createAssetFromUrl } from '../utils/createAssetFromUrl' -import { linksUiOverrides } from '../utils/links' import { useSharing } from '../utils/sharing' import { trackAnalyticsEvent } from '../utils/trackAnalyticsEvent' -import { useCursorChat } from '../utils/useCursorChat' -import { useFileSystem } from '../utils/useFileSystem' +import { CURSOR_CHAT_ACTION, useCursorChat } from '../utils/useCursorChat' +import { OPEN_FILE_ACTION, SAVE_FILE_COPY_ACTION, useFileSystem } from '../utils/useFileSystem' import { useHandleUiEvents } from '../utils/useHandleUiEvent' import { CursorChatBubble } from './CursorChatBubble' import { EmbeddedInIFrameWarning } from './EmbeddedInIFrameWarning' +import { MultiplayerFileMenu } from './FileMenu' +import { Links } from './Links' import { PeopleMenu } from './PeopleMenu/PeopleMenu' import { ShareMenu } from './ShareMenu' import { SneakyOnDropOverride } from './SneakyOnDropOverride' import { StoreErrorScreen } from './StoreErrorScreen' import { ThemeUpdater } from './ThemeUpdater/ThemeUpdater' +const components: TLComponents = { + ErrorFallback: ({ error }) => { + throw error + }, + ContextMenu: (props) => ( + + + + + ), + HelpMenu: () => ( + + + + + + + ), + MainMenu: () => ( + + + + + ), + KeyboardShortcutsDialog: (props) => { + const actions = useActions() + return ( + + + + + + + + + + + ) + }, +} + export function MultiplayerEditor({ isReadOnly, roomSlug, @@ -37,7 +97,7 @@ export function MultiplayerEditor({ }) const isEmbedded = useIsEmbedded(roomSlug) - const sharingUiOverrides = useSharing({ isMultiplayer: true }) + const sharingUiOverrides = useSharing() const fileSystemUiOverrides = useFileSystem({ isMultiplayer: true }) const cursorChatOverrides = useCursorChat() @@ -67,19 +127,10 @@ export function MultiplayerEditor({ store={storeWithStatus} assetUrls={assetUrls} onMount={handleMount} + overrides={[sharingUiOverrides, fileSystemUiOverrides, cursorChatOverrides]} initialState={isReadOnly ? 'hand' : 'select'} - overrides={[ - sharingUiOverrides, - fileSystemUiOverrides, - linksUiOverrides, - cursorChatOverrides, - ]} onUiEvent={handleUiEvent} - components={{ - ErrorFallback: ({ error }) => { - throw error - }, - }} + components={components} topZone={isOffline && } shareZone={
diff --git a/apps/dotcom/src/components/ShareMenu.tsx b/apps/dotcom/src/components/ShareMenu.tsx index 614ab95ec..5c80138d0 100644 --- a/apps/dotcom/src/components/ShareMenu.tsx +++ b/apps/dotcom/src/components/ShareMenu.tsx @@ -1,5 +1,14 @@ import * as Popover from '@radix-ui/react-popover' -import { Button, lns, useActions, useContainer, useTranslation } from '@tldraw/tldraw' +import { + TldrawUiMenuContextProvider, + TldrawUiMenuGroup, + TldrawUiMenuItem, + lns, + unwrapLabel, + useActions, + useContainer, + useTranslation, +} from '@tldraw/tldraw' import React, { useEffect, useState } from 'react' import { useShareMenuIsOpen } from '../hooks/useShareMenuOpen' import { createQRCodeImageDataString } from '../utils/qrcode' @@ -105,114 +114,118 @@ export const ShareMenu = React.memo(function ShareMenu() { sideOffset={2} alignOffset={4} > - {shareState.state === 'shared' || shareState.state === 'readonly' ? ( - <> -
- -
-
- - ) : ( - <> -
-
-
-
- - )} + )} +

+ + + + { + setIsUploadingSnapshot(true) + await shareSnapshot.onSelect('share-menu') + setIsUploadingSnapshot(false) + setDidCopySnapshotLink(true) + setTimeout(() => setDidCopySnapshotLink(false), 1000) + }} + spinner={isUploadingSnapshot} + /> +

+ {msg('share-menu.snapshot-link-note')} +

+
+ + ) : ( + <> + + { + if (isUploading) return + setIsUploading(true) + await shareProject.onSelect('menu') + setIsUploading(false) + }} + spinner={isUploading} + /> +

+ {msg( + shareState.state === 'offline' + ? 'share-menu.offline-note' + : isReadOnlyLink + ? 'share-menu.copy-readonly-link-note' + : 'share-menu.copy-link-note' + )} +

+
+ + { + setIsUploadingSnapshot(true) + await shareSnapshot.onSelect('share-menu') + setIsUploadingSnapshot(false) + setDidCopySnapshotLink(true) + setTimeout(() => setDidCopySnapshotLink(false), 1000) + }} + spinner={isUploadingSnapshot} + /> +

+ {msg('share-menu.snapshot-link-note')} +

+
+ + )} + diff --git a/apps/dotcom/src/components/SnapshotsEditor.tsx b/apps/dotcom/src/components/SnapshotsEditor.tsx index d0b47f82f..ce6ed80b9 100644 --- a/apps/dotcom/src/components/SnapshotsEditor.tsx +++ b/apps/dotcom/src/components/SnapshotsEditor.tsx @@ -1,14 +1,60 @@ -import { SerializedSchema, TLRecord, Tldraw } from '@tldraw/tldraw' +import { + DefaultHelpMenu, + DefaultHelpMenuContent, + DefaultKeyboardShortcutsDialog, + DefaultKeyboardShortcutsDialogContent, + DefaultMainMenu, + DefaultMainMenuContent, + SerializedSchema, + TLComponents, + TLRecord, + Tldraw, + TldrawUiMenuGroup, + TldrawUiMenuItem, + useActions, +} from '@tldraw/tldraw' import { UrlStateSync } from '../components/MultiplayerEditor' import { StoreErrorScreen } from '../components/StoreErrorScreen' import { useLocalStore } from '../hooks/useLocalStore' import { assetUrls } from '../utils/assetUrls' -import { linksUiOverrides } from '../utils/links' import { DebugMenuItems } from '../utils/migration/DebugMenuItems' import { useSharing } from '../utils/sharing' -import { useFileSystem } from '../utils/useFileSystem' +import { SAVE_FILE_COPY_ACTION, useFileSystem } from '../utils/useFileSystem' import { useHandleUiEvents } from '../utils/useHandleUiEvent' import { ExportMenu } from './ExportMenu' +import { MultiplayerFileMenu } from './FileMenu' +import { Links } from './Links' + +const components: TLComponents = { + ErrorFallback: ({ error }) => { + throw error + }, + HelpMenu: () => ( + + + + + + + ), + MainMenu: () => ( + + + + + ), + KeyboardShortcutsDialog: (props) => { + const actions = useActions() + return ( + + + + + + + ) + }, +} type SnapshotEditorProps = { schema: SerializedSchema @@ -17,7 +63,7 @@ type SnapshotEditorProps = { export function SnapshotsEditor(props: SnapshotEditorProps) { const handleUiEvent = useHandleUiEvents() - const sharingUiOverrides = useSharing({ isMultiplayer: true }) + const sharingUiOverrides = useSharing() const fileSystemUiOverrides = useFileSystem({ isMultiplayer: true }) const storeResult = useLocalStore(props.records, props.schema) if (!storeResult?.ok) return @@ -27,16 +73,12 @@ export function SnapshotsEditor(props: SnapshotEditorProps) { { editor.updateInstanceState({ isReadonly: true }) }} - components={{ - ErrorFallback: ({ error }) => { - throw error - }, - }} + components={components} shareZone={
diff --git a/apps/dotcom/src/utils/context-menu/CursorChatMenuItem.tsx b/apps/dotcom/src/utils/context-menu/CursorChatMenuItem.tsx new file mode 100644 index 000000000..50e7603d0 --- /dev/null +++ b/apps/dotcom/src/utils/context-menu/CursorChatMenuItem.tsx @@ -0,0 +1,18 @@ +import { TldrawUiMenuItem, useActions, useEditor, useValue } from '@tldraw/tldraw' +import { CURSOR_CHAT_ACTION } from '../useCursorChat' + +export function CursorChatMenuItem() { + const editor = useEditor() + const actions = useActions() + const shouldShow = useValue( + 'show cursor chat', + () => { + return editor.getInstanceState().isCoarsePointer && !editor.getSelectedShapes().length + }, + [editor] + ) + + if (!shouldShow) return null + + return +} diff --git a/apps/dotcom/src/utils/links.ts b/apps/dotcom/src/utils/links.ts deleted file mode 100644 index b6e912a78..000000000 --- a/apps/dotcom/src/utils/links.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { menuGroup, menuItem, TLUiOverrides } from '@tldraw/tldraw' - -export const GITHUB_URL = 'https://github.com/tldraw/tldraw' - -const linksMenuGroup = menuGroup( - 'links', - menuItem({ - id: 'github', - label: 'help-menu.github', - readonlyOk: true, - icon: 'github', - onSelect() { - window.open(GITHUB_URL) - }, - }), - menuItem({ - id: 'twitter', - label: 'help-menu.twitter', - icon: 'twitter', - readonlyOk: true, - onSelect() { - window.open('https://twitter.com/tldraw') - }, - }), - menuItem({ - id: 'discord', - label: 'help-menu.discord', - icon: 'discord', - readonlyOk: true, - onSelect() { - window.open('https://discord.gg/SBBEVCA4PG') - }, - }), - menuItem({ - id: 'about', - label: 'help-menu.about', - icon: 'external-link', - readonlyOk: true, - onSelect() { - window.open('https://www.tldraw.dev') - }, - }) -)! - -export const linksUiOverrides: TLUiOverrides = { - helpMenu(editor, schema) { - schema.push(linksMenuGroup) - return schema - }, - menu(editor, schema, { isMobile }) { - if (isMobile) { - schema.push(linksMenuGroup) - } - return schema - }, -} diff --git a/apps/dotcom/src/utils/migration/DebugMenuItems.tsx b/apps/dotcom/src/utils/migration/DebugMenuItems.tsx index fbc6f7e9f..e098e9e0c 100644 --- a/apps/dotcom/src/utils/migration/DebugMenuItems.tsx +++ b/apps/dotcom/src/utils/migration/DebugMenuItems.tsx @@ -1,30 +1,28 @@ -import { DropdownMenu } from '@tldraw/tldraw' +import { TldrawUiMenuGroup, TldrawUiMenuItem } from '@tldraw/tldraw' import { env } from '../env' const RELEASE_INFO = `${env} ${process.env.NEXT_PUBLIC_TLDRAW_RELEASE_INFO ?? 'unreleased'}` export function DebugMenuItems() { return ( - - { + + { window.alert(`${RELEASE_INFO}`) }} - title={`${RELEASE_INFO}`} - > - Version - - { + /> + { const { writeV1ContentsToIdb } = await import('./writeV1ContentsToIdb') await writeV1ContentsToIdb() window.location.reload() }} - > - Test v1 content - - + /> + ) } diff --git a/apps/dotcom/src/utils/sharing.ts b/apps/dotcom/src/utils/sharing.ts index 6bf21ad8c..0f2db60d4 100644 --- a/apps/dotcom/src/utils/sharing.ts +++ b/apps/dotcom/src/utils/sharing.ts @@ -12,11 +12,7 @@ import { TLUiOverrides, TLUiToastsContextType, TLUiTranslationKey, - assert, - findMenuItem, isShape, - menuGroup, - menuItem, } from '@tldraw/tldraw' import { useMemo } from 'react' import { useNavigate, useSearchParams } from 'react-router-dom' @@ -30,8 +26,9 @@ import { UI_OVERRIDE_TODO_EVENT, useHandleUiEvents } from './useHandleUiEvent' export const SHARE_PROJECT_ACTION = 'share-project' as const export const SHARE_SNAPSHOT_ACTION = 'share-snapshot' as const -const LEAVE_SHARED_PROJECT_ACTION = 'leave-shared-project' as const +export const LEAVE_SHARED_PROJECT_ACTION = 'leave-shared-project' as const export const FORK_PROJECT_ACTION = 'fork-project' as const + const CREATE_SNAPSHOT_ENDPOINT = `/api/snapshots` const SNAPSHOT_UPLOAD_URL = `/api/new-room` @@ -93,7 +90,7 @@ async function getSnapshotLink( }) } -export function useSharing({ isMultiplayer }: { isMultiplayer: boolean }): TLUiOverrides { +export function useSharing(): TLUiOverrides { const navigate = useNavigate() const id = useSearchParams()[0].get('id') ?? undefined const uploadFileToAsset = useMultiplayerAssets(ASSET_UPLOADER_URL) @@ -188,24 +185,8 @@ export function useSharing({ isMultiplayer }: { isMultiplayer: boolean }): TLUiO } return actions }, - menu(editor, menu, { actions }) { - const fileMenu = findMenuItem(menu, ['menu', 'file']) - assert(fileMenu.type === 'submenu') - if (isMultiplayer) { - fileMenu.children.unshift( - menuGroup( - 'share', - menuItem(actions[FORK_PROJECT_ACTION]), - menuItem(actions[LEAVE_SHARED_PROJECT_ACTION]) - )! - ) - } else { - fileMenu.children.unshift(menuGroup('share', menuItem(actions[SHARE_PROJECT_ACTION]))!) - } - return menu - }, }), - [handleUiEvent, navigate, uploadFileToAsset, id, isMultiplayer] + [handleUiEvent, navigate, uploadFileToAsset, id] ) } diff --git a/apps/dotcom/src/utils/shouldClearDocument.tsx b/apps/dotcom/src/utils/shouldClearDocument.tsx index def4f44fd..2512aece6 100644 --- a/apps/dotcom/src/utils/shouldClearDocument.tsx +++ b/apps/dotcom/src/utils/shouldClearDocument.tsx @@ -1,4 +1,13 @@ -import { Button, Dialog, TLUiDialogsContextType, useTranslation } from '@tldraw/tldraw' +import { + Button, + DialogBody, + DialogCloseButton, + DialogFooter, + DialogHeader, + DialogTitle, + TLUiDialogsContextType, + useTranslation, +} from '@tldraw/tldraw' import { useState } from 'react' import { userPreferences } from './userPreferences' @@ -40,14 +49,14 @@ function ConfirmClearDialog({ const [dontShowAgain, setDontShowAgain] = useState(false) return ( <> - - {msg('file-system.confirm-clear.title')} - - - + + {msg('file-system.confirm-clear.title')} + + + {msg('file-system.confirm-clear.description')} - - + + - + ) } diff --git a/apps/dotcom/src/utils/shouldLeaveSharedProject.tsx b/apps/dotcom/src/utils/shouldLeaveSharedProject.tsx index cd9fd8df5..10a8b4e4e 100644 --- a/apps/dotcom/src/utils/shouldLeaveSharedProject.tsx +++ b/apps/dotcom/src/utils/shouldLeaveSharedProject.tsx @@ -1,6 +1,10 @@ import { Button, - Dialog, + DialogBody, + DialogCloseButton, + DialogFooter, + DialogHeader, + DialogTitle, TLUiDialogsContextType, useLocalStorageState, useTranslation, @@ -46,14 +50,12 @@ function ConfirmLeaveDialog({ return ( <> - - {msg('sharing.confirm-leave.title')} - - - - {msg('sharing.confirm-leave.description')} - - + + {msg('sharing.confirm-leave.title')} + + + {msg('sharing.confirm-leave.description')} + - + ) } diff --git a/apps/dotcom/src/utils/shouldOverrideDocument.tsx b/apps/dotcom/src/utils/shouldOverrideDocument.tsx index bbfdd5d59..bb5d0dee0 100644 --- a/apps/dotcom/src/utils/shouldOverrideDocument.tsx +++ b/apps/dotcom/src/utils/shouldOverrideDocument.tsx @@ -1,4 +1,13 @@ -import { Button, Dialog, TLUiDialogsContextType, useTranslation } from '@tldraw/tldraw' +import { + Button, + DialogBody, + DialogCloseButton, + DialogFooter, + DialogHeader, + DialogTitle, + TLUiDialogsContextType, + useTranslation, +} from '@tldraw/tldraw' import { useState } from 'react' import { userPreferences } from './userPreferences' @@ -40,14 +49,14 @@ function ConfirmOpenDialog({ const [dontShowAgain, setDontShowAgain] = useState(false) return ( <> - - {msg('file-system.confirm-open.title')} - - - + + {msg('file-system.confirm-open.title')} + + + {msg('file-system.confirm-open.description')} - - + + - + ) } diff --git a/apps/dotcom/src/utils/url.ts b/apps/dotcom/src/utils/url.ts new file mode 100644 index 000000000..839eb9e89 --- /dev/null +++ b/apps/dotcom/src/utils/url.ts @@ -0,0 +1,3 @@ +export function openUrl(url: string) { + window.open(url, '_blank') +} diff --git a/apps/dotcom/src/utils/useCursorChat.ts b/apps/dotcom/src/utils/useCursorChat.ts index 75bd743bd..2d6fe6684 100644 --- a/apps/dotcom/src/utils/useCursorChat.ts +++ b/apps/dotcom/src/utils/useCursorChat.ts @@ -1,4 +1,4 @@ -import { TLUiOverrides, menuGroup, menuItem } from '@tldraw/tldraw' +import { TLUiOverrides } from '@tldraw/tldraw' import { useMemo } from 'react' import { useHandleUiEvents } from './useHandleUiEvent' @@ -27,36 +27,6 @@ export function useCursorChat(): TLUiOverrides { } return actions }, - contextMenu(editor, contextMenu, { actions }) { - if (editor.getSelectedShapes().length > 0 || editor.getInstanceState().isCoarsePointer) { - return contextMenu - } - - const cursorChatGroup = menuGroup('cursor-chat', menuItem(actions[CURSOR_CHAT_ACTION])) - if (!cursorChatGroup) { - return contextMenu - } - - const clipboardGroupIndex = contextMenu.findIndex((group) => group.id === 'clipboard-group') - if (clipboardGroupIndex === -1) { - contextMenu.push(cursorChatGroup) - return contextMenu - } - - contextMenu.splice(clipboardGroupIndex + 1, 0, cursorChatGroup) - return contextMenu - }, - keyboardShortcutsMenu(editor, keyboardShortcutsMenu, { actions }) { - const group = menuGroup( - 'shortcuts-dialog.collaboration', - menuItem(actions[CURSOR_CHAT_ACTION]) - ) - if (!group) { - return keyboardShortcutsMenu - } - keyboardShortcutsMenu.push(group) - return keyboardShortcutsMenu - }, }), [handleUiEvent] ) diff --git a/apps/dotcom/src/utils/useFileSystem.tsx b/apps/dotcom/src/utils/useFileSystem.tsx index 095698a0b..2329b0fb7 100644 --- a/apps/dotcom/src/utils/useFileSystem.tsx +++ b/apps/dotcom/src/utils/useFileSystem.tsx @@ -5,10 +5,6 @@ import { TLUiActionItem, TLUiEventHandler, TLUiOverrides, - assert, - findMenuItem, - menuGroup, - menuItem, parseAndLoadDocument, serializeTldrawJsonBlob, transact, @@ -19,9 +15,9 @@ import { shouldClearDocument } from './shouldClearDocument' import { shouldOverrideDocument } from './shouldOverrideDocument' import { useHandleUiEvents } from './useHandleUiEvent' -const SAVE_FILE_COPY_ACTION = 'save-file-copy' -const OPEN_FILE_ACTION = 'open-file' -const NEW_PROJECT_ACTION = 'new-file' +export const SAVE_FILE_COPY_ACTION = 'save-file-copy' +export const OPEN_FILE_ACTION = 'open-file' +export const NEW_PROJECT_ACTION = 'new-file' const saveFileNames = new WeakMap() @@ -92,31 +88,6 @@ export function useFileSystem({ isMultiplayer }: { isMultiplayer: boolean }): TL } return actions }, - menu(editor, menu, { actions }) { - const fileMenu = findMenuItem(menu, ['menu', 'file']) - assert(fileMenu.type === 'submenu') - - const saveItem = menuItem(actions[SAVE_FILE_COPY_ACTION]) - const openItem = menuItem(actions[OPEN_FILE_ACTION]) - const newItem = menuItem(actions[NEW_PROJECT_ACTION]) - const group = isMultiplayer - ? // open is not currently supported in multiplayer - menuGroup('filesystem', saveItem) - : menuGroup('filesystem', newItem, openItem, saveItem) - fileMenu.children.unshift(group!) - - return menu - }, - keyboardShortcutsMenu(editor, menu, { actions }) { - const fileItems = findMenuItem(menu, ['shortcuts-dialog.file']) - assert(fileItems.type === 'group') - fileItems.children.unshift(menuItem(actions[SAVE_FILE_COPY_ACTION])) - if (!isMultiplayer) { - fileItems.children.unshift(menuItem(actions[OPEN_FILE_ACTION])) - } - - return menu - }, } }, [isMultiplayer, handleUiEvent]) } diff --git a/apps/examples/e2e/tests/context-menu.spec.ts b/apps/examples/e2e/tests/context-menu.spec.ts new file mode 100644 index 000000000..0741f33d9 --- /dev/null +++ b/apps/examples/e2e/tests/context-menu.spec.ts @@ -0,0 +1,42 @@ +import test, { Page, expect } from '@playwright/test' +import { setupPage, setupPageWithShapes } from '../shared-e2e' + +declare const __tldraw_ui_event: { name: string } + +// We're just testing the events, not the actual results. + +let page: Page + +test.describe('Context menu', async () => { + test.beforeEach(async ({ browser }) => { + page = await browser.newPage() + await setupPage(page) + await setupPageWithShapes(page) + }) + + test('distribute horizontal', async () => { + // distribute horizontal + await page.keyboard.press('Control+a') + await page.mouse.click(200, 200, { button: 'right' }) + await page.getByTestId('context-menu-sub-trigger.arrange').click() + await page.getByTestId('context-menu.distribute-horizontal').focus() + await page.keyboard.press('Enter') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'distribute-shapes', + data: { operation: 'horizontal', source: 'context-menu' }, + }) + }) + + test('distribute vertical', async () => { + // distribute vertical — Shift+Alt+V + await page.keyboard.press('Control+a') + await page.mouse.click(200, 200, { button: 'right' }) + await page.getByTestId('context-menu-sub-trigger.arrange').click() + await page.getByTestId('context-menu.distribute-vertical').focus() + await page.keyboard.press('Enter') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'distribute-shapes', + data: { operation: 'vertical', source: 'context-menu' }, + }) + }) +}) diff --git a/apps/examples/e2e/tests/export-snapshots.spec.ts b/apps/examples/e2e/tests/export-snapshots.spec.ts index e3a3e9e55..9de66df78 100644 --- a/apps/examples/e2e/tests/export-snapshots.spec.ts +++ b/apps/examples/e2e/tests/export-snapshots.spec.ts @@ -1,10 +1,13 @@ -import test, { Page, expect } from '@playwright/test' -import { Editor, TLShapeId, TLShapePartial } from '@tldraw/tldraw' -import assert from 'assert' -import { rename, writeFile } from 'fs/promises' -import { setupPage } from '../shared-e2e' +import test from '@playwright/test' +import { TLShapeId, TLShapePartial } from '@tldraw/tldraw' -declare const editor: Editor +// import test, { Page, expect } from '@playwright/test' +// import assert from 'assert' +// import { rename, writeFile } from 'fs/promises' +// import { setupPage } from '../shared-e2e' +// import { Editor, TLShapeId, TLShapePartial } from '@tldraw/tldraw' + +// declare const editor: Editor test.describe('Export snapshots', () => { const snapshots = { @@ -186,50 +189,50 @@ test.describe('Export snapshots', () => { ] } - const snapshotsToTest = Object.entries(snapshots) - const filteredSnapshots = snapshotsToTest // maybe we filter these down, there are a lot of them + // const snapshotsToTest = Object.entries(snapshots) + // const filteredSnapshots = snapshotsToTest // maybe we filter these down, there are a lot of them - for (const [name, shapes] of filteredSnapshots) { - test(`Exports with ${name} in dark mode`, async ({ browser }) => { - const page = await browser.newPage() - await setupPage(page) - await page.evaluate((shapes) => { - editor.user.updateUserPreferences({ isDarkMode: true }) - editor - .updateInstanceState({ exportBackground: false }) - .selectAll() - .deleteShapes(editor.getSelectedShapeIds()) - .createShapes(shapes) - }, shapes as any) + // for (const [name, shapes] of filteredSnapshots) { + // test(`Exports with ${name} in dark mode`, async ({ browser }) => { + // const page = await browser.newPage() + // await setupPage(page) + // await page.evaluate((shapes) => { + // editor.user.updateUserPreferences({ isDarkMode: true }) + // editor + // .updateInstanceState({ exportBackground: false }) + // .selectAll() + // .deleteShapes(editor.getSelectedShapeIds()) + // .createShapes(shapes) + // }, shapes as any) - await snapshotTest(page) - }) - } + // await snapshotTest(page) + // }) + // } - async function snapshotTest(page: Page) { - const downloadAndSnapshot = page.waitForEvent('download').then(async (download) => { - const path = (await download.path()) as string - assert(path) - await rename(path, path + '.svg') - await writeFile( - path + '.html', - ` - - - - - `, - 'utf-8' - ) + // async function snapshotTest(page: Page) { + // const downloadAndSnapshot = page.waitForEvent('download').then(async (download) => { + // const path = (await download.path()) as string + // assert(path) + // await rename(path, path + '.svg') + // await writeFile( + // path + '.html', + // ` + // + // + // + // + // `, + // 'utf-8' + // ) - await page.goto(`file://${path}.html`) - const clip = await page.$eval('img', (img) => img.getBoundingClientRect()) - await expect(page).toHaveScreenshot({ - omitBackground: true, - clip, - }) - }) - await page.evaluate(() => (window as any)['tldraw-export']()) - await downloadAndSnapshot - } + // await page.goto(`file://${path}.html`) + // const clip = await page.$eval('img', (img) => img.getBoundingClientRect()) + // await expect(page).toHaveScreenshot({ + // omitBackground: true, + // clip, + // }) + // }) + // await page.evaluate(() => (window as any)['tldraw-export']()) + // await downloadAndSnapshot + // } }) diff --git a/apps/examples/e2e/tests/test-clipboard.spec.ts b/apps/examples/e2e/tests/test-clipboard.spec.ts index 89c1b6f41..cf5df5079 100644 --- a/apps/examples/e2e/tests/test-clipboard.spec.ts +++ b/apps/examples/e2e/tests/test-clipboard.spec.ts @@ -46,12 +46,12 @@ test.describe.skip('clipboard tests', () => { expect(await page.evaluate(() => editor.getSelectedShapes().length)).toBe(1) await page.getByTestId('main.menu').click() - await page.getByTestId('menu-item.edit').click() - await page.getByTestId('menu-item.copy').click() + await page.getByTestId('main-menu-sub-trigger.edit').click() + await page.getByTestId('main-menu.copy').click() await sleep(100) await page.getByTestId('main.menu').click() - await page.getByTestId('menu-item.edit').click() - await page.getByTestId('menu-item.paste').click() + await page.getByTestId('main-menu-sub-trigger.edit').click() + await page.getByTestId('main-menu.paste').click() expect(await page.evaluate(() => editor.getCurrentPageShapes().length)).toBe(2) expect(await page.evaluate(() => editor.getSelectedShapes().length)).toBe(1) @@ -67,11 +67,11 @@ test.describe.skip('clipboard tests', () => { expect(await page.evaluate(() => editor.getSelectedShapes().length)).toBe(1) await page.mouse.click(100, 100, { button: 'right' }) - await page.getByTestId('menu-item.copy').click() + await page.getByTestId('main-menu.copy').click() await sleep(100) await page.mouse.move(200, 200) await page.mouse.click(100, 100, { button: 'right' }) - await page.getByTestId('menu-item.paste').click() + await page.getByTestId('main-menu.paste').click() expect(await page.evaluate(() => editor.getCurrentPageShapes().length)).toBe(2) expect(await page.evaluate(() => editor.getSelectedShapes().length)).toBe(1) diff --git a/apps/examples/e2e/tests/test-kbds.spec.ts b/apps/examples/e2e/tests/test-kbds.spec.ts index 91986e4d0..70a9e9276 100644 --- a/apps/examples/e2e/tests/test-kbds.spec.ts +++ b/apps/examples/e2e/tests/test-kbds.spec.ts @@ -364,38 +364,6 @@ test.describe('Actions on shapes', () => { }) }) -test.describe('Context menu', async () => { - test.beforeEach(async ({ browser }) => { - page = await browser.newPage() - await setupPage(page) - await setupPageWithShapes(page) - }) - - test('distribute horizontal', async () => { - // distribute horizontal - await page.keyboard.press('Control+a') - await page.mouse.click(200, 200, { button: 'right' }) - await page.getByTestId('menu-item.arrange').click() - await page.getByTestId('menu-item.distribute-horizontal').click() - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'distribute-shapes', - data: { operation: 'horizontal', source: 'context-menu' }, - }) - }) - - test('distribute vertical', async () => { - // distribute vertical — Shift+Alt+V - await page.keyboard.press('Control+a') - await page.mouse.click(200, 200, { button: 'right' }) - await page.getByTestId('menu-item.arrange').click() - await page.getByTestId('menu-item.distribute-vertical').click() - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'distribute-shapes', - data: { operation: 'vertical', source: 'context-menu' }, - }) - }) -}) - test.describe('Delete bug', () => { test.beforeEach(async ({ browser }) => { page = await browser.newPage() diff --git a/apps/examples/e2e/tests/test-smoke.spec.ts b/apps/examples/e2e/tests/test-smoke.spec.ts index 2504edcca..c6d856bea 100644 --- a/apps/examples/e2e/tests/test-smoke.spec.ts +++ b/apps/examples/e2e/tests/test-smoke.spec.ts @@ -26,8 +26,8 @@ test.describe('smoke tests', () => { test('undo and redo', async ({ page }) => { // buttons should be disabled when there is no history - expect(page.getByTestId('main.undo')).toBeDisabled() - expect(page.getByTestId('main.redo')).toBeDisabled() + expect(page.getByTestId('quick-actions.undo')).toBeDisabled() + expect(page.getByTestId('quick-actions.redo')).toBeDisabled() // create a shape await page.keyboard.press('r') @@ -39,22 +39,22 @@ test.describe('smoke tests', () => { expect(await getAllShapeTypes(page)).toEqual(['geo']) // We should have an undoable shape - expect(page.getByTestId('main.undo')).not.toBeDisabled() - expect(page.getByTestId('main.redo')).toBeDisabled() + expect(page.getByTestId('quick-actions.undo')).not.toBeDisabled() + expect(page.getByTestId('quick-actions.redo')).toBeDisabled() // Click the undo button to undo the shape - await page.getByTestId('main.undo').click() + await page.getByTestId('quick-actions.undo').click() expect(await getAllShapeTypes(page)).toEqual([]) - expect(page.getByTestId('main.undo')).toBeDisabled() - expect(page.getByTestId('main.redo')).not.toBeDisabled() + expect(page.getByTestId('quick-actions.undo')).toBeDisabled() + expect(page.getByTestId('quick-actions.redo')).not.toBeDisabled() // Click the redo button to redo the shape - await page.getByTestId('main.redo').click() + await page.getByTestId('quick-actions.redo').click() expect(await getAllShapeTypes(page)).toEqual(['geo']) - expect(await page.getByTestId('main.undo').isDisabled()).not.toBe(true) - expect(await page.getByTestId('main.redo').isDisabled()).toBe(true) + expect(await page.getByTestId('quick-actions.undo').isDisabled()).not.toBe(true) + expect(await page.getByTestId('quick-actions.redo').isDisabled()).toBe(true) }) test('style panel + undo and redo squashing', async ({ page }) => { @@ -108,8 +108,8 @@ test.describe('smoke tests', () => { await page.mouse.up() // Now undo and redo - const undo = page.getByTestId('main.undo') - const redo = page.getByTestId('main.redo') + const undo = page.getByTestId('quick-actions.undo') + const redo = page.getByTestId('quick-actions.redo') await undo.click() // orange -> light blue expect(await getSelectedShapeColor()).toBe('light-blue') // skipping squashed colors! @@ -124,7 +124,7 @@ test.describe('smoke tests', () => { await redo.click() // black -> light blue await redo.click() // light-blue -> orange - expect(await page.getByTestId('main.undo').isDisabled()).not.toBe(true) - expect(await page.getByTestId('main.redo').isDisabled()).toBe(true) + expect(await page.getByTestId('quick-actions.undo').isDisabled()).not.toBe(true) + expect(await page.getByTestId('quick-actions.redo').isDisabled()).toBe(true) }) }) diff --git a/apps/examples/src/examples/custom-actions-menu/CustomActionsMenuExample.tsx b/apps/examples/src/examples/custom-actions-menu/CustomActionsMenuExample.tsx new file mode 100644 index 000000000..6b42d12c6 --- /dev/null +++ b/apps/examples/src/examples/custom-actions-menu/CustomActionsMenuExample.tsx @@ -0,0 +1,22 @@ +import { DefaultActionsMenu, TLComponents, Tldraw } from '@tldraw/tldraw' +import '@tldraw/tldraw/tldraw.css' + +function CustomActionsMenu() { + return ( +
+ +
+ ) +} + +const components: TLComponents = { + ActionsMenu: CustomActionsMenu, +} + +export default function CustomActionsMenuExample() { + return ( +
+ +
+ ) +} diff --git a/apps/examples/src/examples/custom-actions-menu/README.md b/apps/examples/src/examples/custom-actions-menu/README.md new file mode 100644 index 000000000..00ae48688 --- /dev/null +++ b/apps/examples/src/examples/custom-actions-menu/README.md @@ -0,0 +1,11 @@ +--- +title: Custom actions menu +component: ./CustomActionsMenuExample.tsx +category: ui +--- + +You can customize tldraw's actions menu. + +--- + +The actions menu can be customized by providing a `ActionsMenu` component to the `Tldraw` component's `uiComponents` prop. If you provide `null`, then that component will be hidden. diff --git a/apps/examples/src/examples/custom-config/ui-overrides.ts b/apps/examples/src/examples/custom-config/ui-overrides.tsx similarity index 67% rename from apps/examples/src/examples/custom-config/ui-overrides.ts rename to apps/examples/src/examples/custom-config/ui-overrides.tsx index 5e346d973..497f45bdc 100644 --- a/apps/examples/src/examples/custom-config/ui-overrides.ts +++ b/apps/examples/src/examples/custom-config/ui-overrides.tsx @@ -1,4 +1,12 @@ -import { TLUiMenuGroup, TLUiOverrides, menuItem, toolbarItem } from '@tldraw/tldraw' +import { + DefaultKeyboardShortcutsDialog, + DefaultKeyboardShortcutsDialogContent, + TLComponents, + TLUiOverrides, + TldrawUiMenuItem, + toolbarItem, + useTools, +} from '@tldraw/tldraw' // There's a guide at the bottom of this file! @@ -10,7 +18,6 @@ export const uiOverrides: TLUiOverrides = { icon: 'color', label: 'Card', kbd: 'c', - readonlyOk: false, onSelect: () => { editor.setCurrentTool('card') }, @@ -22,13 +29,18 @@ export const uiOverrides: TLUiOverrides = { toolbar.splice(4, 0, toolbarItem(tools.card)) return toolbar }, - keyboardShortcutsMenu(_app, keyboardShortcutsMenu, { tools }) { - // Add the tool item from the context to the keyboard shortcuts dialog. - const toolsGroup = keyboardShortcutsMenu.find( - (group) => group.id === 'shortcuts-dialog.tools' - ) as TLUiMenuGroup - toolsGroup.children.push(menuItem(tools.card)) - return keyboardShortcutsMenu +} + +export const components: TLComponents = { + KeyboardShortcutsDialog: (props) => { + const tools = useTools() + return ( + + + {/* Ideally, we'd interleave this into the tools group */} + + + ) }, } diff --git a/apps/examples/src/examples/custom-context-menu/CustomContextMenuExample.tsx b/apps/examples/src/examples/custom-context-menu/CustomContextMenuExample.tsx new file mode 100644 index 000000000..683608bcd --- /dev/null +++ b/apps/examples/src/examples/custom-context-menu/CustomContextMenuExample.tsx @@ -0,0 +1,41 @@ +import { + DefaultContextMenu, + DefaultContextMenuContent, + TLComponents, + TLUiContextMenuProps, + Tldraw, + TldrawUiMenuGroup, + TldrawUiMenuItem, +} from '@tldraw/tldraw' +import '@tldraw/tldraw/tldraw.css' + +function CustomContextMenu(props: TLUiContextMenuProps) { + return ( + + + { + window.open('https://x.com/tldraw', '_blank') + }} + /> + + + + ) +} + +const components: TLComponents = { + ContextMenu: CustomContextMenu, +} + +export default function CustomContextMenuExample() { + return ( +
+ +
+ ) +} diff --git a/apps/examples/src/examples/custom-context-menu/README.md b/apps/examples/src/examples/custom-context-menu/README.md new file mode 100644 index 000000000..7bc741e09 --- /dev/null +++ b/apps/examples/src/examples/custom-context-menu/README.md @@ -0,0 +1,11 @@ +--- +title: Custom context menu +component: ./CustomContextMenuExample.tsx +category: ui +--- + +You can customize tldraw's context menu. + +--- + +The context menu can be customized by providing a `ContextMenu` component to the `Tldraw` component's `uiComponents` prop. If you provide `null`, then that component will be hidden instead. diff --git a/apps/examples/src/examples/custom-debug-menu/CustomDebugMenuExample.tsx b/apps/examples/src/examples/custom-debug-menu/CustomDebugMenuExample.tsx new file mode 100644 index 000000000..77e34633e --- /dev/null +++ b/apps/examples/src/examples/custom-debug-menu/CustomDebugMenuExample.tsx @@ -0,0 +1,40 @@ +import { + DefaultDebugMenu, + DefaultDebugMenuContent, + TLComponents, + Tldraw, + TldrawUiMenuGroup, + TldrawUiMenuItem, +} from '@tldraw/tldraw' +import '@tldraw/tldraw/tldraw.css' + +function CustomDebugMenu() { + return ( + + + + { + window.open('https://x.com/tldraw', '_blank') + }} + /> + + + ) +} + +const components: TLComponents = { + DebugMenu: CustomDebugMenu, +} + +export default function CustomDebugMenuExample() { + return ( +
+ +
+ ) +} diff --git a/apps/examples/src/examples/custom-debug-menu/README.md b/apps/examples/src/examples/custom-debug-menu/README.md new file mode 100644 index 000000000..ec2af79a6 --- /dev/null +++ b/apps/examples/src/examples/custom-debug-menu/README.md @@ -0,0 +1,11 @@ +--- +title: Custom debug menu +component: ./CustomDebugMenuExample.tsx +category: ui +--- + +You can customize tldraw's debug menu. + +--- + +The help menu can be customized by providing a `DebugMenu` component to the `Tldraw` component's `uiComponents` prop. If you provide `null`, then that component will be hidden. diff --git a/apps/examples/src/examples/custom-help-menu/CustomHelpMenuExample.tsx b/apps/examples/src/examples/custom-help-menu/CustomHelpMenuExample.tsx new file mode 100644 index 000000000..87b9583c2 --- /dev/null +++ b/apps/examples/src/examples/custom-help-menu/CustomHelpMenuExample.tsx @@ -0,0 +1,40 @@ +import { + DefaultHelpMenu, + DefaultHelpMenuContent, + TLComponents, + Tldraw, + TldrawUiMenuGroup, + TldrawUiMenuItem, +} from '@tldraw/tldraw' +import '@tldraw/tldraw/tldraw.css' + +function CustomHelpMenu() { + return ( + + + { + window.open('https://x.com/tldraw', '_blank') + }} + /> + + + + ) +} + +const components: TLComponents = { + HelpMenu: CustomHelpMenu, +} + +export default function CustomHelpMenuExample() { + return ( +
+ +
+ ) +} diff --git a/apps/examples/src/examples/custom-help-menu/README.md b/apps/examples/src/examples/custom-help-menu/README.md new file mode 100644 index 000000000..9bfbeed96 --- /dev/null +++ b/apps/examples/src/examples/custom-help-menu/README.md @@ -0,0 +1,11 @@ +--- +title: Custom help menu +component: ./CustomHelpMenuExample.tsx +category: ui +--- + +You can customize tldraw's help menu. + +--- + +The help menu can be customized by providing a `HelpMenu` component to the `Tldraw` component's `uiComponents` prop. If you provide `null`, then that component will be hidden. diff --git a/apps/examples/src/examples/custom-keyboard-shortcuts-dialog/CustomKeyboardShortcutsDialogExample.tsx b/apps/examples/src/examples/custom-keyboard-shortcuts-dialog/CustomKeyboardShortcutsDialogExample.tsx new file mode 100644 index 000000000..4a9cd99ba --- /dev/null +++ b/apps/examples/src/examples/custom-keyboard-shortcuts-dialog/CustomKeyboardShortcutsDialogExample.tsx @@ -0,0 +1,38 @@ +import { + DefaultKeyboardShortcutsDialog, + DefaultKeyboardShortcutsDialogContent, + TLComponents, + TLUiKeyboardShortcutsDialogProps, + Tldraw, + TldrawUiMenuItem, +} from '@tldraw/tldraw' +import '@tldraw/tldraw/tldraw.css' + +function CustomKeyboardShortcutsDialog(props: TLUiKeyboardShortcutsDialogProps) { + return ( + + { + window.open('https://x.com/tldraw', '_blank') + }} + /> + + + ) +} + +const components: TLComponents = { + KeyboardShortcutsDialog: CustomKeyboardShortcutsDialog, +} + +export default function CustomKeyboardShortcutsDialogExample() { + return ( +
+ +
+ ) +} diff --git a/apps/examples/src/examples/custom-keyboard-shortcuts-dialog/README.md b/apps/examples/src/examples/custom-keyboard-shortcuts-dialog/README.md new file mode 100644 index 000000000..3bd55a3ce --- /dev/null +++ b/apps/examples/src/examples/custom-keyboard-shortcuts-dialog/README.md @@ -0,0 +1,11 @@ +--- +title: Custom keyboard shortcuts dialog +component: ./CustomKeyboardShortcutsDialogExample.tsx +category: ui +--- + +You can customize tldraw's keyboard shortcuts dialog. + +--- + +The keyboard shortcuts dialog can be customized by providing a `KeyboardShortcutsDialog` component to the `Tldraw` component's `uiComponents` prop. If you provide `null`, then that component will be hidden instead. diff --git a/apps/examples/src/examples/custom-main-menu/CustomMainMenuExample.tsx b/apps/examples/src/examples/custom-main-menu/CustomMainMenuExample.tsx new file mode 100644 index 000000000..0dcb99a93 --- /dev/null +++ b/apps/examples/src/examples/custom-main-menu/CustomMainMenuExample.tsx @@ -0,0 +1,40 @@ +import { + DefaultMainMenu, + DefaultMainMenuContent, + TLComponents, + Tldraw, + TldrawUiMenuGroup, + TldrawUiMenuItem, +} from '@tldraw/tldraw' +import '@tldraw/tldraw/tldraw.css' + +function CustomMainMenu() { + return ( + + + { + window.open('https://x.com/tldraw', '_blank') + }} + /> + + + + ) +} + +const components: TLComponents = { + MainMenu: CustomMainMenu, +} + +export default function CustomMainMenuExample() { + return ( +
+ +
+ ) +} diff --git a/apps/examples/src/examples/custom-main-menu/README.md b/apps/examples/src/examples/custom-main-menu/README.md new file mode 100644 index 000000000..d44c20839 --- /dev/null +++ b/apps/examples/src/examples/custom-main-menu/README.md @@ -0,0 +1,11 @@ +--- +title: Custom main menu +component: ./CustomMainMenuExample.tsx +category: ui +--- + +You can customize tldraw's main menu. + +--- + +The actions menu can be customized by providing a `MainMenu` component to the `Tldraw` component's `uiComponents` prop. If you provide `null`, then that component will be hidden. diff --git a/apps/examples/src/examples/custom-navigation-panel/CustomNavigationPanelExample.tsx b/apps/examples/src/examples/custom-navigation-panel/CustomNavigationPanelExample.tsx new file mode 100644 index 000000000..ad530fc56 --- /dev/null +++ b/apps/examples/src/examples/custom-navigation-panel/CustomNavigationPanelExample.tsx @@ -0,0 +1,18 @@ +import { TLComponents, Tldraw } from '@tldraw/tldraw' +import '@tldraw/tldraw/tldraw.css' + +function CustomNavigationPanel() { + return
here you are
+} + +const components: TLComponents = { + NavigationPanel: CustomNavigationPanel, // null will hide the panel instead +} + +export default function CustomNagiationPanelExample() { + return ( +
+ +
+ ) +} diff --git a/apps/examples/src/examples/custom-navigation-panel/README.md b/apps/examples/src/examples/custom-navigation-panel/README.md new file mode 100644 index 000000000..5be8bf72f --- /dev/null +++ b/apps/examples/src/examples/custom-navigation-panel/README.md @@ -0,0 +1,11 @@ +--- +title: Custom navigation panel +component: ./CustomNavigationPanelExample.tsx +category: ui +--- + +You can customize tldraw's navigation panel or remove it entirely. + +--- + +The navigation panel can be customized by providing a `NavigationPanel` component to the `Tldraw` component's `uiComponents` prop. If you provide `null`, then that component will be hidden instead. diff --git a/apps/examples/src/examples/custom-page-menu/CustomPageMenuExample.tsx b/apps/examples/src/examples/custom-page-menu/CustomPageMenuExample.tsx new file mode 100644 index 000000000..603cdd352 --- /dev/null +++ b/apps/examples/src/examples/custom-page-menu/CustomPageMenuExample.tsx @@ -0,0 +1,22 @@ +import { DefaultPageMenu, TLComponents, Tldraw } from '@tldraw/tldraw' +import '@tldraw/tldraw/tldraw.css' + +function CustomPageMenu() { + return ( +
+ +
+ ) +} + +const components: TLComponents = { + PageMenu: CustomPageMenu, // null will hide the page menu instead +} + +export default function CustomPageMenuExample() { + return ( +
+ +
+ ) +} diff --git a/apps/examples/src/examples/custom-page-menu/README.md b/apps/examples/src/examples/custom-page-menu/README.md new file mode 100644 index 000000000..38b1fcd49 --- /dev/null +++ b/apps/examples/src/examples/custom-page-menu/README.md @@ -0,0 +1,11 @@ +--- +title: Custom page menu +component: ./CustomPageMenuExample.tsx +category: ui +--- + +You can customize tldraw's page menu, or remove it entirely. + +--- + +The page menu can be customized by providing a `PageMenu` component to the `Tldraw` component's `uiComponents` prop. If you provide `null`, then that component will be hidden. diff --git a/apps/examples/src/examples/custom-quick-actions/CustomQuickActions.tsx b/apps/examples/src/examples/custom-quick-actions/CustomQuickActions.tsx new file mode 100644 index 000000000..9924b7023 --- /dev/null +++ b/apps/examples/src/examples/custom-quick-actions/CustomQuickActions.tsx @@ -0,0 +1,29 @@ +import { + Button, + DefaultQuickActions, + DefaultQuickActionsContent, + TLComponents, + Tldraw, +} from '@tldraw/tldraw' +import '@tldraw/tldraw/tldraw.css' + +function CustomQuickActions() { + return ( + + + + + + + ) +} + +const components: TLComponents = { + StylePanel: CustomStylePanel, // null will hide the panel instead +} + +export default function CustomStylePanelExample() { + return ( +
+ +
+ ) +} diff --git a/apps/examples/src/examples/custom-style-panel/README.md b/apps/examples/src/examples/custom-style-panel/README.md new file mode 100644 index 000000000..0843479a9 --- /dev/null +++ b/apps/examples/src/examples/custom-style-panel/README.md @@ -0,0 +1,11 @@ +--- +title: Custom style panel +component: ./CustomStylePanelExample.tsx +category: ui +--- + +You can customize tldraw's style panel or remove it entirely. + +--- + +The style panel can be customized by providing a `StylePanel` component to the `Tldraw` component's `uiComponents` prop. If you provide `null`, then that component will be hidden instead. diff --git a/apps/examples/src/examples/custom-styles/CustomStylesExample.tsx b/apps/examples/src/examples/custom-styles/CustomStylesExample.tsx index 99135e264..50dd85c9c 100644 --- a/apps/examples/src/examples/custom-styles/CustomStylesExample.tsx +++ b/apps/examples/src/examples/custom-styles/CustomStylesExample.tsx @@ -2,7 +2,7 @@ import { Tldraw } from '@tldraw/tldraw' import '@tldraw/tldraw/tldraw.css' import { CardShapeTool, CardShapeUtil } from './CardShape' import { FilterStyleUi } from './FilterStyleUi' -import { uiOverrides } from './ui-overrides' +import { components, uiOverrides } from './ui-overrides' // There's a guide at the bottom of this file! @@ -19,6 +19,7 @@ export default function CustomStylesExample() { shapeUtils={customShapeUtils} tools={customTools} overrides={uiOverrides} + components={components} > diff --git a/apps/examples/src/examples/custom-styles/ui-overrides.ts b/apps/examples/src/examples/custom-styles/ui-overrides.tsx similarity index 66% rename from apps/examples/src/examples/custom-styles/ui-overrides.ts rename to apps/examples/src/examples/custom-styles/ui-overrides.tsx index fd6acf743..650a97629 100644 --- a/apps/examples/src/examples/custom-styles/ui-overrides.ts +++ b/apps/examples/src/examples/custom-styles/ui-overrides.tsx @@ -1,4 +1,12 @@ -import { TLUiMenuGroup, TLUiOverrides, menuItem, toolbarItem } from '@tldraw/tldraw' +import { + DefaultKeyboardShortcutsDialog, + DefaultKeyboardShortcutsDialogContent, + TLComponents, + TLUiOverrides, + TldrawUiMenuItem, + toolbarItem, + useTools, +} from '@tldraw/tldraw' // There's a guide at the bottom of this file! @@ -9,7 +17,6 @@ export const uiOverrides: TLUiOverrides = { icon: 'color', label: 'Card' as any, kbd: 'c', - readonlyOk: false, onSelect: () => { editor.setCurrentTool('card') }, @@ -20,12 +27,19 @@ export const uiOverrides: TLUiOverrides = { toolbar.splice(4, 0, toolbarItem(tools.card)) return toolbar }, - keyboardShortcutsMenu(_app, keyboardShortcutsMenu, { tools }) { - const toolsGroup = keyboardShortcutsMenu.find( - (group) => group.id === 'shortcuts-dialog.tools' - ) as TLUiMenuGroup - toolsGroup.children.push(menuItem(tools.card)) - return keyboardShortcutsMenu +} + +export const components: TLComponents = { + KeyboardShortcutsDialog: (props) => { + const tools = useTools() + + return ( + + + {/* Ideally, we'd interleave this into the tools section */} + + + ) }, } diff --git a/apps/examples/src/examples/custom-toolbar/CustomToolbarExample.tsx b/apps/examples/src/examples/custom-toolbar/CustomToolbarExample.tsx new file mode 100644 index 000000000..8f33e0f13 --- /dev/null +++ b/apps/examples/src/examples/custom-toolbar/CustomToolbarExample.tsx @@ -0,0 +1,23 @@ +import { TLComponents, Tldraw } from '@tldraw/tldraw' +import { DefaultToolbar } from '@tldraw/tldraw/src/lib/ui/components/Toolbar/DefaultToolbar' +import '@tldraw/tldraw/tldraw.css' + +function CustomToolbar() { + return ( +
+ +
+ ) +} + +const components: TLComponents = { + Toolbar: CustomToolbar, // null will hide the panel instead +} + +export default function CustomToolbarExample() { + return ( +
+ +
+ ) +} diff --git a/apps/examples/src/examples/custom-toolbar/README.md b/apps/examples/src/examples/custom-toolbar/README.md new file mode 100644 index 000000000..cd6c74a2d --- /dev/null +++ b/apps/examples/src/examples/custom-toolbar/README.md @@ -0,0 +1,11 @@ +--- +title: Custom toolbar +component: ./CustomToolbarExample.tsx +category: ui +--- + +You can customize tldraw's toolbar or remove it entirely. + +--- + +The toolbar can be customized by providing a `Toolbar` component to the `Tldraw` component's `uiComponents` prop. If you provide `null`, then that component will be hidden instead. diff --git a/apps/examples/src/examples/custom-zoom-menu/CustomZoomMenuExample.tsx b/apps/examples/src/examples/custom-zoom-menu/CustomZoomMenuExample.tsx new file mode 100644 index 000000000..2ada070e6 --- /dev/null +++ b/apps/examples/src/examples/custom-zoom-menu/CustomZoomMenuExample.tsx @@ -0,0 +1,40 @@ +import { + DefaultZoomMenu, + DefaultZoomMenuContent, + TLComponents, + Tldraw, + TldrawUiMenuGroup, + TldrawUiMenuItem, +} from '@tldraw/tldraw' +import '@tldraw/tldraw/tldraw.css' + +function CustomZoomMenu() { + return ( + + + { + window.open('https://x.com/tldraw', '_blank') + }} + /> + + + + ) +} + +const components: TLComponents = { + ZoomMenu: CustomZoomMenu, +} + +export default function CustomZoomMenuExample() { + return ( +
+ +
+ ) +} diff --git a/apps/examples/src/examples/custom-zoom-menu/README.md b/apps/examples/src/examples/custom-zoom-menu/README.md new file mode 100644 index 000000000..6e64a5a0c --- /dev/null +++ b/apps/examples/src/examples/custom-zoom-menu/README.md @@ -0,0 +1,11 @@ +--- +title: Custom zoom menu +component: ./CustomZoomMenuExample.tsx +category: ui +--- + +You can customize tldraw's zoom menu. + +--- + +The zoom menu can be customized by providing a `ZoomMenu` component to the `Tldraw` component's `uiComponents` prop. If you provide `null`, then that component will be hidden instead. diff --git a/apps/examples/src/examples/exploded/ExplodedExample.tsx b/apps/examples/src/examples/exploded/ExplodedExample.tsx index 09c6e3d87..e2359f3d1 100644 --- a/apps/examples/src/examples/exploded/ExplodedExample.tsx +++ b/apps/examples/src/examples/exploded/ExplodedExample.tsx @@ -1,6 +1,7 @@ import { Canvas, ContextMenu, + DefaultContextMenuContent, TldrawEditor, TldrawHandles, TldrawHoveredShapeIndicator, @@ -38,8 +39,8 @@ export default function ExplodedExample() { persistenceKey="exploded-example" > - - + }> + diff --git a/apps/examples/src/examples/keyboard-shortcuts/KeyboardShortcuts.tsx b/apps/examples/src/examples/keyboard-shortcuts/KeyboardShortcuts.tsx index 2e245e291..f8489e242 100644 --- a/apps/examples/src/examples/keyboard-shortcuts/KeyboardShortcuts.tsx +++ b/apps/examples/src/examples/keyboard-shortcuts/KeyboardShortcuts.tsx @@ -1,11 +1,4 @@ -import { - TLUiActionsContextType, - TLUiMenuGroup, - TLUiOverrides, - TLUiToolsContextType, - Tldraw, - menuItem, -} from '@tldraw/tldraw' +import { TLUiActionsContextType, TLUiOverrides, TLUiToolsContextType, Tldraw } from '@tldraw/tldraw' import '@tldraw/tldraw/tldraw.css' import jsonSnapshot from './snapshot.json' @@ -15,7 +8,6 @@ import jsonSnapshot from './snapshot.json' const overrides: TLUiOverrides = { //[a] actions(_editor, actions): TLUiActionsContextType { - actions['copy-as-png'].kbd = '$1' actions['toggle-grid'].kbd = 'x' return actions }, @@ -24,15 +16,6 @@ const overrides: TLUiOverrides = { tools['draw'].kbd = 'p' return tools }, - //[c] - keyboardShortcutsMenu(_editor, shortcutsMenu, { actions }) { - const editGroup = shortcutsMenu.find( - (group) => group.id === 'shortcuts-dialog.edit' - ) as TLUiMenuGroup - - editGroup.children.push(menuItem(actions['copy-as-png'])) - return shortcutsMenu - }, } // [2] @@ -75,15 +58,6 @@ add a new shortcut to the keyboard shortcuts dialog [c]. We're overriding the draw tool's shortcut to 'p', maybe we want to rename it to the pen tool or something. -[c] keyboardShortcutsMenu - This function takes 3 arguments, the editor instance (which we don't need), the menu - schema, and the ui context. The shortcutsMenu is an array, so we'll need to use the - find method to return the edit group and add our new menu item to it. Check out the - useKeyboardShortcutsSchema.tsx file in the tldraw repo to see the full list of groups - and the menu items they contain. menuItem() is a helper function that creates a new menu - item for us, we just need to pass it an action or tool. We'll use the copy-as-png action - that we modified in [a], we can grab it from the ui context's actions object. - [2] Finally, we pass our overrides object into the Tldraw component's overrides prop. Now when the component mounts, our overrides will be applied. If you open the keyboard shortcuts diff --git a/apps/examples/src/examples/only-editor/OnlyEditor.tsx b/apps/examples/src/examples/only-editor/OnlyEditor.tsx index b6fd1fc2f..368855622 100644 --- a/apps/examples/src/examples/only-editor/OnlyEditor.tsx +++ b/apps/examples/src/examples/only-editor/OnlyEditor.tsx @@ -1,6 +1,6 @@ /* eslint-disable import/no-extraneous-dependencies */ -import { Editor, PositionedOnCanvas, TldrawEditor, createShapeId, track } from '@tldraw/editor' +import { Editor, TldrawEditor, createShapeId } from '@tldraw/editor' import { MiniBoxShapeUtil } from './MiniBoxShape' import { MiniSelectTool } from './MiniSelectTool' @@ -32,24 +32,28 @@ export default function OnlyEditorExample() { ]) }} components={{ - Background: BackgroundComponent, + // [3] + OnTheCanvas: () => { + return ( +
+

Double click to create or delete shapes.

+

Click or Shift+Click to select shapes.

+

Click and drag to move shapes.

+
+ ) + }, }} />
) } -// [3] -const BackgroundComponent = track(() => { - return ( - -

Double click to create or delete shapes.

-

Click or Shift+Click to select shapes.

-

Click and drag to move shapes.

-
- ) -}) - /* This example shows how to use the TldrawEditor component on its own. This is useful if you want to create your own custom UI, shape and tool interactions. diff --git a/apps/examples/src/examples/screenshot-tool/ScreenshotToolExample.tsx b/apps/examples/src/examples/screenshot-tool/ScreenshotToolExample.tsx index e0cf1bfe2..b76f7803d 100644 --- a/apps/examples/src/examples/screenshot-tool/ScreenshotToolExample.tsx +++ b/apps/examples/src/examples/screenshot-tool/ScreenshotToolExample.tsx @@ -26,7 +26,6 @@ const customUiOverrides: TLUiOverrides = { screenshot: { id: 'screenshot', label: 'Screenshot', - readonlyOk: false, icon: 'tool-screenshot', kbd: 'j', onSelect() { diff --git a/apps/examples/src/examples/speech-bubble/CustomShapeWithHandles.tsx b/apps/examples/src/examples/speech-bubble/CustomShapeWithHandles.tsx index 388ce48b8..a658cb1ed 100644 --- a/apps/examples/src/examples/speech-bubble/CustomShapeWithHandles.tsx +++ b/apps/examples/src/examples/speech-bubble/CustomShapeWithHandles.tsx @@ -2,7 +2,7 @@ import { Tldraw } from '@tldraw/tldraw' import '@tldraw/tldraw/tldraw.css' import { SpeechBubbleTool } from './SpeechBubble/SpeechBubbleTool' import { SpeechBubbleUtil } from './SpeechBubble/SpeechBubbleUtil' -import { customAssetUrls, uiOverrides } from './SpeechBubble/ui-overrides' +import { components, customAssetUrls, uiOverrides } from './SpeechBubble/ui-overrides' import './customhandles.css' // There's a guide at the bottom of this file! @@ -20,6 +20,7 @@ export default function CustomShapeWithHandles() { tools={tools} overrides={uiOverrides} assetUrls={customAssetUrls} + components={components} persistenceKey="whatever" /> diff --git a/apps/examples/src/examples/speech-bubble/SpeechBubble/ui-overrides.ts b/apps/examples/src/examples/speech-bubble/SpeechBubble/ui-overrides.tsx similarity index 80% rename from apps/examples/src/examples/speech-bubble/SpeechBubble/ui-overrides.ts rename to apps/examples/src/examples/speech-bubble/SpeechBubble/ui-overrides.tsx index 2d070c8d4..38231fa6b 100644 --- a/apps/examples/src/examples/speech-bubble/SpeechBubble/ui-overrides.ts +++ b/apps/examples/src/examples/speech-bubble/SpeechBubble/ui-overrides.tsx @@ -1,9 +1,12 @@ import { + DefaultKeyboardShortcutsDialog, + DefaultKeyboardShortcutsDialogContent, + TLComponents, TLUiAssetUrlOverrides, - TLUiMenuGroup, TLUiOverrides, - menuItem, + TldrawUiMenuItem, toolbarItem, + useTools, } from '@tldraw/tldraw' // There's a guide at the bottom of this file! @@ -16,7 +19,6 @@ export const uiOverrides: TLUiOverrides = { icon: 'speech-bubble', label: 'Speech Bubble', kbd: 's', - readonlyOk: false, onSelect: () => { editor.setCurrentTool('speech-bubble') }, @@ -27,13 +29,6 @@ export const uiOverrides: TLUiOverrides = { toolbar.splice(4, 0, toolbarItem(tools.speech)) return toolbar }, - keyboardShortcutsMenu(_app, keyboardShortcutsMenu, { tools }) { - const toolsGroup = keyboardShortcutsMenu.find( - (group) => group.id === 'shortcuts-dialog.tools' - ) as TLUiMenuGroup - toolsGroup.children.push(menuItem(tools.speech)) - return keyboardShortcutsMenu - }, } // [2] @@ -44,6 +39,18 @@ export const customAssetUrls: TLUiAssetUrlOverrides = { }, } +export const components: TLComponents = { + KeyboardShortcutsDialog: (props) => { + const tools = useTools() + return ( + + + + + ) + }, +} + /* This file contains overrides for the Tldraw UI. These overrides are used to add your custom tools diff --git a/apps/examples/src/examples/ui-components-hidden/README.md b/apps/examples/src/examples/ui-components-hidden/README.md new file mode 100644 index 000000000..e23ac3176 --- /dev/null +++ b/apps/examples/src/examples/ui-components-hidden/README.md @@ -0,0 +1,11 @@ +--- +title: Hidden UI components +component: ./UiComponentsHiddenExample.tsx +category: ui +--- + +You can hide tldraw's UI components. + +--- + +Custom UI components can be hidden providing a `null` as the value for a component in `uiComponents`. In this case, all configurable UI components are hidden. diff --git a/apps/examples/src/examples/ui-components-hidden/UiComponentsHiddenExample.tsx b/apps/examples/src/examples/ui-components-hidden/UiComponentsHiddenExample.tsx new file mode 100644 index 000000000..386ba933c --- /dev/null +++ b/apps/examples/src/examples/ui-components-hidden/UiComponentsHiddenExample.tsx @@ -0,0 +1,28 @@ +import { TLUiComponents, Tldraw } from '@tldraw/tldraw' +import '@tldraw/tldraw/tldraw.css' + +// The type here is include only to ensure this example contains all possible ui components, +const components: Required = { + ContextMenu: null, + ActionsMenu: null, + HelpMenu: null, + ZoomMenu: null, + MainMenu: null, + Minimap: null, + StylePanel: null, + PageMenu: null, + NavigationPanel: null, + Toolbar: null, + KeyboardShortcutsDialog: null, + QuickActions: null, + HelperButtons: null, + DebugMenu: null, +} + +export default function UiComponentsHiddenExample() { + return ( +
+ +
+ ) +} diff --git a/apps/vscode/editor/src/Links.tsx b/apps/vscode/editor/src/Links.tsx new file mode 100644 index 000000000..adfc21d82 --- /dev/null +++ b/apps/vscode/editor/src/Links.tsx @@ -0,0 +1,45 @@ +import { TldrawUiMenuGroup, TldrawUiMenuItem } from '@tldraw/tldraw' +import { openUrl } from './utils/url' + +export function Links() { + return ( + + { + openUrl('https://github.com/tldraw/tldraw') + }} + /> + { + openUrl('https://twitter.com/tldraw') + }} + /> + { + openUrl('https://discord.gg/SBBEVCA4PG') + }} + /> + { + openUrl('https://tldraw.dev') + }} + /> + + ) +} diff --git a/apps/vscode/editor/src/app.tsx b/apps/vscode/editor/src/app.tsx index bf35df53d..7d727f038 100644 --- a/apps/vscode/editor/src/app.tsx +++ b/apps/vscode/editor/src/app.tsx @@ -1,15 +1,24 @@ -import { linksUiOverrides } from './utils/links' // eslint-disable-next-line import/no-internal-modules import '@tldraw/tldraw/tldraw.css' // eslint-disable-next-line import/no-internal-modules import { getAssetUrlsByImport } from '@tldraw/assets/imports' -import { Editor, ErrorBoundary, TLUiMenuSchema, Tldraw, setRuntimeOverrides } from '@tldraw/tldraw' +import { + DefaultHelpMenu, + DefaultHelpMenuContent, + Editor, + ErrorBoundary, + TLComponents, + Tldraw, + TldrawUiMenuGroup, + setRuntimeOverrides, +} from '@tldraw/tldraw' import { useCallback, useEffect, useMemo, useState } from 'react' import { VscodeMessage } from '../../messages' import '../public/index.css' import { ChangeResponder } from './ChangeResponder' import { FileOpen } from './FileOpen' import { FullPageMessage } from './FullPageMessage' +import { Links } from './Links' import { onCreateAssetFromUrl } from './utils/bookmarks' import { vscode } from './utils/vscode' @@ -53,24 +62,6 @@ export function WrappedTldrawEditor() { ) } -const menuOverrides = { - menu: (_editor: Editor, schema: TLUiMenuSchema, _helpers: any) => { - schema.forEach((item) => { - if (item.id === 'menu' && item.type === 'group') { - item.children = item.children.filter((menuItem) => { - if (!menuItem) return false - if (menuItem.id === 'file' && menuItem.type === 'submenu') { - return false - } - return true - }) - } - }) - - return schema - }, -} - export const TldrawWrapper = () => { const [tldrawInnerProps, setTldrawInnerProps] = useState(null) @@ -114,6 +105,16 @@ export type TLDrawInnerProps = { isDarkMode: boolean } +const components: TLComponents = { + HelpMenu: () => ( + + + + + + + ), +} function TldrawInner({ uri, assetSrc, isDarkMode, fileContents }: TLDrawInnerProps) { const assetUrls = useMemo(() => getAssetUrlsByImport({ baseUrl: assetSrc }), [assetSrc]) @@ -126,10 +127,11 @@ function TldrawInner({ uri, assetSrc, isDarkMode, fileContents }: TLDrawInnerPro assetUrls={assetUrls} persistenceKey={uri} onMount={handleMount} - overrides={[menuOverrides, linksUiOverrides]} + components={components} autoFocus > {/* */} +
diff --git a/apps/vscode/editor/src/utils/links.ts b/apps/vscode/editor/src/utils/links.ts deleted file mode 100644 index a00bdc6f5..000000000 --- a/apps/vscode/editor/src/utils/links.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { menuGroup, menuItem, TLUiOverrides } from '@tldraw/tldraw' -import { openUrl } from './openUrl' - -export const GITHUB_URL = 'https://github.com/tldraw/tldraw' - -const linksMenuGroup = menuGroup( - 'links', - menuItem({ - id: 'github', - label: 'help-menu.github', - readonlyOk: true, - icon: 'github', - onSelect() { - openUrl(GITHUB_URL) - }, - }), - menuItem({ - id: 'twitter', - label: 'help-menu.twitter', - icon: 'twitter', - readonlyOk: true, - onSelect() { - openUrl('https://twitter.com/tldraw') - }, - }), - menuItem({ - id: 'discord', - label: 'help-menu.discord', - icon: 'discord', - readonlyOk: true, - onSelect() { - openUrl('https://discord.gg/SBBEVCA4PG') - }, - }), - menuItem({ - id: 'about', - label: 'help-menu.about', - icon: 'external-link', - readonlyOk: true, - onSelect() { - openUrl('https://www.tldraw.dev') - }, - }) -)! - -export const linksUiOverrides: TLUiOverrides = { - helpMenu(editor, schema) { - schema.push(linksMenuGroup) - return schema - }, - menu(editor, schema, { isMobile }) { - if (isMobile) { - schema.push(linksMenuGroup) - } - return schema - }, -} diff --git a/apps/vscode/editor/src/utils/openUrl.ts b/apps/vscode/editor/src/utils/url.ts similarity index 100% rename from apps/vscode/editor/src/utils/openUrl.ts rename to apps/vscode/editor/src/utils/url.ts diff --git a/assets/translations/sl.json b/assets/translations/sl.json index d1bd0118f..42a38760c 100644 --- a/assets/translations/sl.json +++ b/assets/translations/sl.json @@ -1,358 +1,358 @@ { - "action.convert-to-bookmark": "Pretvori v zaznamek", - "action.convert-to-embed": "Pretvori v vdelavo", - "action.open-embed-link": "Odpri povezavo", - "action.align-bottom": "Poravnaj dno", - "action.align-center-horizontal": "Poravnaj vodoravno", - "action.align-center-vertical": "Poravnaj navpično", - "action.align-center-horizontal.short": "Poravnaj vodoravno", - "action.align-center-vertical.short": "Poravnaj navpično", - "action.align-left": "Poravnaj levo", - "action.align-right": "Poravnaj desno", - "action.align-top": "Poravnaj vrh", - "action.back-to-content": "Nazaj na vsebino", - "action.bring-forward": "Premakni naprej", - "action.bring-to-front": "Premakni v ospredje", - "action.copy-as-json.short": "JSON", - "action.copy-as-json": "Kopiraj kot JSON", - "action.copy-as-png.short": "PNG", - "action.copy-as-png": "Kopiraj kot PNG", - "action.copy-as-svg.short": "SVG", - "action.copy-as-svg": "Kopiraj kot SVG", - "action.copy": "Kopiraj", - "action.cut": "Izreži", - "action.delete": "Izbriši", - "action.distribute-horizontal": "Porazdeli vodoravno", - "action.distribute-vertical": "Porazdeli navpično", - "action.distribute-horizontal.short": "Porazdeli vodoravno", - "action.distribute-vertical.short": "Porazdeli navpično", - "action.duplicate": "Podvoji", - "action.edit-link": "Uredi povezavo", - "action.exit-pen-mode": "Zapustite način peresa", - "action.export-as-json.short": "JSON", - "action.export-as-json": "Izvozi kot JSON", - "action.export-as-png.short": "PNG", - "action.export-as-png": "Izvozi kot PNG", - "action.export-as-svg.short": "SVG", - "action.export-as-svg": "Izvozi kot SVG", - "action.flip-horizontal": "Zrcali vodoravno", - "action.flip-vertical": "Zrcali navpično", - "action.flip-horizontal.short": "Zrcali horizontalno", - "action.flip-vertical.short": "Zrcali vertikalno", - "action.group": "Združi", - "action.insert-media": "Naloži predstavnost", - "action.new-shared-project": "Nov skupni projekt", - "action.open-file": "Odpri datoteko", - "action.pack": "Spakiraj", - "action.paste": "Prilepi", - "action.print": "Natisni", - "action.redo": "Uveljavi", - "action.rotate-ccw": "Zavrti v nasprotni smeri urinega kazalca", - "action.rotate-cw": "Zavrti v smeri urinega kazalca", - "action.save-copy": "Shrani kopijo", - "action.select-all": "Izberi vse", - "action.select-none": "Počisti izbiro", - "action.send-backward": "Pošlji nazaj", - "action.send-to-back": "Pošlji v ozadje", - "action.share-project": "Deli ta projekt", - "action.stack-horizontal": "Naloži vodoravno", - "action.stack-vertical": "Naloži navpično", - "action.stack-horizontal.short": "Naloži vodoravno", - "action.stack-vertical.short": "Naloži navpično", - "action.stretch-horizontal": "Raztegnite vodoravno", - "action.stretch-vertical": "Raztegni navpično", - "action.stretch-horizontal.short": "Raztezanje vodoravno", - "action.stretch-vertical.short": "Raztezanje navpično", - "action.toggle-auto-size": "Preklopi samodejno velikost", - "action.toggle-dark-mode.menu": "Temni način", - "action.toggle-dark-mode": "Preklopi temni način", - "action.toggle-debug-mode.menu": "Način odpravljanja napak", - "action.toggle-debug-mode": "Preklopi način odpravljanja napak", - "action.toggle-focus-mode.menu": "Osredotočen način", - "action.toggle-focus-mode": "Preklopi na osredotočen način", - "action.toggle-grid.menu": "Prikaži mrežo", - "action.toggle-grid": "Preklopi mrežo", - "action.toggle-snap-mode.menu": "Vedno pripni", - "action.toggle-snap-mode": "Preklopi pripenjanje", - "action.toggle-tool-lock.menu": "Zaklepanje orodja", - "action.toggle-tool-lock": "Preklopi zaklepanje orodja", - "action.toggle-transparent.context-menu": "Prozorno", - "action.toggle-transparent.menu": "Prozorno", - "action.toggle-transparent": "Preklopi prosojno ozadje", - "action.undo": "Razveljavi", - "action.ungroup": "Razdruži", - "action.zoom-in": "Povečaj", - "action.zoom-out": "Pomanjšaj", - "action.zoom-to-100": "Povečaj na 100 %", - "action.zoom-to-fit": "Povečaj do prileganja", - "action.zoom-to-selection": "Pomakni na izbiro", - "color-style.black": "Črna", - "color-style.blue": "Modra", - "color-style.green": "Zelena", - "color-style.grey": "Siva", - "color-style.light-blue": "Svetlo modra", - "color-style.light-green": "Svetlo zelena", - "color-style.light-red": "Svetlo rdeča", - "color-style.light-violet": "Svetlo vijolična", - "color-style.orange": "Oranžna", - "color-style.red": "Rdeča", - "color-style.violet": "Vijolična", - "color-style.yellow": "Rumena", - "fill-style.none": "Brez", - "fill-style.semi": "Polovično", - "fill-style.solid": "Polno", - "fill-style.pattern": "Vzorec", - "dash-style.dashed": "Črtkano", - "dash-style.dotted": "Pikčasto", - "dash-style.draw": "Narisano", - "dash-style.solid": "Polno", - "size-style.s": "Malo", - "size-style.m": "Srednje", - "size-style.l": "Veliko", - "size-style.xl": "Zelo veliko", - "opacity-style.0.1": "10 %", - "opacity-style.0.25": "25 %", - "opacity-style.0.5": "50 %", - "opacity-style.0.75": "75 %", - "opacity-style.1": "100 %", - "font-style.draw": "Draw", - "font-style.sans": "Sans", - "font-style.serif": "Serif", - "font-style.mono": "Mono", - "align-style.start": "Začetek", - "align-style.middle": "Sredina", - "align-style.end": "Konec", - "align-style.justify": "Poravnaj", - "geo-style.arrow-down": "Puščica navzdol", - "geo-style.arrow-left": "Puščica levo", - "geo-style.arrow-right": "Puščica desno", - "geo-style.arrow-up": "Puščica navzgor", - "geo-style.diamond": "Diamant", - "geo-style.ellipse": "Elipsa", - "geo-style.hexagon": "Šesterokotnik", - "geo-style.octagon": "Osmerokotnik", - "geo-style.oval": "Oval", - "geo-style.pentagon": "Peterokotnik", - "geo-style.rectangle": "Pravokotnik", - "geo-style.rhombus-2": "Romb 2", - "geo-style.rhombus": "Romb", - "geo-style.star": "Zvezda", - "geo-style.trapezoid": "Trapez", - "geo-style.triangle": "Trikotnik", - "geo-style.x-box": "X polje", - "arrowheadStart-style.none": "Brez", - "arrowheadStart-style.arrow": "Puščica", - "arrowheadStart-style.bar": "Črta", - "arrowheadStart-style.diamond": "Diamant", - "arrowheadStart-style.dot": "Pika", - "arrowheadStart-style.inverted": "Obrnjeno", - "arrowheadStart-style.pipe": "Cev", - "arrowheadStart-style.square": "Kvadrat", - "arrowheadStart-style.triangle": "Trikotnik", - "arrowheadEnd-style.none": "Brez", - "arrowheadEnd-style.arrow": "Puščica", - "arrowheadEnd-style.bar": "Črta", - "arrowheadEnd-style.diamond": "Diamant", - "arrowheadEnd-style.dot": "Pika", - "arrowheadEnd-style.inverted": "Obrnjeno", - "arrowheadEnd-style.pipe": "Cev", - "arrowheadEnd-style.square": "Kvadrat", - "arrowheadEnd-style.triangle": "Trikotnik", - "spline-style.line": "Črta", - "spline-style.cubic": "Kubično", - "tool.select": "Izbor", - "tool.hand": "Roka", - "tool.draw": "Risanje", - "tool.eraser": "Radirka", - "tool.arrow-down": "Puščica navzdol", - "tool.arrow-left": "Puščica levo", - "tool.arrow-right": "Puščica desno", - "tool.arrow-up": "Puščica navzgor", - "tool.arrow": "Puščica", - "tool.diamond": "Diamant", - "tool.ellipse": "Elipsa", - "tool.hexagon": "Šesterokotnik", - "tool.line": "Črta", - "tool.octagon": "Osmerokotnik", - "tool.oval": "Oval", - "tool.pentagon": "Peterokotnik", - "tool.rectangle": "Pravokotnik", - "tool.rhombus": "Romb", - "tool.star": "Zvezda", - "tool.trapezoid": "Trapez", - "tool.triangle": "Trikotnik", - "tool.x-box": "X polje", - "tool.asset": "Sredstvo", - "tool.frame": "Okvir", - "tool.note": "Opomba", - "tool.embed": "Vdelava", - "tool.text": "Besedilo", - "menu.title": "Meni", - "menu.copy-as": "Kopiraj kot", - "menu.edit": "Uredi", - "menu.export-as": "Izvozi kot", - "menu.file": "Datoteka", - "menu.language": "Jezik", - "menu.preferences": "Nastavitve", - "menu.view": "Pogled", - "context-menu.arrange": "Preuredi", - "context-menu.copy-as": "Kopiraj kot", - "context-menu.export-as": "Izvozi kot", - "context-menu.move-to-page": "Premakni na stran", - "context-menu.reorder": "Preuredite", - "page-menu.title": "Strani", - "page-menu.create-new-page": "Ustvari novo stran", - "page-menu.max-page-count-reached": "Doseženo največje število strani", - "page-menu.new-page-initial-name": "Stran 1", - "page-menu.edit-start": "Uredi", - "page-menu.edit-done": "Zaključi", - "page-menu.submenu.rename": "Preimenuj", - "page-menu.submenu.duplicate-page": "Podvoji", - "page-menu.submenu.title": "Meni", - "page-menu.submenu.move-down": "Premakni navzdol", - "page-menu.submenu.move-up": "Premakni navzgor", - "page-menu.submenu.delete": "Izbriši", - "share-menu.title": "Deli", - "share-menu.share-project": "Deli ta projekt", - "share-menu.copy-link": "Kopiraj povezavo", - "share-menu.readonly-link": "Samo za branje", - "share-menu.copy-readonly-link": "Kopiraj povezavo samo za branje", - "share-menu.offline-note": "Skupna raba tega projekta bo ustvarila živo kopijo na novem URL-ju. URL lahko delite z do tridesetimi drugimi osebami, s katerimi lahko skupaj gledate in urejate vsebino.", - "share-menu.copy-link-note": "Vsakdo s povezavo si bo lahko ogledal in urejal ta projekt.", - "share-menu.copy-readonly-link-note": "Vsakdo s povezavo si bo lahko ogledal (vendar ne urejal) ta projekt.", - "share-menu.project-too-large": "Žal tega projekta ni mogoče deliti, ker je prevelik. Delamo na tem!", - "people-menu.title": "Ljudje", - "people-menu.change-name": "Spremeni ime", - "people-menu.change-color": "Spremeni barvo", - "people-menu.user": "(Ti)", - "people-menu.invite": "Povabi ostale", - "help-menu.title": "Pomoč in viri", - "help-menu.about": "O nas", - "help-menu.discord": "Discord", - "help-menu.github": "GitHub", - "help-menu.keyboard-shortcuts": "Bližnjice na tipkovnici", - "help-menu.twitter": "Twitter", - "actions-menu.title": "Akcije", - "edit-link-dialog.title": "Uredi povezavo", - "edit-link-dialog.invalid-url": "Povezava mora biti veljavna", - "edit-link-dialog.detail": "Povezave se bodo odprle v novem zavihku.", - "edit-link-dialog.url": "URL", - "edit-link-dialog.clear": "Počisti", - "edit-link-dialog.save": "Nadaljuj", - "edit-link-dialog.cancel": "Prekliči", - "embed-dialog.title": "Ustvari vdelavo", - "embed-dialog.back": "Nazaj", - "embed-dialog.create": "Ustvari", - "embed-dialog.cancel": "Prekliči", - "embed-dialog.url": "URL", - "embed-dialog.instruction": "Prilepite URL spletnega mesta, da ustvarite vdelavo.", - "embed-dialog.invalid-url": "Iz tega URL-ja nismo mogli ustvariti vdelave.", - "edit-pages-dialog.move-down": "Premakni navzdol", - "edit-pages-dialog.move-up": "Premakni navzgor", - "shortcuts-dialog.title": "Bližnjice na tipkovnici", - "shortcuts-dialog.edit": "Uredi", - "shortcuts-dialog.file": "Datoteka", - "shortcuts-dialog.preferences": "Nastavitve", - "shortcuts-dialog.tools": "Orodja", - "shortcuts-dialog.transform": "Preoblikuj", - "shortcuts-dialog.view": "Pogled", - "style-panel.title": "Stili", - "style-panel.align": "Poravnava", - "style-panel.arrowheads": "Puščice", - "style-panel.color": "Barva", - "style-panel.dash": "Črtasto", - "style-panel.fill": "Polnilo", - "style-panel.font": "Pisava", - "style-panel.geo": "Oblika", - "style-panel.mixed": "Mešano", - "style-panel.opacity": "Motnost", - "style-panel.size": "Velikost", - "style-panel.spline": "Krivulja", - "tool-panel.drawing": "Risanje", - "tool-panel.shapes": "Oblike", - "navigation-zone.toggle-minimap": "Preklopi mini zemljevid", - "navigation-zone.zoom": "Povečava", - "focus-mode.toggle-focus-mode": "Preklopi na osredotočen način", - "toast.close": "Zapri", - "file-system.file-open-error.title": "Datoteke ni bilo mogoče odpreti", - "file-system.file-open-error.not-a-tldraw-file": "Datoteka, ki ste jo poskušali odpreti, ni videti kot datoteka tldraw.", - "file-system.file-open-error.file-format-version-too-new": "Datoteka, ki ste jo poskušali odpreti, je iz novejše različice tldraw. Ponovno naložite stran in poskusite znova.", - "file-system.file-open-error.generic-corrupted-file": "Datoteka, ki ste jo poskušali odpreti, je poškodovana.", - "file-system.confirm-open.title": "Prepiši trenutni projekt?", - "file-system.confirm-open.description": "Če ustvarite nov projekt, boste izbrisali trenutni projekt in vse neshranjene spremembe bodo izgubljene. Ste prepričani, da želite nadaljevati?", - "file-system.confirm-open.cancel": "Prekliči", - "file-system.confirm-open.open": "Odpri datoteko", - "file-system.confirm-open.dont-show-again": "Ne sprašuj znova", - "toast.error.export-fail.title": "Izvoz ni uspel", - "toast.error.export-fail.desc": "Izvoz slike ni uspel", - "toast.error.copy-fail.title": "Kopiranje ni uspelo", - "toast.error.copy-fail.desc": "Kopiranje slike ni uspelo", - "file-system.shared-document-file-open-error.title": "Datoteke ni bilo mogoče odpreti", - "file-system.shared-document-file-open-error.description": "Odpiranje datotek v skupnih projektih ni podprto.", - "vscode.file-open.dont-show-again": "Ne sprašuj znova", - "vscode.file-open.desc": "Ta datoteka je bila ustvarjena s starejšo različico tldraw. Ali jo želite posodobiti, da bo deloval z novo različico?", - "context.pages.new-page": "Nova stran", - "style-panel.arrowhead-start": "Začetek", - "style-panel.arrowhead-end": "Konec", - "vscode.file-open.open": "Nadaljuj", - "vscode.file-open.backup": "Varnostna kopija", - "vscode.file-open.backup-saved": "Varnostna kopija shranjena", - "vscode.file-open.backup-failed": "Varnostno kopiranje ni uspelo: to ni datoteka .tldr.", - "tool-panel.more": "Več", - "debug-panel.more": "Več", - "action.new-project": "Nov projekt", - "file-system.confirm-clear.title": "Počisti trenutni projekt?", - "file-system.confirm-clear.description": "Če ustvarite nov projekt, boste izbrisali trenutni projekt in vse neshranjene spremembe bodo izgubljene. Ste prepričani, da želite nadaljevati?", - "file-system.confirm-clear.cancel": "Prekliči", - "file-system.confirm-clear.continue": "Nadaljuj", - "file-system.confirm-clear.dont-show-again": "Ne sprašuj znova", - "action.stop-following": "Prenehaj slediti", - "people-menu.follow": "Sledi", - "style-panel.position": "Položaj", - "page-menu.go-to-page": "Pojdi na stran", - "action.insert-embed": "Vstavi vdelavo", - "people-menu.following": "Sledim", - "people-menu.leading": "Sledi vam", - "geo-style.check-box": "Potrditveno polje", - "tool.check-box": "Potrditveno polje", - "share-menu.create-snapshot-link": "Ustvari povezavo do posnetka", - "share-menu.save-note": "Ta projekt prenesite na svoj računalnik kot datoteko .tldr.", - "share-menu.fork-note": "Na podlagi tega posnetka ustvarite nov skupni projekt.", - "share-menu.snapshot-link-note": "Zajemite in delite ta projekt kot povezavo do posnetka samo za branje.", - "share-menu.upload-failed": "Oprostite, trenutno nismo mogli naložiti vašega projekta. Poskusite znova ali nam sporočite, če se težava ponovi.", - "style-panel.vertical-align": "Navpična poravnava", - "tool.laser": "Laser", - "action.fork-project": "Naredi kopijo projekta", - "action.leave-shared-project": "Zapusti skupni projekt", - "sharing.confirm-leave.title": "Zapusti trenutni projekt?", - "sharing.confirm-leave.description": "Ali ste prepričani, da želite zapustiti ta skupni projekt? Nanj se lahko vrnete tako, da se ponovno vrnete na njegov URL.", - "sharing.confirm-leave.cancel": "Prekliči", - "sharing.confirm-leave.leave": "Zapusti", - "sharing.confirm-leave.dont-show-again": "Ne sprašuj znova", - "action.toggle-reduce-motion.menu": "Zmanjšaj gibanje", - "action.toggle-reduce-motion": "Preklop zmanjšanja gibanja", - "tool.highlight": "Marker", - "action.toggle-lock": "Zakleni \/ odkleni", - "share-menu.default-project-name": "Skupni projekt", - "home-project-dialog.title": "Lokalni projekt", - "home-project-dialog.description": "To je vaš lokalni projekt. Namenjen je samo vam!", - "rename-project-dialog.title": "Preimenuj projekt", - "rename-project-dialog.cancel": "Prekliči", - "rename-project-dialog.rename": "Preimenuj", - "home-project-dialog.ok": "V redu", - "action.open-cursor-chat": "Klepet s kazalcem", - "shortcuts-dialog.collaboration": "Sodelovanje", - "cursor-chat.type-to-chat": "Vnesite za klepet ...", - "geo-style.cloud": "Oblak", - "tool.cloud": "Oblak", - "action.unlock-all": "Odkleni vse", - "status.offline": "Brez povezave", - "status.online": "Povezan", - "action.remove-frame": "Odstrani okvir", - "action.fit-frame-to-content": "Prilagodi vsebini", - "action.toggle-edge-scrolling.menu": "Pomikanje ob robovih", - "action.toggle-edge-scrolling": "Preklopi pomikanje ob robovih", - "verticalAlign-style.start": "Vrh", - "verticalAlign-style.middle": "Sredina", - "verticalAlign-style.end": "Dno" -} \ No newline at end of file + "action.convert-to-bookmark": "Pretvori v zaznamek", + "action.convert-to-embed": "Pretvori v vdelavo", + "action.open-embed-link": "Odpri povezavo", + "action.align-bottom": "Poravnaj dno", + "action.align-center-horizontal": "Poravnaj vodoravno", + "action.align-center-vertical": "Poravnaj navpično", + "action.align-center-horizontal.short": "Poravnaj vodoravno", + "action.align-center-vertical.short": "Poravnaj navpično", + "action.align-left": "Poravnaj levo", + "action.align-right": "Poravnaj desno", + "action.align-top": "Poravnaj vrh", + "action.back-to-content": "Nazaj na vsebino", + "action.bring-forward": "Premakni naprej", + "action.bring-to-front": "Premakni v ospredje", + "action.copy-as-json.short": "JSON", + "action.copy-as-json": "Kopiraj kot JSON", + "action.copy-as-png.short": "PNG", + "action.copy-as-png": "Kopiraj kot PNG", + "action.copy-as-svg.short": "SVG", + "action.copy-as-svg": "Kopiraj kot SVG", + "action.copy": "Kopiraj", + "action.cut": "Izreži", + "action.delete": "Izbriši", + "action.distribute-horizontal": "Porazdeli vodoravno", + "action.distribute-vertical": "Porazdeli navpično", + "action.distribute-horizontal.short": "Porazdeli vodoravno", + "action.distribute-vertical.short": "Porazdeli navpično", + "action.duplicate": "Podvoji", + "action.edit-link": "Uredi povezavo", + "action.exit-pen-mode": "Zapustite način peresa", + "action.export-as-json.short": "JSON", + "action.export-as-json": "Izvozi kot JSON", + "action.export-as-png.short": "PNG", + "action.export-as-png": "Izvozi kot PNG", + "action.export-as-svg.short": "SVG", + "action.export-as-svg": "Izvozi kot SVG", + "action.flip-horizontal": "Zrcali vodoravno", + "action.flip-vertical": "Zrcali navpično", + "action.flip-horizontal.short": "Zrcali horizontalno", + "action.flip-vertical.short": "Zrcali vertikalno", + "action.group": "Združi", + "action.insert-media": "Naloži predstavnost", + "action.new-shared-project": "Nov skupni projekt", + "action.open-file": "Odpri datoteko", + "action.pack": "Spakiraj", + "action.paste": "Prilepi", + "action.print": "Natisni", + "action.redo": "Uveljavi", + "action.rotate-ccw": "Zavrti v nasprotni smeri urinega kazalca", + "action.rotate-cw": "Zavrti v smeri urinega kazalca", + "action.save-copy": "Shrani kopijo", + "action.select-all": "Izberi vse", + "action.select-none": "Počisti izbiro", + "action.send-backward": "Pošlji nazaj", + "action.send-to-back": "Pošlji v ozadje", + "action.share-project": "Deli ta projekt", + "action.stack-horizontal": "Naloži vodoravno", + "action.stack-vertical": "Naloži navpično", + "action.stack-horizontal.short": "Naloži vodoravno", + "action.stack-vertical.short": "Naloži navpično", + "action.stretch-horizontal": "Raztegnite vodoravno", + "action.stretch-vertical": "Raztegni navpično", + "action.stretch-horizontal.short": "Raztezanje vodoravno", + "action.stretch-vertical.short": "Raztezanje navpično", + "action.toggle-auto-size": "Preklopi samodejno velikost", + "action.toggle-dark-mode.menu": "Temni način", + "action.toggle-dark-mode": "Preklopi temni način", + "action.toggle-debug-mode.menu": "Način odpravljanja napak", + "action.toggle-debug-mode": "Preklopi način odpravljanja napak", + "action.toggle-focus-mode.menu": "Osredotočen način", + "action.toggle-focus-mode": "Preklopi na osredotočen način", + "action.toggle-grid.menu": "Prikaži mrežo", + "action.toggle-grid": "Preklopi mrežo", + "action.toggle-snap-mode.menu": "Vedno pripni", + "action.toggle-snap-mode": "Preklopi pripenjanje", + "action.toggle-tool-lock.menu": "Zaklepanje orodja", + "action.toggle-tool-lock": "Preklopi zaklepanje orodja", + "action.toggle-transparent.context-menu": "Prozorno", + "action.toggle-transparent.menu": "Prozorno", + "action.toggle-transparent": "Preklopi prosojno ozadje", + "action.undo": "Razveljavi", + "action.ungroup": "Razdruži", + "action.zoom-in": "Povečaj", + "action.zoom-out": "Pomanjšaj", + "action.zoom-to-100": "Povečaj na 100 %", + "action.zoom-to-fit": "Povečaj do prileganja", + "action.zoom-to-selection": "Pomakni na izbiro", + "color-style.black": "Črna", + "color-style.blue": "Modra", + "color-style.green": "Zelena", + "color-style.grey": "Siva", + "color-style.light-blue": "Svetlo modra", + "color-style.light-green": "Svetlo zelena", + "color-style.light-red": "Svetlo rdeča", + "color-style.light-violet": "Svetlo vijolična", + "color-style.orange": "Oranžna", + "color-style.red": "Rdeča", + "color-style.violet": "Vijolična", + "color-style.yellow": "Rumena", + "fill-style.none": "Brez", + "fill-style.semi": "Polovično", + "fill-style.solid": "Polno", + "fill-style.pattern": "Vzorec", + "dash-style.dashed": "Črtkano", + "dash-style.dotted": "Pikčasto", + "dash-style.draw": "Narisano", + "dash-style.solid": "Polno", + "size-style.s": "Malo", + "size-style.m": "Srednje", + "size-style.l": "Veliko", + "size-style.xl": "Zelo veliko", + "opacity-style.0.1": "10 %", + "opacity-style.0.25": "25 %", + "opacity-style.0.5": "50 %", + "opacity-style.0.75": "75 %", + "opacity-style.1": "100 %", + "font-style.draw": "Draw", + "font-style.sans": "Sans", + "font-style.serif": "Serif", + "font-style.mono": "Mono", + "align-style.start": "Začetek", + "align-style.middle": "Sredina", + "align-style.end": "Konec", + "align-style.justify": "Poravnaj", + "geo-style.arrow-down": "Puščica navzdol", + "geo-style.arrow-left": "Puščica levo", + "geo-style.arrow-right": "Puščica desno", + "geo-style.arrow-up": "Puščica navzgor", + "geo-style.diamond": "Diamant", + "geo-style.ellipse": "Elipsa", + "geo-style.hexagon": "Šesterokotnik", + "geo-style.octagon": "Osmerokotnik", + "geo-style.oval": "Oval", + "geo-style.pentagon": "Peterokotnik", + "geo-style.rectangle": "Pravokotnik", + "geo-style.rhombus-2": "Romb 2", + "geo-style.rhombus": "Romb", + "geo-style.star": "Zvezda", + "geo-style.trapezoid": "Trapez", + "geo-style.triangle": "Trikotnik", + "geo-style.x-box": "X polje", + "arrowheadStart-style.none": "Brez", + "arrowheadStart-style.arrow": "Puščica", + "arrowheadStart-style.bar": "Črta", + "arrowheadStart-style.diamond": "Diamant", + "arrowheadStart-style.dot": "Pika", + "arrowheadStart-style.inverted": "Obrnjeno", + "arrowheadStart-style.pipe": "Cev", + "arrowheadStart-style.square": "Kvadrat", + "arrowheadStart-style.triangle": "Trikotnik", + "arrowheadEnd-style.none": "Brez", + "arrowheadEnd-style.arrow": "Puščica", + "arrowheadEnd-style.bar": "Črta", + "arrowheadEnd-style.diamond": "Diamant", + "arrowheadEnd-style.dot": "Pika", + "arrowheadEnd-style.inverted": "Obrnjeno", + "arrowheadEnd-style.pipe": "Cev", + "arrowheadEnd-style.square": "Kvadrat", + "arrowheadEnd-style.triangle": "Trikotnik", + "spline-style.line": "Črta", + "spline-style.cubic": "Kubično", + "tool.select": "Izbor", + "tool.hand": "Roka", + "tool.draw": "Risanje", + "tool.eraser": "Radirka", + "tool.arrow-down": "Puščica navzdol", + "tool.arrow-left": "Puščica levo", + "tool.arrow-right": "Puščica desno", + "tool.arrow-up": "Puščica navzgor", + "tool.arrow": "Puščica", + "tool.diamond": "Diamant", + "tool.ellipse": "Elipsa", + "tool.hexagon": "Šesterokotnik", + "tool.line": "Črta", + "tool.octagon": "Osmerokotnik", + "tool.oval": "Oval", + "tool.pentagon": "Peterokotnik", + "tool.rectangle": "Pravokotnik", + "tool.rhombus": "Romb", + "tool.star": "Zvezda", + "tool.trapezoid": "Trapez", + "tool.triangle": "Trikotnik", + "tool.x-box": "X polje", + "tool.asset": "Sredstvo", + "tool.frame": "Okvir", + "tool.note": "Opomba", + "tool.embed": "Vdelava", + "tool.text": "Besedilo", + "menu.title": "Meni", + "menu.copy-as": "Kopiraj kot", + "menu.edit": "Uredi", + "menu.export-as": "Izvozi kot", + "menu.file": "Datoteka", + "menu.language": "Jezik", + "menu.preferences": "Nastavitve", + "menu.view": "Pogled", + "context-menu.arrange": "Preuredi", + "context-menu.copy-as": "Kopiraj kot", + "context-menu.export-as": "Izvozi kot", + "context-menu.move-to-page": "Premakni na stran", + "context-menu.reorder": "Preuredite", + "page-menu.title": "Strani", + "page-menu.create-new-page": "Ustvari novo stran", + "page-menu.max-page-count-reached": "Doseženo največje število strani", + "page-menu.new-page-initial-name": "Stran 1", + "page-menu.edit-start": "Uredi", + "page-menu.edit-done": "Zaključi", + "page-menu.submenu.rename": "Preimenuj", + "page-menu.submenu.duplicate-page": "Podvoji", + "page-menu.submenu.title": "Meni", + "page-menu.submenu.move-down": "Premakni navzdol", + "page-menu.submenu.move-up": "Premakni navzgor", + "page-menu.submenu.delete": "Izbriši", + "share-menu.title": "Deli", + "share-menu.share-project": "Deli ta projekt", + "share-menu.copy-link": "Kopiraj povezavo", + "share-menu.readonly-link": "Samo za branje", + "share-menu.copy-readonly-link": "Kopiraj povezavo samo za branje", + "share-menu.offline-note": "Skupna raba tega projekta bo ustvarila živo kopijo na novem URL-ju. URL lahko delite z do tridesetimi drugimi osebami, s katerimi lahko skupaj gledate in urejate vsebino.", + "share-menu.copy-link-note": "Vsakdo s povezavo si bo lahko ogledal in urejal ta projekt.", + "share-menu.copy-readonly-link-note": "Vsakdo s povezavo si bo lahko ogledal (vendar ne urejal) ta projekt.", + "share-menu.project-too-large": "Žal tega projekta ni mogoče deliti, ker je prevelik. Delamo na tem!", + "people-menu.title": "Ljudje", + "people-menu.change-name": "Spremeni ime", + "people-menu.change-color": "Spremeni barvo", + "people-menu.user": "(Ti)", + "people-menu.invite": "Povabi ostale", + "help-menu.title": "Pomoč in viri", + "help-menu.about": "O nas", + "help-menu.discord": "Discord", + "help-menu.github": "GitHub", + "help-menu.keyboard-shortcuts": "Bližnjice na tipkovnici", + "help-menu.twitter": "Twitter", + "actions-menu.title": "Akcije", + "edit-link-dialog.title": "Uredi povezavo", + "edit-link-dialog.invalid-url": "Povezava mora biti veljavna", + "edit-link-dialog.detail": "Povezave se bodo odprle v novem zavihku.", + "edit-link-dialog.url": "URL", + "edit-link-dialog.clear": "Počisti", + "edit-link-dialog.save": "Nadaljuj", + "edit-link-dialog.cancel": "Prekliči", + "embed-dialog.title": "Ustvari vdelavo", + "embed-dialog.back": "Nazaj", + "embed-dialog.create": "Ustvari", + "embed-dialog.cancel": "Prekliči", + "embed-dialog.url": "URL", + "embed-dialog.instruction": "Prilepite URL spletnega mesta, da ustvarite vdelavo.", + "embed-dialog.invalid-url": "Iz tega URL-ja nismo mogli ustvariti vdelave.", + "edit-pages-dialog.move-down": "Premakni navzdol", + "edit-pages-dialog.move-up": "Premakni navzgor", + "shortcuts-dialog.title": "Bližnjice na tipkovnici", + "shortcuts-dialog.edit": "Uredi", + "shortcuts-dialog.file": "Datoteka", + "shortcuts-dialog.preferences": "Nastavitve", + "shortcuts-dialog.tools": "Orodja", + "shortcuts-dialog.transform": "Preoblikuj", + "shortcuts-dialog.view": "Pogled", + "style-panel.title": "Stili", + "style-panel.align": "Poravnava", + "style-panel.arrowheads": "Puščice", + "style-panel.color": "Barva", + "style-panel.dash": "Črtasto", + "style-panel.fill": "Polnilo", + "style-panel.font": "Pisava", + "style-panel.geo": "Oblika", + "style-panel.mixed": "Mešano", + "style-panel.opacity": "Motnost", + "style-panel.size": "Velikost", + "style-panel.spline": "Krivulja", + "tool-panel.drawing": "Risanje", + "tool-panel.shapes": "Oblike", + "navigation-zone.toggle-minimap": "Preklopi mini zemljevid", + "navigation-zone.zoom": "Povečava", + "focus-mode.toggle-focus-mode": "Preklopi na osredotočen način", + "toast.close": "Zapri", + "file-system.file-open-error.title": "Datoteke ni bilo mogoče odpreti", + "file-system.file-open-error.not-a-tldraw-file": "Datoteka, ki ste jo poskušali odpreti, ni videti kot datoteka tldraw.", + "file-system.file-open-error.file-format-version-too-new": "Datoteka, ki ste jo poskušali odpreti, je iz novejše različice tldraw. Ponovno naložite stran in poskusite znova.", + "file-system.file-open-error.generic-corrupted-file": "Datoteka, ki ste jo poskušali odpreti, je poškodovana.", + "file-system.confirm-open.title": "Prepiši trenutni projekt?", + "file-system.confirm-open.description": "Če ustvarite nov projekt, boste izbrisali trenutni projekt in vse neshranjene spremembe bodo izgubljene. Ste prepričani, da želite nadaljevati?", + "file-system.confirm-open.cancel": "Prekliči", + "file-system.confirm-open.open": "Odpri datoteko", + "file-system.confirm-open.dont-show-again": "Ne sprašuj znova", + "toast.error.export-fail.title": "Izvoz ni uspel", + "toast.error.export-fail.desc": "Izvoz slike ni uspel", + "toast.error.copy-fail.title": "Kopiranje ni uspelo", + "toast.error.copy-fail.desc": "Kopiranje slike ni uspelo", + "file-system.shared-document-file-open-error.title": "Datoteke ni bilo mogoče odpreti", + "file-system.shared-document-file-open-error.description": "Odpiranje datotek v skupnih projektih ni podprto.", + "vscode.file-open.dont-show-again": "Ne sprašuj znova", + "vscode.file-open.desc": "Ta datoteka je bila ustvarjena s starejšo različico tldraw. Ali jo želite posodobiti, da bo deloval z novo različico?", + "context.pages.new-page": "Nova stran", + "style-panel.arrowhead-start": "Začetek", + "style-panel.arrowhead-end": "Konec", + "vscode.file-open.open": "Nadaljuj", + "vscode.file-open.backup": "Varnostna kopija", + "vscode.file-open.backup-saved": "Varnostna kopija shranjena", + "vscode.file-open.backup-failed": "Varnostno kopiranje ni uspelo: to ni datoteka .tldr.", + "tool-panel.more": "Več", + "debug-panel.more": "Več", + "action.new-project": "Nov projekt", + "file-system.confirm-clear.title": "Počisti trenutni projekt?", + "file-system.confirm-clear.description": "Če ustvarite nov projekt, boste izbrisali trenutni projekt in vse neshranjene spremembe bodo izgubljene. Ste prepričani, da želite nadaljevati?", + "file-system.confirm-clear.cancel": "Prekliči", + "file-system.confirm-clear.continue": "Nadaljuj", + "file-system.confirm-clear.dont-show-again": "Ne sprašuj znova", + "action.stop-following": "Prenehaj slediti", + "people-menu.follow": "Sledi", + "style-panel.position": "Položaj", + "page-menu.go-to-page": "Pojdi na stran", + "action.insert-embed": "Vstavi vdelavo", + "people-menu.following": "Sledim", + "people-menu.leading": "Sledi vam", + "geo-style.check-box": "Potrditveno polje", + "tool.check-box": "Potrditveno polje", + "share-menu.create-snapshot-link": "Ustvari povezavo do posnetka", + "share-menu.save-note": "Ta projekt prenesite na svoj računalnik kot datoteko .tldr.", + "share-menu.fork-note": "Na podlagi tega posnetka ustvarite nov skupni projekt.", + "share-menu.snapshot-link-note": "Zajemite in delite ta projekt kot povezavo do posnetka samo za branje.", + "share-menu.upload-failed": "Oprostite, trenutno nismo mogli naložiti vašega projekta. Poskusite znova ali nam sporočite, če se težava ponovi.", + "style-panel.vertical-align": "Navpična poravnava", + "tool.laser": "Laser", + "action.fork-project": "Naredi kopijo projekta", + "action.leave-shared-project": "Zapusti skupni projekt", + "sharing.confirm-leave.title": "Zapusti trenutni projekt?", + "sharing.confirm-leave.description": "Ali ste prepričani, da želite zapustiti ta skupni projekt? Nanj se lahko vrnete tako, da se ponovno vrnete na njegov URL.", + "sharing.confirm-leave.cancel": "Prekliči", + "sharing.confirm-leave.leave": "Zapusti", + "sharing.confirm-leave.dont-show-again": "Ne sprašuj znova", + "action.toggle-reduce-motion.menu": "Zmanjšaj gibanje", + "action.toggle-reduce-motion": "Preklop zmanjšanja gibanja", + "tool.highlight": "Marker", + "action.toggle-lock": "Zakleni / odkleni", + "share-menu.default-project-name": "Skupni projekt", + "home-project-dialog.title": "Lokalni projekt", + "home-project-dialog.description": "To je vaš lokalni projekt. Namenjen je samo vam!", + "rename-project-dialog.title": "Preimenuj projekt", + "rename-project-dialog.cancel": "Prekliči", + "rename-project-dialog.rename": "Preimenuj", + "home-project-dialog.ok": "V redu", + "action.open-cursor-chat": "Klepet s kazalcem", + "shortcuts-dialog.collaboration": "Sodelovanje", + "cursor-chat.type-to-chat": "Vnesite za klepet ...", + "geo-style.cloud": "Oblak", + "tool.cloud": "Oblak", + "action.unlock-all": "Odkleni vse", + "status.offline": "Brez povezave", + "status.online": "Povezan", + "action.remove-frame": "Odstrani okvir", + "action.fit-frame-to-content": "Prilagodi vsebini", + "action.toggle-edge-scrolling.menu": "Pomikanje ob robovih", + "action.toggle-edge-scrolling": "Preklopi pomikanje ob robovih", + "verticalAlign-style.start": "Vrh", + "verticalAlign-style.middle": "Sredina", + "verticalAlign-style.end": "Dno" +} diff --git a/packages/editor/api-report.md b/packages/editor/api-report.md index ed0194828..b0af3a9c3 100644 --- a/packages/editor/api-report.md +++ b/packages/editor/api-report.md @@ -16,11 +16,9 @@ import { EmbedDefinition } from '@tldraw/tlschema'; import { EMPTY_ARRAY } from '@tldraw/state'; import { EventEmitter } from 'eventemitter3'; import { HistoryEntry } from '@tldraw/store'; -import { HTMLProps } from 'react'; import { IndexKey } from '@tldraw/utils'; import { JsonObject } from '@tldraw/utils'; import { JSX as JSX_2 } from 'react/jsx-runtime'; -import { MemoExoticComponent } from 'react'; import { Migrations } from '@tldraw/store'; import { NamedExoticComponent } from 'react'; import { PointerEventHandler } from 'react'; @@ -426,22 +424,7 @@ export function dataUrlToFile(url: string, filename: string, mimeType: string): export type DebugFlag = DebugFlagDef & Atom; // @internal (undocumented) -export const debugFlags: { - preventDefaultLogging: DebugFlag; - pointerCaptureLogging: DebugFlag; - pointerCaptureTracking: DebugFlag; - pointerCaptureTrackingObject: DebugFlag>; - elementRemovalLogging: DebugFlag; - debugSvg: DebugFlag; - showFps: DebugFlag; - throwToBlob: DebugFlag; - logMessages: DebugFlag; - resetConnectionEveryPing: DebugFlag; - debugCursors: DebugFlag; - forceSrgb: DebugFlag; - debugGeometry: DebugFlag; - hideShapes: DebugFlag; -}; +export const debugFlags: Record>; // @internal (undocumented) export const DEFAULT_ANIMATION_OPTIONS: { @@ -1452,13 +1435,6 @@ export class Polyline2d extends Geometry2d { _segments?: Edge2d[]; } -// @public (undocumented) -export const PositionedOnCanvas: MemoExoticComponent<({ x: offsetX, y: offsetY, rotation, ...rest }: { -x?: number | undefined; -y?: number | undefined; -rotation?: number | undefined; -} & HTMLProps) => JSX_2.Element>; - // @public (undocumented) export function precise(A: VecLike): string; diff --git a/packages/editor/api/api.json b/packages/editor/api/api.json index 9835a0cbc..9a8a3d085 100644 --- a/packages/editor/api/api.json +++ b/packages/editor/api/api.json @@ -27915,83 +27915,6 @@ }, "implementsTokenRanges": [] }, - { - "kind": "Variable", - "canonicalReference": "@tldraw/editor!PositionedOnCanvas:var", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "PositionedOnCanvas: " - }, - { - "kind": "Content", - "text": "import(\"react\")." - }, - { - "kind": "Reference", - "text": "MemoExoticComponent", - "canonicalReference": "@types/react!React.MemoExoticComponent:type" - }, - { - "kind": "Content", - "text": "<({ " - }, - { - "kind": "Reference", - "text": "x", - "canonicalReference": "@tldraw/editor!~__type#x" - }, - { - "kind": "Content", - "text": ": offsetX, " - }, - { - "kind": "Reference", - "text": "y", - "canonicalReference": "@tldraw/editor!~__type#y" - }, - { - "kind": "Content", - "text": ": offsetY, rotation, ...rest }: {\n x?: number | undefined;\n y?: number | undefined;\n rotation?: number | undefined;\n} & " - }, - { - "kind": "Reference", - "text": "HTMLProps", - "canonicalReference": "@types/react!React.HTMLProps:interface" - }, - { - "kind": "Content", - "text": "<" - }, - { - "kind": "Reference", - "text": "HTMLDivElement", - "canonicalReference": "!HTMLDivElement:interface" - }, - { - "kind": "Content", - "text": ">) => import(\"react/jsx-runtime\")." - }, - { - "kind": "Reference", - "text": "JSX.Element", - "canonicalReference": "@types/react!JSX.Element:interface" - }, - { - "kind": "Content", - "text": ">" - } - ], - "fileUrlPath": "packages/editor/src/lib/components/PositionedOnCanvas.tsx", - "isReadonly": true, - "releaseTag": "Public", - "name": "PositionedOnCanvas", - "variableTypeTokenRange": { - "startIndex": 1, - "endIndex": 14 - } - }, { "kind": "Function", "canonicalReference": "@tldraw/editor!precise:function(1)", diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts index ac99b948c..80274c553 100644 --- a/packages/editor/src/index.ts +++ b/packages/editor/src/index.ts @@ -41,7 +41,6 @@ export { type TLErrorBoundaryProps, } from './lib/components/ErrorBoundary' export { HTMLContainer, type HTMLContainerProps } from './lib/components/HTMLContainer' -export { PositionedOnCanvas } from './lib/components/PositionedOnCanvas' export { SVGContainer, type SVGContainerProps } from './lib/components/SVGContainer' export { ShapeIndicator, type TLShapeIndicatorComponent } from './lib/components/ShapeIndicator' export { diff --git a/packages/editor/src/lib/components/Canvas.tsx b/packages/editor/src/lib/components/Canvas.tsx index 7fd38a50e..a2b105b8f 100644 --- a/packages/editor/src/lib/components/Canvas.tsx +++ b/packages/editor/src/lib/components/Canvas.tsx @@ -99,7 +99,6 @@ export function Canvas({ className }: { className?: string }) { > {Background && } - {shapeSvgDefs} @@ -511,26 +510,6 @@ const DebugSvgCopy = track(function DupSvg({ id }: { id: TLShapeId }) { ) }) -function UiLogger() { - const uiLog = useValue('debugging ui log', () => debugFlags.logMessages.get(), [debugFlags]) - - if (!uiLog.length) return null - - return ( -
- {uiLog.map((message, messageIndex) => { - const text = typeof message === 'string' ? message : JSON.stringify(message) - - return ( -
- {text} -
- ) - })} -
- ) -} - function SelectionForegroundWrapper() { const editor = useEditor() const selectionRotation = useValue('selection rotation', () => editor.getSelectionRotation(), [ diff --git a/packages/editor/src/lib/components/PositionedOnCanvas.tsx b/packages/editor/src/lib/components/PositionedOnCanvas.tsx deleted file mode 100644 index a219ae896..000000000 --- a/packages/editor/src/lib/components/PositionedOnCanvas.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { track } from '@tldraw/state' -import classNames from 'classnames' -import { HTMLProps, useLayoutEffect, useRef } from 'react' -import { useEditor } from '../hooks/useEditor' - -/** @public */ -export const PositionedOnCanvas = track(function PositionedOnCanvas({ - x: offsetX = 0, - y: offsetY = 0, - rotation = 0, - ...rest -}: { - x?: number - y?: number - rotation?: number -} & HTMLProps) { - const editor = useEditor() - const rContainer = useRef(null) - const camera = editor.getCamera() - - useLayoutEffect(() => { - const { x, y, z } = editor.getCamera() - const elm = rContainer.current - if (!elm) return - if (x === undefined) return - - elm.style.transform = `translate(${x}px, ${y}px) scale(${z}) rotate(${rotation}rad) translate(${offsetX}px, ${offsetY}px)` - }, [camera, editor, offsetX, offsetY, rotation]) - - return
-}) diff --git a/packages/editor/src/lib/components/ShapeIndicator.tsx b/packages/editor/src/lib/components/ShapeIndicator.tsx index 8b92fc595..195b2c88d 100644 --- a/packages/editor/src/lib/components/ShapeIndicator.tsx +++ b/packages/editor/src/lib/components/ShapeIndicator.tsx @@ -2,9 +2,9 @@ import { useStateTracking, useValue } from '@tldraw/state' import { TLShape, TLShapeId } from '@tldraw/tlschema' import classNames from 'classnames' import * as React from 'react' -import { useEditor } from '../..' import type { Editor } from '../editor/Editor' import { ShapeUtil } from '../editor/shapes/ShapeUtil' +import { useEditor } from '../hooks/useEditor' import { useEditorComponents } from '../hooks/useEditorComponents' import { OptionalErrorBoundary } from './ErrorBoundary' diff --git a/packages/editor/src/lib/hooks/useEditorComponents.tsx b/packages/editor/src/lib/hooks/useEditorComponents.tsx index b04199e9f..b15e18724 100644 --- a/packages/editor/src/lib/hooks/useEditorComponents.tsx +++ b/packages/editor/src/lib/hooks/useEditorComponents.tsx @@ -50,6 +50,7 @@ import { } from '../components/default-components/DefaultSnapIndictor' import { DefaultSpinner, TLSpinnerComponent } from '../components/default-components/DefaultSpinner' import { DefaultSvgDefs, TLSvgDefsComponent } from '../components/default-components/DefaultSvgDefs' +import { useShallowObjectIdentity } from './useIdentity' export interface BaseEditorComponents { Background: TLBackgroundComponent @@ -97,7 +98,11 @@ type ComponentsContextProviderProps = { children: any } -export function EditorComponentsProvider({ overrides, children }: ComponentsContextProviderProps) { +export function EditorComponentsProvider({ + overrides = {}, + children, +}: ComponentsContextProviderProps) { + const _overrides = useShallowObjectIdentity(overrides) return ( {children} diff --git a/packages/editor/src/lib/utils/debug-flags.ts b/packages/editor/src/lib/utils/debug-flags.ts index 589f5ee23..ebd489a09 100644 --- a/packages/editor/src/lib/utils/debug-flags.ts +++ b/packages/editor/src/lib/utils/debug-flags.ts @@ -12,26 +12,25 @@ export const featureFlags: Record> = { } /** @internal */ -export const debugFlags = { +export const pointerCaptureTrackingObject = createDebugValue( + 'pointerCaptureTrackingObject', + // ideally we wouldn't store this mutable value in an atom but it's not + // a big deal for debug values + { + defaults: { all: new Map() }, + shouldStoreForSession: false, + } +) + +/** @internal */ +export const debugFlags: Record> = { // --- DEBUG VALUES --- preventDefaultLogging: createDebugValue('preventDefaultLogging', { defaults: { all: false }, }), - pointerCaptureLogging: createDebugValue('pointerCaptureLogging', { - defaults: { all: false }, - }), pointerCaptureTracking: createDebugValue('pointerCaptureTracking', { defaults: { all: false }, }), - pointerCaptureTrackingObject: createDebugValue( - 'pointerCaptureTrackingObject', - // ideally we wouldn't store this mutable value in an atom but it's not - // a big deal for debug values - { - defaults: { all: new Map() }, - shouldStoreForSession: false, - } - ), elementRemovalLogging: createDebugValue('elementRemovalLogging', { defaults: { all: false }, }), @@ -44,7 +43,6 @@ export const debugFlags = { throwToBlob: createDebugValue('throwToBlob', { defaults: { all: false }, }), - logMessages: createDebugValue('uiLog', { defaults: { all: [] as any[] } }), resetConnectionEveryPing: createDebugValue('resetConnectionEveryPing', { defaults: { all: false }, }), @@ -62,12 +60,6 @@ declare global { } } -if (typeof window !== 'undefined') { - window.tldrawLog = (message: any) => { - debugFlags.logMessages.set(debugFlags.logMessages.get().concat(message)) - } -} - // --- 2. USE --- // In normal code, read from debug flags directly by calling .value on them: // if (debugFlags.preventDefaultLogging.value) { ... } diff --git a/packages/editor/src/lib/utils/dom.ts b/packages/editor/src/lib/utils/dom.ts index 8936c91ac..5e0ce7145 100644 --- a/packages/editor/src/lib/utils/dom.ts +++ b/packages/editor/src/lib/utils/dom.ts @@ -14,7 +14,7 @@ whatever reason. */ import React from 'react' -import { debugFlags } from './debug-flags' +import { debugFlags, pointerCaptureTrackingObject } from './debug-flags' /** @public */ export function loopToHtmlElement(elm: Element): HTMLElement { @@ -49,10 +49,8 @@ export function setPointerCapture( ) { element.setPointerCapture(event.pointerId) if (debugFlags.pointerCaptureTracking.get()) { - const trackingObj = debugFlags.pointerCaptureTrackingObject.get() + const trackingObj = pointerCaptureTrackingObject.get() trackingObj.set(element, (trackingObj.get(element) ?? 0) + 1) - } - if (debugFlags.pointerCaptureLogging.get()) { console.warn('setPointerCapture called on element:', element, event) } } @@ -68,7 +66,7 @@ export function releasePointerCapture( element.releasePointerCapture(event.pointerId) if (debugFlags.pointerCaptureTracking.get()) { - const trackingObj = debugFlags.pointerCaptureTrackingObject.get() + const trackingObj = pointerCaptureTrackingObject.get() if (trackingObj.get(element) === 1) { trackingObj.delete(element) } else if (trackingObj.has(element)) { @@ -77,9 +75,7 @@ export function releasePointerCapture( console.warn('Release without capture') } } - if (debugFlags.pointerCaptureLogging.get()) { - console.warn('releasePointerCapture called on element:', element, event) - } + console.warn('releasePointerCapture called on element:', element, event) } /** @public */ diff --git a/packages/tldraw/api-report.md b/packages/tldraw/api-report.md index 5e1892c96..d7017d143 100644 --- a/packages/tldraw/api-report.md +++ b/packages/tldraw/api-report.md @@ -12,6 +12,7 @@ import { BaseBoxShapeUtil } from '@tldraw/editor'; import { BoundsSnapPoint } from '@tldraw/editor'; import { Box } from '@tldraw/editor'; import { Circle2d } from '@tldraw/editor'; +import { ComponentType } from 'react'; import { CubicSpline2d } from '@tldraw/editor'; import { DictValidator } from '@tldraw/editor'; import { Editor } from '@tldraw/editor'; @@ -35,6 +36,7 @@ import { Polyline2d } from '@tldraw/editor'; import { default as React_2 } from 'react'; import * as React_3 from 'react'; import { ReactNode } from 'react'; +import { ReadonlySharedStyleMap } from '@tldraw/editor'; import { Rectangle2d } from '@tldraw/editor'; import { RecursivePartial } from '@tldraw/editor'; import { Result } from '@tldraw/editor'; @@ -43,6 +45,7 @@ import { SelectionEdge } from '@tldraw/editor'; import { SelectionHandle } from '@tldraw/editor'; import { SerializedSchema } from '@tldraw/editor'; import { ShapeUtil } from '@tldraw/editor'; +import { SharedStyle } from '@tldraw/editor'; import { StateNode } from '@tldraw/editor'; import { StoreSnapshot } from '@tldraw/editor'; import { SvgExportContext } from '@tldraw/editor'; @@ -59,6 +62,7 @@ import { TLDefaultSizeStyle } from '@tldraw/editor'; import { TldrawEditorBaseProps } from '@tldraw/editor'; import { TLDrawShape } from '@tldraw/editor'; import { TLDrawShapeSegment } from '@tldraw/editor'; +import { TLEditorComponents } from '@tldraw/editor'; import { TLEmbedShape } from '@tldraw/editor'; import { TLEnterEventHandler } from '@tldraw/editor'; import { TLEventHandlers } from '@tldraw/editor'; @@ -73,7 +77,6 @@ import { TLImageShape } from '@tldraw/editor'; import { TLInterruptEvent } from '@tldraw/editor'; import { TLKeyboardEvent } from '@tldraw/editor'; import { TLKeyboardEventInfo } from '@tldraw/editor'; -import { TLLanguage } from '@tldraw/editor'; import { TLLineShape } from '@tldraw/editor'; import { TLNoteShape } from '@tldraw/editor'; import { TLOnBeforeCreateHandler } from '@tldraw/editor'; @@ -223,13 +226,6 @@ export function AssetUrlsProvider({ assetUrls, children, }: { children: React.ReactNode; }): JSX_2.Element; -// @public (undocumented) -function Body_2({ className, children, style, }: { - className?: string; - children: any; - style?: React.CSSProperties; -}): JSX_2.Element; - // @public (undocumented) export class BookmarkShapeUtil extends BaseBoxShapeUtil { // (undocumented) @@ -271,32 +267,9 @@ export function buildFromV1Document(editor: Editor, document: LegacyTldrawDocume // @public (undocumented) export const Button: React_3.ForwardRefExoticComponent>; -// @public (undocumented) -function CheckboxItem({ children, onSelect, ...rest }: DropdownMenuCheckboxItemProps): JSX_2.Element; - -// @public (undocumented) -function CloseButton(): JSX_2.Element; - -// @public (undocumented) -export function compactMenuItems(arr: T[]): Exclude[]; - // @public export function containBoxSize(originalSize: BoxWidthHeight, containBoxSize: BoxWidthHeight): BoxWidthHeight; -// @public (undocumented) -function Content({ side, align, sideOffset, alignOffset, children, }: { - children: any; - alignOffset?: number; - sideOffset?: number; - align?: 'center' | 'end' | 'start'; - side?: 'bottom' | 'left' | 'right' | 'top'; -}): JSX_2.Element; - -// @public (undocumented) -export const ContextMenu: ({ children }: { - children: any; -}) => JSX_2.Element; - // @public export function copyAs(editor: Editor, ids: TLShapeId[], format?: TLCopyType, opts?: Partial): Promise; @@ -306,25 +279,100 @@ export const DEFAULT_ACCEPTED_IMG_TYPE: string[]; // @public (undocumented) export const DEFAULT_ACCEPTED_VID_TYPE: string[]; +// @public (undocumented) +export const DefaultActionsMenu: NamedExoticComponent; + +// @public (undocumented) +export function DefaultActionsMenuContent(): JSX_2.Element; + +// @public (undocumented) +const DefaultContextMenu: NamedExoticComponent; +export { DefaultContextMenu as ContextMenu } +export { DefaultContextMenu } + +// @public (undocumented) +export function DefaultContextMenuContent(): JSX_2.Element | null; + +// @public (undocumented) +export function DefaultDebugMenu({ children }: TLUiDebugMenuProps): JSX_2.Element; + +// @public (undocumented) +export function DefaultDebugMenuContent(): JSX_2.Element; + +// @public (undocumented) +export function DefaultHelperButtons({ children }: TLUiHelperButtonsProps): JSX_2.Element; + +// @public (undocumented) +export function DefaultHelperButtonsContent(): JSX_2.Element; + +// @public (undocumented) +export const DefaultHelpMenu: NamedExoticComponent; + +// @public (undocumented) +export function DefaultHelpMenuContent(): JSX_2.Element; + +// @public (undocumented) +export const DefaultKeyboardShortcutsDialog: NamedExoticComponent; + +// @public (undocumented) +export function DefaultKeyboardShortcutsDialogContent(): JSX_2.Element; + +// @public (undocumented) +export const DefaultMainMenu: NamedExoticComponent; + +// @public (undocumented) +export function DefaultMainMenuContent(): JSX_2.Element; + +// @public (undocumented) +export function DefaultMinimap(): JSX_2.Element; + +// @public (undocumented) +export const DefaultPageMenu: NamedExoticComponent; + +// @public (undocumented) +export const DefaultQuickActions: NamedExoticComponent; + +// @public (undocumented) +export function DefaultQuickActionsContent(): JSX_2.Element | undefined; + // @public (undocumented) export const defaultShapeTools: (typeof ArrowShapeTool | typeof DrawShapeTool | typeof FrameShapeTool | typeof GeoShapeTool | typeof LineShapeTool | typeof NoteShapeTool | typeof TextShapeTool)[]; // @public (undocumented) export const defaultShapeUtils: TLAnyShapeUtilConstructor[]; +// @public (undocumented) +export const DefaultStylePanel: NamedExoticComponent; + +// @public (undocumented) +export function DefaultStylePanelContent({ relevantStyles }: TLUiStylePanelContentProps): JSX_2.Element | null; + +// @public (undocumented) +export const DefaultToolbar: React_2.NamedExoticComponent; + // @public (undocumented) export const defaultTools: (typeof EraserTool | typeof HandTool | typeof LaserTool | typeof SelectTool | typeof ZoomTool)[]; -declare namespace Dialog { - export { - Header, - Title, - CloseButton, - Body_2 as Body, - Footer - } -} -export { Dialog } +// @public (undocumented) +export const DefaultZoomMenu: NamedExoticComponent; + +// @public (undocumented) +export function DefaultZoomMenuContent(): JSX_2.Element; + +// @public (undocumented) +export function DialogBody({ className, children, style }: TLUiDialogBodyProps): JSX_2.Element; + +// @public (undocumented) +export function DialogCloseButton(): JSX_2.Element; + +// @public (undocumented) +export function DialogFooter({ className, children }: TLUiDialogFooterProps): JSX_2.Element; + +// @public (undocumented) +export function DialogHeader({ className, children }: TLUiDialogHeaderProps): JSX_2.Element; + +// @public (undocumented) +export function DialogTitle({ className, children }: TLUiDialogTitleProps): JSX_2.Element; // @public export function downsizeImage(blob: Blob, width: number, height: number, opts?: { @@ -390,44 +438,35 @@ export class DrawShapeUtil extends ShapeUtil { static type: "draw"; } -declare namespace DropdownMenu { - export { - Root, - Trigger, - Content, - Sub, - SubTrigger, - SubContent, - Group, - Indicator, - Item, - CheckboxItem, - RadioItem, - DropdownMenuItemProps, - DropdownMenuCheckboxItemProps - } -} -export { DropdownMenu } +// @public (undocumented) +export function DropdownMenuCheckboxItem({ children, onSelect, ...rest }: TLUiDropdownMenuCheckboxItemProps): JSX_2.Element; // @public (undocumented) -interface DropdownMenuCheckboxItemProps { - // (undocumented) - checked?: boolean; - // (undocumented) - children: any; - // (undocumented) - disabled?: boolean; - // (undocumented) - onSelect?: (e: Event) => void; - // (undocumented) - title: string; -} +export function DropdownMenuContent({ side, align, sideOffset, alignOffset, children, }: TLUiDropdownMenuContentProps): JSX_2.Element; // @public (undocumented) -interface DropdownMenuItemProps extends TLUiButtonProps { - // (undocumented) - noClose?: boolean; -} +export function DropdownMenuGroup({ children, size }: TLUiDropdownMenuGroupProps): JSX_2.Element; + +// @public (undocumented) +export function DropdownMenuIndicator(): JSX_2.Element; + +// @public (undocumented) +export function DropdownMenuItem({ noClose, ...props }: TLUiDropdownMenuItemProps): JSX_2.Element; + +// @public (undocumented) +export function DropdownMenuRadioItem({ children, ...rest }: TLUiDropdownMenuRadioItemProps): JSX_2.Element; + +// @public (undocumented) +export function DropdownMenuRoot({ id, children, modal, debugOpen, }: TLUiDropdownMenuRootProps): JSX_2.Element; + +// @public (undocumented) +export function DropdownMenuSub({ id, children }: TLUiDropdownMenuSubProps): JSX_2.Element; + +// @public (undocumented) +export function DropdownMenuSubTrigger({ label, title, disabled, }: TLUiDropdownMenuSubTriggerProps): JSX_2.Element; + +// @public (undocumented) +export function DropdownMenuTrigger({ children, ...rest }: TLUiDropdownMenuTriggerProps): JSX_2.Element; // @public (undocumented) export class EmbedShapeUtil extends BaseBoxShapeUtil { @@ -484,20 +523,11 @@ export type EventsProviderProps = { // @public export function exportAs(editor: Editor, ids: TLShapeId[], format?: TLExportType, opts?: Partial): Promise; -// @public (undocumented) -export function findMenuItem(menu: TLUiMenuSchema, path: string[]): TLUiCustomMenuItem | TLUiMenuGroup | TLUiMenuItem | TLUiSubMenu; - // @public export function fitFrameToContent(editor: Editor, id: TLShapeId, opts?: { padding: number; }): void; -// @public (undocumented) -function Footer({ className, children }: { - className?: string; - children: any; -}): JSX_2.Element; - // @public (undocumented) export class FrameShapeTool extends BaseBoxShapeTool { // (undocumented) @@ -711,12 +741,6 @@ export function getSvgAsImage(svg: SVGElement, isSafari: boolean, options: { scale: number; }): Promise; -// @public (undocumented) -function Group({ children, size, }: { - children: any; - size?: 'medium' | 'small' | 'tiny' | 'wide'; -}): JSX_2.Element; - // @public (undocumented) export class HandTool extends StateNode { // (undocumented) @@ -733,12 +757,6 @@ export class HandTool extends StateNode { onTripleClick: TLClickEvent; } -// @public (undocumented) -function Header({ className, children }: { - className?: string; - children: any; -}): JSX_2.Element; - // @public (undocumented) export class HighlightShapeTool extends StateNode { // (undocumented) @@ -835,18 +853,12 @@ export class ImageShapeUtil extends BaseBoxShapeUtil { static type: "image"; } -// @public (undocumented) -function Indicator(): JSX_2.Element; - // @public (undocumented) export const Input: React_3.ForwardRefExoticComponent>; // @public (undocumented) export function isGifAnimated(file: Blob): Promise; -// @public (undocumented) -function Item({ noClose, ...props }: DropdownMenuItemProps): JSX_2.Element; - // @public (undocumented) export class LaserTool extends StateNode { // (undocumented) @@ -929,29 +941,6 @@ export class LineShapeUtil extends ShapeUtil { static type: "line"; } -// @public (undocumented) -export function menuCustom(id: string, opts?: Partial<{ - readonlyOk: boolean; - disabled: boolean; -}>): { - id: string; - type: "custom"; - disabled: boolean; - readonlyOk: boolean; -}; - -// @public (undocumented) -export function menuGroup(id: string, ...children: (false | TLUiMenuChild)[]): null | TLUiMenuGroup; - -// @public (undocumented) -export function menuItem(actionItem: TLUiActionItem | TLUiToolItem, opts?: Partial<{ - checked: boolean; - disabled: boolean; -}>): TLUiMenuItem; - -// @public (undocumented) -export function menuSubmenu(id: string, label: Exclude | TLUiTranslationKey, ...children: (false | TLUiMenuChild)[]): null | TLUiSubMenu; - // @public (undocumented) export class NoteShapeTool extends StateNode { // (undocumented) @@ -1064,19 +1053,17 @@ export function parseTldrawJsonFile({ json, schema, }: { }): Result; // @public (undocumented) -function RadioItem({ children, onSelect, ...rest }: DropdownMenuCheckboxItemProps): JSX_2.Element; +export function Popover({ id, children, onOpenChange, open }: TLUiPopoverProps): JSX_2.Element; + +// @public (undocumented) +export function PopoverContent({ side, children, align, sideOffset, alignOffset, }: TLUiPopoverContentProps): JSX_2.Element; + +// @public (undocumented) +export function PopoverTrigger({ children, ...rest }: TLUiPopoverTriggerProps): JSX_2.Element; // @public export function removeFrame(editor: Editor, ids: TLShapeId[]): void; -// @public (undocumented) -function Root({ id, children, modal, debugOpen, }: { - id: string; - children: any; - modal?: boolean; - debugOpen?: boolean; -}): JSX_2.Element; - // @public (undocumented) export class SelectTool extends StateNode { // (undocumented) @@ -1108,26 +1095,6 @@ export function setDefaultUiAssetUrls(urls: TLUiAssetUrls): void; // @internal (undocumented) export function Spinner(props: React_2.SVGProps): JSX_2.Element; -// @public (undocumented) -function Sub({ id, children }: { - id: string; - children: any; -}): JSX_2.Element; - -// @public (undocumented) -function SubContent({ alignOffset, sideOffset, children, }: { - alignOffset?: number; - sideOffset?: number; - children: any; -}): JSX_2.Element; - -// @public (undocumented) -function SubTrigger({ label, 'data-testid': testId, 'data-direction': dataDirection, }: { - label: Exclude | TLUiTranslationKey; - 'data-testid'?: string; - 'data-direction'?: 'left' | 'right'; -}): JSX_2.Element; - // @public (undocumented) export class TextShapeTool extends StateNode { // (undocumented) @@ -1247,10 +1214,7 @@ export class TextShapeUtil extends ShapeUtil { } // @public (undocumented) -function Title({ className, children }: { - className?: string; - children: any; -}): JSX_2.Element; +export type TLComponents = TLEditorComponents & TLUiComponents; // @public (undocumented) export function Tldraw(props: TldrawProps): JSX_2.Element; @@ -1290,7 +1254,9 @@ export const TldrawHandles: TLHandlesComponent; export const TldrawHoveredShapeIndicator: TLHoveredShapeIndicatorComponent; // @public (undocumented) -export type TldrawProps = TldrawEditorBaseProps & ({ +export type TldrawProps = (Omit & Omit & { + components?: TLComponents; +}) & Partial & ({ store: TLStore | TLStoreWithStatus; } | { store?: undefined; @@ -1298,7 +1264,7 @@ export type TldrawProps = TldrawEditorBaseProps & ({ sessionId?: string; defaultName?: string; snapshot?: StoreSnapshot; -}) & TldrawUiProps & Partial; +}); // @public (undocumented) export const TldrawScribble: TLScribbleComponent; @@ -1316,6 +1282,7 @@ export const TldrawUi: React_2.NamedExoticComponent; export interface TldrawUiBaseProps { assetUrls?: TLUiAssetUrlOverrides; children?: ReactNode; + components?: TLUiComponents; hideUi?: boolean; renderDebugMenuItems?: () => React_2.ReactNode; shareZone?: ReactNode; @@ -1324,17 +1291,36 @@ export interface TldrawUiBaseProps { } // @public (undocumented) -export function TldrawUiContextProvider({ overrides, assetUrls, onUiEvent, forceMobile, children, }: TldrawUiContextProviderProps): JSX_2.Element; +export function TldrawUiComponentsProvider({ overrides, children, }: ComponentsContextProviderProps): JSX_2.Element; + +// @public (undocumented) +export function TldrawUiContextProvider({ overrides, components, assetUrls, onUiEvent, forceMobile, children, }: TldrawUiContextProviderProps): JSX_2.Element; // @public export interface TldrawUiContextProviderProps { assetUrls?: RecursivePartial; children?: any; + components?: TLUiComponents; forceMobile?: boolean; onUiEvent?: TLUiEventHandler; overrides?: TLUiOverrides | TLUiOverrides[]; } +// @public (undocumented) +export function TldrawUiMenuCheckboxItem({ id, kbd, label, readonlyOk, onSelect, disabled, checked, }: TLUiMenuCheckboxItemProps): JSX_2.Element | null; + +// @public (undocumented) +export function TldrawUiMenuContextProvider({ type, sourceId, children, }: TLUiMenuContextProviderProps): JSX_2.Element; + +// @public (undocumented) +export function TldrawUiMenuGroup({ id, label, small, children }: TLUiMenuGroupProps): any; + +// @public (undocumented) +export function TldrawUiMenuItem({ disabled, spinner, readonlyOk, id, kbd, label, icon, onSelect, noClose, }: TLUiMenuItemProps): JSX_2.Element | null; + +// @public (undocumented) +export function TldrawUiMenuSubmenu({ id, disabled, label, size, children, }: TLUiMenuSubmenuProps): any; + // @public export type TldrawUiProps = TldrawUiBaseProps & TldrawUiContextProviderProps; @@ -1343,23 +1329,19 @@ export interface TLUiActionItem Promise | void; // (undocumented) - readonlyOk: boolean; - // (undocumented) - shortcutsLabel?: TransationKey; + readonlyOk?: boolean; // (undocumented) title?: string; } @@ -1368,7 +1350,9 @@ export interface TLUiActionItem; // @public (undocumented) -export type TLUiActionsMenuSchemaContextType = TLUiMenuSchema; +export type TLUiActionsMenuProps = { + children?: any; +}; // @public (undocumented) export type TLUiAssetUrlOverrides = RecursivePartial; @@ -1399,33 +1383,53 @@ export interface TLUiButtonProps extends React_3.HTMLAttributes; + // @public (undocumented) export interface TLUiContextMenuProps { // (undocumented) - children: any; + canvas: any; + // (undocumented) + children?: any; } // @public (undocumented) -export type TLUiContextTTLUiMenuSchemaContextType = TLUiMenuSchema; - -// @public (undocumented) -export type TLUiCustomMenuItem = { - id: string; - type: 'custom'; - disabled: boolean; - readonlyOk: boolean; +export type TLUiDebugMenuProps = { + children?: any; }; // @public (undocumented) export interface TLUiDialog { // (undocumented) - component: (props: TLUiDialogProps) => any; + component: ComponentType; // (undocumented) id: string; // (undocumented) onClose?: () => void; } +// @public (undocumented) +export type TLUiDialogBodyProps = { + className?: string; + children: any; + style?: React.CSSProperties; +}; + +// @public (undocumented) +export type TLUiDialogFooterProps = { + className?: string; + children: any; +}; + +// @public (undocumented) +export type TLUiDialogHeaderProps = { + className?: string; + children: any; +}; + // @public (undocumented) export interface TLUiDialogProps { // (undocumented) @@ -1443,6 +1447,90 @@ export type TLUiDialogsContextType = { dialogs: TLUiDialog[]; }; +// @public (undocumented) +export type TLUiDialogTitleProps = { + className?: string; + children: any; +}; + +// @public (undocumented) +export interface TLUiDropdownMenuCheckboxItemProps { + // (undocumented) + checked?: boolean; + // (undocumented) + children: any; + // (undocumented) + disabled?: boolean; + // (undocumented) + onSelect?: (e: Event) => void; + // (undocumented) + title: string; +} + +// @public (undocumented) +export type TLUiDropdownMenuContentProps = { + id?: string; + children: any; + alignOffset?: number; + sideOffset?: number; + align?: 'center' | 'end' | 'start'; + side?: 'bottom' | 'left' | 'right' | 'top'; +}; + +// @public (undocumented) +export type TLUiDropdownMenuGroupProps = { + children: any; + size?: 'medium' | 'small' | 'tiny' | 'wide'; +}; + +// @public (undocumented) +export interface TLUiDropdownMenuItemProps extends TLUiButtonProps { + // (undocumented) + noClose?: boolean; +} + +// @public (undocumented) +export interface TLUiDropdownMenuRadioItemProps { + // (undocumented) + checked?: boolean; + // (undocumented) + children: any; + // (undocumented) + disabled?: boolean; + // (undocumented) + onSelect?: (e: Event) => void; + // (undocumented) + title: string; +} + +// @public (undocumented) +export type TLUiDropdownMenuRootProps = { + id: string; + children: any; + modal?: boolean; + debugOpen?: boolean; +}; + +// @public (undocumented) +export type TLUiDropdownMenuSubProps = { + id: string; + children: any; +}; + +// @public (undocumented) +export type TLUiDropdownMenuSubTriggerProps = { + label: string; + id?: string; + title?: string; + disabled?: boolean; +}; + +// @public (undocumented) +export interface TLUiDropdownMenuTriggerProps extends TLUiButtonProps { + // (undocumented) + children?: any; +} + // @public (undocumented) export type TLUiEventContextType = TLUiEventHandler; @@ -1458,6 +1546,10 @@ export interface TLUiEventMap { operation: 'bottom' | 'center-horizontal' | 'center-vertical' | 'left' | 'right' | 'top'; }; // (undocumented) + 'change-language': { + locale: string; + }; + // (undocumented) 'close-menu': { id: string; }; @@ -1500,6 +1592,10 @@ export interface TLUiEventMap { // (undocumented) 'insert-media': null; // (undocumented) + 'move-to-page': null; + // (undocumented) + 'new-page': null; + // (undocumented) 'open-cursor-chat': null; // (undocumented) 'open-embed-link': null; @@ -1606,7 +1702,14 @@ export interface TLUiEventMap { export 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' | 'style-panel' | 'toolbar' | 'unknown' | 'zoom-menu'; // @public (undocumented) -export type TLUiHelpMenuSchemaContextType = TLUiMenuSchema; +export type TLUiHelperButtonsProps = { + children?: any; +}; + +// @public (undocumented) +export type TLUiHelpMenuProps = { + children?: any; +}; // @public (undocumented) export interface TLUiIconProps extends React.HTMLProps { @@ -1663,81 +1766,128 @@ export interface TLUiInputProps { } // @public (undocumented) -export type TLUiKeyboardShortcutsSchemaContextType = TLUiMenuSchema; - -// @public (undocumented) -export type TLUiKeyboardShortcutsSchemaProviderProps = { - overrides?: (editor: Editor, schema: TLUiKeyboardShortcutsSchemaContextType, more: { - tools: TLUiToolsContextType; - actions: TLUiActionsContextType; - }) => TLUiKeyboardShortcutsSchemaContextType; - children: any; +export type TLUiKeyboardShortcutsDialogProps = TLUiDialogProps & { + children?: any; }; // @public (undocumented) -export type TLUiMenuChild = null | TLUiCustomMenuItem | TLUiMenuGroup | TLUiMenuItem | TLUiSubMenu; +export type TLUiMainMenuProps = { + children?: any; +}; // @public (undocumented) -export type TLUiMenuGroup = { +export type TLUiMenuCheckboxItemProps = { + icon?: IconType; id: string; - type: 'group'; - checkbox: boolean; - disabled: boolean; - readonlyOk: boolean; - children: TLUiMenuChild[]; + kbd?: string; + title?: string; + label?: { + [key: string]: TranslationKey; + } | TranslationKey; + readonlyOk?: boolean; + onSelect: (source: TLUiEventSource) => Promise | void; + checked?: boolean; + disabled?: boolean; }; // @public (undocumented) -export type TLUiMenuItem = { +export type TLUiMenuContextProviderProps = { + type: TldrawUiMenuContextType; + sourceId: TLUiEventSource; + children: React.ReactNode; +}; + +// @public (undocumented) +export type TLUiMenuGroupProps = { id: string; - type: 'item'; - readonlyOk: boolean; - actionItem: TLUiActionItem; - disabled: boolean; - checked: boolean; + label?: { + [key: string]: TranslationKey; + } | TranslationKey; + small?: boolean; + children?: any; }; // @public (undocumented) -export type TLUiMenuSchema = (TLUiCustomMenuItem | TLUiMenuGroup | TLUiMenuItem)[]; +export type TLUiMenuItemProps = { + id: string; + icon?: IconType; + kbd?: string; + title?: string; + label?: { + [key: string]: TranslationKey; + } | TranslationKey; + readonlyOk?: boolean; + onSelect: (source: TLUiEventSource) => Promise | void; + disabled?: boolean; + noClose?: boolean; + spinner?: boolean; +}; // @public (undocumented) -export type TLUiMenuSchemaContextType = TLUiMenuSchema; - -// @public (undocumented) -export type TLUiMenuSchemaProviderProps = { - overrides?: (editor: Editor, schema: TLUiMenuSchemaContextType, helpers: { - actions: ReturnType; - noneSelected: boolean; - oneSelected: boolean; - twoSelected: boolean; - threeSelected: boolean; - }) => TLUiMenuSchemaContextType; +export type TLUiMenuSubmenuProps = { + id: string; + label?: { + [key: string]: Translation; + } | Translation; + disabled?: boolean; children: any; + size?: 'large' | 'medium' | 'small' | 'tiny'; }; // @public (undocumented) export type TLUiOverrides = Partial<{ - actionsMenu: WithDefaultHelpers>; actions: WithDefaultHelpers>; - contextMenu: WithDefaultHelpers>; - helpMenu: WithDefaultHelpers>; - menu: WithDefaultHelpers>; toolbar: WithDefaultHelpers>; - keyboardShortcutsMenu: WithDefaultHelpers>; tools: WithDefaultHelpers>; translations: TLUiTranslationProviderProps['overrides']; }>; // @public (undocumented) -export type TLUiSubMenu = { - id: string; - type: 'submenu'; - label: TranslationKey; - disabled: boolean; - readonlyOk: boolean; - children: TLUiMenuChild[]; +export type TLUiPopoverContentProps = { + children: React_2.ReactNode; + side: 'bottom' | 'left' | 'right' | 'top'; + align?: 'center' | 'end' | 'start'; + alignOffset?: number; + sideOffset?: number; }; +// @public (undocumented) +export type TLUiPopoverProps = { + id: string; + open?: boolean; + children: React_2.ReactNode; + onOpenChange?: (isOpen: boolean) => void; +}; + +// @public (undocumented) +export interface TLUiPopoverTriggerProps extends TLUiButtonProps { + // (undocumented) + children?: React_2.ReactNode; +} + +// @public (undocumented) +export type TLUiQuickActionsProps = { + children?: any; +}; + +// @public (undocumented) +export type TLUiStylePanelContentProps = { + relevantStyles: ReturnType; +}; + +// @public (undocumented) +export interface TLUiStylePanelProps { + // (undocumented) + children?: any; + // (undocumented) + isMobile?: boolean; + // (undocumented) + relevantStyles: { + styles: ReadonlySharedStyleMap; + opacity: SharedStyle; + } | null; +} + // @public (undocumented) export interface TLUiToast { // (undocumented) @@ -1780,7 +1930,7 @@ export type TLUiToastsContextType = { export type TLUiToolbarItem = { id: string; type: 'item'; - readonlyOk: boolean; + readonlyOk?: boolean; toolItem: TLUiToolItem; }; @@ -1804,7 +1954,7 @@ export interface TLUiToolItem void; // (undocumented) - readonlyOk: boolean; + readonlyOk?: boolean; // (undocumented) shortcutsLabel?: TranslationKey; } @@ -1834,13 +1984,12 @@ export type TLUiTranslationContextType = TLUiTranslation; export 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.fit-frame-to-content' | '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.remove-frame' | '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-edge-scrolling.menu' | 'action.toggle-edge-scrolling' | '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.unlock-all' | '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.cloud' | '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' | 'status.offline' | 'status.online' | '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.cloud' | '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' | 'verticalAlign-style.end' | 'verticalAlign-style.middle' | 'verticalAlign-style.start' | '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 (undocumented) -export function toolbarItem(toolItem: TLUiToolItem): TLUiToolbarItem; +export type TLUiZoomMenuProps = { + children?: any; +}; // @public (undocumented) -function Trigger({ children, 'data-testid': testId, }: { - children: any; - 'data-testid'?: string; -}): JSX_2.Element; +export function toolbarItem(toolItem: TLUiToolItem): TLUiToolbarItem; // @public (undocumented) export const truncateStringWithEllipsis: (str: string, maxLength: number) => string; @@ -1849,10 +1998,10 @@ export const truncateStringWithEllipsis: (str: string, maxLength: number) => str export function UiEventsProvider({ onEvent, children }: EventsProviderProps): JSX_2.Element; // @public (undocumented) -export function useActions(): TLUiActionsContextType; +export function unwrapLabel(label?: TLUiActionItem['label'], menuType?: string): string | undefined; // @public (undocumented) -export function useActionsMenuSchema(): TLUiMenuSchema; +export function useActions(): TLUiActionsContextType; // @internal (undocumented) export function useAssetUrls(): TLUiAssetUrls; @@ -1860,15 +2009,6 @@ export function useAssetUrls(): TLUiAssetUrls; // @public (undocumented) export function useBreakpoint(): number; -// @public (undocumented) -export function useCanRedo(): boolean; - -// @public (undocumented) -export function useCanUndo(): boolean; - -// @public (undocumented) -export function useContextMenuSchema(): TLUiMenuSchema; - // @public (undocumented) export function useCopyAs(): (ids: TLShapeId[], format?: TLCopyType) => void; @@ -1885,7 +2025,7 @@ export function useDefaultHelpers(): { clearDialogs: () => void; removeDialog: (id: string) => string; updateDialog: (id: string, newDialogData: Partial) => string; - msg: (id: string) => string; + msg: (id?: string | undefined) => string; isMobile: boolean; }; @@ -1895,15 +2035,9 @@ export function useDialogs(): TLUiDialogsContextType; // @public (undocumented) export function useExportAs(): (ids: TLShapeId[], format?: TLExportType) => void; -// @public (undocumented) -export function useHelpMenuSchema(): TLUiMenuSchema; - // @public (undocumented) export function useKeyboardShortcuts(): void; -// @public (undocumented) -export function useKeyboardShortcutsSchema(): TLUiKeyboardShortcutsSchemaContextType; - // @public (undocumented) export function useLocalStorageState(key: string, defaultValue: T): readonly [T, (setter: ((value: T) => T) | T) => void]; @@ -1917,15 +2051,30 @@ export function useMenuClipboardEvents(): { // @public (undocumented) export function useMenuIsOpen(id: string, cb?: (isOpen: boolean) => void): readonly [boolean, (isOpen: boolean) => void]; -// @public (undocumented) -export function useMenuSchema(): TLUiMenuSchema; - // @public (undocumented) export function useNativeClipboardEvents(): void; // @public (undocumented) export function useReadonly(): boolean; +// @public (undocumented) +export function useTldrawUiComponents(): Partial<{ + ContextMenu: ComponentType | null; + ActionsMenu: ComponentType | null; + HelpMenu: ComponentType | null; + ZoomMenu: ComponentType | null; + MainMenu: ComponentType | null; + Minimap: ComponentType | null; + StylePanel: ComponentType | null; + PageMenu: ComponentType | null; + NavigationPanel: ComponentType | null; + Toolbar: ComponentType | null; + KeyboardShortcutsDialog: ComponentType | null; + QuickActions: ComponentType | null; + HelperButtons: ComponentType | null; + DebugMenu: ComponentType | null; +}>; + // @public (undocumented) export function useToasts(): TLUiToastsContextType; @@ -1936,7 +2085,7 @@ export function useToolbarSchema(): TLUiToolbarSchemaContextType; export function useTools(): TLUiToolsContextType; // @public -export function useTranslation(): (id: Exclude | string) => string; +export function useTranslation(): (id?: Exclude | string) => string; // @public (undocumented) export function useUiEvents(): TLUiEventContextType; diff --git a/packages/tldraw/api/api.json b/packages/tldraw/api/api.json index 3af1de8c9..55a265bf0 100644 --- a/packages/tldraw/api/api.json +++ b/packages/tldraw/api/api.json @@ -2090,7 +2090,7 @@ "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useBreakpoint.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/breakpoints.tsx", "returnTypeTokenRange": { "startIndex": 3, "endIndex": 5 @@ -2164,69 +2164,6 @@ "endIndex": 9 } }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!compactMenuItems:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function compactMenuItems(arr: " - }, - { - "kind": "Content", - "text": "T[]" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Reference", - "text": "Exclude", - "canonicalReference": "!Exclude:type" - }, - { - "kind": "Content", - "text": "[]" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/menuHelpers.ts", - "returnTypeTokenRange": { - "startIndex": 3, - "endIndex": 5 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "arr", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - } - ], - "typeParameters": [ - { - "typeParameterName": "T", - "constraintTokenRange": { - "startIndex": 0, - "endIndex": 0 - }, - "defaultTypeTokenRange": { - "startIndex": 0, - "endIndex": 0 - } - } - ], - "name": "compactMenuItems" - }, { "kind": "Function", "canonicalReference": "@tldraw/tldraw!containBoxSize:function(1)", @@ -2291,34 +2228,6 @@ ], "name": "containBoxSize" }, - { - "kind": "Variable", - "canonicalReference": "@tldraw/tldraw!ContextMenu:var", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "ContextMenu: " - }, - { - "kind": "Content", - "text": "({ children }: {\n children: any;\n}) => import(\"react/jsx-runtime\")." - }, - { - "kind": "Reference", - "text": "JSX.Element", - "canonicalReference": "@types/react!JSX.Element:interface" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/ContextMenu.tsx", - "isReadonly": true, - "releaseTag": "Public", - "name": "ContextMenu", - "variableTypeTokenRange": { - "startIndex": 1, - "endIndex": 3 - } - }, { "kind": "Function", "canonicalReference": "@tldraw/tldraw!copyAs:function(1)", @@ -2484,6 +2393,691 @@ "endIndex": 2 } }, + { + "kind": "Variable", + "canonicalReference": "@tldraw/tldraw!DefaultActionsMenu:var", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "DefaultActionsMenu: " + }, + { + "kind": "Content", + "text": "import(\"react\")." + }, + { + "kind": "Reference", + "text": "NamedExoticComponent", + "canonicalReference": "@types/react!React.NamedExoticComponent:interface" + }, + { + "kind": "Content", + "text": "<" + }, + { + "kind": "Reference", + "text": "TLUiActionsMenuProps", + "canonicalReference": "@tldraw/tldraw!TLUiActionsMenuProps:type" + }, + { + "kind": "Content", + "text": ">" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/ActionsMenu/DefaultActionsMenu.tsx", + "isReadonly": true, + "releaseTag": "Public", + "name": "DefaultActionsMenu", + "variableTypeTokenRange": { + "startIndex": 1, + "endIndex": 6 + } + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DefaultActionsMenuContent:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DefaultActionsMenuContent(): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.tsx", + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 3 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "DefaultActionsMenuContent" + }, + { + "kind": "Variable", + "canonicalReference": "@tldraw/tldraw!DefaultContextMenu:var", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "DefaultContextMenu: " + }, + { + "kind": "Content", + "text": "import(\"react\")." + }, + { + "kind": "Reference", + "text": "NamedExoticComponent", + "canonicalReference": "@types/react!React.NamedExoticComponent:interface" + }, + { + "kind": "Content", + "text": "<" + }, + { + "kind": "Reference", + "text": "TLUiContextMenuProps", + "canonicalReference": "@tldraw/tldraw!TLUiContextMenuProps:interface" + }, + { + "kind": "Content", + "text": ">" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/ContextMenu/DefaultContextMenu.tsx", + "isReadonly": true, + "releaseTag": "Public", + "name": "DefaultContextMenu", + "variableTypeTokenRange": { + "startIndex": 1, + "endIndex": 6 + } + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DefaultContextMenuContent:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DefaultContextMenuContent(): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": " | null" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/ContextMenu/DefaultContextMenuContent.tsx", + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 4 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "DefaultContextMenuContent" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DefaultDebugMenu:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DefaultDebugMenu({ children }: " + }, + { + "kind": "Reference", + "text": "TLUiDebugMenuProps", + "canonicalReference": "@tldraw/tldraw!TLUiDebugMenuProps:type" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/DebugMenu/DefaultDebugMenu.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ children }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "DefaultDebugMenu" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DefaultDebugMenuContent:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DefaultDebugMenuContent(): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/DebugMenu/DefaultDebugMenuContent.tsx", + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 3 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "DefaultDebugMenuContent" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DefaultHelperButtons:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DefaultHelperButtons({ children }: " + }, + { + "kind": "Reference", + "text": "TLUiHelperButtonsProps", + "canonicalReference": "@tldraw/tldraw!TLUiHelperButtonsProps:type" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/HelperButtons/DefaultHelperButtons.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ children }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "DefaultHelperButtons" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DefaultHelperButtonsContent:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DefaultHelperButtonsContent(): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/HelperButtons/DefaultHelperButtonsContent.tsx", + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 3 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "DefaultHelperButtonsContent" + }, + { + "kind": "Variable", + "canonicalReference": "@tldraw/tldraw!DefaultHelpMenu:var", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "DefaultHelpMenu: " + }, + { + "kind": "Content", + "text": "import(\"react\")." + }, + { + "kind": "Reference", + "text": "NamedExoticComponent", + "canonicalReference": "@types/react!React.NamedExoticComponent:interface" + }, + { + "kind": "Content", + "text": "<" + }, + { + "kind": "Reference", + "text": "TLUiHelpMenuProps", + "canonicalReference": "@tldraw/tldraw!TLUiHelpMenuProps:type" + }, + { + "kind": "Content", + "text": ">" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/HelpMenu/DefaultHelpMenu.tsx", + "isReadonly": true, + "releaseTag": "Public", + "name": "DefaultHelpMenu", + "variableTypeTokenRange": { + "startIndex": 1, + "endIndex": 6 + } + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DefaultHelpMenuContent:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DefaultHelpMenuContent(): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/HelpMenu/DefaultHelpMenuContent.tsx", + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 3 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "DefaultHelpMenuContent" + }, + { + "kind": "Variable", + "canonicalReference": "@tldraw/tldraw!DefaultKeyboardShortcutsDialog:var", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "DefaultKeyboardShortcutsDialog: " + }, + { + "kind": "Content", + "text": "import(\"react\")." + }, + { + "kind": "Reference", + "text": "NamedExoticComponent", + "canonicalReference": "@types/react!React.NamedExoticComponent:interface" + }, + { + "kind": "Content", + "text": "<" + }, + { + "kind": "Reference", + "text": "TLUiKeyboardShortcutsDialogProps", + "canonicalReference": "@tldraw/tldraw!TLUiKeyboardShortcutsDialogProps:type" + }, + { + "kind": "Content", + "text": ">" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialog.tsx", + "isReadonly": true, + "releaseTag": "Public", + "name": "DefaultKeyboardShortcutsDialog", + "variableTypeTokenRange": { + "startIndex": 1, + "endIndex": 6 + } + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DefaultKeyboardShortcutsDialogContent:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DefaultKeyboardShortcutsDialogContent(): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.tsx", + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 3 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "DefaultKeyboardShortcutsDialogContent" + }, + { + "kind": "Variable", + "canonicalReference": "@tldraw/tldraw!DefaultMainMenu:var", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "DefaultMainMenu: " + }, + { + "kind": "Content", + "text": "import(\"react\")." + }, + { + "kind": "Reference", + "text": "NamedExoticComponent", + "canonicalReference": "@types/react!React.NamedExoticComponent:interface" + }, + { + "kind": "Content", + "text": "<" + }, + { + "kind": "Reference", + "text": "TLUiMainMenuProps", + "canonicalReference": "@tldraw/tldraw!TLUiMainMenuProps:type" + }, + { + "kind": "Content", + "text": ">" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/MainMenu/DefaultMainMenu.tsx", + "isReadonly": true, + "releaseTag": "Public", + "name": "DefaultMainMenu", + "variableTypeTokenRange": { + "startIndex": 1, + "endIndex": 6 + } + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DefaultMainMenuContent:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DefaultMainMenuContent(): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/MainMenu/DefaultMainMenuContent.tsx", + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 3 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "DefaultMainMenuContent" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DefaultMinimap:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DefaultMinimap(): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/Minimap/DefaultMinimap.tsx", + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 3 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "DefaultMinimap" + }, + { + "kind": "Variable", + "canonicalReference": "@tldraw/tldraw!DefaultPageMenu:var", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "DefaultPageMenu: " + }, + { + "kind": "Content", + "text": "import(\"react\")." + }, + { + "kind": "Reference", + "text": "NamedExoticComponent", + "canonicalReference": "@types/react!React.NamedExoticComponent:interface" + }, + { + "kind": "Content", + "text": "" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/PageMenu/DefaultPageMenu.tsx", + "isReadonly": true, + "releaseTag": "Public", + "name": "DefaultPageMenu", + "variableTypeTokenRange": { + "startIndex": 1, + "endIndex": 4 + } + }, + { + "kind": "Variable", + "canonicalReference": "@tldraw/tldraw!DefaultQuickActions:var", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "DefaultQuickActions: " + }, + { + "kind": "Content", + "text": "import(\"react\")." + }, + { + "kind": "Reference", + "text": "NamedExoticComponent", + "canonicalReference": "@types/react!React.NamedExoticComponent:interface" + }, + { + "kind": "Content", + "text": "<" + }, + { + "kind": "Reference", + "text": "TLUiQuickActionsProps", + "canonicalReference": "@tldraw/tldraw!TLUiQuickActionsProps:type" + }, + { + "kind": "Content", + "text": ">" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/QuickActions/DefaultQuickActions.tsx", + "isReadonly": true, + "releaseTag": "Public", + "name": "DefaultQuickActions", + "variableTypeTokenRange": { + "startIndex": 1, + "endIndex": 6 + } + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DefaultQuickActionsContent:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DefaultQuickActionsContent(): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": " | undefined" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/QuickActions/DefaultQuickActionsContent.tsx", + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 4 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "DefaultQuickActionsContent" + }, { "kind": "Variable", "canonicalReference": "@tldraw/tldraw!defaultShapeTools:var", @@ -2598,6 +3192,130 @@ "endIndex": 3 } }, + { + "kind": "Variable", + "canonicalReference": "@tldraw/tldraw!DefaultStylePanel:var", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "DefaultStylePanel: " + }, + { + "kind": "Content", + "text": "import(\"react\")." + }, + { + "kind": "Reference", + "text": "NamedExoticComponent", + "canonicalReference": "@types/react!React.NamedExoticComponent:interface" + }, + { + "kind": "Content", + "text": "<" + }, + { + "kind": "Reference", + "text": "TLUiStylePanelProps", + "canonicalReference": "@tldraw/tldraw!TLUiStylePanelProps:interface" + }, + { + "kind": "Content", + "text": ">" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/StylePanel/DefaultStylePanel.tsx", + "isReadonly": true, + "releaseTag": "Public", + "name": "DefaultStylePanel", + "variableTypeTokenRange": { + "startIndex": 1, + "endIndex": 6 + } + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DefaultStylePanelContent:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DefaultStylePanelContent({ relevantStyles }: " + }, + { + "kind": "Reference", + "text": "TLUiStylePanelContentProps", + "canonicalReference": "@tldraw/tldraw!TLUiStylePanelContentProps:type" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": " | null" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/StylePanel/DefaultStylePanelContent.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 6 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ relevantStyles }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "DefaultStylePanelContent" + }, + { + "kind": "Variable", + "canonicalReference": "@tldraw/tldraw!DefaultToolbar:var", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "DefaultToolbar: " + }, + { + "kind": "Reference", + "text": "React.NamedExoticComponent", + "canonicalReference": "@types/react!React.NamedExoticComponent:interface" + }, + { + "kind": "Content", + "text": "" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/Toolbar/DefaultToolbar.tsx", + "isReadonly": true, + "releaseTag": "Public", + "name": "DefaultToolbar", + "variableTypeTokenRange": { + "startIndex": 1, + "endIndex": 3 + } + }, { "kind": "Variable", "canonicalReference": "@tldraw/tldraw!defaultTools:var", @@ -2667,258 +3385,315 @@ } }, { - "kind": "Namespace", - "canonicalReference": "@tldraw/tldraw!Dialog:namespace", - "docComment": "", - "excerptTokens": [], - "fileUrlPath": "packages/tldraw/src/index.ts", - "releaseTag": "None", - "name": "Dialog", - "preserveMemberOrder": false, - "members": [ + "kind": "Variable", + "canonicalReference": "@tldraw/tldraw!DefaultZoomMenu:var", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!Dialog.Body:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function Body({ className, children, style, }: " - }, - { - "kind": "Content", - "text": "{\n className?: string;\n children: any;\n style?: " - }, - { - "kind": "Reference", - "text": "React.CSSProperties", - "canonicalReference": "@types/react!React.CSSProperties:interface" - }, - { - "kind": "Content", - "text": ";\n}" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Content", - "text": "import(\"react/jsx-runtime\")." - }, - { - "kind": "Reference", - "text": "JSX.Element", - "canonicalReference": "@types/react!JSX.Element:interface" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/Dialog.tsx", - "returnTypeTokenRange": { - "startIndex": 5, - "endIndex": 7 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "{ className, children, style, }", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 4 - }, - "isOptional": false - } - ], - "name": "Body" + "kind": "Content", + "text": "DefaultZoomMenu: " }, { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!Dialog.CloseButton:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function CloseButton(): " - }, - { - "kind": "Content", - "text": "import(\"react/jsx-runtime\")." - }, - { - "kind": "Reference", - "text": "JSX.Element", - "canonicalReference": "@types/react!JSX.Element:interface" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/Dialog.tsx", - "returnTypeTokenRange": { - "startIndex": 1, - "endIndex": 3 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [], - "name": "CloseButton" + "kind": "Content", + "text": "import(\"react\")." }, { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!Dialog.Footer:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function Footer({ className, children }: " - }, - { - "kind": "Content", - "text": "{\n className?: string;\n children: any;\n}" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Content", - "text": "import(\"react/jsx-runtime\")." - }, - { - "kind": "Reference", - "text": "JSX.Element", - "canonicalReference": "@types/react!JSX.Element:interface" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/Dialog.tsx", - "returnTypeTokenRange": { - "startIndex": 3, - "endIndex": 5 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "{ className, children }", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - } - ], - "name": "Footer" + "kind": "Reference", + "text": "NamedExoticComponent", + "canonicalReference": "@types/react!React.NamedExoticComponent:interface" }, { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!Dialog.Header:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function Header({ className, children }: " - }, - { - "kind": "Content", - "text": "{\n className?: string;\n children: any;\n}" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Content", - "text": "import(\"react/jsx-runtime\")." - }, - { - "kind": "Reference", - "text": "JSX.Element", - "canonicalReference": "@types/react!JSX.Element:interface" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/.tsbuild-api/lib/ui/components/primitives/Dialog.d.ts", - "returnTypeTokenRange": { - "startIndex": 3, - "endIndex": 5 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "{ className, children }", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - } - ], - "name": "Header" + "kind": "Content", + "text": "<" }, { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!Dialog.Title:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function Title({ className, children }: " - }, - { - "kind": "Content", - "text": "{\n className?: string;\n children: any;\n}" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Content", - "text": "import(\"react/jsx-runtime\")." - }, - { - "kind": "Reference", - "text": "JSX.Element", - "canonicalReference": "@types/react!JSX.Element:interface" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/Dialog.tsx", - "returnTypeTokenRange": { - "startIndex": 3, - "endIndex": 5 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "{ className, children }", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - } - ], - "name": "Title" + "kind": "Reference", + "text": "TLUiZoomMenuProps", + "canonicalReference": "@tldraw/tldraw!TLUiZoomMenuProps:type" + }, + { + "kind": "Content", + "text": ">" } - ] + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/ZoomMenu/DefaultZoomMenu.tsx", + "isReadonly": true, + "releaseTag": "Public", + "name": "DefaultZoomMenu", + "variableTypeTokenRange": { + "startIndex": 1, + "endIndex": 6 + } + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DefaultZoomMenuContent:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DefaultZoomMenuContent(): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/ZoomMenu/DefaultZoomMenuContent.tsx", + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 3 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "DefaultZoomMenuContent" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DialogBody:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DialogBody({ className, children, style }: " + }, + { + "kind": "Reference", + "text": "TLUiDialogBodyProps", + "canonicalReference": "@tldraw/tldraw!TLUiDialogBodyProps:type" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/Dialog.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ className, children, style }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "DialogBody" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DialogCloseButton:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DialogCloseButton(): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/Dialog.tsx", + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 3 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "DialogCloseButton" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DialogFooter:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DialogFooter({ className, children }: " + }, + { + "kind": "Reference", + "text": "TLUiDialogFooterProps", + "canonicalReference": "@tldraw/tldraw!TLUiDialogFooterProps:type" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/Dialog.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ className, children }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "DialogFooter" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DialogHeader:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DialogHeader({ className, children }: " + }, + { + "kind": "Reference", + "text": "TLUiDialogHeaderProps", + "canonicalReference": "@tldraw/tldraw!TLUiDialogHeaderProps:type" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/Dialog.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ className, children }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "DialogHeader" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DialogTitle:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DialogTitle({ className, children }: " + }, + { + "kind": "Reference", + "text": "TLUiDialogTitleProps", + "canonicalReference": "@tldraw/tldraw!TLUiDialogTitleProps:type" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/Dialog.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ className, children }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "DialogTitle" }, { "kind": "Function", @@ -3966,800 +4741,496 @@ "implementsTokenRanges": [] }, { - "kind": "Namespace", - "canonicalReference": "@tldraw/tldraw!DropdownMenu:namespace", - "docComment": "", - "excerptTokens": [], - "fileUrlPath": "packages/tldraw/src/index.ts", - "releaseTag": "None", - "name": "DropdownMenu", - "preserveMemberOrder": false, - "members": [ + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DropdownMenuCheckboxItem:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.CheckboxItem:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function CheckboxItem({ children, onSelect, ...rest }: " - }, - { - "kind": "Reference", - "text": "DropdownMenuCheckboxItemProps", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.DropdownMenuCheckboxItemProps:interface" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Content", - "text": "import(\"react/jsx-runtime\")." - }, - { - "kind": "Reference", - "text": "JSX.Element", - "canonicalReference": "@types/react!JSX.Element:interface" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", - "returnTypeTokenRange": { - "startIndex": 3, - "endIndex": 5 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "{ children, onSelect, ...rest }", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - } - ], - "name": "CheckboxItem" + "kind": "Content", + "text": "export declare function DropdownMenuCheckboxItem({ children, onSelect, ...rest }: " }, { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.Content:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function Content({ side, align, sideOffset, alignOffset, children, }: " - }, - { - "kind": "Content", - "text": "{\n children: any;\n alignOffset?: number;\n sideOffset?: number;\n align?: 'center' | 'end' | 'start';\n side?: 'bottom' | 'left' | 'right' | 'top';\n}" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Content", - "text": "import(\"react/jsx-runtime\")." - }, - { - "kind": "Reference", - "text": "JSX.Element", - "canonicalReference": "@types/react!JSX.Element:interface" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", - "returnTypeTokenRange": { - "startIndex": 3, - "endIndex": 5 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "{ side, align, sideOffset, alignOffset, children, }", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - } - ], - "name": "Content" + "kind": "Reference", + "text": "TLUiDropdownMenuCheckboxItemProps", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuCheckboxItemProps:interface" }, { - "kind": "Interface", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.DropdownMenuCheckboxItemProps:interface", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export interface DropdownMenuCheckboxItemProps " - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", - "releaseTag": "Public", - "name": "DropdownMenuCheckboxItemProps", - "preserveMemberOrder": false, - "members": [ - { - "kind": "PropertySignature", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.DropdownMenuCheckboxItemProps#checked:member", - "docComment": "", - "excerptTokens": [ - { - "kind": "Content", - "text": "checked?: " - }, - { - "kind": "Content", - "text": "boolean" - }, - { - "kind": "Content", - "text": ";" - } - ], - "isReadonly": false, - "isOptional": true, - "releaseTag": "Public", - "name": "checked", - "propertyTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - } - }, - { - "kind": "PropertySignature", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.DropdownMenuCheckboxItemProps#children:member", - "docComment": "", - "excerptTokens": [ - { - "kind": "Content", - "text": "children: " - }, - { - "kind": "Content", - "text": "any" - }, - { - "kind": "Content", - "text": ";" - } - ], - "isReadonly": false, - "isOptional": false, - "releaseTag": "Public", - "name": "children", - "propertyTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - } - }, - { - "kind": "PropertySignature", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.DropdownMenuCheckboxItemProps#disabled:member", - "docComment": "", - "excerptTokens": [ - { - "kind": "Content", - "text": "disabled?: " - }, - { - "kind": "Content", - "text": "boolean" - }, - { - "kind": "Content", - "text": ";" - } - ], - "isReadonly": false, - "isOptional": true, - "releaseTag": "Public", - "name": "disabled", - "propertyTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - } - }, - { - "kind": "PropertySignature", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.DropdownMenuCheckboxItemProps#onSelect:member", - "docComment": "", - "excerptTokens": [ - { - "kind": "Content", - "text": "onSelect?: " - }, - { - "kind": "Content", - "text": "(e: " - }, - { - "kind": "Reference", - "text": "Event", - "canonicalReference": "!Event:interface" - }, - { - "kind": "Content", - "text": ") => void" - }, - { - "kind": "Content", - "text": ";" - } - ], - "isReadonly": false, - "isOptional": true, - "releaseTag": "Public", - "name": "onSelect", - "propertyTypeTokenRange": { - "startIndex": 1, - "endIndex": 4 - } - }, - { - "kind": "PropertySignature", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.DropdownMenuCheckboxItemProps#title:member", - "docComment": "", - "excerptTokens": [ - { - "kind": "Content", - "text": "title: " - }, - { - "kind": "Content", - "text": "string" - }, - { - "kind": "Content", - "text": ";" - } - ], - "isReadonly": false, - "isOptional": false, - "releaseTag": "Public", - "name": "title", - "propertyTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - } - } - ], - "extendsTokenRanges": [] + "kind": "Content", + "text": "): " }, { - "kind": "Interface", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.DropdownMenuItemProps:interface", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export interface DropdownMenuItemProps extends " - }, - { - "kind": "Reference", - "text": "TLUiButtonProps", - "canonicalReference": "@tldraw/tldraw!TLUiButtonProps:interface" - }, - { - "kind": "Content", - "text": " " - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", - "releaseTag": "Public", - "name": "DropdownMenuItemProps", - "preserveMemberOrder": false, - "members": [ - { - "kind": "PropertySignature", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.DropdownMenuItemProps#noClose:member", - "docComment": "", - "excerptTokens": [ - { - "kind": "Content", - "text": "noClose?: " - }, - { - "kind": "Content", - "text": "boolean" - }, - { - "kind": "Content", - "text": ";" - } - ], - "isReadonly": false, - "isOptional": true, - "releaseTag": "Public", - "name": "noClose", - "propertyTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - } - } - ], - "extendsTokenRanges": [ - { - "startIndex": 1, - "endIndex": 2 - } - ] + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." }, { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.Group:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function Group({ children, size, }: " - }, - { - "kind": "Content", - "text": "{\n children: any;\n size?: 'medium' | 'small' | 'tiny' | 'wide';\n}" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Content", - "text": "import(\"react/jsx-runtime\")." - }, - { - "kind": "Reference", - "text": "JSX.Element", - "canonicalReference": "@types/react!JSX.Element:interface" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", - "returnTypeTokenRange": { - "startIndex": 3, - "endIndex": 5 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "{ children, size, }", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - } - ], - "name": "Group" + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" }, { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.Indicator:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function Indicator(): " - }, - { - "kind": "Content", - "text": "import(\"react/jsx-runtime\")." - }, - { - "kind": "Reference", - "text": "JSX.Element", - "canonicalReference": "@types/react!JSX.Element:interface" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", - "returnTypeTokenRange": { - "startIndex": 1, - "endIndex": 3 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [], - "name": "Indicator" - }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.Item:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function Item({ noClose, ...props }: " - }, - { - "kind": "Reference", - "text": "DropdownMenuItemProps", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.DropdownMenuItemProps:interface" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Content", - "text": "import(\"react/jsx-runtime\")." - }, - { - "kind": "Reference", - "text": "JSX.Element", - "canonicalReference": "@types/react!JSX.Element:interface" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", - "returnTypeTokenRange": { - "startIndex": 3, - "endIndex": 5 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "{ noClose, ...props }", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - } - ], - "name": "Item" - }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.RadioItem:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function RadioItem({ children, onSelect, ...rest }: " - }, - { - "kind": "Reference", - "text": "DropdownMenuCheckboxItemProps", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.DropdownMenuCheckboxItemProps:interface" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Content", - "text": "import(\"react/jsx-runtime\")." - }, - { - "kind": "Reference", - "text": "JSX.Element", - "canonicalReference": "@types/react!JSX.Element:interface" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", - "returnTypeTokenRange": { - "startIndex": 3, - "endIndex": 5 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "{ children, onSelect, ...rest }", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - } - ], - "name": "RadioItem" - }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.Root:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function Root({ id, children, modal, debugOpen, }: " - }, - { - "kind": "Content", - "text": "{\n id: string;\n children: any;\n modal?: boolean;\n debugOpen?: boolean;\n}" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Content", - "text": "import(\"react/jsx-runtime\")." - }, - { - "kind": "Reference", - "text": "JSX.Element", - "canonicalReference": "@types/react!JSX.Element:interface" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", - "returnTypeTokenRange": { - "startIndex": 3, - "endIndex": 5 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "{ id, children, modal, debugOpen, }", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - } - ], - "name": "Root" - }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.Sub:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function Sub({ id, children }: " - }, - { - "kind": "Content", - "text": "{\n id: string;\n children: any;\n}" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Content", - "text": "import(\"react/jsx-runtime\")." - }, - { - "kind": "Reference", - "text": "JSX.Element", - "canonicalReference": "@types/react!JSX.Element:interface" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", - "returnTypeTokenRange": { - "startIndex": 3, - "endIndex": 5 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "{ id, children }", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - } - ], - "name": "Sub" - }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.SubContent:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function SubContent({ alignOffset, sideOffset, children, }: " - }, - { - "kind": "Content", - "text": "{\n alignOffset?: number;\n sideOffset?: number;\n children: any;\n}" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Content", - "text": "import(\"react/jsx-runtime\")." - }, - { - "kind": "Reference", - "text": "JSX.Element", - "canonicalReference": "@types/react!JSX.Element:interface" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", - "returnTypeTokenRange": { - "startIndex": 3, - "endIndex": 5 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "{ alignOffset, sideOffset, children, }", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - } - ], - "name": "SubContent" - }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.SubTrigger:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function SubTrigger({ label, 'data-testid': testId, 'data-direction': dataDirection, }: " - }, - { - "kind": "Content", - "text": "{\n label: " - }, - { - "kind": "Reference", - "text": "Exclude", - "canonicalReference": "!Exclude:type" - }, - { - "kind": "Content", - "text": " | " - }, - { - "kind": "Reference", - "text": "TLUiTranslationKey", - "canonicalReference": "@tldraw/tldraw!TLUiTranslationKey:type" - }, - { - "kind": "Content", - "text": ";\n 'data-testid'?: string;\n 'data-direction'?: 'left' | 'right';\n}" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Content", - "text": "import(\"react/jsx-runtime\")." - }, - { - "kind": "Reference", - "text": "JSX.Element", - "canonicalReference": "@types/react!JSX.Element:interface" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", - "returnTypeTokenRange": { - "startIndex": 9, - "endIndex": 11 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "{ label, 'data-testid': testId, 'data-direction': dataDirection, }", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 8 - }, - "isOptional": false - } - ], - "name": "SubTrigger" - }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!DropdownMenu.Trigger:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function Trigger({ children, 'data-testid': testId, }: " - }, - { - "kind": "Content", - "text": "{\n children: any;\n 'data-testid'?: string;\n}" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Content", - "text": "import(\"react/jsx-runtime\")." - }, - { - "kind": "Reference", - "text": "JSX.Element", - "canonicalReference": "@types/react!JSX.Element:interface" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", - "returnTypeTokenRange": { - "startIndex": 3, - "endIndex": 5 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "{ children, 'data-testid': testId, }", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - } - ], - "name": "Trigger" + "kind": "Content", + "text": ";" } - ] + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ children, onSelect, ...rest }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "DropdownMenuCheckboxItem" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DropdownMenuContent:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DropdownMenuContent({ side, align, sideOffset, alignOffset, children, }: " + }, + { + "kind": "Reference", + "text": "TLUiDropdownMenuContentProps", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuContentProps:type" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ side, align, sideOffset, alignOffset, children, }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "DropdownMenuContent" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DropdownMenuGroup:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DropdownMenuGroup({ children, size }: " + }, + { + "kind": "Reference", + "text": "TLUiDropdownMenuGroupProps", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuGroupProps:type" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ children, size }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "DropdownMenuGroup" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DropdownMenuIndicator:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DropdownMenuIndicator(): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 3 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "DropdownMenuIndicator" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DropdownMenuItem:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DropdownMenuItem({ noClose, ...props }: " + }, + { + "kind": "Reference", + "text": "TLUiDropdownMenuItemProps", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuItemProps:interface" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ noClose, ...props }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "DropdownMenuItem" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DropdownMenuRadioItem:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DropdownMenuRadioItem({ children, ...rest }: " + }, + { + "kind": "Reference", + "text": "TLUiDropdownMenuRadioItemProps", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuRadioItemProps:interface" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ children, ...rest }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "DropdownMenuRadioItem" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DropdownMenuRoot:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DropdownMenuRoot({ id, children, modal, debugOpen, }: " + }, + { + "kind": "Reference", + "text": "TLUiDropdownMenuRootProps", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuRootProps:type" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ id, children, modal, debugOpen, }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "DropdownMenuRoot" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DropdownMenuSub:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DropdownMenuSub({ id, children }: " + }, + { + "kind": "Reference", + "text": "TLUiDropdownMenuSubProps", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuSubProps:type" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ id, children }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "DropdownMenuSub" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DropdownMenuSubTrigger:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DropdownMenuSubTrigger({ label, title, disabled, }: " + }, + { + "kind": "Reference", + "text": "TLUiDropdownMenuSubTriggerProps", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuSubTriggerProps:type" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ label, title, disabled, }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "DropdownMenuSubTrigger" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!DropdownMenuTrigger:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function DropdownMenuTrigger({ children, ...rest }: " + }, + { + "kind": "Reference", + "text": "TLUiDropdownMenuTriggerProps", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuTriggerProps:interface" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ children, ...rest }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "DropdownMenuTrigger" }, { "kind": "Class", @@ -5567,7 +6038,7 @@ "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useEventsProvider.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/events.tsx", "releaseTag": "Public", "name": "EventsProviderProps", "typeTokenRange": { @@ -5694,100 +6165,6 @@ ], "name": "exportAs" }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!findMenuItem:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function findMenuItem(menu: " - }, - { - "kind": "Reference", - "text": "TLUiMenuSchema", - "canonicalReference": "@tldraw/tldraw!TLUiMenuSchema:type" - }, - { - "kind": "Content", - "text": ", path: " - }, - { - "kind": "Content", - "text": "string[]" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Reference", - "text": "TLUiCustomMenuItem", - "canonicalReference": "@tldraw/tldraw!TLUiCustomMenuItem:type" - }, - { - "kind": "Content", - "text": " | " - }, - { - "kind": "Reference", - "text": "TLUiMenuGroup", - "canonicalReference": "@tldraw/tldraw!TLUiMenuGroup:type" - }, - { - "kind": "Content", - "text": " | " - }, - { - "kind": "Reference", - "text": "TLUiMenuItem", - "canonicalReference": "@tldraw/tldraw!TLUiMenuItem:type" - }, - { - "kind": "Content", - "text": " | " - }, - { - "kind": "Reference", - "text": "TLUiSubMenu", - "canonicalReference": "@tldraw/tldraw!TLUiSubMenu:type" - }, - { - "kind": "Content", - "text": "" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/menuHelpers.ts", - "returnTypeTokenRange": { - "startIndex": 5, - "endIndex": 13 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "menu", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - }, - { - "parameterName": "path", - "parameterTypeTokenRange": { - "startIndex": 3, - "endIndex": 4 - }, - "isOptional": false - } - ], - "name": "findMenuItem" - }, { "kind": "Function", "canonicalReference": "@tldraw/tldraw!fitFrameToContent:function(1)", @@ -11118,334 +11495,6 @@ }, "implementsTokenRanges": [] }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!menuCustom:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function menuCustom(id: " - }, - { - "kind": "Content", - "text": "string" - }, - { - "kind": "Content", - "text": ", opts?: " - }, - { - "kind": "Reference", - "text": "Partial", - "canonicalReference": "!Partial:type" - }, - { - "kind": "Content", - "text": "<{\n readonlyOk: boolean;\n disabled: boolean;\n}>" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Content", - "text": "{\n id: string;\n type: \"custom\";\n disabled: boolean;\n readonlyOk: boolean;\n}" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/menuHelpers.ts", - "returnTypeTokenRange": { - "startIndex": 6, - "endIndex": 7 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "id", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - }, - { - "parameterName": "opts", - "parameterTypeTokenRange": { - "startIndex": 3, - "endIndex": 5 - }, - "isOptional": true - } - ], - "name": "menuCustom" - }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!menuGroup:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function menuGroup(id: " - }, - { - "kind": "Content", - "text": "string" - }, - { - "kind": "Content", - "text": ", ...children: " - }, - { - "kind": "Content", - "text": "(false | " - }, - { - "kind": "Reference", - "text": "TLUiMenuChild", - "canonicalReference": "@tldraw/tldraw!TLUiMenuChild:type" - }, - { - "kind": "Content", - "text": ")[]" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Content", - "text": "null | " - }, - { - "kind": "Reference", - "text": "TLUiMenuGroup", - "canonicalReference": "@tldraw/tldraw!TLUiMenuGroup:type" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/menuHelpers.ts", - "returnTypeTokenRange": { - "startIndex": 7, - "endIndex": 9 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "id", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - }, - { - "parameterName": "children", - "parameterTypeTokenRange": { - "startIndex": 3, - "endIndex": 6 - }, - "isOptional": false - } - ], - "name": "menuGroup" - }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!menuItem:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function menuItem(actionItem: " - }, - { - "kind": "Reference", - "text": "TLUiActionItem", - "canonicalReference": "@tldraw/tldraw!TLUiActionItem:interface" - }, - { - "kind": "Content", - "text": " | " - }, - { - "kind": "Reference", - "text": "TLUiToolItem", - "canonicalReference": "@tldraw/tldraw!TLUiToolItem:interface" - }, - { - "kind": "Content", - "text": ", opts?: " - }, - { - "kind": "Reference", - "text": "Partial", - "canonicalReference": "!Partial:type" - }, - { - "kind": "Content", - "text": "<{\n checked: boolean;\n disabled: boolean;\n}>" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Reference", - "text": "TLUiMenuItem", - "canonicalReference": "@tldraw/tldraw!TLUiMenuItem:type" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/menuHelpers.ts", - "returnTypeTokenRange": { - "startIndex": 8, - "endIndex": 9 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "actionItem", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 4 - }, - "isOptional": false - }, - { - "parameterName": "opts", - "parameterTypeTokenRange": { - "startIndex": 5, - "endIndex": 7 - }, - "isOptional": true - } - ], - "name": "menuItem" - }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!menuSubmenu:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function menuSubmenu(id: " - }, - { - "kind": "Content", - "text": "string" - }, - { - "kind": "Content", - "text": ", label: " - }, - { - "kind": "Reference", - "text": "Exclude", - "canonicalReference": "!Exclude:type" - }, - { - "kind": "Content", - "text": " | " - }, - { - "kind": "Reference", - "text": "TLUiTranslationKey", - "canonicalReference": "@tldraw/tldraw!TLUiTranslationKey:type" - }, - { - "kind": "Content", - "text": ", ...children: " - }, - { - "kind": "Content", - "text": "(false | " - }, - { - "kind": "Reference", - "text": "TLUiMenuChild", - "canonicalReference": "@tldraw/tldraw!TLUiMenuChild:type" - }, - { - "kind": "Content", - "text": ")[]" - }, - { - "kind": "Content", - "text": "): " - }, - { - "kind": "Content", - "text": "null | " - }, - { - "kind": "Reference", - "text": "TLUiSubMenu", - "canonicalReference": "@tldraw/tldraw!TLUiSubMenu:type" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/menuHelpers.ts", - "returnTypeTokenRange": { - "startIndex": 13, - "endIndex": 15 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [ - { - "parameterName": "id", - "parameterTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isOptional": false - }, - { - "parameterName": "label", - "parameterTypeTokenRange": { - "startIndex": 3, - "endIndex": 8 - }, - "isOptional": false - }, - { - "parameterName": "children", - "parameterTypeTokenRange": { - "startIndex": 9, - "endIndex": 12 - }, - "isOptional": false - } - ], - "name": "menuSubmenu" - }, { "kind": "Class", "canonicalReference": "@tldraw/tldraw!NoteShapeTool:class", @@ -12541,6 +12590,159 @@ ], "name": "parseTldrawJsonFile" }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!Popover:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function Popover({ id, children, onOpenChange, open }: " + }, + { + "kind": "Reference", + "text": "TLUiPopoverProps", + "canonicalReference": "@tldraw/tldraw!TLUiPopoverProps:type" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/Popover.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ id, children, onOpenChange, open }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "Popover" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!PopoverContent:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function PopoverContent({ side, children, align, sideOffset, alignOffset, }: " + }, + { + "kind": "Reference", + "text": "TLUiPopoverContentProps", + "canonicalReference": "@tldraw/tldraw!TLUiPopoverContentProps:type" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/Popover.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ side, children, align, sideOffset, alignOffset, }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "PopoverContent" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!PopoverTrigger:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function PopoverTrigger({ children, ...rest }: " + }, + { + "kind": "Reference", + "text": "TLUiPopoverTriggerProps", + "canonicalReference": "@tldraw/tldraw!TLUiPopoverTriggerProps:interface" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/Popover.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ children, ...rest }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "PopoverTrigger" + }, { "kind": "Function", "canonicalReference": "@tldraw/tldraw!removeFrame:function(1)", @@ -14213,6 +14415,42 @@ }, "implementsTokenRanges": [] }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLComponents:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLComponents = " + }, + { + "kind": "Reference", + "text": "TLEditorComponents", + "canonicalReference": "@tldraw/editor!TLEditorComponents:type" + }, + { + "kind": "Content", + "text": " & " + }, + { + "kind": "Reference", + "text": "TLUiComponents", + "canonicalReference": "@tldraw/tldraw!TLUiComponents:type" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/Tldraw.tsx", + "releaseTag": "Public", + "name": "TLComponents", + "typeTokenRange": { + "startIndex": 1, + "endIndex": 4 + } + }, { "kind": "Variable", "canonicalReference": "@tldraw/tldraw!TLDRAW_FILE_EXTENSION:var", @@ -14626,6 +14864,37 @@ "kind": "Content", "text": "export type TldrawProps = " }, + { + "kind": "Content", + "text": "(" + }, + { + "kind": "Reference", + "text": "Omit", + "canonicalReference": "!Omit:type" + }, + { + "kind": "Content", + "text": "<" + }, + { + "kind": "Reference", + "text": "TldrawUiProps", + "canonicalReference": "@tldraw/tldraw!TldrawUiProps:type" + }, + { + "kind": "Content", + "text": ", 'components'> & " + }, + { + "kind": "Reference", + "text": "Omit", + "canonicalReference": "!Omit:type" + }, + { + "kind": "Content", + "text": "<" + }, { "kind": "Reference", "text": "TldrawEditorBaseProps", @@ -14633,7 +14902,34 @@ }, { "kind": "Content", - "text": " & ({\n store: " + "text": ", 'components'> & {\n components?: " + }, + { + "kind": "Reference", + "text": "TLComponents", + "canonicalReference": "@tldraw/tldraw!TLComponents:type" + }, + { + "kind": "Content", + "text": ";\n}) & " + }, + { + "kind": "Reference", + "text": "Partial", + "canonicalReference": "!Partial:type" + }, + { + "kind": "Content", + "text": "<" + }, + { + "kind": "Reference", + "text": "TLExternalContentProps", + "canonicalReference": "@tldraw/tldraw!~TLExternalContentProps:type" + }, + { + "kind": "Content", + "text": "> & ({\n store: " }, { "kind": "Reference", @@ -14669,34 +14965,7 @@ }, { "kind": "Content", - "text": ">;\n}) & " - }, - { - "kind": "Reference", - "text": "TldrawUiProps", - "canonicalReference": "@tldraw/tldraw!TldrawUiProps:type" - }, - { - "kind": "Content", - "text": " & " - }, - { - "kind": "Reference", - "text": "Partial", - "canonicalReference": "!Partial:type" - }, - { - "kind": "Content", - "text": "<" - }, - { - "kind": "Reference", - "text": "TLExternalContentProps", - "canonicalReference": "@tldraw/tldraw!~TLExternalContentProps:type" - }, - { - "kind": "Content", - "text": ">" + "text": ">;\n})" }, { "kind": "Content", @@ -14708,7 +14977,7 @@ "name": "TldrawProps", "typeTokenRange": { "startIndex": 1, - "endIndex": 17 + "endIndex": 24 } }, { @@ -14891,6 +15160,34 @@ "endIndex": 2 } }, + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TldrawUiBaseProps#components:member", + "docComment": "/**\n * Overrides for the UI components.\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "components?: " + }, + { + "kind": "Reference", + "text": "TLUiComponents", + "canonicalReference": "@tldraw/tldraw!TLUiComponents:type" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": true, + "releaseTag": "Public", + "name": "components", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, { "kind": "PropertySignature", "canonicalReference": "@tldraw/tldraw!TldrawUiBaseProps#hideUi:member", @@ -14981,6 +15278,57 @@ ], "extendsTokenRanges": [] }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!TldrawUiComponentsProvider:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function TldrawUiComponentsProvider({ overrides, children, }: " + }, + { + "kind": "Reference", + "text": "ComponentsContextProviderProps", + "canonicalReference": "@tldraw/tldraw!~ComponentsContextProviderProps:type" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/context/components.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ overrides, children, }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "TldrawUiComponentsProvider" + }, { "kind": "Function", "canonicalReference": "@tldraw/tldraw!TldrawUiContextProvider:function(1)", @@ -14988,7 +15336,7 @@ "excerptTokens": [ { "kind": "Content", - "text": "export declare function TldrawUiContextProvider({ overrides, assetUrls, onUiEvent, forceMobile, children, }: " + "text": "export declare function TldrawUiContextProvider({ overrides, components, assetUrls, onUiEvent, forceMobile, children, }: " }, { "kind": "Reference", @@ -15013,7 +15361,7 @@ "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/TldrawUiContextProvider.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/TldrawUiContextProvider.tsx", "returnTypeTokenRange": { "startIndex": 3, "endIndex": 5 @@ -15022,7 +15370,7 @@ "overloadIndex": 1, "parameters": [ { - "parameterName": "{ overrides, assetUrls, onUiEvent, forceMobile, children, }", + "parameterName": "{ overrides, components, assetUrls, onUiEvent, forceMobile, children, }", "parameterTypeTokenRange": { "startIndex": 1, "endIndex": 2 @@ -15042,7 +15390,7 @@ "text": "export interface TldrawUiContextProviderProps " } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/TldrawUiContextProvider.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/TldrawUiContextProvider.tsx", "releaseTag": "Public", "name": "TldrawUiContextProviderProps", "preserveMemberOrder": false, @@ -15115,6 +15463,34 @@ "endIndex": 2 } }, + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TldrawUiContextProviderProps#components:member", + "docComment": "/**\n * Overrides for the UI components.\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "components?: " + }, + { + "kind": "Reference", + "text": "TLUiComponents", + "canonicalReference": "@tldraw/tldraw!TLUiComponents:type" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": true, + "releaseTag": "Public", + "name": "components", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, { "kind": "PropertySignature", "canonicalReference": "@tldraw/tldraw!TldrawUiContextProviderProps#forceMobile:member", @@ -15214,6 +15590,412 @@ ], "extendsTokenRanges": [] }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!TldrawUiMenuCheckboxItem:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function TldrawUiMenuCheckboxItem({ id, kbd, label, readonlyOk, onSelect, disabled, checked, }: " + }, + { + "kind": "Reference", + "text": "TLUiMenuCheckboxItemProps", + "canonicalReference": "@tldraw/tldraw!TLUiMenuCheckboxItemProps:type" + }, + { + "kind": "Content", + "text": "" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": " | null" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuCheckboxItem.tsx", + "returnTypeTokenRange": { + "startIndex": 12, + "endIndex": 15 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ id, kbd, label, readonlyOk, onSelect, disabled, checked, }", + "parameterTypeTokenRange": { + "startIndex": 9, + "endIndex": 11 + }, + "isOptional": false + } + ], + "typeParameters": [ + { + "typeParameterName": "TranslationKey", + "constraintTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "defaultTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + } + }, + { + "typeParameterName": "IconType", + "constraintTokenRange": { + "startIndex": 5, + "endIndex": 6 + }, + "defaultTypeTokenRange": { + "startIndex": 7, + "endIndex": 8 + } + } + ], + "name": "TldrawUiMenuCheckboxItem" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!TldrawUiMenuContextProvider:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function TldrawUiMenuContextProvider({ type, sourceId, children, }: " + }, + { + "kind": "Reference", + "text": "TLUiMenuContextProviderProps", + "canonicalReference": "@tldraw/tldraw!TLUiMenuContextProviderProps:type" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuContext.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 5 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ type, sourceId, children, }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "TldrawUiMenuContextProvider" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!TldrawUiMenuGroup:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function TldrawUiMenuGroup({ id, label, small, children }: " + }, + { + "kind": "Reference", + "text": "TLUiMenuGroupProps", + "canonicalReference": "@tldraw/tldraw!TLUiMenuGroupProps:type" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "any" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuGroup.tsx", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ id, label, small, children }", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "TldrawUiMenuGroup" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!TldrawUiMenuItem:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function TldrawUiMenuItem({ disabled, spinner, readonlyOk, id, kbd, label, icon, onSelect, noClose, }: " + }, + { + "kind": "Reference", + "text": "TLUiMenuItemProps", + "canonicalReference": "@tldraw/tldraw!TLUiMenuItemProps:type" + }, + { + "kind": "Content", + "text": "" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "import(\"react/jsx-runtime\")." + }, + { + "kind": "Reference", + "text": "JSX.Element", + "canonicalReference": "@types/react!JSX.Element:interface" + }, + { + "kind": "Content", + "text": " | null" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuItem.tsx", + "returnTypeTokenRange": { + "startIndex": 12, + "endIndex": 15 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ disabled, spinner, readonlyOk, id, kbd, label, icon, onSelect, noClose, }", + "parameterTypeTokenRange": { + "startIndex": 9, + "endIndex": 11 + }, + "isOptional": false + } + ], + "typeParameters": [ + { + "typeParameterName": "TranslationKey", + "constraintTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "defaultTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + } + }, + { + "typeParameterName": "IconType", + "constraintTokenRange": { + "startIndex": 5, + "endIndex": 6 + }, + "defaultTypeTokenRange": { + "startIndex": 7, + "endIndex": 8 + } + } + ], + "name": "TldrawUiMenuItem" + }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!TldrawUiMenuSubmenu:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function TldrawUiMenuSubmenu({ id, disabled, label, size, children, }: " + }, + { + "kind": "Reference", + "text": "TLUiMenuSubmenuProps", + "canonicalReference": "@tldraw/tldraw!TLUiMenuSubmenuProps:type" + }, + { + "kind": "Content", + "text": "" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "any" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuSubmenu.tsx", + "returnTypeTokenRange": { + "startIndex": 8, + "endIndex": 9 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ id, disabled, label, size, children, }", + "parameterTypeTokenRange": { + "startIndex": 5, + "endIndex": 7 + }, + "isOptional": false + } + ], + "typeParameters": [ + { + "typeParameterName": "Translation", + "constraintTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "defaultTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + } + } + ], + "name": "TldrawUiMenuSubmenu" + }, { "kind": "TypeAlias", "canonicalReference": "@tldraw/tldraw!TldrawUiProps:type", @@ -15292,7 +16074,7 @@ "text": "> " } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useActions.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/actions.tsx", "releaseTag": "Public", "typeParameters": [ { @@ -15348,33 +16130,6 @@ "endIndex": 2 } }, - { - "kind": "PropertySignature", - "canonicalReference": "@tldraw/tldraw!TLUiActionItem#contextMenuLabel:member", - "docComment": "", - "excerptTokens": [ - { - "kind": "Content", - "text": "contextMenuLabel?: " - }, - { - "kind": "Content", - "text": "TransationKey" - }, - { - "kind": "Content", - "text": ";" - } - ], - "isReadonly": false, - "isOptional": true, - "releaseTag": "Public", - "name": "contextMenuLabel", - "propertyTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - } - }, { "kind": "PropertySignature", "canonicalReference": "@tldraw/tldraw!TLUiActionItem#icon:member", @@ -15467,7 +16222,7 @@ }, { "kind": "Content", - "text": "TransationKey" + "text": "{\n [key: string]: TransationKey;\n } | TransationKey" }, { "kind": "Content", @@ -15483,33 +16238,6 @@ "endIndex": 2 } }, - { - "kind": "PropertySignature", - "canonicalReference": "@tldraw/tldraw!TLUiActionItem#menuLabel:member", - "docComment": "", - "excerptTokens": [ - { - "kind": "Content", - "text": "menuLabel?: " - }, - { - "kind": "Content", - "text": "TransationKey" - }, - { - "kind": "Content", - "text": ";" - } - ], - "isReadonly": false, - "isOptional": true, - "releaseTag": "Public", - "name": "menuLabel", - "propertyTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - } - }, { "kind": "PropertySignature", "canonicalReference": "@tldraw/tldraw!TLUiActionItem#onSelect:member", @@ -15562,7 +16290,7 @@ "excerptTokens": [ { "kind": "Content", - "text": "readonlyOk: " + "text": "readonlyOk?: " }, { "kind": "Content", @@ -15574,36 +16302,9 @@ } ], "isReadonly": false, - "isOptional": false, - "releaseTag": "Public", - "name": "readonlyOk", - "propertyTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - } - }, - { - "kind": "PropertySignature", - "canonicalReference": "@tldraw/tldraw!TLUiActionItem#shortcutsLabel:member", - "docComment": "", - "excerptTokens": [ - { - "kind": "Content", - "text": "shortcutsLabel?: " - }, - { - "kind": "Content", - "text": "TransationKey" - }, - { - "kind": "Content", - "text": ";" - } - ], - "isReadonly": false, "isOptional": true, "releaseTag": "Public", - "name": "shortcutsLabel", + "name": "readonlyOk", "propertyTypeTokenRange": { "startIndex": 1, "endIndex": 2 @@ -15671,7 +16372,7 @@ "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useActions.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/actions.tsx", "releaseTag": "Public", "name": "TLUiActionsContextType", "typeTokenRange": { @@ -15681,26 +16382,25 @@ }, { "kind": "TypeAlias", - "canonicalReference": "@tldraw/tldraw!TLUiActionsMenuSchemaContextType:type", + "canonicalReference": "@tldraw/tldraw!TLUiActionsMenuProps:type", "docComment": "/**\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", - "text": "export type TLUiActionsMenuSchemaContextType = " + "text": "export type TLUiActionsMenuProps = " }, { - "kind": "Reference", - "text": "TLUiMenuSchema", - "canonicalReference": "@tldraw/tldraw!TLUiMenuSchema:type" + "kind": "Content", + "text": "{\n children?: any;\n}" }, { "kind": "Content", "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useActionsMenuSchema.tsx", + "fileUrlPath": "packages/tldraw/.tsbuild-api/lib/ui/components/ActionsMenu/DefaultActionsMenu.d.ts", "releaseTag": "Public", - "name": "TLUiActionsMenuSchemaContextType", + "name": "TLUiActionsMenuProps", "typeTokenRange": { "startIndex": 1, "endIndex": 2 @@ -16145,6 +16845,55 @@ } ] }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiComponents:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLUiComponents = " + }, + { + "kind": "Reference", + "text": "Partial", + "canonicalReference": "!Partial:type" + }, + { + "kind": "Content", + "text": "<{\n [K in keyof " + }, + { + "kind": "Reference", + "text": "BaseTLUiComponents", + "canonicalReference": "@tldraw/tldraw!~BaseTLUiComponents:interface" + }, + { + "kind": "Content", + "text": "]: " + }, + { + "kind": "Reference", + "text": "BaseTLUiComponents", + "canonicalReference": "@tldraw/tldraw!~BaseTLUiComponents:interface" + }, + { + "kind": "Content", + "text": "[K] | null;\n}>" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/context/components.tsx", + "releaseTag": "Public", + "name": "TLUiComponents", + "typeTokenRange": { + "startIndex": 1, + "endIndex": 7 + } + }, { "kind": "Interface", "canonicalReference": "@tldraw/tldraw!TLUiContextMenuProps:interface", @@ -16155,19 +16904,19 @@ "text": "export interface TLUiContextMenuProps " } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/components/ContextMenu.tsx", + "fileUrlPath": "packages/tldraw/.tsbuild-api/lib/ui/components/ContextMenu/DefaultContextMenu.d.ts", "releaseTag": "Public", "name": "TLUiContextMenuProps", "preserveMemberOrder": false, "members": [ { "kind": "PropertySignature", - "canonicalReference": "@tldraw/tldraw!TLUiContextMenuProps#children:member", + "canonicalReference": "@tldraw/tldraw!TLUiContextMenuProps#canvas:member", "docComment": "", "excerptTokens": [ { "kind": "Content", - "text": "children: " + "text": "canvas: " }, { "kind": "Content", @@ -16178,9 +16927,38 @@ "text": ";" } ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/ContextMenu/DefaultContextMenu.tsx", "isReadonly": false, "isOptional": false, "releaseTag": "Public", + "name": "canvas", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiContextMenuProps#children:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "children?: " + }, + { + "kind": "Content", + "text": "any" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/ContextMenu/DefaultContextMenu.tsx", + "isReadonly": false, + "isOptional": true, + "releaseTag": "Public", "name": "children", "propertyTypeTokenRange": { "startIndex": 1, @@ -16192,52 +16970,25 @@ }, { "kind": "TypeAlias", - "canonicalReference": "@tldraw/tldraw!TLUiContextTTLUiMenuSchemaContextType:type", + "canonicalReference": "@tldraw/tldraw!TLUiDebugMenuProps:type", "docComment": "/**\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", - "text": "export type TLUiContextTTLUiMenuSchemaContextType = " + "text": "export type TLUiDebugMenuProps = " }, { - "kind": "Reference", - "text": "TLUiMenuSchema", - "canonicalReference": "@tldraw/tldraw!TLUiMenuSchema:type" + "kind": "Content", + "text": "{\n children?: any;\n}" }, { "kind": "Content", "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useContextMenuSchema.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/components/DebugMenu/DefaultDebugMenu.tsx", "releaseTag": "Public", - "name": "TLUiContextTTLUiMenuSchemaContextType", - "typeTokenRange": { - "startIndex": 1, - "endIndex": 2 - } - }, - { - "kind": "TypeAlias", - "canonicalReference": "@tldraw/tldraw!TLUiCustomMenuItem:type", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export type TLUiCustomMenuItem = " - }, - { - "kind": "Content", - "text": "{\n id: string;\n type: 'custom';\n disabled: boolean;\n readonlyOk: boolean;\n}" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/menuHelpers.ts", - "releaseTag": "Public", - "name": "TLUiCustomMenuItem", + "name": "TLUiDebugMenuProps", "typeTokenRange": { "startIndex": 1, "endIndex": 2 @@ -16253,7 +17004,7 @@ "text": "export interface TLUiDialog " } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useDialogsProvider.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/dialogs.tsx", "releaseTag": "Public", "name": "TLUiDialog", "preserveMemberOrder": false, @@ -16267,9 +17018,14 @@ "kind": "Content", "text": "component: " }, + { + "kind": "Reference", + "text": "ComponentType", + "canonicalReference": "@types/react!React.ComponentType:type" + }, { "kind": "Content", - "text": "(props: " + "text": "<" }, { "kind": "Reference", @@ -16278,7 +17034,7 @@ }, { "kind": "Content", - "text": ") => any" + "text": ">" }, { "kind": "Content", @@ -16291,7 +17047,7 @@ "name": "component", "propertyTypeTokenRange": { "startIndex": 1, - "endIndex": 4 + "endIndex": 5 } }, { @@ -16351,6 +17107,93 @@ ], "extendsTokenRanges": [] }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiDialogBodyProps:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLUiDialogBodyProps = " + }, + { + "kind": "Content", + "text": "{\n className?: string;\n children: any;\n style?: " + }, + { + "kind": "Reference", + "text": "React.CSSProperties", + "canonicalReference": "@types/react!React.CSSProperties:interface" + }, + { + "kind": "Content", + "text": ";\n}" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/Dialog.tsx", + "releaseTag": "Public", + "name": "TLUiDialogBodyProps", + "typeTokenRange": { + "startIndex": 1, + "endIndex": 4 + } + }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiDialogFooterProps:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLUiDialogFooterProps = " + }, + { + "kind": "Content", + "text": "{\n className?: string;\n children: any;\n}" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/Dialog.tsx", + "releaseTag": "Public", + "name": "TLUiDialogFooterProps", + "typeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiDialogHeaderProps:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLUiDialogHeaderProps = " + }, + { + "kind": "Content", + "text": "{\n className?: string;\n children: any;\n}" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/.tsbuild-api/lib/ui/components/primitives/Dialog.d.ts", + "releaseTag": "Public", + "name": "TLUiDialogHeaderProps", + "typeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, { "kind": "Interface", "canonicalReference": "@tldraw/tldraw!TLUiDialogProps:interface", @@ -16361,7 +17204,7 @@ "text": "export interface TLUiDialogProps " } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useDialogsProvider.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/dialogs.tsx", "releaseTag": "Public", "name": "TLUiDialogProps", "preserveMemberOrder": false, @@ -16459,7 +17302,7 @@ "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useDialogsProvider.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/dialogs.tsx", "releaseTag": "Public", "name": "TLUiDialogsContextType", "typeTokenRange": { @@ -16467,6 +17310,604 @@ "endIndex": 12 } }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiDialogTitleProps:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLUiDialogTitleProps = " + }, + { + "kind": "Content", + "text": "{\n className?: string;\n children: any;\n}" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/Dialog.tsx", + "releaseTag": "Public", + "name": "TLUiDialogTitleProps", + "typeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "Interface", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuCheckboxItemProps:interface", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export interface TLUiDropdownMenuCheckboxItemProps " + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "releaseTag": "Public", + "name": "TLUiDropdownMenuCheckboxItemProps", + "preserveMemberOrder": false, + "members": [ + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuCheckboxItemProps#checked:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "checked?: " + }, + { + "kind": "Content", + "text": "boolean" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": true, + "releaseTag": "Public", + "name": "checked", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuCheckboxItemProps#children:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "children: " + }, + { + "kind": "Content", + "text": "any" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": false, + "releaseTag": "Public", + "name": "children", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuCheckboxItemProps#disabled:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "disabled?: " + }, + { + "kind": "Content", + "text": "boolean" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": true, + "releaseTag": "Public", + "name": "disabled", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuCheckboxItemProps#onSelect:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "onSelect?: " + }, + { + "kind": "Content", + "text": "(e: " + }, + { + "kind": "Reference", + "text": "Event", + "canonicalReference": "!Event:interface" + }, + { + "kind": "Content", + "text": ") => void" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": true, + "releaseTag": "Public", + "name": "onSelect", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 4 + } + }, + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuCheckboxItemProps#title:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "title: " + }, + { + "kind": "Content", + "text": "string" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": false, + "releaseTag": "Public", + "name": "title", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + } + ], + "extendsTokenRanges": [] + }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuContentProps:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLUiDropdownMenuContentProps = " + }, + { + "kind": "Content", + "text": "{\n id?: string;\n children: any;\n alignOffset?: number;\n sideOffset?: number;\n align?: 'center' | 'end' | 'start';\n side?: 'bottom' | 'left' | 'right' | 'top';\n}" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "releaseTag": "Public", + "name": "TLUiDropdownMenuContentProps", + "typeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuGroupProps:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLUiDropdownMenuGroupProps = " + }, + { + "kind": "Content", + "text": "{\n children: any;\n size?: 'medium' | 'small' | 'tiny' | 'wide';\n}" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "releaseTag": "Public", + "name": "TLUiDropdownMenuGroupProps", + "typeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "Interface", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuItemProps:interface", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export interface TLUiDropdownMenuItemProps extends " + }, + { + "kind": "Reference", + "text": "TLUiButtonProps", + "canonicalReference": "@tldraw/tldraw!TLUiButtonProps:interface" + }, + { + "kind": "Content", + "text": " " + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "releaseTag": "Public", + "name": "TLUiDropdownMenuItemProps", + "preserveMemberOrder": false, + "members": [ + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuItemProps#noClose:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "noClose?: " + }, + { + "kind": "Content", + "text": "boolean" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": true, + "releaseTag": "Public", + "name": "noClose", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + } + ], + "extendsTokenRanges": [ + { + "startIndex": 1, + "endIndex": 2 + } + ] + }, + { + "kind": "Interface", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuRadioItemProps:interface", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export interface TLUiDropdownMenuRadioItemProps " + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "releaseTag": "Public", + "name": "TLUiDropdownMenuRadioItemProps", + "preserveMemberOrder": false, + "members": [ + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuRadioItemProps#checked:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "checked?: " + }, + { + "kind": "Content", + "text": "boolean" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": true, + "releaseTag": "Public", + "name": "checked", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuRadioItemProps#children:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "children: " + }, + { + "kind": "Content", + "text": "any" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": false, + "releaseTag": "Public", + "name": "children", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuRadioItemProps#disabled:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "disabled?: " + }, + { + "kind": "Content", + "text": "boolean" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": true, + "releaseTag": "Public", + "name": "disabled", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuRadioItemProps#onSelect:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "onSelect?: " + }, + { + "kind": "Content", + "text": "(e: " + }, + { + "kind": "Reference", + "text": "Event", + "canonicalReference": "!Event:interface" + }, + { + "kind": "Content", + "text": ") => void" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": true, + "releaseTag": "Public", + "name": "onSelect", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 4 + } + }, + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuRadioItemProps#title:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "title: " + }, + { + "kind": "Content", + "text": "string" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": false, + "releaseTag": "Public", + "name": "title", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + } + ], + "extendsTokenRanges": [] + }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuRootProps:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLUiDropdownMenuRootProps = " + }, + { + "kind": "Content", + "text": "{\n id: string;\n children: any;\n modal?: boolean;\n debugOpen?: boolean;\n}" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "releaseTag": "Public", + "name": "TLUiDropdownMenuRootProps", + "typeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuSubProps:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLUiDropdownMenuSubProps = " + }, + { + "kind": "Content", + "text": "{\n id: string;\n children: any;\n}" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "releaseTag": "Public", + "name": "TLUiDropdownMenuSubProps", + "typeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuSubTriggerProps:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLUiDropdownMenuSubTriggerProps = " + }, + { + "kind": "Content", + "text": "{\n label: string;\n id?: string;\n title?: string;\n disabled?: boolean;\n}" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "releaseTag": "Public", + "name": "TLUiDropdownMenuSubTriggerProps", + "typeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "Interface", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuTriggerProps:interface", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export interface TLUiDropdownMenuTriggerProps extends " + }, + { + "kind": "Reference", + "text": "TLUiButtonProps", + "canonicalReference": "@tldraw/tldraw!TLUiButtonProps:interface" + }, + { + "kind": "Content", + "text": " " + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/DropdownMenu.tsx", + "releaseTag": "Public", + "name": "TLUiDropdownMenuTriggerProps", + "preserveMemberOrder": false, + "members": [ + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiDropdownMenuTriggerProps#children:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "children?: " + }, + { + "kind": "Content", + "text": "any" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": true, + "releaseTag": "Public", + "name": "children", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + } + ], + "extendsTokenRanges": [ + { + "startIndex": 1, + "endIndex": 2 + } + ] + }, { "kind": "TypeAlias", "canonicalReference": "@tldraw/tldraw!TLUiEventContextType:type", @@ -16499,7 +17940,7 @@ "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useEventsProvider.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/events.tsx", "releaseTag": "Public", "name": "TLUiEventContextType", "typeTokenRange": { @@ -16578,7 +18019,7 @@ "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useEventsProvider.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/events.tsx", "releaseTag": "Public", "name": "TLUiEventHandler", "typeParameters": [ @@ -16609,7 +18050,7 @@ "text": "export interface TLUiEventMap " } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useEventsProvider.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/events.tsx", "releaseTag": "Public", "name": "TLUiEventMap", "preserveMemberOrder": false, @@ -16641,6 +18082,33 @@ "endIndex": 2 } }, + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiEventMap#\"change-language\":member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "'change-language': " + }, + { + "kind": "Content", + "text": "{\n locale: string;\n }" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": false, + "releaseTag": "Public", + "name": "\"change-language\"", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, { "kind": "PropertySignature", "canonicalReference": "@tldraw/tldraw!TLUiEventMap#\"close-menu\":member", @@ -17073,6 +18541,60 @@ "endIndex": 2 } }, + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiEventMap#\"move-to-page\":member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "'move-to-page': " + }, + { + "kind": "Content", + "text": "null" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": false, + "releaseTag": "Public", + "name": "\"move-to-page\"", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiEventMap#\"new-page\":member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "'new-page': " + }, + { + "kind": "Content", + "text": "null" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": false, + "releaseTag": "Public", + "name": "\"new-page\"", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, { "kind": "PropertySignature", "canonicalReference": "@tldraw/tldraw!TLUiEventMap#\"open-cursor-chat\":member", @@ -18282,7 +19804,7 @@ "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useEventsProvider.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/events.tsx", "releaseTag": "Public", "name": "TLUiEventSource", "typeTokenRange": { @@ -18292,26 +19814,51 @@ }, { "kind": "TypeAlias", - "canonicalReference": "@tldraw/tldraw!TLUiHelpMenuSchemaContextType:type", + "canonicalReference": "@tldraw/tldraw!TLUiHelperButtonsProps:type", "docComment": "/**\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", - "text": "export type TLUiHelpMenuSchemaContextType = " + "text": "export type TLUiHelperButtonsProps = " }, { - "kind": "Reference", - "text": "TLUiMenuSchema", - "canonicalReference": "@tldraw/tldraw!TLUiMenuSchema:type" + "kind": "Content", + "text": "{\n children?: any;\n}" }, { "kind": "Content", "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useHelpMenuSchema.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/components/HelperButtons/DefaultHelperButtons.tsx", "releaseTag": "Public", - "name": "TLUiHelpMenuSchemaContextType", + "name": "TLUiHelperButtonsProps", + "typeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiHelpMenuProps:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLUiHelpMenuProps = " + }, + { + "kind": "Content", + "text": "{\n children?: any;\n}" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/.tsbuild-api/lib/ui/components/HelpMenu/DefaultHelpMenu.d.ts", + "releaseTag": "Public", + "name": "TLUiHelpMenuProps", "typeTokenRange": { "startIndex": 1, "endIndex": 2 @@ -19078,26 +20625,56 @@ }, { "kind": "TypeAlias", - "canonicalReference": "@tldraw/tldraw!TLUiKeyboardShortcutsSchemaContextType:type", + "canonicalReference": "@tldraw/tldraw!TLUiKeyboardShortcutsDialogProps:type", "docComment": "/**\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", - "text": "export type TLUiKeyboardShortcutsSchemaContextType = " + "text": "export type TLUiKeyboardShortcutsDialogProps = " }, { "kind": "Reference", - "text": "TLUiMenuSchema", - "canonicalReference": "@tldraw/tldraw!TLUiMenuSchema:type" + "text": "TLUiDialogProps", + "canonicalReference": "@tldraw/tldraw!TLUiDialogProps:interface" + }, + { + "kind": "Content", + "text": " & {\n children?: any;\n}" }, { "kind": "Content", "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useKeyboardShortcutsSchema.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialog.tsx", "releaseTag": "Public", - "name": "TLUiKeyboardShortcutsSchemaContextType", + "name": "TLUiKeyboardShortcutsDialogProps", + "typeTokenRange": { + "startIndex": 1, + "endIndex": 3 + } + }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiMainMenuProps:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLUiMainMenuProps = " + }, + { + "kind": "Content", + "text": "{\n children?: any;\n}" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/.tsbuild-api/lib/ui/components/MainMenu/DefaultMainMenu.d.ts", + "releaseTag": "Public", + "name": "TLUiMainMenuProps", "typeTokenRange": { "startIndex": 1, "endIndex": 2 @@ -19105,83 +20682,28 @@ }, { "kind": "TypeAlias", - "canonicalReference": "@tldraw/tldraw!TLUiKeyboardShortcutsSchemaProviderProps:type", + "canonicalReference": "@tldraw/tldraw!TLUiMenuCheckboxItemProps:type", "docComment": "/**\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", - "text": "export type TLUiKeyboardShortcutsSchemaProviderProps = " + "text": "export type TLUiMenuCheckboxItemProps " - }, - { - "kind": "Reference", - "text": "TLUiKeyboardShortcutsSchemaContextType", - "canonicalReference": "@tldraw/tldraw!TLUiKeyboardShortcutsSchemaContextType:type" - }, - { - "kind": "Content", - "text": ";\n children: any;\n}" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useKeyboardShortcutsSchema.tsx", - "releaseTag": "Public", - "name": "TLUiKeyboardShortcutsSchemaProviderProps", - "typeTokenRange": { - "startIndex": 1, - "endIndex": 12 - } - }, - { - "kind": "TypeAlias", - "canonicalReference": "@tldraw/tldraw!TLUiMenuChild:type", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export type TLUiMenuChild " }, { "kind": "Reference", - "text": "TLUiMenuGroup", - "canonicalReference": "@tldraw/tldraw!TLUiMenuGroup:type" + "text": "Promise", + "canonicalReference": "!Promise:interface" }, { "kind": "Content", - "text": " | " - }, - { - "kind": "Reference", - "text": "TLUiMenuItem", - "canonicalReference": "@tldraw/tldraw!TLUiMenuItem:type" - }, - { - "kind": "Content", - "text": " | " - }, - { - "kind": "Reference", - "text": "TLUiSubMenu", - "canonicalReference": "@tldraw/tldraw!TLUiSubMenu:type" - }, - { - "kind": "Content", - "text": "" + "text": " | void;\n checked?: boolean;\n disabled?: boolean;\n}" }, { "kind": "Content", "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/menuHelpers.ts", + "fileUrlPath": "packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuCheckboxItem.tsx", "releaseTag": "Public", - "name": "TLUiMenuChild", + "name": "TLUiMenuCheckboxItemProps", + "typeParameters": [ + { + "typeParameterName": "TranslationKey", + "constraintTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "defaultTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + } + }, + { + "typeParameterName": "IconType", + "constraintTokenRange": { + "startIndex": 5, + "endIndex": 6 + }, + "defaultTypeTokenRange": { + "startIndex": 7, + "endIndex": 8 + } + } + ], + "typeTokenRange": { + "startIndex": 9, + "endIndex": 14 + } + }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiMenuContextProviderProps:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLUiMenuContextProviderProps = " + }, + { + "kind": "Content", + "text": "{\n type: " + }, + { + "kind": "Reference", + "text": "TldrawUiMenuContextType", + "canonicalReference": "@tldraw/tldraw!~TldrawUiMenuContextType:type" + }, + { + "kind": "Content", + "text": ";\n sourceId: " + }, + { + "kind": "Reference", + "text": "TLUiEventSource", + "canonicalReference": "@tldraw/tldraw!TLUiEventSource:type" + }, + { + "kind": "Content", + "text": ";\n children: " + }, + { + "kind": "Reference", + "text": "React.ReactNode", + "canonicalReference": "@types/react!React.ReactNode:type" + }, + { + "kind": "Content", + "text": ";\n}" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuContext.tsx", + "releaseTag": "Public", + "name": "TLUiMenuContextProviderProps", + "typeTokenRange": { + "startIndex": 1, + "endIndex": 8 + } + }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiMenuGroupProps:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLUiMenuGroupProps = " + }, + { + "kind": "Content", + "text": "{\n id: string;\n label?: {\n [key: string]: TranslationKey;\n } | TranslationKey;\n small?: boolean;\n children?: any;\n}" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuGroup.tsx", + "releaseTag": "Public", + "name": "TLUiMenuGroupProps", "typeParameters": [ { "typeParameterName": "TranslationKey", @@ -19262,228 +20885,162 @@ ], "typeTokenRange": { "startIndex": 5, + "endIndex": 6 + } + }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiMenuItemProps:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLUiMenuItemProps = " + }, + { + "kind": "Content", + "text": "{\n id: string;\n icon?: IconType;\n kbd?: string;\n title?: string;\n label?: {\n [key: string]: TranslationKey;\n } | TranslationKey;\n readonlyOk?: boolean;\n onSelect: (source: " + }, + { + "kind": "Reference", + "text": "TLUiEventSource", + "canonicalReference": "@tldraw/tldraw!TLUiEventSource:type" + }, + { + "kind": "Content", + "text": ") => " + }, + { + "kind": "Reference", + "text": "Promise", + "canonicalReference": "!Promise:interface" + }, + { + "kind": "Content", + "text": " | void;\n disabled?: boolean;\n noClose?: boolean;\n spinner?: boolean;\n}" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuItem.tsx", + "releaseTag": "Public", + "name": "TLUiMenuItemProps", + "typeParameters": [ + { + "typeParameterName": "TranslationKey", + "constraintTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "defaultTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + } + }, + { + "typeParameterName": "IconType", + "constraintTokenRange": { + "startIndex": 5, + "endIndex": 6 + }, + "defaultTypeTokenRange": { + "startIndex": 7, + "endIndex": 8 + } + } + ], + "typeTokenRange": { + "startIndex": 9, "endIndex": 14 } }, { "kind": "TypeAlias", - "canonicalReference": "@tldraw/tldraw!TLUiMenuGroup:type", + "canonicalReference": "@tldraw/tldraw!TLUiMenuSubmenuProps:type", "docComment": "/**\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", - "text": "export type TLUiMenuGroup = " + "text": "export type TLUiMenuSubmenuProps = " + }, + { + "kind": "Content", + "text": "{\n id: string;\n label?: {\n [key: string]: Translation;\n } | Translation;\n disabled?: boolean;\n children: any;\n size?: 'large' | 'medium' | 'small' | 'tiny';\n}" }, { "kind": "Content", "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/menuHelpers.ts", + "fileUrlPath": "packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuSubmenu.tsx", "releaseTag": "Public", - "name": "TLUiMenuGroup", - "typeTokenRange": { - "startIndex": 1, - "endIndex": 4 - } - }, - { - "kind": "TypeAlias", - "canonicalReference": "@tldraw/tldraw!TLUiMenuItem:type", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ + "name": "TLUiMenuSubmenuProps", + "typeParameters": [ { - "kind": "Content", - "text": "export type TLUiMenuItem = " - }, - { - "kind": "Content", - "text": "{\n id: string;\n type: 'item';\n readonlyOk: boolean;\n actionItem: " - }, - { - "kind": "Reference", - "text": "TLUiActionItem", - "canonicalReference": "@tldraw/tldraw!TLUiActionItem:interface" - }, - { - "kind": "Content", - "text": ";\n disabled: boolean;\n checked: boolean;\n}" - }, - { - "kind": "Content", - "text": ";" + "typeParameterName": "Translation", + "constraintTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "defaultTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + } } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/menuHelpers.ts", - "releaseTag": "Public", - "name": "TLUiMenuItem", "typeTokenRange": { - "startIndex": 1, - "endIndex": 4 - } - }, - { - "kind": "TypeAlias", - "canonicalReference": "@tldraw/tldraw!TLUiMenuSchema:type", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export type TLUiMenuSchema = " - }, - { - "kind": "Content", - "text": "(" - }, - { - "kind": "Reference", - "text": "TLUiCustomMenuItem", - "canonicalReference": "@tldraw/tldraw!TLUiCustomMenuItem:type" - }, - { - "kind": "Content", - "text": " | " - }, - { - "kind": "Reference", - "text": "TLUiMenuGroup", - "canonicalReference": "@tldraw/tldraw!TLUiMenuGroup:type" - }, - { - "kind": "Content", - "text": " | " - }, - { - "kind": "Reference", - "text": "TLUiMenuItem", - "canonicalReference": "@tldraw/tldraw!TLUiMenuItem:type" - }, - { - "kind": "Content", - "text": ")[]" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/menuHelpers.ts", - "releaseTag": "Public", - "name": "TLUiMenuSchema", - "typeTokenRange": { - "startIndex": 1, - "endIndex": 8 - } - }, - { - "kind": "TypeAlias", - "canonicalReference": "@tldraw/tldraw!TLUiMenuSchemaContextType:type", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export type TLUiMenuSchemaContextType = " - }, - { - "kind": "Reference", - "text": "TLUiMenuSchema", - "canonicalReference": "@tldraw/tldraw!TLUiMenuSchema:type" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useMenuSchema.tsx", - "releaseTag": "Public", - "name": "TLUiMenuSchemaContextType", - "typeTokenRange": { - "startIndex": 1, - "endIndex": 2 - } - }, - { - "kind": "TypeAlias", - "canonicalReference": "@tldraw/tldraw!TLUiMenuSchemaProviderProps:type", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export type TLUiMenuSchemaProviderProps = " - }, - { - "kind": "Content", - "text": "{\n overrides?: (editor: " - }, - { - "kind": "Reference", - "text": "Editor", - "canonicalReference": "@tldraw/editor!Editor:class" - }, - { - "kind": "Content", - "text": ", schema: " - }, - { - "kind": "Reference", - "text": "TLUiMenuSchemaContextType", - "canonicalReference": "@tldraw/tldraw!TLUiMenuSchemaContextType:type" - }, - { - "kind": "Content", - "text": ", helpers: {\n actions: " - }, - { - "kind": "Reference", - "text": "ReturnType", - "canonicalReference": "!ReturnType:type" - }, - { - "kind": "Content", - "text": ";\n noneSelected: boolean;\n oneSelected: boolean;\n twoSelected: boolean;\n threeSelected: boolean;\n }) => " - }, - { - "kind": "Reference", - "text": "TLUiMenuSchemaContextType", - "canonicalReference": "@tldraw/tldraw!TLUiMenuSchemaContextType:type" - }, - { - "kind": "Content", - "text": ";\n children: any;\n}" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useMenuSchema.tsx", - "releaseTag": "Public", - "name": "TLUiMenuSchemaProviderProps", - "typeTokenRange": { - "startIndex": 1, - "endIndex": 12 + "startIndex": 5, + "endIndex": 6 } }, { @@ -19502,34 +21059,7 @@ }, { "kind": "Content", - "text": "<{\n actionsMenu: " - }, - { - "kind": "Reference", - "text": "WithDefaultHelpers", - "canonicalReference": "@tldraw/tldraw!~WithDefaultHelpers:type" - }, - { - "kind": "Content", - "text": "<" - }, - { - "kind": "Reference", - "text": "NonNullable", - "canonicalReference": "!NonNullable:type" - }, - { - "kind": "Content", - "text": "<" - }, - { - "kind": "Reference", - "text": "ActionsMenuSchemaProviderProps", - "canonicalReference": "@tldraw/tldraw!~ActionsMenuSchemaProviderProps:type" - }, - { - "kind": "Content", - "text": "['overrides']>>;\n actions: " + "text": "<{\n actions: " }, { "kind": "Reference", @@ -19554,87 +21084,6 @@ "text": "ActionsProviderProps", "canonicalReference": "@tldraw/tldraw!~ActionsProviderProps:type" }, - { - "kind": "Content", - "text": "['overrides']>>;\n contextMenu: " - }, - { - "kind": "Reference", - "text": "WithDefaultHelpers", - "canonicalReference": "@tldraw/tldraw!~WithDefaultHelpers:type" - }, - { - "kind": "Content", - "text": "<" - }, - { - "kind": "Reference", - "text": "NonNullable", - "canonicalReference": "!NonNullable:type" - }, - { - "kind": "Content", - "text": "<" - }, - { - "kind": "Reference", - "text": "TLUiContextMenuSchemaProviderProps", - "canonicalReference": "@tldraw/tldraw!~TLUiContextMenuSchemaProviderProps:type" - }, - { - "kind": "Content", - "text": "['overrides']>>;\n helpMenu: " - }, - { - "kind": "Reference", - "text": "WithDefaultHelpers", - "canonicalReference": "@tldraw/tldraw!~WithDefaultHelpers:type" - }, - { - "kind": "Content", - "text": "<" - }, - { - "kind": "Reference", - "text": "NonNullable", - "canonicalReference": "!NonNullable:type" - }, - { - "kind": "Content", - "text": "<" - }, - { - "kind": "Reference", - "text": "TLUiHelpMenuSchemaProviderProps", - "canonicalReference": "@tldraw/tldraw!~TLUiHelpMenuSchemaProviderProps:type" - }, - { - "kind": "Content", - "text": "['overrides']>>;\n menu: " - }, - { - "kind": "Reference", - "text": "WithDefaultHelpers", - "canonicalReference": "@tldraw/tldraw!~WithDefaultHelpers:type" - }, - { - "kind": "Content", - "text": "<" - }, - { - "kind": "Reference", - "text": "NonNullable", - "canonicalReference": "!NonNullable:type" - }, - { - "kind": "Content", - "text": "<" - }, - { - "kind": "Reference", - "text": "TLUiMenuSchemaProviderProps", - "canonicalReference": "@tldraw/tldraw!TLUiMenuSchemaProviderProps:type" - }, { "kind": "Content", "text": "['overrides']>>;\n toolbar: " @@ -19662,33 +21111,6 @@ "text": "TLUiToolbarSchemaProviderProps", "canonicalReference": "@tldraw/tldraw!~TLUiToolbarSchemaProviderProps:type" }, - { - "kind": "Content", - "text": "['overrides']>>;\n keyboardShortcutsMenu: " - }, - { - "kind": "Reference", - "text": "WithDefaultHelpers", - "canonicalReference": "@tldraw/tldraw!~WithDefaultHelpers:type" - }, - { - "kind": "Content", - "text": "<" - }, - { - "kind": "Reference", - "text": "NonNullable", - "canonicalReference": "!NonNullable:type" - }, - { - "kind": "Content", - "text": "<" - }, - { - "kind": "Reference", - "text": "TLUiKeyboardShortcutsSchemaProviderProps", - "canonicalReference": "@tldraw/tldraw!TLUiKeyboardShortcutsSchemaProviderProps:type" - }, { "kind": "Content", "text": "['overrides']>>;\n tools: " @@ -19739,73 +21161,326 @@ "name": "TLUiOverrides", "typeTokenRange": { "startIndex": 1, - "endIndex": 53 + "endIndex": 23 } }, { "kind": "TypeAlias", - "canonicalReference": "@tldraw/tldraw!TLUiSubMenu:type", + "canonicalReference": "@tldraw/tldraw!TLUiPopoverContentProps:type", "docComment": "/**\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", - "text": "export type TLUiSubMenu = " - }, - { - "kind": "Content", - "text": "{\n id: string;\n type: 'submenu';\n label: TranslationKey;\n disabled: boolean;\n readonlyOk: boolean;\n children: " + "text": "{\n children: " }, { "kind": "Reference", - "text": "TLUiMenuChild", - "canonicalReference": "@tldraw/tldraw!TLUiMenuChild:type" + "text": "React.ReactNode", + "canonicalReference": "@types/react!React.ReactNode:type" }, { "kind": "Content", - "text": "[];\n}" + "text": ";\n side: 'bottom' | 'left' | 'right' | 'top';\n align?: 'center' | 'end' | 'start';\n alignOffset?: number;\n sideOffset?: number;\n}" }, { "kind": "Content", "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/menuHelpers.ts", + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/Popover.tsx", "releaseTag": "Public", - "name": "TLUiSubMenu", - "typeParameters": [ + "name": "TLUiPopoverContentProps", + "typeTokenRange": { + "startIndex": 1, + "endIndex": 4 + } + }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiPopoverProps:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ { - "typeParameterName": "TranslationKey", - "constraintTokenRange": { + "kind": "Content", + "text": "export type TLUiPopoverProps = " + }, + { + "kind": "Content", + "text": "{\n id: string;\n open?: boolean;\n children: " + }, + { + "kind": "Reference", + "text": "React.ReactNode", + "canonicalReference": "@types/react!React.ReactNode:type" + }, + { + "kind": "Content", + "text": ";\n onOpenChange?: (isOpen: boolean) => void;\n}" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/Popover.tsx", + "releaseTag": "Public", + "name": "TLUiPopoverProps", + "typeTokenRange": { + "startIndex": 1, + "endIndex": 4 + } + }, + { + "kind": "Interface", + "canonicalReference": "@tldraw/tldraw!TLUiPopoverTriggerProps:interface", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export interface TLUiPopoverTriggerProps extends " + }, + { + "kind": "Reference", + "text": "TLUiButtonProps", + "canonicalReference": "@tldraw/tldraw!TLUiButtonProps:interface" + }, + { + "kind": "Content", + "text": " " + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/primitives/Popover.tsx", + "releaseTag": "Public", + "name": "TLUiPopoverTriggerProps", + "preserveMemberOrder": false, + "members": [ + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiPopoverTriggerProps#children:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "children?: " + }, + { + "kind": "Reference", + "text": "React.ReactNode", + "canonicalReference": "@types/react!React.ReactNode:type" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": true, + "releaseTag": "Public", + "name": "children", + "propertyTypeTokenRange": { "startIndex": 1, "endIndex": 2 - }, - "defaultTypeTokenRange": { - "startIndex": 3, - "endIndex": 4 } } ], + "extendsTokenRanges": [ + { + "startIndex": 1, + "endIndex": 2 + } + ] + }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiQuickActionsProps:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLUiQuickActionsProps = " + }, + { + "kind": "Content", + "text": "{\n children?: any;\n}" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/.tsbuild-api/lib/ui/components/QuickActions/DefaultQuickActions.d.ts", + "releaseTag": "Public", + "name": "TLUiQuickActionsProps", "typeTokenRange": { - "startIndex": 5, - "endIndex": 8 + "startIndex": 1, + "endIndex": 2 } }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiStylePanelContentProps:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLUiStylePanelContentProps = " + }, + { + "kind": "Content", + "text": "{\n relevantStyles: " + }, + { + "kind": "Reference", + "text": "ReturnType", + "canonicalReference": "!ReturnType:type" + }, + { + "kind": "Content", + "text": ";\n}" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/StylePanel/DefaultStylePanelContent.tsx", + "releaseTag": "Public", + "name": "TLUiStylePanelContentProps", + "typeTokenRange": { + "startIndex": 1, + "endIndex": 6 + } + }, + { + "kind": "Interface", + "canonicalReference": "@tldraw/tldraw!TLUiStylePanelProps:interface", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export interface TLUiStylePanelProps " + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/components/StylePanel/DefaultStylePanel.tsx", + "releaseTag": "Public", + "name": "TLUiStylePanelProps", + "preserveMemberOrder": false, + "members": [ + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiStylePanelProps#children:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "children?: " + }, + { + "kind": "Content", + "text": "any" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": true, + "releaseTag": "Public", + "name": "children", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiStylePanelProps#isMobile:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "isMobile?: " + }, + { + "kind": "Content", + "text": "boolean" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": true, + "releaseTag": "Public", + "name": "isMobile", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "PropertySignature", + "canonicalReference": "@tldraw/tldraw!TLUiStylePanelProps#relevantStyles:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "relevantStyles: " + }, + { + "kind": "Content", + "text": "{\n styles: " + }, + { + "kind": "Reference", + "text": "ReadonlySharedStyleMap", + "canonicalReference": "@tldraw/editor!ReadonlySharedStyleMap:class" + }, + { + "kind": "Content", + "text": ";\n opacity: " + }, + { + "kind": "Reference", + "text": "SharedStyle", + "canonicalReference": "@tldraw/editor!SharedStyle:type" + }, + { + "kind": "Content", + "text": ";\n } | null" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": false, + "releaseTag": "Public", + "name": "relevantStyles", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 6 + } + } + ], + "extendsTokenRanges": [] + }, { "kind": "Interface", "canonicalReference": "@tldraw/tldraw!TLUiToast:interface", @@ -19816,7 +21491,7 @@ "text": "export interface TLUiToast " } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useToastsProvider.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/toasts.tsx", "releaseTag": "Public", "name": "TLUiToast", "preserveMemberOrder": false, @@ -20029,7 +21704,7 @@ "text": "export interface TLUiToastAction " } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useToastsProvider.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/toasts.tsx", "releaseTag": "Public", "name": "TLUiToastAction", "preserveMemberOrder": false, @@ -20172,7 +21847,7 @@ "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useToastsProvider.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/toasts.tsx", "releaseTag": "Public", "name": "TLUiToastsContextType", "typeTokenRange": { @@ -20191,7 +21866,7 @@ }, { "kind": "Content", - "text": "{\n id: string;\n type: 'item';\n readonlyOk: boolean;\n toolItem: " + "text": "{\n id: string;\n type: 'item';\n readonlyOk?: boolean;\n toolItem: " }, { "kind": "Reference", @@ -20495,7 +22170,7 @@ "excerptTokens": [ { "kind": "Content", - "text": "readonlyOk: " + "text": "readonlyOk?: " }, { "kind": "Content", @@ -20507,7 +22182,7 @@ } ], "isReadonly": false, - "isOptional": false, + "isOptional": true, "releaseTag": "Public", "name": "readonlyOk", "propertyTypeTokenRange": { @@ -20735,6 +22410,32 @@ "endIndex": 2 } }, + { + "kind": "TypeAlias", + "canonicalReference": "@tldraw/tldraw!TLUiZoomMenuProps:type", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export type TLUiZoomMenuProps = " + }, + { + "kind": "Content", + "text": "{\n children?: any;\n}" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/.tsbuild-api/lib/ui/components/ZoomMenu/DefaultZoomMenu.d.ts", + "releaseTag": "Public", + "name": "TLUiZoomMenuProps", + "typeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, { "kind": "Function", "canonicalReference": "@tldraw/tldraw!toolbarItem:function(1)", @@ -20837,7 +22538,7 @@ "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useEventsProvider.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/events.tsx", "returnTypeTokenRange": { "startIndex": 3, "endIndex": 5 @@ -20856,6 +22557,72 @@ ], "name": "UiEventsProvider" }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!unwrapLabel:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function unwrapLabel(label?: " + }, + { + "kind": "Reference", + "text": "TLUiActionItem", + "canonicalReference": "@tldraw/tldraw!TLUiActionItem:interface" + }, + { + "kind": "Content", + "text": "['label']" + }, + { + "kind": "Content", + "text": ", menuType?: " + }, + { + "kind": "Content", + "text": "string" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "string | undefined" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/context/actions.tsx", + "returnTypeTokenRange": { + "startIndex": 6, + "endIndex": 7 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "label", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 3 + }, + "isOptional": true + }, + { + "parameterName": "menuType", + "parameterTypeTokenRange": { + "startIndex": 4, + "endIndex": 5 + }, + "isOptional": true + } + ], + "name": "unwrapLabel" + }, { "kind": "Function", "canonicalReference": "@tldraw/tldraw!useActions:function(1)", @@ -20875,7 +22642,7 @@ "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useActions.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/actions.tsx", "returnTypeTokenRange": { "startIndex": 1, "endIndex": 2 @@ -20885,35 +22652,6 @@ "parameters": [], "name": "useActions" }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!useActionsMenuSchema:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function useActionsMenuSchema(): " - }, - { - "kind": "Reference", - "text": "TLUiMenuSchema", - "canonicalReference": "@tldraw/tldraw!TLUiMenuSchema:type" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useActionsMenuSchema.tsx", - "returnTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [], - "name": "useActionsMenuSchema" - }, { "kind": "Function", "canonicalReference": "@tldraw/tldraw!useBreakpoint:function(1)", @@ -20932,7 +22670,7 @@ "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useBreakpoint.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/breakpoints.tsx", "returnTypeTokenRange": { "startIndex": 1, "endIndex": 2 @@ -20942,91 +22680,6 @@ "parameters": [], "name": "useBreakpoint" }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!useCanRedo:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function useCanRedo(): " - }, - { - "kind": "Content", - "text": "boolean" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useCanRedo.ts", - "returnTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [], - "name": "useCanRedo" - }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!useCanUndo:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function useCanUndo(): " - }, - { - "kind": "Content", - "text": "boolean" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useCanUndo.ts", - "returnTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [], - "name": "useCanUndo" - }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!useContextMenuSchema:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function useContextMenuSchema(): " - }, - { - "kind": "Reference", - "text": "TLUiMenuSchema", - "canonicalReference": "@tldraw/tldraw!TLUiMenuSchema:type" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useContextMenuSchema.tsx", - "returnTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [], - "name": "useContextMenuSchema" - }, { "kind": "Function", "canonicalReference": "@tldraw/tldraw!useCopyAs:function(1)", @@ -21093,7 +22746,7 @@ }, { "kind": "Content", - "text": ") => string;\n msg: (id: string) => string;\n isMobile: boolean;\n}" + "text": ">) => string;\n msg: (id?: string | undefined) => string;\n isMobile: boolean;\n}" }, { "kind": "Content", @@ -21174,7 +22827,7 @@ "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useDialogsProvider.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/dialogs.tsx", "returnTypeTokenRange": { "startIndex": 1, "endIndex": 2 @@ -21230,35 +22883,6 @@ "parameters": [], "name": "useExportAs" }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!useHelpMenuSchema:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function useHelpMenuSchema(): " - }, - { - "kind": "Reference", - "text": "TLUiMenuSchema", - "canonicalReference": "@tldraw/tldraw!TLUiMenuSchema:type" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useHelpMenuSchema.tsx", - "returnTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [], - "name": "useHelpMenuSchema" - }, { "kind": "Function", "canonicalReference": "@tldraw/tldraw!useKeyboardShortcuts:function(1)", @@ -21287,35 +22911,6 @@ "parameters": [], "name": "useKeyboardShortcuts" }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!useKeyboardShortcutsSchema:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function useKeyboardShortcutsSchema(): " - }, - { - "kind": "Reference", - "text": "TLUiKeyboardShortcutsSchemaContextType", - "canonicalReference": "@tldraw/tldraw!TLUiKeyboardShortcutsSchemaContextType:type" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useKeyboardShortcutsSchema.tsx", - "returnTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [], - "name": "useKeyboardShortcutsSchema" - }, { "kind": "Function", "canonicalReference": "@tldraw/tldraw!useLocalStorageState:function(1)", @@ -21550,35 +23145,6 @@ ], "name": "useMenuIsOpen" }, - { - "kind": "Function", - "canonicalReference": "@tldraw/tldraw!useMenuSchema:function(1)", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export declare function useMenuSchema(): " - }, - { - "kind": "Reference", - "text": "TLUiMenuSchema", - "canonicalReference": "@tldraw/tldraw!TLUiMenuSchema:type" - }, - { - "kind": "Content", - "text": ";" - } - ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useMenuSchema.tsx", - "returnTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [], - "name": "useMenuSchema" - }, { "kind": "Function", "canonicalReference": "@tldraw/tldraw!useNativeClipboardEvents:function(1)", @@ -21635,6 +23201,246 @@ "parameters": [], "name": "useReadonly" }, + { + "kind": "Function", + "canonicalReference": "@tldraw/tldraw!useTldrawUiComponents:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function useTldrawUiComponents(): " + }, + { + "kind": "Reference", + "text": "Partial", + "canonicalReference": "!Partial:type" + }, + { + "kind": "Content", + "text": "<{\n ContextMenu: " + }, + { + "kind": "Reference", + "text": "ComponentType", + "canonicalReference": "@types/react!React.ComponentType:type" + }, + { + "kind": "Content", + "text": "<" + }, + { + "kind": "Reference", + "text": "TLUiContextMenuProps", + "canonicalReference": "@tldraw/tldraw!TLUiContextMenuProps:interface" + }, + { + "kind": "Content", + "text": "> | null;\n ActionsMenu: " + }, + { + "kind": "Reference", + "text": "ComponentType", + "canonicalReference": "@types/react!React.ComponentType:type" + }, + { + "kind": "Content", + "text": "<" + }, + { + "kind": "Reference", + "text": "TLUiActionsMenuProps", + "canonicalReference": "@tldraw/tldraw!TLUiActionsMenuProps:type" + }, + { + "kind": "Content", + "text": "> | null;\n HelpMenu: " + }, + { + "kind": "Reference", + "text": "ComponentType", + "canonicalReference": "@types/react!React.ComponentType:type" + }, + { + "kind": "Content", + "text": "<" + }, + { + "kind": "Reference", + "text": "TLUiHelpMenuProps", + "canonicalReference": "@tldraw/tldraw!TLUiHelpMenuProps:type" + }, + { + "kind": "Content", + "text": "> | null;\n ZoomMenu: " + }, + { + "kind": "Reference", + "text": "ComponentType", + "canonicalReference": "@types/react!React.ComponentType:type" + }, + { + "kind": "Content", + "text": "<" + }, + { + "kind": "Reference", + "text": "TLUiZoomMenuProps", + "canonicalReference": "@tldraw/tldraw!TLUiZoomMenuProps:type" + }, + { + "kind": "Content", + "text": "> | null;\n MainMenu: " + }, + { + "kind": "Reference", + "text": "ComponentType", + "canonicalReference": "@types/react!React.ComponentType:type" + }, + { + "kind": "Content", + "text": "<" + }, + { + "kind": "Reference", + "text": "TLUiMainMenuProps", + "canonicalReference": "@tldraw/tldraw!TLUiMainMenuProps:type" + }, + { + "kind": "Content", + "text": "> | null;\n Minimap: " + }, + { + "kind": "Reference", + "text": "ComponentType", + "canonicalReference": "@types/react!React.ComponentType:type" + }, + { + "kind": "Content", + "text": " | null;\n StylePanel: " + }, + { + "kind": "Reference", + "text": "ComponentType", + "canonicalReference": "@types/react!React.ComponentType:type" + }, + { + "kind": "Content", + "text": "<" + }, + { + "kind": "Reference", + "text": "TLUiStylePanelProps", + "canonicalReference": "@tldraw/tldraw!TLUiStylePanelProps:interface" + }, + { + "kind": "Content", + "text": "> | null;\n PageMenu: " + }, + { + "kind": "Reference", + "text": "ComponentType", + "canonicalReference": "@types/react!React.ComponentType:type" + }, + { + "kind": "Content", + "text": " | null;\n NavigationPanel: " + }, + { + "kind": "Reference", + "text": "ComponentType", + "canonicalReference": "@types/react!React.ComponentType:type" + }, + { + "kind": "Content", + "text": " | null;\n Toolbar: " + }, + { + "kind": "Reference", + "text": "ComponentType", + "canonicalReference": "@types/react!React.ComponentType:type" + }, + { + "kind": "Content", + "text": " | null;\n KeyboardShortcutsDialog: " + }, + { + "kind": "Reference", + "text": "ComponentType", + "canonicalReference": "@types/react!React.ComponentType:type" + }, + { + "kind": "Content", + "text": "<" + }, + { + "kind": "Reference", + "text": "TLUiKeyboardShortcutsDialogProps", + "canonicalReference": "@tldraw/tldraw!TLUiKeyboardShortcutsDialogProps:type" + }, + { + "kind": "Content", + "text": "> | null;\n QuickActions: " + }, + { + "kind": "Reference", + "text": "ComponentType", + "canonicalReference": "@types/react!React.ComponentType:type" + }, + { + "kind": "Content", + "text": "<" + }, + { + "kind": "Reference", + "text": "TLUiQuickActionsProps", + "canonicalReference": "@tldraw/tldraw!TLUiQuickActionsProps:type" + }, + { + "kind": "Content", + "text": "> | null;\n HelperButtons: " + }, + { + "kind": "Reference", + "text": "ComponentType", + "canonicalReference": "@types/react!React.ComponentType:type" + }, + { + "kind": "Content", + "text": "<" + }, + { + "kind": "Reference", + "text": "TLUiHelperButtonsProps", + "canonicalReference": "@tldraw/tldraw!TLUiHelperButtonsProps:type" + }, + { + "kind": "Content", + "text": "> | null;\n DebugMenu: " + }, + { + "kind": "Reference", + "text": "ComponentType", + "canonicalReference": "@types/react!React.ComponentType:type" + }, + { + "kind": "Content", + "text": " | null;\n}>" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "packages/tldraw/src/lib/ui/context/components.tsx", + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 49 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "useTldrawUiComponents" + }, { "kind": "Function", "canonicalReference": "@tldraw/tldraw!useToasts:function(1)", @@ -21654,7 +23460,7 @@ "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useToastsProvider.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/toasts.tsx", "returnTypeTokenRange": { "startIndex": 1, "endIndex": 2 @@ -21733,7 +23539,7 @@ }, { "kind": "Content", - "text": "(id: " + "text": "(id?: " }, { "kind": "Reference", @@ -21787,7 +23593,7 @@ "text": ";" } ], - "fileUrlPath": "packages/tldraw/src/lib/ui/hooks/useEventsProvider.tsx", + "fileUrlPath": "packages/tldraw/src/lib/ui/context/events.tsx", "returnTypeTokenRange": { "startIndex": 1, "endIndex": 2 diff --git a/packages/tldraw/src/index.ts b/packages/tldraw/src/index.ts index 0b3c94c64..480d28eb0 100644 --- a/packages/tldraw/src/index.ts +++ b/packages/tldraw/src/index.ts @@ -39,56 +39,29 @@ export { SelectTool } from './lib/tools/SelectTool/SelectTool' export { ZoomTool } from './lib/tools/ZoomTool/ZoomTool' // UI export { TldrawUi, type TldrawUiBaseProps, type TldrawUiProps } from './lib/ui/TldrawUi' -export { - TldrawUiContextProvider, - type TldrawUiContextProviderProps, -} from './lib/ui/TldrawUiContextProvider' export { setDefaultUiAssetUrls, type TLUiAssetUrlOverrides } from './lib/ui/assetUrls' -export { ContextMenu, type TLUiContextMenuProps } from './lib/ui/components/ContextMenu' export { OfflineIndicator } from './lib/ui/components/OfflineIndicator/OfflineIndicator' export { Spinner } from './lib/ui/components/Spinner' export { Button, type TLUiButtonProps } from './lib/ui/components/primitives/Button' export { Icon, type TLUiIconProps } from './lib/ui/components/primitives/Icon' export { Input, type TLUiInputProps } from './lib/ui/components/primitives/Input' export { - compactMenuItems, - findMenuItem, - menuCustom, - menuGroup, - menuItem, - menuSubmenu, - type TLUiCustomMenuItem, - type TLUiMenuChild, - type TLUiMenuGroup, - type TLUiMenuItem, - type TLUiMenuSchema, - type TLUiSubMenu, -} from './lib/ui/hooks/menuHelpers' + TldrawUiContextProvider, + type TldrawUiContextProviderProps, +} from './lib/ui/context/TldrawUiContextProvider' export { useActions, type TLUiActionItem, type TLUiActionsContextType, -} from './lib/ui/hooks/useActions' -export { - useActionsMenuSchema, - type TLUiActionsMenuSchemaContextType, -} from './lib/ui/hooks/useActionsMenuSchema' -export { AssetUrlsProvider, useAssetUrls } from './lib/ui/hooks/useAssetUrls' -export { BreakPointProvider, useBreakpoint } from './lib/ui/hooks/useBreakpoint' -export { useCanRedo } from './lib/ui/hooks/useCanRedo' -export { useCanUndo } from './lib/ui/hooks/useCanUndo' -export { useMenuClipboardEvents, useNativeClipboardEvents } from './lib/ui/hooks/useClipboardEvents' -export { - useContextMenuSchema, - type TLUiContextTTLUiMenuSchemaContextType, -} from './lib/ui/hooks/useContextMenuSchema' -export { useCopyAs } from './lib/ui/hooks/useCopyAs' +} from './lib/ui/context/actions' +export { AssetUrlsProvider, useAssetUrls } from './lib/ui/context/asset-urls' +export { BreakPointProvider, useBreakpoint } from './lib/ui/context/breakpoints' export { useDialogs, type TLUiDialog, type TLUiDialogProps, type TLUiDialogsContextType, -} from './lib/ui/hooks/useDialogsProvider' +} from './lib/ui/context/dialogs' export { UiEventsProvider, useUiEvents, @@ -97,32 +70,20 @@ export { type TLUiEventHandler, type TLUiEventMap, type TLUiEventSource, -} from './lib/ui/hooks/useEventsProvider' -export { useExportAs } from './lib/ui/hooks/useExportAs' -export { - useHelpMenuSchema, - type TLUiHelpMenuSchemaContextType, -} from './lib/ui/hooks/useHelpMenuSchema' -export { useKeyboardShortcuts } from './lib/ui/hooks/useKeyboardShortcuts' -export { - useKeyboardShortcutsSchema, - type TLUiKeyboardShortcutsSchemaContextType, - type TLUiKeyboardShortcutsSchemaProviderProps, -} from './lib/ui/hooks/useKeyboardShortcutsSchema' -export { useLocalStorageState } from './lib/ui/hooks/useLocalStorageState' -export { useMenuIsOpen } from './lib/ui/hooks/useMenuIsOpen' -export { - useMenuSchema, - type TLUiMenuSchemaContextType, - type TLUiMenuSchemaProviderProps, -} from './lib/ui/hooks/useMenuSchema' -export { useReadonly } from './lib/ui/hooks/useReadonly' +} from './lib/ui/context/events' export { useToasts, type TLUiToast, type TLUiToastAction, type TLUiToastsContextType, -} from './lib/ui/hooks/useToastsProvider' +} from './lib/ui/context/toasts' +export { useMenuClipboardEvents, useNativeClipboardEvents } from './lib/ui/hooks/useClipboardEvents' +export { useCopyAs } from './lib/ui/hooks/useCopyAs' +export { useExportAs } from './lib/ui/hooks/useExportAs' +export { useKeyboardShortcuts } from './lib/ui/hooks/useKeyboardShortcuts' +export { useLocalStorageState } from './lib/ui/hooks/useLocalStorageState' +export { useMenuIsOpen } from './lib/ui/hooks/useMenuIsOpen' +export { useReadonly } from './lib/ui/hooks/useReadonly' export { toolbarItem, useToolbarSchema, @@ -170,9 +131,150 @@ export { type TldrawFile, } from './lib/utils/tldr/file' -import * as Dialog from './lib/ui/components/primitives/Dialog' -import * as DropdownMenu from './lib/ui/components/primitives/DropdownMenu' +// Minimap default component +export { DefaultMinimap } from './lib/ui/components/Minimap/DefaultMinimap' -// N.B. Preserve order of import / export here with this comment. -// Sometimes this can cause an import problem depending on build setup downstream. -export { Dialog, DropdownMenu } +// Helper to unwrap label from action items +export { unwrapLabel } from './lib/ui/context/actions' + +// General UI components for building menus +export { + TldrawUiMenuCheckboxItem, + type TLUiMenuCheckboxItemProps, +} from './lib/ui/components/menus/TldrawUiMenuCheckboxItem' +export { + TldrawUiMenuContextProvider, + type TLUiMenuContextProviderProps, +} from './lib/ui/components/menus/TldrawUiMenuContext' +export { + TldrawUiMenuGroup, + type TLUiMenuGroupProps, +} from './lib/ui/components/menus/TldrawUiMenuGroup' +export { + TldrawUiMenuItem, + type TLUiMenuItemProps, +} from './lib/ui/components/menus/TldrawUiMenuItem' +export { + TldrawUiMenuSubmenu, + type TLUiMenuSubmenuProps, +} from './lib/ui/components/menus/TldrawUiMenuSubmenu' + +export { + TldrawUiComponentsProvider, + useTldrawUiComponents, + type TLUiComponents, +} from './lib/ui/context/components' + +// Menus / UI elements that can be customized +export { DefaultPageMenu } from './lib/ui/components/PageMenu/DefaultPageMenu' + +export { + DefaultStylePanel, + type TLUiStylePanelProps, +} from './lib/ui/components/StylePanel/DefaultStylePanel' +export { + DefaultStylePanelContent, + type TLUiStylePanelContentProps, +} from './lib/ui/components/StylePanel/DefaultStylePanelContent' + +export { + DefaultActionsMenu, + type TLUiActionsMenuProps, +} from './lib/ui/components/ActionsMenu/DefaultActionsMenu' +export { DefaultActionsMenuContent } from './lib/ui/components/ActionsMenu/DefaultActionsMenuContent' + +export { + DefaultContextMenu as ContextMenu, + DefaultContextMenu, + type TLUiContextMenuProps, +} from './lib/ui/components/ContextMenu/DefaultContextMenu' +export { DefaultContextMenuContent } from './lib/ui/components/ContextMenu/DefaultContextMenuContent' + +export { + DefaultHelpMenu, + type TLUiHelpMenuProps, +} from './lib/ui/components/HelpMenu/DefaultHelpMenu' +export { DefaultHelpMenuContent } from './lib/ui/components/HelpMenu/DefaultHelpMenuContent' + +export { + DefaultMainMenu, + type TLUiMainMenuProps, +} from './lib/ui/components/MainMenu/DefaultMainMenu' +export { DefaultMainMenuContent } from './lib/ui/components/MainMenu/DefaultMainMenuContent' + +export { + DefaultQuickActions, + type TLUiQuickActionsProps, +} from './lib/ui/components/QuickActions/DefaultQuickActions' +export { DefaultQuickActionsContent } from './lib/ui/components/QuickActions/DefaultQuickActionsContent' + +export { + DefaultZoomMenu, + type TLUiZoomMenuProps, +} from './lib/ui/components/ZoomMenu/DefaultZoomMenu' +export { DefaultZoomMenuContent } from './lib/ui/components/ZoomMenu/DefaultZoomMenuContent' + +export { + DefaultHelperButtons, + type TLUiHelperButtonsProps, +} from './lib/ui/components/HelperButtons/DefaultHelperButtons' +export { DefaultHelperButtonsContent } from './lib/ui/components/HelperButtons/DefaultHelperButtonsContent' + +export { + DefaultKeyboardShortcutsDialog, + type TLUiKeyboardShortcutsDialogProps, +} from './lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialog' +export { DefaultKeyboardShortcutsDialogContent } from './lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent' + +export { + DefaultDebugMenu, + type TLUiDebugMenuProps, +} from './lib/ui/components/DebugMenu/DefaultDebugMenu' +export { DefaultDebugMenuContent } from './lib/ui/components/DebugMenu/DefaultDebugMenuContent' + +export { DefaultToolbar } from './lib/ui/components/Toolbar/DefaultToolbar' + +export { type TLComponents } from './lib/Tldraw' + +export { + DialogBody, + DialogCloseButton, + DialogFooter, + DialogHeader, + DialogTitle, + type TLUiDialogBodyProps, + type TLUiDialogFooterProps, + type TLUiDialogHeaderProps, + type TLUiDialogTitleProps, +} from './lib/ui/components/primitives/Dialog' + +export { + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuIndicator, + DropdownMenuItem, + DropdownMenuRadioItem, + DropdownMenuRoot, + DropdownMenuSub, + DropdownMenuSubTrigger, + DropdownMenuTrigger, + type TLUiDropdownMenuCheckboxItemProps, + type TLUiDropdownMenuContentProps, + type TLUiDropdownMenuGroupProps, + type TLUiDropdownMenuItemProps, + type TLUiDropdownMenuRadioItemProps, + type TLUiDropdownMenuRootProps, + type TLUiDropdownMenuSubProps, + type TLUiDropdownMenuSubTriggerProps, + type TLUiDropdownMenuTriggerProps, +} from './lib/ui/components/primitives/DropdownMenu' + +export { + Popover, + PopoverContent, + PopoverTrigger, + type TLUiPopoverContentProps, + type TLUiPopoverProps, + type TLUiPopoverTriggerProps, +} from './lib/ui/components/primitives/Popover' diff --git a/packages/tldraw/src/lib/Tldraw.tsx b/packages/tldraw/src/lib/Tldraw.tsx index 5a88ae5ff..090fc3c37 100644 --- a/packages/tldraw/src/lib/Tldraw.tsx +++ b/packages/tldraw/src/lib/Tldraw.tsx @@ -4,13 +4,13 @@ import { ErrorScreen, LoadingScreen, StoreSnapshot, + TLEditorComponents, TLOnMountHandler, TLRecord, TLStore, TLStoreWithStatus, TldrawEditor, TldrawEditorBaseProps, - TldrawEditorProps, assert, useEditor, useShallowArrayIdentity, @@ -31,29 +31,37 @@ import { defaultShapeUtils } from './defaultShapeUtils' import { registerDefaultSideEffects } from './defaultSideEffects' import { defaultTools } from './defaultTools' import { TldrawUi, TldrawUiProps } from './ui/TldrawUi' -import { ContextMenu } from './ui/components/ContextMenu' +import { TLUiComponents, useTldrawUiComponents } from './ui/context/components' import { usePreloadAssets } from './ui/hooks/usePreloadAssets' import { useDefaultEditorAssetsWithOverrides } from './utils/static-assets/assetUrls' +/**@public */ +export type TLComponents = TLEditorComponents & TLUiComponents + /** @public */ -export type TldrawProps = TldrawEditorBaseProps & - ( - | { - store: TLStore | TLStoreWithStatus - } - | { - store?: undefined - persistenceKey?: string - sessionId?: string - defaultName?: string - /** - * A snapshot to load for the store's initial data / schema. - */ - snapshot?: StoreSnapshot - } - ) & - TldrawUiProps & - Partial +export type TldrawProps = + // combine components from base editor and ui + (Omit & + Omit & { + components?: TLComponents + }) & + // external content + Partial & + // store stuff + (| { + store: TLStore | TLStoreWithStatus + } + | { + store?: undefined + persistenceKey?: string + sessionId?: string + defaultName?: string + /** + * A snapshot to load for the store's initial data / schema. + */ + snapshot?: StoreSnapshot + } + ) /** @public */ export function Tldraw(props: TldrawProps) { @@ -64,31 +72,37 @@ export function Tldraw(props: TldrawProps) { acceptedImageMimeTypes, acceptedVideoMimeTypes, onMount, + components = {}, + shapeUtils = [], + tools = [], ...rest } = props - const components = useShallowObjectIdentity(rest.components ?? {}) - const shapeUtils = useShallowArrayIdentity(rest.shapeUtils ?? []) - const tools = useShallowArrayIdentity(rest.tools ?? []) + const _components = useShallowObjectIdentity(components) + const componentsWithDefault = useMemo( + () => ({ + Scribble: TldrawScribble, + CollaboratorScribble: TldrawScribble, + SelectionForeground: TldrawSelectionForeground, + SelectionBackground: TldrawSelectionBackground, + Handles: TldrawHandles, + HoveredShapeIndicator: TldrawHoveredShapeIndicator, + ..._components, + }), + [_components] + ) - const withDefaults: TldrawEditorProps = { - initialState: 'select', - ...rest, - components: useMemo( - () => ({ - Scribble: TldrawScribble, - CollaboratorScribble: TldrawScribble, - SelectionForeground: TldrawSelectionForeground, - SelectionBackground: TldrawSelectionBackground, - Handles: TldrawHandles, - HoveredShapeIndicator: TldrawHoveredShapeIndicator, - ...components, - }), - [components] - ), - shapeUtils: useMemo(() => [...defaultShapeUtils, ...shapeUtils], [shapeUtils]), - tools: useMemo(() => [...defaultTools, ...defaultShapeTools, ...tools], [tools]), - } + const _shapeUtils = useShallowArrayIdentity(shapeUtils) + const shapeUtilsWithDefaults = useMemo( + () => [...defaultShapeUtils, ..._shapeUtils], + [_shapeUtils] + ) + + const _tools = useShallowArrayIdentity(tools) + const toolsWithDefaults = useMemo( + () => [...defaultTools, ...defaultShapeTools, ..._tools], + [_tools] + ) const assets = useDefaultEditorAssetsWithOverrides(rest.assetUrls) @@ -103,11 +117,14 @@ export function Tldraw(props: TldrawProps) { } return ( - - - - - + + ) { const editor = useEditor() @@ -156,7 +182,10 @@ function InsideOfEditorContext({ if (editor) return onMountEvent?.(editor) }, [editor, onMountEvent]) - return null + const { ContextMenu } = useTldrawUiComponents() + if (!ContextMenu) return + + return } /> } // duped from tldraw editor diff --git a/packages/tldraw/src/lib/defaultExternalContentHandlers.ts b/packages/tldraw/src/lib/defaultExternalContentHandlers.ts index ba6b4f427..19acf59cf 100644 --- a/packages/tldraw/src/lib/defaultExternalContentHandlers.ts +++ b/packages/tldraw/src/lib/defaultExternalContentHandlers.ts @@ -30,9 +30,9 @@ export type TLExternalContentProps = { // The maximum size (in bytes) of an asset. Assets larger than this will be rejected. Defaults to 10mb (10 * 1024 * 1024). maxAssetSize: number // The mime types of images that are allowed to be handled. Defaults to ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml']. - acceptedImageMimeTypes: string[] + acceptedImageMimeTypes: readonly string[] // The mime types of videos that are allowed to be handled. Defaults to ['video/mp4', 'video/webm', 'video/quicktime']. - acceptedVideoMimeTypes: string[] + acceptedVideoMimeTypes: readonly string[] } export function registerDefaultExternalContentHandlers( diff --git a/packages/tldraw/src/lib/ui.css b/packages/tldraw/src/lib/ui.css index 586008a11..ed9f1219e 100644 --- a/packages/tldraw/src/lib/ui.css +++ b/packages/tldraw/src/lib/ui.css @@ -38,6 +38,7 @@ .tlui-button:disabled { color: var(--color-text-3); text-shadow: none; + cursor: default; } .tlui-button:disabled .tlui-kbd { @@ -310,6 +311,9 @@ .tlui-buttons__horizontal > *:nth-last-child(1) { margin-right: 0px; } +.tlui-buttons__horizontal > *:only-child { + width: 56px; +} /* Button Grid */ @@ -504,7 +508,6 @@ grid-auto-flow: column; grid-template-columns: auto; grid-auto-columns: minmax(1em, auto); - gap: 1px; align-self: bottom; color: var(--color-text-1); margin-left: var(--space-4); @@ -860,6 +863,10 @@ height: 48px; } +.tlui-toolbar__extras:empty { + display: none; +} + .tlui-toolbar__extras__controls { display: flex; position: relative; @@ -928,6 +935,10 @@ /* ---------------------- Menu ---------------------- */ +.tlui-menu:empty { + display: none; +} + .tlui-menu { z-index: var(--layer-menus); height: fit-content; @@ -954,24 +965,15 @@ stroke-width: 1px; } -.tlui-menu__group[data-size='large'] { - min-width: initial; +.tlui-menu__group:empty { + display: none; } -.tlui-menu__group[data-size='medium'] { - min-width: 144px; +.tlui-menu__group { + border-bottom: 1px solid var(--color-divider); } - -.tlui-menu__group[data-size='small'] { - min-width: 96px; -} - -.tlui-menu__group[data-size='tiny'] { - min-width: 0px; -} - -.tlui-menu__group + .tlui-menu__group { - border-top: 1px solid var(--color-divider); +.tlui-menu__group:nth-last-of-type(1) { + border-bottom: none; } .tlui-menu__submenu__trigger[data-state='open']:not(:hover)::after { @@ -984,6 +986,27 @@ background: linear-gradient(270deg, rgba(144, 144, 144, 0) 0%, var(--color-muted-2) 100%); } +/* Menu Sizes */ + +.tlui-menu[data-size='large'] > .tlui-menu__group, +.tlui-menu__submenu__content[data-size='large'] > .tlui-menu__group { + min-width: initial; +} + +.tlui-menu[data-size='medium'] > .tlui-menu__group, +.tlui-menu__submenu__content[data-size='medium'] > .tlui-menu__group { + min-width: 144px; +} + +.tlui-menu[data-size='small'] > .tlui-menu__group, +.tlui-menu__submenu__content[data-size='small'] > .tlui-menu__group { + min-width: 96px; +} + +.tlui-menu[data-size='tiny'] > .tlui-menu__group, +.tlui-menu__submenu__content[data-size='tiny'] > .tlui-menu__group { + min-width: 0px; +} /* ------------------ Actions Menu ------------------ */ .tlui-actions-menu { @@ -1105,7 +1128,7 @@ /* ------------------- Navigation ------------------- */ -.tlui-navigation-zone { +.tlui-navigation-panel { display: flex; width: min-content; flex-direction: column; @@ -1116,7 +1139,7 @@ bottom: 0px; } -.tlui-navigation-zone::before { +.tlui-navigation-panel::before { content: ''; display: block; position: absolute; @@ -1129,16 +1152,16 @@ background-color: var(--color-low); } -.tlui-navigation-zone__toggle .tlui-icon { +.tlui-navigation-panel__toggle .tlui-icon { opacity: 0.24; } -.tlui-navigation-zone__toggle:active .tlui-icon { +.tlui-navigation-panel__toggle:active .tlui-icon { opacity: 1; } @media (hover: hover) { - .tlui-navigation-zone__toggle:hover .tlui-icon { + .tlui-navigation-panel__toggle:hover .tlui-icon { opacity: 1; } } diff --git a/packages/tldraw/src/lib/ui/TldrawUi.tsx b/packages/tldraw/src/lib/ui/TldrawUi.tsx index a0c6d6a9f..06d820d29 100644 --- a/packages/tldraw/src/lib/ui/TldrawUi.tsx +++ b/packages/tldraw/src/lib/ui/TldrawUi.tsx @@ -2,26 +2,24 @@ import { ToastProvider } from '@radix-ui/react-toast' import { useEditor, useValue } from '@tldraw/editor' import classNames from 'classnames' import React, { ReactNode } from 'react' -import { TldrawUiContextProvider, TldrawUiContextProviderProps } from './TldrawUiContextProvider' import { TLUiAssetUrlOverrides } from './assetUrls' -import { BackToContent } from './components/BackToContent' import { DebugPanel } from './components/DebugPanel' import { Dialogs } from './components/Dialogs' import { FollowingIndicator } from './components/FollowingIndicator' -import { HelpMenu } from './components/HelpMenu' import { MenuZone } from './components/MenuZone' -import { NavigationZone } from './components/NavigationZone/NavigationZone' -import { ExitPenMode } from './components/PenModeToggle' -import { StopFollowing } from './components/StopFollowing' -import { StylePanel } from './components/StylePanel/StylePanel' import { ToastViewport, Toasts } from './components/Toasts' -import { Toolbar } from './components/Toolbar/Toolbar' import { Button } from './components/primitives/Button' -import { useActions } from './hooks/useActions' -import { useBreakpoint } from './hooks/useBreakpoint' +import { + TldrawUiContextProvider, + TldrawUiContextProviderProps, +} from './context/TldrawUiContextProvider' +import { useActions } from './context/actions' +import { useBreakpoint } from './context/breakpoints' +import { TLUiComponents, useTldrawUiComponents } from './context/components' import { useNativeClipboardEvents } from './hooks/useClipboardEvents' import { useEditorEvents } from './hooks/useEditorEvents' import { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts' +import { useRelevantStyles } from './hooks/useRevelantStyles' import { useTranslation } from './hooks/useTranslation/useTranslation' /** @@ -47,6 +45,11 @@ export interface TldrawUiBaseProps { */ hideUi?: boolean + /** + * Overrides for the UI components. + */ + components?: TLUiComponents + /** * A component to use for the share zone (will be deprecated) */ @@ -76,10 +79,11 @@ export const TldrawUi = React.memo(function TldrawUi({ renderDebugMenuItems, children, hideUi, + components, ...rest }: TldrawUiProps) { return ( - + editor.getInstanceState().isFocusMode, [editor]) const isDebugMode = useValue('debug', () => editor.getInstanceState().isDebugMode, [editor]) + const { StylePanel, Toolbar, HelpMenu, NavigationPanel, HelperButtons } = useTldrawUiComponents() + useKeyboardShortcuts() useNativeClipboardEvents() useEditorEvents() @@ -159,29 +161,21 @@ const TldrawUiContent = React.memo(function TldrawUI({
-
- - - -
+ {HelperButtons && }
{topZone}
{shareZone} - {breakpoint >= 5 && !isReadonlyMode && ( -
- -
- )} + {StylePanel && breakpoint >= 5 && !isReadonlyMode && <_StylePanel />}
- - - {breakpoint >= 4 && } + {NavigationPanel && } + {Toolbar && } + {HelpMenu && }
- {isDebugMode && } + {isDebugMode && }
)} @@ -193,3 +187,11 @@ const TldrawUiContent = React.memo(function TldrawUI({ ) }) + +function _StylePanel() { + const { StylePanel } = useTldrawUiComponents() + const relevantStyles = useRelevantStyles() + + if (!StylePanel) return null + return +} diff --git a/packages/tldraw/src/lib/ui/components/ActionsMenu.tsx b/packages/tldraw/src/lib/ui/components/ActionsMenu.tsx deleted file mode 100644 index fa59741f9..000000000 --- a/packages/tldraw/src/lib/ui/components/ActionsMenu.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import * as PopoverPrimitive from '@radix-ui/react-popover' -import { useContainer } from '@tldraw/editor' -import { memo } from 'react' -import { TLUiMenuChild } from '../hooks/menuHelpers' -import { useActionsMenuSchema } from '../hooks/useActionsMenuSchema' -import { useBreakpoint } from '../hooks/useBreakpoint' -import { useReadonly } from '../hooks/useReadonly' -import { useTranslation } from '../hooks/useTranslation/useTranslation' -import { Button } from './primitives/Button' -import { Popover, PopoverTrigger } from './primitives/Popover' -import { kbdStr } from './primitives/shared' - -export const ActionsMenu = memo(function ActionsMenu() { - const msg = useTranslation() - const container = useContainer() - const menuSchema = useActionsMenuSchema() - const isReadonly = useReadonly() - const breakpoint = useBreakpoint() - - function getActionMenuItem(item: TLUiMenuChild) { - if (!item) return null - if (isReadonly && !item.readonlyOk) return null - - switch (item.type) { - case 'item': { - const { id, icon, label, kbd, onSelect } = item.actionItem - - return ( - + )} + + + + + ) +} + +const DebugFlagToggle = track(function DebugFlagToggle({ + flag, + onChange, +}: { + flag: DebugFlag + onChange?: (newValue: boolean) => void +}) { + const value = flag.get() + return ( + `${m[0]} ${m[1].toLowerCase()}`) + .replace(/^[a-z]/, (m) => m.toUpperCase())} + checked={value} + onSelect={() => { + flag.set(!value) + onChange?.(!value) + }} + /> + ) +}) + +let t = 0 + +function createNShapes(editor: Editor, n: number) { + const shapesToCreate: TLShapePartial[] = Array(n) + const cols = Math.floor(Math.sqrt(n)) + + for (let i = 0; i < n; i++) { + t++ + shapesToCreate[i] = { + id: createShapeId('box' + t), + type: 'geo', + x: (i % cols) * 132, + y: Math.floor(i / cols) * 132, + } + } + + editor.batch(() => { + editor.createShapes(shapesToCreate).setSelectedShapes(shapesToCreate.map((s) => s.id)) + }) +} diff --git a/packages/tldraw/src/lib/ui/components/DebugPanel.tsx b/packages/tldraw/src/lib/ui/components/DebugPanel.tsx index a3a841be7..49349532e 100644 --- a/packages/tldraw/src/lib/ui/components/DebugPanel.tsx +++ b/packages/tldraw/src/lib/ui/components/DebugPanel.tsx @@ -1,76 +1,24 @@ -import { - createShapeId, - DebugFlag, - debugFlags, - Editor, - featureFlags, - hardResetEditor, - TLShapePartial, - track, - uniqueId, - useEditor, - useValue, - Vec, -} from '@tldraw/editor' -import * as React from 'react' -import { useDialogs } from '../hooks/useDialogsProvider' -import { useToasts } from '../hooks/useToastsProvider' -import { untranslated, useTranslation } from '../hooks/useTranslation/useTranslation' -import { Button } from './primitives/Button' -import * as Dialog from './primitives/Dialog' -import * as DropdownMenu from './primitives/DropdownMenu' - -let t = 0 - -function createNShapes(editor: Editor, n: number) { - const shapesToCreate: TLShapePartial[] = Array(n) - const cols = Math.floor(Math.sqrt(n)) - - for (let i = 0; i < n; i++) { - t++ - shapesToCreate[i] = { - id: createShapeId('box' + t), - type: 'geo', - x: (i % cols) * 132, - y: Math.floor(i / cols) * 132, - } - } - - editor.batch(() => { - editor.createShapes(shapesToCreate).setSelectedShapes(shapesToCreate.map((s) => s.id)) - }) -} +import { debugFlags, track, useEditor, useValue, Vec } from '@tldraw/editor' +import { memo, useEffect, useRef, useState } from 'react' +import { useTldrawUiComponents } from '../context/components' /** @internal */ -export const DebugPanel = React.memo(function DebugPanel({ - renderDebugMenuItems, -}: { - renderDebugMenuItems: (() => React.ReactNode) | null -}) { - const msg = useTranslation() - const showFps = useValue('show_fps', () => debugFlags.showFps.get(), [debugFlags]) +export const DebugPanel = memo(function DebugPanel() { + const { DebugMenu } = useTldrawUiComponents() return (
- {showFps && } - - - -
) }) function useTick(isEnabled = true) { - const [_, setTick] = React.useState(0) + const [_, setTick] = useState(0) const editor = useEditor() - React.useEffect(() => { + useEffect(() => { if (!isEnabled) return const update = () => setTick((tick) => tick + 1) editor.on('tick', update) @@ -107,9 +55,13 @@ const CurrentState = track(function CurrentState() { }) function FPS() { - const fpsRef = React.useRef(null) + const showFps = useValue('show_fps', () => debugFlags.showFps.get(), [debugFlags]) + + const fpsRef = useRef(null) + + useEffect(() => { + if (!showFps) return - React.useEffect(() => { const TICK_LENGTH = 250 let maxKnownFps = 0 let cancelled = false @@ -168,294 +120,9 @@ function FPS() { return () => { cancelled = true } - }, []) + }, [showFps]) + + if (!showFps) return null return
} - -const ShapeCount = function ShapeCount() { - const editor = useEditor() - const count = useValue('rendering shapes count', () => editor.getRenderingShapes().length, [ - editor, - ]) - - return
{count} Shapes
-} - -const DebugMenuContent = track(function DebugMenuContent({ - renderDebugMenuItems, -}: { - renderDebugMenuItems: (() => React.ReactNode) | null -}) { - const editor = useEditor() - const { addToast } = useToasts() - const { addDialog } = useDialogs() - const [error, setError] = React.useState(false) - - return ( - <> - - { - addToast({ - id: uniqueId(), - title: 'Something happened', - description: 'Hey, attend to this thing over here. It might be important!', - keepOpen: true, - // icon?: string - // title?: string - // description?: string - // actions?: TLUiToastAction[] - }) - addToast({ - id: uniqueId(), - title: 'Something happened', - description: 'Hey, attend to this thing over here. It might be important!', - keepOpen: true, - actions: [ - { - label: 'Primary', - type: 'primary', - onClick: () => { - void null - }, - }, - { - label: 'Normal', - type: 'normal', - onClick: () => { - void null - }, - }, - { - label: 'Danger', - type: 'danger', - onClick: () => { - void null - }, - }, - ], - // icon?: string - // title?: string - // description?: string - // actions?: TLUiToastAction[] - }) - addToast({ - id: uniqueId(), - title: 'Something happened', - description: 'Hey, attend to this thing over here. It might be important!', - keepOpen: true, - icon: 'twitter', - actions: [ - { - label: 'Primary', - type: 'primary', - onClick: () => { - void null - }, - }, - { - label: 'Normal', - type: 'normal', - onClick: () => { - void null - }, - }, - { - label: 'Danger', - type: 'danger', - onClick: () => { - void null - }, - }, - ], - }) - }} - label={untranslated('Show toast')} - /> - - { - addDialog({ - component: ({ onClose }) => ( - { - onClose() - }} - onContinue={() => { - onClose() - }} - /> - ), - onClose: () => { - void null - }, - }) - }} - label={untranslated('Show dialog')} - /> - createNShapes(editor, 100)} - label={untranslated('Create 100 shapes')} - /> - { - function countDescendants({ children }: HTMLElement) { - let count = 0 - if (!children.length) return 0 - for (const el of [...(children as any)]) { - count++ - count += countDescendants(el) - } - return count - } - - const selectedShapes = editor.getSelectedShapes() - - const shapes = - selectedShapes.length === 0 ? editor.getRenderingShapes() : selectedShapes - - const elms = shapes.map( - (shape) => (document.getElementById(shape.id) as HTMLElement)!.parentElement! - ) - - let descendants = elms.length - - for (const elm of elms) { - descendants += countDescendants(elm) - } - - window.alert(`Shapes ${shapes.length}, DOM nodes:${descendants}`) - }} - label={untranslated('Count shapes / nodes')} - /> - {(() => { - if (error) throw Error('oh no!') - })()} - { - setError(true) - }} - label={untranslated('Throw error')} - /> - { - hardResetEditor() - }} - label={untranslated('Hard reset')} - /> - - - - - - - - - - {Object.values(featureFlags).map((flag) => { - return - })} - - {renderDebugMenuItems?.()} - - ) -}) - -function Toggle({ - label, - value, - onChange, -}: { - label: string - value: boolean - onChange: (newValue: boolean) => void -}) { - return ( - onChange(!value)} - > - - {label} - - - ) -} - -const DebugFlagToggle = track(function DebugFlagToggle({ - flag, - onChange, -}: { - flag: DebugFlag - onChange?: (newValue: boolean) => void -}) { - return ( - `${m[0]} ${m[1].toLowerCase()}`) - .replace(/^[a-z]/, (m) => m.toUpperCase())} - value={flag.get()} - onChange={(newValue) => { - flag.set(newValue) - onChange?.(newValue) - }} - /> - ) -}) - -function ExampleDialog({ - title = 'title', - body = 'hello hello hello', - cancel = 'Cancel', - confirm = 'Continue', - displayDontShowAgain = false, - onCancel, - onContinue, -}: { - title?: string - body?: string - cancel?: string - confirm?: string - displayDontShowAgain?: boolean - onCancel: () => void - onContinue: () => void -}) { - const [dontShowAgain, setDontShowAgain] = React.useState(false) - - return ( - <> - - {title} - - - {body} - - {displayDontShowAgain && ( - - )} - - - - - ) -} diff --git a/packages/tldraw/src/lib/ui/components/Dialogs.tsx b/packages/tldraw/src/lib/ui/components/Dialogs.tsx index bebbef94d..797c0c269 100644 --- a/packages/tldraw/src/lib/ui/components/Dialogs.tsx +++ b/packages/tldraw/src/lib/ui/components/Dialogs.tsx @@ -1,7 +1,7 @@ import * as _Dialog from '@radix-ui/react-dialog' import { useContainer } from '@tldraw/editor' import React, { useCallback } from 'react' -import { TLUiDialog, useDialogs } from '../hooks/useDialogsProvider' +import { TLUiDialog, useDialogs } from '../context/dialogs' const Dialog = ({ id, component: ModalContent, onClose }: TLUiDialog) => { const { removeDialog } = useDialogs() diff --git a/packages/tldraw/src/lib/ui/components/DuplicateButton.tsx b/packages/tldraw/src/lib/ui/components/DuplicateButton.tsx deleted file mode 100644 index 81cadef13..000000000 --- a/packages/tldraw/src/lib/ui/components/DuplicateButton.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { track, useEditor } from '@tldraw/editor' -import { useRef } from 'react' -import { useActions } from '../hooks/useActions' -import { useTranslation } from '../hooks/useTranslation/useTranslation' -import { Button } from './primitives/Button' -import { kbdStr } from './primitives/shared' - -export const DuplicateButton = track(function DuplicateButton() { - const editor = useEditor() - const actions = useActions() - const msg = useTranslation() - const action = actions['duplicate'] - const ref = useRef(null) - - return ( - {isRemoving ? ( ) : ( )} - + ) }) diff --git a/packages/tldraw/src/lib/ui/components/EmbedDialog.tsx b/packages/tldraw/src/lib/ui/components/EmbedDialog.tsx index 7debdcfb6..5376225d0 100644 --- a/packages/tldraw/src/lib/ui/components/EmbedDialog.tsx +++ b/packages/tldraw/src/lib/ui/components/EmbedDialog.tsx @@ -1,11 +1,12 @@ +import { DialogTitle } from '@radix-ui/react-dialog' import { EMBED_DEFINITIONS, EmbedDefinition, track, useEditor } from '@tldraw/editor' import { useRef, useState } from 'react' import { TLEmbedResult, getEmbedInfo } from '../../utils/embeds/embeds' -import { useAssetUrls } from '../hooks/useAssetUrls' -import { TLUiDialogProps } from '../hooks/useDialogsProvider' +import { useAssetUrls } from '../context/asset-urls' +import { TLUiDialogProps } from '../context/dialogs' import { untranslated, useTranslation } from '../hooks/useTranslation/useTranslation' import { Button } from './primitives/Button' -import * as Dialog from './primitives/Dialog' +import { DialogBody, DialogCloseButton, DialogFooter, DialogHeader } from './primitives/Dialog' import { Icon } from './primitives/Icon' import { Input } from './primitives/Input' @@ -29,20 +30,20 @@ export const EmbedDialog = track(function EmbedDialog({ onClose }: TLUiDialogPro return ( <> - - + + {embedDefinition - ? `${msg('embed-dialog.title')} — ${embedDefinition.title}` - : msg('embed-dialog.title')} - - - + ? `${msg('embed-title')} — ${embedDefinition.title}` + : msg('embed-title')} + + + {embedDefinition ? ( <> - + { @@ -67,7 +68,7 @@ export const EmbedDialog = track(function EmbedDialog({ onClose }: TLUiDialogPro /> {url === '' ? (
- {msg('embed-dialog.instruction')}{' '} + {msg('embed-instruction')}{' '} {embedDefinition.instructionLink && ( ) : (
- {showError ? msg('embed-dialog.invalid-url') : '\xa0'} + {showError ? msg('embed-invalid-url') : '\xa0'}
)} - - + +
- ) -}) - -function HelpMenuContent() { - const menuSchema = useHelpMenuSchema() - - const isReadonly = useReadonly() - - function getHelpMenuItem(item: TLUiMenuChild) { - if (!item) return null - if (isReadonly && !item.readonlyOk) return null - - switch (item.type) { - case 'custom': { - if (item.id === 'LANGUAGE_MENU') { - return - } - break - } - case 'group': { - return ( - - {item.children.map(getHelpMenuItem)} - - ) - } - case 'submenu': { - return ( - - - {item.children.map(getHelpMenuItem)} - - ) - } - case 'item': { - const { id, kbd, label, onSelect, icon } = item.actionItem - return ( - onSelect('help-menu')} - iconLeft={icon} - /> - ) - } - } - } - - return <>{menuSchema.map(getHelpMenuItem)} -} diff --git a/packages/tldraw/src/lib/ui/components/HelpMenu/DefaultHelpMenu.tsx b/packages/tldraw/src/lib/ui/components/HelpMenu/DefaultHelpMenu.tsx new file mode 100644 index 000000000..3c3c62cb5 --- /dev/null +++ b/packages/tldraw/src/lib/ui/components/HelpMenu/DefaultHelpMenu.tsx @@ -0,0 +1,46 @@ +import { memo } from 'react' +import { useBreakpoint } from '../../context/breakpoints' +import { useTranslation } from '../../hooks/useTranslation/useTranslation' +import { TldrawUiMenuContextProvider } from '../menus/TldrawUiMenuContext' +import { + DropdownMenuContent, + DropdownMenuRoot, + DropdownMenuTrigger, +} from '../primitives/DropdownMenu' +import { DefaultHelpMenuContent } from './DefaultHelpMenuContent' + +/** @public */ +export type TLUiHelpMenuProps = { + children?: any +} + +/** @public */ +export const DefaultHelpMenu = memo(function DefaultHelpMenu({ children }: TLUiHelpMenuProps) { + const msg = useTranslation() + const breakpoint = useBreakpoint() + + // Get the help menu content, either the default component or the user's + // override. If there's no menu content, then the user has set it to null, + // so skip rendering the menu. + const content = children ?? + + if (breakpoint < 4) return null + + return ( +
+ + + + + {content} + + + +
+ ) +}) diff --git a/packages/tldraw/src/lib/ui/components/HelpMenu/DefaultHelpMenuContent.tsx b/packages/tldraw/src/lib/ui/components/HelpMenu/DefaultHelpMenuContent.tsx new file mode 100644 index 000000000..00e1d6247 --- /dev/null +++ b/packages/tldraw/src/lib/ui/components/HelpMenu/DefaultHelpMenuContent.tsx @@ -0,0 +1,32 @@ +import { useTldrawUiComponents } from '../../context/components' +import { useDialogs } from '../../context/dialogs' +import { LanguageMenu } from '../LanguageMenu' +import { TldrawUiMenuItem } from '../menus/TldrawUiMenuItem' + +/** @public */ +export function DefaultHelpMenuContent() { + return ( + <> + + + + ) +} + +function KeyboardShortcutsMenuItem() { + const { KeyboardShortcutsDialog } = useTldrawUiComponents() + const { addDialog } = useDialogs() + + if (!KeyboardShortcutsDialog) return null + + return ( + { + addDialog({ component: KeyboardShortcutsDialog }) + }} + /> + ) +} diff --git a/packages/tldraw/src/lib/ui/components/BackToContent.tsx b/packages/tldraw/src/lib/ui/components/HelperButtons/BackToContent.tsx similarity index 82% rename from packages/tldraw/src/lib/ui/components/BackToContent.tsx rename to packages/tldraw/src/lib/ui/components/HelperButtons/BackToContent.tsx index b0bcfc5af..5d63bec95 100644 --- a/packages/tldraw/src/lib/ui/components/BackToContent.tsx +++ b/packages/tldraw/src/lib/ui/components/HelperButtons/BackToContent.tsx @@ -1,13 +1,12 @@ import { useEditor } from '@tldraw/editor' import { useEffect, useState } from 'react' -import { useActions } from '../hooks/useActions' -import { Button } from './primitives/Button' +import { useActions } from '../../context/actions' +import { TldrawUiMenuItem } from '../menus/TldrawUiMenuItem' export function BackToContent() { const editor = useEditor() const actions = useActions() - const action = actions['back-to-content'] const [showBackToContent, setShowBackToContent] = useState(false) @@ -42,12 +41,10 @@ export function BackToContent() { if (!showBackToContent) return null return ( - + + - + <_StylePanel /> ) } + +function _StylePanel() { + const { StylePanel } = useTldrawUiComponents() + const relevantStyles = useRelevantStyles() + + if (!StylePanel) return null + return +} diff --git a/packages/tldraw/src/lib/ui/components/MoveToPageMenu.tsx b/packages/tldraw/src/lib/ui/components/MoveToPageMenu.tsx deleted file mode 100644 index 7379b5d04..000000000 --- a/packages/tldraw/src/lib/ui/components/MoveToPageMenu.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import * as _ContextMenu from '@radix-ui/react-context-menu' -import { PageRecordType, TLPageId, track, useContainer, useEditor } from '@tldraw/editor' -import { useToasts } from '../hooks/useToastsProvider' -import { useTranslation } from '../hooks/useTranslation/useTranslation' -import { Button } from './primitives/Button' - -export const MoveToPageMenu = track(function MoveToPageMenu() { - const editor = useEditor() - const container = useContainer() - const pages = editor.getPages() - const currentPageId = editor.getCurrentPageId() - const msg = useTranslation() - const { addToast } = useToasts() - - return ( - <_ContextMenu.Sub> - <_ContextMenu.SubTrigger dir="ltr" asChild> - - - ))} - - <_ContextMenu.Group - dir="ltr" - className={'tlui-menu__group'} - data-testid={`menu-item.new-page`} - key="new-page" - > - <_ContextMenu.Item - key="new-page" - onSelect={() => { - const newPageId = PageRecordType.createId() - const ids = editor.getSelectedShapeIds() - editor.batch(() => { - editor.mark('move_shapes_to_page') - editor.createPage({ name: msg('page-menu.new-page-initial-name'), id: newPageId }) - editor.moveShapesToPage(ids, newPageId) - }) - }} - asChild - > - - - - - - - ) -}) diff --git a/packages/tldraw/src/lib/ui/components/NavigationPanel/DefaultNavigationPanel.tsx b/packages/tldraw/src/lib/ui/components/NavigationPanel/DefaultNavigationPanel.tsx new file mode 100644 index 000000000..a32f8d692 --- /dev/null +++ b/packages/tldraw/src/lib/ui/components/NavigationPanel/DefaultNavigationPanel.tsx @@ -0,0 +1,80 @@ +import { memo, useCallback } from 'react' +import { unwrapLabel, useActions } from '../../context/actions' +import { useBreakpoint } from '../../context/breakpoints' +import { useTldrawUiComponents } from '../../context/components' +import { useLocalStorageState } from '../../hooks/useLocalStorageState' +import { useTranslation } from '../../hooks/useTranslation/useTranslation' +import { Button } from '../primitives/Button' +import { kbdStr } from '../primitives/shared' + +/** @public */ +export const DefaultNavigationPanel = memo(function DefaultNavigationPanel() { + const actions = useActions() + const msg = useTranslation() + const breakpoint = useBreakpoint() + + const [collapsed, setCollapsed] = useLocalStorageState('minimap', true) + + const toggleMinimap = useCallback(() => { + setCollapsed((s) => !s) + }, [setCollapsed]) + + const { ZoomMenu, Minimap } = useTldrawUiComponents() + + if (breakpoint < 4) { + return null + } + + return ( +
+
+ {ZoomMenu && breakpoint < 6 ? ( + + ) : collapsed ? ( + <> + {ZoomMenu && } + {Minimap && ( +
+ {Minimap && breakpoint >= 6 && !collapsed && } +
+ ) +}) diff --git a/packages/tldraw/src/lib/ui/components/NavigationZone/NavigationZone.tsx b/packages/tldraw/src/lib/ui/components/NavigationZone/NavigationZone.tsx deleted file mode 100644 index fb77f43c2..000000000 --- a/packages/tldraw/src/lib/ui/components/NavigationZone/NavigationZone.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { memo, useCallback } from 'react' -import { useActions } from '../../hooks/useActions' -import { useBreakpoint } from '../../hooks/useBreakpoint' -import { useLocalStorageState } from '../../hooks/useLocalStorageState' -import { useTranslation } from '../../hooks/useTranslation/useTranslation' -import { Button } from '../primitives/Button' -import { kbdStr } from '../primitives/shared' -import { Minimap } from './Minimap' -import { ZoomMenu } from './ZoomMenu' - -/** @internal */ -export const NavigationZone = memo(function NavigationZone() { - const actions = useActions() - const msg = useTranslation() - const breakpoint = useBreakpoint() - - const [collapsed, setCollapsed] = useLocalStorageState('minimap', true) - - const toggleMinimap = useCallback(() => { - setCollapsed((s) => !s) - }, [setCollapsed]) - - if (breakpoint < 4) { - return null - } - - return ( -
-
- {breakpoint < 6 ? ( - - ) : collapsed ? ( - <> - -
- {breakpoint >= 6 && !collapsed && ( - - )} -
- ) -}) diff --git a/packages/tldraw/src/lib/ui/components/NavigationZone/ZoomMenu.tsx b/packages/tldraw/src/lib/ui/components/NavigationZone/ZoomMenu.tsx deleted file mode 100644 index 51579b8bd..000000000 --- a/packages/tldraw/src/lib/ui/components/NavigationZone/ZoomMenu.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { ANIMATION_MEDIUM_MS, track, useEditor } from '@tldraw/editor' -import * as React from 'react' -import { useActions } from '../../hooks/useActions' -import { useBreakpoint } from '../../hooks/useBreakpoint' -import { useTranslation } from '../../hooks/useTranslation/useTranslation' -import { Button } from '../primitives/Button' -import * as M from '../primitives/DropdownMenu' - -export const ZoomMenu = track(function ZoomMenu() { - const editor = useEditor() - const msg = useTranslation() - const breakpoint = useBreakpoint() - - const zoom = editor.getZoomLevel() - const hasShapes = editor.getCurrentPageShapeIds().size > 0 - const hasSelected = editor.getSelectedShapeIds().length > 0 - const isZoomedTo100 = editor.getZoomLevel() === 1 - - const handleDoubleClick = React.useCallback(() => { - editor.resetZoom(editor.getViewportScreenCenter(), { duration: ANIMATION_MEDIUM_MS }) - }, [editor]) - - return ( - - - - - - - - - - - - - - - ) -}) - -function ZoomMenuItem(props: { - action: string - disabled?: boolean - noClose?: boolean - 'data-testid'?: string -}) { - const { action, disabled = false, noClose = false } = props - const actions = useActions() - - return ( - actions[action].onSelect('zoom-menu')} - noClose={noClose} - disabled={disabled} - /> - ) -} diff --git a/packages/tldraw/src/lib/ui/components/PageMenu/PageMenu.tsx b/packages/tldraw/src/lib/ui/components/PageMenu/DefaultPageMenu.tsx similarity index 96% rename from packages/tldraw/src/lib/ui/components/PageMenu/PageMenu.tsx rename to packages/tldraw/src/lib/ui/components/PageMenu/DefaultPageMenu.tsx index 7c2e99827..c1b911f79 100644 --- a/packages/tldraw/src/lib/ui/components/PageMenu/PageMenu.tsx +++ b/packages/tldraw/src/lib/ui/components/PageMenu/DefaultPageMenu.tsx @@ -7,8 +7,8 @@ import { useEditor, useValue, } from '@tldraw/editor' -import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react' -import { useBreakpoint } from '../../hooks/useBreakpoint' +import { memo, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react' +import { useBreakpoint } from '../../context/breakpoints' import { useMenuIsOpen } from '../../hooks/useMenuIsOpen' import { useReadonly } from '../../hooks/useReadonly' import { useTranslation } from '../../hooks/useTranslation/useTranslation' @@ -19,7 +19,8 @@ import { PageItemInput } from './PageItemInput' import { PageItemSubmenu } from './PageItemSubmenu' import { onMovePage } from './edit-pages-shared' -export const PageMenu = function PageMenu() { +/** @public */ +export const DefaultPageMenu = memo(function DefaultPageMenu() { const editor = useEditor() const msg = useTranslation() const breakpoint = useBreakpoint() @@ -254,16 +255,14 @@ export const PageMenu = function PageMenu() { return ( - - + +
{currentPage.name}
@@ -412,4 +411,4 @@ export const PageMenu = function PageMenu() { ) -} +}) diff --git a/packages/tldraw/src/lib/ui/components/PageMenu/PageItemSubmenu.tsx b/packages/tldraw/src/lib/ui/components/PageMenu/PageItemSubmenu.tsx index 4d761bcc1..eb49acd06 100644 --- a/packages/tldraw/src/lib/ui/components/PageMenu/PageItemSubmenu.tsx +++ b/packages/tldraw/src/lib/ui/components/PageMenu/PageItemSubmenu.tsx @@ -1,9 +1,14 @@ -import * as DropdownMenu from '@radix-ui/react-dropdown-menu' import { MAX_PAGES, PageRecordType, TLPageId, track, useEditor } from '@tldraw/editor' import { useCallback } from 'react' import { useTranslation } from '../../hooks/useTranslation/useTranslation' -import { Button } from '../primitives/Button' -import * as M from '../primitives/DropdownMenu' +import { TldrawUiMenuContextProvider } from '../menus/TldrawUiMenuContext' +import { TldrawUiMenuGroup } from '../menus/TldrawUiMenuGroup' +import { TldrawUiMenuItem } from '../menus/TldrawUiMenuItem' +import { + DropdownMenuContent, + DropdownMenuRoot, + DropdownMenuTrigger, +} from '../primitives/DropdownMenu' import { onMovePage } from './edit-pages-shared' export interface PageItemSubmenuProps { @@ -43,44 +48,46 @@ export const PageItemSubmenu = track(function PageItemSubmenu({ }, [editor, item]) return ( - - -
+ ) } diff --git a/packages/tldraw/src/lib/ui/components/StylePanel/DoubleDropdownPicker.tsx b/packages/tldraw/src/lib/ui/components/StylePanel/DoubleDropdownPicker.tsx index f924f6d57..a5043dec9 100644 --- a/packages/tldraw/src/lib/ui/components/StylePanel/DoubleDropdownPicker.tsx +++ b/packages/tldraw/src/lib/ui/components/StylePanel/DoubleDropdownPicker.tsx @@ -1,11 +1,14 @@ -import { Trigger } from '@radix-ui/react-dropdown-menu' -import { SharedStyle, StyleProp, preventDefault } from '@tldraw/editor' +import { SharedStyle, StyleProp } from '@tldraw/editor' import * as React from 'react' import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey' import { useTranslation } from '../../hooks/useTranslation/useTranslation' import { TLUiIconType } from '../../icon-types' -import { Button } from '../primitives/Button' -import * as DropdownMenu from '../primitives/DropdownMenu' +import { + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuRoot, + DropdownMenuTrigger, +} from '../primitives/DropdownMenu' import { StyleValuesForUi } from './styles' interface DoubleDropdownPickerProps { @@ -60,32 +63,26 @@ export const DoubleDropdownPicker = React.memo(function DoubleDropdownPicker
- - preventDefault(e)} - > -
) diff --git a/packages/tldraw/src/lib/ui/components/StylePanel/DropdownPicker.tsx b/packages/tldraw/src/lib/ui/components/StylePanel/DropdownPicker.tsx index 67386a6fd..c8e6d7517 100644 --- a/packages/tldraw/src/lib/ui/components/StylePanel/DropdownPicker.tsx +++ b/packages/tldraw/src/lib/ui/components/StylePanel/DropdownPicker.tsx @@ -1,11 +1,15 @@ -import { Trigger } from '@radix-ui/react-dropdown-menu' -import { SharedStyle, StyleProp, preventDefault } from '@tldraw/editor' +import { SharedStyle, StyleProp } from '@tldraw/editor' import * as React from 'react' import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey' import { useTranslation } from '../../hooks/useTranslation/useTranslation' import { TLUiIconType } from '../../icon-types' -import { Button, TLUiButtonProps } from '../primitives/Button' -import * as DropdownMenu from '../primitives/DropdownMenu' +import { TLUiButtonProps } from '../primitives/Button' +import { + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuRoot, + DropdownMenuTrigger, +} from '../primitives/DropdownMenu' import { StyleValuesForUi } from './styles' interface DropdownPickerProps { @@ -37,29 +41,23 @@ export const DropdownPicker = React.memo(function DropdownPicker - preventDefault(e)} - > -
- {breakpoint < 5 && !isReadonly && ( + {breakpoint < 5 && !isReadonlyMode && (
@@ -181,7 +179,7 @@ const OverflowToolsContent = track(function OverflowToolsContent({
{toolbarItems.map(({ toolItem: { id, meta, kbd, label, onSelect, icon } }) => { return ( - { - const visibleItems = allToolbarItems.filter((item) => !isReadonly || item.readonlyOk) + const visibleItems = allToolbarItems.filter((item) => !isReadonlyMode || item.readonlyOk) const overflowIndex = Math.min(8, 5 + breakpoint) const itemsInPanel = visibleItems.slice(0, overflowIndex) @@ -256,5 +254,5 @@ export function useToolbarItems() { } return { itemsInPanel, itemsInDropdown } - }, [allToolbarItems, breakpoint, isReadonly]) + }, [allToolbarItems, breakpoint, isReadonlyMode]) } diff --git a/packages/tldraw/src/lib/ui/components/Toolbar/ToggleToolLockedButton.tsx b/packages/tldraw/src/lib/ui/components/Toolbar/ToggleToolLockedButton.tsx index 3781360f7..dfdc6e88b 100644 --- a/packages/tldraw/src/lib/ui/components/Toolbar/ToggleToolLockedButton.tsx +++ b/packages/tldraw/src/lib/ui/components/Toolbar/ToggleToolLockedButton.tsx @@ -1,6 +1,6 @@ import { useEditor, useValue } from '@tldraw/editor' import classNames from 'classnames' -import { useBreakpoint } from '../../hooks/useBreakpoint' +import { useBreakpoint } from '../../context/breakpoints' import { useTranslation } from '../../hooks/useTranslation/useTranslation' import { Button } from '../primitives/Button' diff --git a/packages/tldraw/src/lib/ui/components/TrashButton.tsx b/packages/tldraw/src/lib/ui/components/TrashButton.tsx deleted file mode 100644 index fcf2ada3d..000000000 --- a/packages/tldraw/src/lib/ui/components/TrashButton.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { track, useEditor } from '@tldraw/editor' -import { useRef } from 'react' -import { useActions } from '../hooks/useActions' -import { useReadonly } from '../hooks/useReadonly' -import { useTranslation } from '../hooks/useTranslation/useTranslation' -import { Button } from './primitives/Button' -import { kbdStr } from './primitives/shared' - -export const TrashButton = track(function TrashButton() { - const editor = useEditor() - const actions = useActions() - const msg = useTranslation() - const action = actions['delete'] - - const isReadonly = useReadonly() - const ref = useRef(null) - - if (isReadonly) return null - - return ( - + ) + } +) diff --git a/packages/tldraw/src/lib/ui/components/ZoomMenu/DefaultZoomMenuContent.tsx b/packages/tldraw/src/lib/ui/components/ZoomMenu/DefaultZoomMenuContent.tsx new file mode 100644 index 000000000..aaefb4949 --- /dev/null +++ b/packages/tldraw/src/lib/ui/components/ZoomMenu/DefaultZoomMenuContent.tsx @@ -0,0 +1,17 @@ +import { useActions } from '../../context/actions' +import { ZoomTo100MenuItem, ZoomToFitMenuItem, ZoomToSelectionMenuItem } from '../menu-items' +import { TldrawUiMenuItem } from '../menus/TldrawUiMenuItem' + +/** @public */ +export function DefaultZoomMenuContent() { + const actions = useActions() + return ( + <> + + + + + + + ) +} diff --git a/packages/tldraw/src/lib/ui/components/menu-items.tsx b/packages/tldraw/src/lib/ui/components/menu-items.tsx new file mode 100644 index 000000000..652fa2ff6 --- /dev/null +++ b/packages/tldraw/src/lib/ui/components/menu-items.tsx @@ -0,0 +1,532 @@ +import { + TLBookmarkShape, + TLEmbedShape, + TLFrameShape, + TLPageId, + useEditor, + useValue, +} from '@tldraw/editor' +import { getEmbedInfo } from '../../utils/embeds/embeds' +import { useActions } from '../context/actions' +import { useUiEvents } from '../context/events' +import { useToasts } from '../context/toasts' +import { + showMenuPaste, + useAllowGroup, + useAllowUngroup, + useAnySelectedShapesCount, + useHasLinkShapeSelected, + useOnlyFlippableShape, + useShowAutoSizeToggle, + useThreeStackableItems, + useUnlockedSelectedShapesCount, +} from '../hooks/menu-hooks' +import { TldrawUiMenuCheckboxItem } from './menus/TldrawUiMenuCheckboxItem' +import { TldrawUiMenuGroup } from './menus/TldrawUiMenuGroup' +import { TldrawUiMenuItem } from './menus/TldrawUiMenuItem' +import { TldrawUiMenuSubmenu } from './menus/TldrawUiMenuSubmenu' + +/* -------------------- Selection ------------------- */ + +export function ToggleAutoSizeMenuItem() { + const actions = useActions() + const shouldDisplay = useShowAutoSizeToggle() + if (!shouldDisplay) return null + return +} + +export function EditLinkMenuItem() { + const actions = useActions() + const shouldDisplay = useHasLinkShapeSelected() + if (!shouldDisplay) return null + return +} + +export function DuplicateMenuItem() { + const actions = useActions() + const shouldDisplay = useUnlockedSelectedShapesCount(1) + if (!shouldDisplay) return null + return +} + +export function GroupMenuItem() { + const actions = useActions() + const shouldDisplay = useAllowGroup() + if (!shouldDisplay) return null + return +} + +export function UngroupMenuItem() { + const actions = useActions() + const shouldDisplay = useAllowUngroup() + if (!shouldDisplay) return null + return +} + +export function RemoveFrameMenuItem() { + const editor = useEditor() + const actions = useActions() + const shouldDisplay = useValue( + 'allow unframe', + () => { + const selectedShapes = editor.getSelectedShapes() + if (selectedShapes.length === 0) return false + return selectedShapes.every((shape) => editor.isShapeOfType(shape, 'frame')) + }, + [editor] + ) + if (!shouldDisplay) return null + return +} + +export function FitFrameToContentMenuItem() { + const editor = useEditor() + const actions = useActions() + const shouldDisplay = useValue( + 'allow fit frame to content', + () => { + const onlySelectedShape = editor.getOnlySelectedShape() + if (!onlySelectedShape) return false + return editor.getSortedChildIdsForParent(onlySelectedShape).length > 0 + }, + [editor] + ) + if (!shouldDisplay) return null + return +} + +export function ToggleLockMenuItem() { + const editor = useEditor() + const actions = useActions() + const shouldDisplay = useValue('selected shapes', () => editor.getSelectedShapes().length > 0, [ + editor, + ]) + if (!shouldDisplay) return null + return +} + +export function ToggleTransparentBgMenuItem() { + const actions = useActions() + const editor = useEditor() + const isTransparentBg = useValue( + 'isTransparentBg', + () => editor.getInstanceState().exportBackground, + [editor] + ) + return +} + +export function UnlockAllMenuItem() { + const editor = useEditor() + const actions = useActions() + const shouldDisplay = useValue('any shapes', () => editor.getCurrentPageShapeIds().size > 0, [ + editor, + ]) + if (!shouldDisplay) return null + return +} + +/* ---------------------- Zoom ---------------------- */ + +export function ZoomTo100MenuItem() { + const editor = useEditor() + const isZoomedTo100 = useValue('zoomed to 100', () => editor.getZoomLevel() === 1, [editor]) + const actions = useActions() + + return +} + +export function ZoomToFitMenuItem() { + const editor = useEditor() + const hasShapes = useValue('has shapes', () => editor.getCurrentPageShapeIds().size > 0, [editor]) + const actions = useActions() + + return ( + + ) +} + +export function ZoomToSelectionMenuItem() { + const editor = useEditor() + const hasSelected = useValue('has shapes', () => editor.getSelectedShapeIds().length > 0, [ + editor, + ]) + const actions = useActions() + + return ( + + ) +} + +/* -------------------- Clipboard ------------------- */ + +export function ClipboardMenuGroup() { + return ( + + + + + + ) +} + +export function CutMenuItem() { + const actions = useActions() + const shouldDisplay = useUnlockedSelectedShapesCount(1) + if (!shouldDisplay) return null + return +} + +export function CopyMenuItem() { + const actions = useActions() + const shouldDisplay = useAnySelectedShapesCount(1) + if (!shouldDisplay) return null + return +} + +export function PasteMenuItem() { + const actions = useActions() + const shouldDisplay = showMenuPaste + if (!shouldDisplay) return null + return +} + +/* ------------------- Conversions ------------------ */ + +export function ConversionsMenuGroup() { + const editor = useEditor() + const actions = useActions() + const atLeastOneShapeOnPage = useValue( + 'atLeastOneShapeOnPage', + () => editor.getCurrentPageShapeIds().size > 0, + [] + ) + if (!atLeastOneShapeOnPage) return null + + return ( + + + + + {Boolean(window.navigator.clipboard?.write) && ( + + )} + + + + + + + + + + + + + + + + + + ) +} + +/* ------------------ Set Selection ----------------- */ + +export function SetSelectionGroup() { + const actions = useActions() + const editor = useEditor() + const atLeastOneShapeOnPage = useValue( + 'atLeastOneShapeOnPage', + () => editor.getCurrentPageShapeIds().size > 0, + [editor] + ) + if (!atLeastOneShapeOnPage) return null + return ( + + + + ) +} + +/* ------------------ Delete Group ------------------ */ + +export function DeleteGroup() { + const actions = useActions() + const oneSelected = useUnlockedSelectedShapesCount(1) + if (!oneSelected) return null + return ( + + + + ) +} + +/* --------------------- Modify --------------------- */ + +export function ArrangeMenuSubmenu() { + const twoSelected = useUnlockedSelectedShapesCount(2) + const onlyFlippableShapeSelected = useOnlyFlippableShape() + const actions = useActions() + + if (!(twoSelected || onlyFlippableShapeSelected)) return null + + return ( + + {twoSelected && ( + + + + + + + + + )} + + {twoSelected && ( + + + + + )} + {onlyFlippableShapeSelected && ( + + + + + )} + + + ) +} + +function DistributeMenuGroup() { + const actions = useActions() + const threeSelected = useUnlockedSelectedShapesCount(3) + if (!threeSelected) return null + + return ( + + + + + ) +} + +function OrderMenuGroup() { + const actions = useActions() + const twoSelected = useUnlockedSelectedShapesCount(2) + const threeStackableItems = useThreeStackableItems() + if (!twoSelected) return null + + return ( + + + {threeStackableItems && } + {threeStackableItems && } + + ) +} + +export function ReorderMenuSubmenu() { + const actions = useActions() + const oneSelected = useUnlockedSelectedShapesCount(1) + if (!oneSelected) return null + + return ( + + + + + + + + + ) +} + +export function MoveToPageMenu() { + const editor = useEditor() + const pages = useValue('pages', () => editor.getPages(), [editor]) + const currentPageId = useValue('current page id', () => editor.getCurrentPageId(), [editor]) + const { addToast } = useToasts() + const actions = useActions() + const trackEvent = useUiEvents() + + const oneSelected = useUnlockedSelectedShapesCount(1) + if (!oneSelected) return null + + return ( + + + {pages.map((page) => ( + { + editor.mark('move_shapes_to_page') + editor.moveShapesToPage(editor.getSelectedShapeIds(), page.id as TLPageId) + + const toPage = editor.getPage(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.setCurrentPage(currentPageId) + }, + }, + ], + }) + } + trackEvent('move-to-page', { source: 'context-menu' }) + }} + title={page.name} + /> + ))} + + + + + + ) +} + +export function EmbedsGroup() { + const editor = useEditor() + const actions = useActions() + + const oneEmbedSelected = useValue( + 'oneEmbedSelected', + () => { + const onlySelectedShape = editor.getOnlySelectedShape() + if (!onlySelectedShape) return false + return !!( + editor.isShapeOfType(onlySelectedShape, 'embed') && + onlySelectedShape.props.url && + !editor.isShapeOrAncestorLocked(onlySelectedShape) + ) + }, + [editor] + ) + + const oneEmbeddableBookmarkSelected = useValue( + 'oneEmbeddableBookmarkSelected', + () => { + const onlySelectedShape = editor.getOnlySelectedShape() + if (!onlySelectedShape) return false + return !!( + editor.isShapeOfType(onlySelectedShape, 'bookmark') && + onlySelectedShape.props.url && + getEmbedInfo(onlySelectedShape.props.url) && + !editor.isShapeOrAncestorLocked(onlySelectedShape) + ) + }, + [editor] + ) + + return ( + + {oneEmbedSelected && } + {oneEmbedSelected && } + {oneEmbeddableBookmarkSelected && } + + ) +} + +/* ------------------- Preferences ------------------ */ + +export function ToggleSnapModeItem() { + const actions = useActions() + const editor = useEditor() + const isSnapMode = useValue('isSnapMode', () => editor.user.getIsSnapMode(), [editor]) + return +} + +export function ToggleToolLockItem() { + const actions = useActions() + const editor = useEditor() + const isToolLock = useValue('isToolLock', () => editor.getInstanceState().isToolLocked, [editor]) + return +} + +export function ToggleGridItem() { + const actions = useActions() + const editor = useEditor() + const isGridMode = useValue('isGridMode', () => editor.getInstanceState().isGridMode, [editor]) + return +} + +export function ToggleDarkModeItem() { + const actions = useActions() + const editor = useEditor() + const isDarkMode = useValue('isDarkMode', () => editor.user.getIsDarkMode(), [editor]) + return +} + +export function ToggleFocusModeItem() { + const actions = useActions() + const editor = useEditor() + const isFocusMode = useValue('isFocusMode', () => editor.getInstanceState().isFocusMode, [editor]) + return +} + +export function ToggleEdgeScrollingItem() { + const actions = useActions() + const editor = useEditor() + const edgeScrollSpeed = useValue('edgeScrollSpeed', () => editor.user.getEdgeScrollSpeed(), [ + editor, + ]) + return ( + + ) +} + +export function ToggleReduceMotionItem() { + const actions = useActions() + const editor = useEditor() + const animationSpeed = useValue('animationSpeed', () => editor.user.getAnimationSpeed(), [editor]) + return ( + + ) +} + +export function ToggleDebugModeItem() { + const actions = useActions() + const editor = useEditor() + const isDebugMode = useValue('isDebugMode', () => editor.getInstanceState().isDebugMode, [editor]) + return +} + +/* ---------------------- Print --------------------- */ + +export function PrintItem() { + const editor = useEditor() + const actions = useActions() + const emptyPage = useValue('emptyPage', () => editor.getCurrentPageShapeIds().size === 0, [ + editor, + ]) + return +} diff --git a/packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuCheckboxItem.tsx b/packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuCheckboxItem.tsx new file mode 100644 index 000000000..d828a1903 --- /dev/null +++ b/packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuCheckboxItem.tsx @@ -0,0 +1,105 @@ +import * as _ContextMenu from '@radix-ui/react-context-menu' +import * as _DropdownMenu from '@radix-ui/react-dropdown-menu' +import { preventDefault } from '@tldraw/editor' +import { unwrapLabel } from '../../context/actions' +import { TLUiEventSource } from '../../context/events' +import { useReadonly } from '../../hooks/useReadonly' +import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey' +import { useTranslation } from '../../hooks/useTranslation/useTranslation' +import { Icon } from '../primitives/Icon' +import { Kbd } from '../primitives/Kbd' +import { useTldrawUiMenuContext } from './TldrawUiMenuContext' + +/** @public */ +export type TLUiMenuCheckboxItemProps< + TranslationKey extends string = string, + IconType extends string = string, +> = { + icon?: IconType + id: string + kbd?: string + title?: string + label?: TranslationKey | { [key: string]: TranslationKey } + readonlyOk?: boolean + onSelect: (source: TLUiEventSource) => Promise | void + checked?: boolean + disabled?: boolean +} + +/** @public */ +export function TldrawUiMenuCheckboxItem< + TranslationKey extends string = string, + IconType extends string = string, +>({ + id, + kbd, + label, + readonlyOk, + onSelect, + disabled = false, + checked = false, +}: TLUiMenuCheckboxItemProps) { + const { type: menuType, sourceId } = useTldrawUiMenuContext() + const isReadonlyMode = useReadonly() + const msg = useTranslation() + + // If the editor is in readonly mode and the item is not marked as readonlyok, return null + if (isReadonlyMode && !readonlyOk) return null + + const labelToUse = unwrapLabel(label, menuType) + const labelStr = labelToUse ? msg(labelToUse as TLUiTranslationKey) : undefined + + switch (menuType) { + case 'menu': { + return ( + <_DropdownMenu.CheckboxItem + dir="ltr" + className="tlui-button tlui-button__menu tlui-button__checkbox" + title={labelStr} + onSelect={(e) => { + onSelect?.(sourceId) + preventDefault(e) + }} + disabled={disabled} + checked={checked} + > + + {labelStr && ( + + {labelStr} + + )} + {kbd && {kbd}} + + ) + } + case 'context-menu': { + return ( + <_ContextMenu.CheckboxItem + key={id} + className="tlui-button tlui-button__menu tlui-button__checkbox" + dir="ltr" + title={labelStr} + onSelect={(e) => { + onSelect(sourceId) + preventDefault(e) + }} + disabled={disabled} + checked={checked} + > + + {labelStr && ( + + {labelStr} + + )} + {kbd && {kbd}} + + ) + } + default: { + // no checkbox items in actions menu + return null + } + } +} diff --git a/packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuContext.tsx b/packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuContext.tsx new file mode 100644 index 000000000..2d4442333 --- /dev/null +++ b/packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuContext.tsx @@ -0,0 +1,38 @@ +import { createContext, useContext } from 'react' +import { TLUiEventSource } from '../../context/events' + +/** @public */ +export type TldrawUiMenuContextType = + | 'panel' + | 'menu' + | 'small-icons' + | 'context-menu' + | 'icons' + | 'keyboard-shortcuts' + | 'helper-buttons' + +const menuContext = createContext<{ + type: TldrawUiMenuContextType + sourceId: TLUiEventSource +}>({ type: 'menu', sourceId: 'menu' }) + +/** @public */ +export function useTldrawUiMenuContext() { + return useContext(menuContext) +} + +/** @public */ +export type TLUiMenuContextProviderProps = { + type: TldrawUiMenuContextType + sourceId: TLUiEventSource + children: React.ReactNode +} + +/** @public */ +export function TldrawUiMenuContextProvider({ + type, + sourceId, + children, +}: TLUiMenuContextProviderProps) { + return {children} +} diff --git a/packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuGroup.tsx b/packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuGroup.tsx new file mode 100644 index 000000000..a02e5aafc --- /dev/null +++ b/packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuGroup.tsx @@ -0,0 +1,66 @@ +import { ContextMenuGroup } from '@radix-ui/react-context-menu' +import { unwrapLabel } from '../../context/actions' +import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey' +import { useTranslation } from '../../hooks/useTranslation/useTranslation' +import { DropdownMenuGroup } from '../primitives/DropdownMenu' +import { useTldrawUiMenuContext } from './TldrawUiMenuContext' + +/** @public */ +export type TLUiMenuGroupProps = { + id: string + /** + * The label to display on the item. If it's a string, it will be translated. If it's an object, the keys will be used as the language keys and the values will be translated. + */ + label?: TranslationKey | { [key: string]: TranslationKey } + small?: boolean + children?: any +} + +/** @public */ +export function TldrawUiMenuGroup({ id, label, small = false, children }: TLUiMenuGroupProps) { + const { type: menuType, sourceId } = useTldrawUiMenuContext() + const msg = useTranslation() + const labelToUse = unwrapLabel(label, menuType) + const labelStr = labelToUse ? msg(labelToUse as TLUiTranslationKey) : undefined + + switch (menuType) { + case 'panel': { + return ( +
+ {children} +
+ ) + } + case 'menu': { + return ( + + {children} + + ) + } + case 'context-menu': { + return ( + + {children} + + ) + } + case 'keyboard-shortcuts': { + // todo: if groups need a label, let's give em a label + return ( +
+

{labelStr}

+
{children}
+
+ ) + } + default: { + return children + } + } +} diff --git a/packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuItem.tsx b/packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuItem.tsx new file mode 100644 index 000000000..ac6738fc1 --- /dev/null +++ b/packages/tldraw/src/lib/ui/components/menus/TldrawUiMenuItem.tsx @@ -0,0 +1,190 @@ +import { ContextMenuItem } from '@radix-ui/react-context-menu' +import { preventDefault } from '@tldraw/editor' +import { useState } from 'react' +import { unwrapLabel } from '../../context/actions' +import { TLUiEventSource } from '../../context/events' +import { useReadonly } from '../../hooks/useReadonly' +import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey' +import { useTranslation } from '../../hooks/useTranslation/useTranslation' +import { Spinner } from '../Spinner' +import { Button } from '../primitives/Button' +import { DropdownMenuItem } from '../primitives/DropdownMenu' +import { Kbd } from '../primitives/Kbd' +import { kbdStr } from '../primitives/shared' +import { useTldrawUiMenuContext } from './TldrawUiMenuContext' + +/** @public */ +export type TLUiMenuItemProps< + TranslationKey extends string = string, + IconType extends string = string, +> = { + id: string + /** + * The icon to display on the item. + */ + icon?: IconType + /** + * The keyboard shortcut to display on the item. + */ + kbd?: string + /** + * The title to display on the item. + */ + title?: string + /** + * The label to display on the item. If it's a string, it will be translated. If it's an object, the keys will be used as the language keys and the values will be translated. + */ + label?: TranslationKey | { [key: string]: TranslationKey } + /** + * If the editor is in readonly mode and the item is not marked as readonlyok, it will not be rendered. + */ + readonlyOk?: boolean + /** + * The function to call when the item is clicked. + */ + onSelect: (source: TLUiEventSource) => Promise | void + /** + * Whether this item should be disabled. + */ + disabled?: boolean + /** + * Prevent the menu from closing when the item is clicked + */ + noClose?: boolean + /** + * Whether to show a spinner on the item. + */ + spinner?: boolean +} + +/** @public */ +export function TldrawUiMenuItem< + TranslationKey extends string = string, + IconType extends string = string, +>({ + disabled = false, + spinner = false, + readonlyOk = false, + id, + kbd, + label, + icon, + onSelect, + noClose, +}: TLUiMenuItemProps) { + const { type: menuType, sourceId } = useTldrawUiMenuContext() + + const msg = useTranslation() + + const [disableClicks, setDisableClicks] = useState(false) + + const isReadonlyMode = useReadonly() + if (isReadonlyMode && !readonlyOk) return null + + const labelToUse = unwrapLabel(label, menuType) + const kbdTouse = kbd ? kbdStr(kbd) : undefined + + const labelStr = labelToUse ? msg(labelToUse as TLUiTranslationKey) : undefined + const titleStr = labelStr && kbdTouse ? `${labelStr} ${kbdTouse}` : labelStr + + switch (menuType) { + case 'menu': { + return ( + { + if (noClose) { + preventDefault(e) + } + if (disableClicks) { + setDisableClicks(false) + } else { + onSelect(sourceId) + } + }} + /> + ) + } + case 'context-menu': { + // Hide disabled context menu items + if (disabled) return null + + return ( + { + if (noClose) preventDefault(e) + if (disableClicks) { + setDisableClicks(false) + } else { + onSelect(sourceId) + } + }} + > + + {labelStr} + + {kbd && {kbd}} + {spinner && } + + ) + } + case 'panel': { + return ( + + ) } /** @public */ -export function Content({ - side = 'bottom', - align = 'start', - sideOffset = 8, - alignOffset = 8, - children, -}: { +export type TLUiDropdownMenuContentProps = { + id?: string children: any alignOffset?: number sideOffset?: number align?: 'start' | 'center' | 'end' side?: 'bottom' | 'top' | 'right' | 'left' -}) { +} + +/** @public */ +export function DropdownMenuContent({ + side = 'bottom', + align = 'start', + sideOffset = 8, + alignOffset = 8, + children, +}: TLUiDropdownMenuContentProps) { const container = useContainer() return ( - - + <_DropdownMenu.Content className="tlui-menu" - align={align} - sideOffset={sideOffset} side={side} + sideOffset={sideOffset} + align={align} alignOffset={alignOffset} collisionPadding={4} > {children} - - + + ) } /** @public */ -export function Sub({ id, children }: { id: string; children: any }) { +export type TLUiDropdownMenuSubProps = { id: string; children: any } + +/** @public */ +export function DropdownMenuSub({ id, children }: TLUiDropdownMenuSubProps) { const [open, onOpenChange] = useMenuIsOpen(id) return ( - + <_DropdownMenu.Sub open={open} onOpenChange={onOpenChange}> {children} - + ) } /** @public */ -export function SubTrigger({ +export type TLUiDropdownMenuSubTriggerProps = { + label: string + id?: string + title?: string + disabled?: boolean +} + +/** @public */ +export function DropdownMenuSubTrigger({ label, - 'data-testid': testId, - 'data-direction': dataDirection, -}: { - label: TLUiTranslationKey | Exclude - 'data-testid'?: string - 'data-direction'?: 'left' | 'right' -}) { + title, + disabled, +}: TLUiDropdownMenuSubTriggerProps) { return ( - + <_DropdownMenu.SubTrigger dir="ltr" asChild> ) } -export const PopoverContent: FC<{ + +/** @public */ +export type TLUiPopoverContentProps = { children: React.ReactNode side: 'top' | 'bottom' | 'left' | 'right' align?: 'start' | 'center' | 'end' alignOffset?: number sideOffset?: number -}> = ({ side, children, align = 'center', sideOffset = 8, alignOffset = 0 }) => { +} + +/** @public */ +export function PopoverContent({ + side, + children, + align = 'center', + sideOffset = 8, + alignOffset = 0, +}: TLUiPopoverContentProps) { const container = useContainer() return ( diff --git a/packages/tldraw/src/lib/ui/TldrawUiContextProvider.tsx b/packages/tldraw/src/lib/ui/context/TldrawUiContextProvider.tsx similarity index 52% rename from packages/tldraw/src/lib/ui/TldrawUiContextProvider.tsx rename to packages/tldraw/src/lib/ui/context/TldrawUiContextProvider.tsx index 973664a3c..afad7083d 100644 --- a/packages/tldraw/src/lib/ui/TldrawUiContextProvider.tsx +++ b/packages/tldraw/src/lib/ui/context/TldrawUiContextProvider.tsx @@ -1,20 +1,16 @@ import { RecursivePartial } from '@tldraw/editor' -import { TLUiAssetUrls, useDefaultUiAssetUrlsWithOverrides } from './assetUrls' -import { ActionsProvider } from './hooks/useActions' -import { ActionsMenuSchemaProvider } from './hooks/useActionsMenuSchema' -import { AssetUrlsProvider } from './hooks/useAssetUrls' -import { BreakPointProvider } from './hooks/useBreakpoint' -import { TLUiContextMenuSchemaProvider } from './hooks/useContextMenuSchema' -import { DialogsProvider } from './hooks/useDialogsProvider' -import { TLUiEventHandler, UiEventsProvider } from './hooks/useEventsProvider' -import { HelpMenuSchemaProvider } from './hooks/useHelpMenuSchema' -import { KeyboardShortcutsSchemaProvider } from './hooks/useKeyboardShortcutsSchema' -import { TLUiMenuSchemaProvider } from './hooks/useMenuSchema' -import { ToastsProvider } from './hooks/useToastsProvider' -import { ToolbarSchemaProvider } from './hooks/useToolbarSchema' -import { ToolsProvider } from './hooks/useTools' -import { TranslationProvider } from './hooks/useTranslation/useTranslation' -import { TLUiOverrides, useMergedOverrides, useMergedTranslationOverrides } from './overrides' +import { TLUiAssetUrls, useDefaultUiAssetUrlsWithOverrides } from '../assetUrls' +import { ToolbarSchemaProvider } from '../hooks/useToolbarSchema' +import { ToolsProvider } from '../hooks/useTools' +import { TranslationProvider } from '../hooks/useTranslation/useTranslation' +import { TLUiOverrides, useMergedOverrides, useMergedTranslationOverrides } from '../overrides' +import { ActionsProvider } from './actions' +import { AssetUrlsProvider } from './asset-urls' +import { BreakPointProvider } from './breakpoints' +import { TLUiComponents, TldrawUiComponentsProvider } from './components' +import { DialogsProvider } from './dialogs' +import { TLUiEventHandler, UiEventsProvider } from './events' +import { ToastsProvider } from './toasts' /** * Props for the {@link @tldraw/tldraw#Tldraw} and {@link TldrawUi} components. @@ -32,6 +28,11 @@ export interface TldrawUiContextProviderProps { */ overrides?: TLUiOverrides | TLUiOverrides[] + /** + * Overrides for the UI components. + */ + components?: TLUiComponents + /** * Callback for when an event occurs in the UI. */ @@ -51,6 +52,7 @@ export interface TldrawUiContextProviderProps { /** @public */ export function TldrawUiContextProvider({ overrides, + components, assetUrls, onUiEvent, forceMobile, @@ -63,7 +65,9 @@ export function TldrawUiContextProvider({ - {children} + + {children} + @@ -81,17 +85,7 @@ function InternalProviders({ - - - - - - {children} - - - - - + {children} diff --git a/packages/tldraw/src/lib/ui/hooks/useActions.tsx b/packages/tldraw/src/lib/ui/context/actions.tsx similarity index 86% rename from packages/tldraw/src/lib/ui/hooks/useActions.tsx rename to packages/tldraw/src/lib/ui/context/actions.tsx index a9145e6a7..1f8ec7339 100644 --- a/packages/tldraw/src/lib/ui/hooks/useActions.tsx +++ b/packages/tldraw/src/lib/ui/context/actions.tsx @@ -3,6 +3,7 @@ import { Box, Editor, HALF_PI, + PageRecordType, TLBookmarkShape, TLEmbedShape, TLFrameShape, @@ -22,16 +23,17 @@ import { getEmbedInfo } from '../../utils/embeds/embeds' import { fitFrameToContent, removeFrame } from '../../utils/frames/frames' import { EditLinkDialog } from '../components/EditLinkDialog' import { EmbedDialog } from '../components/EmbedDialog' +import { useMenuClipboardEvents } from '../hooks/useClipboardEvents' +import { useCopyAs } from '../hooks/useCopyAs' +import { useExportAs } from '../hooks/useExportAs' +import { useInsertMedia } from '../hooks/useInsertMedia' +import { usePrint } from '../hooks/usePrint' +import { TLUiTranslationKey } from '../hooks/useTranslation/TLUiTranslationKey' +import { useTranslation } from '../hooks/useTranslation/useTranslation' import { TLUiIconType } from '../icon-types' -import { useMenuClipboardEvents } from './useClipboardEvents' -import { useCopyAs } from './useCopyAs' -import { useDialogs } from './useDialogsProvider' -import { TLUiEventSource, useUiEvents } from './useEventsProvider' -import { useExportAs } from './useExportAs' -import { useInsertMedia } from './useInsertMedia' -import { usePrint } from './usePrint' -import { useToasts } from './useToastsProvider' -import { TLUiTranslationKey } from './useTranslation/TLUiTranslationKey' +import { useDialogs } from './dialogs' +import { TLUiEventSource, useUiEvents } from './events' +import { useToasts } from './toasts' /** @public */ export interface TLUiActionItem< @@ -42,11 +44,8 @@ export interface TLUiActionItem< id: string kbd?: string title?: string - label?: TransationKey - menuLabel?: TransationKey - shortcutsLabel?: TransationKey - contextMenuLabel?: TransationKey - readonlyOk: boolean + label?: TransationKey | { [key: string]: TransationKey } + readonlyOk?: boolean checkbox?: boolean onSelect: (source: TLUiEventSource) => Promise | void } @@ -77,6 +76,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { const { addDialog, clearDialogs } = useDialogs() const { clearToasts } = useToasts() + const msg = useTranslation() const insertMedia = useInsertMedia() const printSelectionOrPages = usePrint() @@ -108,7 +108,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { id: 'edit-link', label: 'action.edit-link', icon: 'link', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -121,7 +120,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { { id: 'insert-embed', label: 'action.insert-embed', - readonlyOk: false, kbd: '$i', onSelect(source) { trackEvent('insert-embed', { source }) @@ -132,7 +130,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { id: 'insert-media', label: 'action.insert-media', kbd: '$u', - readonlyOk: false, onSelect(source) { trackEvent('insert-media', { source }) insertMedia() @@ -143,7 +140,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { label: 'action.undo', icon: 'undo', kbd: '$z', - readonlyOk: false, onSelect(source) { trackEvent('undo', { source }) editor.undo() @@ -154,7 +150,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { label: 'action.redo', icon: 'redo', kbd: '$!z', - readonlyOk: false, onSelect(source) { trackEvent('redo', { source }) editor.redo() @@ -162,9 +157,11 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'export-as-svg', - label: 'action.export-as-svg', - menuLabel: 'action.export-as-svg.short', - contextMenuLabel: 'action.export-as-svg.short', + label: { + default: 'action.export-as-svg', + menu: 'action.export-as-svg.short', + ['context-menu']: 'action.export-as-svg.short', + }, readonlyOk: true, onSelect(source) { trackEvent('export-as', { format: 'svg', source }) @@ -173,9 +170,11 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'export-as-png', - label: 'action.export-as-png', - menuLabel: 'action.export-as-png.short', - contextMenuLabel: 'action.export-as-png.short', + label: { + default: 'action.export-as-png', + menu: 'action.export-as-png.short', + ['context-menu']: 'action.export-as-png.short', + }, readonlyOk: true, onSelect(source) { trackEvent('export-as', { format: 'png', source }) @@ -184,9 +183,11 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'export-as-json', - label: 'action.export-as-json', - menuLabel: 'action.export-as-json.short', - contextMenuLabel: 'action.export-as-json.short', + label: { + default: 'action.export-as-json', + menu: 'action.export-as-json.short', + ['context-menu']: 'action.export-as-json.short', + }, readonlyOk: true, onSelect(source) { trackEvent('export-as', { format: 'json', source }) @@ -195,9 +196,11 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'copy-as-svg', - label: 'action.copy-as-svg', - menuLabel: 'action.copy-as-svg.short', - contextMenuLabel: 'action.copy-as-svg.short', + label: { + default: 'action.copy-as-svg', + menu: 'action.copy-as-svg.short', + ['context-menu']: 'action.copy-as-svg.short', + }, kbd: '$!c', readonlyOk: true, onSelect(source) { @@ -207,9 +210,11 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'copy-as-png', - label: 'action.copy-as-png', - menuLabel: 'action.copy-as-png.short', - contextMenuLabel: 'action.copy-as-png.short', + label: { + default: 'action.copy-as-png', + menu: 'action.copy-as-png.short', + ['context-menu']: 'action.copy-as-png.short', + }, readonlyOk: true, onSelect(source) { trackEvent('copy-as', { format: 'png', source }) @@ -218,9 +223,11 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'copy-as-json', - label: 'action.copy-as-json', - menuLabel: 'action.copy-as-json.short', - contextMenuLabel: 'action.copy-as-json.short', + label: { + default: 'action.copy-as-json', + menu: 'action.copy-as-json.short', + ['context-menu']: 'action.copy-as-json.short', + }, readonlyOk: true, onSelect(source) { trackEvent('copy-as', { format: 'json', source }) @@ -230,7 +237,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { { id: 'toggle-auto-size', label: 'action.toggle-auto-size', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -298,7 +304,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { { id: 'convert-to-bookmark', label: 'action.convert-to-bookmark', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -342,7 +347,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { { id: 'convert-to-embed', label: 'action.convert-to-embed', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -400,7 +404,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { kbd: '$d', label: 'action.duplicate', icon: 'duplicate', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -446,7 +449,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { label: 'action.ungroup', kbd: '$!g', icon: 'ungroup', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -461,7 +463,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { label: 'action.group', kbd: '$g', icon: 'group', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -481,7 +482,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { id: 'remove-frame', label: 'action.remove-frame', kbd: '$!f', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return @@ -502,7 +502,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { { id: 'fit-frame-to-content', label: 'action.fit-frame-to-content', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return @@ -519,7 +518,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { label: 'action.align-left', kbd: '?A', icon: 'align-left', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -531,11 +529,12 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'align-center-horizontal', - label: 'action.align-center-horizontal', - contextMenuLabel: 'action.align-center-horizontal.short', + label: { + default: 'action.align-center-horizontal', + ['context-menu']: 'action.align-center-horizontal.short', + }, kbd: '?H', icon: 'align-center-horizontal', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -550,7 +549,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { label: 'action.align-right', kbd: '?D', icon: 'align-right', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -562,11 +560,12 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'align-center-vertical', - label: 'action.align-center-vertical', - contextMenuLabel: 'action.align-center-vertical.short', + label: { + default: 'action.align-center-vertical', + ['context-menu']: 'action.align-center-vertical.short', + }, kbd: '?V', icon: 'align-center-vertical', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -581,7 +580,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { label: 'action.align-top', icon: 'align-top', kbd: '?W', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -596,7 +594,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { label: 'action.align-bottom', icon: 'align-bottom', kbd: '?S', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -608,11 +605,12 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'distribute-horizontal', - label: 'action.distribute-horizontal', - contextMenuLabel: 'action.distribute-horizontal.short', + label: { + default: 'action.distribute-horizontal', + ['context-menu']: 'action.distribute-horizontal.short', + }, icon: 'distribute-horizontal', kbd: '?!h', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -624,11 +622,12 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'distribute-vertical', - label: 'action.distribute-vertical', - contextMenuLabel: 'action.distribute-vertical.short', + label: { + default: 'action.distribute-vertical', + ['context-menu']: 'action.distribute-vertical.short', + }, icon: 'distribute-vertical', kbd: '?!V', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -640,10 +639,11 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'stretch-horizontal', - label: 'action.stretch-horizontal', - contextMenuLabel: 'action.stretch-horizontal.short', + label: { + default: 'action.stretch-horizontal', + ['context-menu']: 'action.stretch-horizontal.short', + }, icon: 'stretch-horizontal', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -655,10 +655,11 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'stretch-vertical', - label: 'action.stretch-vertical', - contextMenuLabel: 'action.stretch-vertical.short', + label: { + default: 'action.stretch-vertical', + ['context-menu']: 'action.stretch-vertical.short', + }, icon: 'stretch-vertical', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -670,10 +671,11 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'flip-horizontal', - label: 'action.flip-horizontal', - contextMenuLabel: 'action.flip-horizontal.short', + label: { + default: 'action.flip-horizontal', + ['context-menu']: 'action.flip-horizontal.short', + }, kbd: '!h', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -685,10 +687,8 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'flip-vertical', - label: 'action.flip-vertical', - contextMenuLabel: 'action.flip-vertical.short', + label: { default: 'action.flip-vertical', ['context-menu']: 'action.flip-vertical.short' }, kbd: '!v', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -702,7 +702,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { id: 'pack', label: 'action.pack', icon: 'pack', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -714,10 +713,11 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'stack-vertical', - label: 'action.stack-vertical', - contextMenuLabel: 'action.stack-vertical.short', + label: { + default: 'action.stack-vertical', + ['context-menu']: 'action.stack-vertical.short', + }, icon: 'stack-vertical', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -729,10 +729,11 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'stack-horizontal', - label: 'action.stack-horizontal', - contextMenuLabel: 'action.stack-horizontal.short', + label: { + default: 'action.stack-horizontal', + ['context-menu']: 'action.stack-horizontal.short', + }, icon: 'stack-horizontal', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -747,7 +748,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { label: 'action.bring-to-front', kbd: ']', icon: 'bring-to-front', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -762,7 +762,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { label: 'action.bring-forward', icon: 'bring-forward', kbd: '?]', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -777,7 +776,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { label: 'action.send-backward', icon: 'send-backward', kbd: '?[', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -792,7 +790,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { label: 'action.send-to-back', icon: 'send-to-back', kbd: '[', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -806,7 +803,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { id: 'cut', label: 'action.cut', kbd: '$x', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -831,7 +827,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { id: 'paste', label: 'action.paste', kbd: '$v', - readonlyOk: false, onSelect(source) { navigator.clipboard?.read().then((clipboardItems) => { paste( @@ -876,7 +871,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { label: 'action.delete', kbd: '⌫,del,backspace', icon: 'trash', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -890,7 +884,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { id: 'rotate-cw', label: 'action.rotate-cw', icon: 'rotate-cw', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -909,7 +902,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { id: 'rotate-ccw', label: 'action.rotate-ccw', icon: 'rotate-ccw', - readonlyOk: false, onSelect(source) { if (!canApplySelectionAction()) return if (mustGoBackToSelectToolFirst()) return @@ -980,9 +972,10 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'toggle-snap-mode', - label: 'action.toggle-snap-mode', - menuLabel: 'action.toggle-snap-mode.menu', - readonlyOk: false, + label: { + default: 'action.toggle-snap-mode', + menu: 'action.toggle-snap-mode.menu', + }, onSelect(source) { trackEvent('toggle-snap-mode', { source }) editor.user.updateUserPreferences({ isSnapMode: !editor.user.getIsSnapMode() }) @@ -991,8 +984,10 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'toggle-dark-mode', - label: 'action.toggle-dark-mode', - menuLabel: 'action.toggle-dark-mode.menu', + label: { + default: 'action.toggle-dark-mode', + menu: 'action.toggle-dark-mode.menu', + }, kbd: '$/', readonlyOk: true, onSelect(source) { @@ -1003,8 +998,10 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'toggle-reduce-motion', - label: 'action.toggle-reduce-motion', - menuLabel: 'action.toggle-reduce-motion.menu', + label: { + default: 'action.toggle-reduce-motion', + menu: 'action.toggle-reduce-motion.menu', + }, readonlyOk: true, onSelect(source) { trackEvent('toggle-reduce-motion', { source }) @@ -1016,8 +1013,10 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'toggle-edge-scrolling', - label: 'action.toggle-edge-scrolling', - menuLabel: 'action.toggle-edge-scrolling.menu', + label: { + default: 'action.toggle-edge-scrolling', + menu: 'action.toggle-edge-scrolling.menu', + }, readonlyOk: true, onSelect(source) { trackEvent('toggle-edge-scrolling', { source }) @@ -1029,9 +1028,11 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'toggle-transparent', - label: 'action.toggle-transparent', - menuLabel: 'action.toggle-transparent.menu', - contextMenuLabel: 'action.toggle-transparent.context-menu', + label: { + default: 'action.toggle-transparent', + menu: 'action.toggle-transparent.menu', + ['context-menu']: 'action.toggle-transparent.context-menu', + }, readonlyOk: true, onSelect(source) { trackEvent('toggle-transparent', { source }) @@ -1046,9 +1047,10 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'toggle-tool-lock', - label: 'action.toggle-tool-lock', - menuLabel: 'action.toggle-tool-lock.menu', - readonlyOk: false, + label: { + default: 'action.toggle-tool-lock', + menu: 'action.toggle-tool-lock.menu', + }, kbd: 'q', onSelect(source) { trackEvent('toggle-tool-lock', { source }) @@ -1059,7 +1061,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { { id: 'unlock-all', label: 'action.unlock-all', - readonlyOk: false, onSelect(source) { trackEvent('unlock-all', { source }) const updates = [] as TLShapePartial[] @@ -1075,8 +1076,10 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'toggle-focus-mode', - label: 'action.toggle-focus-mode', - menuLabel: 'action.toggle-focus-mode.menu', + label: { + default: 'action.toggle-focus-mode', + menu: 'action.toggle-focus-mode.menu', + }, readonlyOk: true, kbd: '$.', checkbox: true, @@ -1095,8 +1098,10 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'toggle-grid', - label: 'action.toggle-grid', - menuLabel: 'action.toggle-grid.menu', + label: { + default: 'action.toggle-grid', + menu: 'action.toggle-grid.menu', + }, readonlyOk: true, kbd: "$'", onSelect(source) { @@ -1107,8 +1112,10 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { }, { id: 'toggle-debug-mode', - label: 'action.toggle-debug-mode', - menuLabel: 'action.toggle-debug-mode.menu', + label: { + default: 'action.toggle-debug-mode', + menu: 'action.toggle-debug-mode.menu', + }, readonlyOk: true, onSelect(source) { trackEvent('toggle-debug-mode', { source }) @@ -1161,7 +1168,6 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { { id: 'toggle-lock', label: 'action.toggle-lock', - readonlyOk: false, kbd: '!l', onSelect(source) { editor.mark('locking') @@ -1169,6 +1175,20 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { editor.toggleLock(editor.getSelectedShapeIds()) }, }, + { + id: 'new-page', + label: 'context.pages.new-page', + onSelect(source) { + const newPageId = PageRecordType.createId() + const ids = editor.getSelectedShapeIds() + editor.batch(() => { + editor.mark('move_shapes_to_page') + editor.createPage({ name: msg('page-menu.new-page-initial-name'), id: newPageId }) + editor.moveShapesToPage(ids, newPageId) + }) + trackEvent('new-page', { source }) + }, + }, ] const actions = makeActions(actionItems) @@ -1192,6 +1212,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) { clearDialogs, clearToasts, printSelectionOrPages, + msg, ]) return {children} @@ -1211,3 +1232,14 @@ export function useActions() { function asActions>(actions: T) { return actions as Record } + +/** @public */ +export function unwrapLabel(label?: TLUiActionItem['label'], menuType?: string) { + return label + ? typeof label === 'string' + ? label + : menuType + ? label[menuType] ?? label['default'] + : undefined + : undefined +} diff --git a/packages/tldraw/src/lib/ui/hooks/useAssetUrls.tsx b/packages/tldraw/src/lib/ui/context/asset-urls.tsx similarity index 100% rename from packages/tldraw/src/lib/ui/hooks/useAssetUrls.tsx rename to packages/tldraw/src/lib/ui/context/asset-urls.tsx diff --git a/packages/tldraw/src/lib/ui/hooks/useBreakpoint.tsx b/packages/tldraw/src/lib/ui/context/breakpoints.tsx similarity index 100% rename from packages/tldraw/src/lib/ui/hooks/useBreakpoint.tsx rename to packages/tldraw/src/lib/ui/context/breakpoints.tsx diff --git a/packages/tldraw/src/lib/ui/context/components.tsx b/packages/tldraw/src/lib/ui/context/components.tsx new file mode 100644 index 000000000..b7b59068f --- /dev/null +++ b/packages/tldraw/src/lib/ui/context/components.tsx @@ -0,0 +1,100 @@ +import { useShallowObjectIdentity } from '@tldraw/editor' +import { ComponentType, createContext, useContext, useMemo } from 'react' +import { + DefaultActionsMenu, + TLUiActionsMenuProps, +} from '../components/ActionsMenu/DefaultActionsMenu' +import { + DefaultContextMenu, + TLUiContextMenuProps, +} from '../components/ContextMenu/DefaultContextMenu' +import { DefaultDebugMenu } from '../components/DebugMenu/DefaultDebugMenu' +import { DefaultHelpMenu, TLUiHelpMenuProps } from '../components/HelpMenu/DefaultHelpMenu' +import { + DefaultHelperButtons, + TLUiHelperButtonsProps, +} from '../components/HelperButtons/DefaultHelperButtons' +import { + DefaultKeyboardShortcutsDialog, + TLUiKeyboardShortcutsDialogProps, +} from '../components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialog' +import { DefaultMainMenu, TLUiMainMenuProps } from '../components/MainMenu/DefaultMainMenu' +import { DefaultMinimap } from '../components/Minimap/DefaultMinimap' +import { DefaultNavigationPanel } from '../components/NavigationPanel/DefaultNavigationPanel' +import { DefaultPageMenu } from '../components/PageMenu/DefaultPageMenu' +import { + DefaultQuickActions, + TLUiQuickActionsProps, +} from '../components/QuickActions/DefaultQuickActions' +import { DefaultStylePanel, TLUiStylePanelProps } from '../components/StylePanel/DefaultStylePanel' +import { DefaultToolbar } from '../components/Toolbar/DefaultToolbar' +import { DefaultZoomMenu, TLUiZoomMenuProps } from '../components/ZoomMenu/DefaultZoomMenu' + +export interface BaseTLUiComponents { + ContextMenu: ComponentType + ActionsMenu: ComponentType + HelpMenu: ComponentType + ZoomMenu: ComponentType + MainMenu: ComponentType + Minimap: ComponentType + StylePanel: ComponentType + PageMenu: ComponentType + NavigationPanel: ComponentType + Toolbar: ComponentType + KeyboardShortcutsDialog: ComponentType + QuickActions: ComponentType + HelperButtons: ComponentType + DebugMenu: ComponentType +} + +/** @public */ +export type TLUiComponents = Partial<{ + [K in keyof BaseTLUiComponents]: BaseTLUiComponents[K] | null +}> + +const TldrawUiComponentsContext = createContext({} as TLUiComponents) + +type ComponentsContextProviderProps = { + overrides?: TLUiComponents + children: any +} + +/** @public */ +export function TldrawUiComponentsProvider({ + overrides = {}, + children, +}: ComponentsContextProviderProps) { + const _overrides = useShallowObjectIdentity(overrides) + + return ( + ({ + ContextMenu: DefaultContextMenu, + ActionsMenu: DefaultActionsMenu, + HelpMenu: DefaultHelpMenu, + ZoomMenu: DefaultZoomMenu, + MainMenu: DefaultMainMenu, + Minimap: DefaultMinimap, + StylePanel: DefaultStylePanel, + PageMenu: DefaultPageMenu, + NavigationPanel: DefaultNavigationPanel, + Toolbar: DefaultToolbar, + KeyboardShortcutsDialog: DefaultKeyboardShortcutsDialog, + QuickActions: DefaultQuickActions, + HelperButtons: DefaultHelperButtons, + DebugMenu: DefaultDebugMenu, + ..._overrides, + }), + [_overrides] + )} + > + {children} + + ) +} + +/** @public */ +export function useTldrawUiComponents() { + return useContext(TldrawUiComponentsContext) +} diff --git a/packages/tldraw/src/lib/ui/hooks/useDialogsProvider.tsx b/packages/tldraw/src/lib/ui/context/dialogs.tsx similarity index 93% rename from packages/tldraw/src/lib/ui/hooks/useDialogsProvider.tsx rename to packages/tldraw/src/lib/ui/context/dialogs.tsx index a7323865c..391261e05 100644 --- a/packages/tldraw/src/lib/ui/hooks/useDialogsProvider.tsx +++ b/packages/tldraw/src/lib/ui/context/dialogs.tsx @@ -1,6 +1,6 @@ import { Editor, uniqueId, useEditor } from '@tldraw/editor' -import { createContext, useCallback, useContext, useState } from 'react' -import { useUiEvents } from './useEventsProvider' +import { ComponentType, createContext, useCallback, useContext, useState } from 'react' +import { useUiEvents } from './events' /** @public */ export interface TLUiDialogProps { @@ -11,7 +11,7 @@ export interface TLUiDialogProps { export interface TLUiDialog { id: string onClose?: () => void - component: (props: TLUiDialogProps) => any + component: ComponentType } /** @public */ diff --git a/packages/tldraw/src/lib/ui/hooks/useEventsProvider.tsx b/packages/tldraw/src/lib/ui/context/events.tsx similarity index 97% rename from packages/tldraw/src/lib/ui/hooks/useEventsProvider.tsx rename to packages/tldraw/src/lib/ui/context/events.tsx index 916069a40..982830a76 100644 --- a/packages/tldraw/src/lib/ui/hooks/useEventsProvider.tsx +++ b/packages/tldraw/src/lib/ui/context/events.tsx @@ -26,6 +26,9 @@ export interface TLUiEventMap { // Actions undo: null redo: null + 'change-language': { locale: string } + 'new-page': null + 'move-to-page': null 'group-shapes': null 'ungroup-shapes': null 'remove-frame': null diff --git a/packages/tldraw/src/lib/ui/hooks/useToastsProvider.tsx b/packages/tldraw/src/lib/ui/context/toasts.tsx similarity index 100% rename from packages/tldraw/src/lib/ui/hooks/useToastsProvider.tsx rename to packages/tldraw/src/lib/ui/context/toasts.tsx diff --git a/packages/tldraw/src/lib/ui/hooks/menu-hooks.ts b/packages/tldraw/src/lib/ui/hooks/menu-hooks.ts new file mode 100644 index 000000000..a561dd63b --- /dev/null +++ b/packages/tldraw/src/lib/ui/hooks/menu-hooks.ts @@ -0,0 +1,217 @@ +import { + Editor, + TLArrowShape, + TLDrawShape, + TLGroupShape, + TLLineShape, + TLTextShape, + useEditor, + useValue, +} from '@tldraw/editor' + +function shapesWithUnboundArrows(editor: Editor) { + const selectedShapeIds = editor.getSelectedShapeIds() + const selectedShapes = selectedShapeIds.map((id) => { + return editor.getShape(id) + }) + + return selectedShapes.filter((shape) => { + if (!shape) return false + if ( + editor.isShapeOfType(shape, 'arrow') && + shape.props.start.type === 'binding' + ) { + return false + } + if (editor.isShapeOfType(shape, 'arrow') && shape.props.end.type === 'binding') { + return false + } + return true + }) +} + +/** @internal */ +export const useThreeStackableItems = () => { + const editor = useEditor() + return useValue('threeStackableItems', () => shapesWithUnboundArrows(editor).length > 2, [editor]) +} + +/** @internal */ +export const useAllowGroup = () => { + const editor = useEditor() + return useValue( + 'allow group', + () => { + // We can't group arrows that are bound to shapes that aren't selected + // if more than one shape has an arrow bound to it, allow group + const selectedShapes = editor.getSelectedShapes() + + if (selectedShapes.length < 2) return false + + for (const shape of selectedShapes) { + if (editor.isShapeOfType(shape, 'arrow')) { + const { start, end } = shape.props + if (start.type === 'binding') { + // if the other shape is not among the selected shapes... + if (!selectedShapes.some((s) => s.id === start.boundShapeId)) { + return false + } + } + if (end.type === 'binding') { + // if the other shape is not among the selected shapes... + if (!selectedShapes.some((s) => s.id === end.boundShapeId)) { + return false + } + } + } + } + return true + }, + [editor] + ) +} + +/** @internal */ +export const useAllowUngroup = () => { + const editor = useEditor() + return useValue( + 'allowUngroup', + () => editor.getSelectedShapeIds().some((id) => editor.getShape(id)?.type === 'group'), + [editor] + ) +} + +export const showMenuPaste = + typeof window !== 'undefined' && + 'navigator' in window && + Boolean(navigator.clipboard) && + Boolean(navigator.clipboard.read) + +/** + * Returns true if the number of LOCKED OR UNLOCKED selected shapes is at least min or at most max. + */ +export function useAnySelectedShapesCount(min?: number, max?: number) { + const editor = useEditor() + return useValue( + 'selectedShapes', + () => { + const len = editor.getSelectedShapes().length + if (min === undefined) { + if (max === undefined) { + // just length + return len + } else { + // max but no min + return len <= max + } + } else { + if (max === undefined) { + // min but no max + return len >= min + } else { + // max and min + return len >= min && len <= max + } + } + }, + [editor, min, max] + ) +} + +/** + * Returns true if the number of UNLOCKED selected shapes is at least min or at most max. + */ +export function useUnlockedSelectedShapesCount(min?: number, max?: number) { + const editor = useEditor() + return useValue( + 'selectedShapes', + () => { + const len = editor + .getSelectedShapes() + .filter((s) => !editor.isShapeOrAncestorLocked(s)).length + if (min === undefined) { + if (max === undefined) { + // just length + return len + } else { + // max but no min + return len <= max + } + } else { + if (max === undefined) { + // min but no max + return len >= min + } else { + // max and min + return len >= min && len <= max + } + } + }, + [editor] + ) +} + +export function useShowAutoSizeToggle() { + const editor = useEditor() + return useValue( + 'showAutoSizeToggle', + () => { + const selectedShapes = editor.getSelectedShapes() + return ( + selectedShapes.length === 1 && + editor.isShapeOfType(selectedShapes[0], 'text') && + selectedShapes[0].props.autoSize === false + ) + }, + [editor] + ) +} + +export function useHasLinkShapeSelected() { + const editor = useEditor() + return useValue( + 'hasLinkShapeSelected', + () => { + const onlySelectedShape = editor.getOnlySelectedShape() + return !!( + onlySelectedShape && + onlySelectedShape.type !== 'embed' && + 'url' in onlySelectedShape.props + ) + }, + [editor] + ) +} + +export function useOnlyFlippableShape() { + const editor = useEditor() + return useValue( + 'onlyFlippableShape', + () => { + const selectedShapes = editor.getSelectedShapes() + return ( + selectedShapes.length === 1 && + selectedShapes.every( + (shape) => + editor.isShapeOfType(shape, 'group') || + editor.isShapeOfType(shape, 'arrow') || + editor.isShapeOfType(shape, 'line') || + editor.isShapeOfType(shape, 'draw') + ) + ) + }, + [editor] + ) +} + +/** @public */ +export function useCanRedo() { + const editor = useEditor() + return useValue('useCanRedo', () => editor.getCanRedo(), [editor]) +} + +/** @public */ +export function useCanUndo() { + const editor = useEditor() + return useValue('useCanUndo', () => editor.getCanUndo(), [editor]) +} diff --git a/packages/tldraw/src/lib/ui/hooks/menuHelpers.ts b/packages/tldraw/src/lib/ui/hooks/menuHelpers.ts deleted file mode 100644 index 6ca2c3151..000000000 --- a/packages/tldraw/src/lib/ui/hooks/menuHelpers.ts +++ /dev/null @@ -1,247 +0,0 @@ -import { - Editor, - TLArrowShape, - TLShape, - TLShapeId, - assert, - exhaustiveSwitchError, - useEditor, - useValue, -} from '@tldraw/editor' -import { TLUiActionItem } from './useActions' -import { TLUiToolItem } from './useTools' -import { TLUiTranslationKey } from './useTranslation/TLUiTranslationKey' - -/** @public */ -export type TLUiMenuChild = - | TLUiMenuItem - | TLUiSubMenu - | TLUiMenuGroup - | TLUiCustomMenuItem - | null - -/** @public */ -export type TLUiCustomMenuItem = { - id: string - type: 'custom' - disabled: boolean - readonlyOk: boolean -} - -/** @public */ -export type TLUiMenuItem = { - id: string - type: 'item' - readonlyOk: boolean - actionItem: TLUiActionItem - disabled: boolean - checked: boolean -} - -/** @public */ -export type TLUiMenuGroup = { - id: string - type: 'group' - checkbox: boolean - disabled: boolean - readonlyOk: boolean - children: TLUiMenuChild[] -} - -/** @public */ -export type TLUiSubMenu = { - id: string - type: 'submenu' - label: TranslationKey - disabled: boolean - readonlyOk: boolean - children: TLUiMenuChild[] -} - -/** @public */ -export type TLUiMenuSchema = (TLUiMenuGroup | TLUiMenuItem | TLUiCustomMenuItem)[] - -/** @public */ -export function compactMenuItems(arr: T[]): Exclude[] { - return arr.filter((i) => i !== undefined && i !== null && i !== false) as any -} - -/** @public */ -export function menuGroup( - id: string, - ...children: (TLUiMenuChild | false)[] -): TLUiMenuGroup | null { - const childItems = compactMenuItems(children) - - if (childItems.length === 0) return null - - return { - id, - type: 'group', - checkbox: childItems.every((child) => child.type === 'item' && child.actionItem.checkbox), - disabled: childItems.every((child) => child.disabled), - readonlyOk: childItems.some((child) => child.readonlyOk), - children: childItems, - } -} - -/** @public */ -export function menuSubmenu( - id: string, - label: TLUiTranslationKey | Exclude, - ...children: (TLUiMenuChild | false)[] -): TLUiSubMenu | null { - const childItems = compactMenuItems(children) - if (childItems.length === 0) return null - - return { - id, - type: 'submenu', - label, - children: childItems, - disabled: childItems.every((child) => child.disabled), - readonlyOk: childItems.some((child) => child.readonlyOk), - } -} - -/** @public */ -export function menuCustom( - id: string, - opts = {} as Partial<{ readonlyOk: boolean; disabled: boolean }> -) { - const { readonlyOk = true, disabled = false } = opts - return { - id, - type: 'custom' as const, - disabled, - readonlyOk, - } -} - -/** @public */ -export function menuItem( - actionItem: TLUiActionItem | TLUiToolItem, - opts = {} as Partial<{ checked: boolean; disabled: boolean }> -): TLUiMenuItem { - if (!actionItem) { - throw Error('No action item provided to menuItem') - } - - if (!actionItem.label) { - throw Error("Trying to create menu item for action item that doesn't have a label") - } - - const { checked = false, disabled = false } = opts - - return { - id: actionItem.id, - type: 'item' as const, - actionItem, - disabled, - checked, - readonlyOk: actionItem.readonlyOk, - } -} - -function shapesWithUnboundArrows(editor: Editor) { - const selectedShapeIds = editor.getSelectedShapeIds() - const selectedShapes = selectedShapeIds.map((id) => { - return editor.getShape(id) - }) - - return selectedShapes.filter((shape) => { - if (!shape) return false - if ( - editor.isShapeOfType(shape, 'arrow') && - shape.props.start.type === 'binding' - ) { - return false - } - if (editor.isShapeOfType(shape, 'arrow') && shape.props.end.type === 'binding') { - return false - } - return true - }) -} - -/** @internal */ -export const useThreeStackableItems = () => { - const editor = useEditor() - return useValue('threeStackableItems', () => shapesWithUnboundArrows(editor).length > 2, [editor]) -} - -function shapesWithArrowsBoundToThem(editor: Editor) { - const selectedShapes = editor.getSelectedShapes() - const arrows: TLArrowShape[] = [] - const otherShapesMap = new Map() - selectedShapes.forEach((shape) => { - if (shape.type === 'arrow') { - arrows.push(shape as TLArrowShape) - } else { - otherShapesMap.set(shape.id, shape) - } - }) - // We want to get all the arrows that are either unbound or bound to one of the selected shapes - const groupableArrows = arrows.filter((arrow) => { - if (arrow.props.start.type === 'binding') { - if (!otherShapesMap.has(arrow.props.start.boundShapeId)) { - return false - } - } - if (arrow.props.end.type === 'binding') { - if (!otherShapesMap.has(arrow.props.end.boundShapeId)) { - return false - } - } - return true - }) - return Array.from(otherShapesMap.values()).concat(groupableArrows) -} - -/** @internal */ -export const useAllowGroup = () => { - const editor = useEditor() - return useValue('allowGroup', () => shapesWithArrowsBoundToThem(editor).length > 1, [editor]) -} - -/** @internal */ -export const useAllowUngroup = () => { - const editor = useEditor() - return useValue( - 'allowUngroup', - () => editor.getSelectedShapeIds().some((id) => editor.getShape(id)?.type === 'group'), - [editor] - ) -} - -/** @public */ -export function findMenuItem(menu: TLUiMenuSchema, path: string[]) { - const item = _findMenuItem(menu, path) - assert(item, `Menu item ${path.join(' > ')} not found`) - return item -} - -function _findMenuItem(menu: TLUiMenuSchema | TLUiMenuChild[], path: string[]): TLUiMenuChild { - const [next, ...rest] = path - if (!next) return null - - const item = menu.find((item) => item?.id === next) - if (!item) return null - - switch (item.type) { - case 'group': - case 'submenu': - return rest.length === 0 ? item : _findMenuItem(item.children, rest) - case 'item': - case 'custom': - return rest.length === 0 ? item : null - default: - exhaustiveSwitchError(item, 'type') - } -} - -export const showMenuPaste = - typeof window !== 'undefined' && - 'navigator' in window && - Boolean(navigator.clipboard) && - Boolean(navigator.clipboard.read) diff --git a/packages/tldraw/src/lib/ui/hooks/useActionsMenuSchema.tsx b/packages/tldraw/src/lib/ui/hooks/useActionsMenuSchema.tsx deleted file mode 100644 index 0f760e72d..000000000 --- a/packages/tldraw/src/lib/ui/hooks/useActionsMenuSchema.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import { Editor, useEditor, useValue } from '@tldraw/editor' -import React, { useMemo } from 'react' -import { - TLUiMenuSchema, - menuItem, - useAllowGroup, - useAllowUngroup, - useThreeStackableItems, -} from './menuHelpers' -import { useActions } from './useActions' -import { useBreakpoint } from './useBreakpoint' -import { useHasLinkShapeSelected } from './useHasLinkShapeSelected' - -/** @public */ -export type TLUiActionsMenuSchemaContextType = TLUiMenuSchema - -/** @internal */ -export const ActionsMenuSchemaContext = React.createContext({} as TLUiActionsMenuSchemaContextType) - -/** @public */ -export type ActionsMenuSchemaProviderProps = { - overrides?: ( - editor: Editor, - schema: TLUiActionsMenuSchemaContextType, - helpers: { - actions: ReturnType - oneSelected: boolean - twoSelected: boolean - threeSelected: boolean - } - ) => TLUiActionsMenuSchemaContextType - children: any -} - -/** @internal */ -export const ActionsMenuSchemaProvider = ({ - overrides, - children, -}: ActionsMenuSchemaProviderProps) => { - const editor = useEditor() - const actions = useActions() - - const selectedCount = useValue('selected count', () => editor.getSelectedShapeIds().length, [ - editor, - ]) - - const oneSelected = selectedCount > 0 - const twoSelected = selectedCount > 1 - const threeSelected = selectedCount > 2 - const threeStackableItems = useThreeStackableItems() - const allowGroup = useAllowGroup() - const allowUngroup = useAllowUngroup() - const showEditLink = useHasLinkShapeSelected() - const breakpoint = useBreakpoint() - const isZoomedTo100 = useValue('zoom is 1', () => editor.getZoomLevel() === 1, [editor]) - - const actionTLUiMenuSchema = useMemo(() => { - const results = [ - menuItem(actions['align-left'], { disabled: !twoSelected }), - menuItem(actions['align-center-horizontal'], { disabled: !twoSelected }), - menuItem(actions['align-right'], { disabled: !twoSelected }), - menuItem(actions['stretch-horizontal'], { disabled: !twoSelected }), - menuItem(actions['align-top'], { disabled: !twoSelected }), - menuItem(actions['align-center-vertical'], { disabled: !twoSelected }), - menuItem(actions['align-bottom'], { disabled: !twoSelected }), - menuItem(actions['stretch-vertical'], { disabled: !twoSelected }), - menuItem(actions['distribute-horizontal'], { disabled: !threeSelected }), - menuItem(actions['distribute-vertical'], { disabled: !threeSelected }), - menuItem(actions['stack-horizontal'], { disabled: !threeStackableItems }), - menuItem(actions['stack-vertical'], { disabled: !threeStackableItems }), - menuItem(actions['send-to-back'], { disabled: !oneSelected }), - menuItem(actions['send-backward'], { disabled: !oneSelected }), - menuItem(actions['bring-forward'], { disabled: !oneSelected }), - menuItem(actions['bring-to-front'], { disabled: !oneSelected }), - breakpoint < 5 - ? menuItem(actions['zoom-to-100'], { disabled: !!isZoomedTo100 }) - : menuItem(actions['rotate-ccw'], { disabled: !oneSelected }), - menuItem(actions['rotate-cw'], { disabled: !oneSelected }), - menuItem(actions['edit-link'], { disabled: !showEditLink }), - allowGroup - ? menuItem(actions['group'], { disabled: !twoSelected }) - : allowUngroup - ? menuItem(actions['ungroup']) - : menuItem(actions['group'], { disabled: !twoSelected }), - ] - - if (overrides) { - return overrides(editor, results, { actions, oneSelected, twoSelected, threeSelected }) - } - - return results - }, [ - editor, - isZoomedTo100, - allowGroup, - overrides, - actions, - oneSelected, - twoSelected, - threeSelected, - threeStackableItems, - allowUngroup, - showEditLink, - breakpoint, - ]) - - return ( - - {children} - - ) -} - -/** @public */ -export function useActionsMenuSchema(): TLUiMenuSchema { - const ctx = React.useContext(ActionsMenuSchemaContext) - - if (!ctx) { - throw new Error('useActionsMenuSchema must be used inside of a ActionsMenuSchemaProvider.') - } - - return ctx -} diff --git a/packages/tldraw/src/lib/ui/hooks/useCanRedo.ts b/packages/tldraw/src/lib/ui/hooks/useCanRedo.ts deleted file mode 100644 index 27b5f2aac..000000000 --- a/packages/tldraw/src/lib/ui/hooks/useCanRedo.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { useEditor, useValue } from '@tldraw/editor' - -/** @public */ -export function useCanRedo() { - const editor = useEditor() - return useValue('useCanRedo', () => editor.getCanRedo(), [editor]) -} diff --git a/packages/tldraw/src/lib/ui/hooks/useCanUndo.ts b/packages/tldraw/src/lib/ui/hooks/useCanUndo.ts deleted file mode 100644 index 53163e625..000000000 --- a/packages/tldraw/src/lib/ui/hooks/useCanUndo.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { useEditor, useValue } from '@tldraw/editor' - -/** @public */ -export function useCanUndo() { - const editor = useEditor() - return useValue('useCanUndo', () => editor.getCanUndo(), [editor]) -} diff --git a/packages/tldraw/src/lib/ui/hooks/useClipboardEvents.ts b/packages/tldraw/src/lib/ui/hooks/useClipboardEvents.ts index 08b4da549..8541f8ab4 100644 --- a/packages/tldraw/src/lib/ui/hooks/useClipboardEvents.ts +++ b/packages/tldraw/src/lib/ui/hooks/useClipboardEvents.ts @@ -14,11 +14,11 @@ import { } from '@tldraw/editor' import { compressToBase64, decompressFromBase64 } from 'lz-string' import { useCallback, useEffect } from 'react' +import { TLUiEventSource, useUiEvents } from '../context/events' import { pasteExcalidrawContent } from './clipboard/pasteExcalidrawContent' import { pasteFiles } from './clipboard/pasteFiles' import { pasteTldrawContent } from './clipboard/pasteTldrawContent' import { pasteUrl } from './clipboard/pasteUrl' -import { TLUiEventSource, useUiEvents } from './useEventsProvider' /** * Strip HTML tags from a string. diff --git a/packages/tldraw/src/lib/ui/hooks/useContextMenuSchema.tsx b/packages/tldraw/src/lib/ui/hooks/useContextMenuSchema.tsx deleted file mode 100644 index 5b6c91cef..000000000 --- a/packages/tldraw/src/lib/ui/hooks/useContextMenuSchema.tsx +++ /dev/null @@ -1,257 +0,0 @@ -import { Editor, TLFrameShape, track, useEditor, useValue } from '@tldraw/editor' -import React, { useMemo } from 'react' -import { - TLUiMenuSchema, - compactMenuItems, - menuCustom, - menuGroup, - menuItem, - menuSubmenu, - showMenuPaste, - useAllowGroup, - useAllowUngroup, - useThreeStackableItems, -} from './menuHelpers' -import { useActions } from './useActions' -import { useHasLinkShapeSelected } from './useHasLinkShapeSelected' -import { useOnlyFlippableShape } from './useOnlyFlippableShape' -import { useShowAutoSizeToggle } from './useShowAutoSizeToggle' - -/** @public */ -export type TLUiContextTTLUiMenuSchemaContextType = TLUiMenuSchema - -/** @internal */ -export const TLUiContextMenuSchemaContext = React.createContext( - {} as TLUiContextTTLUiMenuSchemaContextType -) - -/** @public */ -export type TLUiContextMenuSchemaProviderProps = { - overrides?: ( - editor: Editor, - schema: TLUiContextTTLUiMenuSchemaContextType, - helpers: { - actions: ReturnType - oneSelected: boolean - twoSelected: boolean - threeSelected: boolean - showAutoSizeToggle: boolean - showUngroup: boolean - onlyFlippableShapeSelected: boolean - } - ) => TLUiContextTTLUiMenuSchemaContextType - children: any -} - -/** @internal */ -export const TLUiContextMenuSchemaProvider = track(function TLUiContextMenuSchemaProvider({ - overrides, - children, -}: TLUiContextMenuSchemaProviderProps) { - const editor = useEditor() - const actions = useActions() - - const showAutoSizeToggle = useShowAutoSizeToggle() - - const onlyFlippableShapeSelected = useOnlyFlippableShape() - - const selectedShapes = editor.getSelectedShapes() - const selectedCount = selectedShapes.length - - const oneSelected = selectedCount > 0 - - const twoSelected = selectedCount > 1 - const threeSelected = selectedCount > 2 - const threeStackableItems = useThreeStackableItems() - const atLeastOneShapeOnPage = useValue( - 'atLeastOneShapeOnPage', - () => editor.getCurrentPageShapeIds().size > 0, - [] - ) - const isTransparentBg = useValue( - 'isTransparentBg', - () => editor.getInstanceState().exportBackground, - [] - ) - const allowGroup = useAllowGroup() - const allowUngroup = useAllowUngroup() - const hasClipboardWrite = Boolean(window.navigator.clipboard?.write) - const showEditLink = useHasLinkShapeSelected() - const onlySelectedShape = editor.getOnlySelectedShape() - const allowRemoveFrame = - oneSelected && - selectedShapes.every((shape) => editor.isShapeOfType(shape, 'frame')) - const allowFitFrameToContent = - onlySelectedShape && - editor.isShapeOfType(onlySelectedShape, 'frame') && - editor.getSortedChildIdsForParent(onlySelectedShape).length > 0 - const isShapeLocked = onlySelectedShape && editor.isShapeOrAncestorLocked(onlySelectedShape) - - const contextTLUiMenuSchema = useMemo(() => { - const contextTLUiMenuSchemaWithoutOverrides: TLUiMenuSchema = compactMenuItems([ - menuGroup( - 'selection', - showAutoSizeToggle && menuItem(actions['toggle-auto-size']), - showEditLink && !isShapeLocked && menuItem(actions['edit-link']), - oneSelected && !isShapeLocked && menuItem(actions['duplicate']), - allowGroup && !isShapeLocked && menuItem(actions['group']), - allowUngroup && !isShapeLocked && menuItem(actions['ungroup']), - allowRemoveFrame && !isShapeLocked && menuItem(actions['remove-frame']), - allowFitFrameToContent && !isShapeLocked && menuItem(actions['fit-frame-to-content']), - oneSelected && menuItem(actions['toggle-lock']) - ), - menuGroup( - 'modify', - (twoSelected || onlyFlippableShapeSelected) && - menuSubmenu( - 'arrange', - 'context-menu.arrange', - twoSelected && - menuGroup( - 'align', - menuItem(actions['align-left']), - menuItem(actions['align-center-horizontal']), - menuItem(actions['align-right']), - menuItem(actions['align-top']), - menuItem(actions['align-center-vertical']), - menuItem(actions['align-bottom']) - ), - threeSelected && - menuGroup( - 'distribute', - menuItem(actions['distribute-horizontal']), - menuItem(actions['distribute-vertical']) - ), - twoSelected && - menuGroup( - 'stretch', - menuItem(actions['stretch-horizontal']), - menuItem(actions['stretch-vertical']) - ), - onlyFlippableShapeSelected && - !isShapeLocked && - menuGroup( - 'flip', - menuItem(actions['flip-horizontal']), - menuItem(actions['flip-vertical']) - ), - twoSelected && - menuGroup( - 'order', - menuItem(actions['pack'], { disabled: !twoSelected }), - threeStackableItems && menuItem(actions['stack-vertical']), - threeStackableItems && menuItem(actions['stack-horizontal']) - ) - ), - oneSelected && - !isShapeLocked && - menuSubmenu( - 'reorder', - 'context-menu.reorder', - menuGroup( - 'reorder', - menuItem(actions['bring-to-front']), - menuItem(actions['bring-forward']), - menuItem(actions['send-backward']), - menuItem(actions['send-to-back']) - ) - ), - oneSelected && !isShapeLocked && menuCustom('MOVE_TO_PAGE_MENU', { readonlyOk: false }) - ), - menuGroup( - 'clipboard-group', - oneSelected && !isShapeLocked && menuItem(actions['cut']), - oneSelected && menuItem(actions['copy']), - showMenuPaste && menuItem(actions['paste']) - ), - atLeastOneShapeOnPage && - menuGroup( - 'conversions', - menuSubmenu( - 'copy-as', - 'context-menu.copy-as', - menuGroup( - 'copy-as-group', - menuItem(actions['copy-as-svg']), - hasClipboardWrite && menuItem(actions['copy-as-png']), - menuItem(actions['copy-as-json']) - ), - menuGroup( - 'export-bg', - menuItem(actions['toggle-transparent'], { checked: !isTransparentBg }) - ) - ), - menuSubmenu( - 'export-as', - 'context-menu.export-as', - menuGroup( - 'export-as-group', - menuItem(actions['export-as-svg']), - menuItem(actions['export-as-png']), - menuItem(actions['export-as-json']) - ), - menuGroup( - 'export-bg,', - menuItem(actions['toggle-transparent'], { checked: !isTransparentBg }) - ) - ) - ), - atLeastOneShapeOnPage && - menuGroup( - 'set-selection-group', - menuItem(actions['select-all']), - oneSelected && menuItem(actions['select-none']) - ), - oneSelected && !isShapeLocked && menuGroup('delete-group', menuItem(actions['delete'])), - ]) - - if (!overrides) return contextTLUiMenuSchemaWithoutOverrides - return overrides(editor, contextTLUiMenuSchemaWithoutOverrides, { - actions, - oneSelected, - twoSelected, - threeSelected, - showAutoSizeToggle, - showUngroup: allowUngroup, - onlyFlippableShapeSelected, - }) - }, [ - actions, - oneSelected, - twoSelected, - threeSelected, - showAutoSizeToggle, - onlyFlippableShapeSelected, - atLeastOneShapeOnPage, - threeStackableItems, - allowGroup, - allowUngroup, - allowRemoveFrame, - allowFitFrameToContent, - hasClipboardWrite, - showEditLink, - // oneEmbedSelected, - // oneEmbeddableBookmarkSelected, - isTransparentBg, - isShapeLocked, - editor, - overrides, - ]) - - return ( - - {children} - - ) -}) - -/** @public */ -export function useContextMenuSchema(): TLUiMenuSchema { - const ctx = React.useContext(TLUiContextMenuSchemaContext) - - if (!ctx) { - throw new Error('useContextMenuSchema must be used inside of a TLUiContextMenuSchemaProvider.') - } - - return ctx -} diff --git a/packages/tldraw/src/lib/ui/hooks/useCopyAs.ts b/packages/tldraw/src/lib/ui/hooks/useCopyAs.ts index 382fd7f76..f89c91d1b 100644 --- a/packages/tldraw/src/lib/ui/hooks/useCopyAs.ts +++ b/packages/tldraw/src/lib/ui/hooks/useCopyAs.ts @@ -1,7 +1,7 @@ import { TLShapeId, useEditor } from '@tldraw/editor' import { useCallback } from 'react' import { TLCopyType, copyAs } from '../../utils/export/copyAs' -import { useToasts } from './useToastsProvider' +import { useToasts } from '../context/toasts' import { useTranslation } from './useTranslation/useTranslation' /** @public */ diff --git a/packages/tldraw/src/lib/ui/hooks/useEditorEvents.ts b/packages/tldraw/src/lib/ui/hooks/useEditorEvents.ts index 4c7c1c97e..6dc87cdb2 100644 --- a/packages/tldraw/src/lib/ui/hooks/useEditorEvents.ts +++ b/packages/tldraw/src/lib/ui/hooks/useEditorEvents.ts @@ -1,6 +1,6 @@ import { useEditor } from '@tldraw/editor' import { useEffect } from 'react' -import { useToasts } from './useToastsProvider' +import { useToasts } from '../context/toasts' /** @internal */ export function useEditorEvents() { diff --git a/packages/tldraw/src/lib/ui/hooks/useExportAs.ts b/packages/tldraw/src/lib/ui/hooks/useExportAs.ts index f854d72a5..77aac0969 100644 --- a/packages/tldraw/src/lib/ui/hooks/useExportAs.ts +++ b/packages/tldraw/src/lib/ui/hooks/useExportAs.ts @@ -1,7 +1,7 @@ import { TLShapeId, useEditor } from '@tldraw/editor' import { useCallback } from 'react' import { TLExportType, exportAs } from '../../utils/export/exportAs' -import { useToasts } from './useToastsProvider' +import { useToasts } from '../context/toasts' import { useTranslation } from './useTranslation/useTranslation' /** @public */ diff --git a/packages/tldraw/src/lib/ui/hooks/useHasLinkShapeSelected.ts b/packages/tldraw/src/lib/ui/hooks/useHasLinkShapeSelected.ts deleted file mode 100644 index 7e4c764df..000000000 --- a/packages/tldraw/src/lib/ui/hooks/useHasLinkShapeSelected.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { useEditor, useValue } from '@tldraw/editor' - -export function useHasLinkShapeSelected() { - const editor = useEditor() - return useValue( - 'hasLinkShapeSelected', - () => { - const selectedShapes = editor.getSelectedShapes() - return ( - selectedShapes.length === 1 && - 'url' in selectedShapes[0].props && - selectedShapes[0].type !== 'embed' - ) - }, - [editor] - ) -} diff --git a/packages/tldraw/src/lib/ui/hooks/useHelpMenuSchema.tsx b/packages/tldraw/src/lib/ui/hooks/useHelpMenuSchema.tsx deleted file mode 100644 index c5b17fa19..000000000 --- a/packages/tldraw/src/lib/ui/hooks/useHelpMenuSchema.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { Editor, TLLanguage, compact, track, useEditor } from '@tldraw/editor' -import React, { useMemo } from 'react' -import { KeyboardShortcutsDialog } from '../components/KeyboardShortcutsDialog' -import { TLUiMenuSchema, menuCustom, menuGroup, menuItem } from './menuHelpers' -import { useActions } from './useActions' -import { useDialogs } from './useDialogsProvider' -import { useLanguages } from './useTranslation/useLanguages' - -/** @public */ -export type TLUiHelpMenuSchemaContextType = TLUiMenuSchema - -/** @internal */ -export const HelpMenuSchemaContext = React.createContext({} as TLUiHelpMenuSchemaContextType) - -/** @public */ -export type TLUiHelpMenuSchemaProviderProps = { - overrides?: ( - editor: Editor, - schema: TLUiHelpMenuSchemaContextType, - helpers: { - actions: ReturnType - languages: readonly TLLanguage[] - currentLanguage: string - oneSelected: boolean - twoSelected: boolean - threeSelected: boolean - } - ) => TLUiHelpMenuSchemaContextType - children: any -} - -/** @internal */ -export const HelpMenuSchemaProvider = track(function HelpMenuSchemaProvider({ - overrides, - children, -}: TLUiHelpMenuSchemaProviderProps) { - const editor = useEditor() - const actions = useActions() - - const selectedCount = editor.getSelectedShapeIds().length - - const oneSelected = selectedCount > 0 - const twoSelected = selectedCount > 1 - const threeSelected = selectedCount > 2 - - const { languages, currentLanguage } = useLanguages() - const { addDialog } = useDialogs() - - const helpTLUiMenuSchema = useMemo(() => { - const helpTLUiMenuSchema = compact([ - menuGroup( - 'top', - menuCustom('LANGUAGE_MENU', { readonlyOk: true }), - menuItem({ - id: 'keyboard-shortcuts', - label: 'help-menu.keyboard-shortcuts', - readonlyOk: true, - onSelect() { - addDialog({ component: KeyboardShortcutsDialog }) - }, - }) - ), - ]) - - if (overrides) { - return overrides(editor, helpTLUiMenuSchema, { - actions, - currentLanguage, - languages, - oneSelected, - twoSelected, - threeSelected, - }) - } - - return helpTLUiMenuSchema - }, [ - editor, - overrides, - languages, - actions, - oneSelected, - twoSelected, - threeSelected, - currentLanguage, - addDialog, - ]) - - return ( - - {children} - - ) -}) - -/** @public */ -export function useHelpMenuSchema(): TLUiMenuSchema { - const ctx = React.useContext(HelpMenuSchemaContext) - - if (!ctx) { - throw new Error('useHelpMenuSchema must be used inside of a helpTLUiMenuSchemaProvider.') - } - - return ctx -} diff --git a/packages/tldraw/src/lib/ui/hooks/useHighDpiCanvas.ts b/packages/tldraw/src/lib/ui/hooks/useHighDpiCanvas.ts deleted file mode 100644 index 4ca17ba79..000000000 --- a/packages/tldraw/src/lib/ui/hooks/useHighDpiCanvas.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { useLayoutEffect } from 'react' - -/** @internal */ -export function useHighDpiCanvas(ref: React.RefObject, dpr: number) { - // Match the resolution of the client - useLayoutEffect(() => { - const canvas = ref.current - if (!canvas) return - - const rect = canvas.getBoundingClientRect() - canvas.width = rect.width * dpr - canvas.height = rect.height * dpr - }, [ref, dpr]) -} diff --git a/packages/tldraw/src/lib/ui/hooks/useKeyboardShortcuts.ts b/packages/tldraw/src/lib/ui/hooks/useKeyboardShortcuts.ts index 5fd564de7..a28ad87f9 100644 --- a/packages/tldraw/src/lib/ui/hooks/useKeyboardShortcuts.ts +++ b/packages/tldraw/src/lib/ui/hooks/useKeyboardShortcuts.ts @@ -1,8 +1,8 @@ import { Editor, TLPointerEventInfo, preventDefault, useEditor, useValue } from '@tldraw/editor' import hotkeys from 'hotkeys-js' import { useEffect } from 'react' -import { useToolbarItems } from '../components/Toolbar/Toolbar' -import { useActions } from './useActions' +import { useToolbarItems } from '../components/Toolbar/DefaultToolbar' +import { useActions } from '../context/actions' import { useReadonly } from './useReadonly' import { useTools } from './useTools' @@ -19,7 +19,7 @@ const SKIP_KBDS = [ export function useKeyboardShortcuts() { const editor = useEditor() - const isReadonly = useReadonly() + const isReadonlyMode = useReadonly() const actions = useActions() const tools = useTools() const isFocused = useValue('is focused', () => editor.getInstanceState().isFocused, [editor]) @@ -48,7 +48,7 @@ export function useKeyboardShortcuts() { // Except those that in SKIP_KBDS! for (const action of Object.values(actions)) { if (!action.kbd) continue - if (isReadonly && !action.readonlyOk) continue + if (isReadonlyMode && !action.readonlyOk) continue if (SKIP_KBDS.includes(action.id)) continue hot(getHotkeysStringFromKbd(action.kbd), (event) => { @@ -127,7 +127,7 @@ export function useKeyboardShortcuts() { return () => { hotkeys.deleteScope(editor.store.id) } - }, [actions, tools, isReadonly, editor, isFocused, toolbarItemsInPanel]) + }, [actions, tools, isReadonlyMode, editor, isFocused, toolbarItemsInPanel]) } function getHotkeysStringFromKbd(kbd: string) { diff --git a/packages/tldraw/src/lib/ui/hooks/useKeyboardShortcutsSchema.tsx b/packages/tldraw/src/lib/ui/hooks/useKeyboardShortcutsSchema.tsx deleted file mode 100644 index e6e1a545d..000000000 --- a/packages/tldraw/src/lib/ui/hooks/useKeyboardShortcutsSchema.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { Editor, compact, track, useEditor } from '@tldraw/editor' -import React, { useMemo } from 'react' -import { TLUiMenuSchema, menuGroup, menuItem } from './menuHelpers' -import { TLUiActionsContextType, useActions } from './useActions' -import { TLUiToolsContextType, useTools } from './useTools' - -/** @public */ -export type TLUiKeyboardShortcutsSchemaContextType = TLUiMenuSchema - -/** @internal */ -export const KeyboardShortcutsSchemaContext = React.createContext( - {} as TLUiKeyboardShortcutsSchemaContextType -) - -/** @public */ -export type TLUiKeyboardShortcutsSchemaProviderProps = { - overrides?: ( - editor: Editor, - schema: TLUiKeyboardShortcutsSchemaContextType, - more: { tools: TLUiToolsContextType; actions: TLUiActionsContextType } - ) => TLUiKeyboardShortcutsSchemaContextType - children: any -} - -/** @internal */ -export const KeyboardShortcutsSchemaProvider = track(function KeyboardShortcutsSchemaProvider({ - overrides, - children, -}: TLUiKeyboardShortcutsSchemaProviderProps) { - const editor = useEditor() - const tools = useTools() - const actions = useActions() - - const keyboardShortcutsSchema = useMemo(() => { - const keyboardShortcutsSchema = compact([ - menuGroup( - 'shortcuts-dialog.tools', - menuItem(actions['toggle-tool-lock']), - menuItem(tools['select']), - menuItem(tools['draw']), - menuItem(tools['eraser']), - menuItem(tools['hand']), - menuItem(tools['rectangle']), - menuItem(tools['ellipse']), - menuItem(tools['arrow']), - menuItem(tools['line']), - menuItem(tools['text']), - menuItem(tools['frame']), - menuItem(tools['note']), - menuItem(tools['laser']) - ), - menuGroup( - 'shortcuts-dialog.file', - menuItem(actions['insert-media']), - menuItem(actions['print']) - ), - menuGroup( - 'shortcuts-dialog.preferences', - menuItem(actions['toggle-dark-mode']), - menuItem(actions['toggle-focus-mode']), - menuItem(actions['toggle-grid']) - ), - menuGroup( - 'shortcuts-dialog.edit', - menuItem(actions['undo']), - menuItem(actions['redo']), - menuItem(actions['cut']), - menuItem(actions['copy']), - menuItem(actions['paste']), - menuItem(actions['select-all']), - menuItem(actions['delete']), - menuItem(actions['duplicate']), - menuItem(actions['export-as-svg']), - menuItem(actions['export-as-png']) - ), - - menuGroup( - 'shortcuts-dialog.view', - menuItem(actions['zoom-in']), - menuItem(actions['zoom-out']), - menuItem(actions['zoom-to-100']), - menuItem(actions['zoom-to-fit']), - menuItem(actions['zoom-to-selection']) - ), - menuGroup( - 'shortcuts-dialog.transform', - menuItem(actions['bring-to-front']), - menuItem(actions['bring-forward']), - menuItem(actions['send-backward']), - menuItem(actions['send-to-back']), - menuItem(actions['group']), - menuItem(actions['ungroup']), - menuItem(actions['flip-horizontal']), - menuItem(actions['flip-vertical']), - menuItem(actions['align-top']), - menuItem(actions['align-center-vertical']), - menuItem(actions['align-bottom']), - menuItem(actions['align-left']), - menuItem(actions['align-center-horizontal']), - menuItem(actions['align-right']) - ), - ]) - - if (overrides) { - return overrides(editor, keyboardShortcutsSchema, { tools, actions }) - } - - return keyboardShortcutsSchema - }, [editor, overrides, actions, tools]) - - return ( - - {children} - - ) -}) - -/** @public */ -export function useKeyboardShortcutsSchema(): TLUiKeyboardShortcutsSchemaContextType { - const ctx = React.useContext(KeyboardShortcutsSchemaContext) - - if (!ctx) { - throw new Error('Shortcuts must be used inside of a ShortcutsProvider.') - } - - return ctx -} diff --git a/packages/tldraw/src/lib/ui/hooks/useMenuIsOpen.ts b/packages/tldraw/src/lib/ui/hooks/useMenuIsOpen.ts index cd0ae51ee..3f4e214a2 100644 --- a/packages/tldraw/src/lib/ui/hooks/useMenuIsOpen.ts +++ b/packages/tldraw/src/lib/ui/hooks/useMenuIsOpen.ts @@ -1,6 +1,6 @@ import { useEditor, useValue } from '@tldraw/editor' import { useCallback, useEffect, useRef } from 'react' -import { useUiEvents } from './useEventsProvider' +import { useUiEvents } from '../context/events' /** @public */ export function useMenuIsOpen(id: string, cb?: (isOpen: boolean) => void) { diff --git a/packages/tldraw/src/lib/ui/hooks/useMenuSchema.tsx b/packages/tldraw/src/lib/ui/hooks/useMenuSchema.tsx deleted file mode 100644 index 85df243bf..000000000 --- a/packages/tldraw/src/lib/ui/hooks/useMenuSchema.tsx +++ /dev/null @@ -1,285 +0,0 @@ -import { Editor, TLBookmarkShape, TLEmbedShape, useEditor, useValue } from '@tldraw/editor' -import React, { useMemo } from 'react' -import { getEmbedInfo } from '../../utils/embeds/embeds' -import { - TLUiMenuSchema, - compactMenuItems, - menuCustom, - menuGroup, - menuItem, - menuSubmenu, - showMenuPaste, - useAllowGroup, - useAllowUngroup, -} from './menuHelpers' -import { useActions } from './useActions' -import { useBreakpoint } from './useBreakpoint' -import { useCanRedo } from './useCanRedo' -import { useCanUndo } from './useCanUndo' -import { useHasLinkShapeSelected } from './useHasLinkShapeSelected' -import { useShowAutoSizeToggle } from './useShowAutoSizeToggle' - -/** @public */ -export type TLUiMenuSchemaContextType = TLUiMenuSchema - -/** @internal */ -export const TLUiMenuSchemaContext = React.createContext({} as TLUiMenuSchemaContextType) - -/** @public */ -export type TLUiMenuSchemaProviderProps = { - overrides?: ( - editor: Editor, - schema: TLUiMenuSchemaContextType, - helpers: { - actions: ReturnType - noneSelected: boolean - oneSelected: boolean - twoSelected: boolean - threeSelected: boolean - } - ) => TLUiMenuSchemaContextType - children: any -} - -/** @internal */ -export function TLUiMenuSchemaProvider({ overrides, children }: TLUiMenuSchemaProviderProps) { - const editor = useEditor() - const actions = useActions() - - const breakpoint = useBreakpoint() - const isMobile = breakpoint < 5 - - const isDarkMode = useValue('isDarkMode', () => editor.user.getIsDarkMode(), [editor]) - const animationSpeed = useValue('animationSpeed', () => editor.user.getAnimationSpeed(), [editor]) - const edgeScrollSpeed = useValue('edgeScrollSpeed', () => editor.user.getEdgeScrollSpeed(), [ - editor, - ]) - const isGridMode = useValue('isGridMode', () => editor.getInstanceState().isGridMode, [editor]) - const isSnapMode = useValue('isSnapMode', () => editor.user.getIsSnapMode(), [editor]) - const isToolLock = useValue('isToolLock', () => editor.getInstanceState().isToolLocked, [editor]) - const isFocusMode = useValue('isFocusMode', () => editor.getInstanceState().isFocusMode, [editor]) - const isDebugMode = useValue('isDebugMode', () => editor.getInstanceState().isDebugMode, [editor]) - const exportBackground = useValue( - 'exportBackground', - () => editor.getInstanceState().exportBackground, - [editor] - ) - - const emptyPage = useValue('emptyPage', () => editor.getCurrentPageShapeIds().size === 0, [ - editor, - ]) - - const selectedCount = useValue('selectedCount', () => editor.getSelectedShapeIds().length, [ - editor, - ]) - const noneSelected = selectedCount === 0 - const oneSelected = selectedCount > 0 - const twoSelected = selectedCount > 1 - const threeSelected = selectedCount > 2 - - const hasClipboardWrite = Boolean(window.navigator.clipboard?.write) - const showEditLink = useHasLinkShapeSelected() - const showAutoSizeToggle = useShowAutoSizeToggle() - const allowGroup = useAllowGroup() - const allowUngroup = useAllowUngroup() - const canUndo = useCanUndo() - const canRedo = useCanRedo() - const isZoomedTo100 = useValue('isZoomedTo100', () => editor.getZoomLevel() === 1, [editor]) - - const oneEmbedSelected = useValue( - 'oneEmbedSelected', - () => { - const onlySelectedShape = editor.getOnlySelectedShape() - if (!onlySelectedShape) return false - return !!( - editor.isShapeOfType(onlySelectedShape, 'embed') && - onlySelectedShape.props.url && - !editor.isShapeOrAncestorLocked(onlySelectedShape) - ) - }, - [] - ) - - const oneEmbeddableBookmarkSelected = useValue( - 'oneEmbeddableBookmarkSelected', - () => { - const onlySelectedShape = editor.getOnlySelectedShape() - if (!onlySelectedShape) return false - return !!( - editor.isShapeOfType(onlySelectedShape, 'bookmark') && - onlySelectedShape.props.url && - getEmbedInfo(onlySelectedShape.props.url) && - !editor.isShapeOrAncestorLocked(onlySelectedShape) - ) - }, - [] - ) - - const menuSchema = useMemo(() => { - const menuSchema: TLUiMenuSchema = compactMenuItems([ - menuGroup( - 'menu', - menuSubmenu( - 'file', - 'menu.file', - menuGroup('print', menuItem(actions['print'], { disabled: emptyPage })) - ), - menuSubmenu( - 'edit', - 'menu.edit', - menuGroup( - 'undo-actions', - menuItem(actions['undo'], { disabled: !canUndo }), - menuItem(actions['redo'], { disabled: !canRedo }) - ), - menuGroup( - 'clipboard-actions', - menuItem(actions['cut'], { disabled: noneSelected }), - menuItem(actions['copy'], { disabled: noneSelected }), - menuItem(actions['paste'], { disabled: !showMenuPaste }) - ), - menuGroup( - 'conversions', - menuSubmenu( - 'copy-as', - 'menu.copy-as', - menuGroup( - 'copy-as-group', - menuItem(actions['copy-as-svg'], { disabled: emptyPage }), - menuItem(actions['copy-as-png'], { disabled: emptyPage || !hasClipboardWrite }), - menuItem(actions['copy-as-json'], { disabled: emptyPage }) - ), - menuGroup( - 'export-bg', - menuItem(actions['toggle-transparent'], { checked: !exportBackground }) - ) - ), - menuSubmenu( - 'export-as', - 'menu.export-as', - menuGroup( - 'export-as-group', - menuItem(actions['export-as-svg'], { disabled: emptyPage }), - menuItem(actions['export-as-png'], { disabled: emptyPage }), - menuItem(actions['export-as-json'], { disabled: emptyPage }) - ), - menuGroup( - 'export-bg', - menuItem(actions['toggle-transparent'], { checked: !exportBackground }) - ) - ) - ), - menuGroup( - 'set-selection-group', - menuItem(actions['select-all'], { disabled: emptyPage }), - menuItem(actions['select-none'], { disabled: !oneSelected }) - ), - menuGroup( - 'selection', - showAutoSizeToggle && menuItem(actions['toggle-auto-size']), - showEditLink && menuItem(actions['edit-link']), - menuItem(actions['duplicate'], { disabled: !oneSelected }), - allowGroup && menuItem(actions['group']), - allowUngroup && menuItem(actions['ungroup']), - menuItem(actions['unlock-all'], { disabled: emptyPage }) - ), - menuGroup('delete-group', menuItem(actions['delete'], { disabled: !oneSelected })), - menuGroup( - 'embeds', - oneEmbedSelected && menuItem(actions['open-embed-link']), - oneEmbedSelected && menuItem(actions['convert-to-bookmark']), - oneEmbeddableBookmarkSelected && menuItem(actions['convert-to-embed']) - ) - ), - menuSubmenu( - 'view', - 'menu.view', - menuGroup( - 'view-actions', - menuItem(actions['zoom-in']), - menuItem(actions['zoom-out']), - menuItem(actions['zoom-to-100'], { disabled: isZoomedTo100 }), - menuItem(actions['zoom-to-fit'], { disabled: emptyPage }), - menuItem(actions['zoom-to-selection'], { disabled: emptyPage || !oneSelected }) - ) - ) - ), - menuGroup('extras', menuItem(actions['insert-embed']), menuItem(actions['insert-media'])), - menuGroup( - 'preferences', - menuSubmenu( - 'preferences', - 'menu.preferences', - menuGroup( - 'preferences-actions', - menuItem(actions['toggle-snap-mode'], { checked: isSnapMode }), - menuItem(actions['toggle-tool-lock'], { checked: isToolLock }), - menuItem(actions['toggle-grid'], { checked: isGridMode }), - menuItem(actions['toggle-dark-mode'], { checked: isDarkMode }), - menuItem(actions['toggle-focus-mode'], { checked: isFocusMode }), - menuItem(actions['toggle-edge-scrolling'], { checked: edgeScrollSpeed === 1 }), - menuItem(actions['toggle-reduce-motion'], { checked: animationSpeed === 0 }), - menuItem(actions['toggle-debug-mode'], { checked: isDebugMode }) - ) - ), - isMobile && menuCustom('LANGUAGE_MENU', { readonlyOk: true }) - ), - ]) - - if (overrides) { - return overrides(editor, menuSchema, { - actions, - noneSelected, - oneSelected, - twoSelected, - threeSelected, - }) - } - - return menuSchema - }, [ - editor, - overrides, - actions, - oneSelected, - twoSelected, - threeSelected, - emptyPage, - isMobile, - allowGroup, - allowUngroup, - showEditLink, - hasClipboardWrite, - showAutoSizeToggle, - noneSelected, - canUndo, - canRedo, - animationSpeed, - isDarkMode, - isGridMode, - isSnapMode, - isToolLock, - isFocusMode, - exportBackground, - isDebugMode, - edgeScrollSpeed, - isZoomedTo100, - oneEmbeddableBookmarkSelected, - oneEmbedSelected, - ]) - - return ( - {children} - ) -} - -/** @public */ -export function useMenuSchema(): TLUiMenuSchema { - const ctx = React.useContext(TLUiMenuSchemaContext) - - if (!ctx) { - throw new Error('useMenuSchema must be used inside of a TLUiMenuSchemaProvider.') - } - - return ctx -} diff --git a/packages/tldraw/src/lib/ui/hooks/useOnlyFlippableShape.ts b/packages/tldraw/src/lib/ui/hooks/useOnlyFlippableShape.ts deleted file mode 100644 index 662aa594c..000000000 --- a/packages/tldraw/src/lib/ui/hooks/useOnlyFlippableShape.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { - TLArrowShape, - TLDrawShape, - TLGroupShape, - TLLineShape, - useEditor, - useValue, -} from '@tldraw/editor' - -export function useOnlyFlippableShape() { - const editor = useEditor() - return useValue( - 'onlyFlippableShape', - () => { - const selectedShapes = editor.getSelectedShapes() - return ( - selectedShapes.length === 1 && - selectedShapes.every( - (shape) => - editor.isShapeOfType(shape, 'group') || - editor.isShapeOfType(shape, 'arrow') || - editor.isShapeOfType(shape, 'line') || - editor.isShapeOfType(shape, 'draw') - ) - ) - }, - [editor] - ) -} diff --git a/packages/tldraw/src/lib/ui/hooks/usePreloadIcons.ts b/packages/tldraw/src/lib/ui/hooks/usePreloadIcons.ts index b1f4845a2..ee7d1aa37 100644 --- a/packages/tldraw/src/lib/ui/hooks/usePreloadIcons.ts +++ b/packages/tldraw/src/lib/ui/hooks/usePreloadIcons.ts @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react' +import { useAssetUrls } from '../context/asset-urls' import { iconTypes } from '../icon-types' -import { useAssetUrls } from './useAssetUrls' /** @internal */ export function usePreloadIcons(): boolean { diff --git a/packages/tldraw/src/lib/ui/hooks/usePrint.ts b/packages/tldraw/src/lib/ui/hooks/usePrint.ts index 5b98a16f7..08f7e8c68 100644 --- a/packages/tldraw/src/lib/ui/hooks/usePrint.ts +++ b/packages/tldraw/src/lib/ui/hooks/usePrint.ts @@ -12,6 +12,7 @@ export function usePrint() { const el = document.createElement('div') const style = document.createElement('style') + // todo: why are these using a ref? this seems like it could be a function rather than a hook const clearElements = (printEl: HTMLDivElement | null, styleEl: HTMLStyleElement | null) => { if (printEl) printEl.innerHTML = '' if (styleEl && document.head.contains(styleEl)) document.head.removeChild(styleEl) diff --git a/packages/tldraw/src/lib/ui/hooks/useShowAutoSizeToggle.ts b/packages/tldraw/src/lib/ui/hooks/useShowAutoSizeToggle.ts deleted file mode 100644 index 6fc3ccd66..000000000 --- a/packages/tldraw/src/lib/ui/hooks/useShowAutoSizeToggle.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { TLTextShape, useEditor, useValue } from '@tldraw/editor' - -export function useShowAutoSizeToggle() { - const editor = useEditor() - return useValue( - 'showAutoSizeToggle', - () => { - const selectedShapes = editor.getSelectedShapes() - return ( - selectedShapes.length === 1 && - editor.isShapeOfType(selectedShapes[0], 'text') && - selectedShapes[0].props.autoSize === false - ) - }, - [editor] - ) -} diff --git a/packages/tldraw/src/lib/ui/hooks/useToolbarSchema.tsx b/packages/tldraw/src/lib/ui/hooks/useToolbarSchema.tsx index 595a1a7f7..5b4cfc51d 100644 --- a/packages/tldraw/src/lib/ui/hooks/useToolbarSchema.tsx +++ b/packages/tldraw/src/lib/ui/hooks/useToolbarSchema.tsx @@ -6,7 +6,7 @@ import { TLUiToolItem, TLUiToolsContextType, useTools } from './useTools' export type TLUiToolbarItem = { id: string type: 'item' - readonlyOk: boolean + readonlyOk?: boolean toolItem: TLUiToolItem } diff --git a/packages/tldraw/src/lib/ui/hooks/useTools.tsx b/packages/tldraw/src/lib/ui/hooks/useTools.tsx index 9817a46de..30500a3bd 100644 --- a/packages/tldraw/src/lib/ui/hooks/useTools.tsx +++ b/packages/tldraw/src/lib/ui/hooks/useTools.tsx @@ -1,9 +1,9 @@ import { Editor, GeoShapeGeoStyle, useEditor } from '@tldraw/editor' import * as React from 'react' import { EmbedDialog } from '../components/EmbedDialog' +import { useDialogs } from '../context/dialogs' +import { TLUiEventSource, useUiEvents } from '../context/events' import { TLUiIconType } from '../icon-types' -import { useDialogs } from './useDialogsProvider' -import { TLUiEventSource, useUiEvents } from './useEventsProvider' import { useInsertMedia } from './useInsertMedia' import { TLUiTranslationKey } from './useTranslation/TLUiTranslationKey' @@ -18,7 +18,7 @@ export interface TLUiToolItem< icon: IconType onSelect: (source: TLUiEventSource) => void kbd?: string - readonlyOk: boolean + readonlyOk?: boolean meta?: { [key: string]: any } @@ -77,7 +77,6 @@ export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) { label: 'tool.eraser', icon: 'tool-eraser', kbd: 'e', - readonlyOk: false, onSelect(source) { editor.setCurrentTool('eraser') trackEvent('select-tool', { source, id: 'eraser' }) @@ -86,7 +85,6 @@ export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) { { id: 'draw', label: 'tool.draw', - readonlyOk: false, icon: 'tool-pencil', kbd: 'd,b,x', onSelect(source) { @@ -97,7 +95,6 @@ export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) { ...[...GeoShapeGeoStyle.values].map((id) => ({ id, label: `tool.${id}` as TLUiTranslationKey, - readonlyOk: false, meta: { geo: id, }, @@ -122,7 +119,6 @@ export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) { { id: 'arrow', label: 'tool.arrow', - readonlyOk: false, icon: 'tool-arrow', kbd: 'a', onSelect(source) { @@ -133,7 +129,6 @@ export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) { { id: 'line', label: 'tool.line', - readonlyOk: false, icon: 'tool-line', kbd: 'l', onSelect(source) { @@ -144,7 +139,6 @@ export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) { { id: 'frame', label: 'tool.frame', - readonlyOk: false, icon: 'tool-frame', kbd: 'f', onSelect(source) { @@ -155,7 +149,6 @@ export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) { { id: 'text', label: 'tool.text', - readonlyOk: false, icon: 'tool-text', kbd: 't', onSelect(source) { @@ -166,7 +159,6 @@ export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) { { id: 'asset', label: 'tool.asset', - readonlyOk: false, icon: 'tool-media', kbd: '$u', onSelect(source) { @@ -177,7 +169,6 @@ export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) { { id: 'note', label: 'tool.note', - readonlyOk: false, icon: 'tool-note', kbd: 'n', onSelect(source) { @@ -199,7 +190,6 @@ export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) { { id: 'embed', label: 'tool.embed', - readonlyOk: false, icon: 'tool-embed', onSelect(source) { addDialog({ component: EmbedDialog }) @@ -211,7 +201,6 @@ export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) { toolsArray.push({ id: 'highlight', label: 'tool.highlight', - readonlyOk: false, icon: 'tool-highlight', // TODO: pick a better shortcut kbd: '!d', diff --git a/packages/tldraw/src/lib/ui/hooks/useTranslation/useTranslation.tsx b/packages/tldraw/src/lib/ui/hooks/useTranslation/useTranslation.tsx index 6de278f33..1f6e2be32 100644 --- a/packages/tldraw/src/lib/ui/hooks/useTranslation/useTranslation.tsx +++ b/packages/tldraw/src/lib/ui/hooks/useTranslation/useTranslation.tsx @@ -1,6 +1,6 @@ import { track, useEditor } from '@tldraw/editor' import * as React from 'react' -import { useAssetUrls } from '../useAssetUrls' +import { useAssetUrls } from '../../context/asset-urls' import { TLUiTranslationKey } from './TLUiTranslationKey' import { DEFAULT_TRANSLATION } from './defaultTranslation' import { TLUiTranslation, fetchTranslation } from './translations' @@ -105,7 +105,7 @@ export const TranslationProvider = track(function TranslationProvider({ export function useTranslation() { const translation = useCurrentTranslation() return React.useCallback( - function msg(id: Exclude | string) { + function msg(id?: Exclude | string) { return translation.messages[id as TLUiTranslationKey] ?? id }, [translation] diff --git a/packages/tldraw/src/lib/ui/overrides.ts b/packages/tldraw/src/lib/ui/overrides.ts index fdaa2a8ab..85cc8877b 100644 --- a/packages/tldraw/src/lib/ui/overrides.ts +++ b/packages/tldraw/src/lib/ui/overrides.ts @@ -1,14 +1,9 @@ import { Editor, objectMapEntries } from '@tldraw/editor' import { useMemo } from 'react' -import { ActionsProviderProps } from './hooks/useActions' -import { ActionsMenuSchemaProviderProps } from './hooks/useActionsMenuSchema' -import { useBreakpoint } from './hooks/useBreakpoint' -import { TLUiContextMenuSchemaProviderProps } from './hooks/useContextMenuSchema' -import { useDialogs } from './hooks/useDialogsProvider' -import { TLUiHelpMenuSchemaProviderProps } from './hooks/useHelpMenuSchema' -import { TLUiKeyboardShortcutsSchemaProviderProps } from './hooks/useKeyboardShortcutsSchema' -import { TLUiMenuSchemaProviderProps } from './hooks/useMenuSchema' -import { useToasts } from './hooks/useToastsProvider' +import { ActionsProviderProps } from './context/actions' +import { useBreakpoint } from './context/breakpoints' +import { useDialogs } from './context/dialogs' +import { useToasts } from './context/toasts' import { TLUiToolbarSchemaProviderProps } from './hooks/useToolbarSchema' import { TLUiToolsProviderProps } from './hooks/useTools' import { TLUiTranslationProviderProps, useTranslation } from './hooks/useTranslation/useTranslation' @@ -57,27 +52,15 @@ type WithDefaultHelpers> = /** @public */ export type TLUiOverrides = Partial<{ - actionsMenu: WithDefaultHelpers> actions: WithDefaultHelpers> - contextMenu: WithDefaultHelpers> - helpMenu: WithDefaultHelpers> - menu: WithDefaultHelpers> toolbar: WithDefaultHelpers> - keyboardShortcutsMenu: WithDefaultHelpers< - NonNullable - > tools: WithDefaultHelpers> translations: TLUiTranslationProviderProps['overrides'] }> export type TLUiOverridesWithoutDefaults = Partial<{ - actionsMenu: ActionsMenuSchemaProviderProps['overrides'] actions: ActionsProviderProps['overrides'] - contextMenu: TLUiContextMenuSchemaProviderProps['overrides'] - helpMenu: TLUiHelpMenuSchemaProviderProps['overrides'] - menu: TLUiMenuSchemaProviderProps['overrides'] toolbar: TLUiToolbarSchemaProviderProps['overrides'] - keyboardShortcutsMenu: TLUiKeyboardShortcutsSchemaProviderProps['overrides'] tools: TLUiToolsProviderProps['overrides'] translations: TLUiTranslationProviderProps['overrides'] }> @@ -99,14 +82,6 @@ export function mergeOverrides( } } return { - actionsMenu: (editor, schema, helpers) => { - for (const override of overrides) { - if (override.actionsMenu) { - schema = override.actionsMenu(editor, schema, { ...defaultHelpers, ...helpers }) - } - } - return schema - }, actions: (editor, schema) => { for (const override of overrides) { if (override.actions) { @@ -115,30 +90,6 @@ export function mergeOverrides( } return schema }, - contextMenu: (editor, schema, helpers) => { - for (const override of overrides) { - if (override.contextMenu) { - schema = override.contextMenu(editor, schema, { ...defaultHelpers, ...helpers }) - } - } - return schema - }, - helpMenu: (editor, schema, helpers) => { - for (const override of overrides) { - if (override.helpMenu) { - schema = override.helpMenu(editor, schema, { ...defaultHelpers, ...helpers }) - } - } - return schema - }, - menu: (editor, schema, helpers) => { - for (const override of overrides) { - if (override.menu) { - schema = override.menu(editor, schema, { ...defaultHelpers, ...helpers }) - } - } - return schema - }, toolbar: (editor, schema, helpers) => { for (const override of overrides) { if (override.toolbar) { @@ -147,14 +98,6 @@ export function mergeOverrides( } return schema }, - keyboardShortcutsMenu: (editor, schema, helpers) => { - for (const override of overrides) { - if (override.keyboardShortcutsMenu) { - schema = override.keyboardShortcutsMenu(editor, schema, { ...defaultHelpers, ...helpers }) - } - } - return schema - }, tools: (editor, schema, helpers) => { for (const override of overrides) { if (override.tools) { diff --git a/packages/tldraw/src/lib/utils/tldr/file.ts b/packages/tldraw/src/lib/utils/tldr/file.ts index 847445416..80ad3b077 100644 --- a/packages/tldraw/src/lib/utils/tldr/file.ts +++ b/packages/tldraw/src/lib/utils/tldr/file.ts @@ -19,7 +19,7 @@ import { partition, transact, } from '@tldraw/editor' -import { TLUiToastsContextType } from '../../ui/hooks/useToastsProvider' +import { TLUiToastsContextType } from '../../ui/context/toasts' import { TLUiTranslationKey } from '../../ui/hooks/useTranslation/TLUiTranslationKey' import { buildFromV1Document } from '../tldr/buildFromV1Document' diff --git a/packages/tldraw/src/test/ui/ContextMenu.test.tsx b/packages/tldraw/src/test/ui/ContextMenu.test.tsx index 7da72ce84..9c790367c 100644 --- a/packages/tldraw/src/test/ui/ContextMenu.test.tsx +++ b/packages/tldraw/src/test/ui/ContextMenu.test.tsx @@ -1,12 +1,8 @@ import { fireEvent, screen } from '@testing-library/react' -import { atom, createShapeId, noop } from '@tldraw/editor' -import { act } from 'react-dom/test-utils' -import { Tldraw } from '../../lib/Tldraw' -import { TLUiOverrides } from '../../lib/ui/overrides' -import { - renderTldrawComponent, - renderTldrawComponentWithEditor, -} from '../testutils/renderTldrawComponent' +import { createShapeId } from '@tldraw/editor' +import { TLComponents, Tldraw } from '../../lib/Tldraw' +import { DefaultContextMenu } from '../../lib/ui/components/ContextMenu/DefaultContextMenu' +import { renderTldrawComponent } from '../testutils/renderTldrawComponent' it('opens on right-click', async () => { await renderTldrawComponent( @@ -20,55 +16,35 @@ it('opens on right-click', async () => { fireEvent.contextMenu(canvas) await screen.findByTestId('context-menu') - await screen.findByTestId('menu-item.select-all') + await screen.findByTestId('context-menu.select-all') fireEvent.keyDown(canvas, { key: 'Escape' }) expect(screen.queryByTestId('context-menu')).toBeNull() }) -it.failing('updates overrides reactively', async () => { - const count = atom('count', 1) - const overrides: TLUiOverrides = { - contextMenu: (editor, schema) => { - if (count.get() === 0) return schema - return [ - ...schema, - { - type: 'item', - id: 'tester', - disabled: false, - readonlyOk: true, - checked: false, - actionItem: { - id: 'tester', - readonlyOk: true, - onSelect: noop, - label: `Count: ${count.get()}`, - }, - }, - ] +it('tunnels context menu', async () => { + const components: TLComponents = { + ContextMenu: (props) => { + return ( + + + + ) }, } - const { editor } = await renderTldrawComponentWithEditor((onMount) => ( - - )) + await renderTldrawComponent( + { + editor.createShape({ id: createShapeId(), type: 'geo' }) + }} + components={components} + /> + ) - await act(() => editor.createShape({ id: createShapeId(), type: 'geo' }).selectAll()) + const canvas = await screen.findByTestId('canvas') - // open the context menu: - fireEvent.contextMenu(await screen.findByTestId('canvas')) - - // check that the context menu item was added: - await screen.findByTestId('menu-item.tester') - - // It should disappear when count is 0: - await act(() => count.set(0)) - expect(screen.queryByTestId('menu-item.tester')).toBeNull() - - // It should update its label when it changes: - await act(() => count.set(1)) - const item = await screen.findByTestId('menu-item.tester') - expect(item.textContent).toBe('Count: 1') - await act(() => count.set(2)) - expect(item.textContent).toBe('Count: 2') + fireEvent.contextMenu(canvas) + await screen.findByTestId('context-menu') + const elm = await screen.findByTestId('abc123') + expect(elm).toBeDefined() }) diff --git a/packages/validate/src/index.d.ts b/packages/validate/src/index.d.ts new file mode 100644 index 000000000..cac6336fa --- /dev/null +++ b/packages/validate/src/index.d.ts @@ -0,0 +1,10 @@ +import * as T from './lib/validation' +export { + ArrayOfValidator, + DictValidator, + ObjectValidator, + UnionValidator, + Validator, +} from './lib/validation' +export { T } +//# sourceMappingURL=index.d.ts.map diff --git a/packages/validate/src/index.d.ts.map b/packages/validate/src/index.d.ts.map new file mode 100644 index 000000000..6f6f02145 --- /dev/null +++ b/packages/validate/src/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,kBAAkB,CAAA;AAErC,OAAO,EACN,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,cAAc,EACd,SAAS,GACT,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,CAAC,EAAE,CAAA"} \ No newline at end of file diff --git a/packages/validate/src/lib/validation.d.ts b/packages/validate/src/lib/validation.d.ts new file mode 100644 index 000000000..67b0d8663 --- /dev/null +++ b/packages/validate/src/lib/validation.d.ts @@ -0,0 +1,305 @@ +import { JsonValue } from '@tldraw/utils' +/** @public */ +export type ValidatorFn = (value: unknown) => T +/** @public */ +export type Validatable = { + validate: (value: unknown) => T +} +/** @public */ +export declare class ValidationError extends Error { + readonly rawMessage: string + readonly path: ReadonlyArray + name: string + constructor(rawMessage: string, path?: ReadonlyArray) +} +/** @public */ +export type TypeOf> = V extends Validatable ? T : never +/** @public */ +export declare class Validator implements Validatable { + readonly validationFn: ValidatorFn + constructor(validationFn: ValidatorFn) + /** + * Asserts that the passed value is of the correct type and returns it. The returned value is + * guaranteed to be referentially equal to the passed value. + */ + validate(value: unknown): T + /** Checks that the passed value is of the correct type. */ + isValid(value: unknown): value is T + /** + * Returns a new validator that also accepts null or undefined. The resulting value will always be + * null. + */ + nullable(): Validator + /** + * Returns a new validator that also accepts null or undefined. The resulting value will always be + * null. + */ + optional(): Validator + /** + * Refine this validation to a new type. The passed-in validation function should throw an error + * if the value can't be converted to the new type, or return the new type otherwise. + */ + refine(otherValidationFn: (value: T) => U): Validator + /** + * Refine this validation with an additional check that doesn't change the resulting value. + * + * @example + * + * ```ts + * const numberLessThan10Validator = T.number.check((value) => { + * if (value >= 10) { + * throw new ValidationError(`Expected number less than 10, got ${value}`) + * } + * }) + * ``` + */ + check(name: string, checkFn: (value: T) => void): Validator + check(checkFn: (value: T) => void): Validator +} +/** @public */ +export declare class ArrayOfValidator extends Validator { + readonly itemValidator: Validatable + constructor(itemValidator: Validatable) + nonEmpty(): Validator + lengthGreaterThan1(): Validator +} +/** @public */ +export declare class ObjectValidator extends Validator { + readonly config: { + readonly [K in keyof Shape]: Validatable + } + private readonly shouldAllowUnknownProperties + constructor( + config: { + readonly [K in keyof Shape]: Validatable + }, + shouldAllowUnknownProperties?: boolean + ) + allowUnknownProperties(): ObjectValidator + /** + * Extend an object validator by adding additional properties. + * + * @example + * + * ```ts + * const animalValidator = T.object({ + * name: T.string, + * }) + * const catValidator = animalValidator.extend({ + * meowVolume: T.number, + * }) + * ``` + */ + extend>(extension: { + readonly [K in keyof Extension]: Validatable + }): ObjectValidator +} +type UnionValidatorConfig = { + readonly [Variant in keyof Config]: Validatable & { + validate: (input: any) => { + readonly [K in Key]: Variant + } + } +} +/** @public */ +export declare class UnionValidator< + Key extends string, + Config extends UnionValidatorConfig, + UnknownValue = never, +> extends Validator | UnknownValue> { + private readonly key + private readonly config + private readonly unknownValueValidation + constructor( + key: Key, + config: Config, + unknownValueValidation: (value: object, variant: string) => UnknownValue + ) + validateUnknownVariants( + unknownValueValidation: (value: object, variant: string) => Unknown + ): UnionValidator +} +/** @public */ +export declare class DictValidator extends Validator< + Record +> { + readonly keyValidator: Validatable + readonly valueValidator: Validatable + constructor(keyValidator: Validatable, valueValidator: Validatable) +} +/** + * Validation that accepts any value. Useful as a starting point for building your own custom + * validations. + * + * @public + */ +export declare const unknown: Validator +/** + * Validation that accepts any value. Generally this should be avoided, but you can use it as an + * escape hatch if you want to work without validations for e.g. a prototype. + * + * @public + */ +export declare const any: Validator +/** + * Validates that a value is a string. + * + * @public + */ +export declare const string: Validator +/** + * Validates that a value is a finite non-NaN number. + * + * @public + */ +export declare const number: Validator +/** + * Fails if value \< 0 + * + * @public + */ +export declare const positiveNumber: Validator +/** + * Fails if value \<= 0 + * + * @public + */ +export declare const nonZeroNumber: Validator +/** + * Fails if number is not an integer + * + * @public + */ +export declare const integer: Validator +/** + * Fails if value \< 0 and is not an integer + * + * @public + */ +export declare const positiveInteger: Validator +/** + * Fails if value \<= 0 and is not an integer + * + * @public + */ +export declare const nonZeroInteger: Validator +/** + * Validates that a value is boolean. + * + * @public + */ +export declare const boolean: Validator +/** + * Validates that a value is a bigint. + * + * @public + */ +export declare const bigint: Validator +/** + * Validates that a value matches another that was passed in. + * + * @example + * + * ```ts + * const trueValidator = T.literal(true) + * ``` + * + * @public + */ +export declare function literal(expectedValue: T): Validator +/** + * Validates that a value is an array. To check the contents of the array, use T.arrayOf. + * + * @public + */ +export declare const array: Validator +/** + * Validates that a value is an array whose contents matches the passed-in validator. + * + * @public + */ +export declare function arrayOf(itemValidator: Validatable): ArrayOfValidator +/** @public */ +export declare const unknownObject: Validator> +/** + * Validate an object has a particular shape. + * + * @public + */ +export declare function object(config: { + readonly [K in keyof Shape]: Validatable +}): ObjectValidator +/** + * Validate that a value is valid JSON. + * + * @public + */ +export declare const jsonValue: Validator +/** + * Validate an object has a particular shape. + * + * @public + */ +export declare function jsonDict(): DictValidator +/** + * Validation that an option is a dict with particular keys and values. + * + * @public + */ +export declare function dict( + keyValidator: Validatable, + valueValidator: Validatable +): DictValidator +/** + * Validate a union of several object types. Each object must have a property matching `key` which + * should be a unique string. + * + * @example + * + * ```ts + * const catValidator = T.object({ kind: T.value('cat'), meow: T.boolean }) + * const dogValidator = T.object({ kind: T.value('dog'), bark: T.boolean }) + * const animalValidator = T.union('kind', { cat: catValidator, dog: dogValidator }) + * ``` + * + * @public + */ +export declare function union>( + key: Key, + config: Config +): UnionValidator +/** + * A named object with an ID. Errors will be reported as being part of the object with the given + * name. + * + * @public + */ +export declare function model< + T extends { + readonly id: string + }, +>(name: string, validator: Validatable): Validator +/** @public */ +export declare function setEnum(values: ReadonlySet): Validator +/** @public */ +export declare function optional(validator: Validatable): Validator +/** @public */ +export declare function nullable(validator: Validatable): Validator +/** @public */ +export declare function literalEnum( + ...values: Values +): Validator +/** + * Validates that a value is a url safe to use as a link. + * + * @public + */ +export declare const linkUrl: Validator +/** + * Validates that a valid is a url safe to load as an asset. + * + * @public + */ +export declare const srcUrl: Validator +export {} +//# sourceMappingURL=validation.d.ts.map diff --git a/packages/validate/src/lib/validation.d.ts.map b/packages/validate/src/lib/validation.d.ts.map new file mode 100644 index 000000000..b11243d65 --- /dev/null +++ b/packages/validate/src/lib/validation.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAyD,MAAM,eAAe,CAAA;AAEhG,cAAc;AACd,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC,CAAA;AAElD,cAAc;AACd,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC,CAAA;CAAE,CAAA;AA+BhE,cAAc;AACd,qBAAa,eAAgB,SAAQ,KAAK;aAIxB,UAAU,EAAE,MAAM;aAClB,IAAI,EAAE,aAAa,CAAC,MAAM,GAAG,MAAM,CAAC;IAJ5C,IAAI,SAAoB;gBAGhB,UAAU,EAAE,MAAM,EAClB,IAAI,GAAE,aAAa,CAAC,MAAM,GAAG,MAAM,CAAM;CAS1D;AAkCD,cAAc;AACd,MAAM,MAAM,MAAM,CAAC,CAAC,SAAS,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;AAE/F,cAAc;AACd,qBAAa,SAAS,CAAC,CAAC,CAAE,YAAW,WAAW,CAAC,CAAC,CAAC;IACtC,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC;gBAA5B,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC;IAEjD;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,CAAC;IAQ3B,2DAA2D;IAC3D,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,CAAC;IASnC;;;OAGG;IACH,QAAQ,IAAI,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC;IAI/B;;;OAGG;IACH,QAAQ,IAAI,SAAS,CAAC,CAAC,GAAG,SAAS,CAAC;IAIpC;;;OAGG;IACH,MAAM,CAAC,CAAC,EAAE,iBAAiB,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC;IAM3D;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC;IAC9D,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC;CAchD;AAED,cAAc;AACd,qBAAa,gBAAgB,CAAC,CAAC,CAAE,SAAQ,SAAS,CAAC,CAAC,EAAE,CAAC;IAC1C,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC;gBAA7B,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC;IAUlD,QAAQ;IAQR,kBAAkB;CAOlB;AAED,cAAc;AACd,qBAAa,eAAe,CAAC,KAAK,SAAS,MAAM,CAAE,SAAQ,SAAS,CAAC,KAAK,CAAC;aAEzD,MAAM,EAAE;QACvB,QAAQ,EAAE,CAAC,IAAI,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;KAClD;IACD,OAAO,CAAC,QAAQ,CAAC,4BAA4B;gBAH7B,MAAM,EAAE;QACvB,QAAQ,EAAE,CAAC,IAAI,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;KAClD,EACgB,4BAA4B,UAAQ;IAyBtD,sBAAsB;IAItB;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE;QAC5D,QAAQ,EAAE,CAAC,IAAI,MAAM,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;KAC1D,GAAG,eAAe,CAAC,KAAK,GAAG,SAAS,CAAC;CAKtC;AAGD,KAAK,oBAAoB,CAAC,GAAG,SAAS,MAAM,EAAE,MAAM,IAAI;IACvD,QAAQ,EAAE,OAAO,IAAI,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG;QACtD,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK;YAAE,QAAQ,EAAE,CAAC,IAAI,GAAG,GAAG,OAAO;SAAE,CAAA;KAC1D;CACD,CAAA;AACD,cAAc;AACd,qBAAa,cAAc,CAC1B,GAAG,SAAS,MAAM,EAClB,MAAM,SAAS,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,EAChD,YAAY,GAAG,KAAK,CACnB,SAAQ,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,MAAM,CAAC,CAAC,GAAG,YAAY,CAAC;IAE9D,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,sBAAsB;gBAFtB,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,EACd,sBAAsB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,YAAY;IAuB1F,uBAAuB,CAAC,OAAO,EAC9B,sBAAsB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,GACjE,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC;CAGvC;AAED,cAAc;AACd,qBAAa,aAAa,CAAC,GAAG,SAAS,MAAM,EAAE,KAAK,CAAE,SAAQ,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;aAEzE,YAAY,EAAE,WAAW,CAAC,GAAG,CAAC;aAC9B,cAAc,EAAE,WAAW,CAAC,KAAK,CAAC;gBADlC,YAAY,EAAE,WAAW,CAAC,GAAG,CAAC,EAC9B,cAAc,EAAE,WAAW,CAAC,KAAK,CAAC;CAiBnD;AAWD;;;;;GAKG;AACH,eAAO,MAAM,OAAO,oBAAkC,CAAA;AACtD;;;;;GAKG;AACH,eAAO,MAAM,GAAG,gBAAuC,CAAA;AAEvD;;;;GAIG;AACH,eAAO,MAAM,MAAM,mBAAoC,CAAA;AAEvD;;;;GAIG;AACH,eAAO,MAAM,MAAM,mBAOjB,CAAA;AACF;;;;GAIG;AACH,eAAO,MAAM,cAAc,mBAEzB,CAAA;AACF;;;;GAIG;AACH,eAAO,MAAM,aAAa,mBAExB,CAAA;AACF;;;;GAIG;AACH,eAAO,MAAM,OAAO,mBAElB,CAAA;AACF;;;;GAIG;AACH,eAAO,MAAM,eAAe,mBAE1B,CAAA;AACF;;;;GAIG;AACH,eAAO,MAAM,cAAc,mBAEzB,CAAA;AAEF;;;;GAIG;AACH,eAAO,MAAM,OAAO,oBAAsC,CAAA;AAC1D;;;;GAIG;AACH,eAAO,MAAM,MAAM,mBAAoC,CAAA;AACvD;;;;;;;;;;GAUG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,aAAa,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAO3F;AAED;;;;GAIG;AACH,eAAO,MAAM,KAAK,sBAKhB,CAAA;AAEF;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAE7E;AAED,cAAc;AACd,eAAO,MAAM,aAAa,oCAKxB,CAAA;AAEF;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,KAAK,SAAS,MAAM,EAAE,MAAM,EAAE;IACpD,QAAQ,EAAE,CAAC,IAAI,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;CAClD,GAAG,eAAe,CAAC,KAAK,CAAC,CAEzB;AAuBD;;;;GAIG;AACH,eAAO,MAAM,SAAS,sBAMpB,CAAA;AAEF;;;;GAIG;AACH,wBAAgB,QAAQ,IAAI,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,CAE3D;AAED;;;;GAIG;AACH,wBAAgB,IAAI,CAAC,GAAG,SAAS,MAAM,EAAE,KAAK,EAC7C,YAAY,EAAE,WAAW,CAAC,GAAG,CAAC,EAC9B,cAAc,EAAE,WAAW,CAAC,KAAK,CAAC,GAChC,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,CAE3B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,KAAK,CAAC,GAAG,SAAS,MAAM,EAAE,MAAM,SAAS,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,EACzF,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,GACZ,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,CAS7B;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EACtD,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC,GACvB,SAAS,CAAC,CAAC,CAAC,CASd;AAED,cAAc;AACd,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAQ/D;AAED,cAAc;AACd,wBAAgB,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,GAAG,SAAS,CAAC,CAK/E;AAED,cAAc;AACd,wBAAgB,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAK1E;AAED,cAAc;AACd,wBAAgB,WAAW,CAAC,KAAK,CAAC,MAAM,SAAS,SAAS,OAAO,EAAE,EAClE,GAAG,MAAM,EAAE,MAAM,GACf,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAE3B;AAmBD;;;;GAIG;AACH,eAAO,MAAM,OAAO,mBASlB,CAAA;AAIF;;;;GAIG;AACH,eAAO,MAAM,MAAM,mBASjB,CAAA"} \ No newline at end of file diff --git a/packages/validate/src/test/validation.test.d.ts b/packages/validate/src/test/validation.test.d.ts new file mode 100644 index 000000000..b13db3b64 --- /dev/null +++ b/packages/validate/src/test/validation.test.d.ts @@ -0,0 +1,2 @@ +export {} +//# sourceMappingURL=validation.test.d.ts.map diff --git a/packages/validate/src/test/validation.test.d.ts.map b/packages/validate/src/test/validation.test.d.ts.map new file mode 100644 index 000000000..cac81a670 --- /dev/null +++ b/packages/validate/src/test/validation.test.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"validation.test.d.ts","sourceRoot":"","sources":["validation.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/scripts/build-api.ts b/scripts/build-api.ts index 71f1dedbc..8d7c1907a 100644 --- a/scripts/build-api.ts +++ b/scripts/build-api.ts @@ -42,10 +42,7 @@ async function buildApi(sourcePackageDir: string) { }, }) } catch (e) { - console.error(e) - // get git diff - console.error('api-report diff') await exec('git', [ 'diff', '--no-color', @@ -55,6 +52,9 @@ async function buildApi(sourcePackageDir: string) { 'api/temp/api-report.md', ]) + // @ts-expect-error + console.error(e.message) + process.exit(1) } }