import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,66 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* 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) {
// ReactJS
const React = require("devtools/client/shared/vendor/react");
// Shortcuts
const { td, span } = React.DOM;
const PropTypes = React.PropTypes;
/**
* Render the default cell used for toggle buttons
*/
let LabelCell = React.createClass({
displayName: "LabelCell",
// See the TreeView component for details related
// to the 'member' object.
propTypes: {
member: PropTypes.object.isRequired
},
render: function () {
let member = this.props.member;
let level = member.level || 0;
// Compute indentation dynamically. The deeper the item is
// inside the hierarchy, the bigger is the left padding.
let rowStyle = {
"paddingInlineStart": (level * 16) + "px",
};
let iconClassList = ["treeIcon"];
if (member.hasChildren && member.loading) {
iconClassList.push("devtools-throbber");
} else if (member.hasChildren) {
iconClassList.push("theme-twisty");
}
if (member.open) {
iconClassList.push("open");
}
return (
td({
className: "treeLabelCell",
key: "default",
style: rowStyle},
span({ className: iconClassList.join(" ") }),
span({
className: "treeLabel " + member.type + "Label",
"data-level": level
}, member.name)
)
);
}
});
// Exports from this module
module.exports = LabelCell;
});

View file

@ -0,0 +1,14 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'label-cell.js',
'object-provider.js',
'tree-cell.js',
'tree-header.js',
'tree-row.js',
'tree-view.css',
'tree-view.js',
)

View file

@ -0,0 +1,90 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* 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) {
/**
* Implementation of the default data provider. A provider is state less
* object responsible for transformation data (usually a state) to
* a structure that can be directly consumed by the tree-view component.
*/
let ObjectProvider = {
getChildren: function (object) {
let children = [];
if (object instanceof ObjectProperty) {
object = object.value;
}
if (!object) {
return [];
}
if (typeof (object) == "string") {
return [];
}
for (let prop in object) {
try {
children.push(new ObjectProperty(prop, object[prop]));
} catch (e) {
console.error(e);
}
}
return children;
},
hasChildren: function (object) {
if (object instanceof ObjectProperty) {
object = object.value;
}
if (!object) {
return false;
}
if (typeof object == "string") {
return false;
}
if (typeof object !== "object") {
return false;
}
return Object.keys(object).length > 0;
},
getLabel: function (object) {
return (object instanceof ObjectProperty) ?
object.name : null;
},
getValue: function (object) {
return (object instanceof ObjectProperty) ?
object.value : null;
},
getKey: function (object) {
return (object instanceof ObjectProperty) ?
object.name : null;
},
getType: function (object) {
return (object instanceof ObjectProperty) ?
typeof object.value : typeof object;
}
};
function ObjectProperty(name, value) {
this.name = name;
this.value = value;
}
// Exports from this module
exports.ObjectProperty = ObjectProperty;
exports.ObjectProvider = ObjectProvider;
});

View file

@ -0,0 +1,101 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* 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 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
},
/**
* Optimize cell rendering. Rerender cell content only if
* the value or expanded state changes.
*/
shouldComponentUpdate: function (nextProps) {
return (this.props.value != nextProps.value) ||
(this.props.member.open != nextProps.member.open);
},
getCellClass: function (object, id) {
let decorator = this.props.decorator;
if (!decorator || !decorator.getCellClass) {
return [];
}
// Decorator can return a simple string or array of strings.
let classNames = decorator.getCellClass(object, id);
if (!classNames) {
return [];
}
if (typeof classNames == "string") {
classNames = [classNames];
}
return classNames;
},
render: function () {
let member = this.props.member;
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;
if (decorator && decorator.renderValue) {
renderValue = decorator.renderValue(member.object, id) || renderValue;
}
let props = Object.assign({}, this.props, {
object: value,
});
// Render me!
return (
td({ className: classNames.join(" ") },
span({}, renderValue(props))
)
);
}
});
// Default value rendering.
let defaultRenderValue = props => {
return (
props.object + ""
);
};
// Exports from this module
module.exports = TreeCell;
});

View file

