@kadconsulting/dry
Version:
KAD Reusable Component Library
128 lines • 5.95 kB
JavaScript
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