UNPKG

@sgnl-pro/react-tree

Version:

A tree view component for React

568 lines (485 loc) 18.3 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var React = require('react'); var React__default = _interopDefault(React); function toVal(mix) { var k, y, str = ''; if (typeof mix === 'string' || typeof mix === 'number') { str += mix; } else if (typeof mix === 'object') { if (Array.isArray(mix)) { for (k = 0; k < mix.length; k++) { if (mix[k]) { if (y = toVal(mix[k])) { str && (str += ' '); str += y; } } } } else { for (k in mix) { if (mix !== null && mix[k]) { str && (str += ' '); str += k; } } } } return str; } function cn () { var i = 0, tmp, x, str = ''; var classesCount = arguments.length; while (i < classesCount) { var _i; if (tmp = (_i = i++, _i < 0 || arguments.length <= _i ? undefined : arguments[_i])) { if (x = toVal(tmp)) { str && (str += ' '); str += x; } } } return str; } (function (SelectionType) { SelectionType["Child"] = "child"; SelectionType["Parent"] = "parent"; SelectionType["All"] = "all"; SelectionType["None"] = "none"; })(exports.SelectionType || (exports.SelectionType = {})); var noop = function noop() {}; var isSelectableItem = function isSelectableItem(selectionType, isParent) { return selectionType === exports.SelectionType.All || selectionType === exports.SelectionType.Parent && isParent === true || selectionType === exports.SelectionType.Child && isParent === false; }; var TreeEventEmitter = /*#__PURE__*/function () { function TreeEventEmitter(_events) { if (_events === void 0) { _events = Object.create(null); } this._events = _events; } var _proto = TreeEventEmitter.prototype; _proto.on = function on(event, listener) { var _this = this; if (!this._events[event]) { this._events[event] = []; } this._events[event].push(listener); return function () { var _this$_events$event; _this._events[event] = ((_this$_events$event = _this._events[event]) != null ? _this$_events$event : []).filter(function (l) { return l !== listener; }); }; }; _proto.emit = function emit(event) { var _this$_events$event2; for (var _len = arguments.length, listenerArgs = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { listenerArgs[_key - 1] = arguments[_key]; } ((_this$_events$event2 = this._events[event]) != null ? _this$_events$event2 : []).forEach(function (cb) { return cb.apply(void 0, listenerArgs); } // TODO ); }; return TreeEventEmitter; }(); function _extends() { _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; }; return _extends.apply(this, arguments); } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } var getInitialTreeState = function getInitialTreeState() { return { expandedIds: Object.create(null), selectedNodes: Object.create(null) }; }; var actions = { toggleExpanded: function toggleExpanded(node) { return { type: 'TOGGLE_EXPANDED', node: node }; }, toggleSelected: function toggleSelected(node, allowMultiple) { return { type: 'TOGGLE_SELECTED', node: node, allowMultiple: allowMultiple }; }, setSelected: function setSelected(nodes, fullUpdate) { return { type: 'SET_SELECTED', nodes: nodes, fullUpdate: fullUpdate }; } }; var treeReducer = function treeReducer(state, action) { switch (action.type) { case 'TOGGLE_EXPANDED': { var _extends2; var nodeId = action.node.id; var expanded = !state.expandedIds[nodeId]; return _extends({}, state, { expandedIds: _extends({}, state.expandedIds, (_extends2 = {}, _extends2[nodeId] = expanded, _extends2)) }); } case 'TOGGLE_SELECTED': { var node = action.node; var selected = state.selectedNodes[node.id] !== undefined; var selectedNodes; if (action.allowMultiple) { var _extends3; selectedNodes = _extends({}, state.selectedNodes, (_extends3 = {}, _extends3[node.id] = selected === true ? undefined : node, _extends3)); } else { var _selectedNodes; selectedNodes = (_selectedNodes = {}, _selectedNodes[node.id] = selected === true ? undefined : node, _selectedNodes); } return _extends({}, state, { selectedNodes: selectedNodes }); } case 'SET_SELECTED': { var nodes = action.nodes, fullUpdate = action.fullUpdate; var _selectedNodes2 = fullUpdate ? {} : _extends({}, state.selectedNodes); nodes.forEach(function (n) { _selectedNodes2[n.id] = n; }); return _extends({}, state, { selectedNodes: _selectedNodes2 }); } default: return state; } }; var treeStateContext = /*#__PURE__*/React.createContext( /*#__PURE__*/getInitialTreeState()); var treeActionsContext = /*#__PURE__*/React.createContext({ toggleExpanded: noop, toggleSelected: noop }); var TreeContextProvider = function TreeContextProvider(_ref) { var selectionType = _ref.selectionType, multiSelect = _ref.multiSelect, disabledIds = _ref.disabledIds, eventEmitter = _ref.eventEmitter, onExpand = _ref.onExpand, onSelect = _ref.onSelect, children = _ref.children; var _useReducer = React.useReducer(treeReducer, getInitialTreeState()), state = _useReducer[0], dispatch = _useReducer[1]; var treeActions = React.useMemo(function () { return { toggleExpanded: function toggleExpanded(item, isExpanded) { if (item.children === void 0) return; dispatch(actions.toggleExpanded(item)); if (!isExpanded && typeof onExpand === 'function') onExpand(item); }, toggleSelected: function toggleSelected(item) { if (selectionType === exports.SelectionType.None) return; if (!isSelectableItem(selectionType, item.children !== void 0)) return; if (disabledIds != null && disabledIds.includes(item.id)) return; dispatch(actions.toggleSelected(item, multiSelect)); } }; }, [dispatch, onExpand, selectionType, multiSelect, disabledIds]); React.useEffect(function () { if (typeof onSelect === 'function') { onSelect(Object.values(state.selectedNodes).filter(function (item) { return item !== undefined; })); } }, [state.selectedNodes, onSelect]); React.useEffect(function () { if (eventEmitter === undefined || eventEmitter instanceof TreeEventEmitter === false) { return; } var selectUnsubscribe = eventEmitter.on('select', function (items, type) { if (type === void 0) { type = 'merge'; } if (selectionType === exports.SelectionType.None) return; var validItems = []; for (var i = 0; i < items.length; i++) { if (multiSelect !== true && validItems.length > 0) break; if (!items[i].id || typeof items[i].label !== 'string') continue; if (!isSelectableItem(selectionType, items[i].children !== void 0)) continue; if (disabledIds != null && disabledIds.includes(items[i].id)) continue; validItems.push(items[i]); } dispatch(actions.setSelected(validItems, type === 'update')); }); return function () { selectUnsubscribe(); }; }, [eventEmitter, selectionType, disabledIds, multiSelect]); return React__default.createElement(treeActionsContext.Provider, { value: treeActions }, React__default.createElement(treeStateContext.Provider, { value: state }, children)); }; var useTreeActions = function useTreeActions() { return React.useContext(treeActionsContext); }; var useTreeState = function useTreeState() { return React.useContext(treeStateContext); }; function styleInject(css, ref) { if ( ref === void 0 ) ref = {}; var insertAt = ref.insertAt; if (!css || typeof document === 'undefined') { return; } var head = document.head || document.getElementsByTagName('head')[0]; var style = document.createElement('style'); style.type = 'text/css'; if (insertAt === 'top') { if (head.firstChild) { head.insertBefore(style, head.firstChild); } else { head.appendChild(style); } } else { head.appendChild(style); } if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } } var css_248z = ".S-Tree-container,.S-Tree-node{position:relative}.S-Tree-container{font-size:1rem}.S-Tree-container,.S-Tree-container *{box-sizing:border-box}.S-Tree-node{margin-left:0;cursor:pointer}.S-Tree-node[disabled]{cursor:not-allowed}.S-Tree-node>.S-Tree-node{margin-left:.5em}.S-Tree-node_selected>.S-Tree-node__content .S-Tree-node__label{font-weight:700}.S-Tree-node__content,.S-Tree-node__label{display:flex;flex-direction:row;justify-content:flex-start}.S-Tree-node__content{width:100%}.S-Tree-node__label{align-items:center;align-self:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.S-Tree-node__icon{flex-shrink:0;max-width:20%;overflow:hidden;margin-right:1em}.S-Tree-svg{display:inline-block;width:.5em;height:auto}.S-Tree-svg_expanded{transform:rotate(90deg)}"; styleInject(css_248z); var NodeContent = function NodeContent(_ref) { var className = _ref.className, children = _ref.children, attrs = _objectWithoutPropertiesLoose(_ref, ["className", "children"]); return React__default.createElement("div", Object.assign({ className: cn('S-Tree-node__content', className) }, attrs), children); }; var NodeLabel = function NodeLabel(_ref) { var className = _ref.className, children = _ref.children, attrs = _objectWithoutPropertiesLoose(_ref, ["className", "children"]); return React__default.createElement("div", Object.assign({ className: cn('S-Tree-node__label', className) }, attrs), children); }; var CarretRight = function CarretRight(props) { return React__default.createElement("svg", Object.assign({ width: "8", viewBox: "0 0 8 9", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, props), React__default.createElement("path", { d: "M1.087 8.77261C0.65721 8.77261 0.227417 8.45477 0.227417 7.84634L0.227417 1.68933C0.227417 0.826625 1.0297 0.581434 1.63141 0.917436L7.23782 3.99594C7.58166 4.17756 7.77267 4.45908 7.77267 4.76784C7.77267 5.07659 7.58166 5.35811 7.24737 5.53973L1.63141 8.61823C1.44994 8.71813 1.26847 8.77261 1.087 8.77261ZM1.19206 1.71658L1.18251 7.81001L6.73162 4.77692L1.19206 1.71658Z", fill: "currentColor" })); }; var NodeIcon = function NodeIcon(_ref) { var isParent = _ref.isParent, expanded = _ref.expanded, iconClassName = _ref.iconClassName, className = _ref.className, children = _ref.children, attrs = _objectWithoutPropertiesLoose(_ref, ["isParent", "expanded", "iconClassName", "className", "children"]); if (!children && !isParent) { return null; } return React__default.createElement("div", Object.assign({ className: cn('S-Tree-node__icon', className) }, attrs), children || React__default.createElement(CarretRight, { className: cn('S-Tree-svg', expanded && 'S-Tree-svg_expanded', iconClassName) })); }; var TreeNode = function TreeNode(_ref) { var item = _ref.item, selectionType = _ref.selectionType, selectOn = _ref.selectOn, className = _ref.className, activeClassName = _ref.activeClassName, contentClassName = _ref.contentClassName, iconBoxClassName = _ref.iconBoxClassName, iconClassName = _ref.iconClassName, labelClassName = _ref.labelClassName, renderCheckbox = _ref.renderCheckbox, renderData = _ref.renderData, renderIcon = _ref.renderIcon, loader = _ref.loader, children = _ref.children; var _useTreeActions = useTreeActions(), toggleExpanded = _useTreeActions.toggleExpanded, toggleSelected = _useTreeActions.toggleSelected; var _useTreeState = useTreeState(), expandedIds = _useTreeState.expandedIds, selectedNodes = _useTreeState.selectedNodes; var isParent = item.children !== void 0; var expanded = (expandedIds == null ? void 0 : expandedIds[item.id]) === true; var selected = (selectedNodes == null ? void 0 : selectedNodes[item.id]) !== undefined; var withCheckbox = selectOn === 'check'; var selectable = isSelectableItem(selectionType, isParent); var onNodeClick = function onNodeClick(e) { e.stopPropagation(); if (withCheckbox || selectable === false) { toggleExpanded(item, expanded); } else { toggleSelected(item); } }; var onIconClick = function onIconClick(e) { e.stopPropagation(); toggleExpanded(item, expanded); }; var renderChecker = function renderChecker() { if (withCheckbox && selectable === true) { var onCheck = function onCheck() { toggleSelected(item); }; if (typeof renderCheckbox === 'function') { return renderCheckbox(selected, onCheck); } else { return React__default.createElement("input", { type: "checkbox", checked: selected, onChange: onCheck, onClick: function onClick(e) { return e.stopPropagation(); } }); } } return null; }; var renderNode = function renderNode(n) { return React__default.createElement(TreeNode, { key: n.id, item: n, selectionType: selectionType, selectOn: selectOn, className: className, activeClassName: activeClassName, contentClassName: contentClassName, iconBoxClassName: iconBoxClassName, iconClassName: iconClassName, labelClassName: labelClassName, renderCheckbox: renderCheckbox, renderData: renderData, renderIcon: renderIcon, loader: loader }); }; return React__default.createElement("div", { className: cn('S-Tree-node', className, selected === true && ['S-Tree-node_selected', activeClassName]) }, React__default.createElement(NodeContent, { className: contentClassName, onClick: onNodeClick }, React__default.createElement(NodeIcon, { isParent: isParent, expanded: expanded, className: iconBoxClassName, iconClassName: iconClassName, onClick: onIconClick }, typeof renderIcon === 'function' && renderIcon(expanded, selected, isParent, item)), renderChecker(), React__default.createElement(NodeLabel, { className: labelClassName }, item.label), typeof renderData === 'function' && renderData(item, selected)), children, isParent && expanded && (Array.isArray(item.children) ? item.children.map(renderNode) : loader || React__default.createElement("div", { className: "S-Tree-info S-Tree-info_loading" }, "..."))); }; var Tree = function Tree(_ref) { var nodes = _ref.nodes, _ref$selectionType = _ref.selectionType, selectionType = _ref$selectionType === void 0 ? exports.SelectionType.None : _ref$selectionType, _ref$multipleSelectio = _ref.multipleSelection, multipleSelection = _ref$multipleSelectio === void 0 ? false : _ref$multipleSelectio, _ref$selectAction = _ref.selectAction, selectAction = _ref$selectAction === void 0 ? 'click' : _ref$selectAction, disabledIds = _ref.disabledIds, containerClassName = _ref.containerClassName, nodeClassName = _ref.nodeClassName, nodeActiveClassName = _ref.nodeActiveClassName, nodeContentClassName = _ref.nodeContentClassName, nodeIconBoxClassName = _ref.nodeIconBoxClassName, nodeIconClassName = _ref.nodeIconClassName, nodeLabelClassName = _ref.nodeLabelClassName, onNodeExpand = _ref.onNodeExpand, onSelect = _ref.onSelect, renderCustomCheckbox = _ref.renderCustomCheckbox, renderNodeData = _ref.renderNodeData, renderNodeIcon = _ref.renderNodeIcon, loader = _ref.loader, noData = _ref.noData, eventEmitter = _ref.eventEmitter, children = _ref.children; var renderNode = function renderNode(n) { return React__default.createElement(TreeNode, { key: n.id, item: n, selectionType: selectionType, selectOn: selectAction, className: nodeClassName, activeClassName: nodeActiveClassName, contentClassName: nodeContentClassName, iconBoxClassName: nodeIconBoxClassName, iconClassName: nodeIconClassName, labelClassName: nodeLabelClassName, renderCheckbox: renderCustomCheckbox, renderData: renderNodeData, renderIcon: renderNodeIcon, loader: loader }); }; return React__default.createElement(TreeContextProvider, { selectionType: selectionType, multiSelect: multipleSelection, disabledIds: disabledIds, eventEmitter: eventEmitter, onSelect: onSelect, onExpand: onNodeExpand }, React__default.createElement("div", { className: cn('S-Tree-container', containerClassName) }, children, !(nodes != null && nodes.length) ? !children && (noData || React__default.createElement("div", { className: "S-Tree-info S-Tree-info_noData" }, "No data")) : nodes.map(renderNode))); }; exports.NodeContent = NodeContent; exports.NodeIcon = NodeIcon; exports.NodeLabel = NodeLabel; exports.Tree = Tree; exports.TreeEventEmitter = TreeEventEmitter; exports.TreeNode = TreeNode; //# sourceMappingURL=react-tree.cjs.development.js.map