[improvement] Ui events followup (#1354)

This PR makes several changes to the ui events APIs.

### Change Type

- [ ] `patch` — Bug Fix
- [ ] `minor` — New Feature
- [x] `major` — Breaking Change

### Release Notes

- [ui] Adds source to ui events data object
- [ui] Corrects source for toolbar events
- [ui] Corrects source for clipboard events
- [examples] Updates events example
This commit is contained in:
Steve Ruiz 2023-05-12 09:16:17 +01:00 committed by GitHub
parent a722e3e6f0
commit 03595bc88d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 162 additions and 161 deletions

View file

@ -13,11 +13,8 @@ export default function Example() {
const [uiEvents, setUiEvents] = useState<string[]>([]) const [uiEvents, setUiEvents] = useState<string[]>([])
const handleEvent = useCallback<TLUiEventHandler>((source, name, data) => { const handleEvent = useCallback<TLUiEventHandler>((name, data) => {
setUiEvents((events) => [ setUiEvents((events) => [`${name} ${JSON.stringify(data)}`, ...events])
data ? `${source} ${name} ${JSON.stringify(data)}` : `${source} ${name}`,
...events,
])
}, []) }, [])
useEffect(() => { useEffect(() => {

View file

@ -282,14 +282,14 @@ export class App extends EventEmitter<TLEventMap> {
true true
) )
this.root.enter(undefined, 'initial')
if (this.instanceState.followingUserId) { if (this.instanceState.followingUserId) {
this.stopFollowingUser() this.stopFollowingUser()
} }
this.updateCullingBounds() this.updateCullingBounds()
this.root.enter(undefined, 'initial')
requestAnimationFrame(() => { requestAnimationFrame(() => {
this._tickManager.start() this._tickManager.start()
}) })

View file

@ -799,7 +799,7 @@ export interface ToolItem {
[key: string]: any; [key: string]: any;
}; };
// (undocumented) // (undocumented)
onSelect: () => void; onSelect: (source: TLUiEventSource) => void;
// (undocumented) // (undocumented)
readonlyOk: boolean; readonlyOk: boolean;
// (undocumented) // (undocumented)
@ -1019,7 +1019,7 @@ export function useLanguages(): {
export function useLocalStorageState<T = any>(key: string, defaultValue: T): readonly [T, (setter: ((value: T) => T) | T) => void]; export function useLocalStorageState<T = any>(key: string, defaultValue: T): readonly [T, (setter: ((value: T) => T) | T) => void];
// @public (undocumented) // @public (undocumented)
export function useMenuClipboardEvents(): { export function useMenuClipboardEvents(source: TLUiEventSource): {
copy: () => void; copy: () => void;
cut: () => void; cut: () => void;
paste: (data: ClipboardItem[] | DataTransfer, point?: VecLike) => Promise<void>; paste: (data: ClipboardItem[] | DataTransfer, point?: VecLike) => Promise<void>;

View file

@ -61,7 +61,7 @@ function ContextMenuContent() {
const handleSubOpenChange = useMenuIsOpen('context menu sub') const handleSubOpenChange = useMenuIsOpen('context menu sub')
const isReadonly = useReadonly() const isReadonly = useReadonly()
const { paste } = useMenuClipboardEvents() const { paste } = useMenuClipboardEvents('context-menu')
const breakpoint = useBreakpoint() const breakpoint = useBreakpoint()
const container = useContainer() const container = useContainer()

View file

@ -37,7 +37,7 @@ function MenuContent() {
const menuSchema = useMenuSchema() const menuSchema = useMenuSchema()
const breakpoint = useBreakpoint() const breakpoint = useBreakpoint()
const isReadonly = useReadonly() const isReadonly = useReadonly()
const { paste } = useMenuClipboardEvents() const { paste } = useMenuClipboardEvents('menu')
function getMenuItem(app: App, item: MenuChild, parent: MenuChild | null, depth: number) { function getMenuItem(app: App, item: MenuChild, parent: MenuChild | null, depth: number) {
switch (item.type) { switch (item.type) {

View file

@ -226,7 +226,7 @@ const OverflowToolsContent = track(function OverflowToolsContent({
data-tool={id} data-tool={id}
data-geo={meta?.geo ?? ''} data-geo={meta?.geo ?? ''}
aria-label={label} aria-label={label}
onClick={onSelect} onClick={() => onSelect('toolbar')}
title={label ? `${msg(label)} ${kbd ? kbdStr(kbd) : ''}` : ''} title={label ? `${msg(label)} ${kbd ? kbdStr(kbd) : ''}` : ''}
icon={icon} icon={icon}
/> />
@ -255,10 +255,10 @@ function ToolbarButton({
title={title} title={title}
icon={item.icon} icon={item.icon}
data-state={isSelected ? 'selected' : undefined} data-state={isSelected ? 'selected' : undefined}
onClick={item.onSelect} onClick={() => item.onSelect('toolbar')}
onTouchStart={(e) => { onTouchStart={(e) => {
preventDefault(e) preventDefault(e)
item.onSelect() item.onSelect('toolbar')
}} }}
/> />
) )

View file

@ -73,7 +73,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
const insertMedia = useInsertMedia() const insertMedia = useInsertMedia()
const printSelectionOrPages = usePrint() const printSelectionOrPages = usePrint()
const { cut, copy } = useMenuClipboardEvents() const { cut, copy } = useMenuClipboardEvents('unknown')
const copyAs = useCopyAs() const copyAs = useCopyAs()
const exportAs = useExportAs() const exportAs = useExportAs()
@ -88,7 +88,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
icon: 'link', icon: 'link',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'edit-link') trackEvent('edit-link', { source })
app.mark('edit-link') app.mark('edit-link')
addDialog({ component: EditLinkDialog }) addDialog({ component: EditLinkDialog })
}, },
@ -99,7 +99,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
readonlyOk: false, readonlyOk: false,
kbd: '$i', kbd: '$i',
onSelect(source) { onSelect(source) {
trackEvent(source, 'insert-embed') trackEvent('insert-embed', { source })
addDialog({ component: EmbedDialog }) addDialog({ component: EmbedDialog })
}, },
}, },
@ -109,7 +109,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '$u', kbd: '$u',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'insert-media') trackEvent('insert-media', { source })
insertMedia() insertMedia()
}, },
}, },
@ -120,7 +120,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '$z', kbd: '$z',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'undo') trackEvent('undo', { source })
app.undo() app.undo()
}, },
}, },
@ -131,7 +131,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '$!z', kbd: '$!z',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'redo') trackEvent('redo', { source })
app.redo() app.redo()
}, },
}, },
@ -142,7 +142,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
contextMenuLabel: 'action.export-as-svg.short', contextMenuLabel: 'action.export-as-svg.short',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'export-as', { format: 'svg' }) trackEvent('export-as', { format: 'svg', source })
exportAs(app.selectedIds, 'svg') exportAs(app.selectedIds, 'svg')
}, },
}, },
@ -153,7 +153,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
contextMenuLabel: 'action.export-as-png.short', contextMenuLabel: 'action.export-as-png.short',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'export-as', { format: 'png' }) trackEvent('export-as', { format: 'png', source })
exportAs(app.selectedIds, 'png') exportAs(app.selectedIds, 'png')
}, },
}, },
@ -164,7 +164,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
contextMenuLabel: 'action.export-as-json.short', contextMenuLabel: 'action.export-as-json.short',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'export-as', { format: 'json' }) trackEvent('export-as', { format: 'json', source })
exportAs(app.selectedIds, 'json') exportAs(app.selectedIds, 'json')
}, },
}, },
@ -176,7 +176,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '$!c', kbd: '$!c',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'copy-as', { format: 'svg' }) trackEvent('copy-as', { format: 'svg', source })
copyAs(app.selectedIds, 'svg') copyAs(app.selectedIds, 'svg')
}, },
}, },
@ -187,7 +187,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
contextMenuLabel: 'action.copy-as-png.short', contextMenuLabel: 'action.copy-as-png.short',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'copy-as', { format: 'png' }) trackEvent('copy-as', { format: 'png', source })
copyAs(app.selectedIds, 'png') copyAs(app.selectedIds, 'png')
}, },
}, },
@ -198,7 +198,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
contextMenuLabel: 'action.copy-as-json.short', contextMenuLabel: 'action.copy-as-json.short',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'copy-as', { format: 'json' }) trackEvent('copy-as', { format: 'json', source })
copyAs(app.selectedIds, 'json') copyAs(app.selectedIds, 'json')
}, },
}, },
@ -207,7 +207,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
label: 'action.toggle-auto-size', label: 'action.toggle-auto-size',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'toggle-auto-size') trackEvent('toggle-auto-size', { source })
app.mark() app.mark()
app.updateShapes( app.updateShapes(
app.selectedShapes app.selectedShapes
@ -231,7 +231,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
label: 'action.open-embed-link', label: 'action.open-embed-link',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'open-embed-link') trackEvent('open-embed-link', { source })
const ids = app.selectedIds const ids = app.selectedIds
const warnMsg = 'No embed shapes selected' const warnMsg = 'No embed shapes selected'
if (ids.length !== 1) { if (ids.length !== 1) {
@ -252,7 +252,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
label: 'action.convert-to-bookmark', label: 'action.convert-to-bookmark',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'convert-to-bookmark') trackEvent('convert-to-bookmark', { source })
const ids = app.selectedIds const ids = app.selectedIds
const shapes = ids.map((id) => app.getShapeById(id)) const shapes = ids.map((id) => app.getShapeById(id))
@ -295,7 +295,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
label: 'action.convert-to-embed', label: 'action.convert-to-embed',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'convert-to-embed') trackEvent('convert-to-embed', { source })
const ids = app.selectedIds const ids = app.selectedIds
const shapes = compact(ids.map((id) => app.getShapeById(id))) const shapes = compact(ids.map((id) => app.getShapeById(id)))
@ -347,7 +347,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
if (app.currentToolId !== 'select') return if (app.currentToolId !== 'select') return
trackEvent(source, 'duplicate-shapes') trackEvent('duplicate-shapes', { source })
const ids = app.selectedIds const ids = app.selectedIds
const commonBounds = Box2d.Common(compact(ids.map((id) => app.getPageBoundsById(id)))) const commonBounds = Box2d.Common(compact(ids.map((id) => app.getPageBoundsById(id))))
const offset = app.canMoveCamera const offset = app.canMoveCamera
@ -370,7 +370,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
icon: 'ungroup', icon: 'ungroup',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'ungroup-shapes') trackEvent('ungroup-shapes', { source })
app.mark('ungroup') app.mark('ungroup')
app.ungroupShapes(app.selectedIds) app.ungroupShapes(app.selectedIds)
}, },
@ -382,7 +382,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
icon: 'group', icon: 'group',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'group-shapes') trackEvent('group-shapes', { source })
if (app.selectedShapes.length === 1 && app.selectedShapes[0].type === 'group') { if (app.selectedShapes.length === 1 && app.selectedShapes[0].type === 'group') {
app.mark('ungroup') app.mark('ungroup')
app.ungroupShapes(app.selectedIds) app.ungroupShapes(app.selectedIds)
@ -399,7 +399,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
icon: 'align-left', icon: 'align-left',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'align-shapes', { operation: 'left' }) trackEvent('align-shapes', { operation: 'left', source })
app.mark('align left') app.mark('align left')
app.alignShapes('left', app.selectedIds) app.alignShapes('left', app.selectedIds)
}, },
@ -412,7 +412,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
icon: 'align-center-horizontal', icon: 'align-center-horizontal',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'align-shapes', { operation: 'center-horizontal' }) trackEvent('align-shapes', { operation: 'center-horizontal', source })
app.mark('align center horizontal') app.mark('align center horizontal')
app.alignShapes('center-horizontal', app.selectedIds) app.alignShapes('center-horizontal', app.selectedIds)
}, },
@ -424,7 +424,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
icon: 'align-right', icon: 'align-right',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'align-shapes', { operation: 'right' }) trackEvent('align-shapes', { operation: 'right', source })
app.mark('align right') app.mark('align right')
app.alignShapes('right', app.selectedIds) app.alignShapes('right', app.selectedIds)
}, },
@ -437,7 +437,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
icon: 'align-center-vertical', icon: 'align-center-vertical',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'align-shapes', { operation: 'center-vertical' }) trackEvent('align-shapes', { operation: 'center-vertical', source })
app.mark('align center vertical') app.mark('align center vertical')
app.alignShapes('center-vertical', app.selectedIds) app.alignShapes('center-vertical', app.selectedIds)
}, },
@ -449,7 +449,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '?W', kbd: '?W',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'align-shapes', { operation: 'top' }) trackEvent('align-shapes', { operation: 'top', source })
app.mark('align top') app.mark('align top')
app.alignShapes('top', app.selectedIds) app.alignShapes('top', app.selectedIds)
}, },
@ -461,7 +461,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '?S', kbd: '?S',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'align-shapes', { operation: 'bottom' }) trackEvent('align-shapes', { operation: 'bottom', source })
app.mark('align bottom') app.mark('align bottom')
app.alignShapes('bottom', app.selectedIds) app.alignShapes('bottom', app.selectedIds)
}, },
@ -473,7 +473,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
icon: 'distribute-horizontal', icon: 'distribute-horizontal',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'distribute-shapes', { operation: 'horizontal' }) trackEvent('distribute-shapes', { operation: 'horizontal', source })
app.mark('distribute horizontal') app.mark('distribute horizontal')
app.distributeShapes('horizontal', app.selectedIds) app.distributeShapes('horizontal', app.selectedIds)
}, },
@ -485,7 +485,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
icon: 'distribute-vertical', icon: 'distribute-vertical',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'distribute-shapes', { operation: 'vertical' }) trackEvent('distribute-shapes', { operation: 'vertical', source })
app.mark('distribute vertical') app.mark('distribute vertical')
app.distributeShapes('vertical', app.selectedIds) app.distributeShapes('vertical', app.selectedIds)
}, },
@ -497,7 +497,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
icon: 'stretch-horizontal', icon: 'stretch-horizontal',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'stretch-shapes', { operation: 'horizontal' }) trackEvent('stretch-shapes', { operation: 'horizontal', source })
app.mark('stretch horizontal') app.mark('stretch horizontal')
app.stretchShapes('horizontal', app.selectedIds) app.stretchShapes('horizontal', app.selectedIds)
}, },
@ -509,7 +509,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
icon: 'stretch-vertical', icon: 'stretch-vertical',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'stretch-shapes', { operation: 'vertical' }) trackEvent('stretch-shapes', { operation: 'vertical', source })
app.mark('stretch vertical') app.mark('stretch vertical')
app.stretchShapes('vertical', app.selectedIds) app.stretchShapes('vertical', app.selectedIds)
}, },
@ -521,7 +521,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '!h', kbd: '!h',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'flip-shapes', { operation: 'horizontal' }) trackEvent('flip-shapes', { operation: 'horizontal', source })
app.mark('flip horizontal') app.mark('flip horizontal')
app.flipShapes('horizontal', app.selectedIds) app.flipShapes('horizontal', app.selectedIds)
}, },
@ -533,7 +533,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '!v', kbd: '!v',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'flip-shapes', { operation: 'vertical' }) trackEvent('flip-shapes', { operation: 'vertical', source })
app.mark('flip vertical') app.mark('flip vertical')
app.flipShapes('vertical', app.selectedIds) app.flipShapes('vertical', app.selectedIds)
}, },
@ -544,7 +544,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
icon: 'pack', icon: 'pack',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'pack-shapes') trackEvent('pack-shapes', { source })
app.mark('pack') app.mark('pack')
app.packShapes(app.selectedIds) app.packShapes(app.selectedIds)
}, },
@ -556,7 +556,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
icon: 'stack-vertical', icon: 'stack-vertical',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'stack-shapes', { operation: 'vertical' }) trackEvent('stack-shapes', { operation: 'vertical', source })
app.mark('stack-vertical') app.mark('stack-vertical')
app.stackShapes('vertical', app.selectedIds) app.stackShapes('vertical', app.selectedIds)
}, },
@ -568,7 +568,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
icon: 'stack-horizontal', icon: 'stack-horizontal',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'stack-shapes', { operation: 'horizontal' }) trackEvent('stack-shapes', { operation: 'horizontal', source })
app.mark('stack-horizontal') app.mark('stack-horizontal')
app.stackShapes('horizontal', app.selectedIds) app.stackShapes('horizontal', app.selectedIds)
}, },
@ -580,7 +580,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
icon: 'bring-to-front', icon: 'bring-to-front',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'reorder-shapes', { operation: 'toFront' }) trackEvent('reorder-shapes', { operation: 'toFront', source })
app.mark('bring to front') app.mark('bring to front')
app.bringToFront() app.bringToFront()
}, },
@ -592,7 +592,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '?]', kbd: '?]',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'reorder-shapes', { operation: 'forward' }) trackEvent('reorder-shapes', { operation: 'forward', source })
app.mark('bring forward') app.mark('bring forward')
app.bringForward() app.bringForward()
}, },
@ -604,7 +604,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '?[', kbd: '?[',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'reorder-shapes', { operation: 'backward' }) trackEvent('reorder-shapes', { operation: 'backward', source })
app.mark('send backward') app.mark('send backward')
app.sendBackward() app.sendBackward()
}, },
@ -616,7 +616,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '[', kbd: '[',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'reorder-shapes', { operation: 'toBack' }) trackEvent('reorder-shapes', { operation: 'toBack', source })
app.mark('send to back') app.mark('send to back')
app.sendToBack() app.sendToBack()
}, },
@ -627,7 +627,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '$x', kbd: '$x',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'cut') trackEvent('cut', { source })
app.mark('cut') app.mark('cut')
cut() cut()
}, },
@ -638,7 +638,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '$c', kbd: '$c',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'copy') trackEvent('copy', { source })
copy() copy()
}, },
}, },
@ -658,7 +658,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '$a', kbd: '$a',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'select-all-shapes') trackEvent('select-all-shapes', { source })
if (app.currentToolId !== 'select') { if (app.currentToolId !== 'select') {
app.cancel() app.cancel()
app.setSelectedTool('select') app.setSelectedTool('select')
@ -673,7 +673,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
label: 'action.select-none', label: 'action.select-none',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'select-none-shapes') trackEvent('select-none-shapes', { source })
app.mark('select none') app.mark('select none')
app.selectNone() app.selectNone()
}, },
@ -686,7 +686,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
if (app.currentToolId !== 'select') return if (app.currentToolId !== 'select') return
trackEvent(source, 'delete-shapes') trackEvent('delete-shapes', { source })
app.mark('delete') app.mark('delete')
app.deleteShapes() app.deleteShapes()
}, },
@ -698,7 +698,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
if (app.selectedIds.length === 0) return if (app.selectedIds.length === 0) return
trackEvent(source, 'rotate-cw') trackEvent('rotate-cw', { source })
app.mark('rotate-cw') app.mark('rotate-cw')
const offset = app.selectionRotation % (TAU / 2) const offset = app.selectionRotation % (TAU / 2)
const dontUseOffset = approximately(offset, 0) || approximately(offset, TAU / 2) const dontUseOffset = approximately(offset, 0) || approximately(offset, TAU / 2)
@ -712,7 +712,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
if (app.selectedIds.length === 0) return if (app.selectedIds.length === 0) return
trackEvent(source, 'rotate-ccw') trackEvent('rotate-ccw', { source })
app.mark('rotate-ccw') app.mark('rotate-ccw')
const offset = app.selectionRotation % (TAU / 2) const offset = app.selectionRotation % (TAU / 2)
const offsetCloseToZero = approximately(offset, 0) const offsetCloseToZero = approximately(offset, 0)
@ -725,7 +725,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '$=', kbd: '$=',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'zoom-in') trackEvent('zoom-in', { source })
app.zoomIn(app.viewportScreenCenter, { duration: ANIMATION_MEDIUM_MS }) app.zoomIn(app.viewportScreenCenter, { duration: ANIMATION_MEDIUM_MS })
}, },
}, },
@ -735,7 +735,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '$-', kbd: '$-',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'zoom-out') trackEvent('zoom-out', { source })
app.zoomOut(app.viewportScreenCenter, { duration: ANIMATION_MEDIUM_MS }) app.zoomOut(app.viewportScreenCenter, { duration: ANIMATION_MEDIUM_MS })
}, },
}, },
@ -746,7 +746,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '!0', kbd: '!0',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'reset-zoom') trackEvent('reset-zoom', { source })
app.resetZoom(app.viewportScreenCenter, { duration: ANIMATION_MEDIUM_MS }) app.resetZoom(app.viewportScreenCenter, { duration: ANIMATION_MEDIUM_MS })
}, },
}, },
@ -756,7 +756,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '!1', kbd: '!1',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'zoom-to-fit') trackEvent('zoom-to-fit', { source })
app.zoomToFit({ duration: ANIMATION_MEDIUM_MS }) app.zoomToFit({ duration: ANIMATION_MEDIUM_MS })
}, },
}, },
@ -766,7 +766,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '!2', kbd: '!2',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'zoom-to-selection') trackEvent('zoom-to-selection', { source })
app.zoomToSelection({ duration: ANIMATION_MEDIUM_MS }) app.zoomToSelection({ duration: ANIMATION_MEDIUM_MS })
}, },
}, },
@ -776,7 +776,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
menuLabel: 'action.toggle-snap-mode.menu', menuLabel: 'action.toggle-snap-mode.menu',
readonlyOk: false, readonlyOk: false,
onSelect(source) { onSelect(source) {
trackEvent(source, 'toggle-snap-mode') trackEvent('toggle-snap-mode', { source })
app.setSnapMode(!app.isSnapMode) app.setSnapMode(!app.isSnapMode)
}, },
checkbox: true, checkbox: true,
@ -788,7 +788,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '$/', kbd: '$/',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'toggle-dark-mode') trackEvent('toggle-dark-mode', { source })
app.setDarkMode(!app.isDarkMode) app.setDarkMode(!app.isDarkMode)
}, },
checkbox: true, checkbox: true,
@ -800,7 +800,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
contextMenuLabel: 'action.toggle-transparent.context-menu', contextMenuLabel: 'action.toggle-transparent.context-menu',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'toggle-transparent') trackEvent('toggle-transparent', { source })
app.updateInstanceState( app.updateInstanceState(
{ {
exportBackground: !app.instanceState.exportBackground, exportBackground: !app.instanceState.exportBackground,
@ -817,7 +817,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
readonlyOk: false, readonlyOk: false,
kbd: 'q', kbd: 'q',
onSelect(source) { onSelect(source) {
trackEvent(source, 'toggle-tool-lock') trackEvent('toggle-tool-lock', { source })
app.setToolLocked(!app.isToolLocked) app.setToolLocked(!app.isToolLocked)
}, },
checkbox: true, checkbox: true,
@ -834,7 +834,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
// UI to unmount which puts us in a dodgy state // UI to unmount which puts us in a dodgy state
requestAnimationFrame(() => { requestAnimationFrame(() => {
app.batch(() => { app.batch(() => {
trackEvent(source, 'toggle-focus-mode') trackEvent('toggle-focus-mode', { source })
clearDialogs() clearDialogs()
clearToasts() clearToasts()
app.setFocusMode(!app.isFocusMode) app.setFocusMode(!app.isFocusMode)
@ -849,7 +849,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
readonlyOk: true, readonlyOk: true,
kbd: "$'", kbd: "$'",
onSelect(source) { onSelect(source) {
trackEvent(source, 'toggle-grid-mode') trackEvent('toggle-grid-mode', { source })
app.setGridMode(!app.isGridMode) app.setGridMode(!app.isGridMode)
}, },
checkbox: true, checkbox: true,
@ -860,7 +860,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
menuLabel: 'action.toggle-debug-mode.menu', menuLabel: 'action.toggle-debug-mode.menu',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'toggle-debug-mode') trackEvent('toggle-debug-mode', { source })
app.updateInstanceState( app.updateInstanceState(
{ {
isDebugMode: !app.instanceState.isDebugMode, isDebugMode: !app.instanceState.isDebugMode,
@ -876,7 +876,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
kbd: '$p', kbd: '$p',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'print') trackEvent('print', { source })
printSelectionOrPages() printSelectionOrPages()
}, },
}, },
@ -886,7 +886,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
icon: 'cross-2', icon: 'cross-2',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'exit-pen-mode') trackEvent('exit-pen-mode', { source })
app.setPenMode(false) app.setPenMode(false)
}, },
}, },
@ -896,7 +896,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
icon: 'cross-2', icon: 'cross-2',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'stop-following') trackEvent('stop-following', { source })
app.stopFollowingUser() app.stopFollowingUser()
}, },
}, },
@ -906,7 +906,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
icon: 'arrow-left', icon: 'arrow-left',
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent(source, 'zoom-to-content') trackEvent('zoom-to-content', { source })
app.zoomToContent() app.zoomToContent()
}, },
}, },

