2023-06-20 13:31:26 +00:00
|
|
|
import { Atom, Computed, atom, computed } from '@tldraw/state'
|
2023-06-02 15:21:45 +00:00
|
|
|
import type { Editor } from '../Editor'
|
2023-04-25 11:01:25 +00:00
|
|
|
import {
|
|
|
|
EVENT_NAME_MAP,
|
2023-06-04 10:38:53 +00:00
|
|
|
TLEnterEventHandler,
|
2023-04-25 11:01:25 +00:00
|
|
|
TLEventHandlers,
|
|
|
|
TLEventInfo,
|
2023-06-04 10:38:53 +00:00
|
|
|
TLExitEventHandler,
|
2023-04-25 11:01:25 +00:00
|
|
|
TLPinchEventInfo,
|
|
|
|
} from '../types/event-types'
|
|
|
|
|
2023-06-04 10:38:53 +00:00
|
|
|
type TLStateNodeType = 'branch' | 'leaf' | 'root'
|
2023-04-25 11:01:25 +00:00
|
|
|
|
|
|
|
/** @public */
|
2023-06-04 10:38:53 +00:00
|
|
|
export interface TLStateNodeConstructor {
|
2023-06-02 15:21:45 +00:00
|
|
|
new (editor: Editor, parent?: StateNode): StateNode
|
2023-04-25 11:01:25 +00:00
|
|
|
id: string
|
|
|
|
initial?: string
|
2023-06-04 10:38:53 +00:00
|
|
|
children?: () => TLStateNodeConstructor[]
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** @public */
|
|
|
|
export abstract class StateNode implements Partial<TLEventHandlers> {
|
2024-01-15 12:33:15 +00:00
|
|
|
constructor(
|
|
|
|
public editor: Editor,
|
|
|
|
parent?: StateNode
|
|
|
|
) {
|
2023-06-04 10:38:53 +00:00
|
|
|
const { id, children, initial } = this.constructor as TLStateNodeConstructor
|
2023-04-25 11:01:25 +00:00
|
|
|
|
|
|
|
this.id = id
|
2023-11-14 13:02:50 +00:00
|
|
|
this._isActive = atom<boolean>('toolIsActive' + this.id, false)
|
|
|
|
this._current = atom<StateNode | undefined>('toolState' + this.id, undefined)
|
2023-04-25 11:01:25 +00:00
|
|
|
|
2023-11-14 13:02:50 +00:00
|
|
|
this._path = computed('toolPath' + this.id, () => {
|
|
|
|
const current = this.getCurrent()
|
|
|
|
return this.id + (current ? `.${current.getPath()}` : '')
|
2023-04-25 11:01:25 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
this.parent = parent ?? ({} as any)
|
|
|
|
|
|
|
|
if (this.parent) {
|
|
|
|
if (children && initial) {
|
|
|
|
this.type = 'branch'
|
|
|
|
this.initial = initial
|
|
|
|
this.children = Object.fromEntries(
|
2023-06-02 15:21:45 +00:00
|
|
|
children().map((Ctor) => [Ctor.id, new Ctor(this.editor, this)])
|
2023-04-25 11:01:25 +00:00
|
|
|
)
|
2023-11-14 13:02:50 +00:00
|
|
|
this._current.set(this.children[this.initial])
|
2023-04-25 11:01:25 +00:00
|
|
|
} else {
|
|
|
|
this.type = 'leaf'
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.type = 'root'
|
|
|
|
|
|
|
|
if (children && initial) {
|
|
|
|
this.initial = initial
|
|
|
|
this.children = Object.fromEntries(
|
2023-06-02 15:21:45 +00:00
|
|
|
children().map((Ctor) => [Ctor.id, new Ctor(this.editor, this)])
|
2023-04-25 11:01:25 +00:00
|
|
|
)
|
2023-11-14 13:02:50 +00:00
|
|
|
this._current.set(this.children[this.initial])
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static id: string
|
|
|
|
static initial?: string
|
2023-06-04 10:38:53 +00:00
|
|
|
static children?: () => TLStateNodeConstructor[]
|
2023-04-25 11:01:25 +00:00
|
|
|
|
|
|
|
id: string
|
2023-06-04 10:38:53 +00:00
|
|
|
type: TLStateNodeType
|
2023-07-07 13:56:31 +00:00
|
|
|
shapeType?: string
|
2023-04-25 11:01:25 +00:00
|
|
|
initial?: string
|
|
|
|
children?: Record<string, StateNode>
|
|
|
|
parent: StateNode
|
|
|
|
|
2023-11-14 13:02:50 +00:00
|
|
|
/**
|
|
|
|
* This node's path of active state nodes
|
|
|
|
*
|
|
|
|
* @public
|
|
|
|
*/
|
2023-11-16 12:07:33 +00:00
|
|
|
getPath() {
|
2023-11-14 13:02:50 +00:00
|
|
|
return this._path.get()
|
|
|
|
}
|
|
|
|
_path: Computed<string>
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This node's current active child node, if any.
|
|
|
|
*
|
|
|
|
* @public
|
|
|
|
*/
|
2023-11-16 12:07:33 +00:00
|
|
|
getCurrent() {
|
2023-11-14 13:02:50 +00:00
|
|
|
return this._current.get()
|
|
|
|
}
|
|
|
|
private _current: Atom<StateNode | undefined>
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether this node is active.
|
|
|
|
*
|
|
|
|
* @public
|
|
|
|
*/
|
2023-11-16 12:07:33 +00:00
|
|
|
getIsActive() {
|
2023-11-14 13:02:50 +00:00
|
|
|
return this._isActive.get()
|
|
|
|
}
|
|
|
|
private _isActive: Atom<boolean>
|
2023-04-25 11:01:25 +00:00
|
|
|
|
2023-11-14 13:02:50 +00:00
|
|
|
/**
|
|
|
|
* Transition to a new active child state node.
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
* ```ts
|
|
|
|
* parentState.transition('childStateA')
|
|
|
|
* parentState.transition('childStateB', { myData: 4 })
|
|
|
|
*```
|
|
|
|
*
|
|
|
|
* @param id - The id of the child state node to transition to.
|
|
|
|
* @param info - Any data to pass to the `onEnter` and `onExit` handlers.
|
|
|
|
*
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
transition = (id: string, info: any = {}) => {
|
2023-04-25 11:01:25 +00:00
|
|
|
const path = id.split('.')
|
|
|
|
|
|
|
|
let currState = this as StateNode
|
|
|
|
|
|
|
|
for (let i = 0; i < path.length; i++) {
|
|
|
|
const id = path[i]
|
2023-11-14 13:02:50 +00:00
|
|
|
const prevChildState = currState.getCurrent()
|
2023-04-25 11:01:25 +00:00
|
|
|
const nextChildState = currState.children?.[id]
|
|
|
|
|
|
|
|
if (!nextChildState) {
|
|
|
|
throw Error(`${currState.id} - no child state exists with the id ${id}.`)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prevChildState?.id !== nextChildState.id) {
|
|
|
|
prevChildState?.exit(info, id)
|
2023-11-14 13:02:50 +00:00
|
|
|
currState._current.set(nextChildState)
|
2023-04-25 11:01:25 +00:00
|
|
|
nextChildState.enter(info, prevChildState?.id || 'initial')
|
2023-11-14 13:02:50 +00:00
|
|
|
if (!nextChildState.getIsActive()) break
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
currState = nextChildState
|
|
|
|
}
|
|
|
|
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2023-09-29 15:29:02 +00:00
|
|
|
handleEvent = (info: Exclude<TLEventInfo, TLPinchEventInfo>) => {
|
2023-04-25 11:01:25 +00:00
|
|
|
const cbName = EVENT_NAME_MAP[info.name]
|
2024-04-13 13:30:30 +00:00
|
|
|
const currentActiveChild = this._current.__unsafe__getWithoutCapture()
|
2023-04-25 11:01:25 +00:00
|
|
|
this[cbName]?.(info as any)
|
2024-04-13 13:30:30 +00:00
|
|
|
if (
|
|
|
|
this._isActive.__unsafe__getWithoutCapture() &&
|
|
|
|
currentActiveChild &&
|
|
|
|
currentActiveChild === this._current.__unsafe__getWithoutCapture()
|
|
|
|
) {
|
|
|
|
currentActiveChild.handleEvent(info)
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-14 13:02:50 +00:00
|
|
|
// todo: move this logic into transition
|
2023-09-29 15:29:02 +00:00
|
|
|
enter = (info: any, from: string) => {
|
2023-11-14 13:02:50 +00:00
|
|
|
this._isActive.set(true)
|
2023-04-25 11:01:25 +00:00
|
|
|
this.onEnter?.(info, from)
|
[fix] Batch tick events (#3181)
This PR fixes an issue where events happening on tick were not batched.
![Kapture 2024-03-17 at 22 49
52](https://github.com/tldraw/tldraw/assets/23072548/2bcfa335-a38f-46c4-a3f3-434cac61b6ce)
We were listening to the `tick` event directly from the state node,
rather than passing the event into the state chart at the top. This
meant that it was bypassing the regular state chart rules, which was
what got me looking at this; but then I noticed that we also weren't
batching the changes, either. This causes computed stuff to re-compute
after each atom is updated within the `onTick` handler, which can be a
LOT.
Before:
<img width="1557" alt="image"
src="https://github.com/tldraw/tldraw/assets/23072548/ba8791f2-faec-463d-945a-9f5920826aab">
After:
<img width="1204" alt="image"
src="https://github.com/tldraw/tldraw/assets/23072548/a00f8e4a-caca-406a-89a2-8cff0e01b642">
It's not game breaking but it's important enough to hotfix at least in
the dot com.
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [x] `bugfix` — Bug fix
- [ ] `feature` — New feature
- [ ] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### Test Plan
1. Select many shapes.
2. Resize them.
### Release Notes
- Fix a performance issue effecting resizing multiple shapes.
2024-03-18 14:33:36 +00:00
|
|
|
|
2023-11-14 13:02:50 +00:00
|
|
|
if (this.children && this.initial && this.getIsActive()) {
|
2023-04-25 11:01:25 +00:00
|
|
|
const initial = this.children[this.initial]
|
2023-11-14 13:02:50 +00:00
|
|
|
this._current.set(initial)
|
2023-04-25 11:01:25 +00:00
|
|
|
initial.enter(info, from)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-14 13:02:50 +00:00
|
|
|
// todo: move this logic into transition
|
2023-09-29 15:29:02 +00:00
|
|
|
exit = (info: any, from: string) => {
|
2023-11-14 13:02:50 +00:00
|
|
|
this._isActive.set(false)
|
2023-04-25 11:01:25 +00:00
|
|
|
this.onExit?.(info, from)
|
[fix] Batch tick events (#3181)
This PR fixes an issue where events happening on tick were not batched.
![Kapture 2024-03-17 at 22 49
52](https://github.com/tldraw/tldraw/assets/23072548/2bcfa335-a38f-46c4-a3f3-434cac61b6ce)
We were listening to the `tick` event directly from the state node,
rather than passing the event into the state chart at the top. This
meant that it was bypassing the regular state chart rules, which was
what got me looking at this; but then I noticed that we also weren't
batching the changes, either. This causes computed stuff to re-compute
after each atom is updated within the `onTick` handler, which can be a
LOT.
Before:
<img width="1557" alt="image"
src="https://github.com/tldraw/tldraw/assets/23072548/ba8791f2-faec-463d-945a-9f5920826aab">
After:
<img width="1204" alt="image"
src="https://github.com/tldraw/tldraw/assets/23072548/a00f8e4a-caca-406a-89a2-8cff0e01b642">
It's not game breaking but it's important enough to hotfix at least in
the dot com.
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [x] `bugfix` — Bug fix
- [ ] `feature` — New feature
- [ ] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### Test Plan
1. Select many shapes.
2. Resize them.
### Release Notes
- Fix a performance issue effecting resizing multiple shapes.
2024-03-18 14:33:36 +00:00
|
|
|
|
2023-11-14 13:02:50 +00:00
|
|
|
if (!this.getIsActive()) {
|
|
|
|
this.getCurrent()?.exit(info, from)
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
tldraw zero - package shuffle (#1710)
This PR moves code between our packages so that:
- @tldraw/editor is a “core” library with the engine and canvas but no
shapes, tools, or other things
- @tldraw/tldraw contains everything particular to the experience we’ve
built for tldraw
At first look, this might seem like a step away from customization and
configuration, however I believe it greatly increases the configuration
potential of the @tldraw/editor while also providing a more accurate
reflection of what configuration options actually exist for
@tldraw/tldraw.
## Library changes
@tldraw/editor re-exports its dependencies and @tldraw/tldraw re-exports
@tldraw/editor.
- users of @tldraw/editor WITHOUT @tldraw/tldraw should almost always
only import things from @tldraw/editor.
- users of @tldraw/tldraw should almost always only import things from
@tldraw/tldraw.
- @tldraw/polyfills is merged into @tldraw/editor
- @tldraw/indices is merged into @tldraw/editor
- @tldraw/primitives is merged mostly into @tldraw/editor, partially
into @tldraw/tldraw
- @tldraw/file-format is merged into @tldraw/tldraw
- @tldraw/ui is merged into @tldraw/tldraw
Many (many) utils and other code is moved from the editor to tldraw. For
example, embeds now are entirely an feature of @tldraw/tldraw. The only
big chunk of code left in core is related to arrow handling.
## API Changes
The editor can now be used without tldraw's assets. We load them in
@tldraw/tldraw instead, so feel free to use whatever fonts or images or
whatever that you like with the editor.
All tools and shapes (except for the `Group` shape) are moved to
@tldraw/tldraw. This includes the `select` tool.
You should use the editor with at least one tool, however, so you now
also need to send in an `initialState` prop to the Editor /
<TldrawEditor> component indicating which state the editor should begin
in.
The `components` prop now also accepts `SelectionForeground`.
The complex selection component that we use for tldraw is moved to
@tldraw/tldraw. The default component is quite basic but can easily be
replaced via the `components` prop. We pass down our tldraw-flavored
SelectionFg via `components`.
Likewise with the `Scribble` component: the `DefaultScribble` no longer
uses our freehand tech and is a simple path instead. We pass down the
tldraw-flavored scribble via `components`.
The `ExternalContentManager` (`Editor.externalContentManager`) is
removed and replaced with a mapping of types to handlers.
- Register new content handlers with
`Editor.registerExternalContentHandler`.
- Register new asset creation handlers (for files and URLs) with
`Editor.registerExternalAssetHandler`
### Change Type
- [x] `major` — Breaking change
### Test Plan
- [x] Unit Tests
- [x] End to end tests
### Release Notes
- [@tldraw/editor] lots, wip
- [@tldraw/ui] gone, merged to tldraw/tldraw
- [@tldraw/polyfills] gone, merged to tldraw/editor
- [@tldraw/primitives] gone, merged to tldraw/editor / tldraw/tldraw
- [@tldraw/indices] gone, merged to tldraw/editor
- [@tldraw/file-format] gone, merged to tldraw/tldraw
---------
Co-authored-by: alex <alex@dytry.ch>
2023-07-17 21:22:34 +00:00
|
|
|
/**
|
|
|
|
* This is a hack / escape hatch that will tell the editor to
|
2023-11-13 11:51:22 +00:00
|
|
|
* report a different state as active (in `getCurrentToolId()`) when
|
tldraw zero - package shuffle (#1710)
This PR moves code between our packages so that:
- @tldraw/editor is a “core” library with the engine and canvas but no
shapes, tools, or other things
- @tldraw/tldraw contains everything particular to the experience we’ve
built for tldraw
At first look, this might seem like a step away from customization and
configuration, however I believe it greatly increases the configuration
potential of the @tldraw/editor while also providing a more accurate
reflection of what configuration options actually exist for
@tldraw/tldraw.
## Library changes
@tldraw/editor re-exports its dependencies and @tldraw/tldraw re-exports
@tldraw/editor.
- users of @tldraw/editor WITHOUT @tldraw/tldraw should almost always
only import things from @tldraw/editor.
- users of @tldraw/tldraw should almost always only import things from
@tldraw/tldraw.
- @tldraw/polyfills is merged into @tldraw/editor
- @tldraw/indices is merged into @tldraw/editor
- @tldraw/primitives is merged mostly into @tldraw/editor, partially
into @tldraw/tldraw
- @tldraw/file-format is merged into @tldraw/tldraw
- @tldraw/ui is merged into @tldraw/tldraw
Many (many) utils and other code is moved from the editor to tldraw. For
example, embeds now are entirely an feature of @tldraw/tldraw. The only
big chunk of code left in core is related to arrow handling.
## API Changes
The editor can now be used without tldraw's assets. We load them in
@tldraw/tldraw instead, so feel free to use whatever fonts or images or
whatever that you like with the editor.
All tools and shapes (except for the `Group` shape) are moved to
@tldraw/tldraw. This includes the `select` tool.
You should use the editor with at least one tool, however, so you now
also need to send in an `initialState` prop to the Editor /
<TldrawEditor> component indicating which state the editor should begin
in.
The `components` prop now also accepts `SelectionForeground`.
The complex selection component that we use for tldraw is moved to
@tldraw/tldraw. The default component is quite basic but can easily be
replaced via the `components` prop. We pass down our tldraw-flavored
SelectionFg via `components`.
Likewise with the `Scribble` component: the `DefaultScribble` no longer
uses our freehand tech and is a simple path instead. We pass down the
tldraw-flavored scribble via `components`.
The `ExternalContentManager` (`Editor.externalContentManager`) is
removed and replaced with a mapping of types to handlers.
- Register new content handlers with
`Editor.registerExternalContentHandler`.
- Register new asset creation handlers (for files and URLs) with
`Editor.registerExternalAssetHandler`
### Change Type
- [x] `major` — Breaking change
### Test Plan
- [x] Unit Tests
- [x] End to end tests
### Release Notes
- [@tldraw/editor] lots, wip
- [@tldraw/ui] gone, merged to tldraw/tldraw
- [@tldraw/polyfills] gone, merged to tldraw/editor
- [@tldraw/primitives] gone, merged to tldraw/editor / tldraw/tldraw
- [@tldraw/indices] gone, merged to tldraw/editor
- [@tldraw/file-format] gone, merged to tldraw/tldraw
---------
Co-authored-by: alex <alex@dytry.ch>
2023-07-17 21:22:34 +00:00
|
|
|
* this state is active. This is usually used when a tool transitions
|
|
|
|
* to a child of a different state for a certain interaction and then
|
|
|
|
* returns to the original tool when that interaction completes; and
|
|
|
|
* where we would want to show the original tool as active in the UI.
|
|
|
|
*
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
_currentToolIdMask = atom('curent tool id mask', undefined as string | undefined)
|
|
|
|
|
2023-11-16 12:07:33 +00:00
|
|
|
getCurrentToolIdMask() {
|
|
|
|
return this._currentToolIdMask.get()
|
|
|
|
}
|
|
|
|
|
|
|
|
setCurrentToolIdMask(id: string | undefined) {
|
|
|
|
this._currentToolIdMask.set(id)
|
|
|
|
}
|
|
|
|
|
2023-04-25 11:01:25 +00:00
|
|
|
onWheel?: TLEventHandlers['onWheel']
|
|
|
|
onPointerDown?: TLEventHandlers['onPointerDown']
|
|
|
|
onPointerMove?: TLEventHandlers['onPointerMove']
|
Add long press event (#3275)
This PR adds a "long press" event that fires when pointing for more than
500ms. This event is used in the same way that dragging is used (e.g. to
transition to from pointing_selection to translating) but only on
desktop. On mobile, long presses are used to open the context menu.
![Kapture 2024-03-26 at 18 57
15](https://github.com/tldraw/tldraw/assets/23072548/34a7ee2b-bde6-443b-93e0-082453a1cb61)
## Background
This idea came out of @TodePond's #3208 PR. We use a "dead zone" to
avoid accidentally moving / rotating things when clicking on them, which
is especially common on mobile if a dead zone feature isn't implemented.
However, this makes it difficult to make "fine adjustments" because you
need to drag out of the dead zone (to start translating) and then drag
back to where you want to go.
![Kapture 2024-03-26 at 19 00
38](https://github.com/tldraw/tldraw/assets/23072548/9a15852d-03d0-4b88-b594-27dbd3b68780)
With this change, you can long press on desktop to get to that
translating state. It's a micro UX optimization but especially nice if
apps want to display different UI for "dragging" shapes before the user
leaves the dead zone.
![Kapture 2024-03-26 at 19 02
59](https://github.com/tldraw/tldraw/assets/23072548/f0ff337e-2cbd-4b73-9ef5-9b7deaf0ae91)
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [ ] `bugfix` — Bug fix
- [x] `feature` — New feature
- [ ] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### Test Plan
1. Long press shapes, selections, resize handles, rotate handles, crop
handles.
2. You should enter the corresponding states, just as you would have
with a drag.
- [ ] Unit Tests TODO
### Release Notes
- Add support for long pressing on desktop.
2024-04-04 21:50:01 +00:00
|
|
|
onLongPress?: TLEventHandlers['onLongPress']
|
2023-04-25 11:01:25 +00:00
|
|
|
onPointerUp?: TLEventHandlers['onPointerUp']
|
|
|
|
onDoubleClick?: TLEventHandlers['onDoubleClick']
|
|
|
|
onTripleClick?: TLEventHandlers['onTripleClick']
|
|
|
|
onQuadrupleClick?: TLEventHandlers['onQuadrupleClick']
|
|
|
|
onRightClick?: TLEventHandlers['onRightClick']
|
|
|
|
onMiddleClick?: TLEventHandlers['onMiddleClick']
|
|
|
|
onKeyDown?: TLEventHandlers['onKeyDown']
|
|
|
|
onKeyUp?: TLEventHandlers['onKeyUp']
|
|
|
|
onKeyRepeat?: TLEventHandlers['onKeyRepeat']
|
|
|
|
onCancel?: TLEventHandlers['onCancel']
|
|
|
|
onComplete?: TLEventHandlers['onComplete']
|
|
|
|
onInterrupt?: TLEventHandlers['onInterrupt']
|
[fix] Batch tick events (#3181)
This PR fixes an issue where events happening on tick were not batched.
![Kapture 2024-03-17 at 22 49
52](https://github.com/tldraw/tldraw/assets/23072548/2bcfa335-a38f-46c4-a3f3-434cac61b6ce)
We were listening to the `tick` event directly from the state node,
rather than passing the event into the state chart at the top. This
meant that it was bypassing the regular state chart rules, which was
what got me looking at this; but then I noticed that we also weren't
batching the changes, either. This causes computed stuff to re-compute
after each atom is updated within the `onTick` handler, which can be a
LOT.
Before:
<img width="1557" alt="image"
src="https://github.com/tldraw/tldraw/assets/23072548/ba8791f2-faec-463d-945a-9f5920826aab">
After:
<img width="1204" alt="image"
src="https://github.com/tldraw/tldraw/assets/23072548/a00f8e4a-caca-406a-89a2-8cff0e01b642">
It's not game breaking but it's important enough to hotfix at least in
the dot com.
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [x] `bugfix` — Bug fix
- [ ] `feature` — New feature
- [ ] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### Test Plan
1. Select many shapes.
2. Resize them.
### Release Notes
- Fix a performance issue effecting resizing multiple shapes.
2024-03-18 14:33:36 +00:00
|
|
|
onTick?: TLEventHandlers['onTick']
|
2023-04-25 11:01:25 +00:00
|
|
|
|
2023-06-04 10:38:53 +00:00
|
|
|
onEnter?: TLEnterEventHandler
|
|
|
|
onExit?: TLExitEventHandler
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|