Skip to content

Commit 37327cc

Browse files
authored
fix (inspector): typing in text controls moves the cursor to the end (#3400)
Co-authored-by: [email protected] <>
1 parent c049a44 commit 37327cc

File tree

4 files changed

+53
-7
lines changed

4 files changed

+53
-7
lines changed

src/components/advanced-text-control/index.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/**
22
* Internal dependencies
33
*/
4+
import { useInternalValue } from '~stackable/hooks'
45
import AdvancedControl, { extractControlProps } from '../base-control2'
56
import { useControlHandlers } from '../base-control2/hooks'
67
import { ResetButton } from '../base-control2/reset-button'
@@ -40,6 +41,15 @@ const AdvancedTextControl = memo( props => {
4041
isFormatType,
4142
} )
4243

44+
// Track the value internally, because if not the value will be updated on
45+
// every render and will make the cursor jump to the end.
46+
const [ internalValue, setInternalValue ] = useInternalValue( typeof props.value === 'undefined' ? value : props.value )
47+
const _onChange = typeof props.onChange === 'undefined' ? onChange : props.onChange
48+
const internalOnChange = value => {
49+
setInternalValue( value )
50+
_onChange( value )
51+
}
52+
4353
const TextInput = isMultiline ? TextareaControl : TextControl
4454

4555
return (
@@ -54,16 +64,16 @@ const AdvancedTextControl = memo( props => {
5464
>
5565
<TextInput
5666
{ ...inputProps }
57-
value={ typeof props.value === 'undefined' ? value : props.value }
58-
onChange={ typeof props.onChange === 'undefined' ? onChange : props.onChange }
67+
value={ internalValue }
68+
onChange={ internalOnChange }
5969
className={ classnames( propsToPass.className, 'ugb-advanced-text-control' ) }
6070
/>
6171
</DynamicContentControl>
6272
<ResetButton
6373
allowReset={ allowReset && ! props.isDynamic }
64-
value={ typeof props.value === 'undefined' ? value : props.value }
74+
value={ internalValue }
6575
default={ props.default }
66-
onChange={ typeof props.onChange === 'undefined' ? onChange : props.onChange }
76+
onChange={ internalOnChange }
6777
hasPanelModifiedIndicator={ props.hasPanelModifiedIndicator }
6878
/>
6979
</AdvancedControl>

src/components/image-alt-control/index.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,28 @@ import { i18n } from 'stackable'
88
*/
99
import { TextareaControl, ExternalLink } from '@wordpress/components'
1010
import { __ } from '@wordpress/i18n'
11-
import { Fragment } from '@wordpress/element'
11+
import { useInternalValue } from '~stackable/hooks'
1212

1313
const ImageAltControl = props => {
14+
// Keep track of the internal value because we will move the cursor to the
15+
// end of the text when the user types.
16+
const [ internalValue, setInternalValue ] = useInternalValue( props.value )
17+
1418
return (
1519
<TextareaControl
1620
{ ...props }
21+
value={ internalValue }
22+
onChange={ value => {
23+
setInternalValue( value )
24+
props.onChange( value )
25+
} }
1726
help={
18-
<Fragment>
27+
<>
1928
<ExternalLink href="https://www.w3.org/WAI/tutorials/images/decision-tree">
2029
{ __( 'Describe the purpose of the image', i18n ) }
2130
</ExternalLink>
2231
{ __( 'Leave empty if the image is purely decorative.', i18n ) }
23-
</Fragment>
32+
</>
2433
}
2534
/>
2635
)

src/hooks/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export * from './use-saved-default-block-style'
99
export * from './use-font-loader'
1010
export * from './use-attribute-edit-handlers'
1111
export * from './use-attribute-name'
12+
export * from './use-internal-value'
1213
export * from './use-linking'
1314
export * from './use-block-hover-state'
1415
export * from './use-on-screen'

src/hooks/use-internal-value.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// This files is based on
2+
// https://github.com/WordPress/gutenberg/blob/trunk/packages/block-editor/src/components/link-control/use-internal-value.js
3+
// This is a hook that keeps track of the internal value of a component, and
4+
// updates the value only when the value changes.
5+
6+
/**
7+
* WordPress dependencies
8+
*/
9+
import { useState } from '@wordpress/element'
10+
11+
/**
12+
* External dependencies
13+
*/
14+
import fastDeepEqual from 'fast-deep-equal'
15+
16+
export const useInternalValue = value => {
17+
const [ internalValue, setInternalValue ] = useState( value )
18+
const [ previousValue, setPreviousValue ] = useState( value )
19+
20+
if ( ! fastDeepEqual( value, previousValue ) ) {
21+
setPreviousValue( value )
22+
setInternalValue( value )
23+
}
24+
25+
return [ internalValue, setInternalValue ]
26+
}

0 commit comments

Comments
 (0)