# All files / src/components/features TranslationPane.tsx

0% Statements 0/5
0% Branches 0/18
0% Functions 0/2
0% Lines 0/5

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

     import { useTranslation } from 'react-i18next';

    interface TranslationPaneProps {
      text: string;
      onChange: (text: string) => void;
      onClear: () => void;
      placeholder?: string;
      maxLength?: number;
      remainingChars?: number;
    }

    function TranslationPane({
      text, onChange, onClear, placeholder,
      maxLength, remainingChars,
    }: TranslationPaneProps) {
      const { t } = useTranslation();
      const usagePercent = maxLength && maxLength > 0
        ? Math.min(100, (text.length / maxLength) * 100)
        : 0;
      const isNearLimit = remainingChars !== undefined && remainingChars < Infinity && remainingChars < 1000;

      return (
        <div className="flex flex-col h-full">
          {/* Header */}
          <div className="flex items-center justify-between mb-3">
            <h3 className="text-[14px] font-semibold text-text-primary">{t('home.title')}</h3>
            <div className="flex items-center gap-3">
              {text && (
                <button
                  onClick={onClear}
                  className="text-[12px] text-text-tertiary hover:text-accent transition-colors"
                >
                  {t('home.actions.clear')}
                </button>
              )}
              <span className="text-[12px] text-text-tertiary font-mono">
                {text.length.toLocaleString()}
                {maxLength && (
                  <span className="text-text-placeholder"> / {maxLength.toLocaleString()}</span>
                )}
              </span>
            </div>
          </div>

          {/* Text area */}
          <textarea
            value={text}
            onChange={e => onChange(e.target.value)}
            placeholder={placeholder ?? t('home.inputPlaceholder')}
            maxLength={maxLength}
            className="
              flex-1 w-full resize-none bg-transparent
              text-text-primary text-[17px] leading-relaxed
              placeholder:text-text-placeholder
              focus:outline-none
              min-h-[280px]
            "
          />

          {/* Quota bar */}
          {remainingChars !== undefined && remainingChars < Infinity && (
            <div className="mt-3 pt-3 border-t border-divider">
              <div className="w-full h-1 rounded-full overflow-hidden bg-gray-100 dark:bg-gray-200">
                <div
                  className="h-full rounded-full bg-gradient-brand transition-all duration-500"
                  style={{ width: `${Math.min(100, usagePercent)}%` }}
                />
              </div>
              <p className={`text-[11px] mt-1 font-mono ${isNearLimit ? 'text-yellow' : 'text-text-tertiary'}`}>
                {t('home.quota.remaining')}: {remainingChars.toLocaleString()}
              </p>
            </div>
          )}
        </div>
      );
    }

    export { TranslationPane };