UNPKG

@nteract/dropdown-menu

Version:

Dropdown Menu for nteract apps

192 lines (186 loc) 6.89 kB
"use strict"; /* eslint jsx-a11y/no-static-element-interactions: 0 */ /* eslint jsx-a11y/click-events-have-key-events: 0 */ var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); // react-hot-loader uses proxies to the original elements so we need to use // their comparison function in case a consumer of these components is // using hot module reloading const React = __importStar(require("react")); const react_hot_loader_1 = require("react-hot-loader"); const styled_components_1 = __importDefault(require("styled-components")); const DropdownDiv = styled_components_1.default.div ` z-index: 10000; display: inline-block; `; DropdownDiv.displayName = "DropdownDiv"; class DropdownMenu extends React.PureComponent { constructor(props) { super(props); this.listRef = React.createRef(); this.handleKeyUp = (ev) => { if (!this.state.menuHidden) { if (ev.key === "Escape") { this.setState({ menuHidden: true }); } else if (ev.key === "ArrowDown") { this.moveListChildFocus(1); } else if (ev.key === "ArrowUp") { this.moveListChildFocus(-1); } } }; this.state = { menuHidden: true }; } componentDidUpdate(prevProps, prevState) { if (prevState.menuHidden !== this.state.menuHidden && this.props.onDisplayChanged) { this.props.onDisplayChanged(!this.state.menuHidden); } } /*** * Looks at the children of the ul, finds the focused child, and moves the focus the specified amount */ moveListChildFocus(amount) { let ulEl = this.listRef.current; if (ulEl) { let activeIndex; for (let i = 0; i < ulEl.children.length; i++) { let li = ulEl.children[i]; if (li == document.activeElement) { activeIndex = i; break; } } let nextActiveIndex = activeIndex === undefined ? 0 : (activeIndex + amount) % ulEl.children.length; // Because JS mod can produce negative numbers, we need a negative check also nextActiveIndex = nextActiveIndex < 0 ? ulEl.children.length + nextActiveIndex : nextActiveIndex; let nextItem = ulEl.children[nextActiveIndex]; nextItem.focus(); } } render() { return (React.createElement(DropdownDiv, { onKeyUp: this.handleKeyUp }, React.Children.map(this.props.children, child => { const childElement = child; if (react_hot_loader_1.areComponentsEqual(childElement.type, DropdownTrigger)) { return React.cloneElement(childElement, { onClick: () => { this.setState({ menuHidden: !this.state.menuHidden }); } }); } else if (react_hot_loader_1.areComponentsEqual(childElement.type, DropdownContent)) { if (this.state.menuHidden) { return null; } else { // DropdownContent child will pass down an onItemClick so that // the menu will collapse return React.cloneElement(childElement, { onItemClick: () => { this.setState({ menuHidden: true }); }, ulRef: this.listRef }); } } else { // fallback return child; } }))); } } exports.DropdownMenu = DropdownMenu; const DropdownTriggerDiv = styled_components_1.default.div ` user-select: none; margin: 0px; padding: 0px; `; DropdownTriggerDiv.displayName = "DropdownTriggerDiv"; // tslint:disable max-classes-per-file class DropdownTrigger extends React.PureComponent { render() { return (React.createElement(DropdownTriggerDiv, { onClick: this.props.onClick }, this.props.children)); } } exports.DropdownTrigger = DropdownTrigger; const DropdownContentDiv = styled_components_1.default.div ` user-select: none; margin: 0px; padding: 0px; width: 200px; opacity: 1; position: absolute; top: 0.2em; right: 0; border-style: none; padding: 0; font-family: var(--nt-font-family-normal); font-size: var(--nt-font-size-m); line-height: 1.5; margin: 20px 0; background-color: var(--theme-cell-menu-bg); ul { list-style: none; text-align: left; padding: 0; margin: 0; opacity: 1; } ul li { padding: 0.5rem; } ul li:hover { background-color: var(--theme-cell-menu-bg-hover, #e2dfe3); cursor: pointer; } `; DropdownContentDiv.displayName = "DropdownContentDiv"; class DropdownContent extends React.PureComponent { render() { return (React.createElement(DropdownContentDiv, null, React.createElement("ul", { role: "listbox", "aria-label": "dropdown-content", ref: this.props.ulRef }, React.Children.map(this.props.children, child => { const childElement = child; const elRef = React.createRef(); return React.cloneElement(childElement, { onClick: (ev) => { childElement.props.onClick(ev); // Hide the menu this.props.onItemClick(ev); }, onKeyUp: (ev) => { if (childElement.props.onKeyUp) { childElement.props.onKeyUp(ev); //forward the event } if (ev.key === "Enter" && elRef.current) { elRef.current.click(); } }, ref: elRef }); })))); } } exports.DropdownContent = DropdownContent; DropdownContent.defaultProps = { // Completely silly standalone, because DropdownMenu injects the onItemClick handler onItemClick: () => { } };