mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-04-12 02:38:15 +00:00
Replace all @untitledui/icons imports across 55 files with equivalent
@fortawesome/pro-duotone-svg-icons icons, using FontAwesomeIcon wrappers
(FC<{ className?: string }>) for prop-based usage and inline replacements
for direct JSX usage. Drops unsupported Untitled UI-specific props
(strokeWidth, numeric size). TypeScript compiles clean with no errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
178 lines
6.4 KiB
TypeScript
178 lines
6.4 KiB
TypeScript
import type { FC, ReactNode } from "react";
|
|
import { isValidElement } from "react";
|
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
import { faArrowRight } from "@fortawesome/pro-duotone-svg-icons";
|
|
|
|
const ArrowRight: FC<{ className?: string }> = ({ className }) => <FontAwesomeIcon icon={faArrowRight} className={className} />;
|
|
import { cx, sortCx } from "@/utils/cx";
|
|
import { isReactComponent } from "@/utils/is-react-component";
|
|
|
|
type Size = "md" | "lg";
|
|
type Color = "brand" | "warning" | "error" | "gray" | "success";
|
|
type Theme = "light" | "modern";
|
|
type Align = "leading" | "trailing";
|
|
|
|
const baseClasses: Record<Theme, { root?: string; addon?: string; icon?: string }> = {
|
|
light: {
|
|
root: "rounded-full ring-1 ring-inset",
|
|
addon: "rounded-full ring-1 ring-inset",
|
|
},
|
|
modern: {
|
|
root: "rounded-[10px] bg-primary text-secondary shadow-xs ring-1 ring-inset ring-primary hover:bg-secondary",
|
|
addon: "flex items-center rounded-md bg-primary shadow-xs ring-1 ring-inset ring-primary",
|
|
icon: "text-utility-gray-500",
|
|
},
|
|
};
|
|
|
|
const getSizeClasses = (
|
|
theme?: Theme,
|
|
text?: boolean,
|
|
icon?: boolean,
|
|
): Record<Align, Record<Size, { root?: string; addon?: string; icon?: string; dot?: string }>> => ({
|
|
leading: {
|
|
md: {
|
|
root: cx("py-1 pr-2 pl-1 text-xs font-medium", !text && !icon && "pr-1"),
|
|
addon: cx("px-2 py-0.5", theme === "modern" && "gap-1 px-1.5", text && "mr-2"),
|
|
icon: "ml-1 size-4",
|
|
},
|
|
lg: {
|
|
root: cx("py-1 pr-2 pl-1 text-sm font-medium", !text && !icon && "pr-1"),
|
|
addon: cx("px-2.5 py-0.5", theme === "modern" && "gap-1.5 px-2", text && "mr-2"),
|
|
icon: "ml-1 size-4",
|
|
},
|
|
},
|
|
trailing: {
|
|
md: {
|
|
root: cx("py-1 pr-1 pl-3 text-xs font-medium", theme === "modern" && "pl-2.5"),
|
|
addon: cx("py-0.5 pr-1.5 pl-2", theme === "modern" && "pr-1.5 pl-2", text && "ml-2"),
|
|
icon: "ml-0.5 size-3 stroke-[3px]",
|
|
dot: "mr-1.5",
|
|
},
|
|
lg: {
|
|
root: "py-1 pr-1 pl-3 text-sm font-medium",
|
|
addon: cx("py-0.5 pr-2 pl-2.5", theme === "modern" && "pr-1.5 pl-2", text && "ml-2"),
|
|
icon: "ml-1 size-3 stroke-[3px]",
|
|
dot: "mr-2",
|
|
},
|
|
},
|
|
});
|
|
|
|
const colorClasses: Record<Theme, Record<Color, { root?: string; addon?: string; icon?: string; dot?: string }>> = sortCx({
|
|
light: {
|
|
brand: {
|
|
root: "bg-utility-brand-50 text-utility-brand-700 ring-utility-brand-200 hover:bg-utility-brand-100",
|
|
addon: "bg-primary text-current ring-utility-brand-200",
|
|
icon: "text-utility-brand-500",
|
|
},
|
|
gray: {
|
|
root: "bg-utility-gray-50 text-utility-gray-700 ring-utility-gray-200 hover:bg-utility-gray-100",
|
|
addon: "bg-primary text-current ring-utility-gray-200",
|
|
icon: "text-utility-gray-500",
|
|
},
|
|
error: {
|
|
root: "bg-utility-error-50 text-utility-error-700 ring-utility-error-200 hover:bg-utility-error-100",
|
|
addon: "bg-primary text-current ring-utility-error-200",
|
|
icon: "text-utility-error-500",
|
|
},
|
|
warning: {
|
|
root: "bg-utility-warning-50 text-utility-warning-700 ring-utility-warning-200 hover:bg-utility-warning-100",
|
|
addon: "bg-primary text-current ring-utility-warning-200",
|
|
icon: "text-utility-warning-500",
|
|
},
|
|
success: {
|
|
root: "bg-utility-success-50 text-utility-success-700 ring-utility-success-200 hover:bg-utility-success-100",
|
|
addon: "bg-primary text-current ring-utility-success-200",
|
|
icon: "text-utility-success-500",
|
|
},
|
|
},
|
|
modern: {
|
|
brand: {
|
|
dot: "bg-utility-brand-500 outline-3 -outline-offset-1 outline-utility-brand-100",
|
|
},
|
|
gray: {
|
|
dot: "bg-utility-gray-500 outline-3 -outline-offset-1 outline-utility-gray-100",
|
|
},
|
|
error: {
|
|
dot: "bg-utility-error-500 outline-3 -outline-offset-1 outline-utility-error-100",
|
|
},
|
|
warning: {
|
|
dot: "bg-utility-warning-500 outline-3 -outline-offset-1 outline-utility-warning-100",
|
|
},
|
|
success: {
|
|
dot: "bg-utility-success-500 outline-3 -outline-offset-1 outline-utility-success-100",
|
|
},
|
|
},
|
|
});
|
|
|
|
interface BadgeGroupProps {
|
|
children?: string | ReactNode;
|
|
addonText: string;
|
|
size?: Size;
|
|
color: Color;
|
|
theme?: Theme;
|
|
/**
|
|
* Alignment of the badge addon element.
|
|
*/
|
|
align?: Align;
|
|
iconTrailing?: FC<{ className?: string }> | ReactNode;
|
|
className?: string;
|
|
}
|
|
|
|
export const BadgeGroup = ({
|
|
children,
|
|
addonText,
|
|
size = "md",
|
|
color = "brand",
|
|
theme = "light",
|
|
align = "leading",
|
|
className,
|
|
iconTrailing: IconTrailing = ArrowRight,
|
|
}: BadgeGroupProps) => {
|
|
const colors = colorClasses[theme][color];
|
|
const sizes = getSizeClasses(theme, !!children, !!IconTrailing)[align][size];
|
|
|
|
const rootClasses = cx(
|
|
"inline-flex w-max cursor-pointer items-center transition duration-100 ease-linear",
|
|
baseClasses[theme].root,
|
|
sizes.root,
|
|
colors.root,
|
|
className,
|
|
);
|
|
const addonClasses = cx("inline-flex items-center", baseClasses[theme].addon, sizes.addon, colors.addon);
|
|
const dotClasses = cx("inline-block size-2 shrink-0 rounded-full", sizes.dot, colors.dot);
|
|
const iconClasses = cx(baseClasses[theme].icon, sizes.icon, colors.icon);
|
|
|
|
if (align === "trailing") {
|
|
return (
|
|
<div className={rootClasses}>
|
|
{theme === "modern" && <span className={dotClasses} />}
|
|
|
|
{children}
|
|
|
|
<span className={addonClasses}>
|
|
{addonText}
|
|
|
|
{/* Trailing icon */}
|
|
{isReactComponent(IconTrailing) && <IconTrailing className={iconClasses} />}
|
|
{isValidElement(IconTrailing) && IconTrailing}
|
|
</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className={rootClasses}>
|
|
<span className={addonClasses}>
|
|
{theme === "modern" && <span className={dotClasses} />}
|
|
{addonText}
|
|
</span>
|
|
|
|
{children}
|
|
|
|
{/* Trailing icon */}
|
|
{isReactComponent(IconTrailing) && <IconTrailing className={iconClasses} />}
|
|
{isValidElement(IconTrailing) && IconTrailing}
|
|
</div>
|
|
);
|
|
};
|