@material-ui/lab
Version:
Material-UI Lab - Incubator for Material-UI React components.
543 lines (455 loc) • 15.4 kB
JavaScript
"use strict";
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.styles = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
var React = _interopRequireWildcard(require("react"));
var _clsx = _interopRequireDefault(require("clsx"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _Typography = _interopRequireDefault(require("@material-ui/core/Typography"));
var _Collapse = _interopRequireDefault(require("@material-ui/core/Collapse"));
var _styles = require("@material-ui/core/styles");
var _utils = require("@material-ui/core/utils");
var _TreeViewContext = _interopRequireDefault(require("../TreeView/TreeViewContext"));
/* eslint-disable jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */
var styles = function styles(theme) {
return {
/* Styles applied to the root element. */
root: {
listStyle: 'none',
margin: 0,
padding: 0,
outline: 0,
WebkitTapHighlightColor: 'transparent',
'&:focus > $content $label': {
backgroundColor: theme.palette.action.hover
},
'&$selected > $content $label': {
backgroundColor: (0, _styles.alpha)(theme.palette.primary.main, theme.palette.action.selectedOpacity)
},
'&$selected > $content $label:hover, &$selected:focus > $content $label': {
backgroundColor: (0, _styles.alpha)(theme.palette.primary.main, theme.palette.action.selectedOpacity + theme.palette.action.hoverOpacity),
// Reset on touch devices, it doesn't add specificity
'@media (hover: none)': {
backgroundColor: 'transparent'
}
}
},
/* Pseudo-class applied to the root element when expanded. */
expanded: {},
/* Pseudo-class applied to the root element when selected. */
selected: {},
/* Styles applied to the `role="group"` element. */
group: {
margin: 0,
padding: 0,
marginLeft: 17
},
/* Styles applied to the tree node content. */
content: {
width: '100%',
display: 'flex',
alignItems: 'center',
cursor: 'pointer'
},
/* Styles applied to the tree node icon and collapse/expand icon. */
iconContainer: {
marginRight: 4,
width: 15,
display: 'flex',
flexShrink: 0,
justifyContent: 'center',
'& svg': {
fontSize: 18
}
},
/* Styles applied to the label element. */
label: {
width: '100%',
paddingLeft: 4,
position: 'relative',
'&:hover': {
backgroundColor: theme.palette.action.hover,
// Reset on touch devices, it doesn't add specificity
'@media (hover: none)': {
backgroundColor: 'transparent'
}
}
}
};
};
exports.styles = styles;
var isPrintableCharacter = function isPrintableCharacter(str) {
return str && str.length === 1 && str.match(/\S/);
};
var TreeItem = /*#__PURE__*/React.forwardRef(function TreeItem(props, ref) {
var children = props.children,
classes = props.classes,
className = props.className,
collapseIcon = props.collapseIcon,
endIcon = props.endIcon,
expandIcon = props.expandIcon,
iconProp = props.icon,
label = props.label,
nodeId = props.nodeId,
onClick = props.onClick,
onLabelClick = props.onLabelClick,
onIconClick = props.onIconClick,
onFocus = props.onFocus,
onKeyDown = props.onKeyDown,
onMouseDown = props.onMouseDown,
_props$TransitionComp = props.TransitionComponent,
TransitionComponent = _props$TransitionComp === void 0 ? _Collapse.default : _props$TransitionComp,
TransitionProps = props.TransitionProps,
other = (0, _objectWithoutProperties2.default)(props, ["children", "classes", "className", "collapseIcon", "endIcon", "expandIcon", "icon", "label", "nodeId", "onClick", "onLabelClick", "onIconClick", "onFocus", "onKeyDown", "onMouseDown", "TransitionComponent", "TransitionProps"]);
var _React$useContext = React.useContext(_TreeViewContext.default),
contextIcons = _React$useContext.icons,
focus = _React$useContext.focus,
focusFirstNode = _React$useContext.focusFirstNode,
focusLastNode = _React$useContext.focusLastNode,
focusNextNode = _React$useContext.focusNextNode,
focusPreviousNode = _React$useContext.focusPreviousNode,
focusByFirstCharacter = _React$useContext.focusByFirstCharacter,
selectNode = _React$useContext.selectNode,
selectRange = _React$useContext.selectRange,
selectNextNode = _React$useContext.selectNextNode,
selectPreviousNode = _React$useContext.selectPreviousNode,
rangeSelectToFirst = _React$useContext.rangeSelectToFirst,
rangeSelectToLast = _React$useContext.rangeSelectToLast,
selectAllNodes = _React$useContext.selectAllNodes,
expandAllSiblings = _React$useContext.expandAllSiblings,
toggleExpansion = _React$useContext.toggleExpansion,
isExpanded = _React$useContext.isExpanded,
isFocused = _React$useContext.isFocused,
isSelected = _React$useContext.isSelected,
isTabbable = _React$useContext.isTabbable,
multiSelect = _React$useContext.multiSelect,
getParent = _React$useContext.getParent,
mapFirstChar = _React$useContext.mapFirstChar,
addNodeToNodeMap = _React$useContext.addNodeToNodeMap,
removeNodeFromNodeMap = _React$useContext.removeNodeFromNodeMap;
var nodeRef = React.useRef(null);
var contentRef = React.useRef(null);
var handleRef = (0, _utils.useForkRef)(nodeRef, ref);
var icon = iconProp;
var expandable = Boolean(Array.isArray(children) ? children.length : children);
var expanded = isExpanded ? isExpanded(nodeId) : false;
var focused = isFocused ? isFocused(nodeId) : false;
var tabbable = isTabbable ? isTabbable(nodeId) : false;
var selected = isSelected ? isSelected(nodeId) : false;
var icons = contextIcons || {};
var theme = (0, _styles.useTheme)();
if (!icon) {
if (expandable) {
if (!expanded) {
icon = expandIcon || icons.defaultExpandIcon;
} else {
icon = collapseIcon || icons.defaultCollapseIcon;
}
if (!icon) {
icon = icons.defaultParentIcon;
}
} else {
icon = endIcon || icons.defaultEndIcon;
}
}
var handleClick = function handleClick(event) {
if (!focused) {
focus(nodeId);
}
var multiple = multiSelect && (event.shiftKey || event.ctrlKey || event.metaKey); // If already expanded and trying to toggle selection don't close
if (expandable && !event.defaultPrevented && !(multiple && isExpanded(nodeId))) {
toggleExpansion(event, nodeId);
}
if (multiple) {
if (event.shiftKey) {
selectRange(event, {
end: nodeId
});
} else {
selectNode(event, nodeId, true);
}
} else {
selectNode(event, nodeId);
}
if (onClick) {
onClick(event);
}
};
var handleMouseDown = function handleMouseDown(event) {
if (event.shiftKey || event.ctrlKey || event.metaKey) {
event.preventDefault();
}
if (onMouseDown) {
onMouseDown(event);
}
};
var handleNextArrow = function handleNextArrow(event) {
if (expandable) {
if (expanded) {
focusNextNode(nodeId);
} else {
toggleExpansion(event);
}
}
return true;
};
var handlePreviousArrow = function handlePreviousArrow(event) {
if (expanded) {
toggleExpansion(event, nodeId);
return true;
}
var parent = getParent(nodeId);
if (parent) {
focus(parent);
return true;
}
return false;
};
var handleKeyDown = function handleKeyDown(event) {
var flag = false;
var key = event.key;
if (event.altKey || event.currentTarget !== event.target) {
return;
}
var ctrlPressed = event.ctrlKey || event.metaKey;
switch (key) {
case ' ':
if (nodeRef.current === event.currentTarget) {
if (multiSelect && event.shiftKey) {
flag = selectRange(event, {
end: nodeId
});
} else if (multiSelect) {
flag = selectNode(event, nodeId, true);
} else {
flag = selectNode(event, nodeId);
}
}
event.stopPropagation();
break;
case 'Enter':
if (nodeRef.current === event.currentTarget && expandable) {
toggleExpansion(event);
flag = true;
}
event.stopPropagation();
break;
case 'ArrowDown':
if (multiSelect && event.shiftKey) {
selectNextNode(event, nodeId);
}
focusNextNode(nodeId);
flag = true;
break;
case 'ArrowUp':
if (multiSelect && event.shiftKey) {
selectPreviousNode(event, nodeId);
}
focusPreviousNode(nodeId);
flag = true;
break;
case 'ArrowRight':
if (theme.direction === 'rtl') {
flag = handlePreviousArrow(event);
} else {
flag = handleNextArrow(event);
}
break;
case 'ArrowLeft':
if (theme.direction === 'rtl') {
flag = handleNextArrow(event);
} else {
flag = handlePreviousArrow(event);
}
break;
case 'Home':
if (multiSelect && ctrlPressed && event.shiftKey) {
rangeSelectToFirst(event, nodeId);
}
focusFirstNode();
flag = true;
break;
case 'End':
if (multiSelect && ctrlPressed && event.shiftKey) {
rangeSelectToLast(event, nodeId);
}
focusLastNode();
flag = true;
break;
default:
if (key === '*') {
expandAllSiblings(event, nodeId);
flag = true;
} else if (multiSelect && ctrlPressed && key.toLowerCase() === 'a') {
flag = selectAllNodes(event);
} else if (!ctrlPressed && !event.shiftKey && isPrintableCharacter(key)) {
focusByFirstCharacter(nodeId, key);
flag = true;
}
}
if (flag) {
event.preventDefault();
event.stopPropagation();
}
if (onKeyDown) {
onKeyDown(event);
}
};
var handleFocus = function handleFocus(event) {
if (!focused && event.currentTarget === event.target) {
focus(nodeId);
}
if (onFocus) {
onFocus(event);
}
};
React.useEffect(function () {
if (addNodeToNodeMap) {
var childIds = [];
React.Children.forEach(children, function (child) {
if ( /*#__PURE__*/React.isValidElement(child) && child.props.nodeId) {
childIds.push(child.props.nodeId);
}
});
addNodeToNodeMap(nodeId, childIds);
}
}, [children, nodeId, addNodeToNodeMap]);
React.useEffect(function () {
if (removeNodeFromNodeMap) {
return function () {
removeNodeFromNodeMap(nodeId);
};
}
return undefined;
}, [nodeId, removeNodeFromNodeMap]);
React.useEffect(function () {
if (mapFirstChar && label) {
mapFirstChar(nodeId, contentRef.current.textContent.substring(0, 1).toLowerCase());
}
}, [mapFirstChar, nodeId, label]);
React.useEffect(function () {
if (focused) {
nodeRef.current.focus();
}
}, [focused]);
var ariaSelected;
if (multiSelect) {
ariaSelected = selected;
} else if (selected) {
// single-selection trees unset aria-selected
ariaSelected = true;
}
return /*#__PURE__*/React.createElement("li", (0, _extends2.default)({
className: (0, _clsx.default)(classes.root, className, expanded && classes.expanded, selected && classes.selected),
role: "treeitem",
onKeyDown: handleKeyDown,
onFocus: handleFocus,
"aria-expanded": expandable ? expanded : null,
"aria-selected": ariaSelected,
ref: handleRef,
tabIndex: tabbable ? 0 : -1
}, other), /*#__PURE__*/React.createElement("div", {
className: classes.content,
onClick: handleClick,
onMouseDown: handleMouseDown,
ref: contentRef
}, /*#__PURE__*/React.createElement("div", {
onClick: onIconClick,
className: classes.iconContainer
}, icon), /*#__PURE__*/React.createElement(_Typography.default, {
onClick: onLabelClick,
component: "div",
className: classes.label
}, label)), children && /*#__PURE__*/React.createElement(TransitionComponent, (0, _extends2.default)({
unmountOnExit: true,
className: classes.group,
in: expanded,
component: "ul",
role: "group"
}, TransitionProps), children));
});
process.env.NODE_ENV !== "production" ? TreeItem.propTypes = {
// ----------------------------- Warning --------------------------------
// | These PropTypes are generated from the TypeScript type definitions |
// | To update them edit the d.ts file and run "yarn proptypes" |
// ----------------------------------------------------------------------
/**
* The content of the component.
*/
children: _propTypes.default.node,
/**
* Override or extend the styles applied to the component.
* See [CSS API](#css) below for more details.
*/
classes: _propTypes.default.object,
/**
* @ignore
*/
className: _propTypes.default.string,
/**
* The icon used to collapse the node.
*/
collapseIcon: _propTypes.default.node,
/**
* The icon displayed next to a end node.
*/
endIcon: _propTypes.default.node,
/**
* The icon used to expand the node.
*/
expandIcon: _propTypes.default.node,
/**
* The icon to display next to the tree node's label.
*/
icon: _propTypes.default.node,
/**
* The tree node label.
*/
label: _propTypes.default.node,
/**
* The id of the node.
*/
nodeId: _propTypes.default.string.isRequired,
/**
* @ignore
*/
onClick: _propTypes.default.func,
/**
* @ignore
*/
onFocus: _propTypes.default.func,
/**
* `onClick` handler for the icon container. Call `event.preventDefault()` to prevent `onNodeToggle` from being called.
*/
onIconClick: _propTypes.default.func,
/**
* @ignore
*/
onKeyDown: _propTypes.default.func,
/**
* `onClick` handler for the label container. Call `event.preventDefault()` to prevent `onNodeToggle` from being called.
*/
onLabelClick: _propTypes.default.func,
/**
* @ignore
*/
onMouseDown: _propTypes.default.func,
/**
* The component used for the transition.
* [Follow this guide](/components/transitions/#transitioncomponent-prop) to learn more about the requirements for this component.
*/
TransitionComponent: _propTypes.default.elementType,
/**
* Props applied to the [`Transition`](http://reactcommunity.org/react-transition-group/transition#Transition-props) element.
*/
TransitionProps: _propTypes.default.object
} : void 0;
var _default = (0, _styles.withStyles)(styles, {
name: 'MuiTreeItem'
})(TreeItem);
exports.default = _default;