View file

@ -40,7 +40,7 @@ import { compact, isNonNull } from '@tldraw/utils'
import { compressToBase64, decompressFromBase64 } from 'lz-string' import { compressToBase64, decompressFromBase64 } from 'lz-string'
import { useCallback, useEffect } from 'react' import { useCallback, useEffect } from 'react'
import { useAppIsFocused } from './useAppIsFocused' import { useAppIsFocused } from './useAppIsFocused'
import { useEvents } from './useEventsProvider' import { TLUiEventSource, useEvents } from './useEventsProvider'
/** @public */ /** @public */
export type EmbedInfo = { export type EmbedInfo = {
@ -968,7 +968,7 @@ const handleNativeClipboardPaste = async (
} }
/** @public */ /** @public */
export function useMenuClipboardEvents() { export function useMenuClipboardEvents(source: TLUiEventSource) {
const app = useApp() const app = useApp()
const trackEvent = useEvents() const trackEvent = useEvents()
@ -977,9 +977,9 @@ export function useMenuClipboardEvents() {
if (app.selectedIds.length === 0) return if (app.selectedIds.length === 0) return
handleMenuCopy(app) handleMenuCopy(app)
trackEvent('menu', 'copy') trackEvent('copy', { source })
}, },
[app, trackEvent] [app, trackEvent, source]
) )
const cut = useCallback( const cut = useCallback(
@ -988,9 +988,9 @@ export function useMenuClipboardEvents() {
handleMenuCopy(app) handleMenuCopy(app)
app.deleteShapes() app.deleteShapes()
trackEvent('menu', 'cut') trackEvent('cut', { source })
}, },
[app, trackEvent] [app, trackEvent, source]
) )
const paste = useCallback( const paste = useCallback(
@ -1007,7 +1007,7 @@ export function useMenuClipboardEvents() {
// handleScenePaste(app, point) // handleScenePaste(app, point)
// } // }
trackEvent('menu', 'paste') trackEvent('paste', { source: 'menu' })
}, },
[app, trackEvent] [app, trackEvent]
) )
@ -1032,7 +1032,7 @@ export function useNativeClipboardEvents() {
if (app.selectedIds.length === 0 || app.editingId !== null || disallowClipboardEvents(app)) if (app.selectedIds.length === 0 || app.editingId !== null || disallowClipboardEvents(app))
return return
handleMenuCopy(app) handleMenuCopy(app)
trackEvent('kbd', 'copy') trackEvent('copy', { source: 'kbd' })
} }
function cut() { function cut() {
@ -1040,7 +1040,7 @@ export function useNativeClipboardEvents() {
return return
handleMenuCopy(app) handleMenuCopy(app)
app.deleteShapes() app.deleteShapes()
trackEvent('kbd', 'cut') trackEvent('cut', { source: 'kbd' })
} }
const paste = (e: ClipboardEvent) => { const paste = (e: ClipboardEvent) => {
@ -1054,7 +1054,7 @@ export function useNativeClipboardEvents() {
} }
}) })
} }
trackEvent('kbd', 'paste') trackEvent('paste', { source: 'kbd' })
} }
document.addEventListener('copy', copy) document.addEventListener('copy', copy)

