kopia lustrzana https://github.com/Tldraw/Tldraw
226 wiersze
5.2 KiB
TypeScript
226 wiersze
5.2 KiB
TypeScript
import { Editor, GeoShapeGeoStyle, useEditor } from '@tldraw/editor'
|
|
import * as React from 'react'
|
|
import { EmbedDialog } from '../components/EmbedDialog'
|
|
import { useDialogs } from '../context/dialogs'
|
|
import { TLUiEventSource, useUiEvents } from '../context/events'
|
|
import { TLUiIconType } from '../icon-types'
|
|
import { useInsertMedia } from './useInsertMedia'
|
|
import { TLUiTranslationKey } from './useTranslation/TLUiTranslationKey'
|
|
|
|
/** @public */
|
|
export interface TLUiToolItem<
|
|
TranslationKey extends string = string,
|
|
IconType extends string = string,
|
|
> {
|
|
id: string
|
|
label: TranslationKey
|
|
shortcutsLabel?: TranslationKey
|
|
icon: IconType
|
|
onSelect: (source: TLUiEventSource) => void
|
|
kbd?: string
|
|
readonlyOk?: boolean
|
|
meta?: {
|
|
[key: string]: any
|
|
}
|
|
}
|
|
|
|
/** @public */
|
|
export type TLUiToolsContextType = Record<string, TLUiToolItem>
|
|
|
|
/** @internal */
|
|
export const ToolsContext = React.createContext({} as TLUiToolsContextType)
|
|
|
|
/** @public */
|
|
export type TLUiToolsProviderProps = {
|
|
overrides?: (
|
|
editor: Editor,
|
|
tools: TLUiToolsContextType,
|
|
helpers: { insertMedia: () => void }
|
|
) => TLUiToolsContextType
|
|
children: React.ReactNode
|
|
}
|
|
|
|
/** @internal */
|
|
export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) {
|
|
const editor = useEditor()
|
|
const trackEvent = useUiEvents()
|
|
|
|
const { addDialog } = useDialogs()
|
|
const insertMedia = useInsertMedia()
|
|
|
|
const tools = React.useMemo<TLUiToolsContextType>(() => {
|
|
const toolsArray: TLUiToolItem<TLUiTranslationKey, TLUiIconType>[] = [
|
|
{
|
|
id: 'select',
|
|
label: 'tool.select',
|
|
icon: 'tool-pointer',
|
|
kbd: 'v',
|
|
readonlyOk: true,
|
|
onSelect(source) {
|
|
editor.setCurrentTool('select')
|
|
trackEvent('select-tool', { source, id: 'select' })
|
|
},
|
|
},
|
|
{
|
|
id: 'hand',
|
|
label: 'tool.hand',
|
|
icon: 'tool-hand',
|
|
kbd: 'h',
|
|
readonlyOk: true,
|
|
onSelect(source) {
|
|
editor.setCurrentTool('hand')
|
|
trackEvent('select-tool', { source, id: 'hand' })
|
|
},
|
|
},
|
|
{
|
|
id: 'eraser',
|
|
label: 'tool.eraser',
|
|
icon: 'tool-eraser',
|
|
kbd: 'e',
|
|
onSelect(source) {
|
|
editor.setCurrentTool('eraser')
|
|
trackEvent('select-tool', { source, id: 'eraser' })
|
|
},
|
|
},
|
|
{
|
|
id: 'draw',
|
|
label: 'tool.draw',
|
|
icon: 'tool-pencil',
|
|
kbd: 'd,b,x',
|
|
onSelect(source) {
|
|
editor.setCurrentTool('draw')
|
|
trackEvent('select-tool', { source, id: 'draw' })
|
|
},
|
|
},
|
|
...[...GeoShapeGeoStyle.values].map((id) => ({
|
|
id,
|
|
label: `tool.${id}` as TLUiTranslationKey,
|
|
meta: {
|
|
geo: id,
|
|
},
|
|
kbd: id === 'rectangle' ? 'r' : id === 'ellipse' ? 'o' : undefined,
|
|
icon: ('geo-' + id) as TLUiIconType,
|
|
onSelect(source: TLUiEventSource) {
|
|
editor.setStyleForNextShapes(GeoShapeGeoStyle, id)
|
|
editor.setCurrentTool('geo')
|
|
trackEvent('select-tool', { source, id: `geo-${id}` })
|
|
},
|
|
})),
|
|
{
|
|
id: 'arrow',
|
|
label: 'tool.arrow',
|
|
icon: 'tool-arrow',
|
|
kbd: 'a',
|
|
onSelect(source) {
|
|
editor.setCurrentTool('arrow')
|
|
trackEvent('select-tool', { source, id: 'arrow' })
|
|
},
|
|
},
|
|
{
|
|
id: 'line',
|
|
label: 'tool.line',
|
|
icon: 'tool-line',
|
|
kbd: 'l',
|
|
onSelect(source) {
|
|
editor.setCurrentTool('line')
|
|
trackEvent('select-tool', { source, id: 'line' })
|
|
},
|
|
},
|
|
{
|
|
id: 'frame',
|
|
label: 'tool.frame',
|
|
icon: 'tool-frame',
|
|
kbd: 'f',
|
|
onSelect(source) {
|
|
editor.setCurrentTool('frame')
|
|
trackEvent('select-tool', { source, id: 'frame' })
|
|
},
|
|
},
|
|
{
|
|
id: 'text',
|
|
label: 'tool.text',
|
|
icon: 'tool-text',
|
|
kbd: 't',
|
|
onSelect(source) {
|
|
editor.setCurrentTool('text')
|
|
trackEvent('select-tool', { source, id: 'text' })
|
|
},
|
|
},
|
|
{
|
|
id: 'asset',
|
|
label: 'tool.asset',
|
|
icon: 'tool-media',
|
|
kbd: '$u',
|
|
onSelect(source) {
|
|
insertMedia()
|
|
trackEvent('select-tool', { source, id: 'media' })
|
|
},
|
|
},
|
|
{
|
|
id: 'note',
|
|
label: 'tool.note',
|
|
icon: 'tool-note',
|
|
kbd: 'n',
|
|
onSelect(source) {
|
|
editor.setCurrentTool('note')
|
|
trackEvent('select-tool', { source, id: 'note' })
|
|
},
|
|
},
|
|
{
|
|
id: 'laser',
|
|
label: 'tool.laser',
|
|
readonlyOk: true,
|
|
icon: 'tool-laser',
|
|
kbd: 'k',
|
|
onSelect(source) {
|
|
editor.setCurrentTool('laser')
|
|
trackEvent('select-tool', { source, id: 'laser' })
|
|
},
|
|
},
|
|
{
|
|
id: 'embed',
|
|
label: 'tool.embed',
|
|
icon: 'tool-embed',
|
|
onSelect(source) {
|
|
addDialog({ component: EmbedDialog })
|
|
trackEvent('select-tool', { source, id: 'embed' })
|
|
},
|
|
},
|
|
{
|
|
id: 'highlight',
|
|
label: 'tool.highlight',
|
|
icon: 'tool-highlight',
|
|
// TODO: pick a better shortcut
|
|
kbd: '!d',
|
|
onSelect(source) {
|
|
editor.setCurrentTool('highlight')
|
|
trackEvent('select-tool', { source, id: 'highlight' })
|
|
},
|
|
},
|
|
]
|
|
|
|
toolsArray.push()
|
|
|
|
const tools = Object.fromEntries(toolsArray.map((t) => [t.id, t]))
|
|
|
|
if (overrides) {
|
|
return overrides(editor, tools, { insertMedia })
|
|
}
|
|
|
|
return tools
|
|
}, [overrides, editor, trackEvent, insertMedia, addDialog])
|
|
|
|
return <ToolsContext.Provider value={tools}>{children}</ToolsContext.Provider>
|
|
}
|
|
|
|
/** @public */
|
|
export function useTools() {
|
|
const ctx = React.useContext(ToolsContext)
|
|
|
|
if (!ctx) {
|
|
throw new Error('useTools must be used within a ToolProvider')
|
|
}
|
|
|
|
return ctx
|
|
}
|