@ -0,0 +1,100 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* 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) {
// ReactJS
const React = require("devtools/client/shared/vendor/react");
// Shortcuts
const { thead, tr, td, div } = React.DOM;
const PropTypes = React.PropTypes;
/**
* This component is responsible for rendering tree header.
* It's based on <thead> element.
*/
let TreeHeader = React.createClass({
displayName: "TreeHeader",
// See also TreeView component for detailed info about properties.
propTypes: {
// Custom tree decorator
decorator: PropTypes.object,
// True if the header should be visible
header: PropTypes.bool,
// Array with column definition
columns: PropTypes.array
},
getDefaultProps: function () {
return {
columns: [{
id: "default"
}]
};
},
getHeaderClass: function (colId) {
let decorator = this.props.decorator;
if (!decorator || !decorator.getHeaderClass) {
return [];
}
// Decorator can return a simple string or array of strings.
let classNames = decorator.getHeaderClass(colId);
if (!classNames) {
return [];
}
if (typeof classNames == "string") {
classNames = [classNames];
}
return classNames;
},
render: function () {
let cells = [];
let visible = this.props.header;
// Render the rest of the columns (if any)
this.props.columns.forEach(col => {
let cellStyle = {
"width": col.width ? col.width : "",
};
let classNames = [];
if (visible) {
classNames = this.getHeaderClass(col.id);
classNames.push("treeHeaderCell");
}
cells.push(
td({
className: classNames.join(" "),
style: cellStyle,
key: col.id},
div({ className: visible ? "treeHeaderCellBox" : "" },
visible ? col.title : ""
)
)
);
});
return (
thead({}, tr({ className: visible ? "treeHeaderRow" : "" },
cells
))
);
}
});
// Exports from this module
module.exports = TreeHeader;
});

View file

@ -0,0 +1,184 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* 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) {
// ReactJS
const React = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
// Tree
const TreeCell = React.createFactory(require("./tree-cell"));
const LabelCell = React.createFactory(require("./label-cell"));
// Shortcuts
const { tr } = React.DOM;
const PropTypes = React.PropTypes;
/**
* This template represents a node in TreeView component. It's rendered
* using <tr> element (the entire tree is one big <table>).
*/
let TreeRow = React.createClass({
displayName: "TreeRow",
// See TreeView component for more details about the props and
// the 'member' object.
propTypes: {
member: PropTypes.shape({
object: PropTypes.obSject,
name: PropTypes.sring,
type: PropTypes.string.isRequired,
rowClass: PropTypes.string.isRequired,
level: PropTypes.number.isRequired,
hasChildren: PropTypes.bool,
value: PropTypes.any,
open: PropTypes.bool.isRequired,
path: PropTypes.string.isRequired,
hidden: PropTypes.bool,
}),
decorator: PropTypes.object,
renderCell: PropTypes.object,
renderLabelCell: PropTypes.object,
columns: PropTypes.array.isRequired,
provider: PropTypes.object.isRequired,
onClick: PropTypes.func.isRequired
},
componentWillReceiveProps(nextProps) {
// I don't like accessing the underlying DOM elements directly,
// but this optimization makes the filtering so damn fast!
// The row doesn't have to be re-rendered, all we really need
// to do is toggling a class name.
// The important part is that DOM elements don't need to be
// re-created when they should appear again.
if (nextProps.member.hidden != this.props.member.hidden) {
let row = ReactDOM.findDOMNode(this);
row.classList.toggle("hidden");
}
},
/**
* Optimize row rendering. If props are the same do not render.
* This makes the rendering a lot faster!
*/
shouldComponentUpdate: function (nextProps) {
let props = ["name", "open", "value", "loading"];
for (let p in props) {
if (nextProps.member[props[p]] != this.props.member[props[p]]) {
return true;
}
}
return false;
},
getRowClass: function (object) {
let decorator = this.props.decorator;
if (!decorator || !decorator.getRowClass) {
return [];
}
// Decorator can return a simple string or array of strings.
let classNames = decorator.getRowClass(object);
if (!classNames) {
return [];
}
if (typeof classNames == "string") {
classNames = [classNames];
}
return classNames;
},
render: function () {
let member = this.props.member;
let decorator = this.props.decorator;
// Compute class name list for the <tr> element.
let classNames = this.getRowClass(member.object) || [];
classNames.push("treeRow");
classNames.push(member.type + "Row");
if (member.hasChildren) {
classNames.push("hasChildren");
}
if (member.open) {
classNames.push("opened");
}
if (member.loading) {
classNames.push("loading");
}
if (member.hidden) {
classNames.push("hidden");
}
// The label column (with toggle buttons) is usually
// the first one, but there might be cases (like in
// the Memory panel) where the toggling is done
// in the last column.
let cells = [];
// Get components for rendering cells.
let renderCell = this.props.renderCell || RenderCell;
let renderLabelCell = this.props.renderLabelCell || RenderLabelCell;
if (decorator && decorator.renderLabelCell) {
renderLabelCell = decorator.renderLabelCell(member.object) ||
renderLabelCell;
}
// Render a cell for every column.
this.props.columns.forEach(col => {
let props = Object.assign({}, this.props, {
key: col.id,
id: col.id,
value: this.props.provider.getValue(member.object, col.id)
});
if (decorator && decorator.renderCell) {
renderCell = decorator.renderCell(member.object, col.id);
}
let render = (col.id == "default") ? renderLabelCell : renderCell;
// Some cells don't have to be rendered. This happens when some
// other cells span more columns. Note that the label cells contains
// toggle buttons and should be usually there unless we are rendering
// a simple non-expandable table.
if (render) {
cells.push(render(props));
}
});
// Render tree row
return (
tr({
className: classNames.join(" "),
onClick: this.props.onClick},
cells
)
);
}
});
// Helpers
let RenderCell = props => {
return TreeCell(props);
};
let RenderLabelCell = props => {
return LabelCell(props);
};
// Exports from this module
module.exports = TreeRow;
});

