From 1329f7c562cd17479ef41f8cee4259ee0b02f8d6 Mon Sep 17 00:00:00 2001 From: Nino Crljenec Date: Tue, 14 Jan 2020 09:37:42 +0100 Subject: [PATCH 01/11] EditRowDialog - initial version --- package-lock.json | 251 +++++++++++++++- .../BrowserCell/BrowserCell.react.js | 7 +- src/components/BrowserRow/BrowserRow.react.js | 6 +- src/components/FileEditor/FileEditor.react.js | 22 +- src/components/FileEditor/FileEditor.scss | 2 +- .../GeoPointEditor/GeoPointEditor.react.js | 27 +- src/components/TextInput/TextInput.react.js | 8 +- src/dashboard/Data/Browser/Browser.react.js | 86 +++++- .../Data/Browser/BrowserTable.react.js | 9 +- .../Data/Browser/BrowserToolbar.react.js | 7 + .../Data/Browser/EditRowDialog.react.js | 283 ++++++++++++++++++ src/dashboard/Data/Browser/Editor.react.js | 5 +- 12 files changed, 671 insertions(+), 42 deletions(-) create mode 100644 src/dashboard/Data/Browser/EditRowDialog.react.js diff --git a/package-lock.json b/package-lock.json index 243b8d31d9..0728a82055 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5022,6 +5022,15 @@ "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" }, + "backoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", + "integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=", + "dev": true, + "requires": { + "precond": "0.2" + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -5421,6 +5430,18 @@ "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", "dev": true }, + "bunyan": { + "version": "1.8.12", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", + "integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=", + "dev": true, + "requires": { + "dtrace-provider": "~0.8", + "moment": "^2.10.6", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, "busboy": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz", @@ -6688,6 +6709,16 @@ "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=", "dev": true }, + "dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.14.0" + } + }, "duplexer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", @@ -8779,6 +8810,7 @@ "integrity": "sha512-+r8qY2JRRs+uaZcrZOxpNhdlCZoS8yS5KQ6X53Twc8WecZ6VtAn+MVHroLOd4u9HVPxTXZ9RUd9+556EpTc0xA==", "requires": { "codemirror": "^5.26.0", + "codemirror-graphql": "^0.6.11", "markdown-it": "^8.4.0" } }, @@ -9039,6 +9071,15 @@ } } }, + "graphql-relay": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/graphql-relay/-/graphql-relay-0.6.0.tgz", + "integrity": "sha512-OVDi6C9/qOT542Q3KxZdXja3NrDvqzbihn1B44PH8P/c5s0Q90RyQwT6guhGqXqbYEH6zbeLJWjQqiYvcg2vVw==", + "dev": true, + "requires": { + "prettier": "^1.16.0" + } + }, "graphql-request": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-1.8.2.tgz", @@ -10012,7 +10053,16 @@ "@babel/template": "^7.4.0", "@babel/traverse": "^7.4.3", "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5" + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "istanbul-lib-report": { @@ -10431,7 +10481,16 @@ "jest-resolve": "^24.9.0", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "pretty-format": "^24.9.0" + "pretty-format": "^24.9.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "jest-util": { @@ -10804,6 +10863,49 @@ "integrity": "sha1-GwuP+ayceIklBYK3C3ExXZ2m2aM=", "dev": true }, + "ldap-filter": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.2.2.tgz", + "integrity": "sha1-8rhCvguG2jNSeYUFsx68rlkNd9A=", + "dev": true, + "requires": { + "assert-plus": "0.1.5" + }, + "dependencies": { + "assert-plus": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=", + "dev": true + } + } + }, + "ldapjs": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-1.0.2.tgz", + "integrity": "sha1-VE/3Ayt7g8aPBwEyjZKXqmlDQPk=", + "dev": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "^1.0.0", + "backoff": "^2.5.0", + "bunyan": "^1.8.3", + "dashdash": "^1.14.0", + "dtrace-provider": "~0.8", + "ldap-filter": "0.2.2", + "once": "^1.4.0", + "vasync": "^1.6.4", + "verror": "^1.8.1" + }, + "dependencies": { + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true + } + } + }, "left-pad": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", @@ -11597,6 +11699,44 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "dev": true, + "optional": true, + "requires": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + }, + "dependencies": { + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "dev": true, + "optional": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "dev": true, + "optional": true, + "requires": { + "glob": "^6.0.1" + } + } + } + }, "nan": { "version": "2.14.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", @@ -11628,6 +11768,13 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "dev": true, + "optional": true + }, "needle": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz", @@ -12607,7 +12754,15 @@ "requires": { "got": "^9.6.0", "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0" + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } } }, "packet-reader": { @@ -12654,6 +12809,14 @@ "xmlhttprequest": "1.8.0" }, "dependencies": { + "@babel/runtime": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.4.tgz", + "integrity": "sha512-r24eVUUr0QqNZa+qrImUk8fn5SPhHq+IfYvIoIMg0do3GdK9sMdiLKP3GYVVaxpPKORgm8KRKaNTEhAjgIpLMw==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, "ws": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.1.tgz", @@ -12698,37 +12861,42 @@ "requires": { "@apollographql/graphql-playground-html": "1.6.24", "@parse/fs-files-adapter": "1.0.1", - "@parse/push-adapter": "3.1.0", - "@parse/s3-files-adapter": "1.3.0", + "@parse/push-adapter": "3.2.0", + "@parse/s3-files-adapter": "1.4.0", "@parse/simple-mailgun-adapter": "1.1.0", - "apollo-server-express": "2.9.7", - "bcrypt": "3.0.6", + "apollo-server-express": "2.9.13", + "bcrypt": "3.0.7", "bcryptjs": "2.4.3", "body-parser": "1.19.0", + "commander": "4.0.1", "cors": "2.8.5", "deepcopy": "2.0.0", "express": "4.17.1", "follow-redirects": "1.9.0", "graphql": "14.5.8", "graphql-list-fields": "2.0.2", + "graphql-relay": "^0.6.0", "graphql-tools": "^4.0.5", - "graphql-upload": "8.1.0", + "graphql-upload": "9.0.0", "intersect": "1.0.1", "jsonwebtoken": "8.5.1", + "ldapjs": "1.0.2", "lodash": "4.17.15", "lru-cache": "5.1.1", "mime": "2.4.4", - "mongodb": "3.3.2", - "node-rsa": "1.0.6", - "pg-promise": "9.3.3", + "mongodb": "3.4.0", + "node-rsa": "1.0.7", + "parse": "2.10.0", + "pg-promise": "10.3.1", "pluralize": "^8.0.0", "redis": "2.8.0", + "semver": "7.1.0", "subscriptions-transport-ws": "0.9.16", "tv4": "1.3.0", "uuid": "3.3.3", "winston": "3.2.1", "winston-daily-rotate-file": "3.10.0", - "ws": "7.2.0" + "ws": "7.2.1" }, "dependencies": { "bcryptjs": { @@ -13191,6 +13359,12 @@ "xtend": "^4.0.0" } }, + "precond": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", + "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=", + "dev": true + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -14472,6 +14646,13 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, + "safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "dev": true, + "optional": true + }, "safe-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", @@ -14738,7 +14919,16 @@ "clone-deep": "^4.0.1", "loader-utils": "^1.2.3", "neo-async": "^2.6.1", - "schema-utils": "^2.1.0" + "schema-utils": "^2.1.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "sax": { @@ -16291,6 +16481,32 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, + "vasync": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/vasync/-/vasync-1.6.4.tgz", + "integrity": "sha1-3+k2Fq0OeugBszKp2Iv8XNyOHR8=", + "dev": true, + "requires": { + "verror": "1.6.0" + }, + "dependencies": { + "extsprintf": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz", + "integrity": "sha1-WtlGwi9bMrp/jNdCZxHG6KP8JSk=", + "dev": true + }, + "verror": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.6.0.tgz", + "integrity": "sha1-fROyex+swuLakEBetepuW90lLqU=", + "dev": true, + "requires": { + "extsprintf": "1.2.0" + } + } + } + }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -16747,9 +16963,18 @@ "requires": { "file-stream-rotator": "^0.4.1", "object-hash": "^1.3.0", + "semver": "^6.2.0", "triple-beam": "^1.3.0", "winston-compat": "^0.1.4", "winston-transport": "^4.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "winston-transport": { diff --git a/src/components/BrowserCell/BrowserCell.react.js b/src/components/BrowserCell/BrowserCell.react.js index b2045d6d4a..c564683f59 100644 --- a/src/components/BrowserCell/BrowserCell.react.js +++ b/src/components/BrowserCell/BrowserCell.react.js @@ -63,7 +63,7 @@ export default class BrowserCell extends Component { } render() { - let { type, value, hidden, width, current, onSelect, onEditChange, setCopyableValue, setRelation, onPointerClick, row, col } = this.props; + let { type, value, hidden, width, current, onSelect, onEditChange, setCopyableValue, setRelation, onPointerClick, row, col, name, onEditSelectedRow } = this.props; let content = value; this.copyableValue = content; let classes = [styles.cell, unselectable]; @@ -157,7 +157,10 @@ export default class BrowserCell extends Component { setCopyableValue(hidden ? undefined : this.copyableValue); }} onDoubleClick={() => { - if (type !== 'Relation') { + // Since objectId can't be edited, double click event opens edit row dialog + if (name === 'objectId') { + onEditSelectedRow(value); + } else if (type !== 'Relation') { onEditChange(true) } }} diff --git a/src/components/BrowserRow/BrowserRow.react.js b/src/components/BrowserRow/BrowserRow.react.js index 11aff817ed..b7774a3696 100644 --- a/src/components/BrowserRow/BrowserRow.react.js +++ b/src/components/BrowserRow/BrowserRow.react.js @@ -19,7 +19,7 @@ export default class BrowserRow extends Component { } render() { - const { className, columns, currentCol, isUnique, obj, onPointerClick, order, readOnlyFields, row, rowWidth, selection, selectRow, setCopyableValue, setCurrent, setEditing, setRelation } = this.props; + const { className, columns, currentCol, isUnique, obj, onPointerClick, order, readOnlyFields, row, rowWidth, selection, selectRow, setCopyableValue, setCurrent, setEditing, setRelation, onEditSelectedRow } = this.props; let attributes = obj.attributes; return (
@@ -61,6 +61,7 @@ export default class BrowserRow extends Component { return (
diff --git a/src/components/FileEditor/FileEditor.react.js b/src/components/FileEditor/FileEditor.react.js index a3c06cafe9..dbd839ceb6 100644 --- a/src/components/FileEditor/FileEditor.react.js +++ b/src/components/FileEditor/FileEditor.react.js @@ -20,6 +20,7 @@ export default class FileEditor extends React.Component { this.checkExternalClick = this.checkExternalClick.bind(this); this.handleKey = this.handleKey.bind(this); + this.removeFile = this.removeFile.bind(this); } componentDidMount() { @@ -33,18 +34,20 @@ export default class FileEditor extends React.Component { } checkExternalClick(e) { - if (!hasAncestor(e.target, this.refs.input)) { - this.props.onCommit(this.state.value); + const { onCancel } = this.props; + if (!hasAncestor(e.target, this.refs.input) && onCancel) { + onCancel(); } } handleKey(e) { - if (e.keyCode === 13) { - this.props.onCommit(this.state.value); + const { onCancel } = this.props; + if (e.keyCode === 13 && onCancel) { + onCancel(); } } - getBase64(file){ + getBase64(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); @@ -53,6 +56,11 @@ export default class FileEditor extends React.Component { }); } + removeFile() { + this.refs.fileInput.value = ''; + this.props.onCommit(undefined); + } + async handleChange(e) { let file = e.target.files[0]; if (file) { @@ -67,10 +75,10 @@ export default class FileEditor extends React.Component {
{file && file.url() ? Download : null} - + {file ? 'Replace file' : 'Upload file'} - {file ? this.props.onCommit(undefined)}>Delete : null} + {file ? Delete : null}
); } diff --git a/src/components/FileEditor/FileEditor.scss b/src/components/FileEditor/FileEditor.scss index 9f4245999d..5a2faf2c3b 100644 --- a/src/components/FileEditor/FileEditor.scss +++ b/src/components/FileEditor/FileEditor.scss @@ -41,8 +41,8 @@ opacity: 0; top: 0; right: 0; - left: -100px; bottom: 0; cursor: pointer; + width: 100%; } } diff --git a/src/components/GeoPointEditor/GeoPointEditor.react.js b/src/components/GeoPointEditor/GeoPointEditor.react.js index e151cff00d..806893919b 100644 --- a/src/components/GeoPointEditor/GeoPointEditor.react.js +++ b/src/components/GeoPointEditor/GeoPointEditor.react.js @@ -6,7 +6,6 @@ * the root directory of this source tree. */ import { GeoPoint } from 'parse'; -import hasAncestor from 'lib/hasAncestor'; import React from 'react'; import styles from 'components/GeoPointEditor/GeoPointEditor.scss'; import validateNumeric from 'lib/validateNumeric'; @@ -26,23 +25,31 @@ export default class GeoPointEditor extends React.Component { } componentDidMount() { - this.refs.latitude.focus(); + if (!this.props.disableAutoFocus) { + this.refs.latitude.focus(); + } this.refs.latitude.setSelectionRange(0, String(this.state.latitude).length); - document.body.addEventListener('click', this.checkExternalClick); this.refs.latitude.addEventListener('keypress', this.handleKeyLatitude); this.refs.longitude.addEventListener('keypress', this.handleKeyLongitude); } componentWillUnmount() { - document.body.removeEventListener('click', this.checkExternalClick); this.refs.latitude.removeEventListener('keypress', this.handleKeyLatitude); this.refs.longitude.removeEventListener('keypress', this.handleKeyLongitude); } - checkExternalClick(e) { - if (!hasAncestor(e.target, this.refs.input)) { - this.commitValue(); - } + checkExternalClick() { + // small timeout is needed here because activeElement is set after onBlur event is done + setTimeout(function() { + // check if activeElement is something else from input fields, + // to avoid commiting new value on every switch of focus beetween latitude and longitude fields + if ( + document.activeElement !== this.refs.latitude && + document.activeElement !== this.refs.longitude + ) { + this.commitValue(); + } + }.bind(this), 1); } handleKeyLatitude(e) { @@ -112,14 +119,16 @@ export default class GeoPointEditor extends React.Component { this.setState({ [target]: validateNumeric(value) ? value : this.state[target] }); }; return ( -
+
); diff --git a/src/components/TextInput/TextInput.react.js b/src/components/TextInput/TextInput.react.js index 124976410e..32fdbc88a8 100644 --- a/src/components/TextInput/TextInput.react.js +++ b/src/components/TextInput/TextInput.react.js @@ -13,6 +13,9 @@ export default class TextInput extends React.Component { changeValue(e) { this.props.onChange(e.nativeEvent.target.value); } + updateValue(e) { + this.props.onBlur(e.nativeEvent.target.value); + } render() { let classes = [styles.text_input]; @@ -27,7 +30,8 @@ export default class TextInput extends React.Component { style={{height: this.props.height || 80}} placeholder={this.props.placeholder} value={this.props.value} - onChange={this.changeValue.bind(this)} /> + onChange={this.changeValue.bind(this)} + onBlur={this.updateValue.bind(this)} /> ); } return ( @@ -39,7 +43,7 @@ export default class TextInput extends React.Component { placeholder={this.props.placeholder} value={this.props.value} onChange={this.changeValue.bind(this)} - onBlur={this.props.onBlur} /> + onBlur={this.updateValue.bind(this)} /> ); } } diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index 1838d6cbdc..4805c4d7ed 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -19,6 +19,7 @@ import ExportDialog from 'dashboard/Data/Browser/ExportDia import AttachRowsDialog from 'dashboard/Data/Browser/AttachRowsDialog.react'; import AttachSelectedRowsDialog from 'dashboard/Data/Browser/AttachSelectedRowsDialog.react'; import CloneSelectedRowsDialog from 'dashboard/Data/Browser/CloneSelectedRowsDialog.react'; +import EditRowDialog from 'dashboard/Data/Browser/EditRowDialog.react'; import history from 'dashboard/history'; import { List, Map } from 'immutable'; import Notification from 'dashboard/Data/Browser/Notification.react'; @@ -56,6 +57,7 @@ class Browser extends DashboardView { showDropClassDialog: false, showExportDialog: false, showAttachRowsDialog: false, + showEditRowDialog: false, rowsToDelete: null, relation: null, @@ -114,6 +116,10 @@ class Browser extends DashboardView { this.addColumn = this.addColumn.bind(this); this.removeColumn = this.removeColumn.bind(this); this.showNote = this.showNote.bind(this); + this.showEditRowDialog = this.showEditRowDialog.bind(this); + this.confirmEditRowDialog = this.confirmEditRowDialog.bind(this); + this.closeEditRowDialog = this.closeEditRowDialog.bind(this); + this.handleShowAcl = this.handleShowAcl.bind(this); } componentWillMount() { @@ -705,7 +711,8 @@ class Browser extends DashboardView { this.state.rowsToDelete || this.state.showAttachRowsDialog || this.state.showAttachSelectedRowsDialog || - this.state.showCloneSelectedRowsDialog + this.state.showCloneSelectedRowsDialog || + this.state.showEditRowDialog ); } @@ -895,6 +902,37 @@ class Browser extends DashboardView { }, 3500); } + showEditRowDialog(objectId) { + // objectId is optional param which is used for doubleClick event on objectId BrowserCell + if (objectId) { + // remove all selected rows and select doubleClicked row + this.setState({ selection: {} }); + this.selectRow(objectId, true); + } + this.setState({ + showEditRowDialog: true, + }); + } + + closeEditRowDialog() { + this.setState({ + selection: {}, + showEditRowDialog: false, + }); + } + + handleShowAcl(row, col){ + this.refs.dataBrowser.setEditing(true); + this.refs.dataBrowser.setCurrent({ row, col }); + } + + async confirmEditRowDialog() { + this.setState({ + selection: {}, + showEditRowDialog: false, + }); + } + renderContent() { let browser = null; let className = this.props.params.className; @@ -945,6 +983,7 @@ class Browser extends DashboardView { } browser = ( ); + } else if (this.state.showEditRowDialog) { + const classColumns = this.getClassColumns(className, false); + // create object with classColumns as property keys needed for ColumnPreferences.getOrder function + const columnsObject = {}; + classColumns.forEach((column) => { + columnsObject[column.name] = column + }); + // get ordered list of class columns + const columns = ColumnPreferences.getOrder( + columnsObject, + this.context.currentApp.applicationId, + className + ); + // extend columns with their type and targetClass properties + columns.forEach(column => { + const { type, targetClass } = columnsObject[column.name]; + column.type = type; + column.targetClass = targetClass; + }); + + const { data, selection } = this.state; + // at this moment only one row must be selected, so take the first and only one + const selectedId = Object.keys(selection)[0]; + const row = data.findIndex(d => d.id === selectedId); + const selectedRowData = data[row]; + + const selectedObject = { + row: row, + objectId: selectedId, + ...selectedRowData.attributes, + }; + + extras = ( + + ) } let notification = null; diff --git a/src/dashboard/Data/Browser/BrowserTable.react.js b/src/dashboard/Data/Browser/BrowserTable.react.js index 4b5b95255f..376c517b78 100644 --- a/src/dashboard/Data/Browser/BrowserTable.react.js +++ b/src/dashboard/Data/Browser/BrowserTable.react.js @@ -137,7 +137,8 @@ export default class BrowserTable extends React.Component { setCurrent={this.props.setCurrent} setEditing={this.props.setEditing} setRelation={this.props.setRelation} - setCopyableValue={this.props.setCopyableValue} /> + setCopyableValue={this.props.setCopyableValue} + onEditSelectedRow={this.props.onEditSelectedRow} />
); } @@ -170,7 +171,8 @@ export default class BrowserTable extends React.Component { setCurrent={this.props.setCurrent} setEditing={this.props.setEditing} setRelation={this.props.setRelation} - setCopyableValue={this.props.setCopyableValue} /> + setCopyableValue={this.props.setCopyableValue} + onEditSelectedRow={this.props.onEditSelectedRow} /> } if (this.props.editing) { @@ -240,7 +242,8 @@ export default class BrowserTable extends React.Component { ); } this.props.setEditing(false); - }} /> + }} + onCancel={() =>this.props.setEditing(false)} /> ); } } diff --git a/src/dashboard/Data/Browser/BrowserToolbar.react.js b/src/dashboard/Data/Browser/BrowserToolbar.react.js index 08ce5fe20c..2682ab5fba 100644 --- a/src/dashboard/Data/Browser/BrowserToolbar.react.js +++ b/src/dashboard/Data/Browser/BrowserToolbar.react.js @@ -32,6 +32,7 @@ let BrowserToolbar = ({ onAddColumn, onAddRow, onAddClass, + onEditSelectedRow, onAttachRows, onAttachSelectedRows, onCloneSelectedRows, @@ -105,6 +106,12 @@ let BrowserToolbar = ({ {enableColumnManipulation ? :