diff --git a/package.json b/package.json
index 576ebd9aeb..4efa7f3c34 100644
--- a/package.json
+++ b/package.json
@@ -41,6 +41,7 @@
"commander": "3.0.1",
"connect-flash": "0.1.1",
"cookie-session": "2.0.0-beta.3",
+ "copy-to-clipboard": "^3.2.0",
"create-react-class": "15.6.3",
"csurf": "1.10.0",
"express": "4.17.1",
diff --git a/src/components/BrowserCell/BrowserCell.react.js b/src/components/BrowserCell/BrowserCell.react.js
index bfb6d9801a..b2045d6d4a 100644
--- a/src/components/BrowserCell/BrowserCell.react.js
+++ b/src/components/BrowserCell/BrowserCell.react.js
@@ -18,6 +18,7 @@ export default class BrowserCell extends Component {
super();
this.cellRef = React.createRef();
+ this.copyableValue = undefined;
}
componentDidUpdate() {
@@ -36,6 +37,10 @@ export default class BrowserCell extends Component {
} else if (top < topBoundary || bottom > window.innerHeight) {
node.scrollIntoView({ block: 'nearest', inline: 'nearest' });
}
+
+ if (!this.props.hidden) {
+ this.props.setCopyableValue(this.copyableValue);
+ }
}
}
@@ -58,21 +63,22 @@ export default class BrowserCell extends Component {
}
render() {
- let { type, value, hidden, width, current, onSelect, onEditChange, setRelation, onPointerClick, row, col } = this.props;
+ let { type, value, hidden, width, current, onSelect, onEditChange, setCopyableValue, setRelation, onPointerClick, row, col } = this.props;
let content = value;
+ this.copyableValue = content;
let classes = [styles.cell, unselectable];
if (hidden) {
content = '(hidden)';
classes.push(styles.empty);
} else if (value === undefined) {
if (type === 'ACL') {
- content = 'Public Read + Write';
+ this.copyableValue = content = 'Public Read + Write';
} else {
- content = '(undefined)';
+ this.copyableValue = content = '(undefined)';
classes.push(styles.empty);
}
} else if (value === null) {
- content = '(null)';
+ this.copyableValue = content = '(null)';
classes.push(styles.empty);
} else if (value === '') {
content = ;
@@ -88,23 +94,22 @@ export default class BrowserCell extends Component {
);
+ this.copyableValue = value.id;
} else if (type === 'Date') {
if (typeof value === 'object' && value.__type) {
value = new Date(value.iso);
} else if (typeof value === 'string') {
value = new Date(value);
}
- content = dateStringUTC(value);
+ this.copyableValue = content = dateStringUTC(value);
} else if (type === 'Boolean') {
- content = value ? 'True' : 'False';
+ this.copyableValue = content = value ? 'True' : 'False';
} else if (type === 'Object' || type === 'Bytes' || type === 'Array') {
- content = JSON.stringify(value);
+ this.copyableValue = content = JSON.stringify(value);
} else if (type === 'File') {
- if (value.url()) {
- content = ;
- } else {
- content = ;
- }
+ const fileName = value.url() ? getFileName(value) : 'Uploading\u2026';
+ content = ;
+ this.copyableValue = fileName;
} else if (type === 'ACL') {
let pieces = [];
let json = value.toJSON();
@@ -125,17 +130,18 @@ export default class BrowserCell extends Component {
if (pieces.length === 0) {
pieces.push('Master Key Only');
}
- content = pieces.join(', ');
+ this.copyableValue = content = pieces.join(', ');
} else if (type === 'GeoPoint') {
- content = `(${value.latitude}, ${value.longitude})`;
+ this.copyableValue = content = `(${value.latitude}, ${value.longitude})`;
} else if (type === 'Polygon') {
- content = value.coordinates.map(coord => `(${coord})`)
+ this.copyableValue = content = value.coordinates.map(coord => `(${coord})`)
} else if (type === 'Relation') {
content = (
setRelation(value)} value='View relation' />
);
+ this.copyableValue = undefined;
}
if (current) {
@@ -146,7 +152,10 @@ export default class BrowserCell extends Component {
ref={this.cellRef}
className={classes.join(' ')}
style={{ width }}
- onClick={() => onSelect({ row, col })}
+ onClick={() => {
+ onSelect({ row, col });
+ setCopyableValue(hidden ? undefined : this.copyableValue);
+ }}
onDoubleClick={() => {
if (type !== 'Relation') {
onEditChange(true)
diff --git a/src/components/BrowserRow/BrowserRow.react.js b/src/components/BrowserRow/BrowserRow.react.js
index ad15e697d9..5b7cf8d4d0 100644
--- a/src/components/BrowserRow/BrowserRow.react.js
+++ b/src/components/BrowserRow/BrowserRow.react.js
@@ -18,7 +18,7 @@ export default class BrowserRow extends Component {
}
render() {
- const { className, columns, currentCol, isUnique, obj, onPointerClick, order, readOnlyFields, row, rowWidth, selection, selectRow, setCurrent, setEditing, setRelation } = this.props;
+ const { className, columns, currentCol, isUnique, obj, onPointerClick, order, readOnlyFields, row, rowWidth, selection, selectRow, setCopyableValue, setCurrent, setEditing, setRelation } = this.props;
let attributes = obj.attributes;
return (
@@ -71,7 +71,8 @@ export default class BrowserRow extends Component {
onPointerClick={onPointerClick}
setRelation={setRelation}
value={attr}
- hidden={hidden} />
+ hidden={hidden}
+ setCopyableValue={setCopyableValue} />
);
})}
diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js
index dcbe2baaec..1f6da6ed1a 100644
--- a/src/dashboard/Data/Browser/Browser.react.js
+++ b/src/dashboard/Data/Browser/Browser.react.js
@@ -113,6 +113,7 @@ class Browser extends DashboardView {
this.createClass = this.createClass.bind(this);
this.addColumn = this.addColumn.bind(this);
this.removeColumn = this.removeColumn.bind(this);
+ this.showNote = this.showNote.bind(this);
}
componentWillMount() {
@@ -974,7 +975,8 @@ class Browser extends DashboardView {
setRelation={this.setRelation}
onAddColumn={this.showAddColumn}
onAddRow={this.addRow}
- onAddClass={this.showCreateClass} />
+ onAddClass={this.showCreateClass}
+ showNote={this.showNote} />
);
}
}
diff --git a/src/dashboard/Data/Browser/BrowserTable.react.js b/src/dashboard/Data/Browser/BrowserTable.react.js
index f88ce875a1..414c2659d8 100644
--- a/src/dashboard/Data/Browser/BrowserTable.react.js
+++ b/src/dashboard/Data/Browser/BrowserTable.react.js
@@ -135,7 +135,8 @@ export default class BrowserTable extends React.Component {
selectRow={this.props.selectRow}
setCurrent={this.props.setCurrent}
setEditing={this.props.setEditing}
- setRelation={this.props.setRelation} />
+ setRelation={this.props.setRelation}
+ setCopyableValue={this.props.setCopyableValue} />
);
}
@@ -167,7 +168,8 @@ export default class BrowserTable extends React.Component {
selectRow={this.props.selectRow}
setCurrent={this.props.setCurrent}
setEditing={this.props.setEditing}
- setRelation={this.props.setRelation} />
+ setRelation={this.props.setRelation}
+ setCopyableValue={this.props.setCopyableValue} />
}
if (this.props.editing) {
diff --git a/src/dashboard/Data/Browser/DataBrowser.react.js b/src/dashboard/Data/Browser/DataBrowser.react.js
index b79f9174b3..87434cdca9 100644
--- a/src/dashboard/Data/Browser/DataBrowser.react.js
+++ b/src/dashboard/Data/Browser/DataBrowser.react.js
@@ -5,6 +5,7 @@
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*/
+import copy from 'copy-to-clipboard';
import BrowserTable from 'dashboard/Data/Browser/BrowserTable.react';
import BrowserToolbar from 'dashboard/Data/Browser/BrowserToolbar.react';
import * as ColumnPreferences from 'lib/ColumnPreferences';
@@ -32,6 +33,7 @@ export default class DataBrowser extends React.Component {
order: order,
current: null,
editing: false,
+ copyableValue: undefined
};
this.handleKey = this.handleKey.bind(this);
@@ -40,13 +42,14 @@ export default class DataBrowser extends React.Component {
this.setCurrent = this.setCurrent.bind(this);
this.setEditing = this.setEditing.bind(this);
this.handleColumnsOrder = this.handleColumnsOrder.bind(this);
+ this.setCopyableValue = this.setCopyableValue.bind(this);
this.saveOrderTimeout = null;
}
shouldComponentUpdate(nextProps, nextState) {
const shallowVerifyStates = [...new Set(Object.keys(this.state).concat(Object.keys(nextState)))]
- .filter(stateName => stateName !== 'order');
+ .filter(stateName => stateName !== 'order' && stateName !== 'copyableValue');
if (shallowVerifyStates.some(stateName => this.state[stateName] !== nextState[stateName])) {
return true;
}
@@ -195,6 +198,13 @@ export default class DataBrowser extends React.Component {
});
e.preventDefault();
break;
+ case 67: // C
+ if ((e.ctrlKey || e.metaKey) && this.state.copyableValue !== undefined) {
+ copy(this.state.copyableValue); // Copies current cell value to clipboard
+ this.props.showNote('Value copied to clipboard', false)
+ e.preventDefault()
+ }
+ break;
}
}
@@ -209,6 +219,12 @@ export default class DataBrowser extends React.Component {
this.setState({ current });
}
}
+
+ setCopyableValue(copyableValue) {
+ if (this.state.copyableValue !== copyableValue) {
+ this.setState({ copyableValue });
+ }
+ }
handleColumnsOrder(order) {
this.setState({ order: [ ...order ] }, () => {
@@ -230,6 +246,7 @@ export default class DataBrowser extends React.Component {
handleResize={this.handleResize}
setEditing={this.setEditing}
setCurrent={this.setCurrent}
+ setCopyableValue={this.setCopyableValue}
{...other} />