Skip to content

Commit 146e95b

Browse files
authored
add EditorAtom helper (tldraw#6531)
When I was working on the workflows starter, I found myself needing to store a lot of state in atoms. This includes: * The current exectution * Which ports on which shapes should be highlighted when dragging a connection * Whether the on-canvas component picker was visible, and which shape it should be attached to All of this state should really belong to a specific `Editor` instance. It's referencing data from that instance. If I was building a full app and navigated from one workflow to another, I would want all the state to be cleared due to the editor getting re-created. It's possible to get this sort of functionality by using a weak map from editors to atoms, but it takes a fair bit of management. This diff adds an `EditorAtom` helper which handles the weak-map juggling. The interface is similar to that of `Editor`, but each method takes an `editor` for scoping. ### Change type - [x] `api` ### Release notes ### API Changes - Added `EditorAtom`, a helper for storing editor UI state in an `Atom`
1 parent fa37496 commit 146e95b

3 files changed

Lines changed: 51 additions & 0 deletions

File tree

packages/editor/api-report.api.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,6 +1549,19 @@ export class Editor extends EventEmitter<TLEventMap> {
15491549
zoomToUser(userId: string, opts?: TLCameraMoveOptions): this;
15501550
}
15511551

1552+
// @public
1553+
export class EditorAtom<T> {
1554+
constructor(name: string, getInitialState: (editor: Editor) => T);
1555+
// (undocumented)
1556+
get(editor: Editor): T;
1557+
// (undocumented)
1558+
getAtom(editor: Editor): Atom<T>;
1559+
// (undocumented)
1560+
set(editor: Editor, state: T): T;
1561+
// (undocumented)
1562+
update(editor: Editor, update: (state: T) => T): T;
1563+
}
1564+
15521565
// @public (undocumented)
15531566
export const EditorContext: React_3.Context<Editor | null>;
15541567

packages/editor/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ export {
450450
setPointerCapture,
451451
stopEventPropagation,
452452
} from './lib/utils/dom'
453+
export { EditorAtom } from './lib/utils/EditorAtom'
453454
export { getIncrementedName } from './lib/utils/getIncrementedName'
454455
export { getPointerInfo } from './lib/utils/getPointerInfo'
455456
export { getSvgPathFromPoints } from './lib/utils/getSvgPathFromPoints'
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { atom, Atom } from '@tldraw/state'
2+
import { WeakCache } from '@tldraw/utils'
3+
import { Editor } from '../editor/Editor'
4+
5+
/**
6+
* An Atom that is scoped to the lifetime of an Editor.
7+
*
8+
* This is useful for storing UI state for tldraw applications. Keeping state scoped to an editor
9+
* instead of stored in a global atom can prevent issues with state being shared between editors
10+
* when navigating between pages, or when multiple editor instances are used on the same page.
11+
*
12+
* @public
13+
*/
14+
export class EditorAtom<T> {
15+
private states = new WeakCache<Editor, Atom<T>>()
16+
17+
constructor(
18+
private name: string,
19+
private getInitialState: (editor: Editor) => T
20+
) {}
21+
22+
getAtom(editor: Editor): Atom<T> {
23+
return this.states.get(editor, () => atom(this.name, this.getInitialState(editor)))
24+
}
25+
26+
get(editor: Editor): T {
27+
return this.getAtom(editor).get()
28+
}
29+
30+
update(editor: Editor, update: (state: T) => T): T {
31+
return this.getAtom(editor).update(update)
32+
}
33+
34+
set(editor: Editor, state: T): T {
35+
return this.getAtom(editor).set(state)
36+
}
37+
}

0 commit comments

Comments
 (0)