[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:
parent
a722e3e6f0
commit
03595bc88d
13 changed files with 162 additions and 161 deletions
|
@ -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(() => {
|
||||||
|
|
|
@ -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()
|
||||||
})
|
})
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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')
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
|
@ -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()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 []
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -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' })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
Loading…
Reference in a new issue