1import { Button, ButtonGroup, createStyles, makeStyles, TextField, Theme } from '@material-ui/core';
2import * as React from 'react';
3import { Controller, useForm } from 'react-hook-form';
4
5import { nameofFactory, noComplete } from '../common';
6
7export interface LoginFormData {
8 nickname: string;
9 roomName: string;
10 roomPass: string;
11 create: boolean;
12}
13
14const formName = nameofFactory<LoginFormData>();
15
16const useStyles = makeStyles((theme: Theme) =>
17 createStyles({
18 padBottom: {
19 marginBottom: theme.spacing(2),
20 },
21 label: {
22 color: theme.palette.text.secondary + ' !important',
23 },
24 })
25);
26
27export interface LoginFormProps {
28 existingRoom: boolean;
29 onSubmit: (data: LoginFormData) => Promise<void>;
30 errorMessage?: string;
31}
32
33// Sync rules with protocol.go.
34export function LoginForm(props: LoginFormProps) {
35 const classes = useStyles();
36 const { control, handleSubmit, errors, setValue, register } = useForm<LoginFormData>({});
37 React.useEffect(() => register({ name: formName('create') }), [register]);
38 const doSubmit = handleSubmit(props.onSubmit);
39
40 return (
41 <form onSubmit={(e) => e.preventDefault()}>
42 {props.existingRoom ? (
43 <div>
44 <em>Joining existing game; please choose a nickname.</em>
45 </div>
46 ) : null}
47
48 <div className={props.existingRoom ? classes.padBottom : undefined}>
49 <Controller
50 control={control}
51 as={TextField}
52 name={formName('nickname')}
53 label="Nickname"
54 defaultValue=""
55 error={!!errors.nickname}
56 rules={{ required: true, minLength: 1, maxLength: 16 }}
57 fullWidth={true}
58 inputProps={noComplete}
59 autoFocus
60 InputLabelProps={{ classes: { focused: classes.label } }}
61 />
62 </div>
63
64 {props.existingRoom ? null : (
65 <>
66 <div>
67 <Controller
68 control={control}
69 as={TextField}
70 name={formName('roomName')}
71 label="Room name"
72 defaultValue=""
73 error={!!errors.roomName}
74 rules={{ required: true, minLength: 1, maxLength: 20 }}
75 fullWidth={true}
76 inputProps={noComplete}
77 InputLabelProps={{ classes: { focused: classes.label } }}
78 />
79 </div>
80
81 <div className={classes.padBottom}>
82 <Controller
83 control={control}
84 as={TextField}
85 name={formName('roomPass')}
86 label="Password"
87 defaultValue=""
88 type="password"
89 error={!!errors.roomPass}
90 rules={{ required: true, minLength: 1 }}
91 fullWidth={true}
92 inputProps={noComplete}
93 InputLabelProps={{ classes: { focused: classes.label } }}
94 />
95 </div>
96 </>
97 )}
98
99 {props.errorMessage && (
100 <div className={classes.padBottom}>
101 <em>{props.errorMessage}</em>
102 </div>
103 )}
104
105 <div>
106 <ButtonGroup variant="contained">
107 <Button
108 type="submit"
109 onClick={() => {
110 setValue(formName('create'), false);
111 doSubmit();
112 }}
113 >
114 Join game
115 </Button>
116
117 {props.existingRoom ? null : (
118 <Button
119 type="button"
120 onClick={() => {
121 setValue(formName('create'), true);
122 doSubmit();
123 }}
124 >
125 Create new game
126 </Button>
127 )}
128 </ButtonGroup>
129 </div>
130 </form>
131 );
132}