Cleanup @tldraw/ui types / exports (#1504)

This PR cleans up exports from TldrawUi, unifying types under `TLUi` and
removing many items from exports / marking others as internal.

### Change Type

- [x] `major` — Breaking Change

### Release Notes

- [editor] clean up / unify types
This commit is contained in:
Steve Ruiz 2023-06-02 22:16:09 +01:00 committed by GitHub
parent 735f1c41b7
commit a5e653b225
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 807 additions and 1255 deletions

View file

@ -1,4 +1,4 @@
import { MenuGroup, Tldraw, menuItem, toolbarItem } from '@tldraw/tldraw' import { TLUiMenuGroup, Tldraw, menuItem, toolbarItem } from '@tldraw/tldraw'
import '@tldraw/tldraw/editor.css' import '@tldraw/tldraw/editor.css'
import '@tldraw/tldraw/ui.css' import '@tldraw/tldraw/ui.css'
import { CardTool } from './CardTool' import { CardTool } from './CardTool'
@ -44,7 +44,7 @@ export default function CustomConfigExample() {
// add it to that before returning the array. // add it to that before returning the array.
const toolsGroup = keyboardShortcutsMenu.find( const toolsGroup = keyboardShortcutsMenu.find(
(group) => group.id === 'shortcuts-dialog.tools' (group) => group.id === 'shortcuts-dialog.tools'
) as MenuGroup ) as TLUiMenuGroup
toolsGroup.children.push(menuItem(tools.card)) toolsGroup.children.push(menuItem(tools.card))
return keyboardShortcutsMenu return keyboardShortcutsMenu
}, },

View file

