forked from mirrors/gecko-dev
MozReview-Commit-ID: 5jEsz5oBB9F --HG-- extra : rebase_source : 31639e8b1fb7c0fed04f9c1e5e70dab20cfdeb43
1074 lines
No EOL
33 KiB
JavaScript
1074 lines
No EOL
33 KiB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
|
|
if(typeof exports === 'object' && typeof module === 'object')
|
|
module.exports = factory(require("devtools/client/shared/vendor/react"), require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/react-prop-types"));
|
|
else if(typeof define === 'function' && define.amd)
|
|
define(["devtools/client/shared/vendor/react", "devtools/client/shared/vendor/react-dom-factories", "devtools/client/shared/vendor/react-prop-types"], factory);
|
|
else {
|
|
var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react"), require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/react-prop-types")) : factory(root["devtools/client/shared/vendor/react"], root["devtools/client/shared/vendor/react-dom-factories"], root["devtools/client/shared/vendor/react-prop-types"]);
|
|
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
|
|
}
|
|
})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_0__, __WEBPACK_EXTERNAL_MODULE_4__, __WEBPACK_EXTERNAL_MODULE_1__) {
|
|
return /******/ (function(modules) { // webpackBootstrap
|
|
/******/ // The module cache
|
|
/******/ var installedModules = {};
|
|
/******/
|
|
/******/ // The require function
|
|
/******/ function __webpack_require__(moduleId) {
|
|
/******/
|
|
/******/ // Check if module is in cache
|
|
/******/ if(installedModules[moduleId]) {
|
|
/******/ return installedModules[moduleId].exports;
|
|
/******/ }
|
|
/******/ // Create a new module (and put it into the cache)
|
|
/******/ var module = installedModules[moduleId] = {
|
|
/******/ i: moduleId,
|
|
/******/ l: false,
|
|
/******/ exports: {}
|
|
/******/ };
|
|
/******/
|
|
/******/ // Execute the module function
|
|
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
|
/******/
|
|
/******/ // Flag the module as loaded
|
|
/******/ module.l = true;
|
|
/******/
|
|
/******/ // Return the exports of the module
|
|
/******/ return module.exports;
|
|
/******/ }
|
|
/******/
|
|
/******/
|
|
/******/ // expose the modules object (__webpack_modules__)
|
|
/******/ __webpack_require__.m = modules;
|
|
/******/
|
|
/******/ // expose the module cache
|
|
/******/ __webpack_require__.c = installedModules;
|
|
/******/
|
|
/******/ // define getter function for harmony exports
|
|
/******/ __webpack_require__.d = function(exports, name, getter) {
|
|
/******/ if(!__webpack_require__.o(exports, name)) {
|
|
/******/ Object.defineProperty(exports, name, {
|
|
/******/ configurable: false,
|
|
/******/ enumerable: true,
|
|
/******/ get: getter
|
|
/******/ });
|
|
/******/ }
|
|
/******/ };
|
|
/******/
|
|
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
|
/******/ __webpack_require__.n = function(module) {
|
|
/******/ var getter = module && module.__esModule ?
|
|
/******/ function getDefault() { return module['default']; } :
|
|
/******/ function getModuleExports() { return module; };
|
|
/******/ __webpack_require__.d(getter, 'a', getter);
|
|
/******/ return getter;
|
|
/******/ };
|
|
/******/
|
|
/******/ // Object.prototype.hasOwnProperty.call
|
|
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
|
/******/
|
|
/******/ // __webpack_public_path__
|
|
/******/ __webpack_require__.p = "/assets/build";
|
|
/******/
|
|
/******/ // Load entry module and return exports
|
|
/******/ return __webpack_require__(__webpack_require__.s = 2);
|
|
/******/ })
|
|
/************************************************************************/
|
|
/******/ ([
|
|
/* 0 */
|
|
/***/ (function(module, exports) {
|
|
|
|
module.exports = __WEBPACK_EXTERNAL_MODULE_0__;
|
|
|
|
/***/ }),
|
|
/* 1 */
|
|
/***/ (function(module, exports) {
|
|
|
|
module.exports = __WEBPACK_EXTERNAL_MODULE_1__;
|
|
|
|
/***/ }),
|
|
/* 2 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
"use strict";
|
|
|
|
|
|
var _tree = __webpack_require__(3);
|
|
|
|
var _tree2 = _interopRequireDefault(_tree);
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
module.exports = {
|
|
Tree: _tree2.default
|
|
}; /* 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/. */
|
|
|
|
/***/ }),
|
|
/* 3 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
"use strict";
|
|
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
|
|
var _react = __webpack_require__(0);
|
|
|
|
var _react2 = _interopRequireDefault(_react);
|
|
|
|
var _reactDomFactories = __webpack_require__(4);
|
|
|
|
var _reactDomFactories2 = _interopRequireDefault(_reactDomFactories);
|
|
|
|
var _propTypes = __webpack_require__(1);
|
|
|
|
var _propTypes2 = _interopRequireDefault(_propTypes);
|
|
|
|
var _svgInlineReact = __webpack_require__(5);
|
|
|
|
var _svgInlineReact2 = _interopRequireDefault(_svgInlineReact);
|
|
|
|
var _arrow = __webpack_require__(7);
|
|
|
|
var _arrow2 = _interopRequireDefault(_arrow);
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
const { Component, createFactory, createElement } = _react2.default; /* 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/. */
|
|
|
|
__webpack_require__(8);
|
|
|
|
const AUTO_EXPAND_DEPTH = 0; // depth
|
|
|
|
/**
|
|
* An arrow that displays whether its node is expanded (▼) or collapsed
|
|
* (▶). When its node has no children, it is hidden.
|
|
*/
|
|
class ArrowExpander extends Component {
|
|
static get propTypes() {
|
|
return {
|
|
expanded: _propTypes2.default.bool
|
|
};
|
|
}
|
|
|
|
shouldComponentUpdate(nextProps, nextState) {
|
|
return this.props.expanded !== nextProps.expanded;
|
|
}
|
|
|
|
render() {
|
|
const {
|
|
expanded
|
|
} = this.props;
|
|
|
|
const classNames = ["arrow"];
|
|
if (expanded) {
|
|
classNames.push("expanded");
|
|
}
|
|
return createElement(_svgInlineReact2.default, {
|
|
className: classNames.join(" "),
|
|
src: _arrow2.default
|
|
});
|
|
}
|
|
}
|
|
|
|
const treeIndent = _reactDomFactories2.default.span({ className: "tree-indent" }, "\u200B");
|
|
|
|
class TreeNode extends Component {
|
|
static get propTypes() {
|
|
return {
|
|
id: _propTypes2.default.any.isRequired,
|
|
index: _propTypes2.default.number.isRequired,
|
|
depth: _propTypes2.default.number.isRequired,
|
|
focused: _propTypes2.default.bool.isRequired,
|
|
expanded: _propTypes2.default.bool.isRequired,
|
|
item: _propTypes2.default.any.isRequired,
|
|
isExpandable: _propTypes2.default.bool.isRequired,
|
|
onClick: _propTypes2.default.func,
|
|
renderItem: _propTypes2.default.func.isRequired
|
|
};
|
|
}
|
|
|
|
shouldComponentUpdate(nextProps) {
|
|
return this.props.item !== nextProps.item || this.props.focused !== nextProps.focused || this.props.expanded !== nextProps.expanded;
|
|
}
|
|
|
|
render() {
|
|
const {
|
|
depth,
|
|
id,
|
|
item,
|
|
focused,
|
|
expanded,
|
|
renderItem,
|
|
isExpandable
|
|
} = this.props;
|
|
|
|
const arrow = isExpandable ? ArrowExpanderFactory({
|
|
item,
|
|
expanded
|
|
}) : null;
|
|
|
|
let ariaExpanded;
|
|
if (this.props.isExpandable) {
|
|
ariaExpanded = false;
|
|
}
|
|
if (this.props.expanded) {
|
|
ariaExpanded = true;
|
|
}
|
|
|
|
const indents = Array.from({ length: depth }).fill(treeIndent);
|
|
let items = indents.concat(renderItem(item, depth, focused, arrow, expanded));
|
|
|
|
return _reactDomFactories2.default.div({
|
|
id,
|
|
className: "tree-node" + (focused ? " focused" : ""),
|
|
onClick: this.props.onClick,
|
|
role: "treeitem",
|
|
"aria-level": depth,
|
|
"aria-expanded": ariaExpanded,
|
|
"data-expandable": this.props.isExpandable
|
|
}, ...items);
|
|
}
|
|
}
|
|
|
|
const ArrowExpanderFactory = createFactory(ArrowExpander);
|
|
const TreeNodeFactory = createFactory(TreeNode);
|
|
|
|
/**
|
|
* Create a function that calls the given function `fn` only once per animation
|
|
* frame.
|
|
*
|
|
* @param {Function} fn
|
|
* @returns {Function}
|
|
*/
|
|
function oncePerAnimationFrame(fn) {
|
|
let animationId = null;
|
|
let argsToPass = null;
|
|
return function (...args) {
|
|
argsToPass = args;
|
|
if (animationId !== null) {
|
|
return;
|
|
}
|
|
|
|
animationId = requestAnimationFrame(() => {
|
|
fn.call(this, ...argsToPass);
|
|
animationId = null;
|
|
argsToPass = null;
|
|
});
|
|
};
|
|
}
|
|
|
|
/**
|
|
* A generic tree component. See propTypes for the public API.
|
|
*
|
|
* This tree component doesn't make any assumptions about the structure of your
|
|
* tree data. Whether children are computed on demand, or stored in an array in
|
|
* the parent's `_children` property, it doesn't matter. We only require the
|
|
* implementation of `getChildren`, `getRoots`, `getParent`, and `isExpanded`
|
|
* functions.
|
|
*
|
|
* This tree component is well tested and reliable. See the tests in ./tests
|
|
* and its usage in the performance and memory panels in mozilla-central.
|
|
*
|
|
* This tree component doesn't make any assumptions about how to render items in
|
|
* the tree. You provide a `renderItem` function, and this component will ensure
|
|
* that only those items whose parents are expanded and which are visible in the
|
|
* viewport are rendered. The `renderItem` function could render the items as a
|
|
* "traditional" tree or as rows in a table or anything else. It doesn't
|
|
* restrict you to only one certain kind of tree.
|
|
*
|
|
* The tree comes with basic styling for the indent, the arrow, as well as hovered
|
|
* and focused styles which can be override in CSS.
|
|
*
|
|
* ### Example Usage
|
|
*
|
|
* Suppose we have some tree data where each item has this form:
|
|
*
|
|
* {
|
|
* id: Number,
|
|
* label: String,
|
|
* parent: Item or null,
|
|
* children: Array of child items,
|
|
* expanded: bool,
|
|
* }
|
|
*
|
|
* Here is how we could render that data with this component:
|
|
*
|
|
* class MyTree extends Component {
|
|
* static get propTypes() {
|
|
* // The root item of the tree, with the form described above.
|
|
* return {
|
|
* root: PropTypes.object.isRequired
|
|
* };
|
|
* },
|
|
*
|
|
* render() {
|
|
* return Tree({
|
|
* itemHeight: 20, // px
|
|
*
|
|
* getRoots: () => [this.props.root],
|
|
*
|
|
* getParent: item => item.parent,
|
|
* getChildren: item => item.children,
|
|
* getKey: item => item.id,
|
|
* isExpanded: item => item.expanded,
|
|
*
|
|
* renderItem: (item, depth, isFocused, arrow, isExpanded) => {
|
|
* let className = "my-tree-item";
|
|
* if (isFocused) {
|
|
* className += " focused";
|
|
* }
|
|
* return dom.div({
|
|
* className,
|
|
* },
|
|
* arrow,
|
|
* // And here is the label for this item.
|
|
* dom.span({ className: "my-tree-item-label" }, item.label)
|
|
* );
|
|
* },
|
|
*
|
|
* onExpand: item => dispatchExpandActionToRedux(item),
|
|
* onCollapse: item => dispatchCollapseActionToRedux(item),
|
|
* });
|
|
* }
|
|
* }
|
|
*/
|
|
class Tree extends Component {
|
|
static get propTypes() {
|
|
return {
|
|
// Required props
|
|
|
|
// A function to get an item's parent, or null if it is a root.
|
|
//
|
|
// Type: getParent(item: Item) -> Maybe<Item>
|
|
//
|
|
// Example:
|
|
//
|
|
// // The parent of this item is stored in its `parent` property.
|
|
// getParent: item => item.parent
|
|
getParent: _propTypes2.default.func.isRequired,
|
|
|
|
// A function to get an item's children.
|
|
//
|
|
// Type: getChildren(item: Item) -> [Item]
|
|
//
|
|
// Example:
|
|
//
|
|
// // This item's children are stored in its `children` property.
|
|
// getChildren: item => item.children
|
|
getChildren: _propTypes2.default.func.isRequired,
|
|
|
|
// A function which takes an item and ArrowExpander component instance and
|
|
// returns a component, or text, or anything else that React considers
|
|
// renderable.
|
|
//
|
|
// Type: renderItem(item: Item,
|
|
// depth: Number,
|
|
// isFocused: Boolean,
|
|
// arrow: ReactComponent,
|
|
// isExpanded: Boolean) -> ReactRenderable
|
|
//
|
|
// Example:
|
|
//
|
|
// renderItem: (item, depth, isFocused, arrow, isExpanded) => {
|
|
// let className = "my-tree-item";
|
|
// if (isFocused) {
|
|
// className += " focused";
|
|
// }
|
|
// return dom.div(
|
|
// {
|
|
// className,
|
|
// style: { marginLeft: depth * 10 + "px" }
|
|
// },
|
|
// arrow,
|
|
// dom.span({ className: "my-tree-item-label" }, item.label)
|
|
// );
|
|
// },
|
|
renderItem: _propTypes2.default.func.isRequired,
|
|
|
|
// A function which returns the roots of the tree (forest).
|
|
//
|
|
// Type: getRoots() -> [Item]
|
|
//
|
|
// Example:
|
|
//
|
|
// // In this case, we only have one top level, root item. You could
|
|
// // return multiple items if you have many top level items in your
|
|
// // tree.
|
|
// getRoots: () => [this.props.rootOfMyTree]
|
|
getRoots: _propTypes2.default.func.isRequired,
|
|
|
|
// A function to get a unique key for the given item. This helps speed up
|
|
// React's rendering a *TON*.
|
|
//
|
|
// Type: getKey(item: Item) -> String
|
|
//
|
|
// Example:
|
|
//
|
|
// getKey: item => `my-tree-item-${item.uniqueId}`
|
|
getKey: _propTypes2.default.func.isRequired,
|
|
|
|
// A function to get whether an item is expanded or not. If an item is not
|
|
// expanded, then it must be collapsed.
|
|
//
|
|
// Type: isExpanded(item: Item) -> Boolean
|
|
//
|
|
// Example:
|
|
//
|
|
// isExpanded: item => item.expanded,
|
|
isExpanded: _propTypes2.default.func.isRequired,
|
|
|
|
// Optional props
|
|
|
|
// The currently focused item, if any such item exists.
|
|
focused: _propTypes2.default.any,
|
|
|
|
// Handle when a new item is focused.
|
|
onFocus: _propTypes2.default.func,
|
|
|
|
// The depth to which we should automatically expand new items.
|
|
autoExpandDepth: _propTypes2.default.number,
|
|
// Should auto expand all new items or just the new items under the first
|
|
// root item.
|
|
autoExpandAll: _propTypes2.default.bool,
|
|
|
|
// Note: the two properties below are mutually exclusive. Only one of the
|
|
// label properties is necessary.
|
|
// ID of an element whose textual content serves as an accessible label for
|
|
// a tree.
|
|
labelledby: _propTypes2.default.string,
|
|
// Accessibility label for a tree widget.
|
|
label: _propTypes2.default.string,
|
|
|
|
// Optional event handlers for when items are expanded or collapsed. Useful
|
|
// for dispatching redux events and updating application state, maybe lazily
|
|
// loading subtrees from a worker, etc.
|
|
//
|
|
// Type:
|
|
// onExpand(item: Item)
|
|
// onCollapse(item: Item)
|
|
//
|
|
// Example:
|
|
//
|
|
// onExpand: item => dispatchExpandActionToRedux(item)
|
|
onExpand: _propTypes2.default.func,
|
|
onCollapse: _propTypes2.default.func,
|
|
isExpandable: _propTypes2.default.func,
|
|
// Additional classes to add to the root element.
|
|
className: _propTypes2.default.string,
|
|
// style object to be applied to the root element.
|
|
style: _propTypes2.default.object
|
|
};
|
|
}
|
|
|
|
static get defaultProps() {
|
|
return {
|
|
autoExpandDepth: AUTO_EXPAND_DEPTH,
|
|
autoExpandAll: true
|
|
};
|
|
}
|
|
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
this.state = {
|
|
seen: new Set()
|
|
};
|
|
|
|
this._onExpand = oncePerAnimationFrame(this._onExpand).bind(this);
|
|
this._onCollapse = oncePerAnimationFrame(this._onCollapse).bind(this);
|
|
this._focusPrevNode = oncePerAnimationFrame(this._focusPrevNode).bind(this);
|
|
this._focusNextNode = oncePerAnimationFrame(this._focusNextNode).bind(this);
|
|
this._focusParentNode = oncePerAnimationFrame(this._focusParentNode).bind(this);
|
|
|
|
this._autoExpand = this._autoExpand.bind(this);
|
|
this._preventArrowKeyScrolling = this._preventArrowKeyScrolling.bind(this);
|
|
this._dfs = this._dfs.bind(this);
|
|
this._dfsFromRoots = this._dfsFromRoots.bind(this);
|
|
this._focus = this._focus.bind(this);
|
|
this._scrollNodeIntoView = this._scrollNodeIntoView.bind(this);
|
|
this._onBlur = this._onBlur.bind(this);
|
|
this._onKeyDown = this._onKeyDown.bind(this);
|
|
this._nodeIsExpandable = this._nodeIsExpandable.bind(this);
|
|
}
|
|
|
|
componentDidMount() {
|
|
this._autoExpand();
|
|
if (this.props.focused) {
|
|
this._scrollNodeIntoView(this.props.focused);
|
|
}
|
|
}
|
|
|
|
componentWillReceiveProps(nextProps) {
|
|
this._autoExpand();
|
|
}
|
|
|
|
componentDidUpdate(prevProps, prevState) {
|
|
if (prevProps.focused !== this.props.focused) {
|
|
this._scrollNodeIntoView(this.props.focused);
|
|
}
|
|
}
|
|
|
|
_autoExpand() {
|
|
if (!this.props.autoExpandDepth) {
|
|
return;
|
|
}
|
|
|
|
// Automatically expand the first autoExpandDepth levels for new items. Do
|
|
// not use the usual DFS infrastructure because we don't want to ignore
|
|
// collapsed nodes.
|
|
const autoExpand = (item, currentDepth) => {
|
|
if (currentDepth >= this.props.autoExpandDepth || this.state.seen.has(item)) {
|
|
return;
|
|
}
|
|
|
|
this.props.onExpand(item);
|
|
this.state.seen.add(item);
|
|
|
|
const children = this.props.getChildren(item);
|
|
const length = children.length;
|
|
for (let i = 0; i < length; i++) {
|
|
autoExpand(children[i], currentDepth + 1);
|
|
}
|
|
};
|
|
|
|
const roots = this.props.getRoots();
|
|
const length = roots.length;
|
|
if (this.props.autoExpandAll) {
|
|
for (let i = 0; i < length; i++) {
|
|
autoExpand(roots[i], 0);
|
|
}
|
|
} else if (length != 0) {
|
|
autoExpand(roots[0], 0);
|
|
}
|
|
}
|
|
|
|
_preventArrowKeyScrolling(e) {
|
|
switch (e.key) {
|
|
case "ArrowUp":
|
|
case "ArrowDown":
|
|
case "ArrowLeft":
|
|
case "ArrowRight":
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
if (e.nativeEvent) {
|
|
if (e.nativeEvent.preventDefault) {
|
|
e.nativeEvent.preventDefault();
|
|
}
|
|
if (e.nativeEvent.stopPropagation) {
|
|
e.nativeEvent.stopPropagation();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform a pre-order depth-first search from item.
|
|
*/
|
|
_dfs(item, maxDepth = Infinity, traversal = [], _depth = 0) {
|
|
traversal.push({ item, depth: _depth });
|
|
|
|
if (!this.props.isExpanded(item)) {
|
|
return traversal;
|
|
}
|
|
|
|
const nextDepth = _depth + 1;
|
|
|
|
if (nextDepth > maxDepth) {
|
|
return traversal;
|
|
}
|
|
|
|
const children = this.props.getChildren(item);
|
|
const length = children.length;
|
|
for (let i = 0; i < length; i++) {
|
|
this._dfs(children[i], maxDepth, traversal, nextDepth);
|
|
}
|
|
|
|
return traversal;
|
|
}
|
|
|
|
/**
|
|
* Perform a pre-order depth-first search over the whole forest.
|
|
*/
|
|
_dfsFromRoots(maxDepth = Infinity) {
|
|
const traversal = [];
|
|
|
|
const roots = this.props.getRoots();
|
|
const length = roots.length;
|
|
for (let i = 0; i < length; i++) {
|
|
this._dfs(roots[i], maxDepth, traversal);
|
|
}
|
|
|
|
return traversal;
|
|
}
|
|
|
|
/**
|
|
* Expands current row.
|
|
*
|
|
* @param {Object} item
|
|
* @param {Boolean} expandAllChildren
|
|
*/
|
|
_onExpand(item, expandAllChildren) {
|
|
if (this.props.onExpand) {
|
|
this.props.onExpand(item);
|
|
|
|
if (expandAllChildren) {
|
|
const children = this._dfs(item);
|
|
const length = children.length;
|
|
for (let i = 0; i < length; i++) {
|
|
this.props.onExpand(children[i].item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Collapses current row.
|
|
*
|
|
* @param {Object} item
|
|
*/
|
|
_onCollapse(item) {
|
|
if (this.props.onCollapse) {
|
|
this.props.onCollapse(item);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the passed in item to be the focused item.
|
|
*
|
|
* @param {Object|undefined} item
|
|
* The item to be focused, or undefined to focus no item.
|
|
*
|
|
* @param {Object|undefined} options
|
|
* An options object which can contain:
|
|
* - dir: "up" or "down" to indicate if we should scroll the element to the
|
|
* top or the bottom of the scrollable container when the element is
|
|
* off canvas.
|
|
*/
|
|
_focus(item, options) {
|
|
this._scrollNodeIntoView(item, options);
|
|
if (this.props.onFocus) {
|
|
this.props.onFocus(item);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the passed in item to be the focused item.
|
|
*
|
|
* @param {Object|undefined} item
|
|
* The item to be scrolled to.
|
|
*
|
|
* @param {Object|undefined} options
|
|
* An options object which can contain:
|
|
* - dir: "up" or "down" to indicate if we should scroll the element to the
|
|
* top or the bottom of the scrollable container when the element is
|
|
* off canvas.
|
|
*/
|
|
_scrollNodeIntoView(item, options = {}) {
|
|
if (item !== undefined) {
|
|
const treeElement = this.treeRef;
|
|
const element = document.getElementById(this.props.getKey(item));
|
|
if (element) {
|
|
const { top, bottom } = element.getBoundingClientRect();
|
|
const closestScrolledParent = node => {
|
|
if (node == null) {
|
|
return null;
|
|
}
|
|
|
|
if (node.scrollHeight > node.clientHeight) {
|
|
return node;
|
|
}
|
|
return closestScrolledParent(node.parentNode);
|
|
};
|
|
const scrolledParent = closestScrolledParent(treeElement);
|
|
const isVisible = !scrolledParent || top >= 0 && bottom <= scrolledParent.clientHeight;
|
|
|
|
if (!isVisible) {
|
|
let scrollToTop = !options.alignTo && top < 0 || options.alignTo === "top";
|
|
element.scrollIntoView(scrollToTop);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the state to have no focused item.
|
|
*/
|
|
_onBlur() {
|
|
this._focus(undefined);
|
|
}
|
|
|
|
/**
|
|
* Handles key down events in the tree's container.
|
|
*
|
|
* @param {Event} e
|
|
*/
|
|
_onKeyDown(e) {
|
|
if (this.props.focused == null) {
|
|
return;
|
|
}
|
|
|
|
// Allow parent nodes to use navigation arrows with modifiers.
|
|
if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
|
|
return;
|
|
}
|
|
|
|
this._preventArrowKeyScrolling(e);
|
|
|
|
switch (e.key) {
|
|
case "ArrowUp":
|
|
this._focusPrevNode();
|
|
return;
|
|
|
|
case "ArrowDown":
|
|
this._focusNextNode();
|
|
return;
|
|
|
|
case "ArrowLeft":
|
|
if (this.props.isExpanded(this.props.focused) && this._nodeIsExpandable(this.props.focused)) {
|
|
this._onCollapse(this.props.focused);
|
|
} else {
|
|
this._focusParentNode();
|
|
}
|
|
return;
|
|
|
|
case "ArrowRight":
|
|
if (this._nodeIsExpandable(this.props.focused) && !this.props.isExpanded(this.props.focused)) {
|
|
this._onExpand(this.props.focused);
|
|
} else {
|
|
this._focusNextNode();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the previous node relative to the currently focused item, to focused.
|
|
*/
|
|
_focusPrevNode() {
|
|
// Start a depth first search and keep going until we reach the currently
|
|
// focused node. Focus the previous node in the DFS, if it exists. If it
|
|
// doesn't exist, we're at the first node already.
|
|
|
|
let prev;
|
|
|
|
const traversal = this._dfsFromRoots();
|
|
const length = traversal.length;
|
|
for (let i = 0; i < length; i++) {
|
|
const item = traversal[i].item;
|
|
if (item === this.props.focused) {
|
|
break;
|
|
}
|
|
prev = item;
|
|
}
|
|
if (prev === undefined) {
|
|
return;
|
|
}
|
|
|
|
this._focus(prev, { alignTo: "top" });
|
|
}
|
|
|
|
/**
|
|
* Handles the down arrow key which will focus either the next child
|
|
* or sibling row.
|
|
*/
|
|
_focusNextNode() {
|
|
// Start a depth first search and keep going until we reach the currently
|
|
// focused node. Focus the next node in the DFS, if it exists. If it
|
|
// doesn't exist, we're at the last node already.
|
|
const traversal = this._dfsFromRoots();
|
|
const length = traversal.length;
|
|
let i = 0;
|
|
|
|
while (i < length) {
|
|
if (traversal[i].item === this.props.focused) {
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if (i + 1 < traversal.length) {
|
|
this._focus(traversal[i + 1].item, { alignTo: "bottom" });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles the left arrow key, going back up to the current rows'
|
|
* parent row.
|
|
*/
|
|
_focusParentNode() {
|
|
const parent = this.props.getParent(this.props.focused);
|
|
if (!parent) {
|
|
this._focusPrevNode(this.props.focused);
|
|
return;
|
|
}
|
|
|
|
const traversal = this._dfsFromRoots();
|
|
const length = traversal.length;
|
|
let parentIndex = 0;
|
|
for (; parentIndex < length; parentIndex++) {
|
|
if (traversal[parentIndex].item === parent) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
this._focus(parent, { alignTo: "top" });
|
|
}
|
|
|
|
_nodeIsExpandable(item) {
|
|
return this.props.isExpandable ? this.props.isExpandable(item) : !!this.props.getChildren(item).length;
|
|
}
|
|
|
|
render() {
|
|
const traversal = this._dfsFromRoots();
|
|
const {
|
|
focused
|
|
} = this.props;
|
|
|
|
const nodes = traversal.map((v, i) => {
|
|
const { item, depth } = traversal[i];
|
|
const key = this.props.getKey(item, i);
|
|
return TreeNodeFactory({
|
|
key,
|
|
id: key,
|
|
index: i,
|
|
item,
|
|
depth,
|
|
renderItem: this.props.renderItem,
|
|
focused: focused === item,
|
|
expanded: this.props.isExpanded(item),
|
|
isExpandable: this._nodeIsExpandable(item),
|
|
onExpand: this._onExpand,
|
|
onCollapse: this._onCollapse,
|
|
onClick: e => {
|
|
this._focus(item);
|
|
if (this.props.isExpanded(item)) {
|
|
this.props.onCollapse(item);
|
|
} else {
|
|
this.props.onExpand(item, e.altKey);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
const style = Object.assign({}, this.props.style || {}, {
|
|
padding: 0,
|
|
margin: 0
|
|
});
|
|
|
|
return _reactDomFactories2.default.div({
|
|
className: `tree ${this.props.className ? this.props.className : ""}`,
|
|
ref: el => {
|
|
this.treeRef = el;
|
|
},
|
|
role: "tree",
|
|
tabIndex: "0",
|
|
onKeyDown: this._onKeyDown,
|
|
onKeyPress: this._preventArrowKeyScrolling,
|
|
onKeyUp: this._preventArrowKeyScrolling,
|
|
onFocus: ({ nativeEvent }) => {
|
|
if (focused || !nativeEvent || !this.treeRef) {
|
|
return;
|
|
}
|
|
|
|
let { explicitOriginalTarget } = nativeEvent;
|
|
// Only set default focus to the first tree node if the focus came
|
|
// from outside the tree (e.g. by tabbing to the tree from other
|
|
// external elements).
|
|
if (explicitOriginalTarget !== this.treeRef && !this.treeRef.contains(explicitOriginalTarget)) {
|
|
this._focus(traversal[0].item);
|
|
}
|
|
},
|
|
onBlur: this._onBlur,
|
|
onClick: () => {
|
|
// Focus should always remain on the tree container itself.
|
|
this.treeRef.focus();
|
|
},
|
|
"aria-label": this.props.label,
|
|
"aria-labelledby": this.props.labelledby,
|
|
"aria-activedescendant": focused && this.props.getKey(focused),
|
|
style
|
|
}, nodes);
|
|
}
|
|
}
|
|
|
|
exports.default = Tree;
|
|
|
|
/***/ }),
|
|
/* 4 */
|
|
/***/ (function(module, exports) {
|
|
|
|
module.exports = __WEBPACK_EXTERNAL_MODULE_4__;
|
|
|
|
/***/ }),
|
|
/* 5 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
"use strict";
|
|
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
|
|
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
|
|
|
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
|
|
var _react = __webpack_require__(0);
|
|
|
|
var _react2 = _interopRequireDefault(_react);
|
|
|
|
var _propTypes = __webpack_require__(1);
|
|
|
|
var _util = __webpack_require__(6);
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
|
|
|
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|
|
|
|
var process = process || { env: {} };
|
|
|
|
var InlineSVG = function (_React$Component) {
|
|
_inherits(InlineSVG, _React$Component);
|
|
|
|
function InlineSVG() {
|
|
_classCallCheck(this, InlineSVG);
|
|
|
|
return _possibleConstructorReturn(this, (InlineSVG.__proto__ || Object.getPrototypeOf(InlineSVG)).apply(this, arguments));
|
|
}
|
|
|
|
_createClass(InlineSVG, [{
|
|
key: 'componentWillReceiveProps',
|
|
value: function componentWillReceiveProps(_ref) {
|
|
var children = _ref.children;
|
|
|
|
if ("production" !== process.env.NODE_ENV && children != null) {
|
|
console.info('<InlineSVG />: `children` prop will be ignored.');
|
|
}
|
|
}
|
|
}, {
|
|
key: 'render',
|
|
value: function render() {
|
|
var Element = void 0,
|
|
__html = void 0,
|
|
svgProps = void 0;
|
|
|
|
var _props = this.props,
|
|
element = _props.element,
|
|
raw = _props.raw,
|
|
src = _props.src,
|
|
otherProps = _objectWithoutProperties(_props, ['element', 'raw', 'src']);
|
|
|
|
if (raw === true) {
|
|
Element = 'svg';
|
|
svgProps = (0, _util.extractSVGProps)(src);
|
|
__html = (0, _util.getSVGFromSource)(src).innerHTML;
|
|
}
|
|
__html = __html || src;
|
|
Element = Element || element;
|
|
svgProps = svgProps || {};
|
|
|
|
return _react2.default.createElement(Element, _extends({}, svgProps, otherProps, { src: null, children: null,
|
|
dangerouslySetInnerHTML: { __html: __html } }));
|
|
}
|
|
}]);
|
|
|
|
return InlineSVG;
|
|
}(_react2.default.Component);
|
|
|
|
exports.default = InlineSVG;
|
|
|
|
|
|
InlineSVG.defaultProps = {
|
|
element: 'i',
|
|
raw: false,
|
|
src: ''
|
|
};
|
|
|
|
InlineSVG.propTypes = {
|
|
src: _propTypes.string.isRequired,
|
|
element: _propTypes.string,
|
|
raw: _propTypes.bool
|
|
};
|
|
|
|
/***/ }),
|
|
/* 6 */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
"use strict";
|
|
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.convertReactSVGDOMProperty = convertReactSVGDOMProperty;
|
|
exports.startsWith = startsWith;
|
|
exports.serializeAttrs = serializeAttrs;
|
|
exports.getSVGFromSource = getSVGFromSource;
|
|
exports.extractSVGProps = extractSVGProps;
|
|
// Transform DOM prop/attr names applicable to `<svg>` element but react-limited
|
|
|
|
function convertReactSVGDOMProperty(str) {
|
|
return str.replace(/[-|:]([a-z])/g, function (g) {
|
|
return g[1].toUpperCase();
|
|
});
|
|
}
|
|
|
|
function startsWith(str, substring) {
|
|
return str.indexOf(substring) === 0;
|
|
}
|
|
|
|
var DataPropPrefix = 'data-';
|
|
// Serialize `Attr` objects in `NamedNodeMap`
|
|
function serializeAttrs(map) {
|
|
var ret = {};
|
|
for (var prop, i = 0; i < map.length; i++) {
|
|
var key = map[i].name;
|
|
if (!startsWith(key, DataPropPrefix)) {
|
|
prop = convertReactSVGDOMProperty(key);
|
|
}
|
|
ret[prop] = map[i].value;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
function getSVGFromSource(src) {
|
|
var svgContainer = document.createElement('div');
|
|
svgContainer.innerHTML = src;
|
|
var svg = svgContainer.firstElementChild;
|
|
svg.remove(); // deref from parent element
|
|
return svg;
|
|
}
|
|
|
|
// get <svg /> element props
|
|
function extractSVGProps(src) {
|
|
var map = getSVGFromSource(src).attributes;
|
|
return map.length > 0 ? serializeAttrs(map) : null;
|
|
}
|
|
|
|
/***/ }),
|
|
/* 7 */
|
|
/***/ (function(module, exports) {
|
|
|
|
module.exports = "<!-- 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/. --><svg viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8 13.4c-.5 0-.9-.2-1.2-.6L.4 5.2C0 4.7-.1 4.3.2 3.7S1 3 1.6 3h12.8c.6 0 1.2.1 1.4.7.3.6.2 1.1-.2 1.6l-6.4 7.6c-.3.4-.7.5-1.2.5z\"></path></svg>"
|
|
|
|
/***/ }),
|
|
/* 8 */
|
|
/***/ (function(module, exports) {
|
|
|
|
// removed by extract-text-webpack-plugin
|
|
|
|
/***/ })
|
|
/******/ ]);
|
|
}); |