diff --git a/packages/tldraw/package.json b/packages/tldraw/package.json index 4272a9ca2..fbd7f9191 100644 --- a/packages/tldraw/package.json +++ b/packages/tldraw/package.json @@ -60,4 +60,4 @@ "tsconfig-replace-paths": "^0.0.5" }, "gitHead": "083b36e167b6911927a6b58cbbb830b11b33f00a" -} +} \ No newline at end of file diff --git a/packages/tldraw/src/state/TLDrawState.ts b/packages/tldraw/src/state/TLDrawState.ts index 02da22fc2..10dd50bbf 100644 --- a/packages/tldraw/src/state/TLDrawState.ts +++ b/packages/tldraw/src/state/TLDrawState.ts @@ -15,7 +15,6 @@ import { TLWheelEventHandler, Utils, TLBounds, - Inputs, } from '@tldraw/core' import { FlipType, @@ -134,6 +133,8 @@ export class TLDrawState extends StateManager { currentTool: BaseTool = this.tools.select + editingStartTime = -1 + private isCreating = false // The editor's bounding client rect @@ -486,6 +487,7 @@ export class TLDrawState extends StateManager { * @param id [string] */ setEditingId = (id?: string) => { + this.editingStartTime = Date.now() this.patchState( { document: { @@ -2527,14 +2529,15 @@ export class TLDrawState extends StateManager { } onShapeBlur = () => { + // This prevents an auto-blur event from Safari + if (Date.now() - this.editingStartTime < 50) return + const { editingId } = this.pageState if (editingId) { // If we're editing text, then delete the text if it's empty const shape = this.getShape(editingId) - this.setEditingId() - if (shape.type === TLDrawShapeType.Text) { if (shape.text.trim().length <= 0) { this.setState(Commands.deleteShapes(this.state, [editingId]), 'delete_empty_text') diff --git a/packages/tldraw/src/state/shapes/EllipseUtil/EllipseUtil.tsx b/packages/tldraw/src/state/shapes/EllipseUtil/EllipseUtil.tsx index 229692430..3af8b172a 100644 --- a/packages/tldraw/src/state/shapes/EllipseUtil/EllipseUtil.tsx +++ b/packages/tldraw/src/state/shapes/EllipseUtil/EllipseUtil.tsx @@ -12,7 +12,11 @@ import { } from '~types' import { BINDING_DISTANCE } from '~constants' import { TLDrawShapeUtil } from '../TLDrawShapeUtil' -import { intersectLineSegmentEllipse, intersectRayEllipse } from '@tldraw/intersect' +import { + intersectEllipseBounds, + intersectLineSegmentEllipse, + intersectRayEllipse, +} from '@tldraw/intersect' import { getEllipseIndicatorPathTLDrawSnapshot, getEllipsePath } from './ellipseHelpers' type T = EllipseShape @@ -156,12 +160,27 @@ export class EllipseUtil extends TLDrawShapeUtil { ) } + hitTestBounds = (shape: T, bounds: TLBounds): boolean => { + const shapeBounds = this.getBounds(shape) + + return ( + Utils.boundsContained(shapeBounds, bounds) || + intersectEllipseBounds( + this.getCenter(shape), + shape.radius[0], + shape.radius[1], + shape.rotation || 0, + bounds + ).length > 0 + ) + } + shouldRender = (prev: T, next: T): boolean => { return next.radius !== prev.radius || next.style !== prev.style } getCenter = (shape: T): number[] => { - return [shape.point[0] + shape.radius[0], shape.point[1] + shape.radius[1]] + return Vec.add(shape.point, shape.radius) } getBindingPoint = ( diff --git a/packages/tldraw/src/state/shapes/StickyUtil/StickyUtil.tsx b/packages/tldraw/src/state/shapes/StickyUtil/StickyUtil.tsx index d7f496f44..18d2cd5aa 100644 --- a/packages/tldraw/src/state/shapes/StickyUtil/StickyUtil.tsx +++ b/packages/tldraw/src/state/shapes/StickyUtil/StickyUtil.tsx @@ -17,6 +17,8 @@ export class StickyUtil extends TLDrawShapeUtil { canBind = true + canEdit = true + getShape = (props: Partial): T => { return Utils.deepMerge( { @@ -89,25 +91,16 @@ export class StickyUtil extends TLDrawShapeUtil { [shape, onShapeChange] ) - const handleBlur = React.useCallback( - (e: React.FocusEvent) => { - if (!isEditing) return - if (rIsMounted.current) { - e.currentTarget.setSelectionRange(0, 0) - onShapeBlur?.() - } - }, - [isEditing] - ) + const handleBlur = React.useCallback((e: React.FocusEvent) => { + e.currentTarget.setSelectionRange(0, 0) + onShapeBlur?.() + }, []) const handleFocus = React.useCallback( (e: React.FocusEvent) => { if (!isEditing) return if (!rIsMounted.current) return - - if (document.activeElement === e.currentTarget) { - e.currentTarget.select() - } + e.currentTarget.select() }, [isEditing] ) @@ -115,14 +108,10 @@ export class StickyUtil extends TLDrawShapeUtil { // Focus when editing changes to true React.useEffect(() => { if (isEditing) { - if (document.activeElement !== rText.current) { - requestAnimationFrame(() => { - rIsMounted.current = true - const elm = rTextArea.current! - elm.focus() - elm.select() - }) - } + rIsMounted.current = true + const elm = rTextArea.current! + elm.focus() + elm.select() } }, [isEditing]) @@ -151,6 +140,9 @@ export class StickyUtil extends TLDrawShapeUtil { onShapeChange?.({ id: shape.id, size: [size[0], MIN_CONTAINER_HEIGHT] }) return } + + const textarea = rTextArea.current + textarea?.focus() }, [shape.text, shape.size[1], shape.style]) const style = { @@ -180,10 +172,13 @@ export class StickyUtil extends TLDrawShapeUtil { onKeyDown={handleKeyDown} onFocus={handleFocus} onBlur={handleBlur} - autoCapitalize="off" - autoComplete="off" - spellCheck={false} + tabIndex={-1} + autoComplete="false" + autoCapitalize="false" + autoCorrect="false" + autoSave="false" autoFocus + spellCheck={false} /> )} diff --git a/packages/tldraw/src/state/shapes/TextUtil/TextUtil.tsx b/packages/tldraw/src/state/shapes/TextUtil/TextUtil.tsx index 36d5b0127..4a244770f 100644 --- a/packages/tldraw/src/state/shapes/TextUtil/TextUtil.tsx +++ b/packages/tldraw/src/state/shapes/TextUtil/TextUtil.tsx @@ -77,16 +77,10 @@ export class TextUtil extends TLDrawShapeUtil { [shape, onShapeChange] ) - const handleBlur = React.useCallback( - (e: React.FocusEvent) => { - if (!isEditing) return - if (rIsMounted.current) { - e.currentTarget.setSelectionRange(0, 0) - onShapeBlur?.() - } - }, - [isEditing] - ) + const handleBlur = React.useCallback((e: React.FocusEvent) => { + e.currentTarget.setSelectionRange(0, 0) + onShapeBlur?.() + }, []) const handleFocus = React.useCallback( (e: React.FocusEvent) => { @@ -117,6 +111,8 @@ export class TextUtil extends TLDrawShapeUtil { elm.focus() elm.select() }) + } else { + onShapeBlur?.() } }, [isEditing]) @@ -156,14 +152,14 @@ export class TextUtil extends TLDrawShapeUtil { autoCapitalize="false" autoCorrect="false" autoSave="false" + autoFocus placeholder="" color={styles.stroke} onFocus={handleFocus} - onBlur={handleBlur} onChange={handleChange} onKeyDown={handleKeyDown} + onBlur={handleBlur} onPointerDown={handlePointerDown} - autoFocus wrap="off" dir="auto" datatype="wysiwyg" diff --git a/packages/tldraw/src/state/tools/SelectTool/SelectTool.ts b/packages/tldraw/src/state/tools/SelectTool/SelectTool.ts index 67932e1d4..f21142a49 100644 --- a/packages/tldraw/src/state/tools/SelectTool/SelectTool.ts +++ b/packages/tldraw/src/state/tools/SelectTool/SelectTool.ts @@ -429,9 +429,7 @@ export class SelectTool extends BaseTool { // Unless the user is holding shift or meta, clear the current selection if (!info.shiftKey) { - if (this.state.pageState.editingId) { - this.state.setEditingId() - } + this.state.onShapeBlur() if (info.altKey && this.state.selectedIds.length > 0) { this.state.duplicate(this.state.selectedIds, this.state.getPagePoint(info.point)) @@ -459,7 +457,11 @@ export class SelectTool extends BaseTool { return } - const { hoveredId } = this.state.pageState + const { editingId, hoveredId } = this.state.pageState + + if (editingId && info.target !== editingId) { + this.state.onShapeBlur() + } // While holding command and shift, select or deselect // the shape, ignoring any group that may contain it. Yikes!