ed7c6232c1
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
90 lines
2.6 KiB
TypeScript
90 lines
2.6 KiB
TypeScript
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>
|
|
);
|
|
}
|