From 47554cdd2711e160f491469970310d049ef1f6f1 Mon Sep 17 00:00:00 2001 From: Frank Schwenk Date: Fri, 20 Jun 2025 10:10:32 +0200 Subject: [PATCH] feat: Implement Player 3 input step - Implemented the third step of the new game creation wizard for Player 3's name input. - The step is optional and includes a 'Skip' button. - Includes autosuggestions from player history and quick-pick buttons. - Aligned styling and layout with previous steps, including fixes for button alignment. - Closes #8 --- .gitea | 2 +- src/components/App.jsx | 13 ++-- src/components/NewGame.jsx | 148 ++++++++++++++++++++++++++++++++++++- 3 files changed, 155 insertions(+), 8 deletions(-) diff --git a/.gitea b/.gitea index 9d32448..ea6732c 100644 --- a/.gitea +++ b/.gitea @@ -1 +1 @@ -@https://gitea.schwenk.online/froxxxy/bscscore/issues/1 \ No newline at end of file +@https://gitea.schwenk.online/froxxxy/bscscore/issues/8 \ No newline at end of file diff --git a/src/components/App.jsx b/src/components/App.jsx index 469e0bd..7437206 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -2,7 +2,7 @@ import { h } from 'preact'; import { useState, useEffect, useCallback } from 'preact/hooks'; import GameList from './GameList.jsx'; import GameDetail from './GameDetail.jsx'; -import { Player1Step, Player2Step } from './NewGame.jsx'; +import { Player1Step, Player2Step, Player3Step } from './NewGame.jsx'; import Modal from './Modal.jsx'; import ValidationModal from './ValidationModal.jsx'; import GameCompletionModal from './GameCompletionModal.jsx'; @@ -147,7 +147,6 @@ const App = () => { setNewGameData(data => ({ ...data, player2: name })); setNewGameStep('player3'); }; - // Placeholder handlers for further steps const handlePlayer3Next = (name) => { setNewGameData(data => ({ ...data, player3: name })); setNewGameStep('gameType'); @@ -207,10 +206,12 @@ const App = () => { /> )} {newGameStep === 'player3' && ( -
-

Player 3 Step (TODO)

- -
+ setNewGameStep('player2')} + initialValue={newGameData.player3} + /> )} {newGameStep === 'gameType' && (
diff --git a/src/components/NewGame.jsx b/src/components/NewGame.jsx index a989cc4..b847efc 100644 --- a/src/components/NewGame.jsx +++ b/src/components/NewGame.jsx @@ -283,4 +283,150 @@ const Player2Step = ({ playerNameHistory, onNext, onCancel, initialValue = '' }) ); }; -export { Player1Step, Player2Step }; \ No newline at end of file +/** + * Player 3 input step for multi-step game creation wizard. + * @param {object} props + * @param {string[]} props.playerNameHistory + * @param {Function} props.onNext + * @param {Function} props.onCancel + * @param {string} [props.initialValue] + * @returns {import('preact').VNode} + */ +const Player3Step = ({ playerNameHistory, onNext, onCancel, initialValue = '' }) => { + const [player3, setPlayer3] = useState(initialValue); + const [filteredNames, setFilteredNames] = useState(playerNameHistory); + const inputRef = useRef(null); + + useEffect(() => { + if (!player3) { + setFilteredNames(playerNameHistory); + } else { + setFilteredNames( + playerNameHistory.filter(name => + name.toLowerCase().includes(player3.toLowerCase()) + ) + ); + } + }, [player3, playerNameHistory]); + + const handleSubmit = (e) => { + e.preventDefault(); + // Player 3 is optional, so always allow submission + onNext(player3.trim()); + }; + + const handleQuickPick = (name) => { + onNext(name); + }; + + const handleClear = () => { + setPlayer3(''); + if (inputRef.current) inputRef.current.focus(); + }; + + const handleSkip = (e) => { + e.preventDefault(); + onNext(''); + }; + + return ( +
+
Neues Spiel – Schritt 3/5
+
+ + + + + +
+
+ +
+ setPlayer3(e.target.value)} + autoFocus + autoComplete="off" + aria-label="Name Spieler 3" + style={{ fontSize: '1.2rem', minHeight: 48, marginTop: 12, marginBottom: 12, width: '100%', paddingRight: 44 }} + ref={inputRef} + /> + {player3 && ( + + )} +
+ {filteredNames.length > 0 && ( +
+ {filteredNames.slice(0, 4).map((name, idx) => ( + + ))} +
+ )} +
+
+ +
+ + +
+
+
+ ); +}; + +export { Player1Step, Player2Step, Player3Step }; \ No newline at end of file