UNPKG

@kadconsulting/dry

Version:
128 lines 5.95 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useEffect, useState, useCallback, useRef, forwardRef, } from 'react'; import { Icon, IconSizes, ArrowUp, ArrowDown, Button } from '../index'; // import * as Utils from "./Toast.utils.js"; import './Toast.scss'; const Toast = forwardRef(({ message, type = 'info', duration = 3000, onClose, className = '', actions, additionalText, additionalButtons = [], position = 'top-right', layout = 'vertical', easing = 'ease-in-out', animationDuration = 300, backgroundColor = '#333', textColor = '#fff', borderRadius = '8px', boxShadow = '0 4px 12px rgba(0, 0, 0, 0.15)', closable = true, persistent = false, onAppearing, onAppeared, onDisappearing, onDisappeared, onClickOutside, onSwipeLeft, onSwipeRight, onSwipeUp, onSwipeDown, }, ref) => { const [isVisible, setIsVisible] = useState(true); const [isOpen, setIsOpen] = useState(false); const [isFocused, setIsFocused] = useState(false); const toastRef = useRef(null); const toastGroupRef = useRef(null); useEffect(() => { const timer = setTimeout(() => { handleCloseToast(); }, duration); return () => clearTimeout(timer); }, [duration]); useEffect(() => { if (isVisible) { onAppearing?.(); setTimeout(() => { onAppeared?.(); }, animationDuration); } else { onDisappearing?.(); setTimeout(() => { onDisappeared?.(); }, animationDuration); } }, [ isVisible, animationDuration, onAppearing, onAppeared, onDisappearing, onDisappeared, ]); const handleCloseToast = useCallback(() => { setIsVisible(false); if (onClose) { onClose(); } }, [onClose]); const handleClickOutside = useCallback((event) => { if (toastRef.current && !toastRef.current.contains(event.target)) { onClickOutside?.(); handleCloseToast(); } }, [handleCloseToast, onClickOutside]); const handleSwipe = useCallback((direction) => { switch (direction) { case 'left': onSwipeLeft?.(); handleCloseToast(); break; case 'right': onSwipeRight?.(); handleCloseToast(); break; case 'up': onSwipeUp?.(); handleCloseToast(); break; case 'down': onSwipeDown?.(); handleCloseToast(); break; } }, [handleCloseToast, onSwipeLeft, onSwipeRight, onSwipeUp, onSwipeDown]); useEffect(() => { document.addEventListener('click', handleClickOutside); return () => { document.removeEventListener('click', handleClickOutside); }; }, [handleClickOutside]); const getPositionClass = () => { switch (position) { case 'top-left': return 'toast--top-left'; case 'bottom-left': return 'toast--bottom-left'; case 'bottom-right': return 'toast--bottom-right'; case 'center': return 'toast--center'; case 'top-right-plus-60': // TODO-p2: add support for custom spacing return 'toast--top-right-plus-60'; default: return 'toast--top-right'; } }; const getLayoutClass = () => { return layout === 'vertical' ? 'toast--vertical' : 'toast--horizontal'; }; const handleKeyDown = useCallback((event) => { if (event.key === 'Escape' && closable) { handleCloseToast(); } }, [closable, handleCloseToast]); const handleFocus = useCallback(() => { setIsFocused(true); }, []); const handleBlur = useCallback(() => { setIsFocused(false); }, []); return (_jsx("div", { ref: toastRef, className: `toast ${`toast--${type}`} ${isVisible ? 'toast--visible' : ''} ${getPositionClass()} ${getLayoutClass()} ${className}`, style: // TODO-p2: add better support for custom styles { // backgroundColor, // color: textColor, // borderRadius, // boxShadow, // transitionTimingFunction: easing, // transitionDuration: `${animationDuration}ms`, }, role: 'alert', "aria-live": 'polite', tabIndex: 0, onKeyDown: handleKeyDown, onFocus: handleFocus, onBlur: handleBlur, children: _jsxs("div", { className: 'toast__content', children: [_jsx("div", { className: 'toast__message', children: message }), additionalText && (_jsx("div", { className: 'toast__additionalText', children: additionalText })), actions && _jsx("div", { className: 'toast__actions', children: actions }), additionalButtons.length > 0 && (_jsx("div", { className: 'toast__buttons', children: additionalButtons.map((buttonProps, index) => (_jsx(Button, { ...buttonProps }, `toast-button-${index}`))) })), closable && (_jsx("button", { className: 'toast__close', onClick: () => { setIsOpen(!isOpen); handleCloseToast(); }, children: isOpen ? (_jsx(Icon, { Path: ArrowUp, size: IconSizes.SMALL })) : (_jsx(Icon, { Path: ArrowDown, size: IconSizes.SMALL })) }))] }) })); }); export const ToastGroup = ({ position = 'top-right', layout = 'vertical', spacing = 16, ...props }) => { const toastGroupRef = useRef(null); return (_jsx("div", { ref: toastGroupRef, className: `toast__group ${`toast__group--${position}`} ${`toast__group--${layout}`} ${`toast__group--spacing-${spacing}`}`, children: _jsx(Toast, { ...props, position: position, layout: layout, className: `toast__groupItem` }) })); }; export default Toast; //# sourceMappingURL=Toast.js.map