pointers-sessions
Steve Ruiz 2024-03-10 15:11:39 +00:00
rodzic 388bfcc7f5
commit af85ed377e
13 zmienionych plików z 805 dodań i 79 usunięć

Wyświetl plik

@ -821,6 +821,20 @@ export class Editor extends EventEmitter<TLEventMap> {
};
pan(offset: VecLike, animation?: TLAnimationOptions): this;
panZoomIntoView(ids: TLShapeId[], animation?: TLAnimationOptions): this;
// (undocumented)
pointers: Map<number, {
id: number;
originPagePoint: Vec;
originScreenPoint: Vec;
previousPagePoint: Vec;
previousScreenPoint: Vec;
prevFrameScreenPoint: Vec;
currentPagePoint: Vec;
currentScreenPoint: Vec;
velocity: Vec;
buttons: Set<number>;
isPen: boolean;
}>;
popFocusedGroupId(): this;
putContentOntoCurrentPage(content: TLContent, options?: {
point?: VecLike;
@ -854,6 +868,7 @@ export class Editor extends EventEmitter<TLEventMap> {
selectNone(): this;
sendBackward(shapes: TLShape[] | TLShapeId[]): this;
sendToBack(shapes: TLShape[] | TLShapeId[]): this;
readonly sessions: SessionManager;
setCamera(point: VecLike, animation?: TLAnimationOptions): this;
setCroppingShape(shape: null | TLShape | TLShapeId): this;
setCurrentPage(page: TLPage | TLPageId, historyOptions?: TLCommandHistoryOptions): this;
@ -1578,8 +1593,8 @@ export type SelectionEdge = 'bottom' | 'left' | 'right' | 'top';
export type SelectionHandle = SelectionCorner | SelectionEdge;
// @public
export abstract class Session {
constructor(editor: Editor);
export abstract class Session<T extends object = object> {
constructor(editor: Editor, options?: T);
// (undocumented)
abstract cancel(): void;
// (undocumented)
@ -1587,15 +1602,32 @@ export abstract class Session {
// (undocumented)
editor: Editor;
// (undocumented)
abstract readonly id: string;
readonly id: string;
// (undocumented)
abstract interrupt(): void;
// (undocumented)
options: T;
// (undocumented)
remove(): void;
// (undocumented)
abstract start(): void;
// (undocumented)
abstract readonly type: string;
// (undocumented)
abstract update(): void;
}
// @public (undocumented)
export class SessionManager {
constructor(editor: Editor);
addSession(session: Session): void;
clearSessions(): void;
// (undocumented)
editor: Editor;
getSessions(): Session<object>[];
removeSession(session: Session): void;
}
// @public (undocumented)
export function setPointerCapture(element: Element, event: PointerEvent | React_2.PointerEvent<Element>): void;

Wyświetl plik

@ -11306,7 +11306,7 @@
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#getOnlySelectedShape:member(1)",
"docComment": "/**\n * The app's only selected shape.\n *\n * @returns Null if there is no shape or more than one selected shape, otherwise the selected shape.\n *\n * @public @readonly\n */\n",
"docComment": "/**\n * The editor's only selected shape.\n *\n * @returns Null if there is no shape or more than one selected shape, otherwise the selected shape.\n *\n * @public @readonly\n */\n",
"excerptTokens": [
{
"kind": "Content",
@ -14307,7 +14307,7 @@
{
"kind": "Property",
"canonicalReference": "@tldraw/editor!Editor#history:member",
"docComment": "/**\n * A manager for the app's history.\n *\n * @readonly\n */\n",
"docComment": "/**\n * A manager for the editor's history.\n *\n * @readonly\n */\n",
"excerptTokens": [
{
"kind": "Content",
@ -14342,7 +14342,7 @@
{
"kind": "Property",
"canonicalReference": "@tldraw/editor!Editor#inputs:member",
"docComment": "/**\n * The app's current input state.\n *\n * @public\n */\n",
"docComment": "/**\n * The editor's current input state.\n *\n * @public\n */\n",
"excerptTokens": [
{
"kind": "Content",
@ -15605,6 +15605,122 @@
"isAbstract": false,
"name": "panZoomIntoView"
},
{
"kind": "Property",
"canonicalReference": "@tldraw/editor!Editor#pointers:member",
"docComment": "",
"excerptTokens": [
{
"kind": "Content",
"text": "pointers: "
},
{
"kind": "Reference",
"text": "Map",
"canonicalReference": "!Map:interface"
},
{
"kind": "Content",
"text": "<number, {\n id: number;\n originPagePoint: "
},
{
"kind": "Reference",
"text": "Vec",
"canonicalReference": "@tldraw/editor!Vec:class"
},
{
"kind": "Content",
"text": ";\n originScreenPoint: "
},
{
"kind": "Reference",
"text": "Vec",
"canonicalReference": "@tldraw/editor!Vec:class"
},
{
"kind": "Content",
"text": ";\n previousPagePoint: "
},
{
"kind": "Reference",
"text": "Vec",
"canonicalReference": "@tldraw/editor!Vec:class"
},
{
"kind": "Content",
"text": ";\n previousScreenPoint: "
},
{
"kind": "Reference",
"text": "Vec",
"canonicalReference": "@tldraw/editor!Vec:class"
},
{
"kind": "Content",
"text": ";\n prevFrameScreenPoint: "
},
{
"kind": "Reference",
"text": "Vec",
"canonicalReference": "@tldraw/editor!Vec:class"
},
{
"kind": "Content",
"text": ";\n currentPagePoint: "
},
{
"kind": "Reference",
"text": "Vec",
"canonicalReference": "@tldraw/editor!Vec:class"
},
{
"kind": "Content",
"text": ";\n currentScreenPoint: "
},
{
"kind": "Reference",
"text": "Vec",
"canonicalReference": "@tldraw/editor!Vec:class"
},
{
"kind": "Content",
"text": ";\n velocity: "
},
{
"kind": "Reference",
"text": "Vec",
"canonicalReference": "@tldraw/editor!Vec:class"
},
{
"kind": "Content",
"text": ";\n buttons: "
},
{
"kind": "Reference",
"text": "Set",
"canonicalReference": "!Set:interface"
},
{
"kind": "Content",
"text": "<number>;\n isPen: boolean;\n }>"
},
{
"kind": "Content",
"text": ";"
}
],
"isReadonly": false,
"isOptional": false,
"releaseTag": "Public",
"name": "pointers",
"propertyTypeTokenRange": {
"startIndex": 1,
"endIndex": 21
},
"isStatic": false,
"isProtected": false,
"isAbstract": false
},
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#popFocusedGroupId:member(1)",
@ -16841,6 +16957,37 @@
"isAbstract": false,
"name": "sendToBack"
},
{
"kind": "Property",
"canonicalReference": "@tldraw/editor!Editor#sessions:member",
"docComment": "/**\n * A manager for the editor's sessions.\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "readonly sessions: "
},
{
"kind": "Reference",
"text": "SessionManager",
"canonicalReference": "@tldraw/editor!SessionManager:class"
},
{
"kind": "Content",
"text": ";"
}
],
"isReadonly": true,
"isOptional": false,
"releaseTag": "Public",
"name": "sessions",
"propertyTypeTokenRange": {
"startIndex": 1,
"endIndex": 2
},
"isStatic": false,
"isProtected": false,
"isAbstract": false
},
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#setCamera:member(1)",
@ -18029,7 +18176,7 @@
{
"kind": "Property",
"canonicalReference": "@tldraw/editor!Editor#snaps:member",
"docComment": "/**\n * A manager for the app's snapping feature.\n *\n * @public\n */\n",
"docComment": "/**\n * A manager for the editor's snapping feature.\n *\n * @public\n */\n",
"excerptTokens": [
{
"kind": "Content",
@ -29955,11 +30102,40 @@
"excerptTokens": [
{
"kind": "Content",
"text": "export declare abstract class Session "
"text": "export declare abstract class Session<T extends "
},
{
"kind": "Content",
"text": "object"
},
{
"kind": "Content",
"text": " = "
},
{
"kind": "Content",
"text": "object"
},
{
"kind": "Content",
"text": "> "
}
],
"fileUrlPath": "packages/editor/src/lib/editor/sessions/Session.ts",
"fileUrlPath": "packages/editor/src/lib/editor/managers/SessionManager.ts",
"releaseTag": "Public",
"typeParameters": [
{
"typeParameterName": "T",
"constraintTokenRange": {
"startIndex": 1,
"endIndex": 2
},
"defaultTypeTokenRange": {
"startIndex": 3,
"endIndex": 4
}
}
],
"isAbstract": true,
"name": "Session",
"preserveMemberOrder": false,
@ -29978,6 +30154,14 @@
"text": "Editor",
"canonicalReference": "@tldraw/editor!Editor:class"
},
{
"kind": "Content",
"text": ", options?: "
},
{
"kind": "Content",
"text": "T"
},
{
"kind": "Content",
"text": ");"
@ -29994,6 +30178,14 @@
"endIndex": 2
},
"isOptional": false
},
{
"parameterName": "options",
"parameterTypeTokenRange": {
"startIndex": 3,
"endIndex": 4
},
"isOptional": true
}
]
},
@ -30097,7 +30289,7 @@
"excerptTokens": [
{
"kind": "Content",
"text": "abstract readonly id: "
"text": "readonly id: "
},
{
"kind": "Content",
@ -30118,7 +30310,7 @@
},
"isStatic": false,
"isProtected": false,
"isAbstract": true
"isAbstract": false
},
{
"kind": "Method",
@ -30151,6 +30343,67 @@
"isAbstract": true,
"name": "interrupt"
},
{
"kind": "Property",
"canonicalReference": "@tldraw/editor!Session#options:member",
"docComment": "",
"excerptTokens": [
{
"kind": "Content",
"text": "options: "
},
{
"kind": "Content",
"text": "T"
},
{
"kind": "Content",
"text": ";"
}
],
"isReadonly": false,
"isOptional": false,
"releaseTag": "Public",
"name": "options",
"propertyTypeTokenRange": {
"startIndex": 1,
"endIndex": 2
},
"isStatic": false,
"isProtected": false,
"isAbstract": false
},
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!Session#remove:member(1)",
"docComment": "",
"excerptTokens": [
{
"kind": "Content",
"text": "remove(): "
},
{
"kind": "Content",
"text": "void"
},
{
"kind": "Content",
"text": ";"
}
],
"isStatic": false,
"returnTypeTokenRange": {
"startIndex": 1,
"endIndex": 2
},
"releaseTag": "Public",
"isProtected": false,
"overloadIndex": 1,
"parameters": [],
"isOptional": false,
"isAbstract": false,
"name": "remove"
},
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!Session#start:member(1)",
@ -30182,6 +30435,36 @@
"isAbstract": true,
"name": "start"
},
{
"kind": "Property",
"canonicalReference": "@tldraw/editor!Session#type:member",
"docComment": "",
"excerptTokens": [
{
"kind": "Content",
"text": "abstract readonly type: "
},
{
"kind": "Content",
"text": "string"
},
{
"kind": "Content",
"text": ";"
}
],
"isReadonly": true,
"isOptional": false,
"releaseTag": "Public",
"name": "type",
"propertyTypeTokenRange": {
"startIndex": 1,
"endIndex": 2
},
"isStatic": false,
"isProtected": false,
"isAbstract": true
},
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!Session#update:member(1)",
@ -30216,6 +30499,254 @@
],
"implementsTokenRanges": []
},
{
"kind": "Class",
"canonicalReference": "@tldraw/editor!SessionManager:class",
"docComment": "/**\n * @public\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "export declare class SessionManager "
}
],
"fileUrlPath": "packages/editor/src/lib/editor/managers/SessionManager.ts",
"releaseTag": "Public",
"isAbstract": false,
"name": "SessionManager",
"preserveMemberOrder": false,
"members": [
{
"kind": "Constructor",
"canonicalReference": "@tldraw/editor!SessionManager:constructor(1)",
"docComment": "/**\n * Constructs a new instance of the `SessionManager` class\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "constructor(editor: "
},
{
"kind": "Reference",
"text": "Editor",
"canonicalReference": "@tldraw/editor!Editor:class"
},
{
"kind": "Content",
"text": ");"
}
],
"releaseTag": "Public",
"isProtected": false,
"overloadIndex": 1,
"parameters": [
{
"parameterName": "editor",
"parameterTypeTokenRange": {
"startIndex": 1,
"endIndex": 2
},
"isOptional": false
}
]
},
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!SessionManager#addSession:member(1)",
"docComment": "/**\n * Add a session to the session manager.\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "addSession(session: "
},
{
"kind": "Reference",
"text": "Session",
"canonicalReference": "@tldraw/editor!Session:class"
},
{
"kind": "Content",
"text": "): "
},
{
"kind": "Content",
"text": "void"
},
{
"kind": "Content",
"text": ";"
}
],
"isStatic": false,
"returnTypeTokenRange": {
"startIndex": 3,
"endIndex": 4
},
"releaseTag": "Public",
"isProtected": false,
"overloadIndex": 1,
"parameters": [
{
"parameterName": "session",
"parameterTypeTokenRange": {
"startIndex": 1,
"endIndex": 2
},
"isOptional": false
}
],
"isOptional": false,
"isAbstract": false,
"name": "addSession"
},
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!SessionManager#clearSessions:member(1)",
"docComment": "/**\n * Cancel and clear all sessions.\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "clearSessions(): "
},
{
"kind": "Content",
"text": "void"
},
{
"kind": "Content",
"text": ";"
}
],
"isStatic": false,
"returnTypeTokenRange": {
"startIndex": 1,
"endIndex": 2
},
"releaseTag": "Public",
"isProtected": false,
"overloadIndex": 1,
"parameters": [],
"isOptional": false,
"isAbstract": false,
"name": "clearSessions"
},
{
"kind": "Property",
"canonicalReference": "@tldraw/editor!SessionManager#editor:member",
"docComment": "",
"excerptTokens": [
{
"kind": "Content",
"text": "editor: "
},
{
"kind": "Reference",
"text": "Editor",
"canonicalReference": "@tldraw/editor!Editor:class"
},
{
"kind": "Content",
"text": ";"
}
],
"isReadonly": false,
"isOptional": false,
"releaseTag": "Public",
"name": "editor",
"propertyTypeTokenRange": {
"startIndex": 1,
"endIndex": 2
},
"isStatic": false,
"isProtected": false,
"isAbstract": false
},
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!SessionManager#getSessions:member(1)",
"docComment": "/**\n * Get an array of the current sessions.\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "getSessions(): "
},
{
"kind": "Reference",
"text": "Session",
"canonicalReference": "@tldraw/editor!Session:class"
},
{
"kind": "Content",
"text": "<object>[]"
},
{
"kind": "Content",
"text": ";"
}
],
"isStatic": false,
"returnTypeTokenRange": {
"startIndex": 1,
"endIndex": 3
},
"releaseTag": "Public",
"isProtected": false,
"overloadIndex": 1,
"parameters": [],
"isOptional": false,
"isAbstract": false,
"name": "getSessions"
},
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!SessionManager#removeSession:member(1)",
"docComment": "/**\n * Remove a session from the session manager.\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "removeSession(session: "
},
{
"kind": "Reference",
"text": "Session",
"canonicalReference": "@tldraw/editor!Session:class"
},
{
"kind": "Content",
"text": "): "
},
{
"kind": "Content",
"text": "void"
},
{
"kind": "Content",
"text": ";"
}
],
"isStatic": false,
"returnTypeTokenRange": {
"startIndex": 3,
"endIndex": 4
},
"releaseTag": "Public",
"isProtected": false,
"overloadIndex": 1,
"parameters": [
{
"parameterName": "session",
"parameterTypeTokenRange": {
"startIndex": 1,
"endIndex": 2
},
"isOptional": false
}
],
"isOptional": false,
"isAbstract": false,
"name": "removeSession"
}
],
"implementsTokenRanges": []
},
{
"kind": "Function",
"canonicalReference": "@tldraw/editor!setPointerCapture:function(1)",
@ -37204,7 +37735,7 @@
{
"kind": "PropertySignature",
"canonicalReference": "@tldraw/editor!TLEditorOptions#store:member",
"docComment": "/**\n * The Store instance to use for keeping the app's data. This may be prepopulated, e.g. by loading from a server or database.\n */\n",
"docComment": "/**\n * The Store instance to use for keeping the editor's data. This may be prepopulated, e.g. by loading from a server or database.\n */\n",
"excerptTokens": [
{
"kind": "Content",

Wyświetl plik

@ -134,6 +134,7 @@ export {
type TLEditorOptions,
type TLResizeShapeOptions,
} from './lib/editor/Editor'
export { Session, SessionManager } from './lib/editor/managers/SessionManager'
export type {
TLAfterChangeHandler,
TLAfterCreateHandler,
@ -154,7 +155,6 @@ export {
type PointsSnapIndicator,
type SnapIndicator,
} from './lib/editor/managers/SnapManager/SnapManager'
export { Session } from './lib/editor/sessions/Session'
export { BaseBoxShapeUtil, type TLBaseBoxShape } from './lib/editor/shapes/BaseBoxShapeUtil'
export {
ShapeUtil,

Wyświetl plik

@ -108,6 +108,7 @@ import { ClickManager } from './managers/ClickManager'
import { EnvironmentManager } from './managers/EnvironmentManager'
import { HistoryManager } from './managers/HistoryManager'
import { ScribbleManager } from './managers/ScribbleManager'
import { SessionManager } from './managers/SessionManager'
import { SideEffectManager } from './managers/SideEffectManager'
import { SnapManager } from './managers/SnapManager/SnapManager'
import { TextManager } from './managers/TextManager'
@ -154,7 +155,7 @@ export type TLResizeShapeOptions = Partial<{
/** @public */
export interface TLEditorOptions {
/**
* The Store instance to use for keeping the app's data. This may be prepopulated, e.g. by loading
* The Store instance to use for keeping the editor's data. This may be prepopulated, e.g. by loading
* from a server or database.
*/
store: TLStore
@ -202,6 +203,8 @@ export class Editor extends EventEmitter<TLEventMap> {
this.snaps = new SnapManager(this)
this.sessions = new SessionManager(this)
this.user = new UserPreferencesManager(user ?? createTLUser(), inferDarkMode ?? false)
this.getContainer = getContainer ?? (() => document.body)
@ -667,7 +670,12 @@ export class Editor extends EventEmitter<TLEventMap> {
private _tickManager = new TickManager(this)
/**
* A manager for the app's snapping feature.
* A manager for the editor's sessions.
*/
readonly sessions: SessionManager
/**
* A manager for the editor's snapping feature.
*
* @public
*/
@ -769,7 +777,7 @@ export class Editor extends EventEmitter<TLEventMap> {
/* --------------------- History -------------------- */
/**
* A manager for the app's history.
* A manager for the editor's history.
*
* @readonly
*/
@ -1023,7 +1031,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* we're in a transaction that's about to be rolled back due to the same error we're currently
* reporting.
*
* Instead, to listen to changes to this value, you need to listen to app's `crash` event.
* Instead, to listen to changes to this value, you need to listen to editor's `crash` event.
*
* @internal
*/
@ -1610,7 +1618,7 @@ export class Editor extends EventEmitter<TLEventMap> {
}
/**
* The app's only selected shape.
* The editor's only selected shape.
*
* @returns Null if there is no shape or more than one selected shape, otherwise the selected
* shape.
@ -8278,8 +8286,25 @@ export class Editor extends EventEmitter<TLEventMap> {
/* --------------------- Events --------------------- */
pointers = new Map<
number,
{
id: number
originPagePoint: Vec
originScreenPoint: Vec
previousPagePoint: Vec
previousScreenPoint: Vec
prevFrameScreenPoint: Vec
currentPagePoint: Vec
currentScreenPoint: Vec
velocity: Vec
buttons: Set<number>
isPen: boolean
}
>()
/**
* The app's current input state.
* The editor's current input state.
*
* @public
*/
@ -8340,6 +8365,41 @@ export class Editor extends EventEmitter<TLEventMap> {
const sy = info.point.y - screenBounds.y
const sz = info.point.z
if (info.type === 'pointer') {
switch (info.name) {
case 'pointer_down': {
this.pointers.set(info.pointerId, {
id: info.pointerId,
originPagePoint: new Vec(sx / cz - cx, sy / cz - cy, sz ?? 0.5),
originScreenPoint: new Vec(sx, sy),
previousPagePoint: new Vec(sx / cz - cx, sy / cz - cy, sz ?? 0.5),
previousScreenPoint: new Vec(sx, sy),
prevFrameScreenPoint: new Vec(sx, sy),
currentPagePoint: new Vec(sx / cz - cx, sy / cz - cy, sz ?? 0.5),
currentScreenPoint: new Vec(sx, sy),
velocity: new Vec(0, 0),
isPen: info.type === 'pointer' && info.isPen,
buttons: new Set([info.button]),
})
break
}
case 'pointer_move': {
const pointer = this.pointers.get(info.pointerId)
if (pointer) {
pointer.previousPagePoint.setTo(pointer.currentPagePoint)
pointer.previousScreenPoint.setTo(pointer.currentScreenPoint)
pointer.currentPagePoint.set(sx / cz - cx, sy / cz - cy, sz ?? 0.5)
pointer.currentScreenPoint.set(sx, sy)
}
break
}
case 'pointer_up': {
this.pointers.delete(info.pointerId)
break
}
}
}
previousScreenPoint.setTo(currentScreenPoint)
previousPagePoint.setTo(currentPagePoint)

Wyświetl plik

@ -0,0 +1,74 @@
import { uniqueId } from '../../utils/uniqueId'
import { Editor } from '../Editor'
/** @public */
export class SessionManager {
constructor(public editor: Editor) {}
/**
* The set of active sessions.
*/
private sessions = new Set<Session>()
/**
* Get an array of the current sessions.
*/
getSessions() {
return Array.from(this.sessions.values())
}
/**
* Add a session to the session manager.
*/
addSession(session: Session) {
this.sessions.add(session)
}
/**
* Remove a session from the session manager.
*/
removeSession(session: Session) {
this.sessions.delete(session)
}
/**
* Cancel and clear all sessions.
*/
clearSessions() {
this.sessions.forEach((session) => session.cancel())
this.sessions.clear()
}
}
/**
* A session is an interaction or other event that occurs over time,
* such as resizing, drawing, or transforming shapes.
*
* @public
*/
export abstract class Session<T extends object = object> {
constructor(
public editor: Editor,
public options = {} as T
) {
editor.sessions.addSession(this)
}
readonly id = uniqueId()
abstract readonly type: string
abstract start(): void
abstract update(): void
abstract complete(): void
abstract cancel(): void
abstract interrupt(): void
remove() {
this.editor.sessions.removeSession(this)
}
}

Wyświetl plik

@ -50,6 +50,30 @@ export class TickManager {
private prevPoint = new Vec()
private updatePointersVelocity = (elapsed: number) => {
if (elapsed === 0) return
this.editor.pointers.forEach((pointer) => {
const { currentScreenPoint, prevFrameScreenPoint, velocity } = pointer
const delta = Vec.Sub(currentScreenPoint, prevFrameScreenPoint)
prevFrameScreenPoint.setTo(currentScreenPoint)
const length = delta.len()
const direction = length ? delta.div(length) : new Vec(0, 0)
// consider adjusting this with an easing rather than a linear interpolation
const next = velocity.clone().lrp(direction.mul(length / elapsed), 0.5)
// if the velocity is very small, just set it to 0
if (Math.abs(next.x) < 0.01) next.x = 0
if (Math.abs(next.y) < 0.01) next.y = 0
if (!velocity.equals(next)) {
velocity.setTo(next)
}
})
}
private updatePointerVelocity = (elapsed: number) => {
const {
prevPoint,

Wyświetl plik

@ -1,23 +0,0 @@
import { Editor } from '../Editor'
/**
* A session is an interaction or other event that occurs over time,
* such as resizing, drawing, or transforming shapes.
*
* @public
*/
export abstract class Session {
constructor(public editor: Editor) {}
abstract readonly id: string
abstract start(): void
abstract update(): void
abstract complete(): void
abstract cancel(): void
abstract interrupt(): void
}

Wyświetl plik

@ -6,6 +6,22 @@ export class RootState extends StateNode {
static override initial = ''
static override children = () => []
override onTick = () => {
this.editor.sessions.getSessions().forEach((session) => session.update())
}
override onKeyUp = () => {
this.editor.sessions.getSessions().forEach((session) => session.update())
}
override onCancel = () => {
this.editor.sessions.getSessions().forEach((session) => session.cancel())
}
override onInterrupt = () => {
this.editor.sessions.getSessions().forEach((session) => session.interrupt())
}
override onKeyDown: TLEventHandlers['onKeyDown'] = (info) => {
// todo: move this logic up to the tldraw library, as the "zoom" tool only exists there
switch (info.code) {
@ -19,5 +35,7 @@ export class RootState extends StateNode {
break
}
}
this.editor.sessions.getSessions().forEach((session) => session.update())
}
}

Wyświetl plik

@ -16,24 +16,26 @@ import {
polygonsIntersect,
} from '@tldraw/editor'
export class BrushingSession extends Session {
readonly id = 'brushing'
export class BrushingSession extends Session<{ pointerId: number }> {
readonly type = 'brushing'
pointerId = -1
strategy: 'box' | 'scribble' = 'box'
isWrapMode = false
brush = new Box()
scribbleId: string | null = null
excludedShapeIds = new Set<TLShapeId>()
initialSelectedShapeIds: TLShapeId[] = []
initialStartShape?: TLShape
scribbledShapeIds = new Set<TLShapeId>()
start() {
if (!this.editor.pointers.has(this.options.pointerId)) {
throw Error('no pointer id')
this.cancel()
return
}
const { altKey, currentPagePoint } = this.editor.inputs
this.strategy = altKey ? 'scribble' : 'box'
@ -55,6 +57,11 @@ export class BrushingSession extends Session {
}
update() {
if (!this.editor.pointers.has(this.options.pointerId)) {
this.complete()
return
}
moveCameraWhenCloseToEdge(this.editor)
const strategy = this.editor.inputs.altKey ? 'scribble' : 'box'
@ -80,6 +87,7 @@ export class BrushingSession extends Session {
complete() {
this.clearStuff()
this.editor.setCurrentTool('select.idle')
this.remove()
return
}
@ -87,6 +95,7 @@ export class BrushingSession extends Session {
this.clearStuff()
this.editor.setSelectedShapes(this.initialSelectedShapeIds, { squashing: true })
this.editor.setCurrentTool('select.idle')
this.remove()
return
}
@ -110,9 +119,13 @@ export class BrushingSession extends Session {
const currentPageShapes = this.editor.getCurrentPageShapes()
const {
inputs: { originPagePoint, previousPagePoint, currentPagePoint, shiftKey },
inputs: { shiftKey },
} = this.editor
const pointer = this.editor.pointers.get(this.options.pointerId)
if (!pointer) throw Error('no pointer')
const { originPagePoint, previousPagePoint, currentPagePoint } = pointer
if (!this.scribbleId) {
// start the scribble brushing
const scribbleItem = this.editor.scribbles.addScribble({
@ -125,7 +138,7 @@ export class BrushingSession extends Session {
const { scribbledShapeIds } = this
const { x, y } = this.editor.inputs.currentPagePoint
const { x, y } = currentPagePoint
this.editor.scribbles.addPoint(this.scribbleId, x, y)
const shapes = currentPageShapes
@ -189,9 +202,13 @@ export class BrushingSession extends Session {
private boxBrush() {
const {
inputs: { originPagePoint, currentPagePoint, shiftKey, ctrlKey },
inputs: { shiftKey, ctrlKey },
} = this.editor
const pointer = this.editor.pointers.get(this.options.pointerId)
if (!pointer) throw Error('no pointer')
const { originPagePoint, currentPagePoint } = pointer
const zoomLevel = this.editor.getZoomLevel()
const currentPageShapes = this.editor.getCurrentPageShapes()
const currentPageId = this.editor.getCurrentPageId()

Wyświetl plik

@ -1,12 +1,4 @@
import {
StateNode,
TLCancelEvent,
TLEventHandlers,
TLInterruptEvent,
TLKeyboardEvent,
TLShape,
TLTickEventHandler,
} from '@tldraw/editor'
import { StateNode, TLShape } from '@tldraw/editor'
import { BrushingSession } from '../../../sessions/BrushingSession'
export class Brushing extends StateNode {
@ -23,27 +15,21 @@ export class Brushing extends StateNode {
this.session.update()
}
override onTick: TLTickEventHandler = () => {
override onTick = () => {
this.session.update()
}
override onPointerUp: TLEventHandlers['onPointerUp'] = () => {
override onPointerUp = () => {
this.session.complete()
this.session.remove()
}
override onCancel?: TLCancelEvent | undefined = () => {
override onCancel = () => {
this.session.cancel()
this.session.remove()
}
override onKeyDown: TLEventHandlers['onKeyDown'] = () => {
this.session.update()
}
override onKeyUp?: TLKeyboardEvent | undefined = () => {
this.session.update()
}
override onInterrupt: TLInterruptEvent = () => {
override onInterrupt = () => {
this.session.interrupt()
}
}

Wyświetl plik

@ -15,6 +15,7 @@ import {
createShapeId,
pointInPolygon,
} from '@tldraw/editor'
import { BrushingSession } from '../../../sessions/BrushingSession'
import { getHitShapeOnCanvasPointerDown } from '../../selection-logic/getHitShapeOnCanvasPointerDown'
import { getShouldEnterCropMode } from '../../selection-logic/getShouldEnterCropModeOnPointerDown'
import { selectOnCanvasPointerUp } from '../../selection-logic/selectOnCanvasPointerUp'
@ -41,6 +42,8 @@ export class Idle extends StateNode {
const shouldEnterCropMode = info.ctrlKey && getShouldEnterCropMode(this.editor)
console.log('hello')
if (info.ctrlKey && !shouldEnterCropMode) {
// On Mac, you can right click using the Control keys + Click.
if (info.target === 'shape' && this.isDarwin && this.editor.inputs.keys.has('ControlLeft')) {
@ -49,8 +52,8 @@ export class Idle extends StateNode {
return
}
}
this.parent.transition('brushing', info)
new BrushingSession(this.editor, { pointerId: info.pointerId })
// this.parent.transition('brushing', info)
return
}

Wyświetl plik

@ -1,4 +1,5 @@
import { StateNode, TLEventHandlers } from '@tldraw/editor'
import { BrushingSession } from '../../../sessions/BrushingSession'
import { selectOnCanvasPointerUp } from '../../selection-logic/selectOnCanvasPointerUp'
export class PointingCanvas extends StateNode {
@ -17,7 +18,10 @@ export class PointingCanvas extends StateNode {
override onPointerMove: TLEventHandlers['onPointerMove'] = (info) => {
if (this.editor.inputs.isDragging) {
this.parent.transition('brushing', info)
const session = new BrushingSession(this.editor, { pointerId: info.pointerId })
session.start()
session.update()
this.parent.transition('idle', info)
}
}

Wyświetl plik

@ -374,12 +374,12 @@ export class TestEditor extends Editor {
}
keyDown = (key: string, options = {} as Partial<Exclude<TLKeyboardEventInfo, 'key'>>) => {
this.dispatch({ ...this.getKeyboardEventInfo(key, 'key_down', options) })
this.dispatch({ ...this.getKeyboardEventInfo(key, 'key_down', options) }).forceTick()
return this
}
keyRepeat = (key: string, options = {} as Partial<Exclude<TLKeyboardEventInfo, 'key'>>) => {
this.dispatch({ ...this.getKeyboardEventInfo(key, 'key_repeat', options) })
this.dispatch({ ...this.getKeyboardEventInfo(key, 'key_repeat', options) }).forceTick()
return this
}
@ -391,7 +391,7 @@ export class TestEditor extends Editor {
altKey: this.inputs.altKey && key !== 'Alt',
...options,
}),
})
}).forceTick()
return this
}