feat: optimize player selection for touch input

- Increases the number of quick-pick buttons from 4 to 10.
- Adds a '...' button that appears when more than 10 players exist in history.
- Clicking '...' opens a scrollable modal listing all past players for easy selection.
- This provides a much faster player selection flow on touch devices.

Closes #4
This commit is contained in:
Frank Schwenk
2025-06-20 11:03:37 +02:00
parent b466dd2a0a
commit dbc173f57b
2 changed files with 116 additions and 3 deletions

View File

@@ -1,6 +1,25 @@
import { h } from 'preact';
import { useState, useEffect, useRef } from 'preact/hooks';
import styles from './NewGame.module.css';
import modalStyles from './PlayerSelectModal.module.css';
const PlayerSelectModal = ({ players, onSelect, onClose }) => (
<div className={modalStyles.modalOverlay} onClick={onClose}>
<div className={modalStyles.modalContent} onClick={e => e.stopPropagation()}>
<div className={modalStyles.modalHeader}>
<h3>Alle Spieler</h3>
<button className={modalStyles.closeButton} onClick={onClose}>×</button>
</div>
<div className={modalStyles.playerList}>
{players.map(player => (
<button key={player} className={modalStyles.playerItem} onClick={() => onSelect(player)}>
{player}
</button>
))}
</div>
</div>
</div>
);
/**
* Player 1 input step for multi-step game creation wizard.
@@ -15,6 +34,7 @@ const Player1Step = ({ playerNameHistory, onNext, onCancel, initialValue = '' })
const [player1, setPlayer1] = useState(initialValue);
const [error, setError] = useState(null);
const [filteredNames, setFilteredNames] = useState(playerNameHistory);
const [isModalOpen, setIsModalOpen] = useState(false);
const inputRef = useRef(null);
useEffect(() => {
@@ -44,6 +64,11 @@ const Player1Step = ({ playerNameHistory, onNext, onCancel, initialValue = '' })
onNext(name);
};
const handleModalSelect = (name) => {
setIsModalOpen(false);
handleQuickPick(name);
};
const handleClear = () => {
setPlayer1('');
setError(null);
@@ -103,7 +128,7 @@ const Player1Step = ({ playerNameHistory, onNext, onCancel, initialValue = '' })
</div>
{filteredNames.length > 0 && (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 12, marginTop: 8 }}>
{filteredNames.slice(0, 4).map((name, idx) => (
{filteredNames.slice(0, 10).map((name, idx) => (
<button
type="button"
key={name + idx}
@@ -115,10 +140,28 @@ const Player1Step = ({ playerNameHistory, onNext, onCancel, initialValue = '' })
{name}
</button>
))}
{playerNameHistory.length > 10 && (
<button
type="button"
className={styles['quick-pick-btn']}
style={{ fontSize: '1.1rem', padding: '12px 20px', borderRadius: 8, background: '#333', color: '#fff', border: 'none', cursor: 'pointer' }}
onClick={() => setIsModalOpen(true)}
aria-label="Weitere Spieler anzeigen"
>
...
</button>
)}
</div>
)}
</div>
{error && <div className={styles['validation-error']} style={{ marginBottom: 16 }}>{error}</div>}
{isModalOpen && (
<PlayerSelectModal
players={playerNameHistory}
onSelect={handleModalSelect}
onClose={() => setIsModalOpen(false)}
/>
)}
<div className={styles['arrow-nav']} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 48 }}>
<button
type="button"
@@ -244,7 +287,7 @@ const Player2Step = ({ playerNameHistory, onNext, onCancel, initialValue = '' })
</div>
{filteredNames.length > 0 && (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 12, marginTop: 8 }}>
{filteredNames.slice(0, 4).map((name, idx) => (
{filteredNames.slice(0, 10).map((name, idx) => (
<button
type="button"
key={name + idx}
@@ -381,7 +424,7 @@ const Player3Step = ({ playerNameHistory, onNext, onCancel, initialValue = '' })
</div>
{filteredNames.length > 0 && (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 12, marginTop: 8 }}>
{filteredNames.slice(0, 4).map((name, idx) => (
{filteredNames.slice(0, 10).map((name, idx) => (
<button
type="button"
key={name + idx}