View file

@ -46,7 +46,7 @@ export function DialogsProvider({ children }: DialogsProviderProps) {
return [...d.filter((m) => m.id !== dialog.id), { ...dialog, id }] return [...d.filter((m) => m.id !== dialog.id), { ...dialog, id }]
}) })
trackEvent('dialog', 'open-menu', { id }) trackEvent('open-menu', { source: 'dialog', id })
app.addOpenMenu(id) app.addOpenMenu(id)
return id return id
@ -68,7 +68,7 @@ export function DialogsProvider({ children }: DialogsProviderProps) {
}) })
) )
trackEvent('dialog', 'open-menu', { id }) trackEvent('open-menu', { source: 'dialog', id })
app.addOpenMenu(id) app.addOpenMenu(id)
return id return id
@ -88,7 +88,7 @@ export function DialogsProvider({ children }: DialogsProviderProps) {
}) })
) )
trackEvent('dialog', 'close-menu', { id }) trackEvent('close-menu', { source: 'dialog', id })
app.deleteOpenMenu(id) app.deleteOpenMenu(id)
return id return id
@ -100,7 +100,7 @@ export function DialogsProvider({ children }: DialogsProviderProps) {
setDialogs((d) => { setDialogs((d) => {
d.forEach((m) => { d.forEach((m) => {
m.onClose?.() m.onClose?.()
trackEvent('dialog', 'close-menu', { id: m.id }) trackEvent('close-menu', { source: 'dialog', id: m.id })
app.deleteOpenMenu(m.id) app.deleteOpenMenu(m.id)
}) })
return [] return []

View file

@ -17,28 +17,29 @@ export type TLUiEventSource =
| 'dialog' | 'dialog'
| 'help-menu' | 'help-menu'
| 'helper-buttons' | 'helper-buttons'
| 'unknown'
/** @public */ /** @public */
export interface TLUiEventMap { export interface TLUiEventMap {
// Actions // Actions
undo: undefined undo: null
redo: undefined redo: null
'group-shapes': undefined 'group-shapes': null
'ungroup-shapes': undefined 'ungroup-shapes': null
'convert-to-embed': undefined 'convert-to-embed': null
'convert-to-bookmark': undefined 'convert-to-bookmark': null
'open-embed-link': undefined 'open-embed-link': null
'toggle-auto-size': undefined 'toggle-auto-size': null
'copy-as': { format: 'svg' | 'png' | 'json' } 'copy-as': { format: 'svg' | 'png' | 'json' }
'export-as': { format: 'svg' | 'png' | 'json' } 'export-as': { format: 'svg' | 'png' | 'json' }
'edit-link': undefined 'edit-link': null
'insert-embed': undefined 'insert-embed': null
'insert-media': undefined 'insert-media': null
'align-shapes': { 'align-shapes': {
operation: 'left' | 'center-horizontal' | 'right' | 'top' | 'center-vertical' | 'bottom' operation: 'left' | 'center-horizontal' | 'right' | 'top' | 'center-vertical' | 'bottom'
} }
'duplicate-shapes': undefined 'duplicate-shapes': null
'pack-shapes': undefined 'pack-shapes': null
'stack-shapes': { operation: 'horizontal' | 'vertical' } 'stack-shapes': { operation: 'horizontal' | 'vertical' }
'flip-shapes': { operation: 'horizontal' | 'vertical' } 'flip-shapes': { operation: 'horizontal' | 'vertical' }
'distribute-shapes': { operation: 'horizontal' | 'vertical' } 'distribute-shapes': { operation: 'horizontal' | 'vertical' }
@ -46,44 +47,47 @@ export interface TLUiEventMap {
'reorder-shapes': { 'reorder-shapes': {
operation: 'toBack' | 'toFront' | 'forward' | 'backward' operation: 'toBack' | 'toFront' | 'forward' | 'backward'
} }
'delete-shapes': undefined 'delete-shapes': null
'select-all-shapes': undefined 'select-all-shapes': null
'select-none-shapes': undefined 'select-none-shapes': null
'rotate-ccw': undefined 'rotate-ccw': null
'rotate-cw': undefined 'rotate-cw': null
'zoom-in': undefined 'zoom-in': null
'zoom-out': undefined 'zoom-out': null
'zoom-to-fit': undefined 'zoom-to-fit': null
'zoom-to-selection': undefined 'zoom-to-selection': null
'reset-zoom': undefined 'reset-zoom': null
'zoom-into-view': undefined 'zoom-into-view': null
'zoom-to-content': undefined 'zoom-to-content': null
'open-menu': { id: string } 'open-menu': { id: string }
'close-menu': { id: string } 'close-menu': { id: string }
'create-new-project': undefined 'create-new-project': null
'save-project-to-file': undefined 'save-project-to-file': null
'open-file': undefined 'open-file': null
'select-tool': { id: string } 'select-tool': { id: string }
print: undefined print: null
copy: undefined copy: null
paste: undefined paste: null
cut: undefined cut: null
'toggle-transparent': undefined 'toggle-transparent': null
'toggle-snap-mode': undefined 'toggle-snap-mode': null
'toggle-tool-lock': undefined 'toggle-tool-lock': null
'toggle-grid-mode': undefined 'toggle-grid-mode': null
'toggle-dark-mode': undefined 'toggle-dark-mode': null
'toggle-focus-mode': undefined 'toggle-focus-mode': null
'toggle-debug-mode': undefined 'toggle-debug-mode': null
'exit-pen-mode': undefined 'exit-pen-mode': null
'stop-following': undefined 'stop-following': null
} }
type Join<T, K> = K extends null
? { [R in keyof T]: T[R] }
: { [R in keyof T]: T[R] } & { [R in keyof K]: K[R] }
/** @public */ /** @public */
export type TLUiEventHandler<T extends keyof TLUiEventMap = keyof TLUiEventMap> = ( export type TLUiEventHandler<T extends keyof TLUiEventMap = keyof TLUiEventMap> = (
source: string,
name: T, name: T,
data?: TLUiEventMap[T] data: Join<{ source: TLUiEventSource }, TLUiEventMap[T]>
) => void ) => void
/** @internal */ /** @internal */

View file

@ -58,7 +58,7 @@ export function useKeyboardShortcuts() {
hot(getHotkeysStringFromKbd(tool.kbd), (event) => { hot(getHotkeysStringFromKbd(tool.kbd), (event) => {
if (areShortcutsDisabled()) return if (areShortcutsDisabled()) return
preventDefault(event) preventDefault(event)
tool.onSelect() tool.onSelect('kbd')
}) })
} }

View file

@ -40,7 +40,7 @@ export function useMenuIsOpen(id: string, cb?: (isOpen: boolean) => void) {
// hook but it's necessary to handle the case where the // hook but it's necessary to handle the case where the
// this effect runs twice or re-runs. // this effect runs twice or re-runs.
if (rIsOpen.current) { if (rIsOpen.current) {
trackEvent('menu', 'open-menu', { id }) trackEvent('open-menu', { source: 'unknown', id })
app.addOpenMenu(id) app.addOpenMenu(id)
} }
@ -52,7 +52,7 @@ export function useMenuIsOpen(id: string, cb?: (isOpen: boolean) => void) {
// Close menu and all submenus when the parent is closed // Close menu and all submenus when the parent is closed
app.openMenus.forEach((menuId) => { app.openMenus.forEach((menuId) => {
if (menuId.startsWith(id)) { if (menuId.startsWith(id)) {
trackEvent('menu', 'close-menu', { id }) trackEvent('close-menu', { source: 'unknown', id })
app.deleteOpenMenu(menuId) app.deleteOpenMenu(menuId)
} }
}) })

View file

@ -3,7 +3,7 @@ import * as React from 'react'
import { EmbedDialog } from '../components/EmbedDialog' import { EmbedDialog } from '../components/EmbedDialog'
import { TLUiIconType } from '../icon-types' import { TLUiIconType } from '../icon-types'
import { useDialogs } from './useDialogsProvider' import { useDialogs } from './useDialogsProvider'
import { useEvents } from './useEventsProvider' import { TLUiEventSource, useEvents } from './useEventsProvider'
import { useInsertMedia } from './useInsertMedia' import { useInsertMedia } from './useInsertMedia'
import { TLTranslationKey } from './useTranslation/TLTranslationKey' import { TLTranslationKey } from './useTranslation/TLTranslationKey'
@ -13,7 +13,7 @@ export interface ToolItem {
label: TLTranslationKey label: TLTranslationKey
shortcutsLabel?: TLTranslationKey shortcutsLabel?: TLTranslationKey
icon: TLUiIconType icon: TLUiIconType
onSelect: () => void onSelect: (source: TLUiEventSource) => void
kbd?: string kbd?: string
readonlyOk: boolean readonlyOk: boolean
meta?: { meta?: {
@ -53,9 +53,9 @@ export function ToolsProvider({ overrides, children }: ToolsProviderProps) {
icon: 'tool-pointer', icon: 'tool-pointer',
kbd: 'v', kbd: 'v',
readonlyOk: true, readonlyOk: true,
onSelect() { onSelect(source) {
app.setSelectedTool('select') app.setSelectedTool('select')
trackEvent('toolbar', 'select-tool', { id: 'select' }) trackEvent('select-tool', { source, id: 'select' })
}, },
}, },
{ {
@ -64,9 +64,9 @@ export function ToolsProvider({ overrides, children }: ToolsProviderProps) {
icon: 'tool-hand', icon: 'tool-hand',
kbd: 'h', kbd: 'h',
readonlyOk: true, readonlyOk: true,
onSelect() { onSelect(source) {
app.setSelectedTool('hand') app.setSelectedTool('hand')
trackEvent('toolbar', 'select-tool', { id: 'hand' }) trackEvent('select-tool', { source, id: 'hand' })
}, },
}, },
{ {
@ -75,9 +75,9 @@ export function ToolsProvider({ overrides, children }: ToolsProviderProps) {
icon: 'tool-eraser', icon: 'tool-eraser',
kbd: 'e', kbd: 'e',
readonlyOk: true, readonlyOk: true,
onSelect() { onSelect(source) {
app.setSelectedTool('eraser') app.setSelectedTool('eraser')
trackEvent('toolbar', 'select-tool', { id: 'eraser' }) trackEvent('select-tool', { source, id: 'eraser' })
}, },
}, },
{ {
@ -86,9 +86,9 @@ export function ToolsProvider({ overrides, children }: ToolsProviderProps) {
readonlyOk: true, readonlyOk: true,
icon: 'tool-pencil', icon: 'tool-pencil',
kbd: 'd,b,x', kbd: 'd,b,x',
onSelect() { onSelect(source) {
app.setSelectedTool('draw') app.setSelectedTool('draw')
trackEvent('toolbar', 'select-tool', { id: 'draw' }) trackEvent('select-tool', { source, id: 'draw' })
}, },
}, },
...[...TL_GEO_TYPES].map((id) => ({ ...[...TL_GEO_TYPES].map((id) => ({
@ -100,14 +100,14 @@ export function ToolsProvider({ overrides, children }: ToolsProviderProps) {
}, },
kbd: id === 'rectangle' ? 'r' : id === 'ellipse' ? 'o' : undefined, kbd: id === 'rectangle' ? 'r' : id === 'ellipse' ? 'o' : undefined,
icon: ('geo-' + id) as TLUiIconType, icon: ('geo-' + id) as TLUiIconType,
onSelect() { onSelect(source: TLUiEventSource) {
app.batch(() => { app.batch(() => {
app.updateInstanceState( app.updateInstanceState(
{ propsForNextShape: { ...app.instanceState.propsForNextShape, geo: id } }, { propsForNextShape: { ...app.instanceState.propsForNextShape, geo: id } },
true true
) )
app.setSelectedTool('geo') app.setSelectedTool('geo')
trackEvent('toolbar', 'select-tool', { id: `geo-${id}` }) trackEvent('select-tool', { source, id: `geo-${id}` })
}) })
}, },
})), })),
@ -117,9 +117,9 @@ export function ToolsProvider({ overrides, children }: ToolsProviderProps) {
readonlyOk: true, readonlyOk: true,
icon: 'tool-arrow', icon: 'tool-arrow',
kbd: 'a', kbd: 'a',
onSelect() { onSelect(source) {
app.setSelectedTool('arrow') app.setSelectedTool('arrow')
trackEvent('toolbar', 'select-tool', { id: 'arrow' }) trackEvent('select-tool', { source, id: 'arrow' })
}, },
}, },
{ {
@ -128,9 +128,9 @@ export function ToolsProvider({ overrides, children }: ToolsProviderProps) {
readonlyOk: true, readonlyOk: true,
icon: 'tool-line', icon: 'tool-line',
kbd: 'l', kbd: 'l',
onSelect() { onSelect(source) {
app.setSelectedTool('line') app.setSelectedTool('line')
trackEvent('toolbar', 'select-tool', { id: 'line' }) trackEvent('select-tool', { source, id: 'line' })
}, },
}, },
{ {
@ -139,9 +139,9 @@ export function ToolsProvider({ overrides, children }: ToolsProviderProps) {
readonlyOk: true, readonlyOk: true,
icon: 'tool-frame', icon: 'tool-frame',
kbd: 'f', kbd: 'f',
onSelect() { onSelect(source) {
app.setSelectedTool('frame') app.setSelectedTool('frame')
trackEvent('toolbar', 'select-tool', { id: 'frame' }) trackEvent('select-tool', { source, id: 'frame' })
}, },
}, },
{ {
@ -150,9 +150,9 @@ export function ToolsProvider({ overrides, children }: ToolsProviderProps) {
readonlyOk: true, readonlyOk: true,
icon: 'tool-text', icon: 'tool-text',
kbd: 't', kbd: 't',
onSelect() { onSelect(source) {
app.setSelectedTool('text') app.setSelectedTool('text')
trackEvent('toolbar', 'select-tool', { id: 'text' }) trackEvent('select-tool', { source, id: 'text' })
}, },
}, },
{ {
@ -161,9 +161,9 @@ export function ToolsProvider({ overrides, children }: ToolsProviderProps) {
readonlyOk: true, readonlyOk: true,
icon: 'tool-media', icon: 'tool-media',
kbd: '$u', kbd: '$u',
onSelect() { onSelect(source) {
insertMedia() insertMedia()
trackEvent('toolbar', 'select-tool', { id: 'media' }) trackEvent('select-tool', { source, id: 'media' })
}, },
}, },
{ {
@ -172,9 +172,9 @@ export function ToolsProvider({ overrides, children }: ToolsProviderProps) {
readonlyOk: true, readonlyOk: true,
icon: 'tool-note', icon: 'tool-note',
kbd: 'n', kbd: 'n',
onSelect() { onSelect(source) {
app.setSelectedTool('note') app.setSelectedTool('note')
trackEvent('toolbar', 'select-tool', { id: 'note' }) trackEvent('select-tool', { source, id: 'note' })
}, },
}, },
{ {
@ -182,9 +182,9 @@ export function ToolsProvider({ overrides, children }: ToolsProviderProps) {
label: 'tool.embed', label: 'tool.embed',
readonlyOk: true, readonlyOk: true,
icon: 'tool-embed', icon: 'tool-embed',
onSelect() { onSelect(source) {
addDialog({ component: EmbedDialog }) addDialog({ component: EmbedDialog })
trackEvent('toolbar', 'select-tool', { id: 'embed' }) trackEvent('select-tool', { source, id: 'embed' })
}, },
}, },
]) ])