View file

@ -0,0 +1,157 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@import url('resource://devtools/client/shared/components/reps/reps.css');
/******************************************************************************/
/* TreeView Colors */
:root {
--tree-link-color: blue;
--tree-header-background: #C8D2DC;
--tree-header-sorted-background: #AAC3DC;
}
/******************************************************************************/
/* TreeView Table*/
.treeTable .treeLabelCell {
padding: 2px 0;
vertical-align: top;
white-space: nowrap;
}
.treeTable .treeLabelCell::after {
content: ":";
color: var(--object-color);
}
.treeTable .treeValueCell {
padding: 2px 0;
padding-inline-start: 5px;
overflow: hidden;
}
.treeTable .treeLabel {
cursor: default;
overflow: hidden;
padding-inline-start: 4px;
white-space: nowrap;
}
/* No paddding if there is actually no label */
.treeTable .treeLabel:empty {
padding-inline-start: 0;
}
.treeTable .treeRow.hasChildren > .treeLabelCell > .treeLabel:hover {
cursor: pointer;
color: var(--tree-link-color);
text-decoration: underline;
}
/* Filtering */
.treeTable .treeRow.hidden {
display: none;
}
/******************************************************************************/
/* Toggle Icon */
.treeTable .treeRow .treeIcon {
height: 14px;
width: 14px;
font-size: 10px; /* Set the size of loading spinner */
display: inline-block;
vertical-align: bottom;
margin-inline-start: 3px;
padding-top: 1px;
}
/* All expanded/collapsed styles need to apply on immediate children
since there might be nested trees within a tree. */
.treeTable .treeRow.hasChildren > .treeLabelCell > .treeIcon {
cursor: pointer;
background-repeat: no-repeat;
}
/******************************************************************************/
/* Header */
.treeTable .treeHeaderRow {
height: 18px;
}
.treeTable .treeHeaderCell {
cursor: pointer;
-moz-user-select: none;
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
padding: 0 !important;
background: linear-gradient(
rgba(255, 255, 255, 0.05),
rgba(0, 0, 0, 0.05)),
radial-gradient(1px 60% at right,
rgba(0, 0, 0, 0.8) 0%,
transparent 80%) repeat-x var(--tree-header-background);
color: var(--theme-body-color);
white-space: nowrap;
}
.treeTable .treeHeaderCellBox {
padding: 2px 0;
padding-inline-start: 10px;
padding-inline-end: 14px;
}
.treeTable .treeHeaderRow > .treeHeaderCell:first-child > .treeHeaderCellBox {
padding: 0;
}
.treeTable .treeHeaderSorted {
background-color: var(--tree-header-sorted-background);
}
.treeTable .treeHeaderSorted > .treeHeaderCellBox {
background: url(chrome://devtools/skin/images/firebug/arrow-down.svg) no-repeat calc(100% - 4px);
}
.treeTable .treeHeaderSorted.sortedAscending > .treeHeaderCellBox {
background-image: url(chrome://devtools/skin/images/firebug/arrow-up.svg);
}
.treeTable .treeHeaderCell:hover:active {
background-image: linear-gradient(
rgba(0, 0, 0, 0.1),
transparent),
radial-gradient(1px 60% at right,
rgba(0, 0, 0, 0.8) 0%,
transparent 80%);
}
/******************************************************************************/
/* Themes */
.theme-light .treeTable .treeRow:hover,
.theme-dark .treeTable .treeRow:hover {
background-color: var(--theme-selection-background-semitransparent) !important;
}
.theme-firebug .treeTable .treeRow:hover {
background-color: var(--theme-body-background);
}
.theme-light .treeTable .treeLabel,
.theme-dark .treeTable .treeLabel {
color: var(--theme-highlight-pink);
}
.theme-light .treeTable .treeRow.hasChildren > .treeLabelCell > .treeLabel:hover,
.theme-dark .treeTable .treeRow.hasChildren > .treeLabelCell > .treeLabel:hover {
color: var(--theme-highlight-pink);
}
.theme-firebug .treeTable .treeLabel {
color: var(--theme-body-color);
}

View file

@ -0,0 +1,352 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* 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) {
// ReactJS
const React = require("devtools/client/shared/vendor/react");
// Reps
const { ObjectProvider } = require("./object-provider");
const TreeRow = React.createFactory(require("./tree-row"));
const TreeHeader = React.createFactory(require("./tree-header"));
// Shortcuts
const DOM = React.DOM;
const PropTypes = React.PropTypes;
/**
* This component represents a tree view with expandable/collapsible nodes.
* The tree is rendered using <table> element where every node is represented
* by <tr> element. The tree is one big table where nodes (rows) are properly
* indented from the left to mimic hierarchical structure of the data.
*
* The tree can have arbitrary number of columns and so, might be use
* as an expandable tree-table UI widget as well. By default, there is
* one column for node label and one for node value.
*
* The tree is maintaining its (presentation) state, which consists
* from list of expanded nodes and list of columns.
*
* Complete data provider interface:
* var TreeProvider = {
* getChildren: function(object);
* hasChildren: function(object);
* getLabel: function(object, colId);
* getValue: function(object, colId);
* getKey: function(object);
* getType: function(object);
* }
*
* Complete tree decorator interface:
* var TreeDecorator = {
* getRowClass: function(object);
* getCellClass: function(object, colId);
* getHeaderClass: function(colId);
* renderValue: function(object, colId);
* renderRow: function(object);
* renderCelL: function(object, colId);
* renderLabelCell: function(object);
* }
*/
let TreeView = React.createClass({
displayName: "TreeView",
// The only required property (not set by default) is the input data
// object that is used to puputate the tree.
propTypes: {
// The input data object.
object: PropTypes.any,
className: PropTypes.string,
// Data provider (see also the interface above)
provider: PropTypes.shape({
getChildren: PropTypes.func,
hasChildren: PropTypes.func,
getLabel: PropTypes.func,
getValue: PropTypes.func,
getKey: PropTypes.func,
getType: PropTypes.func,
}).isRequired,
// Tree decorator (see also the interface above)
decorator: PropTypes.shape({
getRowClass: PropTypes.func,
getCellClass: PropTypes.func,
getHeaderClass: PropTypes.func,
renderValue: PropTypes.func,
renderRow: PropTypes.func,
renderCelL: PropTypes.func,
renderLabelCell: PropTypes.func,
}),
// Custom tree row (node) renderer
renderRow: PropTypes.func,
// Custom cell renderer
renderCell: PropTypes.func,
// Custom value renderef
renderValue: PropTypes.func,
// Custom tree label (including a toggle button) renderer
renderLabelCell: PropTypes.func,
// Set of expanded nodes
expandedNodes: PropTypes.object,
// Custom filtering callback
onFilter: PropTypes.func,
// Custom sorting callback
onSort: PropTypes.func,
// A header is displayed if set to true
header: PropTypes.bool,
// Array of columns
columns: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string.isRequired,
title: PropTypes.string,
width: PropTypes.string
}))
},
getDefaultProps: function () {
return {
object: null,
renderRow: null,
provider: ObjectProvider,
expandedNodes: new Set(),
columns: []
};
},
getInitialState: function () {
return {
expandedNodes: this.props.expandedNodes,
columns: ensureDefaultColumn(this.props.columns)
};
},
// Node expand/collapse
toggle: function (nodePath) {
let nodes = this.state.expandedNodes;
if (this.isExpanded(nodePath)) {
nodes.delete(nodePath);
} else {
nodes.add(nodePath);
}
// Compute new state and update the tree.
this.setState(Object.assign({}, this.state, {
expandedNodes: nodes
}));
},
isExpanded: function (nodePath) {
return this.state.expandedNodes.has(nodePath);
},
// Event Handlers
onClickRow: function (nodePath, event) {
event.stopPropagation();
this.toggle(nodePath);
},
// Filtering & Sorting
/**
* Filter out nodes that don't correspond to the current filter.
* @return {Boolean} true if the node should be visible otherwise false.
*/
onFilter: function (object) {
let onFilter = this.props.onFilter;
return onFilter ? onFilter(object) : true;
},
onSort: function (parent, children) {
let onSort = this.props.onSort;
return onSort ? onSort(parent, children) : children;
},
// Members
/**
* Return children node objects (so called 'members') for given
* parent object.
*/
getMembers: function (parent, level, path) {
// Strings don't have children. Note that 'long' strings are using
// the expander icon (+/-) to display the entire original value,
// but there are no child items.
if (typeof parent == "string") {
return [];
}
let provider = this.props.provider;
let children = provider.getChildren(parent) || [];
// If the return value is non-array, the children
// are being loaded asynchronously.
if (!Array.isArray(children)) {
return children;
}
children = this.onSort(parent, children) || children;
return children.map(child => {
let key = provider.getKey(child);
let nodePath = path + "/" + key;
let type = provider.getType(child);
let hasChildren = provider.hasChildren(child);
// Value with no column specified is used for optimization.
// The row is re-rendered only if this value changes.
// Value for actual column is get when a cell is rendered.
let value = provider.getValue(child);
if (isLongString(value)) {
hasChildren = true;
}
// Return value is a 'member' object containing meta-data about
// tree node. It describes node label, value, type, etc.
return {
// An object associated with this node.
object: child,
// A label for the child node
name: provider.getLabel(child),
// Data type of the child node (used for CSS customization)
type: type,
// Class attribute computed from the type.
rowClass: "treeRow-" + type,
// Level of the child within the hierarchy (top == 0)
level: level,
// True if this node has children.
hasChildren: hasChildren,
// Value associated with this node (as provided by the data provider)
value: value,
// True if the node is expanded.
open: this.isExpanded(nodePath),
// Node path
path: nodePath,
// True if the node is hidden (used for filtering)
hidden: !this.onFilter(child)
};
});
},
/**
* Render tree rows/nodes.
*/
renderRows: function (parent, level = 0, path = "") {
let rows = [];
let decorator = this.props.decorator;
let renderRow = this.props.renderRow || TreeRow;
// Get children for given parent node, iterate over them and render
// a row for every one. Use row template (a component) from properties.
// If the return value is non-array, the children are being loaded
// asynchronously.
let members = this.getMembers(parent, level, path);
if (!Array.isArray(members)) {
return members;
}
members.forEach(member => {
if (decorator && decorator.renderRow) {
renderRow = decorator.renderRow(member.object) || renderRow;
}
let props = Object.assign({}, this.props, {
key: member.path,
member: member,
columns: this.state.columns,
onClick: this.onClickRow.bind(this, member.path)
});
// Render single row.
rows.push(renderRow(props));
// If a child node is expanded render its rows too.
if (member.hasChildren && member.open) {
let childRows = this.renderRows(member.object, level + 1,
member.path);
// If children needs to be asynchronously fetched first,
// set 'loading' property to the parent row. Otherwise
// just append children rows to the array of all rows.
if (!Array.isArray(childRows)) {
let lastIndex = rows.length - 1;
props.member.loading = true;
rows[lastIndex] = React.cloneElement(rows[lastIndex], props);
} else {
rows = rows.concat(childRows);
}
}
});
return rows;
},
render: function () {
let root = this.props.object;
let classNames = ["treeTable"];
// Use custom class name from props.
let className = this.props.className;
if (className) {
classNames.push(...className.split(" "));
}
// Alright, let's render all tree rows. The tree is one big <table>.
let rows = this.renderRows(root, 0, "");
// This happens when the view needs to do initial asynchronous
// fetch for the root object. The tree might provide a hook API
// for rendering animated spinner (just like for tree nodes).
if (!Array.isArray(rows)) {
rows = [];
}
let props = Object.assign({}, this.props, {
columns: this.state.columns
});
return (
DOM.table({
className: classNames.join(" "),
cellPadding: 0,
cellSpacing: 0},
TreeHeader(props),
DOM.tbody({},
rows
)
)
);
}
});
// Helpers
/**
* There should always be at least one column (the one with toggle buttons)
* and this function ensures that it's true.
*/
function ensureDefaultColumn(columns) {
if (!columns) {
columns = [];
}
let defaultColumn = columns.filter(col => col.id == "default");
if (defaultColumn.length) {
return columns;
}
// The default column is usually the first one.
return [{id: "default"}, ...columns];
}
function isLongString(value) {
return typeof value == "string" && value.length > 50;
}
// Exports from this module
module.exports = TreeView;
});