diff --git a/apps/docs/content/docs/user-interface.mdx b/apps/docs/content/docs/user-interface.mdx index 590bc6694..850775907 100644 --- a/apps/docs/content/docs/user-interface.mdx +++ b/apps/docs/content/docs/user-interface.mdx @@ -15,6 +15,24 @@ keywords: - styles --- -Coming soon. +## Events + +The `` component has a prop, `onUiEvent`, that the user interface will call when certain events occur. + +```tsx +function Example() { + function handleEvent(name, data) { + // do something with the event + } + + return ( + + ) +} +``` + +The `onUiEvent` callback is called with the name of the event as a string and an object with information about the event's source (e.g. `menu` or `context-menu`) and possibly other data specific to each event, such as the direction in an `align-shapes` event. + +Note that `onUiEvent` is only called when interacting with the user interface. It is not called when running commands manually against the app, e.g. `app.alignShapes()` will not call `onUiEvent`. See the [tldraw repository](https://github.com/tldraw/tldraw) for an example of how to customize tldraw's user interface. diff --git a/apps/examples/src/12-ui-events/UiEventsExample.tsx b/apps/examples/src/12-ui-events/UiEventsExample.tsx new file mode 100644 index 000000000..fed4299e8 --- /dev/null +++ b/apps/examples/src/12-ui-events/UiEventsExample.tsx @@ -0,0 +1,41 @@ +import { TLUiEventHandler, Tldraw } from '@tldraw/tldraw' +import '@tldraw/tldraw/editor.css' +import '@tldraw/tldraw/ui.css' +import { useCallback, useState } from 'react' + +export default function UiEventsExample() { + const [uiEvents, setUiEvents] = useState([]) + + const handleUiEvent = useCallback((name, data) => { + setUiEvents((events) => [`${name} ${JSON.stringify(data)}`, ...events]) + }, []) + + return ( +
+
+ +
+
+
+ {uiEvents.map((t, i) => ( +
{t}
+ ))} +
+
+
+ ) +} diff --git a/apps/examples/src/12-events/EventsExample.tsx b/apps/examples/src/13-store/StoreEventsExample.tsx similarity index 80% rename from apps/examples/src/12-events/EventsExample.tsx rename to apps/examples/src/13-store/StoreEventsExample.tsx index a6313e270..eb5d372b1 100644 --- a/apps/examples/src/12-events/EventsExample.tsx +++ b/apps/examples/src/13-store/StoreEventsExample.tsx @@ -1,27 +1,22 @@ import { App, TLEventMapHandler, Tldraw } from '@tldraw/tldraw' import '@tldraw/tldraw/editor.css' import '@tldraw/tldraw/ui.css' -import { TLUiEventHandler } from '@tldraw/ui/src/lib/hooks/useEventsProvider' import { useCallback, useEffect, useState } from 'react' -export default function Example() { +export default function StoreEventsExample() { const [app, setApp] = useState() const setAppToState = useCallback((app: App) => { setApp(app) }, []) - const [uiEvents, setUiEvents] = useState([]) - - const handleEvent = useCallback((name, data) => { - setUiEvents((events) => [`${name} ${JSON.stringify(data)}`, ...events]) - }, []) + const [storeEvents, setStoreEvents] = useState([]) useEffect(() => { if (!app) return function logChangeEvent(eventName: string) { - setUiEvents((events) => [eventName, ...events]) + setStoreEvents((events) => [eventName, ...events]) } // This is the fire hose, it will be called at the end of every transaction @@ -64,7 +59,7 @@ export default function Example() { return (
- +
- {uiEvents.map((t, i) => ( + {storeEvents.map((t, i) => (
{t}
))}
diff --git a/apps/examples/src/index.tsx b/apps/examples/src/index.tsx index fbba8211e..795e1a172 100644 --- a/apps/examples/src/index.tsx +++ b/apps/examples/src/index.tsx @@ -11,7 +11,8 @@ import { RouterProvider, createBrowserRouter } from 'react-router-dom' import ExampleBasic from './1-basic/BasicExample' import CustomComponentsExample from './10-custom-components/CustomComponentsExample' import UserPresenceExample from './11-user-presence/UserPresenceExample' -import EventsExample from './12-events/EventsExample' +import UiEventsExample from './12-ui-events/UiEventsExample' +import StoreEventsExample from './13-store/StoreEventsExample' import ExampleApi from './2-api/APIExample' import CustomConfigExample from './3-custom-config/CustomConfigExample' import CustomUiExample from './4-custom-ui/CustomUiExample' @@ -73,8 +74,12 @@ export const allExamples: Example[] = [ element: , }, { - path: '/events', - element: , + path: '/ui-events', + element: , + }, + { + path: '/store-events', + element: , }, { path: '/user-presence', diff --git a/packages/ui/api-report.md b/packages/ui/api-report.md index c837578a7..bf95a9231 100644 --- a/packages/ui/api-report.md +++ b/packages/ui/api-report.md @@ -636,7 +636,7 @@ export const TldrawUi: React_2.NamedExoticComponent<{ export const TldrawUiContent: React_2.NamedExoticComponent; // @public (undocumented) -export function TldrawUiContextProvider({ overrides, assetUrls, onEvent, children, }: TldrawUiContextProviderProps): JSX.Element; +export function TldrawUiContextProvider({ overrides, assetUrls, onUiEvent, children, }: TldrawUiContextProviderProps): JSX.Element; // @public (undocumented) export interface TldrawUiContextProviderProps { @@ -645,7 +645,7 @@ export interface TldrawUiContextProviderProps { // (undocumented) children?: any; // (undocumented) - onEvent?: TLUiEventHandler; + onUiEvent?: TLUiEventHandler; // (undocumented) overrides?: TldrawUiOverrides | TldrawUiOverrides[]; } @@ -728,6 +728,11 @@ export type TLTranslationMessages = Record; // @public (undocumented) export type TLTranslations = TLTranslation[]; +// @public (undocumented) +export type TLUiEventHandler = (name: T, data: Join<{ + source: TLUiEventSource; +}, TLUiEventMap[T]>) => void; + // @public (undocumented) export type TLUiIconType = 'align-bottom-center' | 'align-bottom-left' | 'align-bottom-right' | 'align-bottom' | 'align-center-center' | 'align-center-horizontal' | 'align-center-left' | 'align-center-right' | 'align-center-vertical' | 'align-left' | 'align-right' | 'align-top-center' | 'align-top-left' | 'align-top-right' | 'align-top' | 'arrow-left' | 'arrowhead-arrow' | 'arrowhead-bar' | 'arrowhead-diamond' | 'arrowhead-dot' | 'arrowhead-none' | 'arrowhead-square' | 'arrowhead-triangle-inverted' | 'arrowhead-triangle' | 'aspect-ratio' | 'avatar' | 'blob' | 'bring-forward' | 'bring-to-front' | 'check' | 'checkbox-checked' | 'checkbox-empty' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'chevrons-ne' | 'chevrons-sw' | 'clipboard-copy' | 'code' | 'collab' | 'color' | 'comment' | 'cross-2' | 'cross' | 'dash-dashed' | 'dash-dotted' | 'dash-draw' | 'dash-solid' | 'discord' | 'distribute-horizontal' | 'distribute-vertical' | 'dot' | 'dots-horizontal' | 'dots-vertical' | 'drag-handle-dots' | 'duplicate' | 'edit' | 'external-link' | 'file' | 'fill-none' | 'fill-pattern' | 'fill-semi' | 'fill-solid' | 'follow' | 'following' | 'font-draw' | 'font-mono' | 'font-sans' | 'font-serif' | 'geo-arrow-down' | 'geo-arrow-left' | 'geo-arrow-right' | 'geo-arrow-up' | 'geo-check-box' | 'geo-diamond' | 'geo-ellipse' | 'geo-hexagon' | 'geo-octagon' | 'geo-oval' | 'geo-pentagon' | 'geo-rectangle' | 'geo-rhombus-2' | 'geo-rhombus' | 'geo-star' | 'geo-trapezoid' | 'geo-triangle' | 'geo-x-box' | 'github' | 'group' | 'hidden' | 'image' | 'info-circle' | 'leading' | 'link' | 'lock-small' | 'lock' | 'menu' | 'minus' | 'mixed' | 'pack' | 'page' | 'plus' | 'question-mark-circle' | 'question-mark' | 'redo' | 'reset-zoom' | 'rotate-ccw' | 'rotate-cw' | 'ruler' | 'search' | 'send-backward' | 'send-to-back' | 'settings-horizontal' | 'settings-vertical-1' | 'settings-vertical' | 'share-1' | 'share-2' | 'size-extra-large' | 'size-large' | 'size-medium' | 'size-small' | 'spline-cubic' | 'spline-line' | 'stack-horizontal' | 'stack-vertical' | 'stretch-horizontal' | 'stretch-vertical' | 'text-align-center' | 'text-align-justify' | 'text-align-left' | 'text-align-right' | 'tool-arrow' | 'tool-embed' | 'tool-eraser' | 'tool-frame' | 'tool-hand' | 'tool-highlighter' | 'tool-line' | 'tool-media' | 'tool-note' | 'tool-pencil' | 'tool-pointer' | 'tool-text' | 'trash' | 'triangle-down' | 'triangle-up' | 'twitter' | 'undo' | 'ungroup' | 'unlock-small' | 'unlock' | 'visible' | 'warning-triangle' | 'zoom-in' | 'zoom-out'; diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 666316097..5d272c970 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -79,6 +79,7 @@ export { type DialogsProviderProps, type TLDialog, } from './lib/hooks/useDialogsProvider' +export { type TLUiEventHandler } from './lib/hooks/useEventsProvider' export { useExportAs } from './lib/hooks/useExportAs' export { HelpMenuSchemaContext, diff --git a/packages/ui/src/lib/TldrawUiContextProvider.tsx b/packages/ui/src/lib/TldrawUiContextProvider.tsx index 488ef9d86..deceaf043 100644 --- a/packages/ui/src/lib/TldrawUiContextProvider.tsx +++ b/packages/ui/src/lib/TldrawUiContextProvider.tsx @@ -19,7 +19,7 @@ import { TldrawUiOverrides, useMergedOverrides, useMergedTranslationOverrides } export interface TldrawUiContextProviderProps { assetUrls?: UiAssetUrls overrides?: TldrawUiOverrides | TldrawUiOverrides[] - onEvent?: TLUiEventHandler + onUiEvent?: TLUiEventHandler children?: any } @@ -27,7 +27,7 @@ export interface TldrawUiContextProviderProps { export function TldrawUiContextProvider({ overrides, assetUrls, - onEvent, + onUiEvent, children, }: TldrawUiContextProviderProps) { return ( @@ -36,7 +36,7 @@ export function TldrawUiContextProvider({ - + {children}