[improvement] rename onEvent to onUiEvent (#1358)
This PR: - renames `onEvent` to `onUiEvent` - adds docs for `onUiEvent` to the docs site - splits the `EventsExample` into `UiEventsExample` and `StoreEventsExample` ### Change Type - [x] `major` — Breaking Change ### Release Notes - [docs] Adds docs for ui events - [tldraw] Renames `onEvent` to `onUiEvent`
This commit is contained in:
parent
8b6b244194
commit
9ccd0f480f
7 changed files with 84 additions and 19 deletions
|
@ -15,6 +15,24 @@ keywords:
|
||||||
- styles
|
- styles
|
||||||
---
|
---
|
||||||
|
|
||||||
Coming soon.
|
## Events
|
||||||
|
|
||||||
|
The `<Tldraw>` 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 (
|
||||||
|
<Tldraw onUiEvent={handleEvent}/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
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.
|
See the [tldraw repository](https://github.com/tldraw/tldraw) for an example of how to customize tldraw's user interface.
|
||||||
|
|
41
apps/examples/src/12-ui-events/UiEventsExample.tsx
Normal file
41
apps/examples/src/12-ui-events/UiEventsExample.tsx
Normal file
|
@ -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<string[]>([])
|
||||||
|
|
||||||
|
const handleUiEvent = useCallback<TLUiEventHandler>((name, data) => {
|
||||||
|
setUiEvents((events) => [`${name} ${JSON.stringify(data)}`, ...events])
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex' }}>
|
||||||
|
<div style={{ width: '60vw', height: '100vh' }}>
|
||||||
|
<Tldraw autoFocus onUiEvent={handleUiEvent} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '40vw',
|
||||||
|
height: '100vh',
|
||||||
|
padding: 8,
|
||||||
|
background: '#eee',
|
||||||
|
border: 'none',
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
fontSize: 12,
|
||||||
|
borderLeft: 'solid 2px #333',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column-reverse',
|
||||||
|
overflow: 'auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{uiEvents.map((t, i) => (
|
||||||
|
<div key={i}>{t}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,27 +1,22 @@
|
||||||
import { App, TLEventMapHandler, Tldraw } from '@tldraw/tldraw'
|
import { App, TLEventMapHandler, Tldraw } from '@tldraw/tldraw'
|
||||||
import '@tldraw/tldraw/editor.css'
|
import '@tldraw/tldraw/editor.css'
|
||||||
import '@tldraw/tldraw/ui.css'
|
import '@tldraw/tldraw/ui.css'
|
||||||
import { TLUiEventHandler } from '@tldraw/ui/src/lib/hooks/useEventsProvider'
|
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
|
|
||||||
export default function Example() {
|
export default function StoreEventsExample() {
|
||||||
const [app, setApp] = useState<App>()
|
const [app, setApp] = useState<App>()
|
||||||
|
|
||||||
const setAppToState = useCallback((app: App) => {
|
const setAppToState = useCallback((app: App) => {
|
||||||
setApp(app)
|
setApp(app)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const [uiEvents, setUiEvents] = useState<string[]>([])
|
const [storeEvents, setStoreEvents] = useState<string[]>([])
|
||||||
|
|
||||||
const handleEvent = useCallback<TLUiEventHandler>((name, data) => {
|
|
||||||
setUiEvents((events) => [`${name} ${JSON.stringify(data)}`, ...events])
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!app) return
|
if (!app) return
|
||||||
|
|
||||||
function logChangeEvent(eventName: string) {
|
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
|
// This is the fire hose, it will be called at the end of every transaction
|
||||||
|
@ -64,7 +59,7 @@ export default function Example() {
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex' }}>
|
<div style={{ display: 'flex' }}>
|
||||||
<div style={{ width: '60vw', height: '100vh' }}>
|
<div style={{ width: '60vw', height: '100vh' }}>
|
||||||
<Tldraw autoFocus onMount={setAppToState} onEvent={handleEvent} />
|
<Tldraw autoFocus onMount={setAppToState} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
|
@ -82,7 +77,7 @@ export default function Example() {
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{uiEvents.map((t, i) => (
|
{storeEvents.map((t, i) => (
|
||||||
<div key={i}>{t}</div>
|
<div key={i}>{t}</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
|
@ -11,7 +11,8 @@ import { RouterProvider, createBrowserRouter } from 'react-router-dom'
|
||||||
import ExampleBasic from './1-basic/BasicExample'
|
import ExampleBasic from './1-basic/BasicExample'
|
||||||
import CustomComponentsExample from './10-custom-components/CustomComponentsExample'
|
import CustomComponentsExample from './10-custom-components/CustomComponentsExample'
|
||||||
import UserPresenceExample from './11-user-presence/UserPresenceExample'
|
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 ExampleApi from './2-api/APIExample'
|
||||||
import CustomConfigExample from './3-custom-config/CustomConfigExample'
|
import CustomConfigExample from './3-custom-config/CustomConfigExample'
|
||||||
import CustomUiExample from './4-custom-ui/CustomUiExample'
|
import CustomUiExample from './4-custom-ui/CustomUiExample'
|
||||||
|
@ -73,8 +74,12 @@ export const allExamples: Example[] = [
|
||||||
element: <CustomComponentsExample />,
|
element: <CustomComponentsExample />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/events',
|
path: '/ui-events',
|
||||||
element: <EventsExample />,
|
element: <UiEventsExample />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/store-events',
|
||||||
|
element: <StoreEventsExample />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/user-presence',
|
path: '/user-presence',
|
||||||
|
|
|
@ -636,7 +636,7 @@ export const TldrawUi: React_2.NamedExoticComponent<{
|
||||||
export const TldrawUiContent: React_2.NamedExoticComponent<TldrawUiContentProps>;
|
export const TldrawUiContent: React_2.NamedExoticComponent<TldrawUiContentProps>;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export function TldrawUiContextProvider({ overrides, assetUrls, onEvent, children, }: TldrawUiContextProviderProps): JSX.Element;
|
export function TldrawUiContextProvider({ overrides, assetUrls, onUiEvent, children, }: TldrawUiContextProviderProps): JSX.Element;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export interface TldrawUiContextProviderProps {
|
export interface TldrawUiContextProviderProps {
|
||||||
|
@ -645,7 +645,7 @@ export interface TldrawUiContextProviderProps {
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
children?: any;
|
children?: any;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
onEvent?: TLUiEventHandler;
|
onUiEvent?: TLUiEventHandler;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
overrides?: TldrawUiOverrides | TldrawUiOverrides[];
|
overrides?: TldrawUiOverrides | TldrawUiOverrides[];
|
||||||
}
|
}
|
||||||
|
@ -728,6 +728,11 @@ export type TLTranslationMessages = Record<TLTranslationKey, string>;
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export type TLTranslations = TLTranslation[];
|
export type TLTranslations = TLTranslation[];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
export type TLUiEventHandler<T extends keyof TLUiEventMap = keyof TLUiEventMap> = (name: T, data: Join<{
|
||||||
|
source: TLUiEventSource;
|
||||||
|
}, TLUiEventMap[T]>) => void;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @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';
|
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';
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,7 @@ export {
|
||||||
type DialogsProviderProps,
|
type DialogsProviderProps,
|
||||||
type TLDialog,
|
type TLDialog,
|
||||||
} from './lib/hooks/useDialogsProvider'
|
} from './lib/hooks/useDialogsProvider'
|
||||||
|
export { type TLUiEventHandler } from './lib/hooks/useEventsProvider'
|
||||||
export { useExportAs } from './lib/hooks/useExportAs'
|
export { useExportAs } from './lib/hooks/useExportAs'
|
||||||
export {
|
export {
|
||||||
HelpMenuSchemaContext,
|
HelpMenuSchemaContext,
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { TldrawUiOverrides, useMergedOverrides, useMergedTranslationOverrides }
|
||||||
export interface TldrawUiContextProviderProps {
|
export interface TldrawUiContextProviderProps {
|
||||||
assetUrls?: UiAssetUrls
|
assetUrls?: UiAssetUrls
|
||||||
overrides?: TldrawUiOverrides | TldrawUiOverrides[]
|
overrides?: TldrawUiOverrides | TldrawUiOverrides[]
|
||||||
onEvent?: TLUiEventHandler
|
onUiEvent?: TLUiEventHandler
|
||||||
children?: any
|
children?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ export interface TldrawUiContextProviderProps {
|
||||||
export function TldrawUiContextProvider({
|
export function TldrawUiContextProvider({
|
||||||
overrides,
|
overrides,
|
||||||
assetUrls,
|
assetUrls,
|
||||||
onEvent,
|
onUiEvent,
|
||||||
children,
|
children,
|
||||||
}: TldrawUiContextProviderProps) {
|
}: TldrawUiContextProviderProps) {
|
||||||
return (
|
return (
|
||||||
|
@ -36,7 +36,7 @@ export function TldrawUiContextProvider({
|
||||||
<ToastsProvider>
|
<ToastsProvider>
|
||||||
<DialogsProvider>
|
<DialogsProvider>
|
||||||
<BreakPointProvider>
|
<BreakPointProvider>
|
||||||
<EventsProvider onEvent={onEvent}>
|
<EventsProvider onEvent={onUiEvent}>
|
||||||
<InternalProviders overrides={overrides}>{children}</InternalProviders>
|
<InternalProviders overrides={overrides}>{children}</InternalProviders>
|
||||||
</EventsProvider>
|
</EventsProvider>
|
||||||
</BreakPointProvider>
|
</BreakPointProvider>
|
||||||
|
|
Loading…
Reference in a new issue