UNPKG

ndla-ui

Version:

UI component library for NDLA.

177 lines (167 loc) 4.98 kB
/** * Copyright (c) 2016-present, NDLA. * * This source code is licensed under the GPLv3 license found in the * LICENSE file in the root directory of this source tree. * */ import React from 'react'; import PropTypes from 'prop-types'; import { noScroll } from 'ndla-util'; import elementType from 'react-prop-types/lib/elementType'; import FocusTrap from 'focus-trap-react'; import Button from 'ndla-button'; import Dialog from '../Dialog'; export default class ClickToggle extends React.Component { constructor(props) { super(props); this.state = { isOpen: false, }; this.handleClick = this.handleClick.bind(this); this.handleOnClose = this.handleOnClose.bind(this); this.unmountTrap = this.unmountTrap.bind(this); } unmountTrap() { if (this.props.onToggle) { this.handleOnClose(); } else { noScroll(false); this.setState({ isOpen: false }); } } handleClick() { const useState = this.props.isOpen === null; const isOpen = useState ? !this.state.isOpen : !this.props.isOpen; if (this.props.onToggle) { this.props.onToggle(isOpen); } else { noScroll(this.props.noScrollDisabled ? false : isOpen); this.setState({ isOpen, }); } } handleOnClose() { this.props.onToggle(false); } render() { const { title, id, labelledby, openTitle, buttonClassName, noScrollDisabled, containerClass: Component, renderAsLink, renderAsLightButton, stripped, dialogModifier, isOpen, alwaysRenderChildren, disablePortal, children, ...rest } = this.props; const showDialog = isOpen === null ? this.state.isOpen : isOpen; const useDialog = typeof children !== 'function'; return ( <Component {...rest}> <Button stripped={stripped} link={renderAsLink} lighter={renderAsLightButton} className={`${showDialog ? 'active ' : ''}${buttonClassName}`} onClick={this.handleClick}> {title} </Button> <FocusTrap active={showDialog} focusTrapOptions={{ onDeactivate: this.unmountTrap, clickOutsideDeactivates: true, // Only works when click on scrollbar }}> {useDialog && (alwaysRenderChildren || showDialog) && ( <Dialog id={id} labelledby={labelledby} hidden={!showDialog} onClose={this.handleClick} disablePortal={disablePortal} messages={{ close: openTitle || 'lukk' }} modifier={ showDialog ? ['active', dialogModifier] : dialogModifier }> {children} </Dialog> )} {!useDialog && (isOpen || alwaysRenderChildren) && children(this.handleOnClose, isOpen)} </FocusTrap> </Component> ); } } ClickToggle.propTypes = { id: (props, propName, componentName) => { if (typeof props[propName] !== 'string' && props[propName] !== undefined) { return new Error( `Invalid prop ${propName} supplied to ${componentName}. Type must be a string.`, ); } if ( typeof props.children !== 'function' && typeof props[propName] !== 'string' ) { return new Error( `Invalid prop ${propName} supplied to ${componentName}. When children prop is a node, id prop is isRequired.`, ); } return null; }, labelledby: PropTypes.string, containerClass: elementType, title: PropTypes.node.isRequired, openTitle: PropTypes.node, buttonClassName: PropTypes.string, className: PropTypes.string, children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired, noScrollDisabled: PropTypes.bool, isOpen: PropTypes.bool, onToggle: (props, propName, componentName) => { if (typeof props[propName] !== 'function' && props[propName] !== null) { return new Error( `Invalid prop ${propName} supplied to ${componentName}. Type must be a function.`, ); } if ( typeof props.children === 'function' && typeof props[propName] !== 'function' ) { return new Error( `Invalid prop ${propName} supplied to ${componentName}. When children prop is a function, onToggle prop is isRequired.`, ); } return null; }, renderAsLink: PropTypes.bool, renderAsLightButton: PropTypes.bool, stripped: PropTypes.bool, dialogModifier: PropTypes.oneOfType([PropTypes.string, PropTypes.array]), alwaysRenderChildren: PropTypes.bool, disablePortal: PropTypes.bool, }; ClickToggle.defaultProps = { containerClass: 'div', renderAsLink: false, renderAsLightButton: false, stripped: false, isOpen: null, onToggle: null, id: undefined, alwaysRenderChildren: false, disablePortal: true, };