examples: clean up Canvas/Store events and make UiEvents have code snippets (#2770)
Fixes https://linear.app/tldraw/issue/TLD-2059 <img width="1220" alt="Screenshot 2024-02-07 at 12 38 09" src="https://github.com/tldraw/tldraw/assets/469604/15dc4298-670d-489b-8bee-810d34a0fbae"> ### Change Type - [x] `internal` — Any other changes that don't affect the published package[^2] ### Release Notes - Examples: add an interactive example that shows code snippets for the SDK.
This commit is contained in:
parent
e2a03abf5c
commit
f16e597761
13 changed files with 273 additions and 22 deletions
|
@ -40,12 +40,14 @@
|
|||
"@vercel/analytics": "^1.1.1",
|
||||
"classnames": "^2.3.2",
|
||||
"lazyrepo": "0.0.0-alpha.27",
|
||||
"lodash": "^4.17.21",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.17.0",
|
||||
"vite": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash": "^4.14.188",
|
||||
"@vitejs/plugin-react": "^4.2.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"remark": "^15.0.1",
|
||||
|
|
|
@ -64,6 +64,11 @@ export function ExamplePage({
|
|||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="example__sidebar__header-links">
|
||||
<a className="example__sidebar__header-link" href="/develop">
|
||||
Develop
|
||||
</a>
|
||||
</div>
|
||||
<ul className="example__sidebar__categories scroll-light">
|
||||
{categories.map((currentCategory) => (
|
||||
<li key={currentCategory} className="example__sidebar__category">
|
||||
|
|
|
@ -5,10 +5,23 @@ import { useCallback, useState } from 'react'
|
|||
// There's a guide at the bottom of this file!
|
||||
|
||||
export default function CanvasEventsExample() {
|
||||
const [events, setEvents] = useState<string[]>([])
|
||||
const [events, setEvents] = useState<any[]>([])
|
||||
|
||||
const handleEvent = useCallback((data: TLEventInfo) => {
|
||||
setEvents((events) => [JSON.stringify(data, null, '\t'), ...events.slice(0, 100)])
|
||||
setEvents((events) => {
|
||||
const newEvents = events.slice(0, 100)
|
||||
if (
|
||||
newEvents[newEvents.length - 1] &&
|
||||
newEvents[newEvents.length - 1].type === 'pointer' &&
|
||||
data.type === 'pointer' &&
|
||||
data.target === 'canvas'
|
||||
) {
|
||||
newEvents[newEvents.length - 1] = data
|
||||
} else {
|
||||
newEvents.unshift(data)
|
||||
}
|
||||
return newEvents
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
|
@ -36,9 +49,7 @@ export default function CanvasEventsExample() {
|
|||
whiteSpace: 'pre-wrap',
|
||||
}}
|
||||
>
|
||||
{events.map((t, i) => (
|
||||
<div key={i}>{t}</div>
|
||||
))}
|
||||
<div>{JSON.stringify(events, undefined, 2)}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Editor, TLEventMapHandler, Tldraw } from '@tldraw/tldraw'
|
||||
import '@tldraw/tldraw/tldraw.css'
|
||||
import _ from 'lodash'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
// There's a guide at the bottom of this file!
|
||||
|
@ -17,7 +18,7 @@ export default function StoreEventsExample() {
|
|||
if (!editor) return
|
||||
|
||||
function logChangeEvent(eventName: string) {
|
||||
setStoreEvents((events) => [eventName, ...events])
|
||||
setStoreEvents((events) => [...events, eventName])
|
||||
}
|
||||
|
||||
//[1]
|
||||
|
@ -25,7 +26,7 @@ export default function StoreEventsExample() {
|
|||
// Added
|
||||
for (const record of Object.values(change.changes.added)) {
|
||||
if (record.typeName === 'shape') {
|
||||
logChangeEvent(`created shape (${record.type})`)
|
||||
logChangeEvent(`created shape (${record.type})\n`)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,13 +38,29 @@ export default function StoreEventsExample() {
|
|||
from.currentPageId !== to.currentPageId
|
||||
) {
|
||||
logChangeEvent(`changed page (${from.currentPageId}, ${to.currentPageId})`)
|
||||
} else if (from.id.startsWith('shape') && to.id.startsWith('shape')) {
|
||||
let diff = _.reduce(
|
||||
from,
|
||||
(result: any[], value, key: string) =>
|
||||
_.isEqual(value, (to as any)[key]) ? result : result.concat([key, value]),
|
||||
[]
|
||||
)
|
||||
if (diff?.[0] === 'props') {
|
||||
diff = _.reduce(
|
||||
(from as any).props,
|
||||
(result: any[], value, key) =>
|
||||
_.isEqual(value, (to as any).props[key]) ? result : result.concat([key, value]),
|
||||
[]
|
||||
)
|
||||
}
|
||||
logChangeEvent(`updated shape (${JSON.stringify(diff)})\n`)
|
||||
}
|
||||
}
|
||||
|
||||
// Removed
|
||||
for (const record of Object.values(change.changes.removed)) {
|
||||
if (record.typeName === 'shape') {
|
||||
logChangeEvent(`deleted shape (${record.type})`)
|
||||
logChangeEvent(`deleted shape (${record.type})\n`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -76,9 +93,7 @@ export default function StoreEventsExample() {
|
|||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
{storeEvents.map((t, i) => (
|
||||
<div key={i}>{t}</div>
|
||||
))}
|
||||
<pre>{storeEvents}</pre>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
import { TLUiEventHandler, Tldraw } from '@tldraw/tldraw'
|
||||
import '@tldraw/tldraw/tldraw.css'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { Fragment, useCallback, useState } from 'react'
|
||||
import { getCodeSnippet } from './codeSnippets'
|
||||
|
||||
// There's a guide at the bottom of this file!
|
||||
|
||||
export default function UiEventsExample() {
|
||||
const [uiEvents, setUiEvents] = useState<string[]>([])
|
||||
|
||||
const handleUiEvent = useCallback<TLUiEventHandler>((name, data) => {
|
||||
setUiEvents((events) => [`${name} ${JSON.stringify(data)}`, ...events])
|
||||
const handleUiEvent = useCallback<TLUiEventHandler>((name, data: any) => {
|
||||
const codeSnippet = getCodeSnippet(name, data)
|
||||
setUiEvents((events) => [
|
||||
`event: ${name} ${JSON.stringify(data)}${codeSnippet && `\ncode: ${codeSnippet}`}`,
|
||||
...events,
|
||||
])
|
||||
}, [])
|
||||
|
||||
return (
|
||||
|
@ -32,7 +37,11 @@ export default function UiEventsExample() {
|
|||
}}
|
||||
>
|
||||
{uiEvents.map((t, i) => (
|
||||
<div key={i}>{t}</div>
|
||||
<Fragment key={i}>
|
||||
<pre style={{ borderBottom: '1px solid #000', marginBottom: 0, paddingBottom: '12px' }}>
|
||||
{t}
|
||||
</pre>
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -45,6 +54,9 @@ grouping shapes, zooming etc. Events are included even if they are triggered by
|
|||
However, interactions with the style panel are not included. For a full list of events and sources,
|
||||
check out the TLUiEventSource and TLUiEventMap types.
|
||||
|
||||
It also shows the relevant code snippet for each event. This is useful for debugging and learning
|
||||
the tldraw SDK.
|
||||
|
||||
We can pass a handler function to the onUiEvent prop of the Tldraw component. This handler function
|
||||
will be called with the name of the event and the data associated with the event. We're going to
|
||||
display these events in a list on the right side of the screen.
|
||||
|
|
161
apps/examples/src/examples/ui-events/codeSnippets.ts
Normal file
161
apps/examples/src/examples/ui-events/codeSnippets.ts
Normal file
|
@ -0,0 +1,161 @@
|
|||
const STYLE_EVENT = {
|
||||
'tldraw:color': 'DefaultColorStyle',
|
||||
'tldraw:dash': 'DefaultDashStyle',
|
||||
'tldraw:fill': 'DefaultFillStyle',
|
||||
'tldraw:font': 'DefaultFontStyle',
|
||||
'tldraw:horizontalAlign': 'DefaultHorizontalAlignStyle',
|
||||
'tldraw:size': 'DefaultSizeStyle',
|
||||
'tldraw:verticalAlign': 'DefaultVerticalAlignStyle',
|
||||
'tldraw:geo': 'GeoShapeGeoStyle',
|
||||
}
|
||||
|
||||
const REORDER_EVENT = {
|
||||
toFront: 'bringToFront',
|
||||
forward: 'bringForward',
|
||||
backward: 'sendBackward',
|
||||
toBack: 'sendToBack',
|
||||
}
|
||||
|
||||
const SHAPES_META_EVENT = {
|
||||
'group-shapes': 'groupShapes',
|
||||
'ungroup-shapes': 'ungroupShapes',
|
||||
'delete-shapes': 'deleteShapes',
|
||||
}
|
||||
|
||||
const SHAPES_EVENT = {
|
||||
'distribute-shapes': 'distributeShapes',
|
||||
'align-shapes': 'alignShapes',
|
||||
'stretch-shapes': 'stretchShapes',
|
||||
'flip-shapes': 'flipShapes',
|
||||
}
|
||||
|
||||
const USER_PREFS_EVENT = {
|
||||
'toggle-snap-mode': 'isSnapMode',
|
||||
'toggle-dark-mode': 'isDarkMode',
|
||||
'toggle-reduce-motion': 'animationSpeed',
|
||||
'toggle-edge-scrolling': 'edgeScrollSpeed',
|
||||
}
|
||||
|
||||
const PREFS_EVENT = {
|
||||
'toggle-transparent': 'exportBackground',
|
||||
'toggle-tool-lock': 'isToolLocked',
|
||||
'toggle-focus-mode': 'isFocusMode',
|
||||
'toggle-grid-mode': 'isGridMode',
|
||||
'toggle-debug-mode': 'isDebugMode',
|
||||
}
|
||||
|
||||
const ZOOM_EVENT = {
|
||||
'zoom-in': 'zoomIn',
|
||||
'zoom-out': 'zoomOut',
|
||||
'reset-zoom': 'resetZoom',
|
||||
'zoom-to-fit': 'zoomToFit',
|
||||
'zoom-to-selection': 'zoomToSelection',
|
||||
'zoom-to-content': 'zoomToContent',
|
||||
}
|
||||
|
||||
export function getCodeSnippet(name: string, data: any) {
|
||||
let codeSnippet = ''
|
||||
|
||||
if (name === 'set-style') {
|
||||
if (data.id === 'opacity') {
|
||||
codeSnippet = `editor.setOpacityForNextShapes(${data.value});`
|
||||
} else {
|
||||
codeSnippet = `editor.setStyleForNextShapes(${
|
||||
STYLE_EVENT[data.id as keyof typeof STYLE_EVENT] ?? '?'
|
||||
}, '${data.value}');`
|
||||
}
|
||||
} else if (['rotate-ccw', 'rotate-cw'].includes(name)) {
|
||||
codeSnippet = 'editor.rotateShapesBy(editor.getSelectedShapeIds(), <number>)'
|
||||
} else if (name === 'edit-link') {
|
||||
codeSnippet =
|
||||
'editor.updateShapes([{ id: editor.getOnlySelectedShape().id, type: editor.getOnlySelectedShape().type, props: { url: <url> }, }, ])'
|
||||
} else if (name.startsWith('export-as')) {
|
||||
codeSnippet = `exportAs(editor.getSelectedShapeIds(), '${data.format}')`
|
||||
} else if (name.startsWith('copy-as')) {
|
||||
codeSnippet = `copyAs(editor.getSelectedShapeIds(), '${data.format}')`
|
||||
} else if (name === 'select-all-shapes') {
|
||||
codeSnippet = `editor.selectAll()`
|
||||
} else if (name === 'select-none-shapes') {
|
||||
codeSnippet = `editor.selectNone()`
|
||||
} else if (name === 'reorder-shapes') {
|
||||
codeSnippet = `editor.${
|
||||
REORDER_EVENT[data.operation as keyof typeof REORDER_EVENT] ?? '?'
|
||||
}(editor.getSelectedShapeIds())`
|
||||
} else if (['group-shapes', 'ungroup-shapes', 'delete-shapes'].includes(name)) {
|
||||
codeSnippet = `editor.${
|
||||
SHAPES_META_EVENT[name as keyof typeof SHAPES_META_EVENT] ?? '?'
|
||||
}(editor.getSelectedShapeIds())`
|
||||
} else if (name === 'stack-shapes') {
|
||||
codeSnippet = `editor.stackShapes(editor.getSelectedShapeIds(), '${data.operation}', 16)`
|
||||
} else if (name === 'pack-shapes') {
|
||||
codeSnippet = `editor.packShapes(editor.getSelectedShapeIds(), 16)`
|
||||
} else if (name === 'duplicate-shapes') {
|
||||
codeSnippet = `editor.duplicateShapes(editor.getSelectedShapeIds(), {x: <value>, y: <value>})`
|
||||
} else if (name.endsWith('-shapes')) {
|
||||
codeSnippet = `editor.${
|
||||
SHAPES_EVENT[name as keyof typeof SHAPES_EVENT] ?? '?'
|
||||
}(editor.getSelectedShapeIds(), '${data.operation}')`
|
||||
} else if (name === 'select-tool') {
|
||||
if (data.id === 'media') {
|
||||
codeSnippet = 'insertMedia()'
|
||||
} else if (data.id.startsWith('geo-')) {
|
||||
codeSnippet = `\n editor.updateInstanceState({
|
||||
stylesForNextShape: {
|
||||
...editor.getInstanceState().stylesForNextShape,
|
||||
[GeoShapeGeoStyle.id]: '${data.id.replace('geo-', '')}',
|
||||
},
|
||||
}, { ephemeral: true });
|
||||
editor.setCurrentTool('${data.id}')`
|
||||
} else {
|
||||
codeSnippet = `editor.setCurrentTool('${data.id}')`
|
||||
}
|
||||
} else if (name === 'print') {
|
||||
codeSnippet = 'printSelectionOrPages()'
|
||||
} else if (name === 'unlock-all') {
|
||||
codeSnippet = `\n const updates = [] as TLShapePartial[]
|
||||
for (const shape of editor.getCurrentPageShapes()) {
|
||||
if (shape.isLocked) {
|
||||
updates.push({ id: shape.id, type: shape.type, isLocked: false })
|
||||
}
|
||||
}
|
||||
if (updates.length > 0) {
|
||||
editor.updateShapes(updates)
|
||||
}`
|
||||
} else if (['undo', 'redo'].includes(name)) {
|
||||
codeSnippet = `editor.${name}()`
|
||||
} else if (['cut', 'copy'].includes(name)) {
|
||||
codeSnippet = `\n const { ${name} } = useMenuClipboardEvents();\n ${name}()`
|
||||
} else if (name === 'paste') {
|
||||
codeSnippet = `\n const { paste } = useMenuClipboardEvents();\n navigator.clipboard?.read().then((clipboardItems) => {\n paste(clipboardItems)\n })`
|
||||
} else if (name === 'stop-following') {
|
||||
codeSnippet = `editor.stopFollowingUser()`
|
||||
} else if (name === 'exit-pen-mode') {
|
||||
codeSnippet = `editor.updateInstanceState({ isPenMode: false })`
|
||||
} else if (name === 'remove-frame') {
|
||||
codeSnippet = `removeFrame(editor, editor.getSelectedShapes().map((shape) => shape.id))`
|
||||
} else if (name === 'fit-frame-to-content') {
|
||||
codeSnippet = `fitFrameToContent(editor, editor.getOnlySelectedShape().id)`
|
||||
} else if (name.startsWith('zoom-') || name === 'reset-zoom') {
|
||||
if (name === 'zoom-to-content') {
|
||||
codeSnippet = 'editor.zoomToContent()'
|
||||
} else {
|
||||
codeSnippet = `editor.${ZOOM_EVENT[name as keyof typeof ZOOM_EVENT]}(${
|
||||
name !== 'zoom-to-fit' && name !== 'zoom-to-selection'
|
||||
? 'editor.getViewportScreenCenter(), '
|
||||
: ''
|
||||
}{ duration: 320 })`
|
||||
}
|
||||
} else if (name.startsWith('toggle-')) {
|
||||
if (name === 'toggle-lock') {
|
||||
codeSnippet = `editor.toggleLock(editor.getSelectedShapeIds())`
|
||||
} else {
|
||||
const userPrefName = USER_PREFS_EVENT[name as keyof typeof USER_PREFS_EVENT]
|
||||
const prefName = PREFS_EVENT[name as keyof typeof PREFS_EVENT]
|
||||
codeSnippet = userPrefName
|
||||
? `editor.user.updateUserPreferences({ ${userPrefName}: <value> })`
|
||||
: `editor.updateInstanceState({ ${prefName}: !editor.getInstanceState().${prefName} })`
|
||||
}
|
||||
}
|
||||
|
||||
return codeSnippet
|
||||
}
|
|
@ -213,8 +213,9 @@ li.examples__sidebar__item {
|
|||
align-items: stretch;
|
||||
}
|
||||
|
||||
/* ----------------- Footer Buttons ----------------- */
|
||||
/* ----------------- Header/Footer Buttons ----------------- */
|
||||
|
||||
.example__sidebar__header-links,
|
||||
.example__sidebar__footer-links {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -225,6 +226,7 @@ li.examples__sidebar__item {
|
|||
border-top: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
.example__sidebar__header-link,
|
||||
.example__sidebar__footer-link {
|
||||
padding: 8px 8px;
|
||||
border-radius: 6px;
|
||||
|
@ -241,7 +243,7 @@ li.examples__sidebar__item {
|
|||
0px 1px 3px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.example__sidebar__footer-link > a {
|
||||
a.example__sidebar__header-link {
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
|
|
@ -1533,6 +1533,11 @@ export interface TLUiEventMap {
|
|||
id: string;
|
||||
};
|
||||
// (undocumented)
|
||||
'set-style': {
|
||||
id: string;
|
||||
value: number | string;
|
||||
};
|
||||
// (undocumented)
|
||||
'stack-shapes': {
|
||||
operation: 'horizontal' | 'vertical';
|
||||
};
|
||||
|
@ -1597,7 +1602,7 @@ export interface TLUiEventMap {
|
|||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLUiEventSource = 'actions-menu' | 'context-menu' | 'debug-panel' | 'dialog' | 'export-menu' | 'help-menu' | 'helper-buttons' | 'kbd' | 'menu' | 'navigation-zone' | 'page-menu' | 'people-menu' | 'quick-actions' | 'share-menu' | 'toolbar' | 'unknown' | 'zoom-menu';
|
||||
export type TLUiEventSource = 'actions-menu' | 'context-menu' | 'debug-panel' | 'dialog' | 'export-menu' | 'help-menu' | 'helper-buttons' | 'kbd' | 'menu' | 'navigation-zone' | 'page-menu' | 'people-menu' | 'quick-actions' | 'share-menu' | 'style-panel' | 'toolbar' | 'unknown' | 'zoom-menu';
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLUiHelpMenuSchemaContextType = TLUiMenuSchema;
|
||||
|
|
|
@ -17370,6 +17370,33 @@
|
|||
"endIndex": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "PropertySignature",
|
||||
"canonicalReference": "@tldraw/tldraw!TLUiEventMap#\"set-style\":member",
|
||||
"docComment": "",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "'set-style': "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "{\n id: string;\n value: number | string;\n }"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"isReadonly": false,
|
||||
"isOptional": false,
|
||||
"releaseTag": "Public",
|
||||
"name": "\"set-style\"",
|
||||
"propertyTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "PropertySignature",
|
||||
"canonicalReference": "@tldraw/tldraw!TLUiEventMap#\"stack-shapes\":member",
|
||||
|
@ -18167,7 +18194,7 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "'actions-menu' | 'context-menu' | 'debug-panel' | 'dialog' | 'export-menu' | 'help-menu' | 'helper-buttons' | 'kbd' | 'menu' | 'navigation-zone' | 'page-menu' | 'people-menu' | 'quick-actions' | 'share-menu' | 'toolbar' | 'unknown' | 'zoom-menu'"
|
||||
"text": "'actions-menu' | 'context-menu' | 'debug-panel' | 'dialog' | 'export-menu' | 'help-menu' | 'helper-buttons' | 'kbd' | 'menu' | 'navigation-zone' | 'page-menu' | 'people-menu' | 'quick-actions' | 'share-menu' | 'style-panel' | 'toolbar' | 'unknown' | 'zoom-menu'"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
|
|
|
@ -2,7 +2,7 @@ import { track, useEditor } from '@tldraw/editor'
|
|||
import { useActions } from '../hooks/useActions'
|
||||
import { Button } from './primitives/Button'
|
||||
|
||||
export const StopFollowing = track(function ExitPenMode() {
|
||||
export const StopFollowing = track(function StopFollowing() {
|
||||
const editor = useEditor()
|
||||
const actions = useActions()
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
useEditor,
|
||||
} from '@tldraw/editor'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useUiEvents } from '../../hooks/useEventsProvider'
|
||||
import { useRelevantStyles } from '../../hooks/useRevelantStyles'
|
||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||
import { Button } from '../primitives/Button'
|
||||
|
@ -73,6 +74,7 @@ export const StylePanel = function StylePanel({ isMobile }: StylePanelProps) {
|
|||
|
||||
function useStyleChangeCallback() {
|
||||
const editor = useEditor()
|
||||
const trackEvent = useUiEvents()
|
||||
|
||||
return React.useMemo(() => {
|
||||
return function handleStyleChange<T>(style: StyleProp<T>, value: T, squashing: boolean) {
|
||||
|
@ -83,8 +85,10 @@ function useStyleChangeCallback() {
|
|||
editor.setStyleForNextShapes(style, value, { squashing })
|
||||
editor.updateInstanceState({ isChangingStyle: true })
|
||||
})
|
||||
|
||||
trackEvent('set-style', { source: 'style-panel', id: style.id, value: value as string })
|
||||
}
|
||||
}, [editor])
|
||||
}, [editor, trackEvent])
|
||||
}
|
||||
|
||||
const tldrawSupportedOpacities = [0.1, 0.25, 0.5, 0.75, 1] as const
|
||||
|
@ -97,6 +101,7 @@ function CommonStylePickerSet({
|
|||
opacity: SharedStyle<number>
|
||||
}) {
|
||||
const editor = useEditor()
|
||||
const trackEvent = useUiEvents()
|
||||
const msg = useTranslation()
|
||||
|
||||
const handleValueChange = useStyleChangeCallback()
|
||||
|
@ -111,8 +116,10 @@ function CommonStylePickerSet({
|
|||
editor.setOpacityForNextShapes(item, { ephemeral })
|
||||
editor.updateInstanceState({ isChangingStyle: true })
|
||||
})
|
||||
|
||||
trackEvent('set-style', { source: 'style-panel', id: 'opacity', value })
|
||||
},
|
||||
[editor]
|
||||
[editor, trackEvent]
|
||||
)
|
||||
|
||||
const color = styles.get(DefaultColorStyle)
|
||||
|
|
|
@ -18,6 +18,7 @@ export type TLUiEventSource =
|
|||
| 'dialog'
|
||||
| 'help-menu'
|
||||
| 'helper-buttons'
|
||||
| 'style-panel'
|
||||
| 'unknown'
|
||||
|
||||
/** @public */
|
||||
|
@ -72,6 +73,7 @@ export interface TLUiEventMap {
|
|||
copy: null
|
||||
paste: null
|
||||
cut: null
|
||||
'set-style': { id: string; value: string | number }
|
||||
'toggle-transparent': null
|
||||
'toggle-snap-mode': null
|
||||
'toggle-tool-lock': null
|
||||
|
|
|
@ -13565,11 +13565,13 @@ __metadata:
|
|||
"@radix-ui/react-alert-dialog": "npm:^1.0.5"
|
||||
"@tldraw/assets": "workspace:*"
|
||||
"@tldraw/tldraw": "workspace:*"
|
||||
"@types/lodash": "npm:^4.14.188"
|
||||
"@vercel/analytics": "npm:^1.1.1"
|
||||
"@vitejs/plugin-react": "npm:^4.2.0"
|
||||
classnames: "npm:^2.3.2"
|
||||
dotenv: "npm:^16.3.1"
|
||||
lazyrepo: "npm:0.0.0-alpha.27"
|
||||
lodash: "npm:^4.17.21"
|
||||
react: "npm:^18.2.0"
|
||||
react-dom: "npm:^18.2.0"
|
||||
react-router-dom: "npm:^6.17.0"
|
||||
|
|
Loading…
Reference in a new issue