[fix] Text on Safari (#232)
* Fix editing bug on safari text * Fix text behavior when blurring * Update SelectTool.ts
This commit is contained in:
parent
0b5a516b57
commit
6592608a09
6 changed files with 62 additions and 47 deletions
|
@ -60,4 +60,4 @@
|
|||
"tsconfig-replace-paths": "^0.0.5"
|
||||
},
|
||||
"gitHead": "083b36e167b6911927a6b58cbbb830b11b33f00a"
|
||||
}
|
||||
}
|
|
@ -15,7 +15,6 @@ import {
|
|||
TLWheelEventHandler,
|
||||
Utils,
|
||||
TLBounds,
|
||||
Inputs,
|
||||
} from '@tldraw/core'
|
||||
import {
|
||||
FlipType,
|
||||
|
@ -134,6 +133,8 @@ export class TLDrawState extends StateManager<TLDrawSnapshot> {
|
|||
|
||||
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<TLDrawSnapshot> {
|
|||
* @param id [string]
|
||||
*/
|
||||
setEditingId = (id?: string) => {
|
||||
this.editingStartTime = Date.now()
|
||||
this.patchState(
|
||||
{
|
||||
document: {
|
||||
|
@ -2527,14 +2529,15 @@ export class TLDrawState extends StateManager<TLDrawSnapshot> {
|
|||
}
|
||||
|
||||
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')
|
||||
|
|
|
@ -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<T, E> {
|
|||
)
|
||||
}
|
||||
|
||||
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 = <K extends TLDrawShape>(
|
||||
|
|
|
@ -17,6 +17,8 @@ export class StickyUtil extends TLDrawShapeUtil<T, E> {
|
|||
|
||||
canBind = true
|
||||
|
||||
canEdit = true
|
||||
|
||||
getShape = (props: Partial<T>): T => {
|
||||
return Utils.deepMerge<T>(
|
||||
{
|
||||
|
@ -89,25 +91,16 @@ export class StickyUtil extends TLDrawShapeUtil<T, E> {
|
|||
[shape, onShapeChange]
|
||||
)
|
||||
|
||||
const handleBlur = React.useCallback(
|
||||
(e: React.FocusEvent<HTMLTextAreaElement>) => {
|
||||
if (!isEditing) return
|
||||
if (rIsMounted.current) {
|
||||
e.currentTarget.setSelectionRange(0, 0)
|
||||
onShapeBlur?.()
|
||||
}
|
||||
},
|
||||
[isEditing]
|
||||
)
|
||||
const handleBlur = React.useCallback((e: React.FocusEvent<HTMLTextAreaElement>) => {
|
||||
e.currentTarget.setSelectionRange(0, 0)
|
||||
onShapeBlur?.()
|
||||
}, [])
|
||||
|
||||
const handleFocus = React.useCallback(
|
||||
(e: React.FocusEvent<HTMLTextAreaElement>) => {
|
||||
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<T, E> {
|
|||
// 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<T, E> {
|
|||
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<T, E> {
|
|||
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}
|
||||
/>
|
||||
)}
|
||||
</StyledStickyContainer>
|
||||
|
|
|
@ -77,16 +77,10 @@ export class TextUtil extends TLDrawShapeUtil<T, E> {
|
|||
[shape, onShapeChange]
|
||||
)
|
||||
|
||||
const handleBlur = React.useCallback(
|
||||
(e: React.FocusEvent<HTMLTextAreaElement>) => {
|
||||
if (!isEditing) return
|
||||
if (rIsMounted.current) {
|
||||
e.currentTarget.setSelectionRange(0, 0)
|
||||
onShapeBlur?.()
|
||||
}
|
||||
},
|
||||
[isEditing]
|
||||
)
|
||||
const handleBlur = React.useCallback((e: React.FocusEvent<HTMLTextAreaElement>) => {
|
||||
e.currentTarget.setSelectionRange(0, 0)
|
||||
onShapeBlur?.()
|
||||
}, [])
|
||||
|
||||
const handleFocus = React.useCallback(
|
||||
(e: React.FocusEvent<HTMLTextAreaElement>) => {
|
||||
|
@ -117,6 +111,8 @@ export class TextUtil extends TLDrawShapeUtil<T, E> {
|
|||
elm.focus()
|
||||
elm.select()
|
||||
})
|
||||
} else {
|
||||
onShapeBlur?.()
|
||||
}
|
||||
}, [isEditing])
|
||||
|
||||
|
@ -156,14 +152,14 @@ export class TextUtil extends TLDrawShapeUtil<T, E> {
|
|||
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"
|
||||
|
|
|
@ -429,9 +429,7 @@ export class SelectTool extends BaseTool<Status> {
|
|||
|
||||
// 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<Status> {
|
|||
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!
|
||||
|
|
Loading…
Reference in a new issue