@docyrus/react-icon
Version:
docy-icon component for react
157 lines (156 loc) • 4.66 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
import * as React from "react";
import { cva } from "class-variance-authority";
import { cn } from "../lib/utils";
import { isEmoji, isDotCharacter, getIconComponent, getDefaultIcon, validateIconProps, validateNumericSize } from "./utils";
const DEFAULT_ICON_SIZE = "md";
const DEFAULT_ICON_LIB = "lucide";
const DEFAULT_STROKE_WIDTH = 1.5;
export const SIZE_VALIDATION_RANGE = {
min: 4,
max: 200
};
const iconVariants = cva("inline-flex items-center justify-center shrink-0", {
variants: {
size: {
xs: "w-3 h-3",
sm: "w-3.5 h-3.5",
md: "w-4 h-4",
lg: "w-5 h-5",
xl: "w-6 h-6",
"2xl": "w-8 h-8"
},
animation: {
none: "",
spin: "animate-spin",
pulse: "animate-pulse",
bounce: "animate-bounce"
},
lib: {
lucide: "lucide",
heroicons: "heroicons",
tabler: "tabler",
phosphor: "phosphor",
custom: "custom-icon"
}
},
defaultVariants: {
size: "md",
animation: "none",
lib: 'lucide'
}
});
const sizeMap = {
xs: 12,
sm: 14,
md: 20,
lg: 24,
xl: 28,
"2xl": 32
};
// Helper functions for style optimization
function createDotStyle(size, color) {
return {
width: size,
height: size,
color,
fontSize: size * 0.4,
lineHeight: 1
};
}
function createEmojiStyle(size, color) {
return {
fontSize: size * 0.8,
lineHeight: 1,
color,
width: size,
height: size
};
}
function renderIcon(name, lib, size, color, className, decorative = false) {
if (!validateIconProps(name, lib)) {
const DefaultIcon = getDefaultIcon();
return /*#__PURE__*/ _jsx(DefaultIcon, {
size: size,
className: className,
style: {
color
},
strokeWidth: DEFAULT_STROKE_WIDTH,
"aria-hidden": decorative
});
}
if (isDotCharacter(name)) {
return /*#__PURE__*/ _jsx("span", {
className: cn("flex items-center justify-center", className),
style: createDotStyle(size, color),
role: "img",
"aria-label": decorative ? undefined : "dot",
"aria-hidden": decorative,
children: "•"
});
}
if (isEmoji(name)) {
return /*#__PURE__*/ _jsx("span", {
className: cn("flex items-center justify-center", className),
style: createEmojiStyle(size, color),
role: "img",
"aria-label": decorative ? undefined : `${name} emoji`,
"aria-hidden": decorative,
children: name
});
}
const IconComponent = getIconComponent(name, lib);
if (IconComponent) {
return /*#__PURE__*/ _jsx(IconComponent, {
size: size,
className: className,
style: {
color
},
strokeWidth: DEFAULT_STROKE_WIDTH,
"aria-hidden": decorative
});
}
const DefaultIcon = getDefaultIcon();
return /*#__PURE__*/ _jsx(DefaultIcon, {
size: size,
className: className,
style: {
color
},
strokeWidth: DEFAULT_STROKE_WIDTH,
"aria-hidden": decorative
});
}
const DocyIcon = /*#__PURE__*/ React.forwardRef(({ name, lib = DEFAULT_ICON_LIB, size = DEFAULT_ICON_SIZE, color = "currentColor", animation, className, decorative = false, ...props }, ref)=>{
const validatedSize = typeof size === "number" ? validateNumericSize(size) : sizeMap[size];
const iconClasses = cn(iconVariants({
size: typeof size === "number" ? DEFAULT_ICON_SIZE : size,
animation
}), className);
const iconElement = renderIcon(name, lib, validatedSize, color, iconClasses, decorative);
// Add error boundary around React.cloneElement
try {
if (/*#__PURE__*/ React.isValidElement(iconElement)) {
return /*#__PURE__*/ React.cloneElement(iconElement, {
...props
});
}
} catch {
// Fallback if cloneElement fails
return /*#__PURE__*/ _jsx("span", {
ref: ref,
...props,
children: iconElement
});
}
return /*#__PURE__*/ _jsx("span", {
ref: ref,
...props,
children: iconElement
});
});
DocyIcon.displayName = "DocyIcon";
export { DocyIcon, iconVariants };
export default DocyIcon;