UNPKG

react-mega-menu

Version:
245 lines (244 loc) 9.57 kB
"use strict"; var __extends = undefined && undefined.__extends || function () { var _extendStatics = function extendStatics(d, b) { _extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; } || function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) d[p] = b[p]; } }; return _extendStatics(d, b); }; return function (d, b) { _extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; }(); var __assign = undefined && undefined.__assign || function () { __assign = Object.assign || function (t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) { if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } } return t; }; return __assign.apply(this, arguments); }; Object.defineProperty(exports, "__esModule", { value: true }); var React = require("react"); var glamor_1 = require("glamor"); /** * SUB-COMPONENTS */ function MenuItem(_a) { var selected = _a.selected, label = _a.label, mouseEntered = _a.mouseEntered, hasData = _a.hasData, props = _a.props, selectedProps = _a.selectedProps; return React.createElement("li", __assign({}, props, selected ? selectedProps : {}, { onMouseEnter: mouseEntered }), label); } exports.MenuItem = MenuItem; /** * MAIN COMPONENT */ var ReactMegaMenu = /** @class */function (_super) { __extends(ReactMegaMenu, _super); function ReactMegaMenu() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.state = { activeRow: -1, mouseLocs: [] }; _this.instance = React.createRef(); _this.mouseLeave = function () { _this.dismissTimeout(); var onExit = _this.props.onExit; if (onExit) { onExit(); } _this.setState({ activeRow: -1 }); }; _this.recordMouse = function (e) { var x = e.pageX, y = e.pageY; _this.setState(function (prevState) { var mouseLocs = prevState.mouseLocs.slice(); mouseLocs.push({ x: x, y: y }); if (mouseLocs.length > 8) { mouseLocs.shift(); } return { mouseLocs: mouseLocs }; }); }; _this.enterSubMenu = function () { _this.dismissTimeout(); }; return _this; } ReactMegaMenu.prototype.mouseEnterRow = function (row) { var _this = this; return function () { _this.dismissTimeout(); _this.possiblyActivate(row); }; }; ReactMegaMenu.prototype.possiblyActivate = function (row) { var _this = this; var delay = this.getActivationDelay(); if (delay) { var timeoutID = setTimeout(function () { _this.possiblyActivate(row); }, delay); this.setState({ timeoutID: timeoutID }); } else { this.activate(row); } }; ReactMegaMenu.prototype.activate = function (row) { var activeRow = this.state.activeRow; if (row === activeRow) { return; } this.setState({ activeRow: row }); }; ReactMegaMenu.prototype.genCoords = function (x, y) { return { x: x, y: y }; }; ReactMegaMenu.prototype.calcSlope = function (a, b) { return (b.y - a.y) / (b.x - a.x); }; ReactMegaMenu.prototype.dismissTimeout = function () { var timeoutID = this.state.timeoutID; if (timeoutID) { clearTimeout(timeoutID); } }; /** * Relies on state & props. * * If there is no current active row _OR_ * If the mouse location was not previously recorded _OR_ * If the mouse location comes in from outside of the menu _OR_ * If the mouse hasn't moved since last delay _OR_ * DEFAULT: * @return 0 delay. * * If the slope is smaller than slope to top corner, or bigger than slope to bottom corner * @return DELAY's value * * @memberof ReactMegaMenu */ ReactMegaMenu.prototype.getActivationDelay = function () { var activeRow = this.state.activeRow; var tolerance = this.props.tolerance; if (activeRow < 0) { return 0; } var bounds = this.instance.current.getBoundingClientRect(); var upperLeft = this.genCoords(bounds.left, bounds.top - (tolerance || 0)), upperRight = this.genCoords(bounds.left + bounds.width, upperLeft.y), lowerLeft = this.genCoords(bounds.left, bounds.top + bounds.height + (tolerance || 0)), lowerRight = this.genCoords(bounds.left + bounds.width, lowerLeft.y); var mouseLocs = this.state.mouseLocs; var loc = mouseLocs[mouseLocs.length - 1]; var prevLoc = mouseLocs[0]; if (!loc) { return 0; } if (!prevLoc) { prevLoc = loc; } if (prevLoc.x < bounds.left || prevLoc.x > lowerRight.x || prevLoc.y < bounds.top || prevLoc.y > lowerRight.y) { // If the previous mouse location was outside of the entire // menu's bounds, immediately activate. return 0; } var lastDelayLoc = this.state.lastDelayLoc; if (lastDelayLoc && loc.x === lastDelayLoc.x && loc.y === lastDelayLoc.y) { // If the mouse hasn't moved since the last time we checked // for activation status, immediately activate. return 0; } var decreasingCorner, increasingCorner; switch (this.props.direction) { case "LEFT": default: { decreasingCorner = lowerRight; increasingCorner = upperRight; } break; case "RIGHT": { decreasingCorner = upperLeft; increasingCorner = lowerLeft; } break; } var decreasingSlope = this.calcSlope(loc, decreasingCorner), increasingSlope = this.calcSlope(loc, increasingCorner), prevDecreasingSlope = this.calcSlope(prevLoc, decreasingCorner), prevIncreasingSlope = this.calcSlope(prevLoc, increasingCorner); if (decreasingSlope < prevDecreasingSlope && increasingSlope > prevIncreasingSlope) { // Mouse is moving from previous location towards the // currently activated submenu. Delay before activating a // new menu row, because user may be moving into submenu. this.setState({ lastDelayLoc: loc }); return ReactMegaMenu.DELAY; } this.setState({ lastDelayLoc: undefined }); return 0; }; ReactMegaMenu.prototype.render = function () { var _this = this; var activeRow = this.state.activeRow; var _a = this.props, data = _a.data, styleConfig = _a.styleConfig, direction = _a.direction; var containerProps = styleConfig.containerProps, menuProps = styleConfig.menuProps, contentProps = styleConfig.contentProps, menuItemSelectedProps = styleConfig.menuItemSelectedProps, menuItemProps = styleConfig.menuItemProps; return React.createElement("div", __assign({}, containerProps, { className: (containerProps && containerProps.className || "") + " " + styles.flex + " " + styles["flex" + direction] + " " + styles.menu, onMouseLeave: this.mouseLeave }), React.createElement("ul", __assign({}, menuProps, { className: (menuProps && menuProps.className || "") + " " + styles.menuUl, ref: this.instance, onMouseMove: this.recordMouse }), data.map(function (_a, i) { var label = _a.label, key = _a.key, items = _a.items; return React.createElement(MenuItem, { selectedProps: menuItemSelectedProps, props: menuItemProps, selected: i === activeRow, hasData: items !== undefined, mouseEntered: _this.mouseEnterRow(i), label: label, key: key }); })), activeRow > -1 && data[activeRow].items && React.createElement("div", __assign({}, contentProps, { onMouseEnter: this.enterSubMenu, className: (contentProps && contentProps.className || "") + " " + styles.contentSubMenu }), React.createElement("div", { style: { width: "100%" } }, data[activeRow].items))); }; ReactMegaMenu.DELAY = 150; ReactMegaMenu.defaultProps = { tolerance: 100, direction: "RIGHT", styleConfig: {} }; return ReactMegaMenu; }(React.Component); exports.ReactMegaMenu = ReactMegaMenu; /** * css in js */ var styles = { flex: glamor_1.css({ display: "flex" }), flexLEFT: glamor_1.css({ flexDirection: "row-reverse" }), flexRIGHT: glamor_1.css({ flexDirection: "row" }), menu: glamor_1.css({ position: "absolute" }), contentSubMenu: glamor_1.css({ position: "relative" }), menuUl: glamor_1.css({ listStyle: "none", paddingLeft: 0 }) }; exports.default = ReactMegaMenu; //# sourceMappingURL=index.js.map