refactor: streamline new-game player selection ux
Consolidate new-game wizard steps into reusable components and remove legacy duplicate files. Replace inline player inputs with a chip-first flow and minimal name-entry modal, while improving compact-screen spacing and step-specific selection behavior. Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import { h } from 'preact';
|
||||
import { useEffect, useRef, useState } from 'preact/hooks';
|
||||
import styles from './NameEntryModal.module.css';
|
||||
import newGameStyles from '../NewGame.module.css';
|
||||
|
||||
interface NameEntryModalProps {
|
||||
open: boolean;
|
||||
title: string;
|
||||
placeholder: string;
|
||||
inputId: string;
|
||||
initialValue?: string;
|
||||
enterKeyHint?: 'next' | 'done';
|
||||
onClose: () => void;
|
||||
onConfirm: (name: string) => string | null;
|
||||
}
|
||||
|
||||
export function NameEntryModal({
|
||||
open,
|
||||
title,
|
||||
placeholder,
|
||||
inputId,
|
||||
initialValue = '',
|
||||
enterKeyHint = 'done',
|
||||
onClose,
|
||||
onConfirm,
|
||||
}: NameEntryModalProps) {
|
||||
const [name, setName] = useState(initialValue);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
setName(initialValue);
|
||||
setError(null);
|
||||
window.setTimeout(() => inputRef.current?.focus(), 0);
|
||||
}, [open, initialValue]);
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
return (
|
||||
<div className={styles.modalOverlay} onClick={onClose} role="dialog" aria-modal="true" aria-labelledby={`${inputId}-title`}>
|
||||
<div className={styles.modalContent} onClick={(e) => e.stopPropagation()}>
|
||||
<h3 id={`${inputId}-title`} className={styles.modalTitle}>
|
||||
{title}
|
||||
</h3>
|
||||
|
||||
<input
|
||||
id={inputId}
|
||||
ref={inputRef}
|
||||
className={`${newGameStyles['name-input']} ${styles.modalInput}`}
|
||||
value={name}
|
||||
placeholder={placeholder}
|
||||
autoComplete="off"
|
||||
autoCapitalize="words"
|
||||
spellCheck={false}
|
||||
enterKeyHint={enterKeyHint}
|
||||
onInput={(e: Event) => {
|
||||
setName((e.target as HTMLInputElement).value);
|
||||
if (error) setError(null);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key !== 'Enter') return;
|
||||
e.preventDefault();
|
||||
const validationError = onConfirm(name);
|
||||
if (validationError) setError(validationError);
|
||||
}}
|
||||
/>
|
||||
|
||||
{error && <p className={styles.errorText}>{error}</p>}
|
||||
|
||||
<div className={styles.modalActions}>
|
||||
<button type="button" className={styles.actionButton} onClick={onClose}>
|
||||
Abbrechen
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`${styles.actionButton} ${styles.confirmButton}`}
|
||||
onClick={() => {
|
||||
const validationError = onConfirm(name);
|
||||
if (validationError) setError(validationError);
|
||||
}}
|
||||
>
|
||||
Übernehmen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user