Bug 1328500 - Support input field in TreeView r?honza
MozReview-Commit-ID: G80lLUK8CPT
--- a/devtools/client/shared/components/tree/tree-cell.js
+++ b/devtools/client/shared/components/tree/tree-cell.js
@@ -5,42 +5,49 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// Make this available to both AMD and CJS environments
define(function (require, exports, module) {
const React = require("devtools/client/shared/vendor/react");
// Shortcuts
- const { td, span } = React.DOM;
+ const { input, span, td } = React.DOM;
const PropTypes = React.PropTypes;
/**
* This template represents a cell in TreeView row. It's rendered
* using <td> element (the row is <tr> and the entire tree is <table>).
*/
let TreeCell = React.createClass({
displayName: "TreeCell",
// See TreeView component for detailed property explanation.
propTypes: {
value: PropTypes.any,
decorator: PropTypes.object,
id: PropTypes.string.isRequired,
member: PropTypes.object.isRequired,
- renderValue: PropTypes.func.isRequired
+ renderValue: PropTypes.func.isRequired,
+ enableInput: PropTypes.bool,
+ },
+
+ getInitialState: function () {
+ return {
+ inputEnabled: false,
+ };
},
/**
* Optimize cell rendering. Rerender cell content only if
* the value or expanded state changes.
*/
- shouldComponentUpdate: function (nextProps) {
+ shouldComponentUpdate: function (nextProps, nextState) {
return (this.props.value != nextProps.value) ||
- (this.props.member.open != nextProps.member.open);
+ (this.state !== nextState);
},
getCellClass: function (object, id) {
let decorator = this.props.decorator;
if (!decorator || !decorator.getCellClass) {
return [];
}
@@ -52,43 +59,70 @@ define(function (require, exports, modul
if (typeof classNames == "string") {
classNames = [classNames];
}
return classNames;
},
+ updateInputEnabled: function (evt) {
+ this.setState(Object.assign({}, this.state, {
+ inputEnabled: evt.target.nodeName !== "input",
+ }));
+ },
+
render: function () {
- let member = this.props.member;
+ let {
+ member,
+ id,
+ value,
+ decorator,
+ renderValue,
+ enableInput,
+ } = this.props;
let type = member.type || "";
- let id = this.props.id;
- let value = this.props.value;
- let decorator = this.props.decorator;
// Compute class name list for the <td> element.
let classNames = this.getCellClass(member.object, id) || [];
classNames.push("treeValueCell");
classNames.push(type + "Cell");
// Render value using a default render function or custom
// provided function from props or a decorator.
- let renderValue = this.props.renderValue || defaultRenderValue;
+ renderValue = renderValue || defaultRenderValue;
if (decorator && decorator.renderValue) {
renderValue = decorator.renderValue(member.object, id) || renderValue;
}
let props = Object.assign({}, this.props, {
object: value,
});
+ let cellElement;
+ if (enableInput && this.state.inputEnabled && type !== "object") {
+ classNames.push("inputEnabled");
+ cellElement = input({
+ autoFocus: true,
+ onBlur: this.updateInputEnabled,
+ readOnly: true,
+ value,
+ });
+ } else {
+ cellElement = span({
+ onClick: (type !== "object") ? this.updateInputEnabled : null,
+ },
+ renderValue(props)
+ );
+ }
+
// Render me!
return (
td({ className: classNames.join(" ") },
- span({}, renderValue(props))
+ cellElement
)
);
}
});
// Default value rendering.
let defaultRenderValue = props => {
return (
--- a/devtools/client/shared/components/tree/tree-header.js
+++ b/devtools/client/shared/components/tree/tree-header.js
@@ -75,19 +75,19 @@ define(function (require, exports, modul
classNames.push("treeHeaderCell");
}
cells.push(
td({
className: classNames.join(" "),
style: cellStyle,
key: col.id},
- div({ className: visible ? "treeHeaderCellBox" : "" },
- visible ? col.title : ""
- )
+ visible ? div({ className: "treeHeaderCellBox"},
+ col.title
+ ) : null,
)
);
});
return (
thead({}, tr({ className: visible ? "treeHeaderRow" : "" },
cells
))
--- a/devtools/client/shared/components/tree/tree-view.css
+++ b/devtools/client/shared/components/tree/tree-view.css
@@ -29,16 +29,35 @@
}
.treeTable .treeValueCell {
padding: 2px 0;
padding-inline-start: 5px;
overflow: hidden;
}
+.treeTable .treeValueCell.inputEnabled {
+ padding-top: 0;
+ padding-bottom: 0;
+}
+
+.treeTable .treeValueCell input {
+ width: 100%;
+ background: none;
+ border: none;
+ color: inherit;
+ margin-inline-end: 2px;
+}
+
+.treeTable .treeValueCell input:focus {
+ outline: none;
+ box-shadow: var(--theme-focus-box-shadow-textbox);
+ transition: all 0.2s ease-in-out;
+}
+
.treeTable .treeLabel {
cursor: default;
overflow: hidden;
padding-inline-start: 4px;
white-space: nowrap;
unicode-bidi: -moz-plaintext;
}
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -1097,26 +1097,26 @@
content: "";
}
/* Layout additional warning icon in tree value cell */
.security-info-value {
display: flex;
}
-.security-info-value .textbox-input {
+.treeTable .textbox-input {
text-overflow: ellipsis;
border: none;
background: none;
color: inherit;
width: 100%;
margin-inline-end: 2px;
}
-.security-info-value .textbox-input:focus {
+.treeTable .textbox-input:focus {
outline: 0;
box-shadow: var(--theme-focus-box-shadow-textbox);
}
.treeTable .treeLabel {
font-weight: 600;
}