All files / components/three KoreanText.tsx

100% Statements 17/17
100% Branches 23/23
100% Functions 6/6
100% Lines 13/13

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113                                                                                        2x                       227x 227x   137x   1x   3x     86x         227x                   227x             227x         227x             227x                     2x  
/**
 * KoreanText - Three.js-compatible text component with bilingual support
 * 
 * Displays Korean and English text with cyberpunk styling
 * 
 * @module components/three
 */
 
import { Html } from "@react-three/drei";
import React, { useMemo } from "react";
import { KOREAN_COLORS, FONT_FAMILY } from "../../types/constants";
import { hexToRgbaString } from "../../utils/colorUtils";
 
/**
 * Props for KoreanText component
 */
export interface KoreanTextProps {
  readonly korean: string;
  readonly english: string;
  readonly position?: [number, number, number];
  readonly size?: "small" | "medium" | "large" | "xlarge";
  readonly color?: number;
  readonly align?: "left" | "center" | "right";
  readonly weight?: "normal" | "bold";
  readonly layout?: "vertical" | "horizontal";
  readonly testId?: string;
}
 
/**
 * KoreanText Component
 * 
 * A bilingual text component with Korean cyberpunk styling.
 * Supports both vertical (stacked) and horizontal (inline) layouts.
 * 
 * @example
 * ```tsx
 * <KoreanText
 *   korean="공격"
 *   english="Attack"
 *   size="large"
 *   layout="vertical"
 * />
 * ```
 */
export const KoreanText: React.FC<KoreanTextProps> = ({
  korean,
  english,
  position = [0, 0, 0],
  size = "medium",
  color = KOREAN_COLORS.TEXT_PRIMARY,
  align = "center",
  weight = "normal",
  layout = "vertical",
  testId,
}) => {
  // Memoize font sizes for performance
  const fontSize = useMemo(() => {
    switch (size) {
      case "small":
        return { korean: "14px", english: "12px" };
      case "large":
        return { korean: "24px", english: "18px" };
      case "xlarge":
        return { korean: "32px", english: "24px" };
      case "medium":
      default:
        return { korean: "18px", english: "14px" };
    }
  }, [size]);
 
  // Memoize text styles for performance
  const textStyle = useMemo<React.CSSProperties>(() => ({
    color: hexToRgbaString(color),
    fontFamily: FONT_FAMILY.KOREAN,
    textAlign: align,
    fontWeight: weight === "bold" ? "bold" : "normal",
    textShadow: `0 2px 4px ${hexToRgbaString(KOREAN_COLORS.BLACK_SOLID, 0.5)}`,
    userSelect: "none",
    WebkitUserSelect: "none",
  }), [color, align, weight]);
 
  const containerStyle = useMemo<React.CSSProperties>(() => ({
    display: "flex",
    flexDirection: layout === "vertical" ? "column" : "row",
    alignItems: "center",
    gap: layout === "vertical" ? "4px" : "8px",
  }), [layout]);
 
  const koreanStyle = useMemo<React.CSSProperties>(() => ({
    ...textStyle,
    fontSize: fontSize.korean,
  }), [textStyle, fontSize.korean]);
 
  const englishStyle = useMemo<React.CSSProperties>(() => ({
    ...textStyle,
    fontSize: fontSize.english,
    opacity: 0.8,
    fontStyle: "italic",
  }), [textStyle, fontSize.english]);
 
  return (
    <Html position={position} center>
      <div style={containerStyle} data-testid={testId ?? "korean-text"}>
        <span style={koreanStyle}>{korean}</span>
        {layout === "vertical" && <span style={englishStyle}>{english}</span>}
        {layout === "horizontal" && <span style={englishStyle}>| {english}</span>}
      </div>
    </Html>
  );
};
 
KoreanText.displayName = "KoreanText";