From 51c3e0f6b500374a84c73ab10f4d352c8ec60649 Mon Sep 17 00:00:00 2001 From: Daniel Lautzenheiser Date: Thu, 29 Sep 2022 08:56:55 -0400 Subject: [PATCH] Revert string control to class component --- .../Editor/EditorControlPane/Widget.js | 8 +-- .../src/widgets/string/StringControl.tsx | 70 +++++++++---------- 2 files changed, 36 insertions(+), 42 deletions(-) diff --git a/packages/netlify-cms-core/src/components/Editor/EditorControlPane/Widget.js b/packages/netlify-cms-core/src/components/Editor/EditorControlPane/Widget.js index ca74db83..3bbaebc7 100644 --- a/packages/netlify-cms-core/src/components/Editor/EditorControlPane/Widget.js +++ b/packages/netlify-cms-core/src/components/Editor/EditorControlPane/Widget.js @@ -2,15 +2,10 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { Map, List } from 'immutable'; -import { oneLine } from 'common-tags'; import { getRemarkPlugins } from '../../../lib/registry'; import ValidationErrorTypes from '../../../constants/validationErrorTypes'; -function truthy() { - return { error: false }; -} - function isEmpty(value) { return ( value === null || @@ -23,7 +18,7 @@ function isEmpty(value) { export default class Widget extends Component { static propTypes = { - controlComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).isRequired, + controlComponent: PropTypes.func.isRequired, validator: PropTypes.func, field: ImmutablePropTypes.map.isRequired, hasActiveStyle: PropTypes.bool, @@ -308,6 +303,7 @@ export default class Widget extends Component { onRemoveInsertedMedia, getAsset, forID: uniqueFieldId, + ref: this.processInnerControlRef, validate: this.validate, classNameWrapper, classNameWidget, diff --git a/packages/netlify-cms-core/src/widgets/string/StringControl.tsx b/packages/netlify-cms-core/src/widgets/string/StringControl.tsx index bdac6191..6dba07ba 100644 --- a/packages/netlify-cms-core/src/widgets/string/StringControl.tsx +++ b/packages/netlify-cms-core/src/widgets/string/StringControl.tsx @@ -1,52 +1,50 @@ -import React, { forwardRef, useCallback, useEffect, useState } from 'react'; -import { CmsWidgetControlProps } from '../../interface'; +import React from 'react'; -const StringControl = forwardRef>( - ({ - onChange, - forID, - value = '', - classNameWrapper, - setActiveStyle, - setInactiveStyle, - }: CmsWidgetControlProps, _ref) => { - const [element, setElement] = useState(null); - const [selection, setSelection] = useState(null); +import type { ChangeEvent } from 'react'; +import type { CmsWidgetControlProps } from '../../interface'; - const handleChange = useCallback((e: React.ChangeEvent) => { - setSelection(e.target.selectionStart); - onChange(e.target.value); - }, []); +export default class StringControl extends React.Component> { + // The selection to maintain for the input element + private _sel: number | null = 0; - const handleSetElement = useCallback((el: HTMLInputElement) => { - setElement(el); - }, []); + // The input element ref + private _el: HTMLInputElement | null = null; - useEffect(() => { - if (!element) { - return; - } + // NOTE: This prevents the cursor from jumping to the end of the text for + // nested inputs. In other words, this is not an issue on top-level text + // fields such as the `title` of a collection post. However, it becomes an + // issue on fields nested within other components, namely widgets nested + // within a `markdown` widget. For example, the alt text on a block image + // within markdown. + // SEE: https://github.com/netlify/netlify-cms/issues/4539 + // SEE: https://github.com/netlify/netlify-cms/issues/3578 + componentDidUpdate() { + if (this._el && this._el.selectionStart !== this._sel) { + this._el.setSelectionRange(this._sel, this._sel); + } + } - if (element.selectionStart !== selection) { - element.setSelectionRange(selection, selection); - } - }, [element, element?.selectionStart, selection]); + handleChange = (e: ChangeEvent) => { + this._sel = e.target.selectionStart; + this.props.onChange(e.target.value); + }; + + render() { + const { forID, value, classNameWrapper, setActiveStyle, setInactiveStyle } = this.props; return ( { + this._el = el; + }} type="text" id={forID} className={classNameWrapper} value={value || ''} - onChange={handleChange} + onChange={this.handleChange} onFocus={setActiveStyle} onBlur={setInactiveStyle} /> ); - }, -); - -StringControl.displayName = 'StringControl'; - -export default StringControl; + } +}