From f68703ea38d31b53fea92814380cb9fba69a129d Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Tue, 12 Nov 2019 19:12:31 -0500 Subject: [PATCH 1/2] [core] feat(EditableText): add alwaysRenderInput experimental feature --- .../components/editable-text/editableText.tsx | 63 +++++++++++++------ .../core-examples/editableTextExample.tsx | 10 +++ 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/packages/core/src/components/editable-text/editableText.tsx b/packages/core/src/components/editable-text/editableText.tsx index 9d4ae3b30e0..279578906ab 100644 --- a/packages/core/src/components/editable-text/editableText.tsx +++ b/packages/core/src/components/editable-text/editableText.tsx @@ -23,6 +23,19 @@ import { clamp, safeInvoke } from "../../common/utils"; import { Browser } from "../../compatibility"; export interface IEditableTextProps extends IIntentProps, IProps { + /** + * EXPERIMENTAL FEATURE. + * + * When true, this forces the component to _always_ render an editable input (or textarea) + * both when the component is focussed and unfocussed, instead of the component's default + * behavior of switching between a text span and a text input upon interaction. + * + * This behavior can help in certain applications where, for example, a custom right-click + * context menu is used to supply clipboard copy and paste functionality. + * @default false + */ + alwaysRenderInput?: boolean; + /** * If `true` and in multiline mode, the `enter` key will trigger onConfirm and `mod+enter` * will insert a newline. If `false`, the key bindings are inverted such that `enter` @@ -123,6 +136,7 @@ export class EditableText extends AbstractPureComponent2 { - if (input != null) { - input.focus(); + if (input != null && this.state != null && this.state.isEditing) { const supportsSelection = inputSupportsSelection(input); if (supportsSelection) { const { length } = input.value; @@ -168,7 +181,7 @@ export class EditableText extends AbstractPureComponent2 - {this.maybeRenderInput(value)} - - {hasValue ? value : this.props.placeholder} - + {alwaysRenderInput || this.state.isEditing ? this.renderInput(value) : undefined} + {shouldHideContents ? ( + undefined + ) : ( + + {hasValue ? value : this.props.placeholder} + + )} ); } @@ -296,11 +319,8 @@ export class EditableText extends AbstractPureComponent2 = { className: Classes.EDITABLE_TEXT_INPUT, maxLength, @@ -308,13 +328,18 @@ export class EditableText extends AbstractPureComponent2 ) : ( diff --git a/packages/docs-app/src/examples/core-examples/editableTextExample.tsx b/packages/docs-app/src/examples/core-examples/editableTextExample.tsx index 82cb39ddeef..02f715f5024 100644 --- a/packages/docs-app/src/examples/core-examples/editableTextExample.tsx +++ b/packages/docs-app/src/examples/core-examples/editableTextExample.tsx @@ -24,6 +24,7 @@ import { IntentSelect } from "./common/intentSelect"; const INPUT_ID = "EditableTextExample-max-length"; export interface IEditableTextExampleState { + alwaysRenderInput?: boolean; confirmOnEnterKey?: boolean; intent?: Intent; maxLength?: number; @@ -33,6 +34,7 @@ export interface IEditableTextExampleState { export class EditableTextExample extends React.PureComponent { public state: IEditableTextExampleState = { + alwaysRenderInput: true, confirmOnEnterKey: false, report: "", selectAllOnFocus: false, @@ -41,12 +43,14 @@ export class EditableTextExample extends React.PureComponent this.setState({ intent })); private toggleSelectAll = handleBooleanChange(selectAllOnFocus => this.setState({ selectAllOnFocus })); private toggleSwap = handleBooleanChange(confirmOnEnterKey => this.setState({ confirmOnEnterKey })); + private toggleAlwaysRenderInput = handleBooleanChange(alwaysRenderInput => this.setState({ alwaysRenderInput })); public render() { return (

Swap keypress for confirm and newline (multiline only) + ); } From d4a5b7e3030bf146dc1630b5788a63b95c76777a Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Tue, 12 Nov 2019 19:18:31 -0500 Subject: [PATCH 2/2] Fix selectAllOnFocus for new prop --- .../components/editable-text/editableText.tsx | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/packages/core/src/components/editable-text/editableText.tsx b/packages/core/src/components/editable-text/editableText.tsx index 279578906ab..c761c8a2954 100644 --- a/packages/core/src/components/editable-text/editableText.tsx +++ b/packages/core/src/components/editable-text/editableText.tsx @@ -148,20 +148,25 @@ export class EditableText extends AbstractPureComponent2 { this.valueElement = spanElement; }, input: (input: HTMLInputElement | HTMLTextAreaElement) => { - if (input != null && this.state != null && this.state.isEditing) { - const supportsSelection = inputSupportsSelection(input); - if (supportsSelection) { - const { length } = input.value; - input.setSelectionRange(this.props.selectAllOnFocus ? 0 : length, length); - } - if (!supportsSelection || !this.props.selectAllOnFocus) { - input.scrollLeft = input.scrollWidth; + if (input != null) { + this.inputElement = input; + + if (this.state != null && this.state.isEditing) { + const supportsSelection = inputSupportsSelection(input); + if (supportsSelection) { + const { length } = input.value; + input.setSelectionRange(this.props.selectAllOnFocus ? 0 : length, length); + } + if (!supportsSelection || !this.props.selectAllOnFocus) { + input.scrollLeft = input.scrollWidth; + } } } }, @@ -277,9 +282,16 @@ export class EditableText extends AbstractPureComponent2 { - if (!this.props.disabled) { + const { alwaysRenderInput, disabled, selectAllOnFocus } = this.props; + + if (!disabled) { this.setState({ isEditing: true }); } + + if (alwaysRenderInput && selectAllOnFocus && this.inputElement != null) { + const { length } = this.inputElement.value; + this.inputElement.setSelectionRange(0, length); + } }; private handleTextChange = (event: React.FormEvent) => {