UNPKG

@mskcc/carbon-react

Version:

Carbon react components for the MSKCC DSM

241 lines (233 loc) 8.35 kB
/** * MSKCC 2021, 2024 */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js'); var React = require('react'); var PropTypes = require('prop-types'); var cx = require('classnames'); var uniqueId = require('../../tools/uniqueId.js'); var usePrefix = require('../../internal/usePrefix.js'); var match = require('../../internal/keyboard/match.js'); var keys = require('../../internal/keyboard/keys.js'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes); var cx__default = /*#__PURE__*/_interopDefaultLegacy(cx); function TreeView(_ref) { let { active: prespecifiedActive, children, className, hideLabel = false, label, multiselect = false, onSelect, selected: preselected = [], size = 'sm', ...rest } = _ref; const { current: treeId } = React.useRef(rest.id || uniqueId["default"]()); const prefix = usePrefix.usePrefix(); const treeClasses = cx__default["default"](className, `${prefix}--tree`, { [`${prefix}--tree--${size}`]: size !== 'default' }); const treeRootRef = React.useRef(null); const treeWalker = React.useRef(treeRootRef?.current); const [selected, setSelected] = React.useState(preselected); const [active, setActive] = React.useState(prespecifiedActive); function resetNodeTabIndices() { Array.prototype.forEach.call(treeRootRef?.current?.querySelectorAll('[tabIndex="0"]') ?? [], item => { item.tabIndex = -1; }); } function handleTreeSelect(event) { let node = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; const { id: nodeId } = node; if (multiselect && (event.metaKey || event.ctrlKey)) { if (!selected.includes(nodeId)) { setSelected(selected.concat(nodeId)); } else { setSelected(selected.filter(selectedId => selectedId !== nodeId)); } onSelect?.(event, node); } else { setSelected([nodeId]); setActive(nodeId); onSelect?.(event, { activeNodeId: nodeId, ...node }); } } function handleFocusEvent(event) { if (event.type === 'blur') { const { relatedTarget: currentFocusedNode, target: prevFocusedNode } = event; if (treeRootRef?.current?.contains(currentFocusedNode)) { prevFocusedNode.tabIndex = -1; } } if (event.type === 'focus') { resetNodeTabIndices(); const { relatedTarget: prevFocusedNode, target: currentFocusedNode } = event; if (treeRootRef?.current?.contains(prevFocusedNode)) { prevFocusedNode.tabIndex = -1; } currentFocusedNode.tabIndex = 0; } } let focusTarget = false; const nodesWithProps = React__default["default"].Children.map(children, node => { const sharedNodeProps = { active, depth: 0, onNodeFocusEvent: handleFocusEvent, onTreeSelect: handleTreeSelect, selected, tabIndex: !node.props.disabled && -1 || null }; if (!focusTarget && !node.props.disabled) { sharedNodeProps.tabIndex = 0; focusTarget = true; } if ( /*#__PURE__*/React__default["default"].isValidElement(node)) { return /*#__PURE__*/React__default["default"].cloneElement(node, sharedNodeProps); } }); function handleKeyDown(event) { event.stopPropagation(); if (match.matches(event, [keys.ArrowUp, keys.ArrowDown, keys.Home, keys.End, { code: 'KeyA' }])) { event.preventDefault(); } treeWalker.current.currentNode = event.target; let nextFocusNode; if (match.match(event, keys.ArrowUp)) { nextFocusNode = treeWalker.current.previousNode(); } if (match.match(event, keys.ArrowDown)) { nextFocusNode = treeWalker.current.nextNode(); } if (match.matches(event, [keys.Home, keys.End, { code: 'KeyA' }])) { const nodeIds = []; if (match.matches(event, [keys.Home, keys.End])) { if (multiselect && event.shiftKey && event.ctrlKey && !treeWalker.current.currentNode.getAttribute('aria-disabled')) { nodeIds.push(treeWalker.current.currentNode?.id); } while (match.match(event, keys.Home) ? treeWalker.current.previousNode() : treeWalker.current.nextNode()) { nextFocusNode = treeWalker.current.currentNode; if (multiselect && event.shiftKey && event.ctrlKey && !nextFocusNode.getAttribute('aria-disabled')) { nodeIds.push(nextFocusNode?.id); } } } if (match.match(event, { code: 'KeyA' }) && event.ctrlKey) { treeWalker.current.currentNode = treeWalker.current.root; while (treeWalker.current.nextNode()) { if (!treeWalker.current.currentNode.getAttribute('aria-disabled')) { nodeIds.push(treeWalker.current.currentNode?.id); } } } setSelected(selected.concat(nodeIds)); } if (nextFocusNode && nextFocusNode !== event.target) { resetNodeTabIndices(); nextFocusNode.tabIndex = 0; nextFocusNode.focus(); } rest?.onKeyDown?.(event); } React.useEffect(() => { treeWalker.current = treeWalker.current ?? document.createTreeWalker(treeRootRef?.current, NodeFilter.SHOW_ELEMENT, { acceptNode: function (node) { if (node.classList.contains(`${prefix}--tree-node--disabled`)) { return NodeFilter.FILTER_REJECT; } if (node.matches(`li.${prefix}--tree-node`)) { return NodeFilter.FILTER_ACCEPT; } return NodeFilter.FILTER_SKIP; } }); }, [prefix]); const useActiveAndSelectedOnMount = () => React.useEffect(() => { if (preselected.length) { setSelected(preselected); } if (prespecifiedActive) { setActive(prespecifiedActive); } }, []); useActiveAndSelectedOnMount(); const labelId = `${treeId}__label`; const TreeLabel = () => !hideLabel && /*#__PURE__*/React__default["default"].createElement("label", { id: labelId, className: `${prefix}--label` }, label); return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement(TreeLabel, null), /*#__PURE__*/React__default["default"].createElement("ul", _rollupPluginBabelHelpers["extends"]({}, rest, { "aria-label": hideLabel ? label : null, "aria-labelledby": !hideLabel ? labelId : null, "aria-multiselectable": multiselect || null, className: treeClasses, onKeyDown: handleKeyDown, ref: treeRootRef, role: "tree" }), nodesWithProps)); } TreeView.propTypes = { /** * Mark the active node in the tree, represented by its value */ active: PropTypes__default["default"].oneOfType([PropTypes__default["default"].string, PropTypes__default["default"].number]), /** * Specify the children of the TreeView */ children: PropTypes__default["default"].node, /** * Specify an optional className to be applied to the TreeView */ className: PropTypes__default["default"].string, /** * Specify whether or not the label should be hidden */ hideLabel: PropTypes__default["default"].bool, /** * Provide the label text that will be read by a screen reader */ label: PropTypes__default["default"].string.isRequired, /** * **[Experimental]** Specify the selection mode of the tree. * If `multiselect` is `false` then only one node can be selected at a time */ multiselect: PropTypes__default["default"].bool, /** * Callback function that is called when any node is selected */ onSelect: PropTypes__default["default"].func, /** * Array representing all selected node IDs in the tree */ selected: PropTypes__default["default"].arrayOf(PropTypes__default["default"].oneOfType([PropTypes__default["default"].string, PropTypes__default["default"].number])), /** * Specify the size of the tree from a list of available sizes. */ size: PropTypes__default["default"].oneOf(['xs', 'sm']) }; exports["default"] = TreeView;