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 /** @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(() => { const toolsArray: TLUiToolItem[] = [ { 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 {children} } /** @public */ export function useTools() { const ctx = React.useContext(ToolsContext) if (!ctx) { throw new Error('useTools must be used within a ToolProvider') } return ctx }