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.

608 lines (548 loc) 20.2 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var core = require('@emotion/core'); var React = require('react'); var React__default = _interopDefault(React); var containerCss = (function (_ref) { var width = _ref.width, height = _ref.height; return { width: width, height: 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" }; }); var wrapperCss = function wrapperCss() { return { height: "40px", display: "flex", alignItems: "center", padding: "0 6px" }; }; var backIconCss = function backIconCss() { return { marginRight: "3px", cursor: "pointer" }; }; var css = function css(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" } }; }; var forwardIconCss = function forwardIconCss() { return {}; }; var selectedItem = function selectedItem(props) { return { fontWeight: props.isSelected ? 600 : 400 }; }; var initialCss = function initialCss(props) { return { color: "#545769", fontWeight: props.isSelected ? 600 : 400 }; }; var highlightCss = function highlightCss() { return { fontWeight: 600, color: "#268DEC" }; }; var parentsCss = function parentsCss() { return { color: "#98A1B8", marginTop: "2px" }; }; var itemsCss = (function (_ref) { var height = _ref.height; return { maxHeight: height, overflowY: "auto" }; }); var css$1 = function css() { 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" } }; }; var searchIconCss = function searchIconCss() { return { position: "absolute", top: "8px", left: "8px" }; }; var clearIconCss = function clearIconCss() { return { position: "absolute", top: "8px", right: "8px", cursor: "pointer" }; }; var wrapperCss$1 = function wrapperCss() { return { position: "relative" }; }; var css$2 = function css(_ref) { var height = _ref.height; return { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", height: height }; }; var icon = function icon() { return { fontSize: "20px", marginBottom: "5px" }; }; var text = function text() { return { fontSize: "14px" }; }; var 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 }; /** @jsx jsx */ var SearchedItem = function SearchedItem(props) { var _props$item = props.item, item = _props$item === void 0 ? [""] : _props$item, _props$searchIndex = props.searchIndex, searchIndex = _props$searchIndex === void 0 ? 0 : _props$searchIndex, _props$searchTerm = props.searchTerm, searchTerm = _props$searchTerm === void 0 ? "" : _props$searchTerm, getStyles = props.getStyles; var leaf = item[item.length - 1]; var 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)); }; /** @jsx jsx */ var BasicItem = function BasicItem(props) { var _props$label = props.label, label = _props$label === void 0 ? "" : _props$label, getStyles = props.getStyles; return core.jsx("span", { css: getStyles("selectedItem", props) }, label); }; /** @jsx jsx */ var ForwardIconRenderer = function ForwardIconRenderer() { return core.jsx(React__default.Fragment, null, "\u27A1\uFE0F"); }; var ItemRenderer = function ItemRenderer(props) { var _selectedItem$item; var getStyles = props.getStyles, _props$searchTerm = props.searchTerm, searchTerm = _props$searchTerm === void 0 ? "" : _props$searchTerm, _props$item = props.item; _props$item = _props$item === void 0 ? { item: [""], hasChild: false, currentDepth: 0 } : _props$item; var item = _props$item.item, hasChild = _props$item.hasChild, currentDepth = _props$item.currentDepth, _onClick = props.onClick, _props$forwardIconRen = props.forwardIconRenderer, ForwardIcon = _props$forwardIconRen === void 0 ? ForwardIconRenderer : _props$forwardIconRen, selectedItem = props.selectedItem; var isItemSelected = item.toString() === (selectedItem === null || selectedItem === void 0 ? void 0 : (_selectedItem$item = selectedItem.item) === null || _selectedItem$item === void 0 ? void 0 : _selectedItem$item.toString()); var searchIndex = item[item.length - 1].toLowerCase().indexOf(searchTerm.trim().toLowerCase()); return core.jsx("div", { onClick: function onClick() { 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))); }; /** @jsx jsx */ var BackIconRenderer = function BackIconRenderer() { return core.jsx(React__default.Fragment, null, "\u2B05\uFE0F"); }; var Header = function Header(props) { var headerRef = props.headerRef, _props$parents = props.parents, parents = _props$parents === void 0 ? [] : _props$parents, onClick = props.onClick, _props$title = props.title, title = _props$title === void 0 ? "" : _props$title, getStyles = props.getStyles, _props$backIconRender = props.backIconRenderer, BackIcon = _props$backIconRender === void 0 ? 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]), parents.length === 0 && core.jsx(React__default.Fragment, null, title)); }; /** @jsx jsx */ var InputIconRenderer = function InputIconRenderer() { return core.jsx(React__default.Fragment, null, "\uD83D\uDD0D"); }; var ClearIconRenderer = function ClearIconRenderer() { return core.jsx(React__default.Fragment, null, "\u2718\uFE0F"); }; var Input = function Input(props) { var inputRef = props.inputRef, searchTerm = props.searchTerm, onInputChange = props.onInputChange, getStyles = props.getStyles, _props$inputIconRende = props.inputIconRenderer, InputIcon = _props$inputIconRende === void 0 ? InputIconRenderer : _props$inputIconRende, _props$clearIconRende = props.clearIconRenderer, ClearIcon = _props$clearIconRende === void 0 ? 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))); }; /** @jsx jsx */ var NoResultsIconRenderer = function NoResultsIconRenderer() { return core.jsx(React__default.Fragment, null, "\u26A0\uFE0F"); }; var NoResults = function NoResults(props) { var text = props.text, getStyles = props.getStyles, _props$noResultsIconR = props.noResultsIconRenderer, NoResultsIcon = _props$noResultsIconR === void 0 ? 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)); }; var containsAllParents = function containsAllParents(leaf, parents) { if (parents.length === 0) { return true; } var containsAll = true; parents.forEach(function (parent, index) { if (parent !== leaf[index]) { containsAll = false; } }); return containsAll; }; var filterBySearchTerm = function filterBySearchTerm(leaf, searchTerm) { return searchTerm === "" || leaf[leaf.length - 1].toLowerCase().includes(searchTerm.toLowerCase()); }; var buildLeafForItemRenderer = function buildLeafForItemRenderer(leaf, currentDepth, searchTerm) { return { item: leaf, hasChild: leaf.length - 1 > currentDepth && searchTerm === "", currentDepth: currentDepth, key: searchTerm === "" ? leaf[currentDepth] : leaf.slice().toString() }; }; var removeDuplicateLeafs = function removeDuplicateLeafs(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.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } var useLeavesManager = function useLeavesManager(_ref) { var structure = _ref.structure, parents = _ref.parents, currentDepth = _ref.currentDepth; var _useState = React.useState(""), _useState2 = _slicedToArray(_useState, 2), searchTerm = _useState2[0], setSearchTerm = _useState2[1]; var _useState3 = React.useState([]), _useState4 = _slicedToArray(_useState3, 2), leaves = _useState4[0], setLeaves = _useState4[1]; var onInputChange = function onInputChange(event) { return setSearchTerm(event.target.value || ""); }; 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]); return { searchTerm: searchTerm, setSearchTerm: setSearchTerm, onInputChange: onInputChange, 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.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _arrayWithHoles$1(arr) { if (Array.isArray(arr)) return arr; } var useItemCallbacks = function useItemCallbacks(_ref) { var onSelect = _ref.onSelect; var _useState = React.useState(0), _useState2 = _slicedToArray$1(_useState, 2), currentDepth = _useState2[0], setCurrentDepth = _useState2[1]; var _useState3 = React.useState([]), _useState4 = _slicedToArray$1(_useState3, 2), parents = _useState4[0], setParents = _useState4[1]; var onClick = function onClick(label, item, hasChild) { if (hasChild) { setParents(parents.concat(label)); setCurrentDepth(currentDepth + 1); } else { onSelect(item); } }; var onBackClick = function onBackClick() { setParents(parents.filter(function (parent, index) { return index < parents.length - 1; })); setCurrentDepth(currentDepth - 1); }; return { onClick: onClick, onBackClick: onBackClick, currentDepth: currentDepth, setCurrentDepth: setCurrentDepth, parents: parents, setParents: setParents }; }; /** @jsx jsx */ var TreeContainerRenderer = function TreeContainerRenderer(props) { var containerRef = props.containerRef, getStyles = props.getStyles, children = props.children; return core.jsx("div", { ref: containerRef, css: getStyles("container", props) }, children); }; /** @jsx jsx */ var ItemsRenderer = function ItemsRenderer(props) { var getStyles = props.getStyles, children = props.children; return core.jsx("div", { css: getStyles("items", props) }, children); }; var useComponentsRefs = function useComponentsRefs() { var _containerRef$current, _headerRef$current, _inputRef$current; var containerRef = React.useRef(); var headerRef = React.useRef(); var inputRef = React.useRef(); var itemsHeight = ((containerRef === null || containerRef === void 0 ? void 0 : (_containerRef$current = containerRef.current) === null || _containerRef$current === void 0 ? void 0 : _containerRef$current.clientHeight) || 0) - ((headerRef === null || headerRef === void 0 ? void 0 : (_headerRef$current = headerRef.current) === null || _headerRef$current === void 0 ? void 0 : _headerRef$current.clientHeight) || 0) - ((inputRef === null || inputRef === void 0 ? void 0 : (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.clientHeight) || 0); return { containerRef: containerRef, headerRef: headerRef, inputRef: inputRef, itemsHeight: itemsHeight }; }; /** @jsx jsx */ var DEFAULT_WIDTH = 230; var DEFAULT_HEIGHT = 300; var PIXEL_SUFFIX = "px"; var Tree = function Tree(props) { var _props$structure = props.structure, structure = _props$structure === void 0 ? [] : _props$structure, title = props.title, onSelect = props.onSelect, width = props.width, height = props.height, styles = props.styles, className = props.className, _props$noResultsText = props.noResultsText, noResultsText = _props$noResultsText === void 0 ? "No matching results" : _props$noResultsText, _props$headerRenderer = props.headerRenderer, Header$1 = _props$headerRenderer === void 0 ? Header : _props$headerRenderer, backIconRenderer = props.backIconRenderer, _props$inputRenderer = props.inputRenderer, Input$1 = _props$inputRenderer === void 0 ? Input : _props$inputRenderer, inputIconRenderer = props.inputIconRenderer, clearIconRenderer = props.clearIconRenderer, _props$noResultsRende = props.noResultsRenderer, NoResults$1 = _props$noResultsRende === void 0 ? NoResults : _props$noResultsRende, noResultsIconRenderer = props.noResultsIconRenderer, _props$itemRenderer = props.itemRenderer, Item = _props$itemRenderer === void 0 ? ItemRenderer : _props$itemRenderer, _props$itemsRenderer = props.itemsRenderer, Items = _props$itemsRenderer === void 0 ? ItemsRenderer : _props$itemsRenderer, forwardIconRenderer = props.forwardIconRenderer, _props$treeContainerR = props.treeContainerRenderer, TreeContainer = _props$treeContainerR === void 0 ? TreeContainerRenderer : _props$treeContainerR, customItemsRenderer = props.customItemsRenderer, selectedItem = props.selectedItem; var getStyles = function getStyles(key) { var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var base = defaultStyles[key](props); base.boxSizing = "border-box"; var custom = props.styles && props.styles[key]; return custom ? custom(base, props) : base; }; var _useContainerHeight = useComponentsRefs(), containerRef = _useContainerHeight.containerRef, headerRef = _useContainerHeight.headerRef, inputRef = _useContainerHeight.inputRef, itemsHeight = _useContainerHeight.itemsHeight; var _useItemCallbacks = useItemCallbacks({ onSelect: onSelect }), onClick = _useItemCallbacks.onClick, onBackClick = _useItemCallbacks.onBackClick, currentDepth = _useItemCallbacks.currentDepth, parents = _useItemCallbacks.parents; var _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 && leaves.length === 0 && core.jsx(NoResults$1, { height: itemsHeight, text: noResultsText, getStyles: getStyles, styles: styles, noResultsIconRenderer: noResultsIconRenderer })); }; exports.default = Tree;