BoardConfig.js 9.2 KB

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