import { StateNode, TLClickEventInfo, TLCursorType, TLPointerEventInfo, TLSelectionHandle, } from '@tldraw/editor' export const CursorTypeMap: Record = { bottom: 'ns-resize', top: 'ns-resize', left: 'ew-resize', right: 'ew-resize', bottom_left: 'nesw-resize', bottom_right: 'nwse-resize', top_left: 'nwse-resize', top_right: 'nesw-resize', bottom_left_rotate: 'swne-rotate', bottom_right_rotate: 'senw-rotate', top_left_rotate: 'nwse-rotate', top_right_rotate: 'nesw-rotate', mobile_rotate: 'grabbing', } type PointingResizeHandleInfo = Extract & { onInteractionEnd?: string | (() => void) } export class PointingResizeHandle extends StateNode { static override id = 'pointing_resize_handle' private info = {} as PointingResizeHandleInfo private pendingDoubleClick: TLClickEventInfo | null = null private updateCursor() { const cursorType = CursorTypeMap[this.info.handle!] this.editor.setCursor({ type: cursorType, rotation: this.editor.getSelectionRotation(), }) } override onEnter(info: PointingResizeHandleInfo) { this.info = info this.pendingDoubleClick = null if (typeof info.onInteractionEnd === 'string') { this.parent.setCurrentToolIdMask(info.onInteractionEnd) } this.updateCursor() } override onExit() { this.parent.setCurrentToolIdMask(undefined) } override onPointerMove() { if (this.editor.inputs.getIsDragging()) { this.startResizing() } } override onLongPress() { this.startResizing() } private startResizing() { if (this.editor.getIsReadonly()) return this.parent.transition('resizing', this.info) } override onPointerUp() { if (this.pendingDoubleClick) { this.parent.transition('idle') this.parent.getCurrent()?.handleEvent(this.pendingDoubleClick) return } this.complete() } // A double click's 'down' phase arrives while the second press is still held. Acting on it // immediately would steal a press that is about to become a drag (#9499), so it is deferred // to pointer up. override onDoubleClick(info: TLClickEventInfo) { if ( this.editor.inputs.getShiftKey() || info.phase !== 'down' || info.ctrlKey || info.shiftKey ) { return } this.pendingDoubleClick = info } override onCancel() { this.cancel() } override onComplete() { this.cancel() } override onInterrupt() { this.cancel() } private complete() { const { onInteractionEnd } = this.info if (onInteractionEnd) { if (typeof onInteractionEnd === 'string') { this.editor.setCurrentTool(onInteractionEnd, {}) } else { onInteractionEnd() } return } this.parent.transition('idle') } private cancel() { const { onInteractionEnd } = this.info if (onInteractionEnd) { if (typeof onInteractionEnd === 'string') { this.editor.setCurrentTool(onInteractionEnd, {}) } else { onInteractionEnd() } return } this.parent.transition('idle') } }