BoardConfig.jsx 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. import React from "react";
  2. import { useTranslation } from "react-i18next";
  3. import styled from "styled-components";
  4. import { Form, Field } from "react-final-form";
  5. import AutoSave from "../ui/formUtils/AutoSave";
  6. import Label from "../ui/formUtils/Label";
  7. import useBoardConfig from "./useBoardConfig";
  8. import ImageField from "../ui/formUtils/ImageField";
  9. import Hint from "../ui/formUtils/Hint";
  10. import { Range } from "rc-slider";
  11. import { nanoid } from "nanoid";
  12. const BoardConfigForm = styled.div`
  13. display: flex;
  14. flex-direction: column;
  15. & .trash {
  16. float: right;
  17. }
  18. `;
  19. const BoardConfig = () => {
  20. const { t } = useTranslation();
  21. const [boardConfig, setBoardConfig] = useBoardConfig();
  22. const [defaultPlayerCount] = React.useState([]);
  23. const onSubmitHandler = React.useCallback(
  24. (data) => {
  25. setBoardConfig((prev) => ({
  26. ...prev,
  27. ...data,
  28. }));
  29. },
  30. [setBoardConfig]
  31. );
  32. const addTranslation = React.useCallback(() => {
  33. setBoardConfig((prev) => ({
  34. ...prev,
  35. translations: [...(prev.translations || []), { id: nanoid() }],
  36. }));
  37. }, [setBoardConfig]);
  38. const removeTranslation = (idToRemove) => {
  39. setBoardConfig((prev) => ({
  40. ...prev,
  41. translations: prev.translations.filter(({ id }) => id !== idToRemove),
  42. }));
  43. };
  44. return (
  45. <Form
  46. onSubmit={onSubmitHandler}
  47. render={() => (
  48. <BoardConfigForm>
  49. <AutoSave save={onSubmitHandler} />
  50. <Label>
  51. {t("Publish")}
  52. <Field
  53. name="published"
  54. component="input"
  55. type="checkbox"
  56. initialValue={boardConfig.published}
  57. />
  58. <Hint>{t("Check it to make your board publicly visible")}</Hint>
  59. </Label>
  60. <Label>
  61. {t("Players count")}
  62. <Field
  63. name="playerCount"
  64. initialValue={boardConfig.playerCount || defaultPlayerCount}
  65. >
  66. {({ input: { onChange, value } }) => {
  67. return (
  68. <Range
  69. defaultValue={[2, 4]}
  70. value={value}
  71. min={1}
  72. max={9}
  73. step={1}
  74. marks={{
  75. 1: "1",
  76. 2: "2",
  77. 3: "3",
  78. 4: "4",
  79. 5: "5",
  80. 6: "6",
  81. 7: "7",
  82. 8: "8",
  83. 9: "9+",
  84. }}
  85. style={{ width: "20em" }}
  86. onChange={onChange}
  87. dots
  88. />
  89. );
  90. }}
  91. </Field>
  92. </Label>
  93. <Label>
  94. {t("Duration")}
  95. <Field
  96. name="duration"
  97. initialValue={
  98. Array.isArray(boardConfig.duration)
  99. ? boardConfig.duration
  100. : defaultPlayerCount
  101. }
  102. >
  103. {({ input: { onChange, value } }) => {
  104. return (
  105. <Range
  106. defaultValue={[2, 4]}
  107. value={value}
  108. min={15}
  109. max={90}
  110. step={null}
  111. marks={{
  112. 15: "15",
  113. 30: "30",
  114. 45: "45",
  115. 60: "60",
  116. 90: "90+",
  117. }}
  118. style={{ width: "20em" }}
  119. onChange={onChange}
  120. />
  121. );
  122. }}
  123. </Field>
  124. </Label>
  125. <Label>
  126. {t("Minimal age (years)")}
  127. <Field
  128. name="minAge"
  129. component="input"
  130. initialValue={boardConfig.minAge}
  131. style={{ width: "5em", textAlign: "right" }}
  132. />
  133. </Label>
  134. <Label>
  135. {t("Board size")}
  136. <Field
  137. name="size"
  138. component="input"
  139. initialValue={boardConfig.size}
  140. style={{ width: "5em", textAlign: "right" }}
  141. />
  142. </Label>
  143. <Label>
  144. {t("Magnetic Grid size")}
  145. <Field
  146. name="gridSize"
  147. component="input"
  148. initialValue={boardConfig.gridSize || 1}
  149. style={{ width: "5em", textAlign: "right" }}
  150. />
  151. </Label>
  152. <Label>
  153. {t("Image")}
  154. <Field name="imageUrl" initialValue={boardConfig.imageUrl}>
  155. {({ input: { value, onChange } }) => {
  156. return <ImageField value={value} onChange={onChange} />;
  157. }}
  158. </Field>
  159. </Label>
  160. <Label>
  161. {t("Material language")}
  162. <Field
  163. name="materialLanguage"
  164. component="select"
  165. initialValue={boardConfig.materialLanguage}
  166. style={{ width: "20em" }}
  167. >
  168. <option />
  169. <option value="Multi-lang">🌍 {t("Multi-lang")}</option>
  170. <option value="en">🇬🇧 {t("en")}</option>
  171. <option value="fr">🇫🇷 {t("fr")}</option>
  172. </Field>
  173. </Label>
  174. <fieldset style={{ marginBottom: "2em", paddingBottom: "1em" }}>
  175. <legend>{t("Informations")}</legend>
  176. <Label>
  177. {t("Default language")}
  178. <Field
  179. name="defaultLanguage"
  180. component="select"
  181. initialValue={boardConfig.defaultLanguage}
  182. style={{ width: "15em" }}
  183. >
  184. <option />
  185. <option value="en">🇬🇧 {t("en")}</option>
  186. <option value="fr">🇫🇷 {t("fr")}</option>
  187. </Field>
  188. </Label>
  189. <Label>
  190. {t("Game name")}
  191. <Field
  192. name="defaultName"
  193. component="input"
  194. initialValue={boardConfig.defaultName || boardConfig.name}
  195. style={{ width: "15em" }}
  196. />
  197. </Label>
  198. <Label>
  199. {t("Baseline")}
  200. <Field
  201. name="defaultBaseline"
  202. component="input"
  203. initialValue={boardConfig.defaultBaseline}
  204. style={{ width: "100%" }}
  205. />
  206. </Label>
  207. <Label>
  208. {t("Description")}
  209. <Field
  210. name="defaultDescription"
  211. component="textarea"
  212. initialValue={
  213. boardConfig.defaultDescription || boardConfig.info
  214. }
  215. style={{ minHeight: "10em" }}
  216. />
  217. </Label>
  218. </fieldset>
  219. <div>
  220. {(boardConfig.translations || []).map(({ id }, index) => {
  221. return (
  222. <fieldset
  223. style={{ marginBottom: "2em", paddingBottom: "1em" }}
  224. key={id}
  225. >
  226. <legend>
  227. {t("Translation")} {index}
  228. </legend>
  229. <button
  230. className="button clear icon-only trash"
  231. onClick={() => removeTranslation(id)}
  232. >
  233. <img
  234. src="https://icongr.am/feather/trash.svg?size=25&color=ffffff"
  235. alt={t("Remove")}
  236. />
  237. </button>
  238. <Label>
  239. {t("Language")}
  240. <Field
  241. name={`translations[${index}].language`}
  242. component="select"
  243. initialValue={boardConfig.translations[index].language}
  244. style={{ width: "15em" }}
  245. >
  246. <option />
  247. <option value="en">🇬🇧 {t("en")}</option>
  248. <option value="fr">🇫🇷 {t("fr")}</option>
  249. </Field>
  250. </Label>
  251. <Label>
  252. {t("Game name")}
  253. <Field
  254. name={`translations[${index}].name`}
  255. component="input"
  256. initialValue={boardConfig.translations[index].name}
  257. style={{ width: "15em" }}
  258. />
  259. </Label>
  260. <Label>
  261. {t("Baseline")}
  262. <Field
  263. name={`translations[${index}].baseline`}
  264. component="input"
  265. initialValue={boardConfig.translations[index].baseline}
  266. style={{ width: "100%" }}
  267. />
  268. </Label>
  269. <Label>
  270. {t("Description")}
  271. <Field
  272. name={`translations[${index}].description`}
  273. component="textarea"
  274. initialValue={boardConfig.translations[index].description}
  275. style={{ minHeight: "10em" }}
  276. />
  277. </Label>
  278. </fieldset>
  279. );
  280. })}
  281. <button onClick={addTranslation}>{t("Add translation")}</button>
  282. </div>
  283. </BoardConfigForm>
  284. )}
  285. />
  286. );
  287. };
  288. export default BoardConfig;