From 7a683324f25c6b0679083e54dc8123f30f6bb2d0 Mon Sep 17 00:00:00 2001
From: Anne Ogborn <47622029+AnnieAtHasura@users.noreply.github.com>
Date: Mon, 22 Jul 2019 15:17:31 +0530
Subject: [PATCH] add multiline and rich editing to text fields (close #458)
(#2498)
---
.../{JsonInput.scss => CustomInput.scss} | 12 +-
.../Common/CustomInputTypes/JsonInput.js | 11 +-
.../Common/CustomInputTypes/TextInput.js | 166 ++++++++++++++
.../Services/Data/TableBrowseRows/EditItem.js | 213 ++++--------------
.../Data/TableInsertItem/InsertItem.js | 90 ++++----
5 files changed, 281 insertions(+), 211 deletions(-)
rename console/src/components/Common/CustomInputTypes/{JsonInput.scss => CustomInput.scss} (51%)
create mode 100644 console/src/components/Common/CustomInputTypes/TextInput.js
diff --git a/console/src/components/Common/CustomInputTypes/JsonInput.scss b/console/src/components/Common/CustomInputTypes/CustomInput.scss
similarity index 51%
rename from console/src/components/Common/CustomInputTypes/JsonInput.scss
rename to console/src/components/Common/CustomInputTypes/CustomInput.scss
index ae10091148f1e..15f125f870106 100644
--- a/console/src/components/Common/CustomInputTypes/JsonInput.scss
+++ b/console/src/components/Common/CustomInputTypes/CustomInput.scss
@@ -1,8 +1,8 @@
-.jsonNormalInput {
+.normalInput {
padding-right: 30px;
}
-.jsonToggleButton {
+.modeToggleButton {
position: absolute;
top: 10px;
right: 10px;
@@ -11,4 +11,12 @@
&:hover {
opacity: 1.0;
}
+}
+
+.modeType {
+ position: absolute;
+ top: 6px;
+ right: 28px;
+ z-index: 100;
+ font-style: italic;
}
\ No newline at end of file
diff --git a/console/src/components/Common/CustomInputTypes/JsonInput.js b/console/src/components/Common/CustomInputTypes/JsonInput.js
index 6d10bf8d9e001..4ba824abab721 100644
--- a/console/src/components/Common/CustomInputTypes/JsonInput.js
+++ b/console/src/components/Common/CustomInputTypes/JsonInput.js
@@ -1,7 +1,10 @@
import React, { useState } from 'react';
import AceEditor from 'react-ace';
-const styles = require('./JsonInput.scss');
+import 'brace/mode/markdown';
+import 'brace/theme/github';
+
+const styles = require('./CustomInput.scss');
const NORMALKEY = 'normal';
const JSONKEY = 'json';
@@ -112,7 +115,7 @@ const JsonInput = props => {
value={data}
onChange={handleInputChangeAndPropagate}
onKeyUp={handleKeyUpEvent}
- className={allProps.className + ' ' + styles.jsonNormalInput}
+ className={allProps.className + ' ' + styles.normalInput}
/>
);
};
@@ -126,12 +129,12 @@ const JsonInput = props => {
key="icon_json_editor"
className={
'fa ' +
- styles.jsonToggleButton +
+ styles.modeToggleButton +
(editorType === JSONKEY ? ' fa-compress' : ' fa-expand')
}
onClick={() => updateState(toggleEditorType)}
title={
- (editorType === JSONKEY ? 'Collapse' : 'Expand') + '(Ctrl + Space)'
+ (editorType === JSONKEY ? 'Collapse' : 'Expand') + ' (Ctrl + Space)'
}
/>
diff --git a/console/src/components/Common/CustomInputTypes/TextInput.js b/console/src/components/Common/CustomInputTypes/TextInput.js
new file mode 100644
index 0000000000000..98c2f74faf22a
--- /dev/null
+++ b/console/src/components/Common/CustomInputTypes/TextInput.js
@@ -0,0 +1,166 @@
+import React, { useState } from 'react';
+import AceEditor from 'react-ace';
+
+import 'brace/mode/html';
+import 'brace/mode/markdown';
+import 'brace/theme/github';
+import 'brace/theme/chrome';
+
+const styles = require('./CustomInput.scss');
+
+// editorType is what sort of editor. All are ACE Editor
+// modes except 0, which is text input
+
+// ACE editor mode names
+const EDITORTYPES = [
+ 'normal', // must be first
+ 'text',
+ 'markdown',
+ 'html',
+];
+
+// human readable editor names
+const EDITORTYPENAMES = [
+ 'single line input',
+ 'multi-line text input',
+ 'markdown',
+ 'html',
+];
+
+// short human readable editor names
+// for the visible label
+const SHORTEDITORTYPENAMES = ['', 'multi-line', 'markdown', 'html'];
+
+const NORMALKEY = 0;
+
+const createInitialState = data => {
+ const initialState = {
+ editorType: NORMALKEY,
+ data: data,
+ };
+ return initialState;
+};
+
+const TextInput = props => {
+ const { standardProps, placeholderProp } = props;
+ const { defaultValue, onChange } = standardProps;
+ const allProps = { ...standardProps };
+ delete allProps.defaultValue;
+ const [state, updateState] = useState(createInitialState(defaultValue));
+ const { editorType, data } = state;
+
+ const updateData = (newData, currentState) => {
+ return {
+ ...currentState,
+ data: newData,
+ };
+ };
+
+ const cycleEditorType = currentState => {
+ const nextEditorType = (currentState.editorType + 1) % EDITORTYPES.length;
+
+ return {
+ ...currentState,
+ editorType: nextEditorType,
+ };
+ };
+
+ const handleKeyUpEvent = e => {
+ if ((e.ctrlKey || event.metaKey) && e.which === 32) {
+ updateState(cycleEditorType);
+ }
+ };
+
+ const handleEditorExec = () => {
+ updateState(cycleEditorType);
+ };
+
+ const handleInputChangeAndPropagate = e => {
+ const val = e.target.value;
+ updateState(currentState => updateData(val, currentState));
+ if (onChange) {
+ onChange(e);
+ }
+ };
+
+ const handleTextAreaChangeAndPropagate = (value, e) => {
+ const val = value;
+ updateState(currentState => updateData(val, currentState));
+ if (onChange) {
+ onChange(e, value);
+ }
+ };
+
+ const getAceEditor = curmode => {
+ return (
+
+ );
+ };
+
+ const getNormalEditor = () => {
+ return (
+
+ );
+ };
+
+ const editor =
+ editorType === NORMALKEY
+ ? getNormalEditor()
+ : getAceEditor(EDITORTYPES[editorType]);
+
+ return (
+
+
+ updateState(cycleEditorType)}
+ title={
+ 'Change to ' +
+ EDITORTYPENAMES[(editorType + 1) % EDITORTYPES.length] +
+ ' (Ctrl + Space)'
+ }
+ >
+
+ {SHORTEDITORTYPENAMES[editorType]}
+
+
+
+
+ );
+};
+export default TextInput;
diff --git a/console/src/components/Services/Data/TableBrowseRows/EditItem.js b/console/src/components/Services/Data/TableBrowseRows/EditItem.js
index 84b2b78e2d619..1fc88f16c09e4 100644
--- a/console/src/components/Services/Data/TableBrowseRows/EditItem.js
+++ b/console/src/components/Services/Data/TableBrowseRows/EditItem.js
@@ -5,6 +5,7 @@ import { editItem, E_ONGOING_REQ } from './EditActions';
import globals from '../../../../Globals';
import { modalClose } from './EditActions';
import JsonInput from '../../../Common/CustomInputTypes/JsonInput';
+import TextInput from '../../../Common/CustomInputTypes/TextInput';
import Button from '../../../Common/Button/Button';
import {
@@ -19,6 +20,7 @@ import {
TIMETZ,
JSONB,
JSONDTYPE,
+ TEXT,
} from '../utils';
// import RichTextEditor from 'react-rte';
import { replace } from 'react-router-redux';
@@ -84,180 +86,59 @@ class EditItem extends Component {
e.target.focus();
};
+ const standardEditProps = {
+ className: `form-control ${styles.insertBox}`,
+ onClick: clicker,
+ ref: inputRef,
+ 'data-test': `typed-input-${i}`,
+ type: 'text',
+ defaultValue: oldItem[colName],
+ };
+
// Text type
let typedInput = (
-
+
);
- // Integer
- if (colType === INTEGER) {
- typedInput = (
-
- );
- } else if (colType === BIGINT) {
- typedInput = (
-
- );
- } else if (colType === NUMERIC) {
- typedInput = (
-
- );
- } else if (colType === TIMESTAMP) {
- typedInput = (
-
- );
- } else if (colType === DATE) {
- typedInput = (
-
- );
- } else if (colType === TIMETZ) {
- typedInput = (
-
- );
- } else if (colType === JSONDTYPE || colType === JSONB) {
- const standardEditProps = {
- className: `form-control ${styles.insertBox}`,
- onClick: clicker,
- ref: inputRef,
- defaultValue: JSON.stringify(oldItem[colName]),
- 'data-test': `typed-input-${i}`,
- type: 'text',
- };
- typedInput = (
-
- );
- } else if (colType === BOOLEAN) {
- typedInput = (
-
- );
- } else if (colType === UUID) {
- typedInput = (
-
- );
- } else {
- // find value to be shown. rich text editor vs clone
- let defaultValue = '';
- let currentValue = '';
- if (
- this.state.editorColumnMap[colName] === null ||
- this.state.editorColumnMap[colName] === undefined
- ) {
- defaultValue = oldItem[colName];
- } else if (this.state.editorColumnMap[colName] !== null) {
- defaultValue = this.state.editorColumnMap[colName];
- currentValue = this.state.editorColumnMap[colName];
- }
- if (currentValue !== '') {
+ switch (colType) {
+ case INTEGER:
+ case BIGINT:
+ case NUMERIC:
+ case TIMESTAMP:
+ case DATE:
+ case TIMETZ:
+ case UUID:
+ break;
+ case JSONB:
+ case JSONDTYPE:
typedInput = (
-
- {
- this.onTextChange(e, colName);
- }}
- value={currentValue}
- data-test={`typed-input-${i}`}
- />
-
+
);
- } else {
+ break;
+ case TEXT:
typedInput = (
-
- {
- this.onTextChange(e, colName);
- }}
- value={defaultValue}
- data-test={`typed-input-${i}`}
- />
-
+
+ );
+ break;
+ case BOOLEAN:
+ typedInput = (
+
);
- }
+ break;
+ default:
+ break;
}
return (
diff --git a/console/src/components/Services/Data/TableInsertItem/InsertItem.js b/console/src/components/Services/Data/TableInsertItem/InsertItem.js
index b0dc6aaa5bf57..d86465b5ba5da 100644
--- a/console/src/components/Services/Data/TableInsertItem/InsertItem.js
+++ b/console/src/components/Services/Data/TableInsertItem/InsertItem.js
@@ -5,8 +5,9 @@ import { insertItem, I_RESET } from './InsertActions';
import { ordinalColSort } from '../utils';
import { setTable } from '../DataActions';
import JsonInput from '../../../Common/CustomInputTypes/JsonInput';
+import TextInput from '../../../Common/CustomInputTypes/TextInput';
import Button from '../../../Common/Button/Button';
-import { getPlaceholder, BOOLEAN, JSONB, JSONDTYPE } from '../utils';
+import { getPlaceholder, BOOLEAN, JSONB, JSONDTYPE, TEXT } from '../utils';
import { getParentNodeByClass } from '../../../../utils/domFunctions';
@@ -28,8 +29,8 @@ class InsertItem extends Component {
nextInsert() {
// when use state object remember to do it inside a class method.
- // Since the state variable lifecycle is tired to the instance of the class
- // and making this change using an anonymous function will case errors.
+ // Since the state variable lifecycle is tied to the instance of the class
+ // and making this change using an anonymous function will cause errors.
this.setState({
insertedRows: this.state.insertedRows + 1,
});
@@ -59,6 +60,13 @@ class InsertItem extends Component {
throw new NotFoundError();
}
+ const isColumnAutoIncrement = column => {
+ return (
+ column.column_default ===
+ "nextval('" + tableName + '_' + column.column_name + "_seq'::regclass)"
+ );
+ };
+
const _columns = schemas.find(
x => x.table_name === tableName && x.table_schema === currentSchema
).columns;
@@ -81,14 +89,8 @@ class InsertItem extends Component {
}
e.target.focus();
};
- const colDefault = col.column_default;
- let isAutoIncrement = false;
- if (
- colDefault ===
- "nextval('" + tableName + '_' + colName + "_seq'::regclass)"
- ) {
- isAutoIncrement = true;
- }
+
+ const isAutoIncrement = isColumnAutoIncrement(col);
const standardInputProps = {
className: `form-control ${styles.insertBox}`,
@@ -144,34 +146,44 @@ class InsertItem extends Component {
);
}
- if (colType === JSONDTYPE || colType === JSONB) {
- // JSON/JSONB
- typedInput = (
-
- );
- }
-
- if (colType === BOOLEAN) {
- // Boolean
- typedInput = (
-
- );
+ switch (colType) {
+ case JSONB:
+ case JSONDTYPE:
+ typedInput = (
+
+ );
+ break;
+ case TEXT:
+ typedInput = (
+
+ );
+ break;
+ case BOOLEAN:
+ typedInput = (
+
+ );
+ break;
+ default:
+ break;
}
return (