UNPKG

@intuitionrobotics/thunderstorm

Version:
274 lines 12.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Tree = void 0; const React = require("react"); const ts_common_1 = require("@intuitionrobotics/ts-common"); const KeyboardListener_1 = require("../../tools/KeyboardListener"); const tools_1 = require("../../utils/tools"); class Tree extends React.Component { constructor(props) { super(props); this.containerRefs = {}; this.rendererRefs = {}; this.renderedElements = []; this.renderedElementsInit = () => { const keys = Object.keys(this.state.expanded); this.renderedElements = keys.reduce((carry, key) => { if (this.state.expanded[key]) return carry; this.state.adapter.hideRoot && (0, ts_common_1.removeItemFromArray)(carry, '/'); keys.forEach(el => { if (el.startsWith(key) && el !== key) (0, ts_common_1.removeItemFromArray)(carry, el); }); return carry; }, keys); }; this.renderNode = (_data, key, _path, level) => { const nodePath = `${_path}${key}/`; const adjustedNode = this.state.adapter.adjust(_data); const data = adjustedNode.data; let filteredKeys = []; let expanded = !!this.props.checkExpanded(this.state.expanded, nodePath); if (nodePath.endsWith("_children/")) expanded = true; let renderChildren = expanded; if (typeof data !== "object") renderChildren = false; if (renderChildren) filteredKeys = this.state.adapter.getFilteredChildren(data); const nodeRefResolver = this.nodeResolver(nodePath, renderChildren, filteredKeys); const containerRefResolver = this.resolveContainer(nodePath, renderChildren, filteredKeys); return React.createElement("div", { key: nodePath, ref: nodeRefResolver }, this.renderItem(data, nodePath, key, expanded), this.renderChildren(data, nodePath, _path, level, filteredKeys, renderChildren, adjustedNode, containerRefResolver)); }; this.getChildrenContainerStyle = (level, parentNodeRef, containerRef, parentContainerRef) => { if (!containerRef) return {}; if (this.props.childrenContainerStyle) return this.props.childrenContainerStyle(level, parentNodeRef, containerRef, parentContainerRef); return { marginLeft: this.props.indentPx }; }; this.keyEventHandler = (node, e) => { this.props.keyEventHandler && this.props.keyEventHandler(node, e); let keyCode = e.code; if (keyCode === "Escape") { (0, tools_1.stopPropagation)(e); return this.props.unMountFromOutside ? this.props.unMountFromOutside() : node.blur(); } const focused = this.state.focused; const idx = this.renderedElements.findIndex(el => el === focused); if (idx >= this.renderedElements.length) return; if (focused && keyCode === "ArrowRight") { (0, tools_1.stopPropagation)(e); if (!this.props.checkExpanded(this.state.expanded, focused)) return this.expandOrCollapse(focused, true); else keyCode = "ArrowDown"; } if (focused && keyCode === "ArrowLeft") { (0, tools_1.stopPropagation)(e); if (this.props.checkExpanded(this.state.expanded, focused)) return this.expandOrCollapse(focused, false); else { const temp = focused.substr(0, focused.length - 1); if (temp.length === 0) return; const parentFocused = temp.substring(0, temp.lastIndexOf("/") + 1); return this.setFocusedNode(parentFocused); } } if (keyCode === "ArrowDown") { (0, tools_1.stopPropagation)(e); if (idx === -1 || idx + 1 === this.renderedElements.length) return this.setFocusedNode(this.renderedElements[0]); return this.setFocusedNode(this.renderedElements[idx + 1]); } if (keyCode === "ArrowUp") { (0, tools_1.stopPropagation)(e); if (idx === -1) return this.setFocusedNode(this.renderedElements[0]); if (idx === 0) return this.setFocusedNode(this.renderedElements[this.renderedElements.length - 1]); return this.setFocusedNode(this.renderedElements[idx - 1]); } if (focused && keyCode === "Enter") { (0, tools_1.stopPropagation)(e); const item = this.getItemByPath(focused); if (item.action && typeof item.action === "function") return item.action(); this.props.onNodeClicked && this.props.onNodeClicked(focused, item); } }; this.ignoreToggler = () => { }; this.toggleExpandState = (e, _expanded) => { const path = e.currentTarget.id; this.expandOrCollapse(path, _expanded); }; this.expandOrCollapse = (path, forceExpandState) => { if (path === "/" && this.state.adapter.hideRoot && forceExpandState === false) return; const treeExpandedState = this.state.expanded; const currentExpandState = treeExpandedState[path]; let newExpandState = currentExpandState === undefined; if (forceExpandState !== undefined) newExpandState = forceExpandState ? forceExpandState : false; if (newExpandState) treeExpandedState[path] = newExpandState; else delete treeExpandedState[path]; this.setState({ focused: path }); this.forceUpdate(); }; this.onNodeFocused = (e) => { // This is an assumption that we should document somewhere const path = e.currentTarget.id; const item = this.getItemByPath(path); this.props.onNodeFocused && this.props.onNodeFocused(path, item); if (this.state.focused === path) return; this.setFocusedNode(path); }; this.onNodeClicked = (e) => { this.onNodeFocused(e); // This is an assumption that we should document somewhere const path = e.currentTarget.id; const item = this.getItemByPath(path); this.props.onNodeClicked && this.props.onNodeClicked(path, item); }; this.onBlur = () => { if (this.props.onBlur) this.props.onBlur(); this.setState(state => { if (!state.focused) return state; return Object.assign(Object.assign({}, state), { lastFocused: state.focused, focused: '' }); }); }; this.onFocus = () => { if (this.props.onFocus) this.props.onFocus(); this.setState(state => { const focused = state.lastFocused || (this.state.adapter.hideRoot ? Object.keys(state.expanded)[1] : Object.keys(state.expanded)[0]); if (state.focused === focused) return state; return Object.assign(Object.assign({}, state), { lastFocused: '', focused }); }); }; this.state = { adapter: this.props.adapter, expanded: props.expanded || { "/": true } }; } static getDerivedStateFromProps(props, state) { if (props.adapter.data === state.adapter.data) return null; state.adapter = props.adapter; // Tree.recalculateExpanded(props, state); return state; } componentDidMount() { this.renderedElementsInit(); } render() { return React.createElement(KeyboardListener_1.KeyboardListener, { id: this.props.id, onKeyboardEventListener: this.keyEventHandler, onFocus: this.onFocus, onBlur: this.onBlur }, this.renderNode(this.state.adapter.data, "", "", 1)); } nodeResolver(nodePath, renderChildren, filteredKeys) { return (_ref) => { if (this.rendererRefs[nodePath]) return; this.rendererRefs[nodePath] = _ref; if (this.containerRefs[nodePath] && renderChildren && filteredKeys.length > 0) this.forceUpdate(); }; } resolveContainer(nodePath, renderChildren, filteredKeys) { return (_ref) => { if (this.containerRefs[nodePath]) return; this.containerRefs[nodePath] = _ref; if (renderChildren && filteredKeys.length > 0) this.forceUpdate(); }; } renderChildren(data, nodePath, _path, level, filteredKeys, renderChildren, adjustedNode, containerRefResolver) { if (!(filteredKeys.length > 0 && renderChildren)) return; const containerRef = this.containerRefs[nodePath]; return (React.createElement("div", { style: this.getChildrenContainerStyle(level, this.rendererRefs[nodePath], containerRef, this.containerRefs[_path]), ref: containerRefResolver }, containerRef && filteredKeys.map((childKey) => this.renderNode(data[childKey], childKey, nodePath + (adjustedNode.deltaPath ? adjustedNode.deltaPath + "/" : ""), level + 1)))); } renderItem(item, path, key, expanded) { if (this.state.adapter.hideRoot && path.length === 1) return null; const TreeNodeRenderer = this.state.adapter.treeNodeRenderer; // console.log("isParent: ", this.state.adapter.isParent(item)); const node = { adapter: this.state.adapter, propKey: key, path, item, expandToggler: this.state.adapter.isParent(item) ? this.toggleExpandState : this.ignoreToggler, onClick: this.onNodeClicked, onFocus: this.onNodeFocused, expanded: !!expanded, focused: path === this.state.focused, selected: item === this.props.selectedItem }; return React.createElement("div", { onMouseEnter: () => this.setState({ focused: node.path }), onMouseLeave: () => this.setState({ focused: '' }) }, React.createElement(TreeNodeRenderer, { item: item, node: node })); } setFocusedNode(path) { this.rendererRefs[path].scrollIntoView({ block: "nearest" }); this.setState({ focused: path }); } getItemByPath(path) { let item = this.state.adapter.data; const hierarchy = path.split('/'); hierarchy.shift(); for (const el of hierarchy) { if (el) { item = item[el]; if (!item) return; } } const deltaPath = this.state.adapter.adjust(item).deltaPath; if (deltaPath) item = item[deltaPath]; return item; } static recursivelyExpand(adapter, expandCondition = () => true, state = { '/': expandCondition('/', adapter.data, 0, '/') || undefined }) { return recursivelyExpandImpl(adapter.data, state, expandCondition, adapter); } } exports.Tree = Tree; Tree.defaultProps = { indentPx: 20, checkExpanded: (expanded, path) => expanded[path] }; const recursivelyExpandImpl = (obj, state, condition, adapter, path = "/", level = 1) => { if (obj === null) return state; const _obj = adapter.adjust(obj); const children = adapter.getFilteredChildren(obj); return children.reduce((_state, _key) => { const key = _key; const value = obj[_key]; const newPath = `${path}${key}/`; if (!_obj.deltaPath) { const b = condition(key, value, level, newPath); if (b) _state[newPath] = b; } // if (condition(key, value, level, newPath) && typeof value === "object") if (typeof value === "object") recursivelyExpandImpl(value, _state, condition, adapter, newPath, level + 1); return _state; }, state); }; //# sourceMappingURL=Tree.js.map