import React, {forwardRef, useState} from 'react';
import {useUnit} from "effector-react/compat";

import * as styles from './symbol-input.module.css';
import {TEXT_REGEX} from "@/shared/constants/index.js";
import {setSymbol} from "@/features/input-word/model/events.js";

const CSS_CLASS_OPENED = styles['symbol-opened']

export const SymbolInput = forwardRef((
  {idx, focusNext, focusPrev, value, isRevealed = false},
  ref
) => {
  const triggerSetSymbol = useUnit(setSymbol);
  const symbolClasses = `${styles.symbol} ${isRevealed ? CSS_CLASS_OPENED : ''}`;

  const handleInput = (key) => {
    const formattedKey = key.toLowerCase();

    if(TEXT_REGEX.test(formattedKey) && formattedKey.length === 1) {
      triggerSetSymbol({value: formattedKey, idx});
      focusNext(idx);
    }
  };

  const handleKeyDown = (e) => {
    switch (e.key) {
      case 'ArrowLeft': focusPrev(idx); break;
      case 'ArrowRight': focusNext(idx); break;
      case 'Backspace':
        e.preventDefault();
        triggerSetSymbol({value: '', idx});
        focusPrev(idx);
        break;
      default: handleInput(e.key); break;
    }
  };

  return (
    <input
      ref={ref}
      type={'text'}
      className={symbolClasses}
      disabled={isRevealed}
      maxLength={1}
      value={value}
      onKeyDown={handleKeyDown}
    >
    </input>
  );
})import {useUnit} from 'effector-react';
import {useRef} from 'react';

import {SymbolInput} from '@/features/input-word/ui/symbol-input/index.jsx';
import * as styles from './word-input.module.css';
import {$userGuess} from '@/features/input-word/model/store.js';

export const WordInput = ({}) => {
  const userGuess = useUnit($userGuess);
  const inputRefs = useRef([]);

  const focusNextUnopenedSymbol = (currentIdx, step) => {
    for(let i = currentIdx + step; i >= 0 && i < userGuess.length; i += step) {
      if(!userGuess[i].isRevealed) {
        inputRefs.current[i].focus();
        return;
      }
    }

    inputRefs.current[currentIdx].focus();
  };

  const handleEnter = () => {

  }

  const focusNextSymbol = (currentIdx) => {
    focusNextUnopenedSymbol(currentIdx, 1);
  }

  const focusPrevSymbol = (currentIdx) => {
    focusNextUnopenedSymbol(currentIdx, -1);
  }

  return (
    <div className={styles.wordDisplay}>
      { userGuess.map(({idx, value, isRevealed}) =>
        <SymbolInput
          key={`${value}-${idx}`}
          ref={el => inputRefs.current[idx] = el}
          idx={idx}
          value={value}
          isRevealed={isRevealed}
          focusNext={focusNextSymbol}
          focusPrev={focusPrevSymbol}
        />
      )}
    </div>
  );
}; import { useEffect, useRef } from 'react';
import { useUnit } from 'effector-react';

import { WordInput } from '@/features/input-word/ui/word-input/index.jsx';
import {
  iterateReveal,
  startGame
} from '@/pages/game-page/model/events.js';
import {$canReveal} from '../model/store.js';
import { ProgressBar } from '@/shared/ui/progress-bar';
import * as styles from './game-page.module.css';

export const GamePage = () => {
  const progressBarRef = useRef(null);
  const triggerStartGame = useUnit(startGame);
  const triggerReveal = useUnit(iterateReveal);

  useEffect(() => {
    triggerStartGame();
    progressBarRef.current.start();
  }, []);

  const onProgressEnd = () => {
    triggerReveal();

    const canReveal = $canReveal.getState();
    if(!canReveal) return;
    progressBarRef.current.start();
  }

  return (
    <div className={styles.page}>
      <WordInput />
      <ProgressBar
        ref={progressBarRef}
        fillingDuration={30000}
        onProgressEnd={onProgressEnd}
      />
    </div>
  );
}; где лучше отлавливать enter чтобы сбрасывать progressBar и открывать символы