UNPKG

saagie-ui

Version:

Saagie UI from Saagie Design System

227 lines (208 loc) 5.96 kB
/* eslint-disable no-undef */ import React, { useEffect, useMemo, useState, useRef, } from 'react'; import PropTypes from 'prop-types'; import { Manager, Reference } from 'react-popper'; import FocusTrap from 'focus-trap-react'; import classnames from 'classnames'; import { ClickAwayListener } from '../../helpers/clickAwayListener/ClickAwayListener'; import { usePopper } from '../../../helpers'; import { Button } from '../../atoms/button/Button'; export const stopPropagationForConfirmation = (event, action) => { event.preventDefault(); event.stopPropagation(); action(); }; const propTypes = { /** * The label for the cancel button. */ cancelLabel: PropTypes.node, /** * The color of the cancel button */ cancelColor: PropTypes.oneOf(['', 'primary', 'secondary', 'warning', 'danger']), /** * The children that will be rendered by Confirm. The element onClick event * will be intercepted by Confirm. */ children: PropTypes.element.isRequired, className: PropTypes.string, /** * The label for the confirmation button. */ confirmLabel: PropTypes.node, /** * The color of the confirmation button */ confirmColor: PropTypes.oneOf(['', 'primary', 'secondary', 'warning', 'danger']), /** * The content of the Confirm element. */ content: PropTypes.node, defaultClassName: PropTypes.oneOfType([ PropTypes.string, PropTypes.arrayOf(PropTypes.string), ]), /** * Disable the escape key down listener. */ isEscapeKeyDisabled: PropTypes.bool, /** * Enable or disable the click away behavior. By default, the confirm will * close on click away. */ isClickOutsideDisabled: PropTypes.bool, /** * Render the Confirm in the current DOM hierarchy. */ isPortalDisabled: PropTypes.bool, /** * Render the Confirm element in one of the available placement. */ placement: PropTypes.oneOf(['bottom', 'left', 'top', 'right']), /** * The target where the confirm node should render. */ portalTarget: PropTypes.instanceOf(Element), /** * The component used for the root node. * Either a string to use a DOM element or a component. */ tag: PropTypes.elementType, /** * The title of the Confirm. */ title: PropTypes.node, }; const defaultProps = { cancelLabel: 'Cancel', cancelColor: '', className: '', confirmLabel: 'Confirm', confirmColor: 'primary', content: '', defaultClassName: 'sui-o-popup as--open', isEscapeKeyDisabled: false, isClickOutsideDisabled: false, isPortalDisabled: false, placement: 'top', portalTarget: document.body, tag: 'div', title: 'Are you sure?', }; export const Confirm = ({ cancelLabel, cancelColor, children, className, confirmLabel, confirmColor, content, defaultClassName, isEscapeKeyDisabled, isClickOutsideDisabled, isPortalDisabled: isDisabled, placement, portalTarget: target, tag: Tag, title, ...attributes }) => { if (!children || !children.props) { return ''; } const [isOpen, setIsOpen] = useState(false); const { mountPopperNode } = usePopper(placement, { isDisabled, target }); const callbackRef = useRef(null); const classes = classnames( defaultClassName, className, ); const handleClose = () => { setIsOpen(false); }; const handleShow = (callbackFunction) => () => { callbackRef.current = callbackFunction; setIsOpen(true); }; const handleConfirm = () => { if (callbackRef.current) { callbackRef.current(); } handleClose(); }; /** * Function to handle the Escape key down. * @param {KeyboardEvent} event */ const handleKeyDown = (event) => { if (event.key !== 'Escape' && event.keyCode !== 27) { return; } // Swallow the event, in case someone is listening for the escape key on the body. event.stopPropagation(); if (!isEscapeKeyDisabled) { handleClose(); } }; // Escape Key Listener useEffect(() => { if (!isEscapeKeyDisabled) { window.addEventListener('keydown', handleKeyDown); } return () => { window.removeEventListener('keydown', handleKeyDown); }; }, []); const uniqueId = useMemo(() => Math.random().toString(36).substr(2, 9), []); return ( <Manager> <Reference> {({ ref }) => ( React.cloneElement(children, { ref, onClick: (e) => stopPropagationForConfirmation(e, handleShow(children.props.onClick)), }) )} </Reference> {isOpen && (mountPopperNode( ({ ref, style }) => ( <ClickAwayListener isClickOutsideDisabled={isClickOutsideDisabled} onClickOutside={() => setIsOpen(false)} > <FocusTrap> <Tag className={classes} {...attributes} ref={ref} style={style} role="alertdialog" aria-labelledby={`sui-confirm-title-${uniqueId}`} aria-describedby={`sui-confirm-content-${uniqueId}`} > {!!title && <div id={`sui-confirm-title-${uniqueId}`} className="sui-o-popup__title">{title}</div>} {!!content && <div id={`sui-confirm-content-${uniqueId}`} className="sui-o-popup__content">{content}</div>} <div className="sui-o-popup__buttons"> <Button type="button" onClick={handleClose} color={cancelColor}> {cancelLabel} </Button> <Button type="submit" color={confirmColor} onClick={handleConfirm} > {confirmLabel} </Button> </div> </Tag> </FocusTrap> </ClickAwayListener> )))} </Manager> ); }; Confirm.propTypes = propTypes; Confirm.defaultProps = defaultProps;