UNPKG

react-bulma-dropdown

Version:

It's a Bulma's dropdown, with an auto-height, scroll and portal support.

709 lines (588 loc) 25.9 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(require("react"), require("react-dom")); else if(typeof define === 'function' && define.amd) define(["react", "react-dom"], factory); else if(typeof exports === 'object') exports["Dropdown"] = factory(require("react"), require("react-dom")); else root["Dropdown"] = factory(root["react"], root["react-dom"]); })(window, function(__WEBPACK_EXTERNAL_MODULE__0__, __WEBPACK_EXTERNAL_MODULE__2__) { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 7); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports) { module.exports = __WEBPACK_EXTERNAL_MODULE__0__; /***/ }), /* 1 */ /***/ (function(module, exports, __webpack_require__) { /** * Copyright (c) 2013-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ if (false) { var throwOnDirectAccess, ReactIs; } else { // By explicitly using `prop-types` you are opting into new production behavior. // http://fb.me/prop-types-in-prod module.exports = __webpack_require__(4)(); } /***/ }), /* 2 */ /***/ (function(module, exports) { module.exports = __WEBPACK_EXTERNAL_MODULE__2__; /***/ }), /* 3 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! Copyright (c) 2017 Jed Watson. Licensed under the MIT License (MIT), see http://jedwatson.github.io/classnames */ /* global define */ (function () { 'use strict'; var hasOwn = {}.hasOwnProperty; function classNames () { var classes = []; for (var i = 0; i < arguments.length; i++) { var arg = arguments[i]; if (!arg) continue; var argType = typeof arg; if (argType === 'string' || argType === 'number') { classes.push(arg); } else if (Array.isArray(arg) && arg.length) { var inner = classNames.apply(null, arg); if (inner) { classes.push(inner); } } else if (argType === 'object') { for (var key in arg) { if (hasOwn.call(arg, key) && arg[key]) { classes.push(key); } } } } return classes.join(' '); } if ( true && module.exports) { classNames.default = classNames; module.exports = classNames; } else if (true) { // register as 'classnames', consistent with npm package name !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = (function () { return classNames; }).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); } else {} }()); /***/ }), /* 4 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /** * Copyright (c) 2013-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ var ReactPropTypesSecret = __webpack_require__(5); function emptyFunction() {} function emptyFunctionWithReset() {} emptyFunctionWithReset.resetWarningCache = emptyFunction; module.exports = function() { function shim(props, propName, componentName, location, propFullName, secret) { if (secret === ReactPropTypesSecret) { // It is still safe when called from React. return; } var err = new Error( 'Calling PropTypes validators directly is not supported by the `prop-types` package. ' + 'Use PropTypes.checkPropTypes() to call them. ' + 'Read more at http://fb.me/use-check-prop-types' ); err.name = 'Invariant Violation'; throw err; }; shim.isRequired = shim; function getShim() { return shim; }; // Important! // Keep this list in sync with production version in `./factoryWithTypeCheckers.js`. var ReactPropTypes = { array: shim, bool: shim, func: shim, number: shim, object: shim, string: shim, symbol: shim, any: shim, arrayOf: getShim, element: shim, elementType: shim, instanceOf: getShim, node: shim, objectOf: getShim, oneOf: getShim, oneOfType: getShim, shape: getShim, exact: getShim, checkPropTypes: emptyFunctionWithReset, resetWarningCache: emptyFunction }; ReactPropTypes.PropTypes = ReactPropTypes; return ReactPropTypes; }; /***/ }), /* 5 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /** * Copyright (c) 2013-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ var ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED'; module.exports = ReactPropTypesSecret; /***/ }), /* 6 */ /***/ (function(module, exports, __webpack_require__) { // extracted by mini-css-extract-plugin /***/ }), /* 7 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; // ESM COMPAT FLAG __webpack_require__.r(__webpack_exports__); // EXTERNAL MODULE: external "react" var external_react_ = __webpack_require__(0); var external_react_default = /*#__PURE__*/__webpack_require__.n(external_react_); // EXTERNAL MODULE: ./node_modules/classnames/index.js var classnames = __webpack_require__(3); var classnames_default = /*#__PURE__*/__webpack_require__.n(classnames); // EXTERNAL MODULE: external "react-dom" var external_react_dom_ = __webpack_require__(2); var external_react_dom_default = /*#__PURE__*/__webpack_require__.n(external_react_dom_); // EXTERNAL MODULE: ./node_modules/prop-types/index.js var prop_types = __webpack_require__(1); var prop_types_default = /*#__PURE__*/__webpack_require__.n(prop_types); // CONCATENATED MODULE: ./node_modules/react-portal/es/utils.js var canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement); // CONCATENATED MODULE: ./node_modules/react-portal/es/Portal.js var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var Portal_Portal = function (_React$Component) { _inherits(Portal, _React$Component); function Portal() { _classCallCheck(this, Portal); return _possibleConstructorReturn(this, (Portal.__proto__ || Object.getPrototypeOf(Portal)).apply(this, arguments)); } _createClass(Portal, [{ key: 'componentWillUnmount', value: function componentWillUnmount() { if (this.defaultNode) { document.body.removeChild(this.defaultNode); } this.defaultNode = null; } }, { key: 'render', value: function render() { if (!canUseDOM) { return null; } if (!this.props.node && !this.defaultNode) { this.defaultNode = document.createElement('div'); document.body.appendChild(this.defaultNode); } return external_react_dom_default.a.createPortal(this.props.children, this.props.node || this.defaultNode); } }]); return Portal; }(external_react_default.a.Component); Portal_Portal.propTypes = { children: prop_types_default.a.node.isRequired, node: prop_types_default.a.any }; /* harmony default export */ var es_Portal = (Portal_Portal); // CONCATENATED MODULE: ./node_modules/react-portal/es/LegacyPortal.js var LegacyPortal_createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function LegacyPortal_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function LegacyPortal_possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function LegacyPortal_inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // This file is a fallback for a consumer who is not yet on React 16 // as createPortal was introduced in React 16 var LegacyPortal_Portal = function (_React$Component) { LegacyPortal_inherits(Portal, _React$Component); function Portal() { LegacyPortal_classCallCheck(this, Portal); return LegacyPortal_possibleConstructorReturn(this, (Portal.__proto__ || Object.getPrototypeOf(Portal)).apply(this, arguments)); } LegacyPortal_createClass(Portal, [{ key: 'componentDidMount', value: function componentDidMount() { this.renderPortal(); } }, { key: 'componentDidUpdate', value: function componentDidUpdate(props) { this.renderPortal(); } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { external_react_dom_default.a.unmountComponentAtNode(this.defaultNode || this.props.node); if (this.defaultNode) { document.body.removeChild(this.defaultNode); } this.defaultNode = null; this.portal = null; } }, { key: 'renderPortal', value: function renderPortal(props) { if (!this.props.node && !this.defaultNode) { this.defaultNode = document.createElement('div'); document.body.appendChild(this.defaultNode); } var children = this.props.children; // https://gist.github.com/jimfb/d99e0678e9da715ccf6454961ef04d1b if (typeof this.props.children.type === 'function') { children = external_react_default.a.cloneElement(this.props.children); } this.portal = external_react_dom_default.a.unstable_renderSubtreeIntoContainer(this, children, this.props.node || this.defaultNode); } }, { key: 'render', value: function render() { return null; } }]); return Portal; }(external_react_default.a.Component); /* harmony default export */ var LegacyPortal = (LegacyPortal_Portal); LegacyPortal_Portal.propTypes = { children: prop_types_default.a.node.isRequired, node: prop_types_default.a.any }; // CONCATENATED MODULE: ./node_modules/react-portal/es/PortalCompat.js var PortalCompat_Portal = void 0; if (external_react_dom_default.a.createPortal) { PortalCompat_Portal = es_Portal; } else { PortalCompat_Portal = LegacyPortal; } /* harmony default export */ var PortalCompat = (PortalCompat_Portal); // CONCATENATED MODULE: ./node_modules/react-cool-onclickoutside/dist/index.esm.js var canUsePassiveEvents = (function () { if (typeof window === "undefined" || typeof window.addEventListener !== "function") return false; var passive = false; var options = Object.defineProperty({}, "passive", { // eslint-disable-next-line getter-return get: function get() { passive = true; } }); var noop = function noop() { return null; }; window.addEventListener("test", noop, options); window.removeEventListener("test", noop, options); return passive; }); var DEFAULT_IGNORE_CLASS = "ignore-onclickoutside"; var hasIgnoreClass = function hasIgnoreClass(e, ignoreClass) { var el = e.target || e; while (el) { var _el$classList; if ((_el$classList = el.classList) != null && _el$classList.contains(ignoreClass)) return true; el = el.parentElement; } return false; }; var clickedOnScrollbar = function clickedOnScrollbar(e) { return document.documentElement.clientWidth <= e.clientX || document.documentElement.clientHeight <= e.clientY; }; var getEventOptions = function getEventOptions(type) { return type.includes("touch") && canUsePassiveEvents() ? { passive: true } : false; }; var index_esm_useOnclickOutside = function useOnclickOutside(callback, _temp) { var _ref = _temp === void 0 ? {} : _temp, refsOpt = _ref.refs, disabled = _ref.disabled, _ref$eventTypes = _ref.eventTypes, eventTypes = _ref$eventTypes === void 0 ? ["mousedown", "touchstart"] : _ref$eventTypes, excludeScrollbar = _ref.excludeScrollbar, _ref$ignoreClass = _ref.ignoreClass, ignoreClass = _ref$ignoreClass === void 0 ? DEFAULT_IGNORE_CLASS : _ref$ignoreClass, _ref$detectIFrame = _ref.detectIFrame, detectIFrame = _ref$detectIFrame === void 0 ? true : _ref$detectIFrame; var _useState = Object(external_react_["useState"])([]), refsState = _useState[0], setRefsState = _useState[1]; var callbackRef = Object(external_react_["useRef"])(callback); callbackRef.current = callback; var ref = Object(external_react_["useCallback"])(function (el) { return setRefsState(function (prevState) { return [].concat(prevState, [{ current: el }]); }); }, []); Object(external_react_["useEffect"])(function () { if (!(refsOpt != null && refsOpt.length) && !refsState.length) return; var getEls = function getEls() { var els = []; (refsOpt || refsState).forEach(function (_ref2) { var current = _ref2.current; return current && els.push(current); }); return els; }; var handler = function handler(e) { if (!hasIgnoreClass(e, ignoreClass) && !(excludeScrollbar && clickedOnScrollbar(e)) && getEls().every(function (el) { return !el.contains(e.target); })) callbackRef.current(e); }; var blurHandler = function blurHandler(e) { return (// On firefox the iframe becomes document.activeElement in the next event loop setTimeout(function () { var _document = document, activeElement = _document.activeElement; if ((activeElement == null ? void 0 : activeElement.tagName) === "IFRAME" && !hasIgnoreClass(activeElement, ignoreClass) && !getEls().includes(activeElement)) callbackRef.current(e); }, 0) ); }; var removeEventListener = function removeEventListener() { eventTypes.forEach(function (type) { return (// @ts-expect-error document.removeEventListener(type, handler, getEventOptions(type)) ); }); if (detectIFrame) window.removeEventListener("blur", blurHandler); }; if (disabled) { removeEventListener(); return; } eventTypes.forEach(function (type) { return document.addEventListener(type, handler, getEventOptions(type)); }); if (detectIFrame) window.addEventListener("blur", blurHandler); // eslint-disable-next-line consistent-return return function () { return removeEventListener(); }; }, // eslint-disable-next-line react-hooks/exhaustive-deps [refsState, ignoreClass, excludeScrollbar, disabled, detectIFrame, // eslint-disable-next-line react-hooks/exhaustive-deps JSON.stringify(eventTypes)]); return ref; }; /* harmony default export */ var index_esm = (index_esm_useOnclickOutside); // CONCATENATED MODULE: ./src/ts/Dropdown.tsx const Dropdown = ({ children, className = '', trigger = 'Open dropdown', margin = 20, portal = null, isAnimated = true, openOnMount = false, hideOnLinkClick = true, controlRef: controlRefCallback, }) => { const ref = Object(external_react_["useRef"])(null); const [isOpen, setOpen] = Object(external_react_["useState"])(openOnMount); const controlRef = Object(external_react_["useRef"])({ hide: () => { }, open: () => { } }); Object(external_react_["useEffect"])(() => { if (controlRefCallback) { controlRefCallback(controlRef.current); } }, []); const onToggleClick = () => { if (isOpen) { hide(); } else { open(); } }; const hide = () => { setOpen(false); }; const open = () => { setOpen(true); }; controlRef.current.open = open; controlRef.current.hide = hide; const handleContentClick = (e) => { if (hideOnLinkClick && e.target.closest('a.dropdown-item')) { hide(); } }; const triggerEl = typeof trigger === 'string' ? (external_react_default.a.createElement("button", { className: "button" }, trigger)) : (trigger); return (external_react_default.a.createElement("div", { ref: ref, className: "dropdown" }, external_react_default.a.createElement(Trigger, { onClick: onToggleClick }, triggerEl), isOpen && (external_react_default.a.createElement(Menu, { onClick: handleContentClick, className: className, ref: ref, onClickOutside: hide, isAnimated: isAnimated, portal: portal, margin: margin }, children)))); }; Dropdown.propTypes = { children: prop_types["node"], className: prop_types["string"], trigger: prop_types["oneOfType"]([prop_types["string"], prop_types["node"]]), margin: prop_types["number"], portal: prop_types["instanceOf"](HTMLElement), isAnimated: prop_types["bool"], openOnMount: prop_types["bool"], hideOnLinkClick: prop_types["bool"], }; /* harmony default export */ var ts_Dropdown = (Dropdown); const Trigger = ({ children, onClick }) => { return (external_react_default.a.createElement("div", { className: "dropdown-trigger", onClick: e => { e.stopPropagation(); onClick(); } }, children)); }; const Menu = Object(external_react_["forwardRef"])((props, ref) => { const { children, onClick, className, onClickOutside, isAnimated, portal, margin, } = props; const contentRef = Object(external_react_["useRef"])(null); const triggerRef = Object(external_react_["useRef"])(); const [top, setTop] = Object(external_react_["useState"])(0); const [left, setLeft] = Object(external_react_["useState"])(0); const [maxHeight, setMaxHeight] = Object(external_react_["useState"])(0); const [isMenuMounted, setMenuMounted] = Object(external_react_["useState"])(false); const menuRef = index_esm(onClickOutside); Object(external_react_["useEffect"])(() => { if (!ref.current) { return; } const elToListen = portal === null ? window : portal; setMenuMounted(true); triggerRef.current = ref.current.querySelector('.dropdown-trigger'); calculatePos(); elToListen.addEventListener('scroll', calculatePos); return () => elToListen.removeEventListener('scroll', calculatePos); }, []); const calculatePos = () => { if (!triggerRef.current) { return; } let portalScrollY, portalScrollX, portalHeight, portalOffsetX, portalOffsetY; if (portal === null) { portalScrollY = window.scrollY; portalScrollX = window.scrollX; portalHeight = window.innerHeight; portalOffsetX = 0; portalOffsetY = 0; } else { const portalRect = portal.getBoundingClientRect(); portalScrollY = portal.scrollTop; portalScrollX = portal.scrollLeft; portalHeight = portal.offsetHeight; portalOffsetX = portalRect.x; portalOffsetY = portalRect.y; } const triggerRect = triggerRef.current.getBoundingClientRect(); const triggerYBottom = triggerRect.y + triggerRect.height; setTop(triggerYBottom + portalScrollY - portalOffsetY); setLeft(triggerRect.x + portalScrollX - portalOffsetX); setMaxHeight(portalHeight - triggerYBottom + portalOffsetY - margin); }; return (external_react_default.a.createElement(PortalCompat, { node: portal }, external_react_default.a.createElement("div", { className: classnames_default()('dropdown-portal', className, { 'is-active': isMenuMounted, 'is-animated': isAnimated, }) }, external_react_default.a.createElement("div", { className: "dropdown-menu", style: { top: `${top}px`, left: `${left}px` }, ref: menuRef }, external_react_default.a.createElement("div", { className: "dropdown-content", onClick: onClick, ref: contentRef, style: { maxHeight: `${maxHeight}px` } }, children))))); }); // EXTERNAL MODULE: ./src/scss/index.scss var scss = __webpack_require__(6); // CONCATENATED MODULE: ./src/ts/index.ts /* harmony default export */ var ts = __webpack_exports__["default"] = (ts_Dropdown); /***/ }) /******/ ]); });