Remove targeted editing from text (#1962)
This PR has been tested on Mac Chrome, iOS Safari, and Android Chrome. --- This PR removes 'targeted editing' from text. This affects when you're: * using the text tool * editing a text shape * editing a text label * editing an arrow label When in one of these modes, you were able to click on some other text to immediately start editing it (as long as that text is the same type). It was a bit broken with some of the newer changes, so this PR removes it. The issues included: * selected text 'flashing' * caret going to the start of the text * empty text shapes not disappearing * inconsistent behaviour when clicking near a shape VS on a shape It feels a bit simpler now too, I like it... 🤔💭  ### Change Type - [x] `patch` — Bug fix ### Test Plan - [ ] Unit Tests - [ ] End to end tests ### Release Notes - Fixed some cases where text would get selected in the wrong place. - Changed the behaviour of text selection. Removed 'deep editing'. --------- Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This commit is contained in:
parent
5668209b01
commit
b149fe7e98
6 changed files with 58 additions and 95 deletions
|
@ -25,9 +25,13 @@ export const ArrowTextLabel = React.memo(function ArrowTextLabel({
|
|||
handleChange,
|
||||
isEmpty,
|
||||
handleInputPointerDown,
|
||||
handleDoubleClick,
|
||||
} = useEditableText(id, 'arrow', text)
|
||||
|
||||
if (!isEditing && isEmpty) {
|
||||
const finalText = TextHelpers.normalizeTextForDom(text)
|
||||
const hasText = finalText.trim().length > 0
|
||||
|
||||
if (!isEditing && !hasText) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -74,6 +78,7 @@ export const ArrowTextLabel = React.memo(function ArrowTextLabel({
|
|||
onBlur={handleBlur}
|
||||
onContextMenu={stopEventPropagation}
|
||||
onPointerDown={handleInputPointerDown}
|
||||
onDoubleClick={handleDoubleClick}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -46,7 +46,6 @@ export const TextLabel = React.memo(function TextLabel<
|
|||
rInput,
|
||||
isEmpty,
|
||||
isEditing,
|
||||
isEditingSameShapeType,
|
||||
handleFocus,
|
||||
handleChange,
|
||||
handleKeyDown,
|
||||
|
@ -55,13 +54,15 @@ export const TextLabel = React.memo(function TextLabel<
|
|||
handleDoubleClick,
|
||||
} = useEditableText(id, type, text)
|
||||
|
||||
const isInteractive = isEditing || isEditingSameShapeType
|
||||
const finalText = TextHelpers.normalizeTextForDom(text)
|
||||
const hasText = finalText.trim().length > 0
|
||||
|
||||
const legacyAlign = isLegacyAlign(align)
|
||||
const theme = useDefaultColorTheme()
|
||||
|
||||
if (!isInteractive && !hasText) return null
|
||||
if (!isEditing && !hasText) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -85,7 +86,6 @@ export const TextLabel = React.memo(function TextLabel<
|
|||
: {}),
|
||||
}}
|
||||
>
|
||||
{isEmpty && !isInteractive ? null : (
|
||||
<div
|
||||
className="tl-text-label__inner"
|
||||
style={{
|
||||
|
@ -99,7 +99,7 @@ export const TextLabel = React.memo(function TextLabel<
|
|||
<div className="tl-text tl-text-content" dir="ltr">
|
||||
{finalText}
|
||||
</div>
|
||||
{isInteractive && (
|
||||
{isEditing && (
|
||||
<textarea
|
||||
ref={rInput}
|
||||
className="tl-text tl-text-input"
|
||||
|
@ -109,7 +109,7 @@ export const TextLabel = React.memo(function TextLabel<
|
|||
autoCapitalize="false"
|
||||
autoCorrect="false"
|
||||
autoSave="false"
|
||||
autoFocus={isEditing}
|
||||
autoFocus
|
||||
placeholder=""
|
||||
spellCheck="true"
|
||||
wrap="off"
|
||||
|
@ -126,7 +126,6 @@ export const TextLabel = React.memo(function TextLabel<
|
|||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -25,15 +25,6 @@ export function useEditableText<T extends Extract<TLShape, { props: { text: stri
|
|||
|
||||
const isEditing = useValue('isEditing', () => editor.editingShapeId === id, [editor, id])
|
||||
|
||||
const isEditingSameShapeType = useValue(
|
||||
'is editing same shape type',
|
||||
() => {
|
||||
const { editingShape } = editor
|
||||
return editingShape && editingShape.type === type
|
||||
},
|
||||
[type, id]
|
||||
)
|
||||
|
||||
// If the shape is editing but the input element not focused, focus the element
|
||||
useEffect(() => {
|
||||
const elm = rInput.current
|
||||
|
@ -191,17 +182,6 @@ export function useEditableText<T extends Extract<TLShape, { props: { text: stri
|
|||
|
||||
const handleInputPointerDown = useCallback(
|
||||
(e: React.PointerEvent) => {
|
||||
const { editingShape } = editor
|
||||
|
||||
if (editingShape) {
|
||||
// If there's an editing shape and it's the same type as this shape,
|
||||
// then we can "deep edit" into this shape. Note that this won't work
|
||||
// as expected with the note shape—in that case clicking outside of the
|
||||
// input will not set skipSelectOnFocus to true, and so the input will
|
||||
// blur, re-select, and then re-select-all on a second tap.
|
||||
rSkipSelectOnFocus.current = type === editingShape.type
|
||||
}
|
||||
|
||||
editor.dispatch({
|
||||
...getPointerInfo(e),
|
||||
type: 'pointer',
|
||||
|
@ -212,7 +192,7 @@ export function useEditableText<T extends Extract<TLShape, { props: { text: stri
|
|||
|
||||
stopEventPropagation(e) // we need to prevent blurring the input
|
||||
},
|
||||
[editor, id, type]
|
||||
[editor, id]
|
||||
)
|
||||
|
||||
const handleDoubleClick = stopEventPropagation
|
||||
|
@ -220,7 +200,6 @@ export function useEditableText<T extends Extract<TLShape, { props: { text: stri
|
|||
return {
|
||||
rInput,
|
||||
isEditing,
|
||||
isEditingSameShapeType,
|
||||
handleFocus,
|
||||
handleBlur,
|
||||
handleKeyDown,
|
||||
|
|
|
@ -78,12 +78,12 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
|
|||
rInput,
|
||||
isEmpty,
|
||||
isEditing,
|
||||
isEditingSameShapeType,
|
||||
handleFocus,
|
||||
handleChange,
|
||||
handleKeyDown,
|
||||
handleBlur,
|
||||
handleInputPointerDown,
|
||||
handleDoubleClick,
|
||||
} = useEditableText(id, type, text)
|
||||
|
||||
const zoomLevel = useValue('zoomLevel', () => this.editor.zoomLevel, [this.editor])
|
||||
|
@ -113,7 +113,7 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
|
|||
<div className="tl-text tl-text-content" dir="ltr">
|
||||
{text}
|
||||
</div>
|
||||
{isEditing || isEditingSameShapeType ? (
|
||||
{isEditing ? (
|
||||
<textarea
|
||||
ref={rInput}
|
||||
className="tl-text tl-text-input"
|
||||
|
@ -137,6 +137,7 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
|
|||
onTouchEnd={stopEventPropagation}
|
||||
onContextMenu={stopEventPropagation}
|
||||
onPointerDown={handleInputPointerDown}
|
||||
onDoubleClick={handleDoubleClick}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { StateNode, TLEventHandlers, TLGroupShape, TLTextShape } from '@tldraw/editor'
|
||||
import { StateNode, TLEventHandlers } from '@tldraw/editor'
|
||||
import { updateHoveredId } from '../../../tools/selection-logic/updateHoveredId'
|
||||
|
||||
export class Idle extends StateNode {
|
||||
|
@ -14,26 +14,6 @@ export class Idle extends StateNode {
|
|||
}
|
||||
|
||||
override onPointerDown: TLEventHandlers['onPointerDown'] = (info) => {
|
||||
const { hoveredShape } = this.editor
|
||||
const hitShape =
|
||||
hoveredShape && !this.editor.isShapeOfType<TLGroupShape>(hoveredShape, 'group')
|
||||
? hoveredShape
|
||||
: this.editor.getShapeAtPoint(this.editor.inputs.currentPagePoint)
|
||||
if (hitShape) {
|
||||
if (this.editor.isShapeOfType<TLTextShape>(hitShape, 'text')) {
|
||||
requestAnimationFrame(() => {
|
||||
this.editor.setSelectedShapes([hitShape.id])
|
||||
this.editor.setEditingShape(hitShape.id)
|
||||
this.editor.setCurrentTool('select.editing_shape', {
|
||||
...info,
|
||||
target: 'shape',
|
||||
shape: hitShape,
|
||||
})
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.parent.transition('pointing', info)
|
||||
}
|
||||
|
||||
|
|
|
@ -84,8 +84,7 @@ export class EditingShape extends StateNode {
|
|||
// If we clicked on the editing geo / arrow shape's label, do nothing
|
||||
return
|
||||
} else {
|
||||
this.editor.setEditingShape(shape)
|
||||
this.editor.select(shape)
|
||||
this.parent.transition('pointing_shape', info)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -98,9 +97,9 @@ export class EditingShape extends StateNode {
|
|||
}
|
||||
// If we clicked on the editing shape (which isn't a shape with a label), do nothing
|
||||
} else {
|
||||
// But if we clicked on a different shape of the same type, edit it instead
|
||||
this.editor.setEditingShape(shape)
|
||||
this.editor.select(shape)
|
||||
// But if we clicked on a different shape of the same type, transition to pointing_shape instead
|
||||
this.parent.transition('pointing_shape', info)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue