aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/EditableView.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/EditableView.tsx')
-rw-r--r--src/client/views/EditableView.tsx98
1 files changed, 79 insertions, 19 deletions
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index d1311a60a..6b4132814 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -1,9 +1,11 @@
import React = require('react');
-import { action, observable } from 'mobx';
+import { action, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as Autosuggest from 'react-autosuggest';
import { ObjectField } from '../../fields/ObjectField';
import './EditableView.scss';
+import { DocumentIconContainer } from './nodes/DocumentIcon';
+import { OverlayView } from './OverlayView';
export interface EditableProps {
/**
@@ -37,7 +39,8 @@ export interface EditableProps {
onChange: (e: React.ChangeEvent, { newValue }: { newValue: string }) => void;
autosuggestProps: Autosuggest.AutosuggestProps<string, any>;
};
- oneLine?: boolean;
+ oneLine?: boolean; // whether to display the editable view as a single input line or as a textarea
+ allowCRs?: boolean; // can carriage returns be entered
editing?: boolean;
isEditingCallback?: (isEditing: boolean) => void;
menuCallback?: (x: number, y: number) => void;
@@ -45,6 +48,7 @@ export interface EditableProps {
showMenuOnLoad?: boolean;
background?: string | undefined;
placeholder?: string;
+ wrap?: string; // nowrap, pre-wrap, etc
}
/**
@@ -55,7 +59,9 @@ export interface EditableProps {
@observer
export class EditableView extends React.Component<EditableProps> {
private _ref = React.createRef<HTMLDivElement>();
- private _inputref = React.createRef<HTMLInputElement>();
+ private _inputref: HTMLInputElement | HTMLTextAreaElement | null = null;
+ _overlayDisposer?: () => void;
+ _editingDisposer?: IReactionDisposer;
@observable _editing: boolean = false;
constructor(props: EditableProps) {
@@ -63,21 +69,53 @@ export class EditableView extends React.Component<EditableProps> {
this._editing = this.props.editing ? true : false;
}
+ componentDidMount(): void {
+ this._editingDisposer = reaction(
+ () => this._editing,
+ editing => {
+ if (editing) {
+ setTimeout(() => {
+ if (this._inputref?.value.startsWith('=') || this._inputref?.value.startsWith(':=')) {
+ this._overlayDisposer?.();
+ this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
+ }
+ });
+ } else {
+ this._overlayDisposer?.();
+ this._overlayDisposer = undefined;
+ }
+ },
+ { fireImmediately: true }
+ );
+ }
+
@action
componentDidUpdate() {
if (this._editing && this.props.editing === false) {
- this._inputref.current?.value && this.finalizeEdit(this._inputref.current.value, false, true, false);
+ this._inputref?.value && this.finalizeEdit(this._inputref.value, false, true, false);
} else if (this.props.editing !== undefined) {
this._editing = this.props.editing;
}
}
componentWillUnmount() {
- this._inputref.current?.value && this.finalizeEdit(this._inputref.current.value, false, true, false);
+ this._overlayDisposer?.();
+ this._editingDisposer?.();
+ this._inputref?.value && this.finalizeEdit(this._inputref.value, false, true, false);
}
+ onChange = (e: React.ChangeEvent) => {
+ const targVal = (e.target as any).value;
+ if (!(targVal.startsWith(':=') || targVal.startsWith('='))) {
+ this._overlayDisposer?.();
+ this._overlayDisposer = undefined;
+ } else if (!this._overlayDisposer) {
+ this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
+ }
+ };
+
@action
- onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
+ onKeyDown = (e: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => {
switch (e.key) {
case 'Tab':
e.stopPropagation();
@@ -89,13 +127,15 @@ export class EditableView extends React.Component<EditableProps> {
if (!e.currentTarget.value) this.props.OnEmpty?.();
break;
case 'Enter':
- e.stopPropagation();
- if (!e.ctrlKey) {
- this.finalizeEdit(e.currentTarget.value, e.shiftKey, false, true);
- } else if (this.props.OnFillDown) {
- this.props.OnFillDown(e.currentTarget.value);
- this._editing = false;
- this.props.isEditingCallback?.(false);
+ if (this.props.allowCRs !== true) {
+ e.stopPropagation();
+ if (!e.ctrlKey) {
+ this.finalizeEdit(e.currentTarget.value, e.shiftKey, false, true);
+ } else if (this.props.OnFillDown) {
+ this.props.OnFillDown(e.currentTarget.value);
+ this._editing = false;
+ this.props.isEditingCallback?.(false);
+ }
}
break;
case 'Escape':
@@ -103,9 +143,6 @@ export class EditableView extends React.Component<EditableProps> {
this._editing = false;
this.props.isEditingCallback?.(false);
break;
- case ':':
- this.props.menuCallback?.(e.currentTarget.getBoundingClientRect().x, e.currentTarget.getBoundingClientRect().y);
- break;
case 'ArrowUp':
case 'ArrowDown':
case 'ArrowLeft':
@@ -117,6 +154,12 @@ export class EditableView extends React.Component<EditableProps> {
case 'Meta':
case 'Control':
break;
+ case ':':
+ if (this.props.menuCallback) {
+ this.props.menuCallback(e.currentTarget.getBoundingClientRect().x, e.currentTarget.getBoundingClientRect().y);
+ break;
+ }
+
default:
if (this.props.textCallback?.(e.key)) {
this._editing = false;
@@ -186,15 +229,32 @@ export class EditableView extends React.Component<EditableProps> {
onChange: this.props.autosuggestProps.onChange,
}}
/>
- ) : (
+ ) : this.props.oneLine !== false && this.props.GetValue()?.toString().indexOf('\n') === -1 ? (
<input
className="editableView-input"
- ref={this._inputref}
+ ref={r => (this._inputref = r)}
style={{ display: this.props.display, overflow: 'auto', fontSize: this.props.fontSize, minWidth: 20, background: this.props.background }}
placeholder={this.props.placeholder}
onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true, false)}
defaultValue={this.props.GetValue()}
autoFocus={true}
+ onChange={this.onChange}
+ onKeyDown={this.onKeyDown}
+ onKeyPress={this.stopPropagation}
+ onPointerDown={this.stopPropagation}
+ onClick={this.stopPropagation}
+ onPointerUp={this.stopPropagation}
+ />
+ ) : (
+ <textarea
+ className="editableView-input"
+ ref={r => (this._inputref = r)}
+ style={{ display: this.props.display, overflow: 'auto', fontSize: this.props.fontSize, minHeight: `min(100%, ${(this.props.GetValue()?.split('\n').length || 1) * 15})`, minWidth: 20, background: this.props.background }}
+ placeholder={this.props.placeholder}
+ onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true, false)}
+ defaultValue={this.props.GetValue()}
+ autoFocus={true}
+ onChange={this.onChange}
onKeyDown={this.onKeyDown}
onKeyPress={this.stopPropagation}
onPointerDown={this.stopPropagation}
@@ -220,7 +280,7 @@ export class EditableView extends React.Component<EditableProps> {
<div
className={`editableView-container-editing${this.props.oneLine ? '-oneLine' : ''}`}
ref={this._ref}
- style={{ display: this.props.display, textOverflow: this.props.overflow, minHeight: '10px', whiteSpace: 'nowrap', height: this.props.height || 'auto', maxHeight: this.props.maxHeight }}
+ style={{ display: this.props.display, textOverflow: this.props.overflow, minHeight: '10px', whiteSpace: this.props.oneLine ? 'nowrap' : 'pre-line', height: this.props.height, maxHeight: this.props.maxHeight }}
//onPointerDown={this.stopPropagation}
onClick={this.onClick}
placeholder={this.props.placeholder}>