@gambito-corp/mbs-library
Version:
Librería de componentes React reutilizables - Sistema de diseño modular y escalable
167 lines (159 loc) • 5.49 kB
JSX
import React from 'react';
import PropTypes from 'prop-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { ICON_SIZES, ICON_VARIANTS } from './Icon.constants.js';
import { getIconClasses, renderCustomIcon } from './Icon.utils.js';
import './Icon.css';
const Icon = ({
name,
icon,
type = 'fas',
svg,
svgData,
src,
alt = '',
emoji,
unicode,
size = 'medium',
variant = 'default',
color,
textColor,
className = '',
onClick,
onMouseEnter,
onMouseLeave,
disabled = false,
loading = false,
ariaLabel,
title,
role = 'img',
...props
}) => {
const iconClasses = getIconClasses({ size, variant, className, disabled, loading, textColor });
let iconType = 'default';
if (loading) iconType = 'loading';
else if (emoji) iconType = 'emoji';
else if (unicode) iconType = 'unicode';
else if (svgData) iconType = 'svgData';
else if (svg) iconType = 'svg';
else if (src) iconType = 'image';
else if (name || icon) iconType = 'fontawesome';
const renderIcon = () => {
switch (iconType) {
case 'loading':
return (
<span className="icon-loading animate-spin">
<FontAwesomeIcon icon="spinner" />
</span>
);
case 'emoji':
return (
<span className="icon-emoji" role="img" aria-label={ariaLabel || alt}>
{emoji}
</span>
);
case 'unicode':
return (
<span className="icon-unicode" role="img" aria-label={ariaLabel || alt}>
{String.fromCharCode(unicode)}
</span>
);
case 'svgData':
return (
<img
className="icon-svg-data"
src={svgData}
alt={alt}
aria-label={ariaLabel}
title={title}
/>
);
case 'svg':
if (typeof svg === 'string') {
return (
<span
className="icon-svg-custom"
data-testid="custom-svg"
dangerouslySetInnerHTML={{ __html: svg }}
aria-label={ariaLabel}
title={title}
/>
);
}
return svg;
case 'image':
return (
<img
className="icon-image"
src={src}
alt={alt}
aria-label={ariaLabel}
title={title}
/>
);
case 'fontawesome':
const faIcon = icon || [type, name];
return (
<FontAwesomeIcon
icon={faIcon}
className="icon-fontawesome"
title={title}
/>
);
default:
return (
<span className="icon-default" role="img" aria-label="Icon">
⚡
</span>
);
}
};
const dynamicStyles = {
color,
cursor: onClick && !disabled ? 'pointer' : 'default',
opacity: disabled ? 0.5 : 1,
...props.style
};
return (
<span
className={iconClasses}
style={dynamicStyles}
onClick={!disabled ? onClick : undefined}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
data-testid="Icon"
data-icon-type={iconType}
role={role}
aria-label={ariaLabel}
title={title}
{...props}
>
{renderIcon()}
</span>
);
};
Icon.propTypes = {
name: PropTypes.string,
icon: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
type: PropTypes.oneOf(['fas', 'far', 'fab', 'fal', 'fad']),
svg: PropTypes.string,
svgData: PropTypes.string,
src: PropTypes.string,
alt: PropTypes.string,
emoji: PropTypes.string,
unicode: PropTypes.number,
size: PropTypes.oneOf(Object.keys(ICON_SIZES)),
variant: PropTypes.oneOf(Object.keys(ICON_VARIANTS)),
color: PropTypes.string,
textColor: PropTypes.string,
className: PropTypes.string,
onClick: PropTypes.func,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
disabled: PropTypes.bool,
loading: PropTypes.bool,
ariaLabel: PropTypes.string,
title: PropTypes.string,
role: PropTypes.string
};
export default Icon;