import React, { forwardRef, HTMLAttributes, useEffect, useRef, useState } from 'react' import PropTypes from 'prop-types' import classNames from 'classnames' import { Transition } from 'react-transition-group' import { CBackdrop } from '../backdrop' import { CConditionalPortal } from '../conditional-portal' import { CFocusTrap } from '../focus-trap' import { useForkedRef } from '../../hooks' export interface COffcanvasProps extends HTMLAttributes { /** * Apply a backdrop on body while offcanvas is open. */ backdrop?: boolean | 'static' /** * A string of all className you want applied to the base component. */ className?: string /** * Sets a darker color scheme. */ dark?: boolean /** * Closes the offcanvas when escape key is pressed. */ keyboard?: boolean /** * Callback fired when the component requests to be hidden. */ onHide?: () => void /** * Callback fired when the component requests to be shown. */ onShow?: () => void /** * Components placement, there’s no default placement. */ placement: 'start' | 'end' | 'top' | 'bottom' /** * Generates modal using createPortal. */ portal?: boolean /** * Responsive offcanvas property hide content outside the viewport from a specified breakpoint and down. * * @since 4.6.0 */ responsive?: boolean | 'sm' | 'md' | 'lg' | 'xl' | 'xxl' /** * Allow body scrolling while offcanvas is open */ scroll?: boolean /** * Toggle the visibility of offcanvas component. */ visible?: boolean } export const COffcanvas = forwardRef( ( { children, backdrop = true, className, dark, keyboard = true, onHide, onShow, placement, portal = false, responsive = true, scroll = false, visible = false, ...rest }, ref ) => { const [_visible, setVisible] = useState(visible) const offcanvasRef = useRef(null) const forkedRef = useForkedRef(ref, offcanvasRef) useEffect(() => { setVisible(visible) }, [visible]) useEffect(() => { if (_visible && !scroll) { document.body.style.overflow = 'hidden' document.body.style.paddingRight = '0px' return } if (!scroll) { document.body.style.removeProperty('overflow') document.body.style.removeProperty('padding-right') } }, [_visible]) const handleDismiss = () => { setVisible(false) } const handleBackdropDismiss = () => { if (backdrop !== 'static') { setVisible(false) } } const handleKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'Escape' && keyboard) { return handleDismiss() } } return ( <> {(state) => (
{children}
)}
{backdrop && ( )} ) } ) COffcanvas.propTypes = { backdrop: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf<'static'>(['static'])]), children: PropTypes.node, className: PropTypes.string, dark: PropTypes.bool, keyboard: PropTypes.bool, onHide: PropTypes.func, onShow: PropTypes.func, placement: PropTypes.oneOf<'start' | 'end' | 'top' | 'bottom'>(['start', 'end', 'top', 'bottom']) .isRequired, portal: PropTypes.bool, responsive: PropTypes.oneOfType([ PropTypes.bool, PropTypes.oneOf<'sm' | 'md' | 'lg' | 'xl' | 'xxl'>(['sm', 'md', 'lg', 'xl', 'xxl']), ]), scroll: PropTypes.bool, visible: PropTypes.bool, } COffcanvas.displayName = 'COffcanvas'