fuzzyworld

word-waltz / files / src / components / WordPicker.ts

WordPicker.ts

2.0 KB · history · blame

1/**
2 * WordPicker — word selection UI
3 *
4 * Shown to the challenger before a round. The player types a 5-letter word
5 * to set as the secret challenge for their opponent.
6 */
7
8import { isValidAnswer } from '../utils/wordList.js';
9
10interface WordPickerProps {
11 onWordChosen: (word: string) => void;
12}
13
14export function mountWordPicker(container: HTMLElement, props: WordPickerProps): () => void {
15 const root = document.createElement('div');
16 root.className = 'word-picker';
17 root.innerHTML = `
18 <p class="word-picker__prompt">Pick a 5-letter word to challenge your opponent.</p>
19 <form class="word-picker__form" autocomplete="off">
20 <input
21 class="word-picker__input"
22 type="text"
23 maxlength="5"
24 placeholder="WORD"
25 spellcheck="false"
26 autocorrect="off"
27 autocapitalize="characters"
28 required
29 />
30 <button class="word-picker__submit" type="submit">Challenge!</button>
31 </form>
32 <p class="word-picker__error" aria-live="polite"></p>
33 `;
34
35 const input = root.querySelector<HTMLInputElement>('.word-picker__input')!;
36 const error = root.querySelector<HTMLParagraphElement>('.word-picker__error')!;
37 const btn = root.querySelector<HTMLButtonElement>('.word-picker__submit')!;
38
39 // Force uppercase as the user types
40 input.addEventListener('input', () => {
41 const pos = input.selectionStart ?? input.value.length;
42 input.value = input.value.toUpperCase().replace(/[^A-Z]/g, '');
43 input.setSelectionRange(pos, pos);
44 error.textContent = '';
45 });
46
47 root.querySelector('form')!.addEventListener('submit', (e) => {
48 e.preventDefault();
49 const word = input.value.toUpperCase();
50 if (word.length !== 5) { error.textContent = 'Must be exactly 5 letters.'; return; }
51 if (!isValidAnswer(word)) { error.textContent = 'Not in the word list — try another.'; return; }
52 btn.disabled = true;
53 props.onWordChosen(word);
54 });
55
56 container.appendChild(root);
57 input.focus();
58
59 return function cleanup() {
60 container.removeChild(root);
61 };
62}