@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
JavaScript
;
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;