"use client"; import { memo, useMemo } from "react"; import Image from "next/image"; import TextAnimation from "./text/TextAnimation"; import Button from "./button/Button"; import Tag from "./shared/Tag"; import AvatarGroup from "./shared/AvatarGroup"; import { cls } from "@/lib/utils"; import { getButtonProps } from "@/lib/buttonUtils"; import { LucideIcon } from "lucide-react"; import type { AnimationType } from "./text/types"; import { useTheme } from "@/providers/themeProvider/ThemeProvider"; import type { ButtonConfig } from "@/types/button"; import type { Avatar } from "./shared/AvatarGroup"; import type { TextboxLayout, InvertedBackground } from "@/providers/themeProvider/config/constants"; type TitleSegment = | { type: "text"; content: string } | { type: "image"; src: string; alt?: string }; interface TextBoxProps { title: string; titleSegments?: TitleSegment[]; description: string; type?: AnimationType; textboxLayout?: TextboxLayout; useInvertedBackground?: InvertedBackground; className?: string; titleClassName?: string; titleImageWrapperClassName?: string; titleImageClassName?: string; descriptionClassName?: string; duration?: number; start?: string; end?: string; gradientColors?: { from: string; to: string; }; children?: React.ReactNode; center?: boolean; tag?: string; tagIcon?: LucideIcon; tagClassName?: string; buttons?: ButtonConfig[]; buttonContainerClassName?: string; buttonClassName?: string; buttonTextClassName?: string; avatars?: Avatar[]; avatarText?: string; avatarGroupClassName?: string; } const TextBox = ({ title, titleSegments, description, type, textboxLayout = "default", useInvertedBackground, className = "", titleClassName = "", titleImageWrapperClassName = "", titleImageClassName = "", descriptionClassName = "", duration = 1, start = "top 80%", end = "top 20%", gradientColors, children, center = false, tag, tagIcon: TagIcon, tagClassName = "", buttons, buttonContainerClassName = "", buttonClassName = "", buttonTextClassName = "", avatars, avatarText, avatarGroupClassName = "", }: TextBoxProps) => { const theme = useTheme(); // Shared tag component const tagElement = useMemo(() => tag && ( ), [tag, TagIcon, useInvertedBackground, textboxLayout, tagClassName]); // Shared title component const titleElement = useMemo(() => ( ), [type, theme.defaultTextAnimation, title, textboxLayout, center, useInvertedBackground, titleClassName, duration, start, end, gradientColors]); // Inline image title component (used when textboxLayout === "inline-image") const inlineImageTitleElement = useMemo(() => titleSegments && titleSegments.length > 0 ? (

{titleSegments.map((segment, index) => { const imageIndex = titleSegments .slice(0, index + 1) .filter(s => s.type === "image").length - 1; const element = segment.type === "text" ? ( {segment.content} ) : (
{segment.alt
); return ( {index > 0 && " "} {element} ); })}

) : null, [titleSegments, useInvertedBackground, titleClassName, titleImageWrapperClassName, titleImageClassName]); // Shared description component const descriptionElement = useMemo(() => ( ), [type, theme.defaultTextAnimation, description, center, textboxLayout, useInvertedBackground, descriptionClassName, duration, start, end, gradientColors]); // Shared avatars component const avatarsElement = useMemo(() => avatars && avatars.length > 0 ? ( ) : null, [avatars, avatarText, textboxLayout, center, avatarGroupClassName]); // Shared buttons/children component const actionsElement = useMemo(() => buttons && buttons.length > 0 ? (
{/* Limit to 2 buttons for optimal layout */} {buttons.slice(0, 2).map((button, index) => (
) : ( children ), [buttons, textboxLayout, center, buttonContainerClassName, theme.defaultButtonVariant, buttonClassName, buttonTextClassName, children]); // Split layout if (textboxLayout === "split") { return (
{tagElement} {titleElement} {descriptionElement}
{actionsElement}
); } // Split actions layout - tag and buttons required, no description if (textboxLayout === "split-actions") { return (
{tagElement} {titleElement}
{actionsElement}
); } // Split description layout - tag + title left, description only right (no buttons) if (textboxLayout === "split-description") { return (
{tagElement} {titleElement}
{descriptionElement}
); } // Inline image layout - centered heading with inline images and optional buttons if (textboxLayout === "inline-image") { return (
{inlineImageTitleElement} {actionsElement}
); } // Default layout return (
{tagElement} {titleElement} {descriptionElement} {actionsElement} {avatarsElement}
); }; TextBox.displayName = "TextBox"; export default memo(TextBox);