UI components round two (#2847)
This PR: - replaces the `shareZone` prop with `SharePanel` component - replaces the `topZone` prop with `TopPanel` components - replaces the `Button` component with `TldrawUiButton` and subcomponents - adds `TldrawUi` prefix to our primitives - fixes a couple of bugs with the components ### Change Type - [x] `major` — Breaking change
This commit is contained in:
parent
5d87804a76
commit
7ece89a357
83 changed files with 3122 additions and 2826 deletions
|
@ -67,6 +67,13 @@ const components: TLComponents = {
|
|||
</DefaultDebugMenu>
|
||||
)
|
||||
},
|
||||
SharePanel: () => {
|
||||
return (
|
||||
<div className="tlui-share-zone" draggable={false}>
|
||||
<ShareMenu />
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
export function LocalEditor() {
|
||||
|
@ -88,11 +95,6 @@ export function LocalEditor() {
|
|||
overrides={[sharingUiOverrides, fileSystemUiOverrides]}
|
||||
onUiEvent={handleUiEvent}
|
||||
components={components}
|
||||
shareZone={
|
||||
<div className="tlui-share-zone" draggable={false}>
|
||||
<ShareMenu />
|
||||
</div>
|
||||
}
|
||||
inferDarkMode
|
||||
>
|
||||
<LocalMigration />
|
||||
|
|
|
@ -13,8 +13,10 @@ import {
|
|||
Tldraw,
|
||||
TldrawUiMenuGroup,
|
||||
TldrawUiMenuItem,
|
||||
atom,
|
||||
lns,
|
||||
useActions,
|
||||
useValue,
|
||||
} from '@tldraw/tldraw'
|
||||
import { useCallback, useEffect } from 'react'
|
||||
import { useRemoteSyncClient } from '../hooks/useRemoteSyncClient'
|
||||
|
@ -39,6 +41,8 @@ import { SneakyOnDropOverride } from './SneakyOnDropOverride'
|
|||
import { StoreErrorScreen } from './StoreErrorScreen'
|
||||
import { ThemeUpdater } from './ThemeUpdater/ThemeUpdater'
|
||||
|
||||
const shittyOfflineAtom = atom('shitty offline atom', false)
|
||||
|
||||
const components: TLComponents = {
|
||||
ErrorFallback: ({ error }) => {
|
||||
throw error
|
||||
|
@ -78,6 +82,19 @@ const components: TLComponents = {
|
|||
</DefaultKeyboardShortcutsDialog>
|
||||
)
|
||||
},
|
||||
TopPanel: () => {
|
||||
const isOffline = useValue('offline', () => shittyOfflineAtom.get(), [])
|
||||
if (!isOffline) return null
|
||||
return <OfflineIndicator />
|
||||
},
|
||||
SharePanel: () => {
|
||||
return (
|
||||
<div className="tlui-share-zone" draggable={false}>
|
||||
<PeopleMenu />
|
||||
<ShareMenu />
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
export function MultiplayerEditor({
|
||||
|
@ -96,6 +113,12 @@ export function MultiplayerEditor({
|
|||
roomId,
|
||||
})
|
||||
|
||||
const isOffline =
|
||||
storeWithStatus.status === 'synced-remote' && storeWithStatus.connectionStatus === 'offline'
|
||||
useEffect(() => {
|
||||
shittyOfflineAtom.set(isOffline)
|
||||
}, [isOffline])
|
||||
|
||||
const isEmbedded = useIsEmbedded(roomSlug)
|
||||
const sharingUiOverrides = useSharing()
|
||||
const fileSystemUiOverrides = useFileSystem({ isMultiplayer: true })
|
||||
|
@ -118,9 +141,6 @@ export function MultiplayerEditor({
|
|||
return <EmbeddedInIFrameWarning />
|
||||
}
|
||||
|
||||
const isOffline =
|
||||
storeWithStatus.status === 'synced-remote' && storeWithStatus.connectionStatus === 'offline'
|
||||
|
||||
return (
|
||||
<div className="tldraw__editor">
|
||||
<Tldraw
|
||||
|
@ -131,13 +151,6 @@ export function MultiplayerEditor({
|
|||
initialState={isReadOnly ? 'hand' : 'select'}
|
||||
onUiEvent={handleUiEvent}
|
||||
components={components}
|
||||
topZone={isOffline && <OfflineIndicator />}
|
||||
shareZone={
|
||||
<div className="tlui-share-zone" draggable={false}>
|
||||
<PeopleMenu />
|
||||
<ShareMenu />
|
||||
</div>
|
||||
}
|
||||
autoFocus
|
||||
inferDarkMode
|
||||
>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import * as Popover from '@radix-ui/react-popover'
|
||||
import {
|
||||
Button,
|
||||
TldrawUiButton,
|
||||
TldrawUiButtonIcon,
|
||||
TldrawUiButtonLabel,
|
||||
track,
|
||||
useContainer,
|
||||
useEditor,
|
||||
|
@ -73,13 +75,16 @@ export const PeopleMenu = track(function PeopleMenu({
|
|||
)}
|
||||
{!hideShareMenu && (
|
||||
<div className="tlui-people-menu__section">
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="menu"
|
||||
data-wd="people-menu.invite"
|
||||
label={'people-menu.invite'}
|
||||
icon="plus"
|
||||
data-testid="people-menu.invite"
|
||||
onClick={() => editor.addOpenMenu('share menu')}
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonLabel>
|
||||
{msg('people-menu.invite')}
|
||||
<TldrawUiButtonIcon icon="plus" />
|
||||
</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
Button,
|
||||
Icon,
|
||||
TldrawUiButton,
|
||||
TldrawUiButtonIcon,
|
||||
TldrawUiIcon,
|
||||
track,
|
||||
useEditor,
|
||||
usePresence,
|
||||
|
@ -34,16 +35,16 @@ export const PeopleMenuItem = track(function PeopleMenuItem({ userId }: { userId
|
|||
|
||||
return (
|
||||
<div className="tlui-people-menu__item tlui-buttons__horizontal">
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="menu"
|
||||
className="tlui-people-menu__item__button"
|
||||
onClick={() => editor.animateToUser(userId)}
|
||||
onDoubleClick={handleFollowClick}
|
||||
>
|
||||
<Icon icon="color" color={presence.color} />
|
||||
<TldrawUiIcon icon="color" color={presence.color} />
|
||||
<div className="tlui-people-menu__name">{presence.userName ?? 'New User'}</div>
|
||||
</Button>
|
||||
<Button
|
||||
</TldrawUiButton>
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
className="tlui-people-menu__item__follow"
|
||||
title={
|
||||
|
@ -53,11 +54,14 @@ export const PeopleMenuItem = track(function PeopleMenuItem({ userId }: { userId
|
|||
? msg('people-menu.following')
|
||||
: msg('people-menu.follow')
|
||||
}
|
||||
icon={theyAreFollowingYou ? 'leading' : youAreFollowingThem ? 'following' : 'follow'}
|
||||
onClick={handleFollowClick}
|
||||
disabled={theyAreFollowingYou}
|
||||
data-active={youAreFollowingThem || theyAreFollowingYou}
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonIcon
|
||||
icon={theyAreFollowingYou ? 'leading' : youAreFollowingThem ? 'following' : 'follow'}
|
||||
/>
|
||||
</TldrawUiButton>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import * as Popover from '@radix-ui/react-popover'
|
||||
import {
|
||||
Button,
|
||||
TldrawUiButton,
|
||||
TldrawUiButtonIcon,
|
||||
USER_COLORS,
|
||||
track,
|
||||
useContainer,
|
||||
|
@ -88,13 +89,14 @@ export const UserPresenceColorPicker = track(function UserPresenceColorPicker()
|
|||
return (
|
||||
<Popover.Root onOpenChange={handleOpenChange} open={isOpen}>
|
||||
<Popover.Trigger dir="ltr" asChild>
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
className="tlui-people-menu__user__color"
|
||||
icon="color"
|
||||
style={{ color: editor.user.getColor() }}
|
||||
title={msg('people-menu.change-color')}
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonIcon icon="color" />
|
||||
</TldrawUiButton>
|
||||
</Popover.Trigger>
|
||||
<Popover.Portal container={container}>
|
||||
<Popover.Content
|
||||
|
@ -106,11 +108,11 @@ export const UserPresenceColorPicker = track(function UserPresenceColorPicker()
|
|||
>
|
||||
<div className={'tlui-buttons__grid'}>
|
||||
{USER_COLORS.map((item: string) => (
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
key={item}
|
||||
data-id={item}
|
||||
data-wd={item}
|
||||
data-testid={item}
|
||||
aria-label={item}
|
||||
data-state={value === item ? 'hinted' : undefined}
|
||||
title={item}
|
||||
|
@ -120,8 +122,9 @@ export const UserPresenceColorPicker = track(function UserPresenceColorPicker()
|
|||
onPointerDown={handleButtonPointerDown}
|
||||
onPointerUp={handleButtonPointerUp}
|
||||
onClick={handleButtonClick}
|
||||
icon={'color'}
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonIcon icon="color" />
|
||||
</TldrawUiButton>
|
||||
))}
|
||||
</div>
|
||||
</Popover.Content>
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
import { Button, Input, useEditor, useTranslation, useUiEvents, useValue } from '@tldraw/tldraw'
|
||||
import {
|
||||
TldrawUiButton,
|
||||
TldrawUiButtonIcon,
|
||||
TldrawUiInput,
|
||||
useEditor,
|
||||
useTranslation,
|
||||
useUiEvents,
|
||||
useValue,
|
||||
} from '@tldraw/tldraw'
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { UI_OVERRIDE_TODO_EVENT } from '../../utils/useHandleUiEvent'
|
||||
import { UserPresenceColorPicker } from './UserPresenceColorPicker'
|
||||
|
@ -36,7 +44,7 @@ export function UserPresenceEditor() {
|
|||
<div className="tlui-people-menu__user">
|
||||
<UserPresenceColorPicker />
|
||||
{isEditingName ? (
|
||||
<Input
|
||||
<TldrawUiInput
|
||||
className="tlui-people-menu__user__input"
|
||||
defaultValue={userName}
|
||||
onValueChange={handleValueChange}
|
||||
|
@ -62,14 +70,15 @@ export function UserPresenceEditor() {
|
|||
) : null}
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
className="tlui-people-menu__user__edit"
|
||||
data-wd="people-menu.change-name"
|
||||
data-testid="people-menu.change-name"
|
||||
title={msg('people-menu.change-name')}
|
||||
icon={isEditingName ? 'check' : 'edit'}
|
||||
onClick={toggleEditingName}
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonIcon icon={isEditingName ? 'check' : 'edit'} />
|
||||
</TldrawUiButton>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -54,6 +54,13 @@ const components: TLComponents = {
|
|||
</DefaultKeyboardShortcutsDialog>
|
||||
)
|
||||
},
|
||||
SharePanel: () => {
|
||||
return (
|
||||
<div className="tlui-share-zone" draggable={false}>
|
||||
<ExportMenu />
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
type SnapshotEditorProps = {
|
||||
|
@ -79,11 +86,6 @@ export function SnapshotsEditor(props: SnapshotEditorProps) {
|
|||
editor.updateInstanceState({ isReadonly: true })
|
||||
}}
|
||||
components={components}
|
||||
shareZone={
|
||||
<div className="tlui-share-zone" draggable={false}>
|
||||
<ExportMenu />
|
||||
</div>
|
||||
}
|
||||
renderDebugMenuItems={() => <DebugMenuItems />}
|
||||
autoFocus
|
||||
inferDarkMode
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import { Button, LegacyTldrawDocument, useEditor, useValue } from '@tldraw/tldraw'
|
||||
import {
|
||||
LegacyTldrawDocument,
|
||||
TldrawUiButton,
|
||||
TldrawUiButtonLabel,
|
||||
useEditor,
|
||||
useValue,
|
||||
} from '@tldraw/tldraw'
|
||||
|
||||
export function MigrationAnnouncement({
|
||||
onClose,
|
||||
|
@ -107,16 +113,16 @@ export function MigrationAnnouncement({
|
|||
marginTop: 8,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="normal"
|
||||
style={{ fontSize: 14, marginRight: 'auto' }}
|
||||
onClick={downloadFile}
|
||||
>
|
||||
Download original
|
||||
</Button>
|
||||
<Button style={{ fontSize: 14 }} type="primary" onClick={onClose}>
|
||||
Continue
|
||||
</Button>
|
||||
<TldrawUiButtonLabel>Download original</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
<TldrawUiButton style={{ fontSize: 14 }} type="primary" onClick={onClose}>
|
||||
<TldrawUiButtonLabel>Continue</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import {
|
||||
Button,
|
||||
DialogBody,
|
||||
DialogCloseButton,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
TLUiDialogsContextType,
|
||||
TldrawUiButton,
|
||||
TldrawUiButtonCheck,
|
||||
TldrawUiButtonLabel,
|
||||
TldrawUiDialogBody,
|
||||
TldrawUiDialogCloseButton,
|
||||
TldrawUiDialogFooter,
|
||||
TldrawUiDialogHeader,
|
||||
TldrawUiDialogTitle,
|
||||
useTranslation,
|
||||
} from '@tldraw/tldraw'
|
||||
import { useState } from 'react'
|
||||
|
@ -49,26 +51,28 @@ function ConfirmClearDialog({
|
|||
const [dontShowAgain, setDontShowAgain] = useState(false)
|
||||
return (
|
||||
<>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{msg('file-system.confirm-clear.title')}</DialogTitle>
|
||||
<DialogCloseButton />
|
||||
</DialogHeader>
|
||||
<DialogBody style={{ maxWidth: 350 }}>
|
||||
<TldrawUiDialogHeader>
|
||||
<TldrawUiDialogTitle>{msg('file-system.confirm-clear.title')}</TldrawUiDialogTitle>
|
||||
<TldrawUiDialogCloseButton />
|
||||
</TldrawUiDialogHeader>
|
||||
<TldrawUiDialogBody style={{ maxWidth: 350 }}>
|
||||
{msg('file-system.confirm-clear.description')}
|
||||
</DialogBody>
|
||||
<DialogFooter className="tlui-dialog__footer__actions">
|
||||
<Button
|
||||
</TldrawUiDialogBody>
|
||||
<TldrawUiDialogFooter className="tlui-dialog__footer__actions">
|
||||
<TldrawUiButton
|
||||
type="normal"
|
||||
onClick={() => setDontShowAgain(!dontShowAgain)}
|
||||
iconLeft={dontShowAgain ? 'checkbox-checked' : 'checkbox-empty'}
|
||||
style={{ marginRight: 'auto' }}
|
||||
>
|
||||
{msg('file-system.confirm-clear.dont-show-again')}
|
||||
</Button>
|
||||
<Button type="normal" onClick={onCancel}>
|
||||
{msg('file-system.confirm-clear.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
<TldrawUiButtonCheck checked={dontShowAgain} />
|
||||
<TldrawUiButtonLabel>
|
||||
{msg('file-system.confirm-clear.dont-show-again')}
|
||||
</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
<TldrawUiButton type="normal" onClick={onCancel}>
|
||||
<TldrawUiButtonLabel>{msg('file-system.confirm-clear.cancel')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
<TldrawUiButton
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
if (dontShowAgain) {
|
||||
|
@ -77,9 +81,9 @@ function ConfirmClearDialog({
|
|||
onContinue()
|
||||
}}
|
||||
>
|
||||
{msg('file-system.confirm-clear.continue')}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
<TldrawUiButtonLabel>{msg('file-system.confirm-clear.continue')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDialogFooter>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import {
|
||||
Button,
|
||||
DialogBody,
|
||||
DialogCloseButton,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
TLUiDialogsContextType,
|
||||
TldrawUiButton,
|
||||
TldrawUiButtonCheck,
|
||||
TldrawUiButtonLabel,
|
||||
TldrawUiDialogBody,
|
||||
TldrawUiDialogCloseButton,
|
||||
TldrawUiDialogFooter,
|
||||
TldrawUiDialogHeader,
|
||||
TldrawUiDialogTitle,
|
||||
useLocalStorageState,
|
||||
useTranslation,
|
||||
} from '@tldraw/tldraw'
|
||||
|
@ -50,24 +52,26 @@ function ConfirmLeaveDialog({
|
|||
|
||||
return (
|
||||
<>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{msg('sharing.confirm-leave.title')}</DialogTitle>
|
||||
<DialogCloseButton />
|
||||
</DialogHeader>
|
||||
<DialogBody style={{ maxWidth: 350 }}>{msg('sharing.confirm-leave.description')}</DialogBody>
|
||||
<DialogFooter className="tlui-dialog__footer__actions">
|
||||
<Button
|
||||
<TldrawUiDialogHeader>
|
||||
<TldrawUiDialogTitle>{msg('sharing.confirm-leave.title')}</TldrawUiDialogTitle>
|
||||
<TldrawUiDialogCloseButton />
|
||||
</TldrawUiDialogHeader>
|
||||
<TldrawUiDialogBody style={{ maxWidth: 350 }}>
|
||||
{msg('sharing.confirm-leave.description')}
|
||||
</TldrawUiDialogBody>
|
||||
<TldrawUiDialogFooter className="tlui-dialog__footer__actions">
|
||||
<TldrawUiButton
|
||||
type="normal"
|
||||
onClick={() => setDontShowAgain(!dontShowAgain)}
|
||||
iconLeft={dontShowAgain ? 'checkbox-checked' : 'checkbox-empty'}
|
||||
style={{ marginRight: 'auto' }}
|
||||
onClick={() => setDontShowAgain(!dontShowAgain)}
|
||||
>
|
||||
{msg('sharing.confirm-leave.dont-show-again')}
|
||||
</Button>
|
||||
<Button type="normal" onClick={onCancel}>
|
||||
{msg('sharing.confirm-leave.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
<TldrawUiButtonCheck checked={dontShowAgain} />
|
||||
<TldrawUiButtonLabel>{msg('sharing.confirm-leave.dont-show-again')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
<TldrawUiButton type="normal" onClick={onCancel}>
|
||||
<TldrawUiButtonLabel>{msg('sharing.confirm-leave.cancel')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
<TldrawUiButton
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
if (dontShowAgain) {
|
||||
|
@ -76,9 +80,9 @@ function ConfirmLeaveDialog({
|
|||
onContinue()
|
||||
}}
|
||||
>
|
||||
{msg('sharing.confirm-leave.leave')}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
<TldrawUiButtonLabel>{msg('sharing.confirm-leave.leave')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDialogFooter>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import {
|
||||
Button,
|
||||
DialogBody,
|
||||
DialogCloseButton,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
TLUiDialogsContextType,
|
||||
TldrawUiButton,
|
||||
TldrawUiButtonCheck,
|
||||
TldrawUiButtonLabel,
|
||||
TldrawUiDialogBody,
|
||||
TldrawUiDialogCloseButton,
|
||||
TldrawUiDialogFooter,
|
||||
TldrawUiDialogHeader,
|
||||
TldrawUiDialogTitle,
|
||||
useTranslation,
|
||||
} from '@tldraw/tldraw'
|
||||
import { useState } from 'react'
|
||||
|
@ -49,26 +51,28 @@ function ConfirmOpenDialog({
|
|||
const [dontShowAgain, setDontShowAgain] = useState(false)
|
||||
return (
|
||||
<>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{msg('file-system.confirm-open.title')}</DialogTitle>
|
||||
<DialogCloseButton />
|
||||
</DialogHeader>
|
||||
<DialogBody style={{ maxWidth: 350 }}>
|
||||
<TldrawUiDialogHeader>
|
||||
<TldrawUiDialogTitle>{msg('file-system.confirm-open.title')}</TldrawUiDialogTitle>
|
||||
<TldrawUiDialogCloseButton />
|
||||
</TldrawUiDialogHeader>
|
||||
<TldrawUiDialogBody style={{ maxWidth: 350 }}>
|
||||
{msg('file-system.confirm-open.description')}
|
||||
</DialogBody>
|
||||
<DialogFooter className="tlui-dialog__footer__actions">
|
||||
<Button
|
||||
</TldrawUiDialogBody>
|
||||
<TldrawUiDialogFooter className="tlui-dialog__footer__actions">
|
||||
<TldrawUiButton
|
||||
type="normal"
|
||||
onClick={() => setDontShowAgain(!dontShowAgain)}
|
||||
iconLeft={dontShowAgain ? 'checkbox-checked' : 'checkbox-empty'}
|
||||
style={{ marginRight: 'auto' }}
|
||||
>
|
||||
{msg('file-system.confirm-open.dont-show-again')}
|
||||
</Button>
|
||||
<Button type="normal" onClick={onCancel}>
|
||||
{msg('file-system.confirm-open.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
<TldrawUiButtonCheck checked={dontShowAgain} />
|
||||
<TldrawUiButtonLabel>
|
||||
{msg('file-system.confirm-open.dont-show-again')}
|
||||
</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
<TldrawUiButton type="normal" onClick={onCancel}>
|
||||
<TldrawUiButtonLabel>{msg('file-system.confirm-open.cancel')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
<TldrawUiButton
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
if (dontShowAgain) {
|
||||
|
@ -77,9 +81,9 @@ function ConfirmOpenDialog({
|
|||
onContinue()
|
||||
}}
|
||||
>
|
||||
{msg('file-system.confirm-open.open')}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
<TldrawUiButtonLabel>{msg('file-system.confirm-open.open')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDialogFooter>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import {
|
||||
DefaultSizeStyle,
|
||||
Icon,
|
||||
SharedStyleMap,
|
||||
Tldraw,
|
||||
TldrawUiIcon,
|
||||
TLEditorComponents,
|
||||
track,
|
||||
useEditor,
|
||||
|
@ -85,7 +85,7 @@ const ContextToolbarComponent = track(() => {
|
|||
editor.setStyleForSelectedShapes(DefaultSizeStyle, value, { squashing: false })
|
||||
}
|
||||
>
|
||||
<Icon icon={icon} />
|
||||
<TldrawUiIcon icon={icon} />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import {
|
||||
Button,
|
||||
DefaultQuickActions,
|
||||
DefaultQuickActionsContent,
|
||||
TLComponents,
|
||||
Tldraw,
|
||||
TldrawUiMenuItem,
|
||||
} from '@tldraw/tldraw'
|
||||
import '@tldraw/tldraw/tldraw.css'
|
||||
|
||||
|
@ -11,7 +11,7 @@ function CustomQuickActions() {
|
|||
return (
|
||||
<DefaultQuickActions>
|
||||
<DefaultQuickActionsContent />
|
||||
<Button type="icon" icon="code" smallIcon />
|
||||
<TldrawUiMenuItem id="code" icon="code" onSelect={() => window.alert('code')} />
|
||||
</DefaultQuickActions>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import {
|
||||
Button,
|
||||
DefaultColorStyle,
|
||||
DefaultStylePanel,
|
||||
DefaultStylePanelContent,
|
||||
TLComponents,
|
||||
TLUiStylePanelProps,
|
||||
Tldraw,
|
||||
TldrawUiButton,
|
||||
TldrawUiButtonLabel,
|
||||
useEditor,
|
||||
} from '@tldraw/tldraw'
|
||||
import '@tldraw/tldraw/tldraw.css'
|
||||
|
@ -17,22 +18,22 @@ function CustomStylePanel(props: TLUiStylePanelProps) {
|
|||
|
||||
return (
|
||||
<DefaultStylePanel {...props}>
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="menu"
|
||||
onClick={() => {
|
||||
editor.setStyleForSelectedShapes(DefaultColorStyle, 'red', { squashing: true })
|
||||
}}
|
||||
>
|
||||
Red
|
||||
</Button>
|
||||
<Button
|
||||
<TldrawUiButtonLabel>Red</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
<TldrawUiButton
|
||||
type="menu"
|
||||
onClick={() => {
|
||||
editor.setStyleForSelectedShapes(DefaultColorStyle, 'green', { squashing: true })
|
||||
}}
|
||||
>
|
||||
Green
|
||||
</Button>
|
||||
<TldrawUiButtonLabel>Green</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
<DefaultStylePanelContent relevantStyles={props.relevantStyles} />
|
||||
</DefaultStylePanel>
|
||||
)
|
||||
|
|
|
@ -17,6 +17,9 @@ const components: Required<TLUiComponents> = {
|
|||
QuickActions: null,
|
||||
HelperButtons: null,
|
||||
DebugMenu: null,
|
||||
SharePanel: null,
|
||||
MenuPanel: null,
|
||||
TopPanel: null,
|
||||
}
|
||||
|
||||
export default function UiComponentsHiddenExample() {
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
import { Tldraw } from '@tldraw/tldraw'
|
||||
import { TLComponents, Tldraw } from '@tldraw/tldraw'
|
||||
import '@tldraw/tldraw/tldraw.css'
|
||||
|
||||
// There's a guide at the bottom of this file!
|
||||
|
||||
const components: TLComponents = {
|
||||
SharePanel: CustomShareZone,
|
||||
TopPanel: CustomTopZone,
|
||||
}
|
||||
|
||||
// [1]
|
||||
export default function Example() {
|
||||
return (
|
||||
<div className="tldraw__editor">
|
||||
<Tldraw topZone={<CustomTopZone />} shareZone={<CustomShareZone />} />
|
||||
<Tldraw components={components} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -46,8 +51,8 @@ function CustomShareZone() {
|
|||
}
|
||||
|
||||
/*
|
||||
This example shows how to pass in a custom component to the share zone and top zone.
|
||||
The share zone is in the top right corner above the style menu, the top zone is in
|
||||
This example shows how to pass in a custom component to the share panel and top panel.
|
||||
The share panel is in the top right corner above the style menu, the top panel is in
|
||||
the top center.
|
||||
|
||||
[1]
|
||||
|
|
|
@ -28,6 +28,7 @@ import { JSX as JSX_2 } from 'react/jsx-runtime';
|
|||
import { LANGUAGES } from '@tldraw/editor';
|
||||
import { Mat } from '@tldraw/editor';
|
||||
import { MatModel } from '@tldraw/editor';
|
||||
import { MemoExoticComponent } from 'react';
|
||||
import { MigrationFailureReason } from '@tldraw/editor';
|
||||
import { Migrations } from '@tldraw/editor';
|
||||
import { NamedExoticComponent } from 'react';
|
||||
|
@ -49,6 +50,7 @@ import { ShapeUtil } from '@tldraw/editor';
|
|||
import { SharedStyle } from '@tldraw/editor';
|
||||
import { StateNode } from '@tldraw/editor';
|
||||
import { StoreSnapshot } from '@tldraw/editor';
|
||||
import { StyleProp } from '@tldraw/editor';
|
||||
import { SvgExportContext } from '@tldraw/editor';
|
||||
import { T } from '@tldraw/editor';
|
||||
import { TLAnyShapeUtilConstructor } from '@tldraw/editor';
|
||||
|
@ -265,9 +267,6 @@ export function BreakPointProvider({ forceMobile, children, }: {
|
|||
// @internal (undocumented)
|
||||
export function buildFromV1Document(editor: Editor, document: LegacyTldrawDocument): void;
|
||||
|
||||
// @public (undocumented)
|
||||
export const Button: React_3.ForwardRefExoticComponent<TLUiButtonProps & React_3.RefAttributes<HTMLButtonElement>>;
|
||||
|
||||
// @public
|
||||
export function containBoxSize(originalSize: BoxWidthHeight, containBoxSize: BoxWidthHeight): BoxWidthHeight;
|
||||
|
||||
|
@ -360,21 +359,6 @@ export const DefaultZoomMenu: NamedExoticComponent<TLUiZoomMenuProps>;
|
|||
// @public (undocumented)
|
||||
export function DefaultZoomMenuContent(): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function DialogBody({ className, children, style }: TLUiDialogBodyProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function DialogCloseButton(): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function DialogFooter({ className, children }: TLUiDialogFooterProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function DialogHeader({ className, children }: TLUiDialogHeaderProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function DialogTitle({ className, children }: TLUiDialogTitleProps): JSX_2.Element;
|
||||
|
||||
// @public
|
||||
export function downsizeImage(blob: Blob, width: number, height: number, opts?: {
|
||||
type?: string | undefined;
|
||||
|
@ -439,36 +423,6 @@ export class DrawShapeUtil extends ShapeUtil<TLDrawShape> {
|
|||
static type: "draw";
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export function DropdownMenuCheckboxItem({ children, onSelect, ...rest }: TLUiDropdownMenuCheckboxItemProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function DropdownMenuContent({ side, align, sideOffset, alignOffset, children, }: TLUiDropdownMenuContentProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function DropdownMenuGroup({ children, size }: TLUiDropdownMenuGroupProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function DropdownMenuIndicator(): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function DropdownMenuItem({ noClose, ...props }: TLUiDropdownMenuItemProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function DropdownMenuRadioItem({ children, ...rest }: TLUiDropdownMenuRadioItemProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function DropdownMenuRoot({ id, children, modal, debugOpen, }: TLUiDropdownMenuRootProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function DropdownMenuSub({ id, children }: TLUiDropdownMenuSubProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function DropdownMenuSubTrigger({ label, title, disabled, }: TLUiDropdownMenuSubTriggerProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function DropdownMenuTrigger({ children, ...rest }: TLUiDropdownMenuTriggerProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export class EmbedShapeUtil extends BaseBoxShapeUtil<TLEmbedShape> {
|
||||
// (undocumented)
|
||||
|
@ -815,9 +769,6 @@ export class HighlightShapeUtil extends ShapeUtil<TLHighlightShape> {
|
|||
static type: "highlight";
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const Icon: NamedExoticComponent<TLUiIconProps>;
|
||||
|
||||
// @public (undocumented)
|
||||
export class ImageShapeUtil extends BaseBoxShapeUtil<TLImageShape> {
|
||||
// (undocumented)
|
||||
|
@ -856,9 +807,6 @@ export class ImageShapeUtil extends BaseBoxShapeUtil<TLImageShape> {
|
|||
static type: "image";
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const Input: React_3.ForwardRefExoticComponent<TLUiInputProps & React_3.RefAttributes<HTMLInputElement>>;
|
||||
|
||||
// @public (undocumented)
|
||||
export function isGifAnimated(file: Blob): Promise<boolean>;
|
||||
|
||||
|
@ -1059,15 +1007,6 @@ export function parseTldrawJsonFile({ json, schema, }: {
|
|||
json: string;
|
||||
}): Result<TLStore, TldrawFileParseError>;
|
||||
|
||||
// @public (undocumented)
|
||||
export function Popover({ id, children, onOpenChange, open }: TLUiPopoverProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function PopoverContent({ side, children, align, sideOffset, alignOffset, }: TLUiPopoverContentProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function PopoverTrigger({ children, ...rest }: TLUiPopoverTriggerProps): JSX_2.Element;
|
||||
|
||||
// @public
|
||||
export function removeFrame(editor: Editor, ids: TLShapeId[]): void;
|
||||
|
||||
|
@ -1292,13 +1231,25 @@ export interface TldrawUiBaseProps {
|
|||
components?: TLUiComponents;
|
||||
hideUi?: boolean;
|
||||
renderDebugMenuItems?: () => React_2.ReactNode;
|
||||
shareZone?: ReactNode;
|
||||
// @internal
|
||||
topZone?: ReactNode;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiComponentsProvider({ overrides, children, }: ComponentsContextProviderProps): JSX_2.Element;
|
||||
export const TldrawUiButton: React_3.ForwardRefExoticComponent<TLUiButtonProps & React_3.RefAttributes<HTMLButtonElement>>;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiButtonCheck({ checked }: TLUiButtonCheckProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiButtonIcon({ icon, small, invertIcon }: TLUiButtonIconProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiButtonLabel({ children }: TLUiButtonLabelProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export const TldrawUiButtonPicker: MemoExoticComponent<(<T extends string>(props: TLUiButtonPickerProps<T>) => JSX_2.Element)>;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiComponentsProvider({ overrides, children, }: TLUiComponentsProviderProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiContextProvider({ overrides, components, assetUrls, onUiEvent, forceMobile, children, }: TldrawUiContextProviderProps): JSX_2.Element;
|
||||
|
@ -1313,6 +1264,57 @@ export interface TldrawUiContextProviderProps {
|
|||
overrides?: TLUiOverrides | TLUiOverrides[];
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiDialogBody({ className, children, style }: TLUiDialogBodyProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiDialogCloseButton(): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiDialogFooter({ className, children }: TLUiDialogFooterProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiDialogHeader({ className, children }: TLUiDialogHeaderProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiDialogTitle({ className, children }: TLUiDialogTitleProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiDropdownMenuCheckboxItem({ children, onSelect, ...rest }: TLUiDropdownMenuCheckboxItemProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiDropdownMenuContent({ side, align, sideOffset, alignOffset, children, }: TLUiDropdownMenuContentProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiDropdownMenuGroup({ children, size, }: TLUiDropdownMenuGroupProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiDropdownMenuIndicator(): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiDropdownMenuItem({ noClose, children }: TLUiDropdownMenuItemProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiDropdownMenuRoot({ id, children, modal, debugOpen, }: TLUiDropdownMenuRootProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiDropdownMenuSub({ id, children }: TLUiDropdownMenuSubProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiDropdownMenuSubTrigger({ label, title, disabled, }: TLUiDropdownMenuSubTriggerProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiDropdownMenuTrigger({ children, ...rest }: TLUiDropdownMenuTriggerProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export const TldrawUiIcon: NamedExoticComponent<TLUiIconProps>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const TldrawUiInput: React_3.ForwardRefExoticComponent<TLUiInputProps & React_3.RefAttributes<HTMLInputElement>>;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiKbd({ children }: TLUiKbdProps): JSX_2.Element | null;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiMenuCheckboxItem<TranslationKey extends string = string, IconType extends string = string>({ id, kbd, label, readonlyOk, onSelect, disabled, checked, }: TLUiMenuCheckboxItemProps<TranslationKey, IconType>): JSX_2.Element | null;
|
||||
|
||||
|
@ -1328,9 +1330,21 @@ export function TldrawUiMenuItem<TranslationKey extends string = string, IconTyp
|
|||
// @public (undocumented)
|
||||
export function TldrawUiMenuSubmenu<Translation extends string = string>({ id, disabled, label, size, children, }: TLUiMenuSubmenuProps<Translation>): any;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiPopover({ id, children, onOpenChange, open }: TLUiPopoverProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiPopoverContent({ side, children, align, sideOffset, alignOffset, }: TLUiPopoverContentProps): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export function TldrawUiPopoverTrigger({ children }: TLUiPopoverTriggerProps): JSX_2.Element;
|
||||
|
||||
// @public
|
||||
export type TldrawUiProps = TldrawUiBaseProps & TldrawUiContextProviderProps;
|
||||
|
||||
// @internal (undocumented)
|
||||
export const TldrawUiSlider: NamedExoticComponent<TLUiSliderProps>;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TLUiActionItem<TransationKey extends string = string, IconType extends string = string> {
|
||||
// (undocumented)
|
||||
|
@ -1364,29 +1378,44 @@ export type TLUiActionsMenuProps = {
|
|||
// @public (undocumented)
|
||||
export type TLUiAssetUrlOverrides = RecursivePartial<TLUiAssetUrls>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLUiButtonCheckProps = {
|
||||
checked: boolean;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLUiButtonIconProps = {
|
||||
icon: string;
|
||||
small?: boolean;
|
||||
invertIcon?: boolean;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLUiButtonLabelProps = {
|
||||
children?: any;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TLUiButtonPickerProps<T extends string> {
|
||||
// (undocumented)
|
||||
items: StyleValuesForUi<T>;
|
||||
// (undocumented)
|
||||
onValueChange: (style: StyleProp<T>, value: T, squashing: boolean) => void;
|
||||
// (undocumented)
|
||||
style: StyleProp<T>;
|
||||
// (undocumented)
|
||||
title: string;
|
||||
// (undocumented)
|
||||
uiType: string;
|
||||
// (undocumented)
|
||||
value: SharedStyle<T>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TLUiButtonProps extends React_3.HTMLAttributes<HTMLButtonElement> {
|
||||
// (undocumented)
|
||||
disabled?: boolean;
|
||||
// (undocumented)
|
||||
icon?: Exclude<string, TLUiIconType> | TLUiIconType;
|
||||
// (undocumented)
|
||||
iconLeft?: Exclude<string, TLUiIconType> | TLUiIconType;
|
||||
// (undocumented)
|
||||
invertIcon?: boolean;
|
||||
// (undocumented)
|
||||
isChecked?: boolean;
|
||||
// (undocumented)
|
||||
kbd?: string;
|
||||
// (undocumented)
|
||||
label?: Exclude<string, TLUiTranslationKey> | TLUiTranslationKey;
|
||||
// (undocumented)
|
||||
loading?: boolean;
|
||||
// (undocumented)
|
||||
smallIcon?: boolean;
|
||||
// (undocumented)
|
||||
spinner?: boolean;
|
||||
// (undocumented)
|
||||
type: 'danger' | 'help' | 'icon' | 'low' | 'menu' | 'normal' | 'primary' | 'tool';
|
||||
}
|
||||
|
||||
|
@ -1395,6 +1424,12 @@ export type TLUiComponents = Partial<{
|
|||
[K in keyof BaseTLUiComponents]: BaseTLUiComponents[K] | null;
|
||||
}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLUiComponentsProviderProps = {
|
||||
overrides?: TLUiComponents;
|
||||
children: any;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TLUiContextMenuProps {
|
||||
// (undocumented)
|
||||
|
@ -1491,23 +1526,11 @@ export type TLUiDropdownMenuGroupProps = {
|
|||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TLUiDropdownMenuItemProps extends TLUiButtonProps {
|
||||
// (undocumented)
|
||||
noClose?: boolean;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TLUiDropdownMenuRadioItemProps {
|
||||
// (undocumented)
|
||||
checked?: boolean;
|
||||
export interface TLUiDropdownMenuItemProps {
|
||||
// (undocumented)
|
||||
children: any;
|
||||
// (undocumented)
|
||||
disabled?: boolean;
|
||||
// (undocumented)
|
||||
onSelect?: (e: Event) => void;
|
||||
// (undocumented)
|
||||
title: string;
|
||||
noClose?: boolean;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
|
@ -1533,7 +1556,7 @@ export type TLUiDropdownMenuSubTriggerProps = {
|
|||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TLUiDropdownMenuTriggerProps extends TLUiButtonProps {
|
||||
export interface TLUiDropdownMenuTriggerProps {
|
||||
// (undocumented)
|
||||
children?: any;
|
||||
}
|
||||
|
@ -1772,6 +1795,12 @@ export interface TLUiInputProps {
|
|||
value?: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TLUiKbdProps {
|
||||
// (undocumented)
|
||||
children: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLUiKeyboardShortcutsDialogProps = TLUiDialogProps & {
|
||||
children?: any;
|
||||
|
@ -1867,7 +1896,7 @@ export type TLUiPopoverProps = {
|
|||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TLUiPopoverTriggerProps extends TLUiButtonProps {
|
||||
export interface TLUiPopoverTriggerProps {
|
||||
// (undocumented)
|
||||
children?: React_2.ReactNode;
|
||||
}
|
||||
|
@ -1877,6 +1906,22 @@ export type TLUiQuickActionsProps = {
|
|||
children?: any;
|
||||
};
|
||||
|
||||
// @internal (undocumented)
|
||||
export interface TLUiSliderProps {
|
||||
// (undocumented)
|
||||
'data-testid'?: string;
|
||||
// (undocumented)
|
||||
label: string;
|
||||
// (undocumented)
|
||||
onValueChange: (value: number, emphemeral: boolean) => void;
|
||||
// (undocumented)
|
||||
steps: number;
|
||||
// (undocumented)
|
||||
title: string;
|
||||
// (undocumented)
|
||||
value: null | number;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLUiStylePanelContentProps = {
|
||||
relevantStyles: ReturnType<typeof useRelevantStyles>;
|
||||
|
@ -2080,6 +2125,9 @@ export function useTldrawUiComponents(): Partial<{
|
|||
QuickActions: ComponentType<TLUiQuickActionsProps> | null;
|
||||
HelperButtons: ComponentType<TLUiHelperButtonsProps> | null;
|
||||
DebugMenu: ComponentType | null;
|
||||
MenuPanel: ComponentType | null;
|
||||
TopPanel: ComponentType | null;
|
||||
SharePanel: ComponentType | null;
|
||||
}>;
|
||||
|
||||
// @public (undocumented)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -42,9 +42,6 @@ export { TldrawUi, type TldrawUiBaseProps, type TldrawUiProps } from './lib/ui/T
|
|||
export { setDefaultUiAssetUrls, type TLUiAssetUrlOverrides } from './lib/ui/assetUrls'
|
||||
export { OfflineIndicator } from './lib/ui/components/OfflineIndicator/OfflineIndicator'
|
||||
export { Spinner } from './lib/ui/components/Spinner'
|
||||
export { Button, type TLUiButtonProps } from './lib/ui/components/primitives/Button'
|
||||
export { Icon, type TLUiIconProps } from './lib/ui/components/primitives/Icon'
|
||||
export { Input, type TLUiInputProps } from './lib/ui/components/primitives/Input'
|
||||
export {
|
||||
TldrawUiContextProvider,
|
||||
type TldrawUiContextProviderProps,
|
||||
|
@ -99,7 +96,7 @@ export {
|
|||
export { type TLUiTranslationKey } from './lib/ui/hooks/useTranslation/TLUiTranslationKey'
|
||||
export { type TLUiTranslation } from './lib/ui/hooks/useTranslation/translations'
|
||||
export {
|
||||
useTranslation as useTranslation,
|
||||
useTranslation,
|
||||
type TLUiTranslationContextType,
|
||||
} from './lib/ui/hooks/useTranslation/useTranslation'
|
||||
export { type TLUiIconType } from './lib/ui/icon-types'
|
||||
|
@ -137,35 +134,13 @@ export { DefaultMinimap } from './lib/ui/components/Minimap/DefaultMinimap'
|
|||
// Helper to unwrap label from action items
|
||||
export { unwrapLabel } from './lib/ui/context/actions'
|
||||
|
||||
// General UI components for building menus
|
||||
export {
|
||||
TldrawUiMenuCheckboxItem,
|
||||
type TLUiMenuCheckboxItemProps,
|
||||
} from './lib/ui/components/menus/TldrawUiMenuCheckboxItem'
|
||||
export {
|
||||
TldrawUiMenuContextProvider,
|
||||
type TLUiMenuContextProviderProps,
|
||||
} from './lib/ui/components/menus/TldrawUiMenuContext'
|
||||
export {
|
||||
TldrawUiMenuGroup,
|
||||
type TLUiMenuGroupProps,
|
||||
} from './lib/ui/components/menus/TldrawUiMenuGroup'
|
||||
export {
|
||||
TldrawUiMenuItem,
|
||||
type TLUiMenuItemProps,
|
||||
} from './lib/ui/components/menus/TldrawUiMenuItem'
|
||||
export {
|
||||
TldrawUiMenuSubmenu,
|
||||
type TLUiMenuSubmenuProps,
|
||||
} from './lib/ui/components/menus/TldrawUiMenuSubmenu'
|
||||
|
||||
export {
|
||||
TldrawUiComponentsProvider,
|
||||
useTldrawUiComponents,
|
||||
type TLUiComponents,
|
||||
type TLUiComponentsProviderProps,
|
||||
} from './lib/ui/context/components'
|
||||
|
||||
// Menus / UI elements that can be customized
|
||||
export { DefaultPageMenu } from './lib/ui/components/PageMenu/DefaultPageMenu'
|
||||
|
||||
export {
|
||||
|
@ -236,45 +211,108 @@ export { DefaultToolbar } from './lib/ui/components/Toolbar/DefaultToolbar'
|
|||
|
||||
export { type TLComponents } from './lib/Tldraw'
|
||||
|
||||
/* ------------------- Primitives ------------------- */
|
||||
|
||||
// Button
|
||||
export {
|
||||
DialogBody,
|
||||
DialogCloseButton,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
TldrawUiButton,
|
||||
type TLUiButtonProps,
|
||||
} from './lib/ui/components/primitives/Button/TldrawUiButton'
|
||||
export {
|
||||
TldrawUiButtonCheck,
|
||||
type TLUiButtonCheckProps,
|
||||
} from './lib/ui/components/primitives/Button/TldrawUiButtonCheck'
|
||||
export {
|
||||
TldrawUiButtonIcon,
|
||||
type TLUiButtonIconProps,
|
||||
} from './lib/ui/components/primitives/Button/TldrawUiButtonIcon'
|
||||
export {
|
||||
TldrawUiButtonLabel,
|
||||
type TLUiButtonLabelProps,
|
||||
} from './lib/ui/components/primitives/Button/TldrawUiButtonLabel'
|
||||
|
||||
// Button picker
|
||||
export {
|
||||
TldrawUiButtonPicker,
|
||||
type TLUiButtonPickerProps,
|
||||
} from './lib/ui/components/primitives/TldrawUiButtonPicker'
|
||||
|
||||
// Dialog
|
||||
export {
|
||||
TldrawUiDialogBody,
|
||||
TldrawUiDialogCloseButton,
|
||||
TldrawUiDialogFooter,
|
||||
TldrawUiDialogHeader,
|
||||
TldrawUiDialogTitle,
|
||||
type TLUiDialogBodyProps,
|
||||
type TLUiDialogFooterProps,
|
||||
type TLUiDialogHeaderProps,
|
||||
type TLUiDialogTitleProps,
|
||||
} from './lib/ui/components/primitives/Dialog'
|
||||
} from './lib/ui/components/primitives/TldrawUiDialog'
|
||||
|
||||
// Dropdown Menu
|
||||
export {
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuIndicator,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuRoot,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuTrigger,
|
||||
TldrawUiDropdownMenuCheckboxItem,
|
||||
TldrawUiDropdownMenuContent,
|
||||
TldrawUiDropdownMenuGroup,
|
||||
TldrawUiDropdownMenuIndicator,
|
||||
TldrawUiDropdownMenuItem,
|
||||
TldrawUiDropdownMenuRoot,
|
||||
TldrawUiDropdownMenuSub,
|
||||
TldrawUiDropdownMenuSubTrigger,
|
||||
TldrawUiDropdownMenuTrigger,
|
||||
type TLUiDropdownMenuCheckboxItemProps,
|
||||
type TLUiDropdownMenuContentProps,
|
||||
type TLUiDropdownMenuGroupProps,
|
||||
type TLUiDropdownMenuItemProps,
|
||||
type TLUiDropdownMenuRadioItemProps,
|
||||
type TLUiDropdownMenuRootProps,
|
||||
type TLUiDropdownMenuSubProps,
|
||||
type TLUiDropdownMenuSubTriggerProps,
|
||||
type TLUiDropdownMenuTriggerProps,
|
||||
} from './lib/ui/components/primitives/DropdownMenu'
|
||||
} from './lib/ui/components/primitives/TldrawUiDropdownMenu'
|
||||
|
||||
// Icon
|
||||
export { TldrawUiIcon, type TLUiIconProps } from './lib/ui/components/primitives/TldrawUiIcon'
|
||||
|
||||
// Input
|
||||
export { TldrawUiInput, type TLUiInputProps } from './lib/ui/components/primitives/TldrawUiInput'
|
||||
|
||||
// Kbd
|
||||
export { TldrawUiKbd, type TLUiKbdProps } from './lib/ui/components/primitives/TldrawUiKbd'
|
||||
|
||||
// Popover
|
||||
export {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
TldrawUiPopover,
|
||||
TldrawUiPopoverContent,
|
||||
TldrawUiPopoverTrigger,
|
||||
type TLUiPopoverContentProps,
|
||||
type TLUiPopoverProps,
|
||||
type TLUiPopoverTriggerProps,
|
||||
} from './lib/ui/components/primitives/Popover'
|
||||
} from './lib/ui/components/primitives/TldrawUiPopover'
|
||||
|
||||
// Slider
|
||||
export { TldrawUiSlider, type TLUiSliderProps } from './lib/ui/components/primitives/TldrawUiSlider'
|
||||
|
||||
/* ----------------- Menu Primitives ---------------- */
|
||||
|
||||
// General UI components for building menus
|
||||
export {
|
||||
TldrawUiMenuCheckboxItem,
|
||||
type TLUiMenuCheckboxItemProps,
|
||||
} from './lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem'
|
||||
export {
|
||||
TldrawUiMenuContextProvider,
|
||||
type TLUiMenuContextProviderProps,
|
||||
} from './lib/ui/components/primitives/menus/TldrawUiMenuContext'
|
||||
export {
|
||||
TldrawUiMenuGroup,
|
||||
type TLUiMenuGroupProps,
|
||||
} from './lib/ui/components/primitives/menus/TldrawUiMenuGroup'
|
||||
export {
|
||||
TldrawUiMenuItem,
|
||||
type TLUiMenuItemProps,
|
||||
} from './lib/ui/components/primitives/menus/TldrawUiMenuItem'
|
||||
export {
|
||||
TldrawUiMenuSubmenu,
|
||||
type TLUiMenuSubmenuProps,
|
||||
} from './lib/ui/components/primitives/menus/TldrawUiMenuSubmenu'
|
||||
|
|
|
@ -3,6 +3,7 @@ export type StyleValuesForUi<T> = readonly {
|
|||
readonly icon: string
|
||||
}[]
|
||||
|
||||
// todo: default styles prop?
|
||||
export const STYLES = {
|
||||
color: [
|
||||
{ value: 'black', icon: 'color' },
|
|
@ -75,6 +75,10 @@
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
.tlui-button__icon + .tlui-button__label {
|
||||
margin-left: var(--space-2);
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
.tlui-button::after {
|
||||
background-color: var(--color-muted-2);
|
||||
|
@ -127,46 +131,6 @@
|
|||
position: relative;
|
||||
}
|
||||
|
||||
/* Icon button */
|
||||
|
||||
.tlui-button__icon {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
min-height: 40px;
|
||||
min-width: 40px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.tlui-button__icon-left {
|
||||
margin-right: var(--space-2);
|
||||
}
|
||||
|
||||
/* Hinted */
|
||||
|
||||
.tlui-button__icon[data-state='hinted']::after {
|
||||
background: var(--color-hint);
|
||||
opacity: 1;
|
||||
/* box-shadow: inset 0 0 0 1px var(--color-muted-1); */
|
||||
}
|
||||
|
||||
.tlui-button__icon[data-state='hinted']:not(:disabled, :focus-visible):active::after {
|
||||
background: var(--color-hint);
|
||||
opacity: 1;
|
||||
/* box-shadow: inset 0 0 0 1px var(--color-text-3); */
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
.tlui-button__icon::after {
|
||||
inset: 4px;
|
||||
border-radius: var(--radius-2);
|
||||
}
|
||||
|
||||
.tlui-button__icon[data-state='hinted']:not(:disabled, :focus-visible):hover::after {
|
||||
background: var(--color-hint);
|
||||
/* box-shadow: inset 0 0 0 1px var(--color-text-3); */
|
||||
}
|
||||
}
|
||||
|
||||
/* Menu button */
|
||||
|
||||
.tlui-button__menu {
|
||||
|
@ -1225,11 +1189,6 @@
|
|||
width: 128px;
|
||||
}
|
||||
|
||||
.tlui-page-menu__trigger > span {
|
||||
flex-grow: 2;
|
||||
margin-right: var(--space-4);
|
||||
}
|
||||
|
||||
.tlui-page-menu__header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
@ -1373,17 +1332,29 @@
|
|||
|
||||
.tlui-page_menu__item__submenu[data-isediting='true'] {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tlui-page_menu__item__submenu > .tlui-button {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.tlui-page-menu__item__button .tlui-button__icon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
.tlui-page_menu__item__submenu {
|
||||
opacity: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tlui-page_menu__item__submenu:hover,
|
||||
.tlui-page-menu__item:focus-within > .tlui-page_menu__item__submenu,
|
||||
.tlui-page_menu__item__sortable:focus-within > .tlui-page_menu__item__submenu {
|
||||
.tlui-page_menu__item__submenu[data-isediting='true'] > .tlui-button {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.tlui-page_menu__item__submenu > .tlui-button[data-state='open'],
|
||||
.tlui-page_menu__item__submenu:hover > .tlui-button,
|
||||
.tlui-page_menu__item__sortable:focus-within > .tlui-page_menu__item__submenu > .tlui-button {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,9 @@ import { TLUiAssetUrlOverrides } from './assetUrls'
|
|||
import { DebugPanel } from './components/DebugPanel'
|
||||
import { Dialogs } from './components/Dialogs'
|
||||
import { FollowingIndicator } from './components/FollowingIndicator'
|
||||
import { MenuZone } from './components/MenuZone'
|
||||
import { ToastViewport, Toasts } from './components/Toasts'
|
||||
import { Button } from './components/primitives/Button'
|
||||
import { TldrawUiButton } from './components/primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiButtonIcon } from './components/primitives/Button/TldrawUiButtonIcon'
|
||||
import { PORTRAIT_BREAKPOINT } from './constants'
|
||||
import {
|
||||
TldrawUiContextProvider,
|
||||
|
@ -20,6 +20,7 @@ import { TLUiComponents, useTldrawUiComponents } from './context/components'
|
|||
import { useNativeClipboardEvents } from './hooks/useClipboardEvents'
|
||||
import { useEditorEvents } from './hooks/useEditorEvents'
|
||||
import { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts'
|
||||
import { useReadonly } from './hooks/useReadonly'
|
||||
import { useRelevantStyles } from './hooks/useRevelantStyles'
|
||||
import { useTranslation } from './hooks/useTranslation/useTranslation'
|
||||
|
||||
|
@ -51,17 +52,6 @@ export interface TldrawUiBaseProps {
|
|||
*/
|
||||
components?: TLUiComponents
|
||||
|
||||
/**
|
||||
* A component to use for the share zone (will be deprecated)
|
||||
*/
|
||||
shareZone?: ReactNode
|
||||
|
||||
/**
|
||||
* A component to use for the top zone (will be deprecated)
|
||||
* @internal
|
||||
*/
|
||||
topZone?: ReactNode
|
||||
|
||||
/**
|
||||
* Additional items to add to the debug menu (will be deprecated)
|
||||
*/
|
||||
|
@ -75,8 +65,6 @@ export interface TldrawUiBaseProps {
|
|||
* @public
|
||||
*/
|
||||
export const TldrawUi = React.memo(function TldrawUi({
|
||||
shareZone,
|
||||
topZone,
|
||||
renderDebugMenuItems,
|
||||
children,
|
||||
hideUi,
|
||||
|
@ -85,12 +73,7 @@ export const TldrawUi = React.memo(function TldrawUi({
|
|||
}: TldrawUiProps) {
|
||||
return (
|
||||
<TldrawUiContextProvider {...rest} components={components}>
|
||||
<TldrawUiInner
|
||||
hideUi={hideUi}
|
||||
shareZone={shareZone}
|
||||
topZone={topZone}
|
||||
renderDebugMenuItems={renderDebugMenuItems}
|
||||
>
|
||||
<TldrawUiInner hideUi={hideUi} renderDebugMenuItems={renderDebugMenuItems}>
|
||||
{children}
|
||||
</TldrawUiInner>
|
||||
</TldrawUiContextProvider>
|
||||
|
@ -121,17 +104,24 @@ const TldrawUiInner = React.memo(function TldrawUiInner({
|
|||
)
|
||||
})
|
||||
|
||||
const TldrawUiContent = React.memo(function TldrawUI({ shareZone, topZone }: TldrawUiContentProps) {
|
||||
const TldrawUiContent = React.memo(function TldrawUI() {
|
||||
const editor = useEditor()
|
||||
const msg = useTranslation()
|
||||
const breakpoint = useBreakpoint()
|
||||
const isReadonlyMode = useValue('isReadonlyMode', () => editor.getInstanceState().isReadonly, [
|
||||
editor,
|
||||
])
|
||||
const isReadonlyMode = useReadonly()
|
||||
const isFocusMode = useValue('focus', () => editor.getInstanceState().isFocusMode, [editor])
|
||||
const isDebugMode = useValue('debug', () => editor.getInstanceState().isDebugMode, [editor])
|
||||
|
||||
const { StylePanel, Toolbar, HelpMenu, NavigationPanel, HelperButtons } = useTldrawUiComponents()
|
||||
const {
|
||||
SharePanel,
|
||||
TopPanel,
|
||||
MenuPanel,
|
||||
StylePanel,
|
||||
Toolbar,
|
||||
HelpMenu,
|
||||
NavigationPanel,
|
||||
HelperButtons,
|
||||
} = useTldrawUiComponents()
|
||||
|
||||
useKeyboardShortcuts()
|
||||
useNativeClipboardEvents()
|
||||
|
@ -149,24 +139,25 @@ const TldrawUiContent = React.memo(function TldrawUI({ shareZone, topZone }: Tld
|
|||
>
|
||||
{isFocusMode ? (
|
||||
<div className="tlui-layout__top">
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
className="tlui-focus-button"
|
||||
title={`${msg('focus-mode.toggle-focus-mode')}`}
|
||||
icon="dot"
|
||||
title={msg('focus-mode.toggle-focus-mode')}
|
||||
onClick={() => toggleFocus.onSelect('menu')}
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonIcon icon="dot" />
|
||||
</TldrawUiButton>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="tlui-layout__top">
|
||||
<div className="tlui-layout__top__left">
|
||||
<MenuZone />
|
||||
{MenuPanel && <MenuPanel />}
|
||||
{HelperButtons && <HelperButtons />}
|
||||
</div>
|
||||
<div className="tlui-layout__top__center">{topZone}</div>
|
||||
<div className="tlui-layout__top__center">{TopPanel && <TopPanel />}</div>
|
||||
<div className="tlui-layout__top__right">
|
||||
{shareZone}
|
||||
{SharePanel && <SharePanel />}
|
||||
{StylePanel && breakpoint >= PORTRAIT_BREAKPOINT.TABLET_SM && !isReadonlyMode && (
|
||||
<_StylePanel />
|
||||
)}
|
||||
|
|
|
@ -4,8 +4,14 @@ import { PORTRAIT_BREAKPOINT } from '../../constants'
|
|||
import { useBreakpoint } from '../../context/breakpoints'
|
||||
import { useReadonly } from '../../hooks/useReadonly'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { TldrawUiMenuContextProvider } from '../menus/TldrawUiMenuContext'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '../primitives/Popover'
|
||||
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
|
||||
import {
|
||||
TldrawUiPopover,
|
||||
TldrawUiPopoverContent,
|
||||
TldrawUiPopoverTrigger,
|
||||
} from '../primitives/TldrawUiPopover'
|
||||
import { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'
|
||||
import { DefaultActionsMenuContent } from './DefaultActionsMenuContent'
|
||||
|
||||
/** @public */
|
||||
|
@ -37,16 +43,17 @@ export const DefaultActionsMenu = memo(function DefaultActionsMenu({
|
|||
if (isReadonlyMode && !isInAcceptableReadonlyState) return
|
||||
|
||||
return (
|
||||
<Popover id="actions-menu">
|
||||
<PopoverTrigger
|
||||
className="tlui-menu__trigger"
|
||||
data-testid="main.action-menu"
|
||||
icon="dots-vertical"
|
||||
title={msg('actions-menu.title')}
|
||||
type="icon" // needs to be here because the trigger also passes down type="button"
|
||||
smallIcon
|
||||
/>
|
||||
<PopoverContent
|
||||
<TldrawUiPopover id="actions-menu">
|
||||
<TldrawUiPopoverTrigger>
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
data-testid="main.action-menu"
|
||||
title={msg('actions-menu.title')}
|
||||
>
|
||||
<TldrawUiButtonIcon icon="dots-vertical" small />
|
||||
</TldrawUiButton>
|
||||
</TldrawUiPopoverTrigger>
|
||||
<TldrawUiPopoverContent
|
||||
side={breakpoint >= PORTRAIT_BREAKPOINT.TABLET ? 'bottom' : 'top'}
|
||||
sideOffset={6}
|
||||
>
|
||||
|
@ -55,7 +62,7 @@ export const DefaultActionsMenu = memo(function DefaultActionsMenu({
|
|||
{content}
|
||||
</TldrawUiMenuContextProvider>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</TldrawUiPopoverContent>
|
||||
</TldrawUiPopover>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
useThreeStackableItems,
|
||||
useUnlockedSelectedShapesCount,
|
||||
} from '../../hooks/menu-hooks'
|
||||
import { TldrawUiMenuItem } from '../menus/TldrawUiMenuItem'
|
||||
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
|
||||
|
||||
/** @public */
|
||||
export function DefaultActionsMenuContent() {
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as _ContextMenu from '@radix-ui/react-context-menu'
|
|||
import { preventDefault, useContainer, useEditor } from '@tldraw/editor'
|
||||
import { memo, useCallback } from 'react'
|
||||
import { useMenuIsOpen } from '../../hooks/useMenuIsOpen'
|
||||
import { TldrawUiMenuContextProvider } from '../menus/TldrawUiMenuContext'
|
||||
import { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'
|
||||
import { DefaultContextMenuContent } from './DefaultContextMenuContent'
|
||||
|
||||
/** @public */
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
ToggleLockMenuItem,
|
||||
UngroupMenuItem,
|
||||
} from '../menu-items'
|
||||
import { TldrawUiMenuGroup } from '../menus/TldrawUiMenuGroup'
|
||||
import { TldrawUiMenuGroup } from '../primitives/menus/TldrawUiMenuGroup'
|
||||
|
||||
/** @public */
|
||||
export function DefaultContextMenuContent() {
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { TldrawUiMenuContextProvider } from '../menus/TldrawUiMenuContext'
|
||||
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
|
||||
import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuRoot,
|
||||
DropdownMenuTrigger,
|
||||
} from '../primitives/DropdownMenu'
|
||||
TldrawUiDropdownMenuContent,
|
||||
TldrawUiDropdownMenuRoot,
|
||||
TldrawUiDropdownMenuTrigger,
|
||||
} from '../primitives/TldrawUiDropdownMenu'
|
||||
import { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'
|
||||
import { DefaultDebugMenuContent } from './DefaultDebugMenuContent'
|
||||
|
||||
/** @public */
|
||||
|
@ -18,13 +20,17 @@ export function DefaultDebugMenu({ children }: TLUiDebugMenuProps) {
|
|||
const content = children ?? <DefaultDebugMenuContent />
|
||||
|
||||
return (
|
||||
<DropdownMenuRoot id="debug">
|
||||
<DropdownMenuTrigger type="icon" icon="dots-horizontal" title={msg('debug-panel.more')} />
|
||||
<DropdownMenuContent side="top" align="end" alignOffset={0}>
|
||||
<TldrawUiDropdownMenuRoot id="debug">
|
||||
<TldrawUiDropdownMenuTrigger>
|
||||
<TldrawUiButton type="icon" title={msg('debug-menu.title')}>
|
||||
<TldrawUiButtonIcon icon="dots-horizontal" />
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDropdownMenuTrigger>
|
||||
<TldrawUiDropdownMenuContent side="top" align="end" alignOffset={0}>
|
||||
<TldrawUiMenuContextProvider type="menu" sourceId="debug-panel">
|
||||
{content}
|
||||
</TldrawUiMenuContextProvider>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenuRoot>
|
||||
</TldrawUiDropdownMenuContent>
|
||||
</TldrawUiDropdownMenuRoot>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { DialogTitle } from '@radix-ui/react-dialog'
|
||||
import {
|
||||
DebugFlag,
|
||||
Editor,
|
||||
|
@ -15,12 +14,20 @@ import React from 'react'
|
|||
import { useDialogs } from '../../context/dialogs'
|
||||
import { useToasts } from '../../context/toasts'
|
||||
import { untranslated } from '../../hooks/useTranslation/useTranslation'
|
||||
import { TldrawUiMenuCheckboxItem } from '../menus/TldrawUiMenuCheckboxItem'
|
||||
import { TldrawUiMenuGroup } from '../menus/TldrawUiMenuGroup'
|
||||
import { TldrawUiMenuItem } from '../menus/TldrawUiMenuItem'
|
||||
import { TldrawUiMenuSubmenu } from '../menus/TldrawUiMenuSubmenu'
|
||||
import { Button } from '../primitives/Button'
|
||||
import { DialogBody, DialogCloseButton, DialogFooter, DialogHeader } from '../primitives/Dialog'
|
||||
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiButtonCheck } from '../primitives/Button/TldrawUiButtonCheck'
|
||||
import { TldrawUiButtonLabel } from '../primitives/Button/TldrawUiButtonLabel'
|
||||
import {
|
||||
TldrawUiDialogBody,
|
||||
TldrawUiDialogCloseButton,
|
||||
TldrawUiDialogFooter,
|
||||
TldrawUiDialogHeader,
|
||||
TldrawUiDialogTitle,
|
||||
} from '../primitives/TldrawUiDialog'
|
||||
import { TldrawUiMenuCheckboxItem } from '../primitives/menus/TldrawUiMenuCheckboxItem'
|
||||
import { TldrawUiMenuGroup } from '../primitives/menus/TldrawUiMenuGroup'
|
||||
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
|
||||
import { TldrawUiMenuSubmenu } from '../primitives/menus/TldrawUiMenuSubmenu'
|
||||
|
||||
/** @public */
|
||||
export function DefaultDebugMenuContent() {
|
||||
|
@ -225,29 +232,29 @@ function ExampleDialog({
|
|||
|
||||
return (
|
||||
<>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
<DialogCloseButton />
|
||||
</DialogHeader>
|
||||
<DialogBody style={{ maxWidth: 350 }}>{body}</DialogBody>
|
||||
<DialogFooter className="tlui-dialog__footer__actions">
|
||||
<TldrawUiDialogHeader>
|
||||
<TldrawUiDialogTitle>{title}</TldrawUiDialogTitle>
|
||||
<TldrawUiDialogCloseButton />
|
||||
</TldrawUiDialogHeader>
|
||||
<TldrawUiDialogBody style={{ maxWidth: 350 }}>{body}</TldrawUiDialogBody>
|
||||
<TldrawUiDialogFooter className="tlui-dialog__footer__actions">
|
||||
{displayDontShowAgain && (
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="normal"
|
||||
onClick={() => setDontShowAgain(!dontShowAgain)}
|
||||
iconLeft={dontShowAgain ? 'check' : 'checkbox-empty'}
|
||||
style={{ marginRight: 'auto' }}
|
||||
>
|
||||
{`Don't show again`}
|
||||
</Button>
|
||||
<TldrawUiButtonCheck checked={dontShowAgain} />
|
||||
<TldrawUiButtonLabel>Don't show again</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
)}
|
||||
<Button type="normal" onClick={onCancel}>
|
||||
{cancel}
|
||||
</Button>
|
||||
<Button type="primary" onClick={async () => onContinue()}>
|
||||
{confirm}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
<TldrawUiButton type="normal" onClick={onCancel}>
|
||||
<TldrawUiButtonLabel>{cancel}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
<TldrawUiButton type="primary" onClick={async () => onContinue()}>
|
||||
<TldrawUiButtonLabel>{confirm}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDialogFooter>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3,9 +3,15 @@ import { T, TLBaseShape, track, useEditor } from '@tldraw/editor'
|
|||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { TLUiDialogProps } from '../context/dialogs'
|
||||
import { useTranslation } from '../hooks/useTranslation/useTranslation'
|
||||
import { Button } from './primitives/Button'
|
||||
import { DialogBody, DialogCloseButton, DialogFooter, DialogHeader } from './primitives/Dialog'
|
||||
import { Input } from './primitives/Input'
|
||||
import { TldrawUiButton } from './primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiButtonLabel } from './primitives/Button/TldrawUiButtonLabel'
|
||||
import {
|
||||
TldrawUiDialogBody,
|
||||
TldrawUiDialogCloseButton,
|
||||
TldrawUiDialogFooter,
|
||||
TldrawUiDialogHeader,
|
||||
} from './primitives/TldrawUiDialog'
|
||||
import { TldrawUiInput } from './primitives/TldrawUiInput'
|
||||
|
||||
// A url can either be invalid, or valid with a protocol, or valid without a protocol.
|
||||
// For example, "aol.com" would be valid with a protocol ()
|
||||
|
@ -134,13 +140,13 @@ export const EditLinkDialogInner = track(function EditLinkDialogInner({
|
|||
|
||||
return (
|
||||
<>
|
||||
<DialogHeader>
|
||||
<TldrawUiDialogHeader>
|
||||
<DialogTitle>{msg('edit-link-title')}</DialogTitle>
|
||||
<DialogCloseButton />
|
||||
</DialogHeader>
|
||||
<DialogBody>
|
||||
<TldrawUiDialogCloseButton />
|
||||
</TldrawUiDialogHeader>
|
||||
<TldrawUiDialogBody>
|
||||
<div className="tlui-edit-link-dialog">
|
||||
<Input
|
||||
<TldrawUiInput
|
||||
ref={rInput}
|
||||
className="tlui-edit-link-dialog__input"
|
||||
label="edit-link-url"
|
||||
|
@ -152,26 +158,26 @@ export const EditLinkDialogInner = track(function EditLinkDialogInner({
|
|||
/>
|
||||
<div>{urlInputState.valid ? msg('edit-link-detail') : msg('edit-link-invalid-url')}</div>
|
||||
</div>
|
||||
</DialogBody>
|
||||
<DialogFooter className="tlui-dialog__footer__actions">
|
||||
<Button type="normal" onClick={handleCancel} onTouchEnd={handleCancel}>
|
||||
{msg('edit-link-cancel')}
|
||||
</Button>
|
||||
</TldrawUiDialogBody>
|
||||
<TldrawUiDialogFooter className="tlui-dialog__footer__actions">
|
||||
<TldrawUiButton type="normal" onClick={handleCancel} onTouchEnd={handleCancel}>
|
||||
<TldrawUiButtonLabel>{msg('edit-link-cancel')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
{isRemoving ? (
|
||||
<Button type={'danger'} onTouchEnd={handleClear} onClick={handleClear}>
|
||||
{msg('edit-link-clear')}
|
||||
</Button>
|
||||
<TldrawUiButton type={'danger'} onTouchEnd={handleClear} onClick={handleClear}>
|
||||
<TldrawUiButtonLabel>{msg('edit-link-clear')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
) : (
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="primary"
|
||||
disabled={!urlInputState.valid}
|
||||
onTouchEnd={handleComplete}
|
||||
onClick={handleComplete}
|
||||
>
|
||||
{msg('edit-link-save')}
|
||||
</Button>
|
||||
<TldrawUiButtonLabel>{msg('edit-link-save')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
)}
|
||||
</DialogFooter>
|
||||
</TldrawUiDialogFooter>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -5,10 +5,16 @@ import { TLEmbedResult, getEmbedInfo } from '../../utils/embeds/embeds'
|
|||
import { useAssetUrls } from '../context/asset-urls'
|
||||
import { TLUiDialogProps } from '../context/dialogs'
|
||||
import { untranslated, useTranslation } from '../hooks/useTranslation/useTranslation'
|
||||
import { Button } from './primitives/Button'
|
||||
import { DialogBody, DialogCloseButton, DialogFooter, DialogHeader } from './primitives/Dialog'
|
||||
import { Icon } from './primitives/Icon'
|
||||
import { Input } from './primitives/Input'
|
||||
import { TldrawUiButton } from './primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiButtonLabel } from './primitives/Button/TldrawUiButtonLabel'
|
||||
import {
|
||||
TldrawUiDialogBody,
|
||||
TldrawUiDialogCloseButton,
|
||||
TldrawUiDialogFooter,
|
||||
TldrawUiDialogHeader,
|
||||
} from './primitives/TldrawUiDialog'
|
||||
import { TldrawUiIcon } from './primitives/TldrawUiIcon'
|
||||
import { TldrawUiInput } from './primitives/TldrawUiInput'
|
||||
|
||||
export const EmbedDialog = track(function EmbedDialog({ onClose }: TLUiDialogProps) {
|
||||
const editor = useEditor()
|
||||
|
@ -30,18 +36,18 @@ export const EmbedDialog = track(function EmbedDialog({ onClose }: TLUiDialogPro
|
|||
|
||||
return (
|
||||
<>
|
||||
<DialogHeader>
|
||||
<TldrawUiDialogHeader>
|
||||
<DialogTitle>
|
||||
{embedDefinition
|
||||
? `${msg('embed-title')} — ${embedDefinition.title}`
|
||||
: msg('embed-title')}
|
||||
</DialogTitle>
|
||||
<DialogCloseButton />
|
||||
</DialogHeader>
|
||||
<TldrawUiDialogCloseButton />
|
||||
</TldrawUiDialogHeader>
|
||||
{embedDefinition ? (
|
||||
<>
|
||||
<DialogBody className="tlui-embed-dialog__enter">
|
||||
<Input
|
||||
<TldrawUiDialogBody className="tlui-embed-dialog__enter">
|
||||
<TldrawUiInput
|
||||
className="tlui-embed-dialog__input"
|
||||
label="embed-url"
|
||||
placeholder="http://example.com"
|
||||
|
@ -77,7 +83,7 @@ export const EmbedDialog = track(function EmbedDialog({ onClose }: TLUiDialogPro
|
|||
className="tlui-embed-dialog__instruction__link"
|
||||
>
|
||||
Learn more.
|
||||
<Icon icon="external-link" small />
|
||||
<TldrawUiIcon icon="external-link" small />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
|
@ -86,23 +92,25 @@ export const EmbedDialog = track(function EmbedDialog({ onClose }: TLUiDialogPro
|
|||
{showError ? msg('embed-invalid-url') : '\xa0'}
|
||||
</div>
|
||||
)}
|
||||
</DialogBody>
|
||||
<DialogFooter className="tlui-dialog__footer__actions">
|
||||
<Button
|
||||
</TldrawUiDialogBody>
|
||||
<TldrawUiDialogFooter className="tlui-dialog__footer__actions">
|
||||
<TldrawUiButton
|
||||
type="normal"
|
||||
onClick={() => {
|
||||
setEmbedDefinition(null)
|
||||
setEmbedInfoForUrl(null)
|
||||
setUrl('')
|
||||
}}
|
||||
label="embed-back"
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonLabel>{msg('embed-back')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
<div className="tlui-embed__spacer" />
|
||||
<Button type="normal" label="embed-cancel" onClick={onClose} />
|
||||
<Button
|
||||
<TldrawUiButton type="normal" onClick={onClose}>
|
||||
<TldrawUiButtonLabel>{msg('embed-cancel')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
<TldrawUiButton
|
||||
type="primary"
|
||||
disabled={!embedInfoForUrl}
|
||||
label="embed-create"
|
||||
onClick={() => {
|
||||
if (!embedInfoForUrl) return
|
||||
|
||||
|
@ -115,28 +123,26 @@ export const EmbedDialog = track(function EmbedDialog({ onClose }: TLUiDialogPro
|
|||
|
||||
onClose()
|
||||
}}
|
||||
/>
|
||||
</DialogFooter>
|
||||
>
|
||||
<TldrawUiButtonLabel>{msg('embed-create')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDialogFooter>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<DialogBody className="tlui-embed-dialog__list">
|
||||
<TldrawUiDialogBody className="tlui-embed-dialog__list">
|
||||
{EMBED_DEFINITIONS.map((def) => {
|
||||
return (
|
||||
<Button
|
||||
type="menu"
|
||||
key={def.type}
|
||||
onClick={() => setEmbedDefinition(def)}
|
||||
label={untranslated(def.title)}
|
||||
>
|
||||
<TldrawUiButton type="menu" key={def.type} onClick={() => setEmbedDefinition(def)}>
|
||||
<TldrawUiButtonLabel>{untranslated(def.title)}</TldrawUiButtonLabel>
|
||||
<div
|
||||
className="tlui-embed-dialog__item__image"
|
||||
style={{ backgroundImage: `url(${assetUrls.embedIcons[def.type]})` }}
|
||||
/>
|
||||
</Button>
|
||||
</TldrawUiButton>
|
||||
)
|
||||
})}
|
||||
</DialogBody>
|
||||
</TldrawUiDialogBody>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -2,12 +2,14 @@ import { memo } from 'react'
|
|||
import { PORTRAIT_BREAKPOINT } from '../../constants'
|
||||
import { useBreakpoint } from '../../context/breakpoints'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { TldrawUiMenuContextProvider } from '../menus/TldrawUiMenuContext'
|
||||
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
|
||||
import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuRoot,
|
||||
DropdownMenuTrigger,
|
||||
} from '../primitives/DropdownMenu'
|
||||
TldrawUiDropdownMenuContent,
|
||||
TldrawUiDropdownMenuRoot,
|
||||
TldrawUiDropdownMenuTrigger,
|
||||
} from '../primitives/TldrawUiDropdownMenu'
|
||||
import { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'
|
||||
import { DefaultHelpMenuContent } from './DefaultHelpMenuContent'
|
||||
|
||||
/** @public */
|
||||
|
@ -29,19 +31,18 @@ export const DefaultHelpMenu = memo(function DefaultHelpMenu({ children }: TLUiH
|
|||
|
||||
return (
|
||||
<div className="tlui-help-menu">
|
||||
<DropdownMenuRoot id="help menu">
|
||||
<DropdownMenuTrigger
|
||||
type="help"
|
||||
smallIcon
|
||||
title={msg('help-menu.title')}
|
||||
icon="question-mark"
|
||||
/>
|
||||
<DropdownMenuContent side="top" align="end" alignOffset={0} sideOffset={8}>
|
||||
<TldrawUiDropdownMenuRoot id="help menu">
|
||||
<TldrawUiDropdownMenuTrigger>
|
||||
<TldrawUiButton type="help" title={msg('help-menu.title')}>
|
||||
<TldrawUiButtonIcon icon="question-mark" small />
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDropdownMenuTrigger>
|
||||
<TldrawUiDropdownMenuContent side="top" align="end" alignOffset={0} sideOffset={8}>
|
||||
<TldrawUiMenuContextProvider type="menu" sourceId="help-menu">
|
||||
{content}
|
||||
</TldrawUiMenuContextProvider>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenuRoot>
|
||||
</TldrawUiDropdownMenuContent>
|
||||
</TldrawUiDropdownMenuRoot>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useTldrawUiComponents } from '../../context/components'
|
||||
import { useDialogs } from '../../context/dialogs'
|
||||
import { LanguageMenu } from '../LanguageMenu'
|
||||
import { TldrawUiMenuItem } from '../menus/TldrawUiMenuItem'
|
||||
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
|
||||
|
||||
/** @public */
|
||||
export function DefaultHelpMenuContent() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useEditor } from '@tldraw/editor'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useActions } from '../../context/actions'
|
||||
import { TldrawUiMenuItem } from '../menus/TldrawUiMenuItem'
|
||||
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
|
||||
|
||||
export function BackToContent() {
|
||||
const editor = useEditor()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { TldrawUiMenuContextProvider } from '../menus/TldrawUiMenuContext'
|
||||
import { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'
|
||||
import { DefaultHelperButtonsContent } from './DefaultHelperButtonsContent'
|
||||
|
||||
/** @public */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useEditor, useValue } from '@tldraw/editor'
|
||||
import { useActions } from '../../context/actions'
|
||||
import { TldrawUiMenuItem } from '../menus/TldrawUiMenuItem'
|
||||
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
|
||||
|
||||
export function ExitPenMode() {
|
||||
const editor = useEditor()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useEditor, useValue } from '@tldraw/editor'
|
||||
import { useActions } from '../../context/actions'
|
||||
import { TldrawUiMenuItem } from '../menus/TldrawUiMenuItem'
|
||||
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
|
||||
|
||||
export function StopFollowing() {
|
||||
const editor = useEditor()
|
||||
|
|
|
@ -2,8 +2,12 @@ import { DialogTitle } from '@radix-ui/react-dialog'
|
|||
import { memo } from 'react'
|
||||
import { TLUiDialogProps } from '../../context/dialogs'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { TldrawUiMenuContextProvider } from '../menus/TldrawUiMenuContext'
|
||||
import { DialogBody, DialogCloseButton, DialogHeader } from '../primitives/Dialog'
|
||||
import {
|
||||
TldrawUiDialogBody,
|
||||
TldrawUiDialogCloseButton,
|
||||
TldrawUiDialogHeader,
|
||||
} from '../primitives/TldrawUiDialog'
|
||||
import { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'
|
||||
import { DefaultKeyboardShortcutsDialogContent } from './DefaultKeyboardShortcutsDialogContent'
|
||||
|
||||
/** @public */
|
||||
|
@ -21,15 +25,15 @@ export const DefaultKeyboardShortcutsDialog = memo(function DefaultKeyboardShort
|
|||
|
||||
return (
|
||||
<>
|
||||
<DialogHeader className="tlui-shortcuts-dialog__header">
|
||||
<TldrawUiDialogHeader className="tlui-shortcuts-dialog__header">
|
||||
<DialogTitle>{msg('shortcuts-title')}</DialogTitle>
|
||||
<DialogCloseButton />
|
||||
</DialogHeader>
|
||||
<DialogBody className="tlui-shortcuts-dialog__body">
|
||||
<TldrawUiDialogCloseButton />
|
||||
</TldrawUiDialogHeader>
|
||||
<TldrawUiDialogBody className="tlui-shortcuts-dialog__body">
|
||||
<TldrawUiMenuContextProvider type="keyboard-shortcuts" sourceId="kbd">
|
||||
{content}
|
||||
</TldrawUiMenuContextProvider>
|
||||
</DialogBody>
|
||||
</TldrawUiDialogBody>
|
||||
<div className="tlui-dialog__scrim" />
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useActions } from '../../context/actions'
|
||||
import { useTools } from '../../hooks/useTools'
|
||||
import { TldrawUiMenuGroup } from '../menus/TldrawUiMenuGroup'
|
||||
import { TldrawUiMenuItem } from '../menus/TldrawUiMenuItem'
|
||||
import { TldrawUiMenuGroup } from '../primitives/menus/TldrawUiMenuGroup'
|
||||
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
|
||||
|
||||
/** @public */
|
||||
export function DefaultKeyboardShortcutsDialogContent() {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { useEditor } from '@tldraw/editor'
|
||||
import { useUiEvents } from '../context/events'
|
||||
import { useLanguages } from '../hooks/useTranslation/useLanguages'
|
||||
import { TldrawUiMenuCheckboxItem } from './menus/TldrawUiMenuCheckboxItem'
|
||||
import { TldrawUiMenuGroup } from './menus/TldrawUiMenuGroup'
|
||||
import { TldrawUiMenuSubmenu } from './menus/TldrawUiMenuSubmenu'
|
||||
import { TldrawUiMenuCheckboxItem } from './primitives/menus/TldrawUiMenuCheckboxItem'
|
||||
import { TldrawUiMenuGroup } from './primitives/menus/TldrawUiMenuGroup'
|
||||
import { TldrawUiMenuSubmenu } from './primitives/menus/TldrawUiMenuSubmenu'
|
||||
|
||||
export function LanguageMenu() {
|
||||
const editor = useEditor()
|
||||
|
|
|
@ -3,8 +3,9 @@ import { useContainer } from '@tldraw/editor'
|
|||
import { memo } from 'react'
|
||||
import { useMenuIsOpen } from '../../hooks/useMenuIsOpen'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { TldrawUiMenuContextProvider } from '../menus/TldrawUiMenuContext'
|
||||
import { Button } from '../primitives/Button'
|
||||
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
|
||||
import { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'
|
||||
import { DefaultMainMenuContent } from './DefaultMainMenuContent'
|
||||
|
||||
/** @public */
|
||||
|
@ -26,14 +27,9 @@ export const DefaultMainMenu = memo(function DefaultMainMenu({ children }: TLUiM
|
|||
return (
|
||||
<_Dropdown.Root dir="ltr" open={isOpen} onOpenChange={onOpenChange} modal={false}>
|
||||
<_Dropdown.Trigger asChild dir="ltr">
|
||||
<Button
|
||||
type="icon"
|
||||
className="tlui-menu__trigger"
|
||||
data-testid="main.menu"
|
||||
title={msg('menu.title')}
|
||||
icon="menu"
|
||||
smallIcon
|
||||
/>
|
||||
<TldrawUiButton type="icon" data-testid="main.menu" title={msg('menu.title')}>
|
||||
<TldrawUiButtonIcon icon="menu" small />
|
||||
</TldrawUiButton>
|
||||
</_Dropdown.Trigger>
|
||||
<_Dropdown.Portal container={container}>
|
||||
<_Dropdown.Content
|
||||
|
|
|
@ -29,9 +29,9 @@ import {
|
|||
ZoomToFitMenuItem,
|
||||
ZoomToSelectionMenuItem,
|
||||
} from '../menu-items'
|
||||
import { TldrawUiMenuGroup } from '../menus/TldrawUiMenuGroup'
|
||||
import { TldrawUiMenuItem } from '../menus/TldrawUiMenuItem'
|
||||
import { TldrawUiMenuSubmenu } from '../menus/TldrawUiMenuSubmenu'
|
||||
import { TldrawUiMenuGroup } from '../primitives/menus/TldrawUiMenuGroup'
|
||||
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
|
||||
import { TldrawUiMenuSubmenu } from '../primitives/menus/TldrawUiMenuSubmenu'
|
||||
|
||||
/** @public */
|
||||
export function DefaultMainMenuContent() {
|
||||
|
|
27
packages/tldraw/src/lib/ui/components/MenuPanel.tsx
Normal file
27
packages/tldraw/src/lib/ui/components/MenuPanel.tsx
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { memo } from 'react'
|
||||
import { useBreakpoint } from '../context/breakpoints'
|
||||
import { useTldrawUiComponents } from '../context/components'
|
||||
|
||||
/** @public */
|
||||
export const DefaultMenuPanel = memo(function MenuPanel() {
|
||||
const breakpoint = useBreakpoint()
|
||||
|
||||
const { MainMenu, QuickActions, ActionsMenu, PageMenu } = useTldrawUiComponents()
|
||||
|
||||
if (!MainMenu && !PageMenu && breakpoint < 6) return null
|
||||
|
||||
return (
|
||||
<div className="tlui-menu-zone">
|
||||
<div className="tlui-buttons__horizontal">
|
||||
{MainMenu && <MainMenu />}
|
||||
{PageMenu && <PageMenu />}
|
||||
{breakpoint < 6 ? null : (
|
||||
<>
|
||||
{QuickActions && <QuickActions />}
|
||||
{ActionsMenu && <ActionsMenu />}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
|
@ -9,8 +9,13 @@ import { useCallback } from 'react'
|
|||
import { useTldrawUiComponents } from '../context/components'
|
||||
import { useRelevantStyles } from '../hooks/useRevelantStyles'
|
||||
import { useTranslation } from '../hooks/useTranslation/useTranslation'
|
||||
import { Icon } from './primitives/Icon'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from './primitives/Popover'
|
||||
import { TldrawUiButton } from './primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiButtonIcon } from './primitives/Button/TldrawUiButtonIcon'
|
||||
import {
|
||||
TldrawUiPopover,
|
||||
TldrawUiPopoverContent,
|
||||
TldrawUiPopoverTrigger,
|
||||
} from './primitives/TldrawUiPopover'
|
||||
|
||||
export function MobileStylePanel() {
|
||||
const editor = useEditor()
|
||||
|
@ -42,22 +47,24 @@ export function MobileStylePanel() {
|
|||
if (!StylePanel) return null
|
||||
|
||||
return (
|
||||
<Popover id="style menu" onOpenChange={handleStylesOpenChange}>
|
||||
<PopoverTrigger
|
||||
disabled={disableStylePanel}
|
||||
type="tool"
|
||||
data-testid="mobile.styles"
|
||||
style={{
|
||||
color: disableStylePanel ? 'var(--color-muted-1)' : currentColor,
|
||||
}}
|
||||
title={msg('style-panel.title')}
|
||||
>
|
||||
<Icon icon={disableStylePanel ? 'blob' : color?.type === 'mixed' ? 'mixed' : 'blob'} />
|
||||
</PopoverTrigger>
|
||||
<PopoverContent side="top" align="end">
|
||||
<TldrawUiPopover id="mobile style menu" onOpenChange={handleStylesOpenChange}>
|
||||
<TldrawUiPopoverTrigger>
|
||||
<TldrawUiButton
|
||||
type="tool"
|
||||
title={msg('style-panel.title')}
|
||||
data-testid="mobile.styles"
|
||||
disabled={disableStylePanel}
|
||||
style={{ color: disableStylePanel ? 'var(--color-muted-1)' : currentColor }}
|
||||
>
|
||||
<TldrawUiButtonIcon
|
||||
icon={disableStylePanel ? 'blob' : color?.type === 'mixed' ? 'mixed' : 'blob'}
|
||||
/>
|
||||
</TldrawUiButton>
|
||||
</TldrawUiPopoverTrigger>
|
||||
<TldrawUiPopoverContent side="top" align="end">
|
||||
<_StylePanel />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</TldrawUiPopoverContent>
|
||||
</TldrawUiPopover>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -66,5 +73,5 @@ function _StylePanel() {
|
|||
const relevantStyles = useRelevantStyles()
|
||||
|
||||
if (!StylePanel) return null
|
||||
return <StylePanel relevantStyles={relevantStyles} />
|
||||
return <StylePanel relevantStyles={relevantStyles} isMobile />
|
||||
}
|
||||
|
|
|
@ -5,8 +5,9 @@ import { useBreakpoint } from '../../context/breakpoints'
|
|||
import { useTldrawUiComponents } from '../../context/components'
|
||||
import { useLocalStorageState } from '../../hooks/useLocalStorageState'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { Button } from '../primitives/Button'
|
||||
import { kbdStr } from '../primitives/shared'
|
||||
import { kbdStr } from '../../kbd-utils'
|
||||
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
|
||||
|
||||
/** @public */
|
||||
export const DefaultNavigationPanel = memo(function DefaultNavigationPanel() {
|
||||
|
@ -35,42 +36,46 @@ export const DefaultNavigationPanel = memo(function DefaultNavigationPanel() {
|
|||
<>
|
||||
{ZoomMenu && <ZoomMenu />}
|
||||
{Minimap && (
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
icon={collapsed ? 'chevrons-ne' : 'chevrons-sw'}
|
||||
data-testid="minimap.toggle"
|
||||
title={msg('navigation-zone.toggle-minimap')}
|
||||
className="tlui-navigation-panel__toggle"
|
||||
onClick={toggleMinimap}
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonIcon icon={collapsed ? 'chevrons-ne' : 'chevrons-sw'} />
|
||||
</TldrawUiButton>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
icon="minus"
|
||||
data-testid="minimap.zoom-out"
|
||||
title={`${msg(unwrapLabel(actions['zoom-out'].label))} ${kbdStr(actions['zoom-out'].kbd!)}`}
|
||||
onClick={() => actions['zoom-out'].onSelect('navigation-zone')}
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonIcon icon="minus" />
|
||||
</TldrawUiButton>
|
||||
{ZoomMenu && <ZoomMenu />}
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
icon="plus"
|
||||
data-testid="minimap.zoom-in"
|
||||
title={`${msg(unwrapLabel(actions['zoom-in'].label))} ${kbdStr(actions['zoom-in'].kbd!)}`}
|
||||
onClick={() => actions['zoom-in'].onSelect('navigation-zone')}
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonIcon icon="plus" />
|
||||
</TldrawUiButton>
|
||||
{Minimap && (
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
icon={collapsed ? 'chevrons-ne' : 'chevrons-sw'}
|
||||
data-testid="minimap.toggle"
|
||||
title={msg('navigation-zone.toggle-minimap')}
|
||||
className="tlui-navigation-panel__toggle"
|
||||
onClick={toggleMinimap}
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonIcon icon={collapsed ? 'chevrons-ne' : 'chevrons-sw'} />
|
||||
</TldrawUiButton>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import classNames from 'classnames'
|
||||
import { useRef } from 'react'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { Icon } from '../primitives/Icon'
|
||||
import { TldrawUiIcon } from '../primitives/TldrawUiIcon'
|
||||
|
||||
/** @public */
|
||||
export function OfflineIndicator() {
|
||||
|
@ -11,7 +11,7 @@ export function OfflineIndicator() {
|
|||
return (
|
||||
<div className={classNames('tlui-offline-indicator')} ref={rContainer}>
|
||||
{msg('status.offline')}
|
||||
<Icon aria-label="offline" icon="status-offline" small />
|
||||
<TldrawUiIcon aria-label="offline" icon="status-offline" small />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -13,9 +13,15 @@ import { useBreakpoint } from '../../context/breakpoints'
|
|||
import { useMenuIsOpen } from '../../hooks/useMenuIsOpen'
|
||||
import { useReadonly } from '../../hooks/useReadonly'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { Button } from '../primitives/Button'
|
||||
import { Icon } from '../primitives/Icon'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '../primitives/Popover'
|
||||
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiButtonCheck } from '../primitives/Button/TldrawUiButtonCheck'
|
||||
import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
|
||||
import { TldrawUiButtonLabel } from '../primitives/Button/TldrawUiButtonLabel'
|
||||
import {
|
||||
TldrawUiPopover,
|
||||
TldrawUiPopoverContent,
|
||||
TldrawUiPopoverTrigger,
|
||||
} from '../primitives/TldrawUiPopover'
|
||||
import { PageItemInput } from './PageItemInput'
|
||||
import { PageItemSubmenu } from './PageItemSubmenu'
|
||||
import { onMovePage } from './edit-pages-shared'
|
||||
|
@ -255,33 +261,30 @@ export const DefaultPageMenu = memo(function DefaultPageMenu() {
|
|||
}, [editor, msg, isReadonlyMode])
|
||||
|
||||
return (
|
||||
<Popover id="pages" onOpenChange={onOpenChange} open={isOpen}>
|
||||
<PopoverTrigger
|
||||
className="tlui-page-menu__trigger tlui-menu__trigger"
|
||||
data-testid="main.page-menu"
|
||||
icon="chevron-down"
|
||||
type="menu"
|
||||
title={currentPage.name}
|
||||
>
|
||||
<div className="tlui-page-menu__name">{currentPage.name}</div>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent side="bottom" align="start" sideOffset={6}>
|
||||
<TldrawUiPopover id="pages" onOpenChange={onOpenChange} open={isOpen}>
|
||||
<TldrawUiPopoverTrigger data-testid="main.page-menu">
|
||||
<TldrawUiButton type="menu" title={currentPage.name} className="tlui-page-menu__trigger">
|
||||
<div className="tlui-page-menu__name">{currentPage.name}</div>
|
||||
<TldrawUiButtonIcon icon="chevron-down" small />
|
||||
</TldrawUiButton>
|
||||
</TldrawUiPopoverTrigger>
|
||||
<TldrawUiPopoverContent side="bottom" align="start" sideOffset={6}>
|
||||
<div className="tlui-page-menu__wrapper">
|
||||
<div className="tlui-page-menu__header">
|
||||
<div className="tlui-page-menu__header__title">{msg('page-menu.title')}</div>
|
||||
{!isReadonlyMode && (
|
||||
<div className="tlui-buttons__horizontal">
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
data-testid="page-menu.edit"
|
||||
title={msg(isEditing ? 'page-menu.edit-done' : 'page-menu.edit-start')}
|
||||
icon={isEditing ? 'check' : 'edit'}
|
||||
onClick={toggleEditing}
|
||||
/>
|
||||
<Button
|
||||
>
|
||||
<TldrawUiButtonIcon icon={isEditing ? 'check' : 'edit'} />
|
||||
</TldrawUiButton>
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
data-testid="page-menu.create"
|
||||
icon="plus"
|
||||
title={msg(
|
||||
maxPageCountReached
|
||||
? 'page-menu.max-page-count-reached'
|
||||
|
@ -289,7 +292,9 @@ export const DefaultPageMenu = memo(function DefaultPageMenu() {
|
|||
)}
|
||||
disabled={maxPageCountReached}
|
||||
onClick={handleCreatePageClick}
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonIcon icon="plus" />
|
||||
</TldrawUiButton>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@ -314,24 +319,25 @@ export const DefaultPageMenu = memo(function DefaultPageMenu() {
|
|||
transform: `translate(0px, ${position.y + position.offsetY}px)`,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
tabIndex={-1}
|
||||
className="tlui-page_menu__item__sortable__handle"
|
||||
icon="drag-handle-dots"
|
||||
onPointerDown={handlePointerDown}
|
||||
onPointerUp={handlePointerUp}
|
||||
onPointerMove={handlePointerMove}
|
||||
onKeyDown={handleKeyDown}
|
||||
data-id={page.id}
|
||||
data-index={index}
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonIcon icon="drag-handle-dots" />
|
||||
</TldrawUiButton>
|
||||
{breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM && isCoarsePointer ? (
|
||||
// sigh, this is a workaround for iOS Safari
|
||||
// because the device and the radix popover seem
|
||||
// to be fighting over scroll position. Nothing
|
||||
// else seems to work!
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="normal"
|
||||
className="tlui-page-menu__item__button"
|
||||
onClick={() => {
|
||||
|
@ -341,10 +347,10 @@ export const DefaultPageMenu = memo(function DefaultPageMenu() {
|
|||
}
|
||||
}}
|
||||
onDoubleClick={toggleEditing}
|
||||
isChecked={page.id === currentPage.id}
|
||||
>
|
||||
<span>{page.name}</span>
|
||||
</Button>
|
||||
<TldrawUiButtonCheck checked={page.id === currentPage.id} />
|
||||
<TldrawUiButtonLabel>{page.name}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
) : (
|
||||
<div
|
||||
className="tlui-page_menu__item__sortable__title"
|
||||
|
@ -369,19 +375,16 @@ export const DefaultPageMenu = memo(function DefaultPageMenu() {
|
|||
data-testid={`page-menu-item-${page.id}`}
|
||||
className="tlui-page-menu__item"
|
||||
>
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="normal"
|
||||
className="tlui-page-menu__item__button tlui-page-menu__item__button__checkbox"
|
||||
className="tlui-page-menu__item__button"
|
||||
onClick={() => editor.setCurrentPage(page.id)}
|
||||
onDoubleClick={toggleEditing}
|
||||
isChecked={page.id === currentPage.id}
|
||||
title={msg('page-menu.go-to-page')}
|
||||
>
|
||||
<div className="tlui-page-menu__item__button__check">
|
||||
{page.id === currentPage.id && <Icon icon="check" />}
|
||||
</div>
|
||||
<span>{page.name}</span>
|
||||
</Button>
|
||||
<TldrawUiButtonCheck checked={page.id === currentPage.id} />
|
||||
<TldrawUiButtonLabel>{page.name}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
{!isReadonlyMode && (
|
||||
<div className="tlui-page_menu__item__submenu">
|
||||
<PageItemSubmenu
|
||||
|
@ -409,7 +412,7 @@ export const DefaultPageMenu = memo(function DefaultPageMenu() {
|
|||
})}
|
||||
</div>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</TldrawUiPopoverContent>
|
||||
</TldrawUiPopover>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { TLPageId, useEditor } from '@tldraw/editor'
|
||||
import { useCallback, useRef } from 'react'
|
||||
import { Input } from '../primitives/Input'
|
||||
import { TldrawUiInput } from '../primitives/TldrawUiInput'
|
||||
|
||||
export const PageItemInput = function PageItemInput({
|
||||
name,
|
||||
|
@ -31,7 +31,7 @@ export const PageItemInput = function PageItemInput({
|
|||
)
|
||||
|
||||
return (
|
||||
<Input
|
||||
<TldrawUiInput
|
||||
className="tlui-page-menu__item__input"
|
||||
ref={(el) => (rInput.current = el)}
|
||||
defaultValue={name}
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import { MAX_PAGES, PageRecordType, TLPageId, track, useEditor } from '@tldraw/editor'
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { TldrawUiMenuContextProvider } from '../menus/TldrawUiMenuContext'
|
||||
import { TldrawUiMenuGroup } from '../menus/TldrawUiMenuGroup'
|
||||
import { TldrawUiMenuItem } from '../menus/TldrawUiMenuItem'
|
||||
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
|
||||
import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuRoot,
|
||||
DropdownMenuTrigger,
|
||||
} from '../primitives/DropdownMenu'
|
||||
TldrawUiDropdownMenuContent,
|
||||
TldrawUiDropdownMenuRoot,
|
||||
TldrawUiDropdownMenuTrigger,
|
||||
} from '../primitives/TldrawUiDropdownMenu'
|
||||
import { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'
|
||||
import { TldrawUiMenuGroup } from '../primitives/menus/TldrawUiMenuGroup'
|
||||
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
|
||||
import { onMovePage } from './edit-pages-shared'
|
||||
|
||||
export interface PageItemSubmenuProps {
|
||||
|
@ -48,13 +50,13 @@ export const PageItemSubmenu = track(function PageItemSubmenu({
|
|||
}, [editor, item])
|
||||
|
||||
return (
|
||||
<DropdownMenuRoot id={`page item submenu ${index}`}>
|
||||
<DropdownMenuTrigger
|
||||
type="icon"
|
||||
title={msg('page-menu.submenu.title')}
|
||||
icon="dots-vertical"
|
||||
/>
|
||||
<DropdownMenuContent alignOffset={0} side="right" sideOffset={-4}>
|
||||
<TldrawUiDropdownMenuRoot id={`page item submenu ${index}`}>
|
||||
<TldrawUiDropdownMenuTrigger>
|
||||
<TldrawUiButton type="icon" title={msg('page-menu.submenu.title')}>
|
||||
<TldrawUiButtonIcon icon="dots-vertical" />
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDropdownMenuTrigger>
|
||||
<TldrawUiDropdownMenuContent alignOffset={0} side="right" sideOffset={-4}>
|
||||
<TldrawUiMenuContextProvider type="menu" sourceId="page-menu">
|
||||
<TldrawUiMenuGroup id="modify">
|
||||
{onRename && (
|
||||
|
@ -87,7 +89,7 @@ export const PageItemSubmenu = track(function PageItemSubmenu({
|
|||
</TldrawUiMenuGroup>
|
||||
)}
|
||||
</TldrawUiMenuContextProvider>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenuRoot>
|
||||
</TldrawUiDropdownMenuContent>
|
||||
</TldrawUiDropdownMenuRoot>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { memo } from 'react'
|
||||
import { TldrawUiMenuContextProvider } from '../menus/TldrawUiMenuContext'
|
||||
import { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'
|
||||
import { DefaultQuickActionsContent } from './DefaultQuickActionsContent'
|
||||
|
||||
/** @public */
|
||||
|
|
|
@ -2,7 +2,7 @@ import { useEditor, useValue } from '@tldraw/editor'
|
|||
import { useActions } from '../../context/actions'
|
||||
import { useCanRedo, useCanUndo, useUnlockedSelectedShapesCount } from '../../hooks/menu-hooks'
|
||||
import { useReadonly } from '../../hooks/useReadonly'
|
||||
import { TldrawUiMenuItem } from '../menus/TldrawUiMenuItem'
|
||||
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
|
||||
|
||||
/** @public */
|
||||
export function DefaultQuickActionsContent() {
|
||||
|
|
|
@ -33,7 +33,7 @@ export const DefaultStylePanel = memo(function DefaultStylePanel({
|
|||
|
||||
return (
|
||||
<div
|
||||
className={classNames('tlui-style-panel', { 'tlui-style-panel__wrapper': !isMobile })}
|
||||
className={classNames('', { 'tlui-style-panel__wrapper': !isMobile })}
|
||||
data-ismobile={isMobile}
|
||||
onPointerLeave={handlePointerOut}
|
||||
>
|
||||
|
|
|
@ -17,15 +17,16 @@ import {
|
|||
useEditor,
|
||||
} from '@tldraw/editor'
|
||||
import React from 'react'
|
||||
import { STYLES } from '../../../styles'
|
||||
import { useUiEvents } from '../../context/events'
|
||||
import { useRelevantStyles } from '../../hooks/useRevelantStyles'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { Button } from '../primitives/Button'
|
||||
import { ButtonPicker } from '../primitives/ButtonPicker'
|
||||
import { Slider } from '../primitives/Slider'
|
||||
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
|
||||
import { TldrawUiButtonPicker } from '../primitives/TldrawUiButtonPicker'
|
||||
import { TldrawUiSlider } from '../primitives/TldrawUiSlider'
|
||||
import { DoubleDropdownPicker } from './DoubleDropdownPicker'
|
||||
import { DropdownPicker } from './DropdownPicker'
|
||||
import { STYLES } from './styles'
|
||||
|
||||
/** @public */
|
||||
export type TLUiStylePanelContentProps = {
|
||||
|
@ -137,7 +138,7 @@ function CommonStylePickerSet({
|
|||
aria-label="style panel styles"
|
||||
>
|
||||
{color === undefined ? null : (
|
||||
<ButtonPicker
|
||||
<TldrawUiButtonPicker
|
||||
title={msg('style-panel.color')}
|
||||
uiType="color"
|
||||
style={DefaultColorStyle}
|
||||
|
@ -147,7 +148,7 @@ function CommonStylePickerSet({
|
|||
/>
|
||||
)}
|
||||
{opacity === undefined ? null : (
|
||||
<Slider
|
||||
<TldrawUiSlider
|
||||
data-testid="style.opacity"
|
||||
value={opacityIndex >= 0 ? opacityIndex : tldrawSupportedOpacities.length - 1}
|
||||
label={
|
||||
|
@ -162,7 +163,7 @@ function CommonStylePickerSet({
|
|||
{showPickers && (
|
||||
<div className="tlui-style-panel__section" aria-label="style panel styles">
|
||||
{fill === undefined ? null : (
|
||||
<ButtonPicker
|
||||
<TldrawUiButtonPicker
|
||||
title={msg('style-panel.fill')}
|
||||
uiType="fill"
|
||||
style={DefaultFillStyle}
|
||||
|
@ -172,7 +173,7 @@ function CommonStylePickerSet({
|
|||
/>
|
||||
)}
|
||||
{dash === undefined ? null : (
|
||||
<ButtonPicker
|
||||
<TldrawUiButtonPicker
|
||||
title={msg('style-panel.dash')}
|
||||
uiType="dash"
|
||||
style={DefaultDashStyle}
|
||||
|
@ -182,7 +183,7 @@ function CommonStylePickerSet({
|
|||
/>
|
||||
)}
|
||||
{size === undefined ? null : (
|
||||
<ButtonPicker
|
||||
<TldrawUiButtonPicker
|
||||
title={msg('style-panel.size')}
|
||||
uiType="size"
|
||||
style={DefaultSizeStyle}
|
||||
|
@ -211,7 +212,7 @@ function TextStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap }) {
|
|||
return (
|
||||
<div className="tlui-style-panel__section" aria-label="style panel text">
|
||||
{font === undefined ? null : (
|
||||
<ButtonPicker
|
||||
<TldrawUiButtonPicker
|
||||
title={msg('style-panel.font')}
|
||||
uiType="font"
|
||||
style={DefaultFontStyle}
|
||||
|
@ -223,7 +224,7 @@ function TextStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap }) {
|
|||
|
||||
{align === undefined ? null : (
|
||||
<div className="tlui-style-panel__row">
|
||||
<ButtonPicker
|
||||
<TldrawUiButtonPicker
|
||||
title={msg('style-panel.align')}
|
||||
uiType="align"
|
||||
style={DefaultHorizontalAlignStyle}
|
||||
|
@ -233,13 +234,14 @@ function TextStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap }) {
|
|||
/>
|
||||
<div className="tlui-style-panel__row__extra-button">
|
||||
{verticalAlign === undefined ? (
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
title={msg('style-panel.vertical-align')}
|
||||
data-testid="vertical-align"
|
||||
icon="vertical-align-center"
|
||||
disabled
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonIcon icon="vertical-align-center" />
|
||||
</TldrawUiButton>
|
||||
) : (
|
||||
<DropdownPicker
|
||||
type="icon"
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import { SharedStyle, StyleProp } from '@tldraw/editor'
|
||||
import * as React from 'react'
|
||||
import { StyleValuesForUi } from '../../../styles'
|
||||
import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { TLUiIconType } from '../../icon-types'
|
||||
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
|
||||
import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuRoot,
|
||||
DropdownMenuTrigger,
|
||||
} from '../primitives/DropdownMenu'
|
||||
import { StyleValuesForUi } from './styles'
|
||||
TldrawUiDropdownMenuContent,
|
||||
TldrawUiDropdownMenuItem,
|
||||
TldrawUiDropdownMenuRoot,
|
||||
TldrawUiDropdownMenuTrigger,
|
||||
} from '../primitives/TldrawUiDropdownMenu'
|
||||
|
||||
interface DoubleDropdownPickerProps<T extends string> {
|
||||
uiTypeA: string
|
||||
|
@ -63,78 +64,76 @@ export const DoubleDropdownPicker = React.memo(function DoubleDropdownPicker<T e
|
|||
{msg(label)}
|
||||
</div>
|
||||
<div className="tlui-buttons__horizontal">
|
||||
<DropdownMenuRoot id={`style panel ${uiTypeA} A`}>
|
||||
<DropdownMenuTrigger
|
||||
type="icon"
|
||||
data-testid={`style.${uiTypeA}`}
|
||||
title={
|
||||
msg(labelA) +
|
||||
' — ' +
|
||||
(valueA === null || valueA.type === 'mixed'
|
||||
? msg('style-panel.mixed')
|
||||
: msg(`${uiTypeA}-style.${valueA.value}` as TLUiTranslationKey))
|
||||
}
|
||||
icon={iconA as any}
|
||||
invertIcon
|
||||
smallIcon
|
||||
/>
|
||||
<DropdownMenuContent side="bottom" align="end" sideOffset={0} alignOffset={-2}>
|
||||
<TldrawUiDropdownMenuRoot id={`style panel ${uiTypeA} A`}>
|
||||
<TldrawUiDropdownMenuTrigger>
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
data-testid={`style.${uiTypeA}`}
|
||||
title={
|
||||
msg(labelA) +
|
||||
' — ' +
|
||||
(valueA === null || valueA.type === 'mixed'
|
||||
? msg('style-panel.mixed')
|
||||
: msg(`${uiTypeA}-style.${valueA.value}` as TLUiTranslationKey))
|
||||
}
|
||||
>
|
||||
<TldrawUiButtonIcon icon={iconA} small invertIcon />
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDropdownMenuTrigger>
|
||||
<TldrawUiDropdownMenuContent side="bottom" align="end" sideOffset={0} alignOffset={-2}>
|
||||
<div className="tlui-buttons__grid">
|
||||
{itemsA.map((item) => {
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
type="icon"
|
||||
title={
|
||||
msg(labelA) +
|
||||
' — ' +
|
||||
msg(`${uiTypeA}-style.${item.value}` as TLUiTranslationKey)
|
||||
}
|
||||
data-testid={`style.${uiTypeA}.${item.value}`}
|
||||
key={item.value}
|
||||
icon={item.icon as TLUiIconType}
|
||||
onClick={() => onValueChange(styleA, item.value, false)}
|
||||
invertIcon
|
||||
/>
|
||||
<TldrawUiDropdownMenuItem data-testid={`style.${uiTypeA}.${item.value}`}>
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
key={item.value}
|
||||
onClick={() => onValueChange(styleA, item.value, false)}
|
||||
title={`${msg(labelA)} — ${msg(`${uiTypeA}-style.${item.value}`)}`}
|
||||
>
|
||||
<TldrawUiButtonIcon icon={item.icon} invertIcon />
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDropdownMenuItem>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenuRoot>
|
||||
<DropdownMenuRoot id={`style panel ${uiTypeB}`}>
|
||||
<DropdownMenuTrigger
|
||||
type="icon"
|
||||
data-testid={`style.${uiTypeB}`}
|
||||
title={
|
||||
msg(labelB) +
|
||||
' — ' +
|
||||
(valueB === null || valueB.type === 'mixed'
|
||||
? msg('style-panel.mixed')
|
||||
: msg(`${uiTypeB}-style.${valueB.value}` as TLUiTranslationKey))
|
||||
}
|
||||
icon={iconB as any}
|
||||
smallIcon
|
||||
/>
|
||||
<DropdownMenuContent side="bottom" align="end" sideOffset={0} alignOffset={-2}>
|
||||
</TldrawUiDropdownMenuContent>
|
||||
</TldrawUiDropdownMenuRoot>
|
||||
<TldrawUiDropdownMenuRoot id={`style panel ${uiTypeB}`}>
|
||||
<TldrawUiDropdownMenuTrigger>
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
data-testid={`style.${uiTypeB}`}
|
||||
title={
|
||||
msg(labelB) +
|
||||
' — ' +
|
||||
(valueB === null || valueB.type === 'mixed'
|
||||
? msg('style-panel.mixed')
|
||||
: msg(`${uiTypeB}-style.${valueB.value}` as TLUiTranslationKey))
|
||||
}
|
||||
>
|
||||
<TldrawUiButtonIcon icon={iconB} small />
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDropdownMenuTrigger>
|
||||
<TldrawUiDropdownMenuContent side="bottom" align="end" sideOffset={0} alignOffset={-2}>
|
||||
<div className="tlui-buttons__grid">
|
||||
{itemsB.map((item) => {
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
type="icon"
|
||||
title={
|
||||
msg(labelB) +
|
||||
' — ' +
|
||||
msg(`${uiTypeB}-style.${item.value}` as TLUiTranslationKey)
|
||||
}
|
||||
data-testid={`style.${uiTypeB}.${item.value}`}
|
||||
key={item.value}
|
||||
icon={item.icon as TLUiIconType}
|
||||
onClick={() => onValueChange(styleB, item.value, false)}
|
||||
/>
|
||||
<TldrawUiDropdownMenuItem key={item.value}>
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
title={`${msg(labelB)} — ${msg(`${uiTypeB}-style.${item.value}` as TLUiTranslationKey)}`}
|
||||
data-testid={`style.${uiTypeB}.${item.value}`}
|
||||
onClick={() => onValueChange(styleB, item.value, false)}
|
||||
>
|
||||
<TldrawUiButtonIcon icon={item.icon} />
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDropdownMenuItem>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenuRoot>
|
||||
</TldrawUiDropdownMenuContent>
|
||||
</TldrawUiDropdownMenuRoot>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import { SharedStyle, StyleProp } from '@tldraw/editor'
|
||||
import * as React from 'react'
|
||||
import { StyleValuesForUi } from '../../../styles'
|
||||
import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { TLUiIconType } from '../../icon-types'
|
||||
import { TLUiButtonProps } from '../primitives/Button'
|
||||
import { TLUiButtonProps, TldrawUiButton } from '../primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
|
||||
import { TldrawUiButtonLabel } from '../primitives/Button/TldrawUiButtonLabel'
|
||||
import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuRoot,
|
||||
DropdownMenuTrigger,
|
||||
} from '../primitives/DropdownMenu'
|
||||
import { StyleValuesForUi } from './styles'
|
||||
TldrawUiDropdownMenuContent,
|
||||
TldrawUiDropdownMenuItem,
|
||||
TldrawUiDropdownMenuRoot,
|
||||
TldrawUiDropdownMenuTrigger,
|
||||
} from '../primitives/TldrawUiDropdownMenu'
|
||||
|
||||
interface DropdownPickerProps<T extends string> {
|
||||
id: string
|
||||
|
@ -40,35 +42,38 @@ export const DropdownPicker = React.memo(function DropdownPicker<T extends strin
|
|||
[items, value]
|
||||
)
|
||||
|
||||
const titleStr =
|
||||
value.type === 'mixed'
|
||||
? msg('style-panel.mixed')
|
||||
: msg(`${uiType}-style.${value.value}` as TLUiTranslationKey)
|
||||
const labelStr = label ? msg(label) : ''
|
||||
|
||||
return (
|
||||
<DropdownMenuRoot id={`style panel ${id}`}>
|
||||
<DropdownMenuTrigger
|
||||
type={type}
|
||||
data-testid={`style.${uiType}`}
|
||||
title={
|
||||
value.type === 'mixed'
|
||||
? msg('style-panel.mixed')
|
||||
: msg(`${uiType}-style.${value.value}` as TLUiTranslationKey)
|
||||
}
|
||||
label={label}
|
||||
icon={(icon as TLUiIconType) ?? 'mixed'}
|
||||
/>
|
||||
<DropdownMenuContent side="left" align="center" alignOffset={0}>
|
||||
<TldrawUiDropdownMenuRoot id={`style panel ${id}`}>
|
||||
<TldrawUiDropdownMenuTrigger>
|
||||
<TldrawUiButton type={type} data-testid={`style.${uiType}`} title={titleStr}>
|
||||
<TldrawUiButtonLabel>{labelStr}</TldrawUiButtonLabel>
|
||||
<TldrawUiButtonIcon icon={(icon as TLUiIconType) ?? 'mixed'} />
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDropdownMenuTrigger>
|
||||
<TldrawUiDropdownMenuContent side="left" align="center" alignOffset={0}>
|
||||
<div className="tlui-buttons__grid">
|
||||
{items.map((item) => {
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
type="icon"
|
||||
data-testid={`style.${uiType}.${item.value}`}
|
||||
title={msg(`${uiType}-style.${item.value}` as TLUiTranslationKey)}
|
||||
key={item.value}
|
||||
icon={item.icon as TLUiIconType}
|
||||
onClick={() => onValueChange(style, item.value, false)}
|
||||
/>
|
||||
<TldrawUiDropdownMenuItem key={item.value}>
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
data-testid={`style.${uiType}.${item.value}`}
|
||||
title={msg(`${uiType}-style.${item.value}` as TLUiTranslationKey)}
|
||||
onClick={() => onValueChange(style, item.value, false)}
|
||||
>
|
||||
<TldrawUiButtonIcon icon={item.icon} />
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDropdownMenuItem>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenuRoot>
|
||||
</TldrawUiDropdownMenuContent>
|
||||
</TldrawUiDropdownMenuRoot>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -3,8 +3,9 @@ import * as React from 'react'
|
|||
import { TLUiToast, useToasts } from '../context/toasts'
|
||||
import { useTranslation } from '../hooks/useTranslation/useTranslation'
|
||||
import { TLUiIconType } from '../icon-types'
|
||||
import { Button } from './primitives/Button'
|
||||
import { Icon } from './primitives/Icon'
|
||||
import { TldrawUiButton } from './primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiButtonLabel } from './primitives/Button/TldrawUiButtonLabel'
|
||||
import { TldrawUiIcon } from './primitives/TldrawUiIcon'
|
||||
|
||||
function Toast({ toast }: { toast: TLUiToast }) {
|
||||
const { removeToast } = useToasts()
|
||||
|
@ -26,7 +27,7 @@ function Toast({ toast }: { toast: TLUiToast }) {
|
|||
>
|
||||
{toast.icon && (
|
||||
<div className="tlui-toast__icon">
|
||||
<Icon icon={toast.icon as TLUiIconType} />
|
||||
<TldrawUiIcon icon={toast.icon as TLUiIconType} />
|
||||
</div>
|
||||
)}
|
||||
<div className="tlui-toast__main">
|
||||
|
@ -40,22 +41,28 @@ function Toast({ toast }: { toast: TLUiToast }) {
|
|||
<div className="tlui-toast__actions">
|
||||
{toast.actions.map((action, i) => (
|
||||
<T.Action key={i} altText={action.label} asChild onClick={action.onClick}>
|
||||
<Button type={action.type}>{action.label}</Button>
|
||||
<TldrawUiButton type={action.type}>
|
||||
<TldrawUiButtonLabel>{action.label}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
</T.Action>
|
||||
))}
|
||||
<T.Close asChild>
|
||||
<Button type="normal" className="tlui-toast__close" style={{ marginLeft: 'auto' }}>
|
||||
{toast.closeLabel ?? msg('toast.close')}
|
||||
</Button>
|
||||
<TldrawUiButton
|
||||
type="normal"
|
||||
className="tlui-toast__close"
|
||||
style={{ marginLeft: 'auto' }}
|
||||
>
|
||||
<TldrawUiButtonLabel>{toast.closeLabel ?? msg('toast.close')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
</T.Close>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{!hasActions && (
|
||||
<T.Close asChild>
|
||||
<Button type="normal" className="tlui-toast__close">
|
||||
{toast.closeLabel ?? msg('toast.close')}
|
||||
</Button>
|
||||
<TldrawUiButton type="normal" className="tlui-toast__close">
|
||||
<TldrawUiButtonLabel>{toast.closeLabel ?? msg('toast.close')}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
</T.Close>
|
||||
)}
|
||||
</T.Root>
|
||||
|
|
|
@ -10,15 +10,16 @@ import { useReadonly } from '../../hooks/useReadonly'
|
|||
import { TLUiToolbarItem, useToolbarSchema } from '../../hooks/useToolbarSchema'
|
||||
import { TLUiToolItem } from '../../hooks/useTools'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { kbdStr } from '../../kbd-utils'
|
||||
import { MobileStylePanel } from '../MobileStylePanel'
|
||||
import { Button } from '../primitives/Button'
|
||||
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
|
||||
import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuRoot,
|
||||
DropdownMenuTrigger,
|
||||
} from '../primitives/DropdownMenu'
|
||||
import { kbdStr } from '../primitives/shared'
|
||||
TldrawUiDropdownMenuContent,
|
||||
TldrawUiDropdownMenuItem,
|
||||
TldrawUiDropdownMenuRoot,
|
||||
TldrawUiDropdownMenuTrigger,
|
||||
} from '../primitives/TldrawUiDropdownMenu'
|
||||
import { ToggleToolLockedButton } from './ToggleToolLockedButton'
|
||||
|
||||
/** @public */
|
||||
|
@ -143,18 +144,21 @@ export const DefaultToolbar = memo(function DefaultToolbar() {
|
|||
)}
|
||||
/>
|
||||
{/* The dropdown to select everything else */}
|
||||
<DropdownMenuRoot id="toolbar overflow" modal={false}>
|
||||
<DropdownMenuTrigger
|
||||
className="tlui-toolbar__overflow"
|
||||
icon="chevron-up"
|
||||
type="tool"
|
||||
data-testid="tools.more"
|
||||
title={msg('tool-panel.more')}
|
||||
/>
|
||||
<DropdownMenuContent side="top" align="center">
|
||||
<TldrawUiDropdownMenuRoot id="toolbar overflow" modal={false}>
|
||||
<TldrawUiDropdownMenuTrigger>
|
||||
<TldrawUiButton
|
||||
title={msg('tool-panel.more')}
|
||||
type="tool"
|
||||
className="tlui-toolbar__overflow"
|
||||
data-testid="tools.more"
|
||||
>
|
||||
<TldrawUiButtonIcon icon="chevron-up" />
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDropdownMenuTrigger>
|
||||
<TldrawUiDropdownMenuContent side="top" align="center">
|
||||
<OverflowToolsContent toolbarItems={itemsInDropdown} />
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenuRoot>
|
||||
</TldrawUiDropdownMenuContent>
|
||||
</TldrawUiDropdownMenuRoot>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
|
@ -180,18 +184,22 @@ const OverflowToolsContent = track(function OverflowToolsContent({
|
|||
<div className="tlui-buttons__grid">
|
||||
{toolbarItems.map(({ toolItem: { id, meta, kbd, label, onSelect, icon } }) => {
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
<TldrawUiDropdownMenuItem
|
||||
key={id}
|
||||
type="icon"
|
||||
className="tlui-button-grid__button"
|
||||
data-testid={`tools.more.${id}`}
|
||||
data-tool={id}
|
||||
data-geo={meta?.geo ?? ''}
|
||||
aria-label={label}
|
||||
onClick={() => onSelect('toolbar')}
|
||||
title={label ? `${msg(label)} ${kbd ? kbdStr(kbd) : ''}` : ''}
|
||||
icon={icon}
|
||||
/>
|
||||
>
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
className="tlui-button-grid__button"
|
||||
onClick={() => onSelect('toolbar')}
|
||||
data-testid={`tools.more.${id}`}
|
||||
title={label ? `${msg(label)} ${kbd ? kbdStr(kbd) : ''}` : ''}
|
||||
>
|
||||
<TldrawUiButtonIcon icon={icon} />
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDropdownMenuItem>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
@ -208,21 +216,22 @@ function ToolbarButton({
|
|||
isSelected: boolean
|
||||
}) {
|
||||
return (
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="tool"
|
||||
data-testid={`tools.${item.id}`}
|
||||
data-tool={item.id}
|
||||
data-geo={item.meta?.geo ?? ''}
|
||||
aria-label={item.label}
|
||||
title={title}
|
||||
icon={item.icon}
|
||||
data-state={isSelected ? 'selected' : undefined}
|
||||
onClick={() => item.onSelect('toolbar')}
|
||||
title={title}
|
||||
onTouchStart={(e) => {
|
||||
preventDefault(e)
|
||||
item.onSelect('toolbar')
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonIcon icon={item.icon} />
|
||||
</TldrawUiButton>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@ import classNames from 'classnames'
|
|||
import { PORTRAIT_BREAKPOINT } from '../../constants'
|
||||
import { useBreakpoint } from '../../context/breakpoints'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { Button } from '../primitives/Button'
|
||||
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
|
||||
|
||||
interface ToggleToolLockedButtonProps {
|
||||
activeToolId?: string
|
||||
|
@ -32,15 +33,15 @@ export function ToggleToolLockedButton({ activeToolId }: ToggleToolLockedButtonP
|
|||
if (!activeToolId || NOT_LOCKABLE_TOOLS.includes(activeToolId)) return null
|
||||
|
||||
return (
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="normal"
|
||||
title={msg('action.toggle-tool-lock')}
|
||||
className={classNames('tlui-toolbar__lock-button', {
|
||||
'tlui-toolbar__lock-button__mobile': breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM,
|
||||
})}
|
||||
icon={isToolLocked ? 'lock' : 'unlock'}
|
||||
onClick={() => editor.updateInstanceState({ isToolLocked: !isToolLocked })}
|
||||
smallIcon
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonIcon icon={isToolLocked ? 'lock' : 'unlock'} small />
|
||||
</TldrawUiButton>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@ import { PORTRAIT_BREAKPOINT } from '../../constants'
|
|||
import { useBreakpoint } from '../../context/breakpoints'
|
||||
import { useMenuIsOpen } from '../../hooks/useMenuIsOpen'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { TldrawUiMenuContextProvider } from '../menus/TldrawUiMenuContext'
|
||||
import { Button } from '../primitives/Button'
|
||||
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
|
||||
import { TldrawUiMenuContextProvider } from '../primitives/menus/TldrawUiMenuContext'
|
||||
import { DefaultZoomMenuContent } from './DefaultZoomMenuContent'
|
||||
|
||||
/** @public */
|
||||
|
@ -59,7 +59,7 @@ const ZoomTriggerButton = forwardRef<HTMLButtonElement, any>(
|
|||
}, [editor])
|
||||
|
||||
return (
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
ref={ref}
|
||||
{...props}
|
||||
type="icon"
|
||||
|
@ -76,7 +76,7 @@ const ZoomTriggerButton = forwardRef<HTMLButtonElement, any>(
|
|||
{breakpoint < PORTRAIT_BREAKPOINT.MOBILE ? null : (
|
||||
<span style={{ flexGrow: 0, textAlign: 'center' }}>{Math.floor(zoom * 100)}%</span>
|
||||
)}
|
||||
</Button>
|
||||
</TldrawUiButton>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useActions } from '../../context/actions'
|
||||
import { ZoomTo100MenuItem, ZoomToFitMenuItem, ZoomToSelectionMenuItem } from '../menu-items'
|
||||
import { TldrawUiMenuItem } from '../menus/TldrawUiMenuItem'
|
||||
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
|
||||
|
||||
/** @public */
|
||||
export function DefaultZoomMenuContent() {
|
||||
|
|
|
@ -21,10 +21,10 @@ import {
|
|||
useThreeStackableItems,
|
||||
useUnlockedSelectedShapesCount,
|
||||
} from '../hooks/menu-hooks'
|
||||
import { TldrawUiMenuCheckboxItem } from './menus/TldrawUiMenuCheckboxItem'
|
||||
import { TldrawUiMenuGroup } from './menus/TldrawUiMenuGroup'
|
||||
import { TldrawUiMenuItem } from './menus/TldrawUiMenuItem'
|
||||
import { TldrawUiMenuSubmenu } from './menus/TldrawUiMenuSubmenu'
|
||||
import { TldrawUiMenuCheckboxItem } from './primitives/menus/TldrawUiMenuCheckboxItem'
|
||||
import { TldrawUiMenuGroup } from './primitives/menus/TldrawUiMenuGroup'
|
||||
import { TldrawUiMenuItem } from './primitives/menus/TldrawUiMenuItem'
|
||||
import { TldrawUiMenuSubmenu } from './primitives/menus/TldrawUiMenuSubmenu'
|
||||
|
||||
/* -------------------- Selection ------------------- */
|
||||
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
import { useEditor } from '@tldraw/editor'
|
||||
import classnames from 'classnames'
|
||||
import * as React from 'react'
|
||||
import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { TLUiIconType } from '../../icon-types'
|
||||
import { Spinner } from '../Spinner'
|
||||
import { Icon } from './Icon'
|
||||
import { Kbd } from './Kbd'
|
||||
|
||||
/** @public */
|
||||
export interface TLUiButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
|
||||
loading?: boolean // TODO: loading spinner
|
||||
disabled?: boolean
|
||||
label?: TLUiTranslationKey | Exclude<string, TLUiTranslationKey>
|
||||
icon?: TLUiIconType | Exclude<string, TLUiIconType>
|
||||
spinner?: boolean
|
||||
iconLeft?: TLUiIconType | Exclude<string, TLUiIconType>
|
||||
smallIcon?: boolean
|
||||
kbd?: string
|
||||
isChecked?: boolean
|
||||
invertIcon?: boolean
|
||||
type: 'normal' | 'primary' | 'danger' | 'low' | 'icon' | 'tool' | 'menu' | 'help'
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const Button = React.forwardRef<HTMLButtonElement, TLUiButtonProps>(function Button(
|
||||
{
|
||||
label,
|
||||
icon,
|
||||
invertIcon,
|
||||
iconLeft,
|
||||
smallIcon,
|
||||
kbd,
|
||||
isChecked = false,
|
||||
type,
|
||||
children,
|
||||
spinner,
|
||||
disabled,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) {
|
||||
const msg = useTranslation()
|
||||
const labelStr = label ? msg(label) : ''
|
||||
const editor = useEditor()
|
||||
|
||||
// If the button is getting disabled while it's focused, move focus to the editor
|
||||
// so that the user can continue using keyboard shortcuts
|
||||
const current = (ref as React.MutableRefObject<HTMLButtonElement | null>)?.current
|
||||
if (disabled && current === document.activeElement) {
|
||||
editor.getContainer().focus()
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
draggable={false}
|
||||
type="button"
|
||||
disabled={disabled}
|
||||
{...props}
|
||||
title={props.title ?? labelStr}
|
||||
className={classnames('tlui-button', `tlui-button__${type}`, props.className)}
|
||||
>
|
||||
{iconLeft && <Icon icon={iconLeft} className="tlui-button__icon-left" small />}
|
||||
{children}
|
||||
{label && (
|
||||
<span className="tlui-button__label" draggable={false}>
|
||||
{labelStr}
|
||||
{isChecked && <Icon icon="check" />}
|
||||
</span>
|
||||
)}
|
||||
{kbd && <Kbd>{kbd}</Kbd>}
|
||||
{icon && !spinner && (
|
||||
<Icon icon={icon} small={!!label || smallIcon} invertIcon={invertIcon} />
|
||||
)}
|
||||
{spinner && <Spinner />}
|
||||
</button>
|
||||
)
|
||||
})
|
|
@ -0,0 +1,36 @@
|
|||
import { useEditor } from '@tldraw/editor'
|
||||
import classnames from 'classnames'
|
||||
import * as React from 'react'
|
||||
|
||||
/** @public */
|
||||
export interface TLUiButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
|
||||
disabled?: boolean
|
||||
type: 'normal' | 'primary' | 'danger' | 'low' | 'icon' | 'tool' | 'menu' | 'help'
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const TldrawUiButton = React.forwardRef<HTMLButtonElement, TLUiButtonProps>(
|
||||
function TldrawUiButton({ children, disabled, type, ...props }, ref) {
|
||||
const editor = useEditor()
|
||||
|
||||
// If the button is getting disabled while it's focused, move focus to the editor
|
||||
// so that the user can continue using keyboard shortcuts
|
||||
const current = (ref as React.MutableRefObject<HTMLButtonElement | null>)?.current
|
||||
if (disabled && current === document.activeElement) {
|
||||
editor.getContainer().focus()
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
type="button"
|
||||
draggable={false}
|
||||
disabled={disabled}
|
||||
{...props}
|
||||
className={classnames('tlui-button', `tlui-button__${type}`, props.className)}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
)
|
|
@ -0,0 +1,11 @@
|
|||
import { TldrawUiIcon } from '../TldrawUiIcon'
|
||||
|
||||
/** @public */
|
||||
export type TLUiButtonCheckProps = { checked: boolean }
|
||||
|
||||
/** @public */
|
||||
export function TldrawUiButtonCheck({ checked }: TLUiButtonCheckProps) {
|
||||
return (
|
||||
<TldrawUiIcon icon={checked ? 'check' : 'checkbox-empty'} className="tlui-button__icon" small />
|
||||
)
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import { TldrawUiIcon } from '../TldrawUiIcon'
|
||||
|
||||
/** @public */
|
||||
export type TLUiButtonIconProps = {
|
||||
icon: string
|
||||
small?: boolean
|
||||
invertIcon?: boolean
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function TldrawUiButtonIcon({ icon, small, invertIcon }: TLUiButtonIconProps) {
|
||||
return (
|
||||
<TldrawUiIcon className="tlui-button__icon" icon={icon} small={small} invertIcon={invertIcon} />
|
||||
)
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
/** @public */
|
||||
export type TLUiButtonLabelProps = { children?: any }
|
||||
|
||||
/** @public */
|
||||
export function TldrawUiButtonLabel({ children }: TLUiButtonLabelProps) {
|
||||
return <span className="tlui-button__label">{children}</span>
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import { Spinner } from '../../Spinner'
|
||||
|
||||
/** @public */
|
||||
export function TldrawUiButtonSpinner() {
|
||||
return <Spinner />
|
||||
}
|
|
@ -1,165 +0,0 @@
|
|||
import { stopEventPropagation, useEditor } from '@tldraw/editor'
|
||||
import classNames from 'classnames'
|
||||
import * as React from 'react'
|
||||
import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { TLUiIconType } from '../../icon-types'
|
||||
import { Icon } from './Icon'
|
||||
|
||||
/** @public */
|
||||
export interface TLUiInputProps {
|
||||
disabled?: boolean
|
||||
label?: TLUiTranslationKey | Exclude<string, TLUiTranslationKey>
|
||||
icon?: TLUiIconType | Exclude<string, TLUiIconType>
|
||||
iconLeft?: TLUiIconType | Exclude<string, TLUiIconType>
|
||||
autofocus?: boolean
|
||||
autoselect?: boolean
|
||||
children?: any
|
||||
defaultValue?: string
|
||||
placeholder?: string
|
||||
onComplete?: (value: string) => void
|
||||
onValueChange?: (value: string) => void
|
||||
onCancel?: (value: string) => void
|
||||
onBlur?: (value: string) => void
|
||||
className?: string
|
||||
/**
|
||||
* Usually on iOS when you focus an input, the browser will adjust the viewport to bring the input
|
||||
* into view. Sometimes this doesn't work properly though - for example, if the input is newly
|
||||
* created, iOS seems to have a hard time adjusting the viewport for it. This prop allows you to
|
||||
* opt-in to some extra code to manually bring the input into view when the visual viewport of the
|
||||
* browser changes, but we don't want to use it everywhere because generally the native behavior
|
||||
* looks nicer in scenarios where it's sufficient.
|
||||
*/
|
||||
shouldManuallyMaintainScrollPositionWhenFocused?: boolean
|
||||
value?: string
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const Input = React.forwardRef<HTMLInputElement, TLUiInputProps>(function Input(
|
||||
{
|
||||
className,
|
||||
label,
|
||||
icon,
|
||||
iconLeft,
|
||||
autoselect = false,
|
||||
autofocus = false,
|
||||
defaultValue,
|
||||
placeholder,
|
||||
onComplete,
|
||||
onValueChange,
|
||||
onCancel,
|
||||
onBlur,
|
||||
shouldManuallyMaintainScrollPositionWhenFocused = false,
|
||||
children,
|
||||
value,
|
||||
},
|
||||
ref
|
||||
) {
|
||||
const editor = useEditor()
|
||||
const rInputRef = React.useRef<HTMLInputElement>(null)
|
||||
|
||||
// combine rInputRef and ref
|
||||
React.useImperativeHandle(ref, () => rInputRef.current as HTMLInputElement)
|
||||
|
||||
const msg = useTranslation()
|
||||
const rInitialValue = React.useRef<string>(defaultValue ?? '')
|
||||
const rCurrentValue = React.useRef<string>(defaultValue ?? '')
|
||||
|
||||
const [isFocused, setIsFocused] = React.useState(false)
|
||||
const handleFocus = React.useCallback(
|
||||
(e: React.FocusEvent<HTMLInputElement>) => {
|
||||
setIsFocused(true)
|
||||
const elm = e.currentTarget as HTMLInputElement
|
||||
rCurrentValue.current = elm.value
|
||||
requestAnimationFrame(() => {
|
||||
if (autoselect) {
|
||||
elm.select()
|
||||
}
|
||||
})
|
||||
},
|
||||
[autoselect]
|
||||
)
|
||||
|
||||
const handleChange = React.useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.currentTarget.value
|
||||
rCurrentValue.current = value
|
||||
onValueChange?.(value)
|
||||
},
|
||||
[onValueChange]
|
||||
)
|
||||
|
||||
const handleKeyUp = React.useCallback(
|
||||
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
switch (e.key) {
|
||||
case 'Enter': {
|
||||
e.currentTarget.blur()
|
||||
stopEventPropagation(e)
|
||||
onComplete?.(e.currentTarget.value)
|
||||
break
|
||||
}
|
||||
case 'Escape': {
|
||||
e.currentTarget.value = rInitialValue.current
|
||||
e.currentTarget.blur()
|
||||
stopEventPropagation(e)
|
||||
onCancel?.(e.currentTarget.value)
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
[onComplete, onCancel]
|
||||
)
|
||||
|
||||
const handleBlur = React.useCallback(
|
||||
(e: React.FocusEvent<HTMLInputElement>) => {
|
||||
setIsFocused(false)
|
||||
const value = e.currentTarget.value
|
||||
onBlur?.(value)
|
||||
},
|
||||
[onBlur]
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!editor.environment.isIos) return
|
||||
|
||||
const visualViewport = window.visualViewport
|
||||
if (isFocused && shouldManuallyMaintainScrollPositionWhenFocused && visualViewport) {
|
||||
const onViewportChange = () => {
|
||||
rInputRef.current?.scrollIntoView({ block: 'center' })
|
||||
}
|
||||
visualViewport.addEventListener('resize', onViewportChange)
|
||||
visualViewport.addEventListener('scroll', onViewportChange)
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
rInputRef.current?.scrollIntoView({ block: 'center' })
|
||||
})
|
||||
|
||||
return () => {
|
||||
visualViewport.removeEventListener('resize', onViewportChange)
|
||||
visualViewport.removeEventListener('scroll', onViewportChange)
|
||||
}
|
||||
}
|
||||
}, [editor, isFocused, shouldManuallyMaintainScrollPositionWhenFocused])
|
||||
|
||||
return (
|
||||
<div draggable={false} className="tlui-input__wrapper">
|
||||
{children}
|
||||
{label && <label>{msg(label)}</label>}
|
||||
{iconLeft && <Icon icon={iconLeft} className="tlui-icon-left" small />}
|
||||
<input
|
||||
ref={rInputRef}
|
||||
className={classNames('tlui-input', className)}
|
||||
type="text"
|
||||
defaultValue={defaultValue}
|
||||
onKeyUp={handleKeyUp}
|
||||
onChange={handleChange}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
autoFocus={autofocus}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
/>
|
||||
{icon && <Icon icon={icon} small={!!label} />}
|
||||
</div>
|
||||
)
|
||||
})
|
|
@ -8,16 +8,15 @@ import {
|
|||
useValue,
|
||||
} from '@tldraw/editor'
|
||||
import classNames from 'classnames'
|
||||
import * as React from 'react'
|
||||
import { useRef } from 'react'
|
||||
import { memo, useMemo, useRef } from 'react'
|
||||
import { StyleValuesForUi } from '../../../styles'
|
||||
import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { TLUiIconType } from '../../icon-types'
|
||||
import { StyleValuesForUi } from '../StylePanel/styles'
|
||||
import { Button } from './Button'
|
||||
import { TldrawUiButton } from './Button/TldrawUiButton'
|
||||
import { TldrawUiButtonIcon } from './Button/TldrawUiButtonIcon'
|
||||
|
||||
/** @internal */
|
||||
export interface ButtonPickerProps<T extends string> {
|
||||
/** @public */
|
||||
export interface TLUiButtonPickerProps<T extends string> {
|
||||
title: string
|
||||
uiType: string
|
||||
style: StyleProp<T>
|
||||
|
@ -26,7 +25,10 @@ export interface ButtonPickerProps<T extends string> {
|
|||
onValueChange: (style: StyleProp<T>, value: T, squashing: boolean) => void
|
||||
}
|
||||
|
||||
function _ButtonPicker<T extends string>(props: ButtonPickerProps<T>) {
|
||||
/** @public */
|
||||
export const TldrawUiButtonPicker = memo(function TldrawUiButtonPicker<T extends string>(
|
||||
props: TLUiButtonPickerProps<T>
|
||||
) {
|
||||
const {
|
||||
uiType,
|
||||
items,
|
||||
|
@ -46,7 +48,7 @@ function _ButtonPicker<T extends string>(props: ButtonPickerProps<T>) {
|
|||
handleButtonPointerDown,
|
||||
handleButtonPointerEnter,
|
||||
handleButtonPointerUp,
|
||||
} = React.useMemo(() => {
|
||||
} = useMemo(() => {
|
||||
const handlePointerUp = () => {
|
||||
rPointing.current = false
|
||||
window.removeEventListener('pointerup', handlePointerUp)
|
||||
|
@ -99,7 +101,7 @@ function _ButtonPicker<T extends string>(props: ButtonPickerProps<T>) {
|
|||
return (
|
||||
<div className={classNames('tlui-buttons__grid')}>
|
||||
{items.map((item) => (
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
key={item.value}
|
||||
data-id={item.value}
|
||||
|
@ -117,12 +119,10 @@ function _ButtonPicker<T extends string>(props: ButtonPickerProps<T>) {
|
|||
onPointerDown={handleButtonPointerDown}
|
||||
onPointerUp={handleButtonPointerUp}
|
||||
onClick={handleButtonClick}
|
||||
icon={item.icon as TLUiIconType}
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonIcon icon={item.icon} />
|
||||
</TldrawUiButton>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export const ButtonPicker = React.memo(_ButtonPicker) as typeof _ButtonPicker
|
||||
})
|
|
@ -1,7 +1,7 @@
|
|||
import * as _Dialog from '@radix-ui/react-dialog'
|
||||
import classNames from 'classnames'
|
||||
import { Button } from './Button'
|
||||
import { Icon } from './Icon'
|
||||
import { TldrawUiButton } from './Button/TldrawUiButton'
|
||||
import { TldrawUiButtonIcon } from './Button/TldrawUiButtonIcon'
|
||||
|
||||
/** @public */
|
||||
export type TLUiDialogHeaderProps = {
|
||||
|
@ -10,7 +10,7 @@ export type TLUiDialogHeaderProps = {
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export function DialogHeader({ className, children }: TLUiDialogHeaderProps) {
|
||||
export function TldrawUiDialogHeader({ className, children }: TLUiDialogHeaderProps) {
|
||||
return <div className={classNames('tlui-dialog__header', className)}>{children}</div>
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ export type TLUiDialogTitleProps = {
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export function DialogTitle({ className, children }: TLUiDialogTitleProps) {
|
||||
export function TldrawUiDialogTitle({ className, children }: TLUiDialogTitleProps) {
|
||||
return (
|
||||
<_Dialog.DialogTitle dir="ltr" className={classNames('tlui-dialog__header__title', className)}>
|
||||
{children}
|
||||
|
@ -30,17 +30,17 @@ export function DialogTitle({ className, children }: TLUiDialogTitleProps) {
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export function DialogCloseButton() {
|
||||
export function TldrawUiDialogCloseButton() {
|
||||
return (
|
||||
<div className="tlui-dialog__header__close">
|
||||
<_Dialog.DialogClose data-testid="dialog.close" dir="ltr" asChild>
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="icon"
|
||||
aria-label="Close"
|
||||
onTouchEnd={(e) => (e.target as HTMLButtonElement).click()}
|
||||
>
|
||||
<Icon small icon="cross-2" />
|
||||
</Button>
|
||||
<TldrawUiButtonIcon small icon="cross-2" />
|
||||
</TldrawUiButton>
|
||||
</_Dialog.DialogClose>
|
||||
</div>
|
||||
)
|
||||
|
@ -54,7 +54,7 @@ export type TLUiDialogBodyProps = {
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export function DialogBody({ className, children, style }: TLUiDialogBodyProps) {
|
||||
export function TldrawUiDialogBody({ className, children, style }: TLUiDialogBodyProps) {
|
||||
return (
|
||||
<div className={classNames('tlui-dialog__body', className)} style={style}>
|
||||
{children}
|
||||
|
@ -69,6 +69,6 @@ export type TLUiDialogFooterProps = {
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export function DialogFooter({ className, children }: TLUiDialogFooterProps) {
|
||||
export function TldrawUiDialogFooter({ className, children }: TLUiDialogFooterProps) {
|
||||
return <div className={classNames('tlui-dialog__footer', className)}>{children}</div>
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
import * as _DropdownMenu from '@radix-ui/react-dropdown-menu'
|
||||
import { preventDefault, useContainer } from '@tldraw/editor'
|
||||
import { useMenuIsOpen } from '../../hooks/useMenuIsOpen'
|
||||
import { Button, TLUiButtonProps } from './Button'
|
||||
import { Icon } from './Icon'
|
||||
import { TldrawUiButton } from './Button/TldrawUiButton'
|
||||
import { TldrawUiButtonIcon } from './Button/TldrawUiButtonIcon'
|
||||
import { TldrawUiButtonLabel } from './Button/TldrawUiButtonLabel'
|
||||
import { TldrawUiIcon } from './TldrawUiIcon'
|
||||
|
||||
/** @public */
|
||||
export type TLUiDropdownMenuRootProps = {
|
||||
|
@ -13,7 +15,7 @@ export type TLUiDropdownMenuRootProps = {
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export function DropdownMenuRoot({
|
||||
export function TldrawUiDropdownMenuRoot({
|
||||
id,
|
||||
children,
|
||||
modal = false,
|
||||
|
@ -34,20 +36,21 @@ export function DropdownMenuRoot({
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export interface TLUiDropdownMenuTriggerProps extends TLUiButtonProps {
|
||||
export interface TLUiDropdownMenuTriggerProps {
|
||||
children?: any
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function DropdownMenuTrigger({ children, ...rest }: TLUiDropdownMenuTriggerProps) {
|
||||
export function TldrawUiDropdownMenuTrigger({ children, ...rest }: TLUiDropdownMenuTriggerProps) {
|
||||
return (
|
||||
<_DropdownMenu.Trigger
|
||||
dir="ltr"
|
||||
asChild
|
||||
// Firefox fix: Stop the dropdown immediately closing after touch
|
||||
onTouchEnd={(e) => preventDefault(e)}
|
||||
{...rest}
|
||||
>
|
||||
<Button {...rest}>{children}</Button>
|
||||
{children}
|
||||
</_DropdownMenu.Trigger>
|
||||
)
|
||||
}
|
||||
|
@ -63,7 +66,7 @@ export type TLUiDropdownMenuContentProps = {
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export function DropdownMenuContent({
|
||||
export function TldrawUiDropdownMenuContent({
|
||||
side = 'bottom',
|
||||
align = 'start',
|
||||
sideOffset = 8,
|
||||
|
@ -92,7 +95,7 @@ export function DropdownMenuContent({
|
|||
export type TLUiDropdownMenuSubProps = { id: string; children: any }
|
||||
|
||||
/** @public */
|
||||
export function DropdownMenuSub({ id, children }: TLUiDropdownMenuSubProps) {
|
||||
export function TldrawUiDropdownMenuSub({ id, children }: TLUiDropdownMenuSubProps) {
|
||||
const [open, onOpenChange] = useMenuIsOpen(id)
|
||||
|
||||
return (
|
||||
|
@ -111,21 +114,22 @@ export type TLUiDropdownMenuSubTriggerProps = {
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export function DropdownMenuSubTrigger({
|
||||
export function TldrawUiDropdownMenuSubTrigger({
|
||||
label,
|
||||
title,
|
||||
disabled,
|
||||
}: TLUiDropdownMenuSubTriggerProps) {
|
||||
return (
|
||||
<_DropdownMenu.SubTrigger dir="ltr" asChild>
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
type="menu"
|
||||
className="tlui-menu__submenu__trigger"
|
||||
disabled={disabled}
|
||||
title={title}
|
||||
label={label}
|
||||
icon="chevron-right"
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonLabel>{label}</TldrawUiButtonLabel>
|
||||
<TldrawUiButtonIcon icon="chevron-right" small />
|
||||
</TldrawUiButton>
|
||||
</_DropdownMenu.SubTrigger>
|
||||
)
|
||||
}
|
||||
|
@ -139,7 +143,7 @@ export type TLUiDropdownMenuSubContentProps = {
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export function DropdownMenuSubContent({
|
||||
export function TldrawUiDropdownMenuSubContent({
|
||||
alignOffset = -1,
|
||||
sideOffset = -4,
|
||||
children,
|
||||
|
@ -166,7 +170,10 @@ export type TLUiDropdownMenuGroupProps = {
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export function DropdownMenuGroup({ children, size = 'medium' }: TLUiDropdownMenuGroupProps) {
|
||||
export function TldrawUiDropdownMenuGroup({
|
||||
children,
|
||||
size = 'medium',
|
||||
}: TLUiDropdownMenuGroupProps) {
|
||||
return (
|
||||
<_DropdownMenu.Group dir="ltr" className="tlui-menu__group" data-size={size}>
|
||||
{children}
|
||||
|
@ -175,28 +182,25 @@ export function DropdownMenuGroup({ children, size = 'medium' }: TLUiDropdownMen
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export function DropdownMenuIndicator() {
|
||||
export function TldrawUiDropdownMenuIndicator() {
|
||||
return (
|
||||
<_DropdownMenu.ItemIndicator dir="ltr" asChild>
|
||||
<Icon icon="check" />
|
||||
<TldrawUiIcon icon="check" />
|
||||
</_DropdownMenu.ItemIndicator>
|
||||
)
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface TLUiDropdownMenuItemProps extends TLUiButtonProps {
|
||||
export interface TLUiDropdownMenuItemProps {
|
||||
noClose?: boolean
|
||||
children: any
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function DropdownMenuItem({ noClose, ...props }: TLUiDropdownMenuItemProps) {
|
||||
export function TldrawUiDropdownMenuItem({ noClose, children }: TLUiDropdownMenuItemProps) {
|
||||
return (
|
||||
<_DropdownMenu.Item
|
||||
dir="ltr"
|
||||
asChild
|
||||
onClick={noClose || props.isChecked !== undefined ? preventDefault : undefined}
|
||||
>
|
||||
<Button {...props} />
|
||||
<_DropdownMenu.Item dir="ltr" asChild onClick={noClose ? preventDefault : undefined}>
|
||||
{children}
|
||||
</_DropdownMenu.Item>
|
||||
)
|
||||
}
|
||||
|
@ -211,7 +215,7 @@ export interface TLUiDropdownMenuCheckboxItemProps {
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export function DropdownMenuCheckboxItem({
|
||||
export function TldrawUiDropdownMenuCheckboxItem({
|
||||
children,
|
||||
onSelect,
|
||||
...rest
|
||||
|
@ -228,38 +232,7 @@ export function DropdownMenuCheckboxItem({
|
|||
>
|
||||
<div className="tlui-button__checkbox__indicator">
|
||||
<_DropdownMenu.ItemIndicator dir="ltr">
|
||||
<Icon icon="check" small />
|
||||
</_DropdownMenu.ItemIndicator>
|
||||
</div>
|
||||
{children}
|
||||
</_DropdownMenu.CheckboxItem>
|
||||
)
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface TLUiDropdownMenuRadioItemProps {
|
||||
checked?: boolean
|
||||
onSelect?: (e: Event) => void
|
||||
disabled?: boolean
|
||||
title: string
|
||||
children: any
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function DropdownMenuRadioItem({ children, ...rest }: TLUiDropdownMenuRadioItemProps) {
|
||||
return (
|
||||
<_DropdownMenu.CheckboxItem
|
||||
dir="ltr"
|
||||
className="tlui-button tlui-button__menu tlui-button__checkbox"
|
||||
{...rest}
|
||||
onSelect={(e) => {
|
||||
preventDefault(e)
|
||||
rest.onSelect?.(e)
|
||||
}}
|
||||
>
|
||||
<div className="tlui-button__checkbox__indicator">
|
||||
<_DropdownMenu.ItemIndicator dir="ltr">
|
||||
<Icon icon="check" small />
|
||||
<TldrawUiIcon icon="check" small />
|
||||
</_DropdownMenu.ItemIndicator>
|
||||
</div>
|
||||
{children}
|
|
@ -14,7 +14,7 @@ export interface TLUiIconProps extends React.HTMLProps<HTMLDivElement> {
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export const Icon = memo(function Icon({
|
||||
export const TldrawUiIcon = memo(function TldrawUi({
|
||||
small,
|
||||
invertIcon,
|
||||
icon,
|
|
@ -0,0 +1,167 @@
|
|||
import { stopEventPropagation, useEditor } from '@tldraw/editor'
|
||||
import classNames from 'classnames'
|
||||
import * as React from 'react'
|
||||
import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { TLUiIconType } from '../../icon-types'
|
||||
import { TldrawUiIcon } from './TldrawUiIcon'
|
||||
|
||||
/** @public */
|
||||
export interface TLUiInputProps {
|
||||
disabled?: boolean
|
||||
label?: TLUiTranslationKey | Exclude<string, TLUiTranslationKey>
|
||||
icon?: TLUiIconType | Exclude<string, TLUiIconType>
|
||||
iconLeft?: TLUiIconType | Exclude<string, TLUiIconType>
|
||||
autofocus?: boolean
|
||||
autoselect?: boolean
|
||||
children?: any
|
||||
defaultValue?: string
|
||||
placeholder?: string
|
||||
onComplete?: (value: string) => void
|
||||
onValueChange?: (value: string) => void
|
||||
onCancel?: (value: string) => void
|
||||
onBlur?: (value: string) => void
|
||||
className?: string
|
||||
/**
|
||||
* Usually on iOS when you focus an input, the browser will adjust the viewport to bring the input
|
||||
* into view. Sometimes this doesn't work properly though - for example, if the input is newly
|
||||
* created, iOS seems to have a hard time adjusting the viewport for it. This prop allows you to
|
||||
* opt-in to some extra code to manually bring the input into view when the visual viewport of the
|
||||
* browser changes, but we don't want to use it everywhere because generally the native behavior
|
||||
* looks nicer in scenarios where it's sufficient.
|
||||
*/
|
||||
shouldManuallyMaintainScrollPositionWhenFocused?: boolean
|
||||
value?: string
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const TldrawUiInput = React.forwardRef<HTMLInputElement, TLUiInputProps>(
|
||||
function TldrawUiInput(
|
||||
{
|
||||
className,
|
||||
label,
|
||||
icon,
|
||||
iconLeft,
|
||||
autoselect = false,
|
||||
autofocus = false,
|
||||
defaultValue,
|
||||
placeholder,
|
||||
onComplete,
|
||||
onValueChange,
|
||||
onCancel,
|
||||
onBlur,
|
||||
shouldManuallyMaintainScrollPositionWhenFocused = false,
|
||||
children,
|
||||
value,
|
||||
},
|
||||
ref
|
||||
) {
|
||||
const editor = useEditor()
|
||||
const rInputRef = React.useRef<HTMLInputElement>(null)
|
||||
|
||||
// combine rInputRef and ref
|
||||
React.useImperativeHandle(ref, () => rInputRef.current as HTMLInputElement)
|
||||
|
||||
const msg = useTranslation()
|
||||
const rInitialValue = React.useRef<string>(defaultValue ?? '')
|
||||
const rCurrentValue = React.useRef<string>(defaultValue ?? '')
|
||||
|
||||
const [isFocused, setIsFocused] = React.useState(false)
|
||||
const handleFocus = React.useCallback(
|
||||
(e: React.FocusEvent<HTMLInputElement>) => {
|
||||
setIsFocused(true)
|
||||
const elm = e.currentTarget as HTMLInputElement
|
||||
rCurrentValue.current = elm.value
|
||||
requestAnimationFrame(() => {
|
||||
if (autoselect) {
|
||||
elm.select()
|
||||
}
|
||||
})
|
||||
},
|
||||
[autoselect]
|
||||
)
|
||||
|
||||
const handleChange = React.useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.currentTarget.value
|
||||
rCurrentValue.current = value
|
||||
onValueChange?.(value)
|
||||
},
|
||||
[onValueChange]
|
||||
)
|
||||
|
||||
const handleKeyUp = React.useCallback(
|
||||
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
switch (e.key) {
|
||||
case 'Enter': {
|
||||
e.currentTarget.blur()
|
||||
stopEventPropagation(e)
|
||||
onComplete?.(e.currentTarget.value)
|
||||
break
|
||||
}
|
||||
case 'Escape': {
|
||||
e.currentTarget.value = rInitialValue.current
|
||||
e.currentTarget.blur()
|
||||
stopEventPropagation(e)
|
||||
onCancel?.(e.currentTarget.value)
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
[onComplete, onCancel]
|
||||
)
|
||||
|
||||
const handleBlur = React.useCallback(
|
||||
(e: React.FocusEvent<HTMLInputElement>) => {
|
||||
setIsFocused(false)
|
||||
const value = e.currentTarget.value
|
||||
onBlur?.(value)
|
||||
},
|
||||
[onBlur]
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!editor.environment.isIos) return
|
||||
|
||||
const visualViewport = window.visualViewport
|
||||
if (isFocused && shouldManuallyMaintainScrollPositionWhenFocused && visualViewport) {
|
||||
const onViewportChange = () => {
|
||||
rInputRef.current?.scrollIntoView({ block: 'center' })
|
||||
}
|
||||
visualViewport.addEventListener('resize', onViewportChange)
|
||||
visualViewport.addEventListener('scroll', onViewportChange)
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
rInputRef.current?.scrollIntoView({ block: 'center' })
|
||||
})
|
||||
|
||||
return () => {
|
||||
visualViewport.removeEventListener('resize', onViewportChange)
|
||||
visualViewport.removeEventListener('scroll', onViewportChange)
|
||||
}
|
||||
}
|
||||
}, [editor, isFocused, shouldManuallyMaintainScrollPositionWhenFocused])
|
||||
|
||||
return (
|
||||
<div draggable={false} className="tlui-input__wrapper">
|
||||
{children}
|
||||
{label && <label>{msg(label)}</label>}
|
||||
{iconLeft && <TldrawUiIcon icon={iconLeft} className="tlui-icon-left" small />}
|
||||
<input
|
||||
ref={rInputRef}
|
||||
className={classNames('tlui-input', className)}
|
||||
type="text"
|
||||
defaultValue={defaultValue}
|
||||
onKeyUp={handleKeyUp}
|
||||
onChange={handleChange}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
autoFocus={autofocus}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
/>
|
||||
{icon && <TldrawUiIcon icon={icon} small={!!label} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
|
@ -1,14 +1,14 @@
|
|||
import { PORTRAIT_BREAKPOINT } from '../../constants'
|
||||
import { useBreakpoint } from '../../context/breakpoints'
|
||||
import { kbd } from './shared'
|
||||
import { kbd } from '../../kbd-utils'
|
||||
|
||||
/** @internal */
|
||||
export interface KbdProps {
|
||||
/** @public */
|
||||
export interface TLUiKbdProps {
|
||||
children: string
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function Kbd({ children }: KbdProps) {
|
||||
/** @public */
|
||||
export function TldrawUiKbd({ children }: TLUiKbdProps) {
|
||||
const breakpoint = useBreakpoint()
|
||||
if (breakpoint < PORTRAIT_BREAKPOINT.MOBILE) return null
|
||||
return (
|
|
@ -2,7 +2,6 @@ import * as PopoverPrimitive from '@radix-ui/react-popover'
|
|||
import { useContainer } from '@tldraw/editor'
|
||||
import React from 'react'
|
||||
import { useMenuIsOpen } from '../../hooks/useMenuIsOpen'
|
||||
import { Button, TLUiButtonProps } from './Button'
|
||||
|
||||
/** @public */
|
||||
export type TLUiPopoverProps = {
|
||||
|
@ -13,7 +12,7 @@ export type TLUiPopoverProps = {
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export function Popover({ id, children, onOpenChange, open }: TLUiPopoverProps) {
|
||||
export function TldrawUiPopover({ id, children, onOpenChange, open }: TLUiPopoverProps) {
|
||||
const [isOpen, handleOpenChange] = useMenuIsOpen(id, onOpenChange)
|
||||
|
||||
return (
|
||||
|
@ -27,15 +26,15 @@ export function Popover({ id, children, onOpenChange, open }: TLUiPopoverProps)
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export interface TLUiPopoverTriggerProps extends TLUiButtonProps {
|
||||
export interface TLUiPopoverTriggerProps {
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function PopoverTrigger({ children, ...rest }: TLUiPopoverTriggerProps) {
|
||||
export function TldrawUiPopoverTrigger({ children }: TLUiPopoverTriggerProps) {
|
||||
return (
|
||||
<PopoverPrimitive.Trigger asChild dir="ltr">
|
||||
<Button {...rest}>{children}</Button>
|
||||
{children}
|
||||
</PopoverPrimitive.Trigger>
|
||||
)
|
||||
}
|
||||
|
@ -50,7 +49,7 @@ export type TLUiPopoverContentProps = {
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export function PopoverContent({
|
||||
export function TldrawUiPopoverContent({
|
||||
side,
|
||||
children,
|
||||
align = 'center',
|
|
@ -5,7 +5,7 @@ import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKe
|
|||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
|
||||
/** @internal */
|
||||
export interface SliderProps {
|
||||
export interface TLUiSliderProps {
|
||||
steps: number
|
||||
value: number | null
|
||||
label: string
|
||||
|
@ -15,7 +15,7 @@ export interface SliderProps {
|
|||
}
|
||||
|
||||
/** @internal */
|
||||
export const Slider = memo(function Slider(props: SliderProps) {
|
||||
export const TldrawUiSlider = memo(function Slider(props: TLUiSliderProps) {
|
||||
const { title, steps, value, label, onValueChange } = props
|
||||
const editor = useEditor()
|
||||
const msg = useTranslation()
|
|
@ -1,13 +1,13 @@
|
|||
import * as _ContextMenu from '@radix-ui/react-context-menu'
|
||||
import * as _DropdownMenu from '@radix-ui/react-dropdown-menu'
|
||||
import { preventDefault } from '@tldraw/editor'
|
||||
import { unwrapLabel } from '../../context/actions'
|
||||
import { TLUiEventSource } from '../../context/events'
|
||||
import { useReadonly } from '../../hooks/useReadonly'
|
||||
import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { Icon } from '../primitives/Icon'
|
||||
import { Kbd } from '../primitives/Kbd'
|
||||
import { unwrapLabel } from '../../../context/actions'
|
||||
import { TLUiEventSource } from '../../../context/events'
|
||||
import { useReadonly } from '../../../hooks/useReadonly'
|
||||
import { TLUiTranslationKey } from '../../../hooks/useTranslation/TLUiTranslationKey'
|
||||
import { useTranslation } from '../../../hooks/useTranslation/useTranslation'
|
||||
import { TldrawUiIcon } from '../TldrawUiIcon'
|
||||
import { TldrawUiKbd } from '../TldrawUiKbd'
|
||||
import { useTldrawUiMenuContext } from './TldrawUiMenuContext'
|
||||
|
||||
/** @public */
|
||||
|
@ -63,13 +63,13 @@ export function TldrawUiMenuCheckboxItem<
|
|||
disabled={disabled}
|
||||
checked={checked}
|
||||
>
|
||||
<Icon small icon={checked ? 'check' : 'checkbox-empty'} />
|
||||
<TldrawUiIcon small icon={checked ? 'check' : 'checkbox-empty'} />
|
||||
{labelStr && (
|
||||
<span className="tlui-button__label" draggable={false}>
|
||||
{labelStr}
|
||||
</span>
|
||||
)}
|
||||
{kbd && <Kbd>{kbd}</Kbd>}
|
||||
{kbd && <TldrawUiKbd>{kbd}</TldrawUiKbd>}
|
||||
</_DropdownMenu.CheckboxItem>
|
||||
)
|
||||
}
|
||||
|
@ -87,13 +87,13 @@ export function TldrawUiMenuCheckboxItem<
|
|||
disabled={disabled}
|
||||
checked={checked}
|
||||
>
|
||||
<Icon small icon={checked ? 'check' : 'checkbox-empty'} />
|
||||
<TldrawUiIcon small icon={checked ? 'check' : 'checkbox-empty'} />
|
||||
{labelStr && (
|
||||
<span className="tlui-button__label" draggable={false}>
|
||||
{labelStr}
|
||||
</span>
|
||||
)}
|
||||
{kbd && <Kbd>{kbd}</Kbd>}
|
||||
{kbd && <TldrawUiKbd>{kbd}</TldrawUiKbd>}
|
||||
</_ContextMenu.CheckboxItem>
|
||||
)
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { createContext, useContext } from 'react'
|
||||
import { TLUiEventSource } from '../../context/events'
|
||||
import { TLUiEventSource } from '../../../context/events'
|
||||
|
||||
/** @public */
|
||||
export type TldrawUiMenuContextType =
|
|
@ -1,8 +1,8 @@
|
|||
import { ContextMenuGroup } from '@radix-ui/react-context-menu'
|
||||
import { unwrapLabel } from '../../context/actions'
|
||||
import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { DropdownMenuGroup } from '../primitives/DropdownMenu'
|
||||
import { unwrapLabel } from '../../../context/actions'
|
||||
import { TLUiTranslationKey } from '../../../hooks/useTranslation/TLUiTranslationKey'
|
||||
import { useTranslation } from '../../../hooks/useTranslation/useTranslation'
|
||||
import { TldrawUiDropdownMenuGroup } from '../TldrawUiDropdownMenu'
|
||||
import { useTldrawUiMenuContext } from './TldrawUiMenuContext'
|
||||
|
||||
/** @public */
|
||||
|
@ -33,9 +33,12 @@ export function TldrawUiMenuGroup({ id, label, small = false, children }: TLUiMe
|
|||
}
|
||||
case 'menu': {
|
||||
return (
|
||||
<DropdownMenuGroup data-testid={`${sourceId}-group.${id}`} size={small ? 'tiny' : 'medium'}>
|
||||
<TldrawUiDropdownMenuGroup
|
||||
data-testid={`${sourceId}-group.${id}`}
|
||||
size={small ? 'tiny' : 'medium'}
|
||||
>
|
||||
{children}
|
||||
</DropdownMenuGroup>
|
||||
</TldrawUiDropdownMenuGroup>
|
||||
)
|
||||
}
|
||||
case 'context-menu': {
|
|
@ -1,16 +1,18 @@
|
|||
import { ContextMenuItem } from '@radix-ui/react-context-menu'
|
||||
import { preventDefault } from '@tldraw/editor'
|
||||
import { useState } from 'react'
|
||||
import { unwrapLabel } from '../../context/actions'
|
||||
import { TLUiEventSource } from '../../context/events'
|
||||
import { useReadonly } from '../../hooks/useReadonly'
|
||||
import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { Spinner } from '../Spinner'
|
||||
import { Button } from '../primitives/Button'
|
||||
import { DropdownMenuItem } from '../primitives/DropdownMenu'
|
||||
import { Kbd } from '../primitives/Kbd'
|
||||
import { kbdStr } from '../primitives/shared'
|
||||
import { unwrapLabel } from '../../../context/actions'
|
||||
import { TLUiEventSource } from '../../../context/events'
|
||||
import { useReadonly } from '../../../hooks/useReadonly'
|
||||
import { TLUiTranslationKey } from '../../../hooks/useTranslation/TLUiTranslationKey'
|
||||
import { useTranslation } from '../../../hooks/useTranslation/useTranslation'
|
||||
import { kbdStr } from '../../../kbd-utils'
|
||||
import { Spinner } from '../../Spinner'
|
||||
import { TldrawUiButton } from '../Button/TldrawUiButton'
|
||||
import { TldrawUiButtonIcon } from '../Button/TldrawUiButtonIcon'
|
||||
import { TldrawUiButtonLabel } from '../Button/TldrawUiButtonLabel'
|
||||
import { TldrawUiDropdownMenuItem } from '../TldrawUiDropdownMenu'
|
||||
import { TldrawUiKbd } from '../TldrawUiKbd'
|
||||
import { useTldrawUiMenuContext } from './TldrawUiMenuContext'
|
||||
|
||||
/** @public */
|
||||
|
@ -90,24 +92,27 @@ export function TldrawUiMenuItem<
|
|||
switch (menuType) {
|
||||
case 'menu': {
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
type="menu"
|
||||
data-testid={`${sourceId}.${id}`}
|
||||
kbd={kbd}
|
||||
label={labelStr}
|
||||
disabled={disabled}
|
||||
title={titleStr}
|
||||
onClick={(e) => {
|
||||
if (noClose) {
|
||||
preventDefault(e)
|
||||
}
|
||||
if (disableClicks) {
|
||||
setDisableClicks(false)
|
||||
} else {
|
||||
onSelect(sourceId)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<TldrawUiDropdownMenuItem>
|
||||
<TldrawUiButton
|
||||
type="menu"
|
||||
data-testid={`${sourceId}.${id}`}
|
||||
disabled={disabled}
|
||||
title={titleStr}
|
||||
onClick={(e) => {
|
||||
if (noClose) {
|
||||
preventDefault(e)
|
||||
}
|
||||
if (disableClicks) {
|
||||
setDisableClicks(false)
|
||||
} else {
|
||||
onSelect(sourceId)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<TldrawUiButtonLabel>{labelStr}</TldrawUiButtonLabel>
|
||||
{kbd && <TldrawUiKbd>{kbd}</TldrawUiKbd>}
|
||||
</TldrawUiButton>
|
||||
</TldrawUiDropdownMenuItem>
|
||||
)
|
||||
}
|
||||
case 'context-menu': {
|
||||
|
@ -133,37 +138,37 @@ export function TldrawUiMenuItem<
|
|||
<span className="tlui-button__label" draggable={false}>
|
||||
{labelStr}
|
||||
</span>
|
||||
{kbd && <Kbd>{kbd}</Kbd>}
|
||||
{kbd && <TldrawUiKbd>{kbd}</TldrawUiKbd>}
|
||||
{spinner && <Spinner />}
|
||||
</ContextMenuItem>
|
||||
)
|
||||
}
|
||||
case 'panel': {
|
||||
return (
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
data-testid={`${sourceId}.${id}`}
|
||||
icon={icon}
|
||||
type="menu"
|
||||
label={labelStr}
|
||||
title={titleStr}
|
||||
onClick={() => onSelect(sourceId)}
|
||||
smallIcon
|
||||
disabled={disabled}
|
||||
/>
|
||||
onClick={() => onSelect(sourceId)}
|
||||
>
|
||||
<TldrawUiButtonLabel>{labelStr}</TldrawUiButtonLabel>
|
||||
{icon && <TldrawUiButtonIcon icon={icon} />}
|
||||
</TldrawUiButton>
|
||||
)
|
||||
}
|
||||
case 'small-icons':
|
||||
case 'icons': {
|
||||
return (
|
||||
<Button
|
||||
<TldrawUiButton
|
||||
data-testid={`${sourceId}.${id}`}
|
||||
icon={icon}
|
||||
type="icon"
|
||||
title={titleStr}
|
||||
onClick={() => onSelect(sourceId)}
|
||||
disabled={disabled}
|
||||
smallIcon={menuType === 'small-icons'}
|
||||
/>
|
||||
onClick={() => onSelect(sourceId)}
|
||||
>
|
||||
<TldrawUiButtonIcon icon={icon!} small={menuType === 'small-icons'} />
|
||||
</TldrawUiButton>
|
||||
)
|
||||
}
|
||||
case 'keyboard-shortcuts': {
|
||||
|
@ -173,14 +178,17 @@ export function TldrawUiMenuItem<
|
|||
<div className="tlui-shortcuts-dialog__key-pair" data-testid={`${sourceId}.${id}`}>
|
||||
<div className="tlui-shortcuts-dialog__key-pair__key">{labelStr}</div>
|
||||
<div className="tlui-shortcuts-dialog__key-pair__value">
|
||||
<Kbd>{kbd!}</Kbd>
|
||||
<TldrawUiKbd>{kbd!}</TldrawUiKbd>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
case 'helper-buttons': {
|
||||
return (
|
||||
<Button type="low" label={labelStr} iconLeft={icon} onClick={() => onSelect(sourceId)} />
|
||||
<TldrawUiButton type="low" onClick={() => onSelect(sourceId)}>
|
||||
<TldrawUiButtonIcon icon={icon!} />
|
||||
<TldrawUiButtonLabel>{labelStr}</TldrawUiButtonLabel>
|
||||
</TldrawUiButton>
|
||||
)
|
||||
}
|
||||
default: {
|
|
@ -5,15 +5,17 @@ import {
|
|||
ContextMenuSubTrigger,
|
||||
} from '@radix-ui/react-context-menu'
|
||||
import { useContainer } from '@tldraw/editor'
|
||||
import { useMenuIsOpen } from '../../hooks/useMenuIsOpen'
|
||||
import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { Button } from '../primitives/Button'
|
||||
import { useMenuIsOpen } from '../../../hooks/useMenuIsOpen'
|
||||
import { TLUiTranslationKey } from '../../../hooks/useTranslation/TLUiTranslationKey'
|
||||
import { useTranslation } from '../../../hooks/useTranslation/useTranslation'
|
||||
import { TldrawUiButton } from '../Button/TldrawUiButton'
|
||||
import { TldrawUiButtonIcon } from '../Button/TldrawUiButtonIcon'
|
||||
import { TldrawUiButtonLabel } from '../Button/TldrawUiButtonLabel'
|
||||
import {
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
} from '../primitives/DropdownMenu'
|
||||
TldrawUiDropdownMenuSub,
|
||||
TldrawUiDropdownMenuSubContent,
|
||||
TldrawUiDropdownMenuSubTrigger,
|
||||
} from '../TldrawUiDropdownMenu'
|
||||
import { useTldrawUiMenuContext } from './TldrawUiMenuContext'
|
||||
|
||||
/** @public */
|
||||
|
@ -46,34 +48,33 @@ export function TldrawUiMenuSubmenu<Translation extends string = string>({
|
|||
switch (menuType) {
|
||||
case 'menu': {
|
||||
return (
|
||||
<DropdownMenuSub id={`${sourceId}-sub.${id}`}>
|
||||
<DropdownMenuSubTrigger
|
||||
<TldrawUiDropdownMenuSub id={`${sourceId}-sub.${id}`}>
|
||||
<TldrawUiDropdownMenuSubTrigger
|
||||
id={`${sourceId}-sub.${id}`}
|
||||
disabled={disabled}
|
||||
label={labelStr!}
|
||||
title={labelStr!}
|
||||
/>
|
||||
<DropdownMenuSubContent id={`${sourceId}-sub-content.${id}`} data-size={size}>
|
||||
<TldrawUiDropdownMenuSubContent id={`${sourceId}-sub-content.${id}`} data-size={size}>
|
||||
{children}
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuSub>
|
||||
</TldrawUiDropdownMenuSubContent>
|
||||
</TldrawUiDropdownMenuSub>
|
||||
)
|
||||
}
|
||||
case 'context-menu': {
|
||||
if (disabled) return null
|
||||
|
||||
return (
|
||||
<ContextMenuSubWithMenu id={`${sourceId}-sub.${id}`}>
|
||||
<ContextMenuSubTrigger
|
||||
dir="ltr"
|
||||
disabled={disabled}
|
||||
data-testid={`${sourceId}-sub-trigger.${id}`}
|
||||
asChild
|
||||
>
|
||||
<Button
|
||||
<ContextMenuSubTrigger dir="ltr" disabled={disabled} asChild>
|
||||
<TldrawUiButton
|
||||
data-testid={`${sourceId}-sub-trigger.${id}`}
|
||||
type="menu"
|
||||
className="tlui-menu__submenu__trigger"
|
||||
label={labelStr}
|
||||
icon="chevron-right"
|
||||
/>
|
||||
>
|
||||
<TldrawUiButtonLabel>{labelStr}</TldrawUiButtonLabel>
|
||||
<TldrawUiButtonIcon icon="chevron-right" small />
|
||||
</TldrawUiButton>
|
||||
</ContextMenuSubTrigger>
|
||||
<ContextMenuPortal container={container}>
|
||||
<ContextMenuSubContent
|
|
@ -76,6 +76,7 @@ export function TldrawUiContextProvider({
|
|||
</AssetUrlsProvider>
|
||||
)
|
||||
}
|
||||
|
||||
function InternalProviders({
|
||||
overrides,
|
||||
children,
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
TLUiKeyboardShortcutsDialogProps,
|
||||
} from '../components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialog'
|
||||
import { DefaultMainMenu, TLUiMainMenuProps } from '../components/MainMenu/DefaultMainMenu'
|
||||
import { DefaultMenuPanel } from '../components/MenuPanel'
|
||||
import { DefaultMinimap } from '../components/Minimap/DefaultMinimap'
|
||||
import { DefaultNavigationPanel } from '../components/NavigationPanel/DefaultNavigationPanel'
|
||||
import { DefaultPageMenu } from '../components/PageMenu/DefaultPageMenu'
|
||||
|
@ -45,6 +46,9 @@ export interface BaseTLUiComponents {
|
|||
QuickActions: ComponentType<TLUiQuickActionsProps>
|
||||
HelperButtons: ComponentType<TLUiHelperButtonsProps>
|
||||
DebugMenu: ComponentType
|
||||
MenuPanel: ComponentType
|
||||
TopPanel: ComponentType
|
||||
SharePanel: ComponentType
|
||||
}
|
||||
|
||||
/** @public */
|
||||
|
@ -54,7 +58,8 @@ export type TLUiComponents = Partial<{
|
|||
|
||||
const TldrawUiComponentsContext = createContext({} as TLUiComponents)
|
||||
|
||||
type ComponentsContextProviderProps = {
|
||||
/** @public */
|
||||
export type TLUiComponentsProviderProps = {
|
||||
overrides?: TLUiComponents
|
||||
children: any
|
||||
}
|
||||
|
@ -63,7 +68,7 @@ type ComponentsContextProviderProps = {
|
|||
export function TldrawUiComponentsProvider({
|
||||
overrides = {},
|
||||
children,
|
||||
}: ComponentsContextProviderProps) {
|
||||
}: TLUiComponentsProviderProps) {
|
||||
const _overrides = useShallowObjectIdentity(overrides)
|
||||
|
||||
return (
|
||||
|
@ -84,6 +89,7 @@ export function TldrawUiComponentsProvider({
|
|||
QuickActions: DefaultQuickActions,
|
||||
HelperButtons: DefaultHelperButtons,
|
||||
DebugMenu: DefaultDebugMenu,
|
||||
MenuPanel: DefaultMenuPanel,
|
||||
..._overrides,
|
||||
}),
|
||||
[_overrides]
|
||||
|
|
|
@ -1,11 +1,3 @@
|
|||
/** @internal */
|
||||
export function toStartCase(str: string) {
|
||||
return str
|
||||
.split(' ')
|
||||
.map((s) => s.charAt(0).toUpperCase() + s.slice(1))
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
const isDarwin =
|
||||
typeof window === 'undefined'
|
||||
? false
|
||||
|
@ -13,7 +5,7 @@ const isDarwin =
|
|||
const cmdKey = isDarwin ? '⌘' : 'Ctrl'
|
||||
const altKey = isDarwin ? '⌥' : 'Alt'
|
||||
|
||||
/** @internal */
|
||||
/** @public */
|
||||
export function kbd(str: string) {
|
||||
return str
|
||||
.split(',')[0]
|
||||
|
@ -24,17 +16,7 @@ export function kbd(str: string) {
|
|||
})
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
/** @public */
|
||||
export function kbdStr(str: string) {
|
||||
return (
|
||||
'— ' +
|
||||
str
|
||||
.split(',')[0]
|
||||
.split('')
|
||||
.map((sub) => {
|
||||
const subStr = sub.replace(/\$/g, cmdKey).replace(/\?/g, altKey).replace(/!/g, '⇧')
|
||||
return subStr[0].toUpperCase() + subStr.slice(1)
|
||||
})
|
||||
.join(' ')
|
||||
)
|
||||
return '— ' + kbd(str).join(' ')
|
||||
}
|
Loading…
Reference in a new issue