@ -9,7 +9,7 @@ import {
import { linksUiOverrides } from './utils/links' import { linksUiOverrides } from './utils/links'
// eslint-disable-next-line import/no-internal-modules // eslint-disable-next-line import/no-internal-modules
import '@tldraw/editor/editor.css' import '@tldraw/editor/editor.css'
import { ContextMenu, MenuSchema, TldrawUi } from '@tldraw/ui' import { ContextMenu, TLUiMenuSchema, TldrawUi } from '@tldraw/ui'
// eslint-disable-next-line import/no-internal-modules // eslint-disable-next-line import/no-internal-modules
import '@tldraw/ui/ui.css' import '@tldraw/ui/ui.css'
// eslint-disable-next-line import/no-internal-modules // eslint-disable-next-line import/no-internal-modules
@ -64,7 +64,7 @@ export function WrappedTldrawEditor() {
} }
const menuOverrides = { const menuOverrides = {
menu: (_editor: Editor, schema: MenuSchema, _helpers: any) => { menu: (_editor: Editor, schema: TLUiMenuSchema, _helpers: any) => {
schema.forEach((item) => { schema.forEach((item) => {
if (item.id === 'menu' && item.type === 'group') { if (item.id === 'menu' && item.type === 'group') {
item.children = item.children.filter((menuItem) => { item.children = item.children.filter((menuItem) => {

View file

@ -1,4 +1,4 @@
import { menuGroup, menuItem, TldrawUiOverrides } from '@tldraw/ui' import { menuGroup, menuItem, TLUiOverrides } from '@tldraw/ui'
import { openUrl } from './openUrl' import { openUrl } from './openUrl'
export const GITHUB_URL = 'https://github.com/tldraw/tldraw' export const GITHUB_URL = 'https://github.com/tldraw/tldraw'
@ -43,7 +43,7 @@ const linksMenuGroup = menuGroup(
}) })
)! )!
export const linksUiOverrides: TldrawUiOverrides = { export const linksUiOverrides: TLUiOverrides = {
helpMenu(editor, schema) { helpMenu(editor, schema) {
schema.push(linksMenuGroup) schema.push(linksMenuGroup)
return schema return schema

View file

@ -10,8 +10,8 @@ import { Result } from '@tldraw/utils';
import { SerializedSchema } from '@tldraw/tlstore'; import { SerializedSchema } from '@tldraw/tlstore';
import { TLInstanceId } from '@tldraw/editor'; import { TLInstanceId } from '@tldraw/editor';
import { TLStore } from '@tldraw/editor'; import { TLStore } from '@tldraw/editor';
import { TLTranslationKey } from '@tldraw/ui'; import { TLUiToastsContextType } from '@tldraw/ui';
import { ToastsContextType } from '@tldraw/ui'; import { TLUiTranslationKey } from '@tldraw/ui';
import { UnknownRecord } from '@tldraw/tlstore'; import { UnknownRecord } from '@tldraw/tlstore';
// @internal (undocumented) // @internal (undocumented)
@ -37,7 +37,7 @@ export interface LegacyTldrawDocument {
} }
// @internal (undocumented) // @internal (undocumented)
export function parseAndLoadDocument(editor: Editor, document: string, msg: (id: TLTranslationKey) => string, addToast: ToastsContextType['addToast'], onV1FileLoad?: () => void, forceDarkMode?: boolean): Promise<void>; export function parseAndLoadDocument(editor: Editor, document: string, msg: (id: TLUiTranslationKey) => string, addToast: TLUiToastsContextType['addToast'], onV1FileLoad?: () => void, forceDarkMode?: boolean): Promise<void>;
// @public (undocumented) // @public (undocumented)
export function parseTldrawJsonFile({ json, instanceId, store, }: { export function parseTldrawJsonFile({ json, instanceId, store, }: {

View file

@ -16,7 +16,7 @@ import {
UnknownRecord, UnknownRecord,
} from '@tldraw/tlstore' } from '@tldraw/tlstore'
import { T } from '@tldraw/tlvalidate' import { T } from '@tldraw/tlvalidate'
import { TLTranslationKey, ToastsContextType } from '@tldraw/ui' import { TLUiToastsContextType, TLUiTranslationKey } from '@tldraw/ui'
import { exhaustiveSwitchError, Result } from '@tldraw/utils' import { exhaustiveSwitchError, Result } from '@tldraw/utils'
import { buildFromV1Document } from './buildFromV1Document' import { buildFromV1Document } from './buildFromV1Document'
@ -204,8 +204,8 @@ export async function serializeTldrawJsonBlob(store: TLStore): Promise<Blob> {
export async function parseAndLoadDocument( export async function parseAndLoadDocument(
editor: Editor, editor: Editor,
document: string, document: string,
msg: (id: TLTranslationKey) => string, msg: (id: TLUiTranslationKey) => string,
addToast: ToastsContextType['addToast'], addToast: TLUiToastsContextType['addToast'],
onV1FileLoad?: () => void, onV1FileLoad?: () => void,
forceDarkMode?: boolean forceDarkMode?: boolean
) { ) {

View file

@ -364,7 +364,7 @@ export const geoShapeTypeValidator: T.Validator<TLGeoShape>;
export const geoValidator: T.Validator<"arrow-down" | "arrow-left" | "arrow-right" | "arrow-up" | "check-box" | "diamond" | "ellipse" | "hexagon" | "octagon" | "oval" | "pentagon" | "rectangle" | "rhombus-2" | "rhombus" | "star" | "trapezoid" | "triangle" | "x-box">; export const geoValidator: T.Validator<"arrow-down" | "arrow-left" | "arrow-right" | "arrow-up" | "check-box" | "diamond" | "ellipse" | "hexagon" | "octagon" | "oval" | "pentagon" | "rectangle" | "rhombus-2" | "rhombus" | "star" | "trapezoid" | "triangle" | "x-box">;
// @public (undocumented) // @public (undocumented)
export function getDefaultTranslationLocale(): TLTranslationLocale; export function getDefaultTranslationLocale(): TLUiTranslationLocale;
// @public (undocumented) // @public (undocumented)
export const groupShapeTypeMigrations: Migrations; export const groupShapeTypeMigrations: Migrations;

View file

@ -1,21 +1,21 @@
import { LANGUAGES } from './languages' import { LANGUAGES } from './languages'
type TLListedTranslation = { /** @public */
export type TLUiLanguage = {
readonly locale: string readonly locale: string
readonly label: string readonly label: string
} }
type TLListedTranslations = TLListedTranslation[] type TLUiTranslationLocale = TLUiLanguage['locale']
type TLTranslationLocale = TLListedTranslations[number]['locale']
/** @public */ /** @public */
export function getDefaultTranslationLocale(): TLTranslationLocale { export function getDefaultTranslationLocale(): TLUiTranslationLocale {
const locales = typeof window !== 'undefined' ? window.navigator.languages ?? ['en'] : ['en'] const locales = typeof window !== 'undefined' ? window.navigator.languages ?? ['en'] : ['en']
return _getDefaultTranslationLocale(locales) return _getDefaultTranslationLocale(locales)
} }
/** @internal */ /** @internal */
export function _getDefaultTranslationLocale(locales: readonly string[]): TLTranslationLocale { export function _getDefaultTranslationLocale(locales: readonly string[]): TLUiTranslationLocale {
for (const locale of locales) { for (const locale of locales) {
const supportedLocale = getSupportedLocale(locale) const supportedLocale = getSupportedLocale(locale)
if (supportedLocale) { if (supportedLocale) {
@ -26,7 +26,7 @@ export function _getDefaultTranslationLocale(locales: readonly string[]): TLTran
} }
/** @public */ /** @public */
const DEFAULT_LOCALE_REGIONS: { [locale: string]: TLTranslationLocale } = { const DEFAULT_LOCALE_REGIONS: { [locale: string]: TLUiTranslationLocale } = {
zh: 'zh-cn', zh: 'zh-cn',
pt: 'pt-br', pt: 'pt-br',
ko: 'ko-kr', ko: 'ko-kr',
@ -34,7 +34,7 @@ const DEFAULT_LOCALE_REGIONS: { [locale: string]: TLTranslationLocale } = {
} }
/** @public */ /** @public */
function getSupportedLocale(locale: string): TLTranslationLocale | null { function getSupportedLocale(locale: string): TLUiTranslationLocale | null {
// If we have an exact match, return it! // If we have an exact match, return it!
// (e.g. if the user has 'fr' and we have 'fr') // (e.g. if the user has 'fr' and we have 'fr')
// (or if the user has 'pt-BR' and we have 'pt-br') // (or if the user has 'pt-BR' and we have 'pt-br')

File diff suppressed because one or more lines are too long

View file

@ -1,25 +1,16 @@
import * as Dialog from './lib/components/primitives/Dialog' import * as Dialog from './lib/components/primitives/Dialog'
import * as DropdownMenu from './lib/components/primitives/DropdownMenu' import * as DropdownMenu from './lib/components/primitives/DropdownMenu'
export { TldrawUi, TldrawUiContent, type TldrawUiProps } from './lib/TldrawUi' export { TldrawUi, type TldrawUiProps } from './lib/TldrawUi'
export { export {
TldrawUiContextProvider, TldrawUiContextProvider,
type TldrawUiContextProviderProps, type TldrawUiContextProviderProps,
} from './lib/TldrawUiContextProvider' } from './lib/TldrawUiContextProvider'
export { setDefaultUiAssetUrls } from './lib/assetUrls' export { setDefaultUiAssetUrls } from './lib/assetUrls'
export { ContextMenu, type ContextMenuProps } from './lib/components/ContextMenu' export { ContextMenu, type TLUiContextMenuProps } from './lib/components/ContextMenu'
export { DebugPanel } from './lib/components/DebugPanel' export { Button, type TLUiButtonProps } from './lib/components/primitives/Button'
export { HTMLCanvas } from './lib/components/HTMLCanvas' export { Icon, type TLUiIconProps } from './lib/components/primitives/Icon'
export { HelpMenu, type HelpMenuProps } from './lib/components/HelpMenu' export { Input, type TLUiInputProps } from './lib/components/primitives/Input'
export { NavigationZone } from './lib/components/NavigationZone/NavigationZone'
export { StylePanel } from './lib/components/StylePanel/StylePanel'
export { Button, type ButtonProps } from './lib/components/primitives/Button'
export { ButtonPicker, type ButtonPickerProps } from './lib/components/primitives/ButtonPicker'
export { Icon, type IconProps } from './lib/components/primitives/Icon'
export { Input, type InputProps } from './lib/components/primitives/Input'
export { Kbd, type KbdProps } from './lib/components/primitives/Kbd'
export { Slider, type SliderProps } from './lib/components/primitives/Slider'
export { BASE_URL, getBaseUrl, kbd, kbdStr, toStartCase } from './lib/components/primitives/shared'
export { export {
compactMenuItems, compactMenuItems,
findMenuItem, findMenuItem,
@ -27,30 +18,21 @@ export {
menuGroup, menuGroup,
menuItem, menuItem,
menuSubmenu, menuSubmenu,
useAllowGroup, type TLUiCustomMenuItem,
useAllowUngroup, type TLUiMenuChild,
useThreeStackableItems, type TLUiMenuGroup,
type CustomMenuItem, type TLUiMenuItem,
type MenuChild, type TLUiMenuSchema,
type MenuGroup, type TLUiSubMenu,
type MenuItem,
type MenuSchema,
type SubMenu,
} from './lib/hooks/menuHelpers' } from './lib/hooks/menuHelpers'
export { export {
ActionsContext,
ActionsProvider,
useActions, useActions,
type ActionItem, type TLUiActionItem,
type ActionsContextType, type TLUiActionsContextType,
type ActionsProviderProps,
} from './lib/hooks/useActions' } from './lib/hooks/useActions'
export { export {
ActionsMenuSchemaContext,
ActionsMenuSchemaProvider,
useActionsMenuSchema, useActionsMenuSchema,
type ActionsMenuSchemaContextType, type TLUiActionsMenuSchemaContextType,
type ActionsMenuSchemaProviderProps,
} from './lib/hooks/useActionsMenuSchema' } from './lib/hooks/useActionsMenuSchema'
export { AssetUrlsProvider, useAssetUrls } from './lib/hooks/useAssetUrls' export { AssetUrlsProvider, useAssetUrls } from './lib/hooks/useAssetUrls'
export { BreakPointProvider, useBreakpoint } from './lib/hooks/useBreakpoint' export { BreakPointProvider, useBreakpoint } from './lib/hooks/useBreakpoint'
@ -58,101 +40,65 @@ export { useCanRedo } from './lib/hooks/useCanRedo'
export { useCanUndo } from './lib/hooks/useCanUndo' export { useCanUndo } from './lib/hooks/useCanUndo'
export { useMenuClipboardEvents, useNativeClipboardEvents } from './lib/hooks/useClipboardEvents' export { useMenuClipboardEvents, useNativeClipboardEvents } from './lib/hooks/useClipboardEvents'
export { export {
ContextMenuSchemaContext,
ContextMenuSchemaProvider,
useContextMenuSchema, useContextMenuSchema,
type ContextMenuSchemaContextType, type TLUiContextTTLUiMenuSchemaContextType,
type ContextMenuSchemaProviderProps,
} from './lib/hooks/useContextMenuSchema' } from './lib/hooks/useContextMenuSchema'
export { useCopyAs } from './lib/hooks/useCopyAs' export { useCopyAs } from './lib/hooks/useCopyAs'
export { export {
DialogsContext,
DialogsProvider,
useDialogs, useDialogs,
type DialogProps, type TLUiDialog,
type DialogsContextType, type TLUiDialogProps,
type DialogsProviderProps, type TLUiDialogsContextType,
type TLDialog,
} from './lib/hooks/useDialogsProvider' } from './lib/hooks/useDialogsProvider'
export { useEditorEvents } from './lib/hooks/useEditorEvents'
export { export {
useEvents, useEvents,
type EventsProviderProps, type TLUiEventContextType,
type TLUiEventHandler, type TLUiEventHandler,
type TLUiEventSource, type TLUiEventSource,
} from './lib/hooks/useEventsProvider' } from './lib/hooks/useEventsProvider'
export { useExportAs } from './lib/hooks/useExportAs' export { useExportAs } from './lib/hooks/useExportAs'
export { export {
HelpMenuSchemaContext,
HelpMenuSchemaProvider,
useHelpMenuSchema, useHelpMenuSchema,
type HelpMenuSchemaProviderProps, type TLUiHelpMenuSchemaContextType,
type HelpMenuSchemaProviderType,
} from './lib/hooks/useHelpMenuSchema' } from './lib/hooks/useHelpMenuSchema'
export { useHighDpiCanvas } from './lib/hooks/useHighDpiCanvas'
export { useKeyboardShortcuts } from './lib/hooks/useKeyboardShortcuts' export { useKeyboardShortcuts } from './lib/hooks/useKeyboardShortcuts'
export { export {
KeyboardShortcutsSchemaContext,
KeyboardShortcutsSchemaProvider,
useKeyboardShortcutsSchema, useKeyboardShortcutsSchema,
type KeyboardShortcutsSchemaContextType, type TLUiKeyboardShortcutsSchemaContextType,
type KeyboardShortcutsSchemaProviderProps, type TLUiKeyboardShortcutsSchemaProviderProps,
} from './lib/hooks/useKeyboardShortcutsSchema' } from './lib/hooks/useKeyboardShortcutsSchema'
export { useLocalStorageState } from './lib/hooks/useLocalStorageState' export { useLocalStorageState } from './lib/hooks/useLocalStorageState'
export { useMenuIsOpen } from './lib/hooks/useMenuIsOpen' export { useMenuIsOpen } from './lib/hooks/useMenuIsOpen'
export { export {
MenuSchemaContext,
MenuSchemaProvider,
useMenuSchema, useMenuSchema,
type MenuSchemaContextType, type TLUiMenuSchemaContextType,
type MenuSchemaProviderProps, type TLUiMenuSchemaProviderProps,
} from './lib/hooks/useMenuSchema' } from './lib/hooks/useMenuSchema'
export { usePrint } from './lib/hooks/usePrint'
export { useReadonly } from './lib/hooks/useReadonly' export { useReadonly } from './lib/hooks/useReadonly'
export { export {
ToastsContext,
ToastsProvider,
useToasts, useToasts,
type TLToast, type TLUiToast,
type TLToastAction, type TLUiToastAction,
type ToastsContextType, type TLUiToastsContextType,
type ToastsProviderProps,
} from './lib/hooks/useToastsProvider' } from './lib/hooks/useToastsProvider'
export { export {
ToolbarSchemaContext,
ToolbarSchemaProvider,
toolbarItem, toolbarItem,
useToolbarSchema, useToolbarSchema,
type ToolbarItem, type TLUiToolbarItem,
type ToolbarSchemaContextType, type TLUiToolbarSchemaContextType,
type ToolbarSchemaProviderProps,
} from './lib/hooks/useToolbarSchema' } from './lib/hooks/useToolbarSchema'
export { export {
ToolsContext,
ToolsProvider,
useTools, useTools,
type ToolItem, type TLUiToolItem,
type ToolsContextType, type TLUiToolsContextType,
type ToolsProviderProps, type TLUiToolsProviderProps,
} from './lib/hooks/useTools' } from './lib/hooks/useTools'
export type { TLTranslationKey } from './lib/hooks/useTranslation/TLTranslationKey' export { type TLUiTranslationKey } from './lib/hooks/useTranslation/TLUiTranslationKey'
export { type TLUiLanguage, type TLUiTranslation } from './lib/hooks/useTranslation/translations'
export { export {
EN_TRANSLATION, useTranslation as useTranslation,
fetchTranslation, type TLUiTranslationContextType,
getTranslation,
type TLListedTranslation,
type TLListedTranslations,
type TLTranslation,
type TLTranslationLocale,
type TLTranslationMessages,
type TLTranslations,
} from './lib/hooks/useTranslation/translations'
export { useLanguages } from './lib/hooks/useTranslation/useLanguages'
export {
TranslationProvider,
useTranslation,
type TranslationProviderProps,
} from './lib/hooks/useTranslation/useTranslation' } from './lib/hooks/useTranslation/useTranslation'
export { TLUiIconTypes, type TLUiIconType } from './lib/icon-types' export { type TLUiIconType } from './lib/icon-types'
export { useDefaultHelpers, type TldrawUiOverrides } from './lib/overrides' export { useDefaultHelpers, type TLUiOverrides } from './lib/overrides'
export { Dialog, DropdownMenu } export { Dialog, DropdownMenu }

View file

@ -85,8 +85,7 @@ const TldrawUiInner = React.memo(function TldrawUiInner({
) )
}) })
/** @public */ const TldrawUiContent = React.memo(function TldrawUI({
export const TldrawUiContent = React.memo(function TldrawUI({
shareZone, shareZone,
topZone, topZone,
renderDebugMenuItems, renderDebugMenuItems,

View file

@ -1,24 +1,24 @@
import { defaultUiAssetUrls, UiAssetUrls } from './assetUrls' import { defaultUiAssetUrls, TLUiAssetUrls } from './assetUrls'
import { ActionsProvider } from './hooks/useActions' import { ActionsProvider } from './hooks/useActions'
import { ActionsMenuSchemaProvider } from './hooks/useActionsMenuSchema' import { ActionsMenuSchemaProvider } from './hooks/useActionsMenuSchema'
import { AssetUrlsProvider } from './hooks/useAssetUrls' import { AssetUrlsProvider } from './hooks/useAssetUrls'
import { BreakPointProvider } from './hooks/useBreakpoint' import { BreakPointProvider } from './hooks/useBreakpoint'
import { ContextMenuSchemaProvider } from './hooks/useContextMenuSchema' import { TLUiContextMenuSchemaProvider } from './hooks/useContextMenuSchema'
import { DialogsProvider } from './hooks/useDialogsProvider' import { DialogsProvider } from './hooks/useDialogsProvider'
import { EventsProvider, TLUiEventHandler } from './hooks/useEventsProvider' import { EventsProvider, TLUiEventHandler } from './hooks/useEventsProvider'
import { HelpMenuSchemaProvider } from './hooks/useHelpMenuSchema' import { HelpMenuSchemaProvider } from './hooks/useHelpMenuSchema'
import { KeyboardShortcutsSchemaProvider } from './hooks/useKeyboardShortcutsSchema' import { KeyboardShortcutsSchemaProvider } from './hooks/useKeyboardShortcutsSchema'
import { MenuSchemaProvider } from './hooks/useMenuSchema' import { TLUiMenuSchemaProvider } from './hooks/useMenuSchema'
import { ToastsProvider } from './hooks/useToastsProvider' import { ToastsProvider } from './hooks/useToastsProvider'
import { ToolbarSchemaProvider } from './hooks/useToolbarSchema' import { ToolbarSchemaProvider } from './hooks/useToolbarSchema'
import { ToolsProvider } from './hooks/useTools' import { ToolsProvider } from './hooks/useTools'
import { TranslationProvider } from './hooks/useTranslation/useTranslation' import { TranslationProvider } from './hooks/useTranslation/useTranslation'
import { TldrawUiOverrides, useMergedOverrides, useMergedTranslationOverrides } from './overrides' import { TLUiOverrides, useMergedOverrides, useMergedTranslationOverrides } from './overrides'
/** @public */ /** @public */
export interface TldrawUiContextProviderProps { export interface TldrawUiContextProviderProps {
assetUrls?: UiAssetUrls assetUrls?: TLUiAssetUrls
overrides?: TldrawUiOverrides | TldrawUiOverrides[] overrides?: TLUiOverrides | TLUiOverrides[]
onUiEvent?: TLUiEventHandler onUiEvent?: TLUiEventHandler
children?: any children?: any
} }
@ -57,13 +57,13 @@ function InternalProviders({
<ToolbarSchemaProvider overrides={mergedOverrides.toolbar}> <ToolbarSchemaProvider overrides={mergedOverrides.toolbar}>
<ActionsMenuSchemaProvider overrides={mergedOverrides.actionsMenu}> <ActionsMenuSchemaProvider overrides={mergedOverrides.actionsMenu}>
<KeyboardShortcutsSchemaProvider overrides={mergedOverrides.keyboardShortcutsMenu}> <KeyboardShortcutsSchemaProvider overrides={mergedOverrides.keyboardShortcutsMenu}>
<ContextMenuSchemaProvider overrides={mergedOverrides.contextMenu}> <TLUiContextMenuSchemaProvider overrides={mergedOverrides.contextMenu}>
<HelpMenuSchemaProvider overrides={mergedOverrides.helpMenu}> <HelpMenuSchemaProvider overrides={mergedOverrides.helpMenu}>
<MenuSchemaProvider overrides={mergedOverrides.menu}> <TLUiMenuSchemaProvider overrides={mergedOverrides.menu}>
{children} {children}
</MenuSchemaProvider> </TLUiMenuSchemaProvider>
</HelpMenuSchemaProvider> </HelpMenuSchemaProvider>
</ContextMenuSchemaProvider> </TLUiContextMenuSchemaProvider>
</KeyboardShortcutsSchemaProvider> </KeyboardShortcutsSchemaProvider>
</ActionsMenuSchemaProvider> </ActionsMenuSchemaProvider>
</ToolbarSchemaProvider> </ToolbarSchemaProvider>

View file

@ -1,18 +1,19 @@
import { defaultEditorAssetUrls, EditorAssetUrls, EMBED_DEFINITIONS } from '@tldraw/editor' import { defaultEditorAssetUrls, EditorAssetUrls, EMBED_DEFINITIONS } from '@tldraw/editor'
import { LANGUAGES } from './hooks/useTranslation/languages' import { LANGUAGES } from './hooks/useTranslation/languages'
import { TLUiIconType, TLUiIconTypes } from './icon-types' import { iconTypes, TLUiIconType } from './icon-types'
export type UiAssetUrls = EditorAssetUrls & { export type TLUiAssetUrls = EditorAssetUrls & {
icons: Record<TLUiIconType, string> icons: Record<TLUiIconType, string>
translations: Record<(typeof LANGUAGES)[number]['locale'], string> translations: Record<(typeof LANGUAGES)[number]['locale'], string>
embedIcons: Record<(typeof EMBED_DEFINITIONS)[number]['type'], string> embedIcons: Record<(typeof EMBED_DEFINITIONS)[number]['type'], string>
} }
export let defaultUiAssetUrls: UiAssetUrls = { export let defaultUiAssetUrls: TLUiAssetUrls = {
...defaultEditorAssetUrls, ...defaultEditorAssetUrls,
icons: Object.fromEntries( icons: Object.fromEntries(iconTypes.map((name) => [name, `/icons/icon/${name}.svg`])) as Record<
TLUiIconTypes.map((name) => [name, `/icons/icon/${name}.svg`]) TLUiIconType,
) as Record<TLUiIconType, string>, string
>,
translations: Object.fromEntries( translations: Object.fromEntries(
LANGUAGES.map((lang) => [lang.locale, `/translations/${lang.locale}.json`]) LANGUAGES.map((lang) => [lang.locale, `/translations/${lang.locale}.json`])
) as Record<(typeof LANGUAGES)[number]['locale'], string>, ) as Record<(typeof LANGUAGES)[number]['locale'], string>,
@ -22,6 +23,6 @@ export let defaultUiAssetUrls: UiAssetUrls = {
} }
/** @internal */ /** @internal */
export function setDefaultUiAssetUrls(urls: UiAssetUrls) { export function setDefaultUiAssetUrls(urls: TLUiAssetUrls) {
defaultUiAssetUrls = urls defaultUiAssetUrls = urls
} }

View file

@ -1,7 +1,7 @@
import * as PopoverPrimitive from '@radix-ui/react-popover' import * as PopoverPrimitive from '@radix-ui/react-popover'
import { useContainer } from '@tldraw/editor' import { useContainer } from '@tldraw/editor'
import { memo } from 'react' import { memo } from 'react'
import { MenuChild } from '../hooks/menuHelpers' import { TLUiMenuChild } from '../hooks/menuHelpers'
import { useActionsMenuSchema } from '../hooks/useActionsMenuSchema' import { useActionsMenuSchema } from '../hooks/useActionsMenuSchema'
import { useReadonly } from '../hooks/useReadonly' import { useReadonly } from '../hooks/useReadonly'
import { useTranslation } from '../hooks/useTranslation/useTranslation' import { useTranslation } from '../hooks/useTranslation/useTranslation'
@ -15,7 +15,7 @@ export const ActionsMenu = memo(function ActionsMenu() {
const menuSchema = useActionsMenuSchema() const menuSchema = useActionsMenuSchema()
const isReadonly = useReadonly() const isReadonly = useReadonly()
function getActionMenuItem(item: MenuChild) { function getActionMenuItem(item: TLUiMenuChild) {
if (isReadonly && !item.readonlyOk) return null if (isReadonly && !item.readonlyOk) return null
switch (item.type) { switch (item.type) {

View file

@ -3,7 +3,7 @@ import { Editor, preventDefault, useContainer, useEditor } from '@tldraw/editor'
import classNames from 'classnames' import classNames from 'classnames'
import * as React from 'react' import * as React from 'react'
import { useValue } from 'signia-react' import { useValue } from 'signia-react'
import { MenuChild } from '../hooks/menuHelpers' import { TLUiMenuChild } from '../hooks/menuHelpers'
import { useBreakpoint } from '../hooks/useBreakpoint' import { useBreakpoint } from '../hooks/useBreakpoint'
import { useContextMenuSchema } from '../hooks/useContextMenuSchema' import { useContextMenuSchema } from '../hooks/useContextMenuSchema'
import { useMenuIsOpen } from '../hooks/useMenuIsOpen' import { useMenuIsOpen } from '../hooks/useMenuIsOpen'
@ -15,7 +15,7 @@ import { Icon } from './primitives/Icon'
import { Kbd } from './primitives/Kbd' import { Kbd } from './primitives/Kbd'
/** @public */ /** @public */
export interface ContextMenuProps { export interface TLUiContextMenuProps {
children: any children: any
} }
@ -23,7 +23,7 @@ export interface ContextMenuProps {
export const ContextMenu = function ContextMenu({ children }: { children: any }) { export const ContextMenu = function ContextMenu({ children }: { children: any }) {
const editor = useEditor() const editor = useEditor()
const contextMenuSchema = useContextMenuSchema() const contextTLUiMenuSchema = useContextMenuSchema()
const cb = (isOpen: boolean) => { const cb = (isOpen: boolean) => {
if (isOpen) return if (isOpen) return
if (shouldDeselect(editor)) { if (shouldDeselect(editor)) {
@ -37,8 +37,8 @@ export const ContextMenu = function ContextMenu({ children }: { children: any })
const isReadonly = useReadonly() const isReadonly = useReadonly()
const noItemsToShow = const noItemsToShow =
contextMenuSchema.length === 0 || contextTLUiMenuSchema.length === 0 ||
(isReadonly && contextMenuSchema.every((item) => !item.readonlyOk)) (isReadonly && contextTLUiMenuSchema.every((item) => !item.readonlyOk))
const selectToolActive = useValue('isSelectToolActive', () => editor.currentToolId === 'select', [ const selectToolActive = useValue('isSelectToolActive', () => editor.currentToolId === 'select', [
editor, editor,
@ -80,8 +80,8 @@ function ContextMenuContent() {
function getContextMenuItem( function getContextMenuItem(
editor: Editor, editor: Editor,
item: MenuChild, item: TLUiMenuChild,
parent: MenuChild | null, parent: TLUiMenuChild | null,
depth: number depth: number
) { ) {
if (isReadonly && !item.readonlyOk) return null if (isReadonly && !item.readonlyOk) return null

View file

@ -38,7 +38,7 @@ function createNShapes(editor: Editor, n: number) {
}) })
} }
/** @public */ /** @internal */
export const DebugPanel = React.memo(function DebugPanel({ export const DebugPanel = React.memo(function DebugPanel({
renderDebugMenuItems, renderDebugMenuItems,
}: { }: {
@ -96,7 +96,7 @@ const DebugMenuContent = track(function DebugMenuContent({
// icon?: string // icon?: string
// title?: string // title?: string
// description?: string // description?: string
// actions?: TLToastAction[] // actions?: TLUiToastAction[]
}) })
}} }}
> >

View file

@ -1,9 +1,9 @@
import * as _Dialog from '@radix-ui/react-dialog' import * as _Dialog from '@radix-ui/react-dialog'
import { useContainer } from '@tldraw/editor' import { useContainer } from '@tldraw/editor'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import { TLDialog, useDialogs } from '../hooks/useDialogsProvider' import { TLUiDialog, useDialogs } from '../hooks/useDialogsProvider'
const Dialog = ({ id, component: ModalContent, onClose }: TLDialog) => { const Dialog = ({ id, component: ModalContent, onClose }: TLUiDialog) => {
const { removeDialog } = useDialogs() const { removeDialog } = useDialogs()
const container = useContainer() const container = useContainer()
@ -42,7 +42,7 @@ function _Dialogs() {
return ( return (
<> <>
{dialogs.map((dialog: TLDialog) => ( {dialogs.map((dialog: TLUiDialog) => (
<Dialog key={dialog.id} {...dialog} /> <Dialog key={dialog.id} {...dialog} />
))} ))}
</> </>

View file

@ -1,7 +1,7 @@
import { TLBaseShape, TLBookmarkUtil, useEditor } from '@tldraw/editor' import { TLBaseShape, TLBookmarkUtil, useEditor } from '@tldraw/editor'
import { useCallback, useRef, useState } from 'react' import { useCallback, useRef, useState } from 'react'
import { track } from 'signia-react' import { track } from 'signia-react'
import { DialogProps } from '../hooks/useDialogsProvider' import { TLUiDialogProps } from '../hooks/useDialogsProvider'
import { useTranslation } from '../hooks/useTranslation/useTranslation' import { useTranslation } from '../hooks/useTranslation/useTranslation'
import { Button } from './primitives/Button' import { Button } from './primitives/Button'
import * as Dialog from './primitives/Dialog' import * as Dialog from './primitives/Dialog'
@ -21,7 +21,7 @@ function validateUrl(url: string) {
type ShapeWithUrl = TLBaseShape<string, { url: string }> type ShapeWithUrl = TLBaseShape<string, { url: string }>
export const EditLinkDialog = track(function EditLinkDialog({ onClose }: DialogProps) { export const EditLinkDialog = track(function EditLinkDialog({ onClose }: TLUiDialogProps) {
const editor = useEditor() const editor = useEditor()
const selectedShape = editor.onlySelectedShape const selectedShape = editor.onlySelectedShape
@ -38,7 +38,7 @@ export const EditLinkDialog = track(function EditLinkDialog({ onClose }: DialogP
export const EditLinkDialogInner = track(function EditLinkDialogInner({ export const EditLinkDialogInner = track(function EditLinkDialogInner({
onClose, onClose,
selectedShape, selectedShape,
}: DialogProps & { selectedShape: ShapeWithUrl }) { }: TLUiDialogProps & { selectedShape: ShapeWithUrl }) {
const editor = useEditor() const editor = useEditor()
const msg = useTranslation() const msg = useTranslation()

View file

@ -3,14 +3,14 @@ import { EMBED_DEFINITIONS, EmbedDefinition } from '@tldraw/tlschema'
import { useRef, useState } from 'react' import { useRef, useState } from 'react'
import { track } from 'signia-react' import { track } from 'signia-react'
import { useAssetUrls } from '../hooks/useAssetUrls' import { useAssetUrls } from '../hooks/useAssetUrls'
import { DialogProps } from '../hooks/useDialogsProvider' import { TLUiDialogProps } from '../hooks/useDialogsProvider'
import { useTranslation } from '../hooks/useTranslation/useTranslation' import { useTranslation } from '../hooks/useTranslation/useTranslation'
import { Button } from './primitives/Button' import { Button } from './primitives/Button'
import * as Dialog from './primitives/Dialog' import * as Dialog from './primitives/Dialog'
import { Icon } from './primitives/Icon' import { Icon } from './primitives/Icon'
import { Input } from './primitives/Input' import { Input } from './primitives/Input'
export const EmbedDialog = track(function EmbedDialog({ onClose }: DialogProps) { export const EmbedDialog = track(function EmbedDialog({ onClose }: TLUiDialogProps) {
const editor = useEditor() const editor = useEditor()
const msg = useTranslation() const msg = useTranslation()
const assetUrls = useAssetUrls() const assetUrls = useAssetUrls()

View file

@ -2,7 +2,7 @@ import { useEditor } from '@tldraw/editor'
import * as React from 'react' import * as React from 'react'
import { track } from 'signia-react' import { track } from 'signia-react'
/** @public */ /** @internal */
export const HTMLCanvas = track(function HTMLCanvas() { export const HTMLCanvas = track(function HTMLCanvas() {
const editor = useEditor() const editor = useEditor()
const rCanvas = React.useRef<HTMLCanvasElement>(null) const rCanvas = React.useRef<HTMLCanvasElement>(null)

View file

@ -1,11 +1,11 @@
import { Content, Portal, Root, Trigger } from '@radix-ui/react-dropdown-menu' import { Content, Portal, Root, Trigger } from '@radix-ui/react-dropdown-menu'
import { useContainer } from '@tldraw/editor' import { useContainer } from '@tldraw/editor'
import * as React from 'react' import * as React from 'react'
import { MenuChild } from '../hooks/menuHelpers' import { TLUiMenuChild } from '../hooks/menuHelpers'
import { useHelpMenuSchema } from '../hooks/useHelpMenuSchema' import { useHelpMenuSchema } from '../hooks/useHelpMenuSchema'
import { useMenuIsOpen } from '../hooks/useMenuIsOpen' import { useMenuIsOpen } from '../hooks/useMenuIsOpen'
import { useReadonly } from '../hooks/useReadonly' import { useReadonly } from '../hooks/useReadonly'
import { TLTranslationKey } from '../hooks/useTranslation/TLTranslationKey' import { TLUiTranslationKey } from '../hooks/useTranslation/TLUiTranslationKey'
import { useTranslation } from '../hooks/useTranslation/useTranslation' import { useTranslation } from '../hooks/useTranslation/useTranslation'
import { TLUiIconType } from '../icon-types' import { TLUiIconType } from '../icon-types'
import { LanguageMenu } from './LanguageMenu' import { LanguageMenu } from './LanguageMenu'
@ -13,17 +13,17 @@ import * as M from './primitives/DropdownMenu'
import { Icon } from './primitives/Icon' import { Icon } from './primitives/Icon'
interface HelpMenuLink { interface HelpMenuLink {
label: TLTranslationKey label: TLUiTranslationKey
icon: TLUiIconType icon: TLUiIconType
url: string url: string
} }
/** @public */ /** @internal */
export interface HelpMenuProps { export interface HelpMenuProps {
links?: HelpMenuLink[] links?: HelpMenuLink[]
} }
/** @public */ /** @internal */
export const HelpMenu = React.memo(function HelpMenu() { export const HelpMenu = React.memo(function HelpMenu() {
const container = useContainer() const container = useContainer()
const msg = useTranslation() const msg = useTranslation()
@ -61,7 +61,7 @@ function HelpMenuContent() {
const isReadonly = useReadonly() const isReadonly = useReadonly()
function getHelpMenuItem(item: MenuChild) { function getHelpMenuItem(item: TLUiMenuChild) {
if (isReadonly && !item.readonlyOk) return null if (isReadonly && !item.readonlyOk) return null
switch (item.type) { switch (item.type) {

View file

@ -1,7 +1,7 @@
import { MenuChild } from '../hooks/menuHelpers' import { TLUiMenuChild } from '../hooks/menuHelpers'
import { useKeyboardShortcutsSchema } from '../hooks/useKeyboardShortcutsSchema' import { useKeyboardShortcutsSchema } from '../hooks/useKeyboardShortcutsSchema'
import { useReadonly } from '../hooks/useReadonly' import { useReadonly } from '../hooks/useReadonly'
import { TLTranslationKey } from '../hooks/useTranslation/TLTranslationKey' import { TLUiTranslationKey } from '../hooks/useTranslation/TLUiTranslationKey'
import { useTranslation } from '../hooks/useTranslation/useTranslation' import { useTranslation } from '../hooks/useTranslation/useTranslation'
import * as Dialog from './primitives/Dialog' import * as Dialog from './primitives/Dialog'
import { Kbd } from './primitives/Kbd' import { Kbd } from './primitives/Kbd'
@ -11,7 +11,7 @@ export const KeyboardShortcutsDialog = () => {
const isReadonly = useReadonly() const isReadonly = useReadonly()
const shortcutsItems = useKeyboardShortcutsSchema() const shortcutsItems = useKeyboardShortcutsSchema()
function getKeyboardShortcutItem(item: MenuChild) { function getKeyboardShortcutItem(item: TLUiMenuChild) {
if (isReadonly && !item.readonlyOk) return null if (isReadonly && !item.readonlyOk) return null
switch (item.type) { switch (item.type) {
@ -19,7 +19,7 @@ export const KeyboardShortcutsDialog = () => {
return ( return (
<div className="tlui-shortcuts-dialog__group" key={item.id}> <div className="tlui-shortcuts-dialog__group" key={item.id}>
<h2 className="tlui-shortcuts-dialog__group__title"> <h2 className="tlui-shortcuts-dialog__group__title">
{msg(item.id as TLTranslationKey)} {msg(item.id as TLUiTranslationKey)}
</h2> </h2>
<div className="tlui-shortcuts-dialog__group__content"> <div className="tlui-shortcuts-dialog__group__content">
{item.children {item.children

View file

@ -1,6 +1,6 @@
import { useEditor } from '@tldraw/editor' import { useEditor } from '@tldraw/editor'
import { useCallback } from 'react' import { useCallback } from 'react'
import { TLTranslationLocale } from '../hooks/useTranslation/translations' import { TLUiTranslation } from '../hooks/useTranslation/translations'
import { useLanguages } from '../hooks/useTranslation/useLanguages' import { useLanguages } from '../hooks/useTranslation/useLanguages'
import * as D from './primitives/DropdownMenu' import * as D from './primitives/DropdownMenu'
@ -9,7 +9,7 @@ export function LanguageMenu() {
const { languages, currentLanguage } = useLanguages() const { languages, currentLanguage } = useLanguages()
const handleLanguageSelect = useCallback( const handleLanguageSelect = useCallback(
(locale: TLTranslationLocale) => editor.setLocale(locale), (locale: TLUiTranslation['locale']) => editor.setLocale(locale),
[editor] [editor]
) )

View file

@ -1,6 +1,6 @@
import { Editor, useEditor } from '@tldraw/editor' import { Editor, useEditor } from '@tldraw/editor'
import * as React from 'react' import * as React from 'react'
import { MenuChild } from '../hooks/menuHelpers' import { TLUiMenuChild } from '../hooks/menuHelpers'
import { useBreakpoint } from '../hooks/useBreakpoint' import { useBreakpoint } from '../hooks/useBreakpoint'
import { useMenuSchema } from '../hooks/useMenuSchema' import { useMenuSchema } from '../hooks/useMenuSchema'
import { useReadonly } from '../hooks/useReadonly' import { useReadonly } from '../hooks/useReadonly'
@ -37,7 +37,12 @@ function MenuContent() {
const breakpoint = useBreakpoint() const breakpoint = useBreakpoint()
const isReadonly = useReadonly() const isReadonly = useReadonly()
function getMenuItem(editor: Editor, item: MenuChild, parent: MenuChild | null, depth: number) { function getMenuItem(
editor: Editor,
item: TLUiMenuChild,
parent: TLUiMenuChild | null,
depth: number
) {
switch (item.type) { switch (item.type) {
case 'custom': { case 'custom': {
if (isReadonly && !item.readonlyOk) return null if (isReadonly && !item.readonlyOk) return null

View file

@ -8,7 +8,7 @@ import { Button } from '../primitives/Button'
import { kbdStr } from '../primitives/shared' import { kbdStr } from '../primitives/shared'
import { ZoomMenu } from './ZoomMenu' import { ZoomMenu } from './ZoomMenu'
/** @public */ /** @internal */
export const NavigationZone = memo(function NavigationZone() { export const NavigationZone = memo(function NavigationZone() {
const actions = useActions() const actions = useActions()
const msg = useTranslation() const msg = useTranslation()

View file

@ -2,7 +2,7 @@ import { Trigger } from '@radix-ui/react-dropdown-menu'
import { Editor, TLStyleItem, TLStyleType } from '@tldraw/editor' import { Editor, TLStyleItem, TLStyleType } from '@tldraw/editor'
import classNames from 'classnames' import classNames from 'classnames'
import * as React from 'react' import * as React from 'react'
import { TLTranslationKey } from '../../hooks/useTranslation/TLTranslationKey' import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
import { useTranslation } from '../../hooks/useTranslation/useTranslation' import { useTranslation } from '../../hooks/useTranslation/useTranslation'
import { TLUiIconType } from '../../icon-types' import { TLUiIconType } from '../../icon-types'
import { Button } from '../primitives/Button' import { Button } from '../primitives/Button'
@ -11,9 +11,9 @@ import * as DropdownMenu from '../primitives/DropdownMenu'
type AllStyles = typeof Editor.styles type AllStyles = typeof Editor.styles
interface DoubleDropdownPickerProps<T extends AllStyles[keyof AllStyles][number]> { interface DoubleDropdownPickerProps<T extends AllStyles[keyof AllStyles][number]> {
label: TLTranslationKey label: TLUiTranslationKey
labelA: TLTranslationKey labelA: TLUiTranslationKey
labelB: TLTranslationKey labelB: TLUiTranslationKey
itemsA: T[] itemsA: T[]
itemsB: T[] itemsB: T[]
styleTypeA: TLStyleType styleTypeA: TLStyleType
@ -69,7 +69,7 @@ export const DoubleDropdownPicker = React.memo(function DoubleDropdownPicker<
' — ' + ' — ' +
(valueA === null (valueA === null
? msg('style-panel.mixed') ? msg('style-panel.mixed')
: msg(`${styleTypeA}-style.${valueA}` as TLTranslationKey)) : msg(`${styleTypeA}-style.${valueA}` as TLUiTranslationKey))
} }
icon={iconA as any} icon={iconA as any}
invertIcon invertIcon
@ -88,7 +88,9 @@ export const DoubleDropdownPicker = React.memo(function DoubleDropdownPicker<
<DropdownMenu.Item <DropdownMenu.Item
className="tlui-button-grid__button" className="tlui-button-grid__button"
title={ title={
msg(labelA) + ' — ' + msg(`${styleTypeA}-style.${item.id}` as TLTranslationKey) msg(labelA) +
' — ' +
msg(`${styleTypeA}-style.${item.id}` as TLUiTranslationKey)
} }
data-testid={`${startWdPrefix}.${item.id}`} data-testid={`${startWdPrefix}.${item.id}`}
key={item.id} key={item.id}
@ -110,7 +112,7 @@ export const DoubleDropdownPicker = React.memo(function DoubleDropdownPicker<
' — ' + ' — ' +
(valueB === null (valueB === null
? msg('style-panel.mixed') ? msg('style-panel.mixed')
: msg(`${styleTypeB}-style.${valueB}` as TLTranslationKey)) : msg(`${styleTypeB}-style.${valueB}` as TLUiTranslationKey))
} }
icon={iconB as any} icon={iconB as any}
smallIcon smallIcon
@ -128,7 +130,9 @@ export const DoubleDropdownPicker = React.memo(function DoubleDropdownPicker<
<DropdownMenu.Item <DropdownMenu.Item
className="tlui-button-grid__button" className="tlui-button-grid__button"
title={ title={
msg(labelB) + ' — ' + msg(`${styleTypeB}-style.${item.id}` as TLTranslationKey) msg(labelB) +
' — ' +
msg(`${styleTypeB}-style.${item.id}` as TLUiTranslationKey)
} }
data-testid={`${endWdPrefix}.${item.id}`} data-testid={`${endWdPrefix}.${item.id}`}
key={item.id} key={item.id}

View file

@ -2,7 +2,7 @@ import { Trigger } from '@radix-ui/react-dropdown-menu'
import { Editor, TLStyleItem, TLStyleType } from '@tldraw/editor' import { Editor, TLStyleItem, TLStyleType } from '@tldraw/editor'
import classNames from 'classnames' import classNames from 'classnames'
import * as React from 'react' import * as React from 'react'
import { TLTranslationKey } from '../../hooks/useTranslation/TLTranslationKey' import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
import { useTranslation } from '../../hooks/useTranslation/useTranslation' import { useTranslation } from '../../hooks/useTranslation/useTranslation'
import { TLUiIconType } from '../../icon-types' import { TLUiIconType } from '../../icon-types'
import { Button } from '../primitives/Button' import { Button } from '../primitives/Button'
@ -12,7 +12,7 @@ type AllStyles = typeof Editor.styles
interface DropdownPickerProps<T extends AllStyles[keyof AllStyles][number]> { interface DropdownPickerProps<T extends AllStyles[keyof AllStyles][number]> {
id: string id: string
label?: TLTranslationKey label?: TLUiTranslationKey
items: T[] items: T[]
styleType: TLStyleType styleType: TLStyleType
value: T['id'] | null value: T['id'] | null
@ -43,7 +43,7 @@ export const DropdownPicker = React.memo(function DropdownPicker<
title={ title={
value === null value === null
? msg('style-panel.mixed') ? msg('style-panel.mixed')
: msg(`${styleType}-style.${value}` as TLTranslationKey) : msg(`${styleType}-style.${value}` as TLUiTranslationKey)
} }
label={label} label={label}
icon={(icon as TLUiIconType) ?? 'mixed'} icon={(icon as TLUiIconType) ?? 'mixed'}
@ -62,7 +62,7 @@ export const DropdownPicker = React.memo(function DropdownPicker<
<DropdownMenu.Item <DropdownMenu.Item
className="tlui-button-grid__button" className="tlui-button-grid__button"
data-testid={`${testId}.${item.id}`} data-testid={`${testId}.${item.id}`}
title={msg(`${styleType}-style.${item.id}` as TLTranslationKey)} title={msg(`${styleType}-style.${item.id}` as TLUiTranslationKey)}
key={item.id} key={item.id}
icon={item.icon as TLUiIconType} icon={item.icon as TLUiIconType}
onClick={() => onValueChange(item as TLStyleItem, false)} onClick={() => onValueChange(item as TLStyleItem, false)}

View file

@ -13,7 +13,7 @@ interface StylePanelProps {
isMobile?: boolean isMobile?: boolean
} }
/** @public */ /** @internal */
export const StylePanel = function StylePanel({ isMobile }: StylePanelProps) { export const StylePanel = function StylePanel({ isMobile }: StylePanelProps) {
const editor = useEditor() const editor = useEditor()

View file

@ -1,12 +1,12 @@
import * as T from '@radix-ui/react-toast' import * as T from '@radix-ui/react-toast'
import * as React from 'react' import * as React from 'react'
import { TLToast, useToasts } from '../hooks/useToastsProvider' import { TLUiToast, useToasts } from '../hooks/useToastsProvider'
import { useTranslation } from '../hooks/useTranslation/useTranslation' import { useTranslation } from '../hooks/useTranslation/useTranslation'
import { TLUiIconType } from '../icon-types' import { TLUiIconType } from '../icon-types'
import { Button } from './primitives/Button' import { Button } from './primitives/Button'
import { Icon } from './primitives/Icon' import { Icon } from './primitives/Icon'
function Toast({ toast }: { toast: TLToast }) { function Toast({ toast }: { toast: TLUiToast }) {
const { removeToast } = useToasts() const { removeToast } = useToasts()
const msg = useTranslation() const msg = useTranslation()

View file

@ -4,8 +4,8 @@ import React from 'react'
import { track, useValue } from 'signia-react' import { track, useValue } from 'signia-react'
import { useBreakpoint } from '../../hooks/useBreakpoint' import { useBreakpoint } from '../../hooks/useBreakpoint'
import { useReadonly } from '../../hooks/useReadonly' import { useReadonly } from '../../hooks/useReadonly'
import { ToolbarItem, useToolbarSchema } from '../../hooks/useToolbarSchema' import { TLUiToolbarItem, useToolbarSchema } from '../../hooks/useToolbarSchema'
import { ToolItem } from '../../hooks/useTools' import { TLUiToolItem } from '../../hooks/useTools'
import { useTranslation } from '../../hooks/useTranslation/useTranslation' import { useTranslation } from '../../hooks/useTranslation/useTranslation'
import { ActionsMenu } from '../ActionsMenu' import { ActionsMenu } from '../ActionsMenu'
import { DuplicateButton } from '../DuplicateButton' import { DuplicateButton } from '../DuplicateButton'
@ -24,7 +24,7 @@ export const Toolbar = function Toolbar() {
const msg = useTranslation() const msg = useTranslation()
const breakpoint = useBreakpoint() const breakpoint = useBreakpoint()
const rMostRecentlyActiveDropdownItem = React.useRef<ToolbarItem | undefined>(undefined) const rMostRecentlyActiveDropdownItem = React.useRef<TLUiToolbarItem | undefined>(undefined)
const isReadOnly = useReadonly() const isReadOnly = useReadonly()
const toolbarItems = useToolbarSchema() const toolbarItems = useToolbarSchema()
@ -38,17 +38,17 @@ export const Toolbar = function Toolbar() {
const showEditingTools = !isReadOnly const showEditingTools = !isReadOnly
const showExtraActions = !(isReadOnly || isHandTool) const showExtraActions = !(isReadOnly || isHandTool)
const getTitle = (item: ToolItem) => const getTitle = (item: TLUiToolItem) =>
item.label ? `${msg(item.label)} ${item.kbd ? kbdStr(item.kbd) : ''}` : '' item.label ? `${msg(item.label)} ${item.kbd ? kbdStr(item.kbd) : ''}` : ''
const activeToolbarItem = toolbarItems.find((item) => { const activeTLUiToolbarItem = toolbarItems.find((item) => {
return isActiveToolItem(item.toolItem, activeToolId, geoState) return isActiveTLUiToolItem(item.toolItem, activeToolId, geoState)
}) })
const { itemsInPanel, itemsInDropdown, dropdownFirstItem } = React.useMemo(() => { const { itemsInPanel, itemsInDropdown, dropdownFirstItem } = React.useMemo(() => {
const itemsInPanel: ToolbarItem[] = [] const itemsInPanel: TLUiToolbarItem[] = []
const itemsInDropdown: ToolbarItem[] = [] const itemsInDropdown: TLUiToolbarItem[] = []
let dropdownFirstItem: ToolbarItem | undefined let dropdownFirstItem: TLUiToolbarItem | undefined
const overflowIndex = Math.min(8, 5 + breakpoint) const overflowIndex = Math.min(8, 5 + breakpoint)
@ -60,7 +60,7 @@ export const Toolbar = function Toolbar() {
} else { } else {
// Items above will be in the dropdown menu unless the item // Items above will be in the dropdown menu unless the item
// is active (or was the most recently selected active item) // is active (or was the most recently selected active item)
if (item === activeToolbarItem) { if (item === activeTLUiToolbarItem) {
// If the dropdown item is active, make it the dropdownFirstItem // If the dropdown item is active, make it the dropdownFirstItem
dropdownFirstItem = item dropdownFirstItem = item
} }
@ -103,7 +103,7 @@ export const Toolbar = function Toolbar() {
} }
return { itemsInPanel, itemsInDropdown, dropdownFirstItem } return { itemsInPanel, itemsInDropdown, dropdownFirstItem }
}, [toolbarItems, activeToolbarItem, breakpoint]) }, [toolbarItems, activeTLUiToolbarItem, breakpoint])
return ( return (
<div className="tlui-toolbar"> <div className="tlui-toolbar">
@ -139,7 +139,7 @@ export const Toolbar = function Toolbar() {
key={toolItem.id} key={toolItem.id}
item={toolItem} item={toolItem}
title={getTitle(toolItem)} title={getTitle(toolItem)}
isSelected={isActiveToolItem(toolItem, activeToolId, geoState)} isSelected={isActiveTLUiToolItem(toolItem, activeToolId, geoState)}
/> />
) )
})} })}
@ -148,7 +148,7 @@ export const Toolbar = function Toolbar() {
key={laserTool.toolItem.id} key={laserTool.toolItem.id}
item={laserTool.toolItem} item={laserTool.toolItem}
title={getTitle(laserTool.toolItem)} title={getTitle(laserTool.toolItem)}
isSelected={isActiveToolItem(laserTool.toolItem, activeToolId, geoState)} isSelected={isActiveTLUiToolItem(laserTool.toolItem, activeToolId, geoState)}
/> />
)} )}
{showEditingTools && ( {showEditingTools && (
@ -160,7 +160,7 @@ export const Toolbar = function Toolbar() {
key={toolItem.id} key={toolItem.id}
item={toolItem} item={toolItem}
title={getTitle(toolItem)} title={getTitle(toolItem)}
isSelected={isActiveToolItem(toolItem, activeToolId, geoState)} isSelected={isActiveTLUiToolItem(toolItem, activeToolId, geoState)}
/> />
))} ))}
{/* Everything Else */} {/* Everything Else */}
@ -170,7 +170,7 @@ export const Toolbar = function Toolbar() {
key={toolItem.id} key={toolItem.id}
item={toolItem} item={toolItem}
title={getTitle(toolItem)} title={getTitle(toolItem)}
isSelected={isActiveToolItem(toolItem, activeToolId, geoState)} isSelected={isActiveTLUiToolItem(toolItem, activeToolId, geoState)}
/> />
))} ))}
{/* Overflowing Shapes */} {/* Overflowing Shapes */}
@ -181,7 +181,7 @@ export const Toolbar = function Toolbar() {
key={dropdownFirstItem.toolItem.id} key={dropdownFirstItem.toolItem.id}
item={dropdownFirstItem.toolItem} item={dropdownFirstItem.toolItem}
title={getTitle(dropdownFirstItem.toolItem)} title={getTitle(dropdownFirstItem.toolItem)}
isSelected={isActiveToolItem( isSelected={isActiveTLUiToolItem(
dropdownFirstItem.toolItem, dropdownFirstItem.toolItem,
activeToolId, activeToolId,
geoState geoState
@ -220,7 +220,7 @@ export const Toolbar = function Toolbar() {
const OverflowToolsContent = track(function OverflowToolsContent({ const OverflowToolsContent = track(function OverflowToolsContent({
toolbarItems, toolbarItems,
}: { }: {
toolbarItems: ToolbarItem[] toolbarItems: TLUiToolbarItem[]
}) { }) {
const msg = useTranslation() const msg = useTranslation()
@ -250,7 +250,7 @@ function ToolbarButton({
title, title,
isSelected, isSelected,
}: { }: {
item: ToolItem item: TLUiToolItem
title: string title: string
isSelected: boolean isSelected: boolean
}) { }) {
@ -273,8 +273,8 @@ function ToolbarButton({
) )
} }
const isActiveToolItem = ( const isActiveTLUiToolItem = (
item: ToolItem, item: TLUiToolItem,
activeToolId: string | undefined, activeToolId: string | undefined,
geoState: string | null | undefined geoState: string | null | undefined
) => { ) => {

View file

@ -1,6 +1,6 @@
import classnames from 'classnames' import classnames from 'classnames'
import * as React from 'react' import * as React from 'react'
import { TLTranslationKey } from '../../hooks/useTranslation/TLTranslationKey' import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
import { useTranslation } from '../../hooks/useTranslation/useTranslation' import { useTranslation } from '../../hooks/useTranslation/useTranslation'
import { TLUiIconType } from '../../icon-types' import { TLUiIconType } from '../../icon-types'
import { Spinner } from '../Spinner' import { Spinner } from '../Spinner'
@ -8,10 +8,10 @@ import { Icon } from './Icon'
import { Kbd } from './Kbd' import { Kbd } from './Kbd'
/** @public */ /** @public */
export interface ButtonProps extends React.HTMLAttributes<HTMLButtonElement> { export interface TLUiButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
loading?: boolean // TODO: loading spinner loading?: boolean // TODO: loading spinner
disabled?: boolean disabled?: boolean
label?: TLTranslationKey label?: TLUiTranslationKey
icon?: TLUiIconType icon?: TLUiIconType
spinner?: boolean spinner?: boolean
iconLeft?: TLUiIconType iconLeft?: TLUiIconType
@ -23,7 +23,7 @@ export interface ButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
} }
/** @public */ /** @public */
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(function Button( export const Button = React.forwardRef<HTMLButtonElement, TLUiButtonProps>(function Button(
{ {
label, label,
icon, icon,

View file

@ -3,12 +3,12 @@ import { clamp } from '@tldraw/primitives'
import classNames from 'classnames' import classNames from 'classnames'
import * as React from 'react' import * as React from 'react'
import { useRef } from 'react' import { useRef } from 'react'
import { TLTranslationKey } from '../../hooks/useTranslation/TLTranslationKey' import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
import { useTranslation } from '../../hooks/useTranslation/useTranslation' import { useTranslation } from '../../hooks/useTranslation/useTranslation'
import { TLUiIconType } from '../../icon-types' import { TLUiIconType } from '../../icon-types'
import { Button } from './Button' import { Button } from './Button'
/** @public */ /** @internal */
export interface ButtonPickerProps<T extends TLStyleItem> { export interface ButtonPickerProps<T extends TLStyleItem> {
title: string title: string
items: T[] items: T[]
@ -97,7 +97,7 @@ function _ButtonPicker<T extends TLStyleItem>(props: ButtonPickerProps<T>) {
data-testid={`${props['data-testid']}.${item.id}`} data-testid={`${props['data-testid']}.${item.id}`}
aria-label={item.id} aria-label={item.id}
data-state={value === item.id ? 'hinted' : undefined} data-state={value === item.id ? 'hinted' : undefined}
title={title + ' — ' + msg(`${styleType}-style.${item.id}` as TLTranslationKey)} title={title + ' — ' + msg(`${styleType}-style.${item.id}` as TLUiTranslationKey)}
className={classNames('tlui-button-grid__button')} className={classNames('tlui-button-grid__button')}
style={item.type === 'color' ? { color: `var(--palette-${item.id})` } : undefined} style={item.type === 'color' ? { color: `var(--palette-${item.id})` } : undefined}
onPointerEnter={handleButtonPointerEnter} onPointerEnter={handleButtonPointerEnter}
@ -111,5 +111,5 @@ function _ButtonPicker<T extends TLStyleItem>(props: ButtonPickerProps<T>) {
) )
} }
/** @public */ /** @internal */
export const ButtonPicker = React.memo(_ButtonPicker) export const ButtonPicker = React.memo(_ButtonPicker)

View file

@ -1,8 +1,8 @@
import * as DropdownMenu from '@radix-ui/react-dropdown-menu' import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
import { preventDefault, useContainer } from '@tldraw/editor' import { preventDefault, useContainer } from '@tldraw/editor'
import { useMenuIsOpen } from '../../hooks/useMenuIsOpen' import { useMenuIsOpen } from '../../hooks/useMenuIsOpen'
import { TLTranslationKey } from '../../hooks/useTranslation/TLTranslationKey' import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
import { Button, ButtonProps } from './Button' import { Button, TLUiButtonProps } from './Button'
import { Icon } from './Icon' import { Icon } from './Icon'
/** @public */ /** @public */
@ -88,7 +88,7 @@ export function SubTrigger({
'data-testid': testId, 'data-testid': testId,
'data-direction': dataDirection, 'data-direction': dataDirection,
}: { }: {
label: TLTranslationKey label: TLUiTranslationKey
'data-testid'?: string 'data-testid'?: string
'data-direction'?: 'left' | 'right' 'data-direction'?: 'left' | 'right'
}) { }) {
@ -153,7 +153,7 @@ export function Indicator() {
} }
/** @public */ /** @public */
export interface DropdownMenuItemProps extends ButtonProps { export interface DropdownMenuItemProps extends TLUiButtonProps {
noClose?: boolean noClose?: boolean
} }

View file

@ -4,7 +4,7 @@ import { useAssetUrls } from '../../hooks/useAssetUrls'
import { TLUiIconType } from '../../icon-types' import { TLUiIconType } from '../../icon-types'
/** @public */ /** @public */
export interface IconProps extends React.HTMLProps<HTMLDivElement> { export interface TLUiIconProps extends React.HTMLProps<HTMLDivElement> {
icon: TLUiIconType icon: TLUiIconType
small?: boolean small?: boolean
color?: string color?: string
@ -21,7 +21,7 @@ export const Icon = memo(function Icon({
color, color,
className, className,
...props ...props
}: IconProps) { }: TLUiIconProps) {
const assetUrls = useAssetUrls() const assetUrls = useAssetUrls()
const asset = assetUrls.icons[icon] const asset = assetUrls.icons[icon]
const ref = useRef<HTMLDivElement>(null) const ref = useRef<HTMLDivElement>(null)

View file

@ -1,14 +1,14 @@
import classNames from 'classnames' import classNames from 'classnames'
import * as React from 'react' import * as React from 'react'
import { TLTranslationKey } from '../../hooks/useTranslation/TLTranslationKey' import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
import { useTranslation } from '../../hooks/useTranslation/useTranslation' import { useTranslation } from '../../hooks/useTranslation/useTranslation'
import { TLUiIconType } from '../../icon-types' import { TLUiIconType } from '../../icon-types'
import { Icon } from './Icon' import { Icon } from './Icon'
/** @public */ /** @public */
export interface InputProps { export interface TLUiInputProps {
disabled?: boolean disabled?: boolean
label?: TLTranslationKey label?: TLUiTranslationKey
icon?: TLUiIconType icon?: TLUiIconType
iconLeft?: TLUiIconType iconLeft?: TLUiIconType
autofocus?: boolean autofocus?: boolean
@ -34,7 +34,7 @@ export interface InputProps {
} }
/** @public */ /** @public */
export const Input = React.forwardRef<HTMLInputElement, InputProps>(function Input( export const Input = React.forwardRef<HTMLInputElement, TLUiInputProps>(function Input(
{ {
className, className,
label, label,

View file

@ -1,11 +1,11 @@
import { kbd } from './shared' import { kbd } from './shared'
/** @public */ /** @internal */
export interface KbdProps { export interface KbdProps {
children: string children: string
} }
/** @public */ /** @internal */
export function Kbd({ children }: KbdProps) { export function Kbd({ children }: KbdProps) {
return ( return (
<kbd className="tlui-kbd"> <kbd className="tlui-kbd">

View file

@ -1,10 +1,10 @@
import { Range, Root, Thumb, Track } from '@radix-ui/react-slider' import { Range, Root, Thumb, Track } from '@radix-ui/react-slider'
import { useEditor } from '@tldraw/editor' import { useEditor } from '@tldraw/editor'
import { useCallback } from 'react' import { useCallback } from 'react'
import { TLTranslationKey } from '../../hooks/useTranslation/TLTranslationKey' import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
import { useTranslation } from '../../hooks/useTranslation/useTranslation' import { useTranslation } from '../../hooks/useTranslation/useTranslation'
/** @public */ /** @internal */
export interface SliderProps { export interface SliderProps {
steps: number steps: number
value: number | null value: number | null
@ -14,7 +14,7 @@ export interface SliderProps {
'data-testid'?: string 'data-testid'?: string
} }
/** @public */ /** @internal */
export function Slider(props: SliderProps) { export function Slider(props: SliderProps) {
const { title, steps, value, label, onValueChange } = props const { title, steps, value, label, onValueChange } = props
const editor = useEditor() const editor = useEditor()
@ -50,7 +50,7 @@ export function Slider(props: SliderProps) {
onPointerDown={handlePointerDown} onPointerDown={handlePointerDown}
onValueChange={handleValueChange} onValueChange={handleValueChange}
onPointerUp={handlePointerUp} onPointerUp={handlePointerUp}
title={title + ' — ' + msg(label as TLTranslationKey)} title={title + ' — ' + msg(label as TLUiTranslationKey)}
> >
<Track className="tlui-slider__track" dir="ltr"> <Track className="tlui-slider__track" dir="ltr">
{value !== null && <Range className="tlui-slider__range" dir="ltr" />} {value !== null && <Range className="tlui-slider__range" dir="ltr" />}

View file

@ -1,4 +1,4 @@
/** @public */ /** @internal */
export function toStartCase(str: string) { export function toStartCase(str: string) {
return str return str
.split(' ') .split(' ')
@ -13,7 +13,7 @@ const isDarwin =
const cmdKey = isDarwin ? '⌘' : 'Ctrl' const cmdKey = isDarwin ? '⌘' : 'Ctrl'
const altKey = isDarwin ? '⌥' : 'Alt' const altKey = isDarwin ? '⌥' : 'Alt'
/** @public */ /** @internal */
export function kbd(str: string) { export function kbd(str: string) {
return str return str
.split(',')[0] .split(',')[0]
@ -24,7 +24,7 @@ export function kbd(str: string) {
}) })
} }
/** @public */ /** @internal */
export function kbdStr(str: string) { export function kbdStr(str: string) {
return ( return (
'— ' + '— ' +
@ -38,27 +38,3 @@ export function kbdStr(str: string) {
.join('') .join('')
) )
} }
/** @public */
export const getBaseUrl = () => {
if (typeof process === 'undefined') {
return 'http://localhost:5420'
}
if (process.env.NODE_ENV === 'development') {
return 'http://localhost:3000'
}
if (process.env.NEXT_PUBLIC_VERCEL_ENV === 'production') {
return 'https://www.tldraw.com'
}
if (process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview') {
return `https://${process.env.NEXT_PUBLIC_VERCEL_URL}`
}
return 'http://localhost:3000'
}
/** @public */
export const BASE_URL = getBaseUrl()

View file

@ -1,15 +1,15 @@
import { Editor, TLArrowUtil, useEditor } from '@tldraw/editor' import { Editor, TLArrowUtil, useEditor } from '@tldraw/editor'
import { assert, exhaustiveSwitchError } from '@tldraw/utils' import { assert, exhaustiveSwitchError } from '@tldraw/utils'
import { useValue } from 'signia-react' import { useValue } from 'signia-react'
import { ActionItem } from './useActions' import { TLUiActionItem } from './useActions'
import { ToolItem } from './useTools' import { TLUiToolItem } from './useTools'
import { TLTranslationKey } from './useTranslation/TLTranslationKey' import { TLUiTranslationKey } from './useTranslation/TLUiTranslationKey'
/** @public */ /** @public */
export type MenuChild = MenuItem | SubMenu | MenuGroup | CustomMenuItem export type TLUiMenuChild = TLUiMenuItem | TLUiSubMenu | TLUiMenuGroup | TLUiCustomMenuItem
/** @public */ /** @public */
export type CustomMenuItem = { export type TLUiCustomMenuItem = {
id: string id: string
type: 'custom' type: 'custom'
disabled: boolean disabled: boolean
@ -17,37 +17,37 @@ export type CustomMenuItem = {
} }
/** @public */ /** @public */
export type MenuItem = { export type TLUiMenuItem = {
id: string id: string
type: 'item' type: 'item'
readonlyOk: boolean readonlyOk: boolean
actionItem: ActionItem actionItem: TLUiActionItem
disabled: boolean disabled: boolean
checked: boolean checked: boolean
} }
/** @public */ /** @public */
export type MenuGroup = { export type TLUiMenuGroup = {
id: string id: string
type: 'group' type: 'group'
checkbox: boolean checkbox: boolean
disabled: boolean disabled: boolean
readonlyOk: boolean readonlyOk: boolean
children: MenuChild[] children: TLUiMenuChild[]
} }
/** @public */ /** @public */
export type SubMenu = { export type TLUiSubMenu = {
id: string id: string
type: 'submenu' type: 'submenu'
label: TLTranslationKey label: TLUiTranslationKey
disabled: boolean disabled: boolean
readonlyOk: boolean readonlyOk: boolean
children: MenuChild[] children: TLUiMenuChild[]
} }
/** @public */ /** @public */
export type MenuSchema = (MenuGroup | MenuItem | CustomMenuItem)[] export type TLUiMenuSchema = (TLUiMenuGroup | TLUiMenuItem | TLUiCustomMenuItem)[]
/** @public */ /** @public */
export function compactMenuItems<T>(arr: T[]): Exclude<T, null | false | undefined>[] { export function compactMenuItems<T>(arr: T[]): Exclude<T, null | false | undefined>[] {
@ -55,7 +55,10 @@ export function compactMenuItems<T>(arr: T[]): Exclude<T, null | false | undefin
} }
/** @public */ /** @public */
export function menuGroup(id: string, ...children: (MenuChild | null | false)[]): MenuGroup | null { export function menuGroup(
id: string,
...children: (TLUiMenuChild | null | false)[]
): TLUiMenuGroup | null {
const childItems = compactMenuItems(children) const childItems = compactMenuItems(children)
if (childItems.length === 0) return null if (childItems.length === 0) return null
@ -73,9 +76,9 @@ export function menuGroup(id: string, ...children: (MenuChild | null | false)[])
/** @public */ /** @public */
export function menuSubmenu( export function menuSubmenu(
id: string, id: string,
label: TLTranslationKey, label: TLUiTranslationKey,
...children: (MenuChild | null | false)[] ...children: (TLUiMenuChild | null | false)[]
): SubMenu | null { ): TLUiSubMenu | null {
const childItems = compactMenuItems(children) const childItems = compactMenuItems(children)
if (childItems.length === 0) return null if (childItems.length === 0) return null
@ -105,9 +108,9 @@ export function menuCustom(
/** @public */ /** @public */
export function menuItem( export function menuItem(
actionItem: ActionItem | ToolItem, actionItem: TLUiActionItem | TLUiToolItem,
opts = {} as Partial<{ checked: boolean; disabled: boolean }> opts = {} as Partial<{ checked: boolean; disabled: boolean }>
): MenuItem { ): TLUiMenuItem {
if (!actionItem) { if (!actionItem) {
throw Error('No action item provided to menuItem') throw Error('No action item provided to menuItem')
} }
@ -146,19 +149,19 @@ function shapesWithUnboundArrows(editor: Editor) {
}) })
} }
/** @public */ /** @internal */
export const useThreeStackableItems = () => { export const useThreeStackableItems = () => {
const editor = useEditor() const editor = useEditor()
return useValue('threeStackableItems', () => shapesWithUnboundArrows(editor).length > 2, [editor]) return useValue('threeStackableItems', () => shapesWithUnboundArrows(editor).length > 2, [editor])
} }
/** @public */ /** @internal */
export const useAllowGroup = () => { export const useAllowGroup = () => {
const editor = useEditor() const editor = useEditor()
return useValue('allowGroup', () => shapesWithUnboundArrows(editor).length > 1, [editor]) return useValue('allowGroup', () => shapesWithUnboundArrows(editor).length > 1, [editor])
} }
/** @public */ /** @internal */
export const useAllowUngroup = () => { export const useAllowUngroup = () => {
const editor = useEditor() const editor = useEditor()
return useValue( return useValue(
@ -169,13 +172,16 @@ export const useAllowUngroup = () => {
} }
/** @public */ /** @public */
export function findMenuItem(menu: MenuSchema, path: string[]) { export function findMenuItem(menu: TLUiMenuSchema, path: string[]) {
const item = _findMenuItem(menu, path) const item = _findMenuItem(menu, path)
assert(item, `Menu item ${path.join(' > ')} not found`) assert(item, `Menu item ${path.join(' > ')} not found`)
return item return item
} }
function _findMenuItem(menu: MenuSchema | MenuChild[], path: string[]): MenuChild | null { function _findMenuItem(
menu: TLUiMenuSchema | TLUiMenuChild[],
path: string[]
): TLUiMenuChild | null {
const [next, ...rest] = path const [next, ...rest] = path
if (!next) return null if (!next) return null

View file

@ -27,44 +27,44 @@ import { useExportAs } from './useExportAs'
import { useInsertMedia } from './useInsertMedia' import { useInsertMedia } from './useInsertMedia'
import { usePrint } from './usePrint' import { usePrint } from './usePrint'
import { useToasts } from './useToastsProvider' import { useToasts } from './useToastsProvider'
import { TLTranslationKey } from './useTranslation/TLTranslationKey' import { TLUiTranslationKey } from './useTranslation/TLUiTranslationKey'
/** @public */ /** @public */
export interface ActionItem { export interface TLUiActionItem {
icon?: TLUiIconType icon?: TLUiIconType
id: string id: string
kbd?: string kbd?: string
title?: string title?: string
label?: TLTranslationKey label?: TLUiTranslationKey
menuLabel?: TLTranslationKey menuLabel?: TLUiTranslationKey
shortcutsLabel?: TLTranslationKey shortcutsLabel?: TLUiTranslationKey
contextMenuLabel?: TLTranslationKey contextMenuLabel?: TLUiTranslationKey
readonlyOk: boolean readonlyOk: boolean
checkbox?: boolean checkbox?: boolean
onSelect: (source: TLUiEventSource) => Promise<void> | void onSelect: (source: TLUiEventSource) => Promise<void> | void
} }
/** @public */ /** @public */
export type ActionsContextType = Record<string, ActionItem> export type TLUiActionsContextType = Record<string, TLUiActionItem>
/** @public */ /** @internal */
export const ActionsContext = React.createContext<ActionsContextType>({}) export const ActionsContext = React.createContext<TLUiActionsContextType>({})
/** @public */ /** @public */
export type ActionsProviderProps = { export type ActionsProviderProps = {
overrides?: ( overrides?: (
editor: Editor, editor: Editor,
actions: ActionsContextType, actions: TLUiActionsContextType,
helpers: undefined helpers: undefined
) => ActionsContextType ) => TLUiActionsContextType
children: any children: any
} }
function makeActions(actions: ActionItem[]) { function makeActions(actions: TLUiActionItem[]) {
return Object.fromEntries(actions.map((action) => [action.id, action])) as ActionsContextType return Object.fromEntries(actions.map((action) => [action.id, action])) as TLUiActionsContextType
} }
/** @public */ /** @internal */
export function ActionsProvider({ overrides, children }: ActionsProviderProps) { export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
const editor = useEditor() const editor = useEditor()
@ -80,7 +80,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
const trackEvent = useEvents() const trackEvent = useEvents()
// should this be a useMemo? looks like it doesn't actually deref any reactive values // should this be a useMemo? looks like it doesn't actually deref any reactive values
const actions = React.useMemo<ActionsContextType>(() => { const actions = React.useMemo<TLUiActionsContextType>(() => {
const actions = makeActions([ const actions = makeActions([
{ {
id: 'edit-link', id: 'edit-link',
@ -976,6 +976,6 @@ export function useActions() {
return ctx return ctx
} }
function asActions<T extends Record<string, ActionItem>>(actions: T) { function asActions<T extends Record<string, TLUiActionItem>>(actions: T) {
return actions as Record<keyof typeof actions, ActionItem> return actions as Record<keyof typeof actions, TLUiActionItem>
} }

View file

@ -2,7 +2,7 @@ import { Editor, useEditor } from '@tldraw/editor'
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { track } from 'signia-react' import { track } from 'signia-react'
import { import {
MenuSchema, TLUiMenuSchema,
menuItem, menuItem,
useAllowGroup, useAllowGroup,
useAllowUngroup, useAllowUngroup,
@ -13,27 +13,27 @@ import { useBreakpoint } from './useBreakpoint'
import { useHasLinkShapeSelected } from './useHasLinkShapeSelected' import { useHasLinkShapeSelected } from './useHasLinkShapeSelected'
/** @public */ /** @public */
export type ActionsMenuSchemaContextType = MenuSchema export type TLUiActionsMenuSchemaContextType = TLUiMenuSchema
/** @public */ /** @internal */
export const ActionsMenuSchemaContext = React.createContext({} as ActionsMenuSchemaContextType) export const ActionsMenuSchemaContext = React.createContext({} as TLUiActionsMenuSchemaContextType)
/** @public */ /** @public */
export type ActionsMenuSchemaProviderProps = { export type ActionsMenuSchemaProviderProps = {
overrides?: ( overrides?: (
editor: Editor, editor: Editor,
schema: ActionsMenuSchemaContextType, schema: TLUiActionsMenuSchemaContextType,
helpers: { helpers: {
actions: ReturnType<typeof useActions> actions: ReturnType<typeof useActions>
oneSelected: boolean oneSelected: boolean
twoSelected: boolean twoSelected: boolean
threeSelected: boolean threeSelected: boolean
} }
) => ActionsMenuSchemaContextType ) => TLUiActionsMenuSchemaContextType
children: any children: any
} }
/** @public */ /** @internal */
export const ActionsMenuSchemaProvider = track(function ActionsMenuSchemaProvider({ export const ActionsMenuSchemaProvider = track(function ActionsMenuSchemaProvider({
overrides, overrides,
children, children,
@ -53,7 +53,7 @@ export const ActionsMenuSchemaProvider = track(function ActionsMenuSchemaProvide
const breakpoint = useBreakpoint() const breakpoint = useBreakpoint()
const isZoomedTo100 = editor.zoomLevel === 1 const isZoomedTo100 = editor.zoomLevel === 1
const actionMenuSchema = useMemo<MenuSchema>(() => { const actionTLUiMenuSchema = useMemo<TLUiMenuSchema>(() => {
const results = [ const results = [
menuItem(actions['align-left'], { disabled: !twoSelected }), menuItem(actions['align-left'], { disabled: !twoSelected }),
menuItem(actions['align-center-horizontal'], { disabled: !twoSelected }), menuItem(actions['align-center-horizontal'], { disabled: !twoSelected }),
@ -104,14 +104,14 @@ export const ActionsMenuSchemaProvider = track(function ActionsMenuSchemaProvide
]) ])
return ( return (
<ActionsMenuSchemaContext.Provider value={actionMenuSchema}> <ActionsMenuSchemaContext.Provider value={actionTLUiMenuSchema}>
{children} {children}
</ActionsMenuSchemaContext.Provider> </ActionsMenuSchemaContext.Provider>
) )
}) })
/** @public */ /** @public */
export function useActionsMenuSchema(): MenuSchema { export function useActionsMenuSchema(): TLUiMenuSchema {
const ctx = React.useContext(ActionsMenuSchemaContext) const ctx = React.useContext(ActionsMenuSchemaContext)
if (!ctx) { if (!ctx) {

View file

@ -1,20 +1,23 @@
import { createContext, useContext } from 'react' import { createContext, useContext } from 'react'
import { UiAssetUrls } from '../assetUrls' import { TLUiAssetUrls } from '../assetUrls'
const AssetUrlsContext = createContext<UiAssetUrls | null>(null) /** @internal */
type UiAssetUrlsContextType = TLUiAssetUrls | null
/** @public */ const AssetUrlsContext = createContext<UiAssetUrlsContextType>(null)
/** @internal */
export function AssetUrlsProvider({ export function AssetUrlsProvider({
assetUrls, assetUrls,
children, children,
}: { }: {
assetUrls: UiAssetUrls assetUrls: TLUiAssetUrls
children: React.ReactNode children: React.ReactNode
}) { }) {
return <AssetUrlsContext.Provider value={assetUrls}>{children}</AssetUrlsContext.Provider> return <AssetUrlsContext.Provider value={assetUrls}>{children}</AssetUrlsContext.Provider>
} }
/** @public */ /** @internal */
export function useAssetUrls() { export function useAssetUrls() {
const assetUrls = useContext(AssetUrlsContext) const assetUrls = useContext(AssetUrlsContext)
if (!assetUrls) { if (!assetUrls) {

View file

@ -2,7 +2,7 @@ import { Editor, TLBookmarkUtil, TLEmbedUtil, getEmbedInfo, useEditor } from '@t
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { track, useValue } from 'signia-react' import { track, useValue } from 'signia-react'
import { import {
MenuSchema, TLUiMenuSchema,
compactMenuItems, compactMenuItems,
menuCustom, menuCustom,
menuGroup, menuGroup,
@ -19,16 +19,18 @@ import { useOnlyFlippableShape } from './useOnlyFlippableShape'
import { useShowAutoSizeToggle } from './useShowAutoSizeToggle' import { useShowAutoSizeToggle } from './useShowAutoSizeToggle'
/** @public */ /** @public */
export type ContextMenuSchemaContextType = MenuSchema export type TLUiContextTTLUiMenuSchemaContextType = TLUiMenuSchema
/** @internal */
export const TLUiContextMenuSchemaContext = React.createContext(
{} as TLUiContextTTLUiMenuSchemaContextType
)
/** @public */ /** @public */
export const ContextMenuSchemaContext = React.createContext({} as ContextMenuSchemaContextType) export type TLUiContextMenuSchemaProviderProps = {
/** @public */
export type ContextMenuSchemaProviderProps = {
overrides?: ( overrides?: (
editor: Editor, editor: Editor,
schema: ContextMenuSchemaContextType, schema: TLUiContextTTLUiMenuSchemaContextType,
helpers: { helpers: {
actions: ReturnType<typeof useActions> actions: ReturnType<typeof useActions>
oneSelected: boolean oneSelected: boolean
@ -38,15 +40,15 @@ export type ContextMenuSchemaProviderProps = {
showUngroup: boolean showUngroup: boolean
onlyFlippableShapeSelected: boolean onlyFlippableShapeSelected: boolean
} }
) => ContextMenuSchemaContextType ) => TLUiContextTTLUiMenuSchemaContextType
children: any children: any
} }
/** @public */ /** @internal */
export const ContextMenuSchemaProvider = track(function ContextMenuSchemaProvider({ export const TLUiContextMenuSchemaProvider = track(function TLUiContextMenuSchemaProvider({
overrides, overrides,
children, children,
}: ContextMenuSchemaProviderProps) { }: TLUiContextMenuSchemaProviderProps) {
const editor = useEditor() const editor = useEditor()
const actions = useActions() const actions = useActions()
@ -100,8 +102,8 @@ export const ContextMenuSchemaProvider = track(function ContextMenuSchemaProvide
const { onlySelectedShape } = editor const { onlySelectedShape } = editor
const isShapeLocked = onlySelectedShape && editor.isShapeOrAncestorLocked(onlySelectedShape) const isShapeLocked = onlySelectedShape && editor.isShapeOrAncestorLocked(onlySelectedShape)
const contextMenuSchema = useMemo<MenuSchema>(() => { const contextTLUiMenuSchema = useMemo<TLUiMenuSchema>(() => {
let contextMenuSchema: ContextMenuSchemaContextType = compactMenuItems([ let contextTLUiMenuSchema: TLUiContextTTLUiMenuSchemaContextType = compactMenuItems([
menuGroup( menuGroup(
'selection', 'selection',
oneEmbedSelected && menuItem(actions['open-embed-link']), oneEmbedSelected && menuItem(actions['open-embed-link']),
@ -220,7 +222,7 @@ export const ContextMenuSchemaProvider = track(function ContextMenuSchemaProvide
]) ])
if (overrides) { if (overrides) {
contextMenuSchema = overrides(editor, contextMenuSchema, { contextTLUiMenuSchema = overrides(editor, contextTLUiMenuSchema, {
actions, actions,
oneSelected, oneSelected,
twoSelected, twoSelected,
@ -231,7 +233,7 @@ export const ContextMenuSchemaProvider = track(function ContextMenuSchemaProvide
}) })
} }
return contextMenuSchema return contextTLUiMenuSchema
}, [ }, [
editor, editor,
overrides, overrides,
@ -254,18 +256,18 @@ export const ContextMenuSchemaProvider = track(function ContextMenuSchemaProvide
]) ])
return ( return (
<ContextMenuSchemaContext.Provider value={contextMenuSchema}> <TLUiContextMenuSchemaContext.Provider value={contextTLUiMenuSchema}>
{children} {children}
</ContextMenuSchemaContext.Provider> </TLUiContextMenuSchemaContext.Provider>
) )
}) })
/** @public */ /** @public */
export function useContextMenuSchema(): MenuSchema { export function useContextMenuSchema(): TLUiMenuSchema {
const ctx = React.useContext(ContextMenuSchemaContext) const ctx = React.useContext(TLUiContextMenuSchemaContext)
if (!ctx) { if (!ctx) {
throw new Error('useContextMenuSchema must be used inside of a ContextMenuSchemaProvider.') throw new Error('useContextMenuSchema must be used inside of a TLUiContextMenuSchemaProvider.')
} }
return ctx return ctx

View file

@ -3,44 +3,44 @@ import { createContext, useCallback, useContext, useState } from 'react'
import { useEvents } from './useEventsProvider' import { useEvents } from './useEventsProvider'
/** @public */ /** @public */
export interface DialogProps { export interface TLUiDialogProps {
onClose: () => void onClose: () => void
} }
/** @public */ /** @public */
export interface TLDialog { export interface TLUiDialog {
id: string id: string
onClose?: () => void onClose?: () => void
component: (props: DialogProps) => any component: (props: TLUiDialogProps) => any
} }
/** @public */ /** @public */
export type DialogsContextType = { export type TLUiDialogsContextType = {
addDialog: (dialog: Omit<TLDialog, 'id'> & { id?: string }) => string addDialog: (dialog: Omit<TLUiDialog, 'id'> & { id?: string }) => string
removeDialog: (id: string) => string removeDialog: (id: string) => string
updateDialog: (id: string, newDialogData: Partial<TLDialog>) => string updateDialog: (id: string, newDialogData: Partial<TLUiDialog>) => string
clearDialogs: () => void clearDialogs: () => void
dialogs: TLDialog[] dialogs: TLUiDialog[]
} }
/** @public */ /** @internal */
export const DialogsContext = createContext({} as DialogsContextType) export const DialogsContext = createContext({} as TLUiDialogsContextType)
/** @public */ /** @internal */
export type DialogsProviderProps = { export type DialogsProviderProps = {
overrides?: (editor: Editor) => DialogsContextType overrides?: (editor: Editor) => TLUiDialogsContextType
children: any children: any
} }
/** @public */ /** @internal */
export function DialogsProvider({ children }: DialogsProviderProps) { export function DialogsProvider({ children }: DialogsProviderProps) {
const editor = useEditor() const editor = useEditor()
const trackEvent = useEvents() const trackEvent = useEvents()
const [dialogs, setDialogs] = useState<TLDialog[]>([]) const [dialogs, setDialogs] = useState<TLUiDialog[]>([])
const addDialog = useCallback( const addDialog = useCallback(
(dialog: Omit<TLDialog, 'id'> & { id?: string }) => { (dialog: Omit<TLUiDialog, 'id'> & { id?: string }) => {
const id = dialog.id ?? uniqueId() const id = dialog.id ?? uniqueId()
setDialogs((d) => { setDialogs((d) => {
return [...d.filter((m) => m.id !== dialog.id), { ...dialog, id }] return [...d.filter((m) => m.id !== dialog.id), { ...dialog, id }]
@ -55,7 +55,7 @@ export function DialogsProvider({ children }: DialogsProviderProps) {
) )
const updateDialog = useCallback( const updateDialog = useCallback(
(id: string, newDialogData: Partial<TLDialog>) => { (id: string, newDialogData: Partial<TLUiDialog>) => {
setDialogs((d) => setDialogs((d) =>
d.map((m) => { d.map((m) => {
if (m.id === id) { if (m.id === id) {

View file

@ -2,7 +2,7 @@ import { useEditor } from '@tldraw/editor'
import { useEffect } from 'react' import { useEffect } from 'react'
import { useToasts } from './useToastsProvider' import { useToasts } from './useToastsProvider'
/** @public */ /** @internal */
export function useEditorEvents() { export function useEditorEvents() {
const editor = useEditor() const editor = useEditor()
const { addToast } = useToasts() const { addToast } = useToasts()

View file

@ -96,16 +96,19 @@ export type TLUiEventHandler<T extends keyof TLUiEventMap = keyof TLUiEventMap>
/** @internal */ /** @internal */
const defaultEventHandler: TLUiEventHandler = () => void null const defaultEventHandler: TLUiEventHandler = () => void null
/** @internal */
export const EventsContext = React.createContext({} as TLUiEventHandler)
/** @public */ /** @public */
export type TLUiEventContextType = TLUiEventHandler<keyof TLUiEventMap>
/** @internal */
export const EventsContext = React.createContext<TLUiEventContextType>({} as TLUiEventContextType)
/** @internal */
export type EventsProviderProps = { export type EventsProviderProps = {
onEvent?: TLUiEventHandler onEvent?: TLUiEventHandler
children: any children: any
} }
/** @public */ /** @internal */
export function EventsProvider({ onEvent, children }: EventsProviderProps) { export function EventsProvider({ onEvent, children }: EventsProviderProps) {
return ( return (
<EventsContext.Provider value={onEvent ?? defaultEventHandler}> <EventsContext.Provider value={onEvent ?? defaultEventHandler}>
@ -115,6 +118,6 @@ export function EventsProvider({ onEvent, children }: EventsProviderProps) {
} }
/** @public */ /** @public */
export function useEvents(): TLUiEventHandler<keyof TLUiEventMap> { export function useEvents() {
return React.useContext(EventsContext) return React.useContext(EventsContext)
} }

View file

@ -2,41 +2,41 @@ import { Editor, useEditor } from '@tldraw/editor'
import { compact } from '@tldraw/utils' import { compact } from '@tldraw/utils'
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { track } from 'signia-react' import { track } from 'signia-react'
import { TLUiLanguage } from '../..'
import { KeyboardShortcutsDialog } from '../components/KeyboardShortcutsDialog' import { KeyboardShortcutsDialog } from '../components/KeyboardShortcutsDialog'
import { MenuSchema, menuCustom, menuGroup, menuItem } from './menuHelpers' import { TLUiMenuSchema, menuCustom, menuGroup, menuItem } from './menuHelpers'
import { useActions } from './useActions' import { useActions } from './useActions'
import { useDialogs } from './useDialogsProvider' import { useDialogs } from './useDialogsProvider'
import { TLListedTranslations } from './useTranslation/translations'
import { useLanguages } from './useTranslation/useLanguages' import { useLanguages } from './useTranslation/useLanguages'
/** @public */ /** @public */
export type HelpMenuSchemaProviderType = MenuSchema export type TLUiHelpMenuSchemaContextType = TLUiMenuSchema
/** @internal */ /** @internal */
export const HelpMenuSchemaContext = React.createContext({} as HelpMenuSchemaProviderType) export const HelpMenuSchemaContext = React.createContext({} as TLUiHelpMenuSchemaContextType)
/** @public */ /** @public */
export type HelpMenuSchemaProviderProps = { export type TLUiHelpMenuSchemaProviderProps = {
overrides?: ( overrides?: (
editor: Editor, editor: Editor,
schema: HelpMenuSchemaProviderType, schema: TLUiHelpMenuSchemaContextType,
helpers: { helpers: {
actions: ReturnType<typeof useActions> actions: ReturnType<typeof useActions>
languages: TLListedTranslations languages: readonly TLUiLanguage[]
currentLanguage: string currentLanguage: string
oneSelected: boolean oneSelected: boolean
twoSelected: boolean twoSelected: boolean
threeSelected: boolean threeSelected: boolean
} }
) => HelpMenuSchemaProviderType ) => TLUiHelpMenuSchemaContextType
children: any children: any
} }
/** @public */ /** @internal */
export const HelpMenuSchemaProvider = track(function HelpMenuSchemaProvider({ export const HelpMenuSchemaProvider = track(function HelpMenuSchemaProvider({
overrides, overrides,
children, children,
}: HelpMenuSchemaProviderProps) { }: TLUiHelpMenuSchemaProviderProps) {
const editor = useEditor() const editor = useEditor()
const actions = useActions() const actions = useActions()
@ -49,8 +49,8 @@ export const HelpMenuSchemaProvider = track(function HelpMenuSchemaProvider({
const { languages, currentLanguage } = useLanguages() const { languages, currentLanguage } = useLanguages()
const { addDialog } = useDialogs() const { addDialog } = useDialogs()
const helpMenuSchema = useMemo<MenuSchema>(() => { const helpTLUiMenuSchema = useMemo<TLUiMenuSchema>(() => {
const helpMenuSchema = compact([ const helpTLUiMenuSchema = compact([
menuGroup( menuGroup(
'top', 'top',
menuCustom('LANGUAGE_MENU', { readonlyOk: true }), menuCustom('LANGUAGE_MENU', { readonlyOk: true }),
@ -66,7 +66,7 @@ export const HelpMenuSchemaProvider = track(function HelpMenuSchemaProvider({
]) ])
if (overrides) { if (overrides) {
return overrides(editor, helpMenuSchema, { return overrides(editor, helpTLUiMenuSchema, {
actions, actions,
currentLanguage, currentLanguage,
languages, languages,
@ -76,7 +76,7 @@ export const HelpMenuSchemaProvider = track(function HelpMenuSchemaProvider({
}) })
} }
return helpMenuSchema return helpTLUiMenuSchema
}, [ }, [
editor, editor,
overrides, overrides,
@ -90,18 +90,18 @@ export const HelpMenuSchemaProvider = track(function HelpMenuSchemaProvider({
]) ])
return ( return (
<HelpMenuSchemaContext.Provider value={helpMenuSchema}> <HelpMenuSchemaContext.Provider value={helpTLUiMenuSchema}>
{children} {children}
</HelpMenuSchemaContext.Provider> </HelpMenuSchemaContext.Provider>
) )
}) })
/** @public */ /** @public */
export function useHelpMenuSchema(): MenuSchema { export function useHelpMenuSchema(): TLUiMenuSchema {
const ctx = React.useContext(HelpMenuSchemaContext) const ctx = React.useContext(HelpMenuSchemaContext)
if (!ctx) { if (!ctx) {
throw new Error('useHelpMenuSchema must be used inside of a helpMenuSchemaProvider.') throw new Error('useHelpMenuSchema must be used inside of a helpTLUiMenuSchemaProvider.')
} }
return ctx return ctx

View file

@ -1,6 +1,6 @@
import { useLayoutEffect } from 'react' import { useLayoutEffect } from 'react'
/** @public */ /** @internal */
export function useHighDpiCanvas(ref: React.RefObject<HTMLCanvasElement>, dpr: number) { export function useHighDpiCanvas(ref: React.RefObject<HTMLCanvasElement>, dpr: number) {
// Match the resolution of the client // Match the resolution of the client
useLayoutEffect(() => { useLayoutEffect(() => {

View file

@ -2,38 +2,38 @@ import { Editor, useEditor } from '@tldraw/editor'
import { compact } from '@tldraw/utils' import { compact } from '@tldraw/utils'
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { track } from 'signia-react' import { track } from 'signia-react'
import { MenuSchema, menuGroup, menuItem } from './menuHelpers' import { TLUiMenuSchema, menuGroup, menuItem } from './menuHelpers'
import { ActionsContextType, useActions } from './useActions' import { TLUiActionsContextType, useActions } from './useActions'
import { ToolsContextType, useTools } from './useTools' import { TLUiToolsContextType, useTools } from './useTools'
/** @public */ /** @public */
export type KeyboardShortcutsSchemaContextType = MenuSchema export type TLUiKeyboardShortcutsSchemaContextType = TLUiMenuSchema
/** @public */ /** @internal */
export const KeyboardShortcutsSchemaContext = React.createContext( export const KeyboardShortcutsSchemaContext = React.createContext(
{} as KeyboardShortcutsSchemaContextType {} as TLUiKeyboardShortcutsSchemaContextType
) )
/** @public */ /** @public */
export type KeyboardShortcutsSchemaProviderProps = { export type TLUiKeyboardShortcutsSchemaProviderProps = {
overrides?: ( overrides?: (
editor: Editor, editor: Editor,
schema: KeyboardShortcutsSchemaContextType, schema: TLUiKeyboardShortcutsSchemaContextType,
more: { tools: ToolsContextType; actions: ActionsContextType } more: { tools: TLUiToolsContextType; actions: TLUiActionsContextType }
) => KeyboardShortcutsSchemaContextType ) => TLUiKeyboardShortcutsSchemaContextType
children: any children: any
} }
/** @public */ /** @internal */
export const KeyboardShortcutsSchemaProvider = track(function KeyboardShortcutsSchemaProvider({ export const KeyboardShortcutsSchemaProvider = track(function KeyboardShortcutsSchemaProvider({
overrides, overrides,
children, children,
}: KeyboardShortcutsSchemaProviderProps) { }: TLUiKeyboardShortcutsSchemaProviderProps) {
const editor = useEditor() const editor = useEditor()
const tools = useTools() const tools = useTools()
const actions = useActions() const actions = useActions()
const keyboardShortcutsSchema = useMemo<MenuSchema>(() => { const keyboardShortcutsSchema = useMemo<TLUiMenuSchema>(() => {
const keyboardShortcutsSchema = compact([ const keyboardShortcutsSchema = compact([
menuGroup( menuGroup(
'shortcuts-dialog.tools', 'shortcuts-dialog.tools',
@ -118,7 +118,7 @@ export const KeyboardShortcutsSchemaProvider = track(function KeyboardShortcutsS
}) })
/** @public */ /** @public */
export function useKeyboardShortcutsSchema(): KeyboardShortcutsSchemaContextType { export function useKeyboardShortcutsSchema(): TLUiKeyboardShortcutsSchemaContextType {
const ctx = React.useContext(KeyboardShortcutsSchemaContext) const ctx = React.useContext(KeyboardShortcutsSchemaContext)
if (!ctx) { if (!ctx) {

View file

@ -3,7 +3,7 @@ import { compact } from '@tldraw/utils'
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { useValue } from 'signia-react' import { useValue } from 'signia-react'
import { import {
MenuSchema, TLUiMenuSchema,
menuCustom, menuCustom,
menuGroup, menuGroup,
menuItem, menuItem,
@ -20,16 +20,16 @@ import { useHasLinkShapeSelected } from './useHasLinkShapeSelected'
import { useShowAutoSizeToggle } from './useShowAutoSizeToggle' import { useShowAutoSizeToggle } from './useShowAutoSizeToggle'
/** @public */ /** @public */
export type MenuSchemaContextType = MenuSchema export type TLUiMenuSchemaContextType = TLUiMenuSchema
/** @internal */
export const TLUiMenuSchemaContext = React.createContext({} as TLUiMenuSchemaContextType)
/** @public */ /** @public */
export const MenuSchemaContext = React.createContext({} as MenuSchemaContextType) export type TLUiMenuSchemaProviderProps = {
/** @public */
export type MenuSchemaProviderProps = {
overrides?: ( overrides?: (
editor: Editor, editor: Editor,
schema: MenuSchemaContextType, schema: TLUiMenuSchemaContextType,
helpers: { helpers: {
actions: ReturnType<typeof useActions> actions: ReturnType<typeof useActions>
noneSelected: boolean noneSelected: boolean
@ -37,12 +37,12 @@ export type MenuSchemaProviderProps = {
twoSelected: boolean twoSelected: boolean
threeSelected: boolean threeSelected: boolean
} }
) => MenuSchemaContextType ) => TLUiMenuSchemaContextType
children: any children: any
} }
/** @public */ /** @internal */
export function MenuSchemaProvider({ overrides, children }: MenuSchemaProviderProps) { export function TLUiMenuSchemaProvider({ overrides, children }: TLUiMenuSchemaProviderProps) {
const editor = useEditor() const editor = useEditor()
const actions = useActions() const actions = useActions()
@ -79,7 +79,7 @@ export function MenuSchemaProvider({ overrides, children }: MenuSchemaProviderPr
const canRedo = useCanRedo() const canRedo = useCanRedo()
const isZoomedTo100 = useValue('isZoomedTo100', () => editor.zoomLevel === 1, [editor]) const isZoomedTo100 = useValue('isZoomedTo100', () => editor.zoomLevel === 1, [editor])
const menuSchema = useMemo<MenuSchema>(() => { const menuSchema = useMemo<TLUiMenuSchema>(() => {
const menuSchema = compact([ const menuSchema = compact([
menuGroup( menuGroup(
'menu', 'menu',
@ -221,15 +221,17 @@ export function MenuSchemaProvider({ overrides, children }: MenuSchemaProviderPr
isZoomedTo100, isZoomedTo100,
]) ])
return <MenuSchemaContext.Provider value={menuSchema}>{children}</MenuSchemaContext.Provider> return (
<TLUiMenuSchemaContext.Provider value={menuSchema}>{children}</TLUiMenuSchemaContext.Provider>
)
} }
/** @public */ /** @public */
export function useMenuSchema(): MenuSchema { export function useMenuSchema(): TLUiMenuSchema {
const ctx = React.useContext(MenuSchemaContext) const ctx = React.useContext(TLUiMenuSchemaContext)
if (!ctx) { if (!ctx) {
throw new Error('useMenuSchema must be used inside of a MenuSchemaProvider.') throw new Error('useMenuSchema must be used inside of a TLUiMenuSchemaProvider.')
} }
return ctx return ctx

View file

@ -1,5 +1,5 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { TLUiIconTypes } from '../icon-types' import { iconTypes } from '../icon-types'
import { useAssetUrls } from './useAssetUrls' import { useAssetUrls } from './useAssetUrls'
/** @internal */ /** @internal */
@ -16,7 +16,7 @@ export function usePreloadIcons(): boolean {
// all of the icons it can so that they're available when we need them. // all of the icons it can so that they're available when we need them.
await Promise.allSettled( await Promise.allSettled(
TLUiIconTypes.map((icon) => { iconTypes.map((icon) => {
const image = new Image() const image = new Image()
image.src = assetUrls.icons[icon] image.src = assetUrls.icons[icon]
return image.decode() return image.decode()

View file

@ -1,7 +1,7 @@
import { uniqueId, useEditor } from '@tldraw/editor' import { uniqueId, useEditor } from '@tldraw/editor'
import { useCallback, useRef } from 'react' import { useCallback, useRef } from 'react'
/** @public */ /** @internal */
export function usePrint() { export function usePrint() {
const editor = useEditor() const editor = useEditor()
const prevPrintEl = useRef<HTMLDivElement | null>(null) const prevPrintEl = useRef<HTMLDivElement | null>(null)

View file

@ -2,45 +2,45 @@ import { Editor, uniqueId } from '@tldraw/editor'
import { createContext, useCallback, useContext, useState } from 'react' import { createContext, useCallback, useContext, useState } from 'react'
/** @public */ /** @public */
export interface TLToast { export interface TLUiToast {
id: string id: string
icon?: string icon?: string
title?: string title?: string
description?: string description?: string
actions?: TLToastAction[] actions?: TLUiToastAction[]
keepOpen?: boolean keepOpen?: boolean
closeLabel?: string closeLabel?: string
} }
/** @public */ /** @public */
export interface TLToastAction { export interface TLUiToastAction {
type: 'primary' | 'secondary' | 'warn' type: 'primary' | 'secondary' | 'warn'
label: string label: string
onClick: () => void onClick: () => void
} }
/** @public */ /** @public */
export type ToastsContextType = { export type TLUiToastsContextType = {
addToast: (toast: Omit<TLToast, 'id'> & { id?: string }) => string addToast: (toast: Omit<TLUiToast, 'id'> & { id?: string }) => string
removeToast: (id: TLToast['id']) => string removeToast: (id: TLUiToast['id']) => string
clearToasts: () => void clearToasts: () => void
toasts: TLToast[] toasts: TLUiToast[]
} }
/** @public */ /** @internal */
export const ToastsContext = createContext({} as ToastsContextType) export const ToastsContext = createContext({} as TLUiToastsContextType)
/** @public */ /** @internal */
export type ToastsProviderProps = { export type ToastsProviderProps = {
overrides?: (editor: Editor) => ToastsContextType overrides?: (editor: Editor) => TLUiToastsContextType
children: any children: any
} }
/** @public */ /** @internal */
export function ToastsProvider({ children }: ToastsProviderProps) { export function ToastsProvider({ children }: ToastsProviderProps) {
const [toasts, setToasts] = useState<TLToast[]>([]) const [toasts, setToasts] = useState<TLUiToast[]>([])
const addToast = useCallback((toast: Omit<TLToast, 'id'> & { id?: string }) => { const addToast = useCallback((toast: Omit<TLUiToast, 'id'> & { id?: string }) => {
const id = toast.id ?? uniqueId() const id = toast.id ?? uniqueId()
setToasts((d) => [...d.filter((m) => m.id !== toast.id), { ...toast, id }]) setToasts((d) => [...d.filter((m) => m.id !== toast.id), { ...toast, id }])
return id return id

View file

@ -2,18 +2,18 @@ import { Editor, featureFlags, useEditor } from '@tldraw/editor'
import { compact } from '@tldraw/utils' import { compact } from '@tldraw/utils'
import React from 'react' import React from 'react'
import { useValue } from 'signia-react' import { useValue } from 'signia-react'
import { ToolItem, ToolsContextType, useTools } from './useTools' import { TLUiToolItem, TLUiToolsContextType, useTools } from './useTools'
/** @public */ /** @public */
export type ToolbarItem = { export type TLUiToolbarItem = {
id: string id: string
type: 'item' type: 'item'
readonlyOk: boolean readonlyOk: boolean
toolItem: ToolItem toolItem: TLUiToolItem
} }
/** @public */ /** @public */
export function toolbarItem(toolItem: ToolItem): ToolbarItem { export function toolbarItem(toolItem: TLUiToolItem): TLUiToolbarItem {
return { return {
id: toolItem.id, id: toolItem.id,
type: 'item', type: 'item',
@ -23,30 +23,30 @@ export function toolbarItem(toolItem: ToolItem): ToolbarItem {
} }
/** @public */ /** @public */
export type ToolbarSchemaContextType = ToolbarItem[] export type TLUiToolbarSchemaContextType = TLUiToolbarItem[]
/** @internal */
export const ToolbarSchemaContext = React.createContext([] as TLUiToolbarSchemaContextType)
/** @public */ /** @public */
export const ToolbarSchemaContext = React.createContext([] as ToolbarSchemaContextType) export type TLUiToolbarSchemaProviderProps = {
/** @public */
export type ToolbarSchemaProviderProps = {
overrides?: ( overrides?: (
editor: Editor, editor: Editor,
schema: ToolbarSchemaContextType, schema: TLUiToolbarSchemaContextType,
more: { tools: ToolsContextType } more: { tools: TLUiToolsContextType }
) => ToolbarSchemaContextType ) => TLUiToolbarSchemaContextType
children: any children: any
} }
/** @public */ /** @internal */
export function ToolbarSchemaProvider({ overrides, children }: ToolbarSchemaProviderProps) { export function ToolbarSchemaProvider({ overrides, children }: TLUiToolbarSchemaProviderProps) {
const editor = useEditor() const editor = useEditor()
const tools = useTools() const tools = useTools()
const highlighterEnabled = useValue(featureFlags.highlighterTool) const highlighterEnabled = useValue(featureFlags.highlighterTool)
const toolbarSchema = React.useMemo<ToolbarSchemaContextType>(() => { const toolbarSchema = React.useMemo<TLUiToolbarSchemaContextType>(() => {
const schema: ToolbarSchemaContextType = compact([ const schema: TLUiToolbarSchemaContextType = compact([
toolbarItem(tools.select), toolbarItem(tools.select),
toolbarItem(tools.hand), toolbarItem(tools.hand),
toolbarItem(tools.draw), toolbarItem(tools.draw),

View file

@ -6,13 +6,13 @@ import { TLUiIconType } from '../icon-types'
import { useDialogs } from './useDialogsProvider' import { useDialogs } from './useDialogsProvider'
import { TLUiEventSource, useEvents } from './useEventsProvider' import { TLUiEventSource, useEvents } from './useEventsProvider'
import { useInsertMedia } from './useInsertMedia' import { useInsertMedia } from './useInsertMedia'
import { TLTranslationKey } from './useTranslation/TLTranslationKey' import { TLUiTranslationKey } from './useTranslation/TLUiTranslationKey'
/** @public */ /** @public */
export interface ToolItem { export interface TLUiToolItem {
id: string id: string
label: TLTranslationKey label: TLUiTranslationKey
shortcutsLabel?: TLTranslationKey shortcutsLabel?: TLUiTranslationKey
icon: TLUiIconType icon: TLUiIconType
onSelect: (source: TLUiEventSource) => void onSelect: (source: TLUiEventSource) => void
kbd?: string kbd?: string
@ -23,23 +23,23 @@ export interface ToolItem {
} }
/** @public */ /** @public */
export type ToolsContextType = Record<string, ToolItem> export type TLUiToolsContextType = Record<string, TLUiToolItem>
/** @internal */
export const ToolsContext = React.createContext({} as TLUiToolsContextType)
/** @public */ /** @public */
export const ToolsContext = React.createContext({} as ToolsContextType) export type TLUiToolsProviderProps = {
/** @public */
export type ToolsProviderProps = {
overrides?: ( overrides?: (
editor: Editor, editor: Editor,
tools: ToolsContextType, tools: TLUiToolsContextType,
helpers: { insertMedia: () => void } helpers: { insertMedia: () => void }
) => ToolsContextType ) => TLUiToolsContextType
children: any children: any
} }
/** @public */ /** @internal */
export function ToolsProvider({ overrides, children }: ToolsProviderProps) { export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) {
const editor = useEditor() const editor = useEditor()
const trackEvent = useEvents() const trackEvent = useEvents()
@ -48,8 +48,8 @@ export function ToolsProvider({ overrides, children }: ToolsProviderProps) {
const highlighterEnabled = useValue(featureFlags.highlighterTool) const highlighterEnabled = useValue(featureFlags.highlighterTool)
const tools = React.useMemo<ToolsContextType>(() => { const tools = React.useMemo<TLUiToolsContextType>(() => {
const toolsArray: ToolItem[] = [ const toolsArray: TLUiToolItem[] = [
{ {
id: 'select', id: 'select',
label: 'tool.select', label: 'tool.select',
@ -96,7 +96,7 @@ export function ToolsProvider({ overrides, children }: ToolsProviderProps) {
}, },
...[...TL_GEO_TYPES].map((id) => ({ ...[...TL_GEO_TYPES].map((id) => ({
id, id,
label: `tool.${id}` as TLTranslationKey, label: `tool.${id}` as TLUiTranslationKey,
readonlyOk: false, readonlyOk: false,
meta: { meta: {
geo: id, geo: id,
@ -218,7 +218,7 @@ export function ToolsProvider({ overrides, children }: ToolsProviderProps) {
}) })
} }
const tools = makeTools(toolsArray) const tools = Object.fromEntries(toolsArray.map((t) => [t.id, t]))
if (overrides) { if (overrides) {
return overrides(editor, tools, { insertMedia }) return overrides(editor, tools, { insertMedia })
@ -230,10 +230,6 @@ export function ToolsProvider({ overrides, children }: ToolsProviderProps) {
return <ToolsContext.Provider value={tools}>{children}</ToolsContext.Provider> return <ToolsContext.Provider value={tools}>{children}</ToolsContext.Provider>
} }
function makeTools(tools: ToolItem[]) {
return Object.fromEntries(tools.map((t) => [t.id, t]))
}
/** @public */ /** @public */
export function useTools() { export function useTools() {
const ctx = React.useContext(ToolsContext) const ctx = React.useContext(ToolsContext)

View file

@ -2,7 +2,7 @@
// Do not edit manually. // Do not edit manually.
/** @public */ /** @public */
export type TLTranslationKey = export type TLUiTranslationKey =
| 'action.convert-to-bookmark' | 'action.convert-to-bookmark'
| 'action.convert-to-embed' | 'action.convert-to-embed'
| 'action.open-embed-link' | 'action.open-embed-link'

View file

@ -1,7 +1,7 @@
import { UiAssetUrls } from '../../assetUrls' import { TLUiAssetUrls } from '../../assetUrls'
import { TLUiTranslationKey } from './TLUiTranslationKey'
import { DEFAULT_TRANSLATION } from './defaultTranslation' import { DEFAULT_TRANSLATION } from './defaultTranslation'
import { LANGUAGES } from './languages' import { LANGUAGES } from './languages'
import { TLTranslationKey } from './TLTranslationKey'
// The default language (english) must have a value for every message. // The default language (english) must have a value for every message.
// Other languages may have missing messages. If the application finds // Other languages may have missing messages. If the application finds
@ -11,42 +11,29 @@ import { TLTranslationKey } from './TLTranslationKey'
/* ----------------- (do not change) ---------------- */ /* ----------------- (do not change) ---------------- */
/** @public */ /** @public */
export type TLListedTranslation = { export type TLUiLanguage = {
readonly locale: string readonly locale: string
readonly label: string readonly label: string
} }
/** @public */ /** @public */
export type TLListedTranslations = readonly TLListedTranslation[] export type TLUiTranslation = {
/** @public */
export type TLTranslationMessages = Record<TLTranslationKey, string>
/** @public */
export type TLTranslation = {
readonly locale: string readonly locale: string
readonly label: string readonly label: string
readonly messages: TLTranslationMessages readonly messages: Record<TLUiTranslationKey, string>
} }
/** @public */ const EN_TRANSLATION: TLUiTranslation = {
export type TLTranslations = TLTranslation[]
/** @public */
export type TLTranslationLocale = TLTranslations[number]['locale']
/** @public */
export const EN_TRANSLATION: TLTranslation = {
locale: 'en', locale: 'en',
label: 'English', label: 'English',
messages: DEFAULT_TRANSLATION as TLTranslationMessages, messages: DEFAULT_TRANSLATION as TLUiTranslation['messages'],
} }
/** @public */ /** @internal */
export async function fetchTranslation( export async function fetchTranslation(
locale: TLTranslationLocale, locale: TLUiTranslation['locale'],
assetUrls: UiAssetUrls assetUrls: TLUiAssetUrls
): Promise<TLTranslation> { ): Promise<TLUiTranslation> {
const mainRes = await fetch(assetUrls.translations.en) const mainRes = await fetch(assetUrls.translations.en)
if (!mainRes.ok) { if (!mainRes.ok) {
@ -66,7 +53,7 @@ export async function fetchTranslation(
} }
const res = await fetch(assetUrls.translations[language.locale]) const res = await fetch(assetUrls.translations[language.locale])
const messages: TLTranslationMessages = await res.json() const messages: TLUiTranslation['messages'] = await res.json()
if (!messages) { if (!messages) {
console.warn(`No messages found for locale ${locale}`) console.warn(`No messages found for locale ${locale}`)
@ -76,7 +63,7 @@ export async function fetchTranslation(
const missing: string[] = [] const missing: string[] = []
for (const key in EN_TRANSLATION) { for (const key in EN_TRANSLATION) {
if (!messages[key as TLTranslationKey]) { if (!messages[key as TLUiTranslationKey]) {
missing.push(key) missing.push(key)
} }
} }
@ -91,11 +78,3 @@ export async function fetchTranslation(
messages: { ...EN_TRANSLATION.messages, ...messages }, messages: { ...EN_TRANSLATION.messages, ...messages },
} }
} }
/** @public */
export async function getTranslation(
locale: TLTranslationLocale,
assetUrls: UiAssetUrls
): Promise<TLTranslation> {
return await fetchTranslation(locale, assetUrls)
}

View file

@ -1,8 +1,12 @@
import { useEditor } from '@tldraw/editor' import { useEditor } from '@tldraw/editor'
import { LANGUAGES } from './languages' import { LANGUAGES } from './languages'
import { TLUiLanguage } from './translations'
/** @public */ /** @internal */
export function useLanguages() { export function useLanguages() {
const editor = useEditor() const editor = useEditor()
return { languages: LANGUAGES, currentLanguage: editor.locale } return {
languages: LANGUAGES as readonly TLUiLanguage[],
currentLanguage: editor.locale,
}
} }

View file

@ -2,12 +2,12 @@ import { useEditor } from '@tldraw/editor'
import * as React from 'react' import * as React from 'react'
import { track } from 'signia-react' import { track } from 'signia-react'
import { useAssetUrls } from '../useAssetUrls' import { useAssetUrls } from '../useAssetUrls'
import { TLTranslationKey } from './TLTranslationKey' import { TLUiTranslationKey } from './TLUiTranslationKey'
import { DEFAULT_TRANSLATION } from './defaultTranslation' import { DEFAULT_TRANSLATION } from './defaultTranslation'
import { TLTranslation, getTranslation } from './translations' import { TLUiTranslation, fetchTranslation } from './translations'
/** @public */ /** @public */
export interface TranslationProviderProps { export interface TLUiTranslationProviderProps {
children: any children: any
/** /**
* (optional) A collection of overrides different locales. * (optional) A collection of overrides different locales.
@ -21,24 +21,29 @@ export interface TranslationProviderProps {
overrides?: Record<string, Record<string, string>> overrides?: Record<string, Record<string, string>>
} }
const TranslationsContext = React.createContext<TLTranslation>({} as TLTranslation) /** @public */
export type TLUiTranslationContextType = TLUiTranslation
const TranslationsContext = React.createContext<TLUiTranslationContextType>(
{} as TLUiTranslationContextType
)
const useCurrentTranslation = () => React.useContext(TranslationsContext) const useCurrentTranslation = () => React.useContext(TranslationsContext)
/** /**
* Provides a translation context to the editor. * Provides a translation context to the editor.
* *
* @public * @internal
*/ */
export const TranslationProvider = track(function TranslationProvider({ export const TranslationProvider = track(function TranslationProvider({
overrides, overrides,
children, children,
}: TranslationProviderProps) { }: TLUiTranslationProviderProps) {
const editor = useEditor() const editor = useEditor()
const locale = editor.locale const locale = editor.locale
const getAssetUrl = useAssetUrls() const getAssetUrl = useAssetUrls()
const [currentTranslation, setCurrentTranslation] = React.useState<TLTranslation>(() => { const [currentTranslation, setCurrentTranslation] = React.useState<TLUiTranslation>(() => {
if (overrides && overrides['en']) { if (overrides && overrides['en']) {
return { return {
locale: 'en', locale: 'en',
@ -58,7 +63,7 @@ export const TranslationProvider = track(function TranslationProvider({
let isCancelled = false let isCancelled = false
async function loadTranslation() { async function loadTranslation() {
const translation = await getTranslation(locale, getAssetUrl) const translation = await fetchTranslation(locale, getAssetUrl)
if (translation && !isCancelled) { if (translation && !isCancelled) {
if (overrides && overrides[locale]) { if (overrides && overrides[locale]) {
@ -101,7 +106,7 @@ export const TranslationProvider = track(function TranslationProvider({
export function useTranslation() { export function useTranslation() {
const translation = useCurrentTranslation() const translation = useCurrentTranslation()
return React.useCallback( return React.useCallback(
function msg(id: TLTranslationKey) { function msg(id: TLUiTranslationKey) {
return translation.messages[id] ?? id return translation.messages[id] ?? id
}, },
[translation] [translation]

View file

@ -166,7 +166,7 @@ export type TLUiIconType =
| 'zoom-out' | 'zoom-out'
/** @public */ /** @public */
export const TLUiIconTypes = [ export const iconTypes = [
'align-bottom-center', 'align-bottom-center',
'align-bottom-left', 'align-bottom-left',
'align-bottom-right', 'align-bottom-right',

View file

@ -4,15 +4,15 @@ import { useMemo } from 'react'
import { ActionsProviderProps } from './hooks/useActions' import { ActionsProviderProps } from './hooks/useActions'
import { ActionsMenuSchemaProviderProps } from './hooks/useActionsMenuSchema' import { ActionsMenuSchemaProviderProps } from './hooks/useActionsMenuSchema'
import { useBreakpoint } from './hooks/useBreakpoint' import { useBreakpoint } from './hooks/useBreakpoint'
import { ContextMenuSchemaProviderProps } from './hooks/useContextMenuSchema' import { TLUiContextMenuSchemaProviderProps } from './hooks/useContextMenuSchema'
import { useDialogs } from './hooks/useDialogsProvider' import { useDialogs } from './hooks/useDialogsProvider'
import { HelpMenuSchemaProviderProps } from './hooks/useHelpMenuSchema' import { TLUiHelpMenuSchemaProviderProps } from './hooks/useHelpMenuSchema'
import { KeyboardShortcutsSchemaProviderProps } from './hooks/useKeyboardShortcutsSchema' import { TLUiKeyboardShortcutsSchemaProviderProps } from './hooks/useKeyboardShortcutsSchema'
import { MenuSchemaProviderProps } from './hooks/useMenuSchema' import { TLUiMenuSchemaProviderProps } from './hooks/useMenuSchema'
import { useToasts } from './hooks/useToastsProvider' import { useToasts } from './hooks/useToastsProvider'
import { ToolbarSchemaProviderProps } from './hooks/useToolbarSchema' import { TLUiToolbarSchemaProviderProps } from './hooks/useToolbarSchema'
import { ToolsProviderProps } from './hooks/useTools' import { TLUiToolsProviderProps } from './hooks/useTools'
import { TranslationProviderProps, useTranslation } from './hooks/useTranslation/useTranslation' import { TLUiTranslationProviderProps, useTranslation } from './hooks/useTranslation/useTranslation'
/** @public */ /** @public */
export function useDefaultHelpers() { export function useDefaultHelpers() {
@ -49,51 +49,47 @@ export function useDefaultHelpers() {
type DefaultHelpers = ReturnType<typeof useDefaultHelpers> type DefaultHelpers = ReturnType<typeof useDefaultHelpers>
export type TldrawUiOverride<Type, Helpers> = ( export type TLUiOverride<Type, Helpers> = (editor: Editor, schema: Type, helpers: Helpers) => Type
editor: Editor,
schema: Type,
helpers: Helpers
) => Type
type WithDefaultHelpers<T extends TldrawUiOverride<any, any>> = T extends TldrawUiOverride< type WithDefaultHelpers<T extends TLUiOverride<any, any>> = T extends TLUiOverride<
infer Type, infer Type,
infer Helpers infer Helpers
> >
? TldrawUiOverride<Type, Helpers extends undefined ? DefaultHelpers : Helpers & DefaultHelpers> ? TLUiOverride<Type, Helpers extends undefined ? DefaultHelpers : Helpers & DefaultHelpers>
: never : never
/** @public */ /** @public */
export interface TldrawUiOverrides { export interface TLUiOverrides {
actionsMenu?: WithDefaultHelpers<NonNullable<ActionsMenuSchemaProviderProps['overrides']>> actionsMenu?: WithDefaultHelpers<NonNullable<ActionsMenuSchemaProviderProps['overrides']>>
actions?: WithDefaultHelpers<NonNullable<ActionsProviderProps['overrides']>> actions?: WithDefaultHelpers<NonNullable<ActionsProviderProps['overrides']>>
contextMenu?: WithDefaultHelpers<NonNullable<ContextMenuSchemaProviderProps['overrides']>> contextMenu?: WithDefaultHelpers<NonNullable<TLUiContextMenuSchemaProviderProps['overrides']>>
helpMenu?: WithDefaultHelpers<NonNullable<HelpMenuSchemaProviderProps['overrides']>> helpMenu?: WithDefaultHelpers<NonNullable<TLUiHelpMenuSchemaProviderProps['overrides']>>
menu?: WithDefaultHelpers<NonNullable<MenuSchemaProviderProps['overrides']>> menu?: WithDefaultHelpers<NonNullable<TLUiMenuSchemaProviderProps['overrides']>>
toolbar?: WithDefaultHelpers<NonNullable<ToolbarSchemaProviderProps['overrides']>> toolbar?: WithDefaultHelpers<NonNullable<TLUiToolbarSchemaProviderProps['overrides']>>
keyboardShortcutsMenu?: WithDefaultHelpers< keyboardShortcutsMenu?: WithDefaultHelpers<
NonNullable<KeyboardShortcutsSchemaProviderProps['overrides']> NonNullable<TLUiKeyboardShortcutsSchemaProviderProps['overrides']>
> >
tools?: WithDefaultHelpers<NonNullable<ToolsProviderProps['overrides']>> tools?: WithDefaultHelpers<NonNullable<TLUiToolsProviderProps['overrides']>>
translations?: TranslationProviderProps['overrides'] translations?: TLUiTranslationProviderProps['overrides']
} }
export interface TldrawUiOverridesWithoutDefaults { export interface TLUiOverridesWithoutDefaults {
actionsMenu?: ActionsMenuSchemaProviderProps['overrides'] actionsMenu?: ActionsMenuSchemaProviderProps['overrides']
actions?: ActionsProviderProps['overrides'] actions?: ActionsProviderProps['overrides']
contextMenu?: ContextMenuSchemaProviderProps['overrides'] contextMenu?: TLUiContextMenuSchemaProviderProps['overrides']
helpMenu?: HelpMenuSchemaProviderProps['overrides'] helpMenu?: TLUiHelpMenuSchemaProviderProps['overrides']
menu?: MenuSchemaProviderProps['overrides'] menu?: TLUiMenuSchemaProviderProps['overrides']
toolbar?: ToolbarSchemaProviderProps['overrides'] toolbar?: TLUiToolbarSchemaProviderProps['overrides']
keyboardShortcutsMenu?: KeyboardShortcutsSchemaProviderProps['overrides'] keyboardShortcutsMenu?: TLUiKeyboardShortcutsSchemaProviderProps['overrides']
tools?: ToolsProviderProps['overrides'] tools?: TLUiToolsProviderProps['overrides']
translations?: TranslationProviderProps['overrides'] translations?: TLUiTranslationProviderProps['overrides']
} }
export function mergeOverrides( export function mergeOverrides(
overrides: TldrawUiOverrides[], overrides: TLUiOverrides[],
defaultHelpers: DefaultHelpers defaultHelpers: DefaultHelpers
): TldrawUiOverridesWithoutDefaults { ): TLUiOverridesWithoutDefaults {
const mergedTranslations: TranslationProviderProps['overrides'] = {} const mergedTranslations: TLUiTranslationProviderProps['overrides'] = {}
for (const override of overrides) { for (const override of overrides) {
if (override.translations) { if (override.translations) {
for (const [key, value] of objectMapEntries(override.translations)) { for (const [key, value] of objectMapEntries(override.translations)) {
@ -180,13 +176,13 @@ function useShallowArrayEquality<T extends unknown[]>(array: T): T {
} }
export function useMergedTranslationOverrides( export function useMergedTranslationOverrides(
overrides?: TldrawUiOverrides[] | TldrawUiOverrides overrides?: TLUiOverrides[] | TLUiOverrides
): NonNullable<TranslationProviderProps['overrides']> { ): NonNullable<TLUiTranslationProviderProps['overrides']> {
const overridesArray = useShallowArrayEquality( const overridesArray = useShallowArrayEquality(
overrides == null ? [] : Array.isArray(overrides) ? overrides : [overrides] overrides == null ? [] : Array.isArray(overrides) ? overrides : [overrides]
) )
return useMemo(() => { return useMemo(() => {
const mergedTranslations: TranslationProviderProps['overrides'] = {} const mergedTranslations: TLUiTranslationProviderProps['overrides'] = {}
for (const override of overridesArray) { for (const override of overridesArray) {
if (override.translations) { if (override.translations) {
for (const [key, value] of objectMapEntries(override.translations)) { for (const [key, value] of objectMapEntries(override.translations)) {
@ -203,8 +199,8 @@ export function useMergedTranslationOverrides(
} }
export function useMergedOverrides( export function useMergedOverrides(
overrides?: TldrawUiOverrides[] | TldrawUiOverrides overrides?: TLUiOverrides[] | TLUiOverrides
): TldrawUiOverridesWithoutDefaults { ): TLUiOverridesWithoutDefaults {
const defaultHelpers = useDefaultHelpers() const defaultHelpers = useDefaultHelpers()
const overridesArray = useShallowArrayEquality( const overridesArray = useShallowArrayEquality(
overrides == null ? [] : Array.isArray(overrides) ? overrides : [overrides] overrides == null ? [] : Array.isArray(overrides) ? overrides : [overrides]

View file

@ -85,7 +85,7 @@ async function copyIcons() {
${icons.map((icon) => JSON.stringify(icon.replace('.svg', ''))).join(' | ')} ${icons.map((icon) => JSON.stringify(icon.replace('.svg', ''))).join(' | ')}
/** @public */ /** @public */
export const TLUiIconTypes = [ export const iconTypes = [
${icons.map((icon) => JSON.stringify(icon.replace('.svg', ''))).join(', ')} ${icons.map((icon) => JSON.stringify(icon.replace('.svg', ''))).join(', ')}
] as const` ] as const`
@ -239,10 +239,10 @@ async function copyTranslations() {
// translationKeys.ts // translationKeys.ts
const translationKeys = Object.keys(defaultTranslation).map((key) => `'${key}'`) const translationKeys = Object.keys(defaultTranslation).map((key) => `'${key}'`)
const translationKeysFilePath = join(uiPath, 'TLTranslationKey.ts') const translationKeysFilePath = join(uiPath, 'TLUiTranslationKey.ts')
const translationKeysFile = ` const translationKeysFile = `
/** @public */ /** @public */
export type TLTranslationKey = ${translationKeys.join(' | ')} export type TLUiTranslationKey = ${translationKeys.join(' | ')}
` `
await writeCodeFile( await writeCodeFile(
'scripts/refresh-assets.ts', 'scripts/refresh-assets.ts',