UNPKG

@kenshooui/react-tree

Version:

React Tree is a straight forward component that allows a user to display and manage a hierarchical structure of items in a clear and comfortable way.

433 lines (418 loc) 18.2 kB
"use strict"; function _interopDefault(ex) { return ex && "object" == typeof ex && "default" in ex ? ex.default : ex; } Object.defineProperty(exports, "__esModule", { value: !0 }); var core = require("@emotion/core"), React = require("react"), React__default = _interopDefault(React), containerCss = function(_ref) { return { width: _ref.width, height: _ref.height, backgroundColor: "#F8F9FA", boxShadow: "0 6px 10px 0 rgba(27, 32, 70, 0.09)", border: "1px solid #E1E4EB", color: "#545769", borderRadius: "4px", position: "relative", fontFamily: "Arial, Helvetica, sans-serif" }; }, wrapperCss = function() { return { height: "40px", display: "flex", alignItems: "center", padding: "0 6px" }; }, backIconCss = function() { return { marginRight: "3px", cursor: "pointer" }; }, css = function(props) { return { display: "flex", justifyContent: "space-between", alignItems: "center", cursor: "pointer", height: "" !== props.searchTerm ? "50px" : "30px", marginTop: "3px", padding: "0 6px", fontSize: "14px", "&:hover": { backgroundColor: "#ECEEF3" }, "&:active": { backgroundColor: "#E1E4EB" } }; }, forwardIconCss = function() { return {}; }, selectedItem = function(props) { return { fontWeight: props.isSelected ? 600 : 400 }; }, initialCss = function(props) { return { color: "#545769", fontWeight: props.isSelected ? 600 : 400 }; }, highlightCss = function() { return { fontWeight: 600, color: "#268DEC" }; }, parentsCss = function() { return { color: "#98A1B8", marginTop: "2px" }; }, itemsCss = function(_ref) { return { maxHeight: _ref.height, overflowY: "auto" }; }, css$1 = function() { return { width: "100%", height: "35px", paddingLeft: "35px", border: "1px solid #cfd1d2", borderRight: "none", borderLeft: "none", backgroundColor: "#F8F9FA", "&:focus": { outline: "none !important" }, "&:active": { outline: "none !important" } }; }, searchIconCss = function() { return { position: "absolute", top: "8px", left: "8px" }; }, clearIconCss = function() { return { position: "absolute", top: "8px", right: "8px", cursor: "pointer" }; }, wrapperCss$1 = function() { return { position: "relative" }; }, css$2 = function(_ref) { return { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", height: _ref.height }; }, icon = function() { return { fontSize: "20px", marginBottom: "5px" }; }, text = function() { return { fontSize: "14px" }; }, defaultStyles = { container: containerCss, header: wrapperCss, headerBackIcon: backIconCss, item: css, highlight: highlightCss, searchItemInitial: initialCss, parents: parentsCss, items: itemsCss, noResults: css$2, noResultsIcon: icon, noResultsText: text, input: css$1, searchInput: searchIconCss, clearInput: clearIconCss, forwardIcon: forwardIconCss, selectedItem: selectedItem, inputWrapper: wrapperCss$1 }, SearchedItem = function(props) { var _props$item = props.item, item = void 0 === _props$item ? [ "" ] : _props$item, _props$searchIndex = props.searchIndex, searchIndex = void 0 === _props$searchIndex ? 0 : _props$searchIndex, _props$searchTerm = props.searchTerm, searchTerm = void 0 === _props$searchTerm ? "" : _props$searchTerm, getStyles = props.getStyles, leaf = item[item.length - 1], parents = item.slice(0, item.length - 1).join(" / "); return core.jsx("div", null, core.jsx("div", null, core.jsx("span", { css: getStyles("searchItemInitial", props) }, leaf.substring(0, searchIndex)), core.jsx("span", { css: getStyles("highlight", props) }, leaf.substring(searchIndex, searchIndex + searchTerm.length)), core.jsx("span", { css: getStyles("searchItemInitial", props) }, leaf.substring(searchIndex + searchTerm.length))), core.jsx("div", { css: getStyles("parents", props) }, parents)); }, BasicItem = function(props) { var _props$label = props.label, label = void 0 === _props$label ? "" : _props$label, getStyles = props.getStyles; return core.jsx("span", { css: getStyles("selectedItem", props) }, label); }, ForwardIconRenderer = function() { return core.jsx(React__default.Fragment, null, "➡️"); }, ItemRenderer = function(props) { var _selectedItem$item, getStyles = props.getStyles, _props$searchTerm = props.searchTerm, searchTerm = void 0 === _props$searchTerm ? "" : _props$searchTerm, _props$item = props.item, item = (_props$item = void 0 === _props$item ? { item: [ "" ], hasChild: !1, currentDepth: 0 } : _props$item).item, hasChild = _props$item.hasChild, currentDepth = _props$item.currentDepth, _onClick = props.onClick, _props$forwardIconRen = props.forwardIconRenderer, ForwardIcon = void 0 === _props$forwardIconRen ? ForwardIconRenderer : _props$forwardIconRen, selectedItem = props.selectedItem, isItemSelected = item.toString() === (null == selectedItem ? void 0 : null === (_selectedItem$item = selectedItem.item) || void 0 === _selectedItem$item ? void 0 : _selectedItem$item.toString()), searchIndex = item[item.length - 1].toLowerCase().indexOf(searchTerm.trim().toLowerCase()); return core.jsx("div", { onClick: function() { return _onClick(item[currentDepth], item, hasChild); }, css: getStyles("item", props) }, "" !== searchTerm && core.jsx(SearchedItem, { item: item, searchIndex: searchIndex, searchTerm: searchTerm.trim(), getStyles: getStyles, isSelected: isItemSelected && item[item.length - 1] === selectedItem.leaf }), "" === searchTerm && core.jsx(BasicItem, { label: item[currentDepth], getStyles: getStyles, isSelected: isItemSelected && item[currentDepth] === selectedItem.leaf }), hasChild && core.jsx("span", { css: getStyles("forwardIcon", props) }, core.jsx(ForwardIcon, null))); }, BackIconRenderer = function() { return core.jsx(React__default.Fragment, null, "⬅️"); }, Header = function(props) { var headerRef = props.headerRef, _props$parents = props.parents, parents = void 0 === _props$parents ? [] : _props$parents, onClick = props.onClick, _props$title = props.title, title = void 0 === _props$title ? "" : _props$title, getStyles = props.getStyles, _props$backIconRender = props.backIconRenderer, BackIcon = void 0 === _props$backIconRender ? BackIconRenderer : _props$backIconRender; return core.jsx("div", { ref: headerRef, css: getStyles("header", props) }, parents.length > 0 && core.jsx(React__default.Fragment, null, core.jsx("span", { css: getStyles("headerBackIcon", props), onClick: onClick }, core.jsx(BackIcon, null)), parents[parents.length - 1]), 0 === parents.length && core.jsx(React__default.Fragment, null, title)); }, InputIconRenderer = function() { return core.jsx(React__default.Fragment, null, "🔍"); }, ClearIconRenderer = function() { return core.jsx(React__default.Fragment, null, "✘️"); }, Input = function(props) { var inputRef = props.inputRef, searchTerm = props.searchTerm, onInputChange = props.onInputChange, getStyles = props.getStyles, _props$inputIconRende = props.inputIconRenderer, InputIcon = void 0 === _props$inputIconRende ? InputIconRenderer : _props$inputIconRende, _props$clearIconRende = props.clearIconRenderer, ClearIcon = void 0 === _props$clearIconRende ? ClearIconRenderer : _props$clearIconRende; return core.jsx("div", { ref: inputRef, css: getStyles("inputWrapper", props) }, core.jsx("span", { css: getStyles("searchInput", props) }, core.jsx(InputIcon, null)), core.jsx("input", { css: getStyles("input", props), value: searchTerm, onChange: onInputChange }), "" !== searchTerm && core.jsx("button", { css: getStyles("clearInput", props), onClick: onInputChange }, core.jsx(ClearIcon, null))); }, NoResultsIconRenderer = function() { return core.jsx(React__default.Fragment, null, "⚠️"); }, NoResults = function(props) { var text = props.text, getStyles = props.getStyles, _props$noResultsIconR = props.noResultsIconRenderer, NoResultsIcon = void 0 === _props$noResultsIconR ? NoResultsIconRenderer : _props$noResultsIconR; return core.jsx("div", { css: getStyles("noResults", props) }, core.jsx("div", { css: getStyles("noResultsIcon", props) }, core.jsx(NoResultsIcon, null)), core.jsx("div", { css: getStyles("noResultsText", props) }, text)); }, containsAllParents = function(leaf, parents) { if (0 === parents.length) return !0; var containsAll = !0; return parents.forEach((function(parent, index) { parent !== leaf[index] && (containsAll = !1); })), containsAll; }, filterBySearchTerm = function(leaf, searchTerm) { return "" === searchTerm || leaf[leaf.length - 1].toLowerCase().includes(searchTerm.toLowerCase()); }, buildLeafForItemRenderer = function(leaf, currentDepth, searchTerm) { return { item: leaf, hasChild: leaf.length - 1 > currentDepth && "" === searchTerm, currentDepth: currentDepth, key: "" === searchTerm ? leaf[currentDepth] : leaf.slice().toString() }; }, removeDuplicateLeafs = function(leaves) { return leaves.filter((function(item, index) { return leaves.findIndex((function(leaf) { return leaf.key === item.key; })) === index; })); }; function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } function _iterableToArrayLimit(arr, i) { if (Symbol.iterator in Object(arr) || "[object Arguments]" === Object.prototype.toString.call(arr)) { var _arr = [], _n = !0, _d = !1, _e = void 0; try { for (var _s, _i = arr[Symbol.iterator](); !(_n = (_s = _i.next()).done) && (_arr.push(_s.value), !i || _arr.length !== i); _n = !0) ; } catch (err) { _d = !0, _e = err; } finally { try { _n || null == _i.return || _i.return(); } finally { if (_d) throw _e; } } return _arr; } } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } var useLeavesManager = function(_ref) { var structure = _ref.structure, parents = _ref.parents, currentDepth = _ref.currentDepth, _useState2 = _slicedToArray(React.useState(""), 2), searchTerm = _useState2[0], setSearchTerm = _useState2[1], _useState4 = _slicedToArray(React.useState([]), 2), leaves = _useState4[0], setLeaves = _useState4[1]; return React.useEffect((function() { var leaves = structure.filter((function(leaf) { return filterBySearchTerm(leaf, searchTerm) && containsAllParents(leaf, parents); })).map((function(leaf) { return buildLeafForItemRenderer(leaf, currentDepth, searchTerm); })); setLeaves(removeDuplicateLeafs(leaves)); }), [ searchTerm, parents, currentDepth ]), { searchTerm: searchTerm, setSearchTerm: setSearchTerm, onInputChange: function(event) { return setSearchTerm(event.target.value || ""); }, leaves: leaves }; }; function _slicedToArray$1(arr, i) { return _arrayWithHoles$1(arr) || _iterableToArrayLimit$1(arr, i) || _nonIterableRest$1(); } function _nonIterableRest$1() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } function _iterableToArrayLimit$1(arr, i) { if (Symbol.iterator in Object(arr) || "[object Arguments]" === Object.prototype.toString.call(arr)) { var _arr = [], _n = !0, _d = !1, _e = void 0; try { for (var _s, _i = arr[Symbol.iterator](); !(_n = (_s = _i.next()).done) && (_arr.push(_s.value), !i || _arr.length !== i); _n = !0) ; } catch (err) { _d = !0, _e = err; } finally { try { _n || null == _i.return || _i.return(); } finally { if (_d) throw _e; } } return _arr; } } function _arrayWithHoles$1(arr) { if (Array.isArray(arr)) return arr; } var useItemCallbacks = function(_ref) { var onSelect = _ref.onSelect, _useState2 = _slicedToArray$1(React.useState(0), 2), currentDepth = _useState2[0], setCurrentDepth = _useState2[1], _useState4 = _slicedToArray$1(React.useState([]), 2), parents = _useState4[0], setParents = _useState4[1]; return { onClick: function(label, item, hasChild) { hasChild ? (setParents(parents.concat(label)), setCurrentDepth(currentDepth + 1)) : onSelect(item); }, onBackClick: function() { setParents(parents.filter((function(parent, index) { return index < parents.length - 1; }))), setCurrentDepth(currentDepth - 1); }, currentDepth: currentDepth, setCurrentDepth: setCurrentDepth, parents: parents, setParents: setParents }; }, TreeContainerRenderer = function(props) { var containerRef = props.containerRef, getStyles = props.getStyles, children = props.children; return core.jsx("div", { ref: containerRef, css: getStyles("container", props) }, children); }, ItemsRenderer = function(props) { var getStyles = props.getStyles, children = props.children; return core.jsx("div", { css: getStyles("items", props) }, children); }, useComponentsRefs = function() { var _containerRef$current, _headerRef$current, _inputRef$current, containerRef = React.useRef(), headerRef = React.useRef(), inputRef = React.useRef(); return { containerRef: containerRef, headerRef: headerRef, inputRef: inputRef, itemsHeight: ((null == containerRef ? void 0 : null === (_containerRef$current = containerRef.current) || void 0 === _containerRef$current ? void 0 : _containerRef$current.clientHeight) || 0) - ((null == headerRef ? void 0 : null === (_headerRef$current = headerRef.current) || void 0 === _headerRef$current ? void 0 : _headerRef$current.clientHeight) || 0) - ((null == inputRef ? void 0 : null === (_inputRef$current = inputRef.current) || void 0 === _inputRef$current ? void 0 : _inputRef$current.clientHeight) || 0) }; }, DEFAULT_WIDTH = 230, DEFAULT_HEIGHT = 300, PIXEL_SUFFIX = "px", Tree = function(props) { var _props$structure = props.structure, structure = void 0 === _props$structure ? [] : _props$structure, title = props.title, onSelect = props.onSelect, width = props.width, height = props.height, styles = props.styles, _props$noResultsText = (props.className, props.noResultsText), noResultsText = void 0 === _props$noResultsText ? "No matching results" : _props$noResultsText, _props$headerRenderer = props.headerRenderer, Header$1 = void 0 === _props$headerRenderer ? Header : _props$headerRenderer, backIconRenderer = props.backIconRenderer, _props$inputRenderer = props.inputRenderer, Input$1 = void 0 === _props$inputRenderer ? Input : _props$inputRenderer, inputIconRenderer = props.inputIconRenderer, clearIconRenderer = props.clearIconRenderer, _props$noResultsRende = props.noResultsRenderer, NoResults$1 = void 0 === _props$noResultsRende ? NoResults : _props$noResultsRende, noResultsIconRenderer = props.noResultsIconRenderer, _props$itemRenderer = props.itemRenderer, Item = void 0 === _props$itemRenderer ? ItemRenderer : _props$itemRenderer, _props$itemsRenderer = props.itemsRenderer, Items = void 0 === _props$itemsRenderer ? ItemsRenderer : _props$itemsRenderer, forwardIconRenderer = props.forwardIconRenderer, _props$treeContainerR = props.treeContainerRenderer, TreeContainer = void 0 === _props$treeContainerR ? TreeContainerRenderer : _props$treeContainerR, customItemsRenderer = props.customItemsRenderer, selectedItem = props.selectedItem, getStyles = function(key) { var props = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}, base = defaultStyles[key](props); base.boxSizing = "border-box"; var custom = props.styles && props.styles[key]; return custom ? custom(base, props) : base; }, _useContainerHeight = useComponentsRefs(), containerRef = _useContainerHeight.containerRef, headerRef = _useContainerHeight.headerRef, inputRef = _useContainerHeight.inputRef, itemsHeight = _useContainerHeight.itemsHeight, _useItemCallbacks = useItemCallbacks({ onSelect: onSelect }), onClick = _useItemCallbacks.onClick, onBackClick = _useItemCallbacks.onBackClick, currentDepth = _useItemCallbacks.currentDepth, parents = _useItemCallbacks.parents, _useLeavesManager = useLeavesManager({ structure: structure, parents: parents, currentDepth: currentDepth }), searchTerm = _useLeavesManager.searchTerm, onInputChange = _useLeavesManager.onInputChange, leaves = _useLeavesManager.leaves; return core.jsx(TreeContainer, { containerRef: containerRef, getStyles: getStyles, styles: styles, width: (width || DEFAULT_WIDTH) + PIXEL_SUFFIX, height: (height || DEFAULT_HEIGHT) + PIXEL_SUFFIX }, core.jsx(Header$1, { headerRef: headerRef, parents: parents, title: title, onClick: onBackClick, getStyles: getStyles, styles: styles, backIconRenderer: backIconRenderer }, title), core.jsx(Input$1, { inputRef: inputRef, getStyles: getStyles, styles: styles, searchTerm: searchTerm, onInputChange: onInputChange, inputIconRenderer: inputIconRenderer, clearIconRenderer: clearIconRenderer }), customItemsRenderer ? React__default.cloneElement(customItemsRenderer, { leaves: leaves, searchTerm: searchTerm, onClick: onClick, selectedItem: selectedItem, forwardIconRenderer: forwardIconRenderer }) : core.jsx(Items, { styles: styles, getStyles: getStyles, height: itemsHeight }, leaves && leaves.length > 0 && leaves.map((function(item) { return core.jsx(Item, { getStyles: getStyles, searchTerm: searchTerm, item: item, onClick: onClick, forwardIconRenderer: forwardIconRenderer, selectedItem: selectedItem }); }))), leaves && 0 === leaves.length && core.jsx(NoResults$1, { height: itemsHeight, text: noResultsText, getStyles: getStyles, styles: styles, noResultsIconRenderer: noResultsIconRenderer })); }; exports.default = Tree;