ItemForm.jsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import React from "react";
  2. import { useTranslation } from "react-i18next";
  3. import { Field } from "react-final-form";
  4. import Label from "../ui/formUtils/Label";
  5. import Hint from "../ui/formUtils/Hint";
  6. import Slider from "../ui/Slider";
  7. import ActionsField from "./forms/ActionsField";
  8. import { itemTemplates } from "./";
  9. import useGameItemActions from "./useGameItemActions";
  10. export const getFormFieldComponent = (type) => {
  11. if (type in itemTemplates) {
  12. return itemTemplates[type].form;
  13. }
  14. return () => null;
  15. };
  16. const getDefaultActionsFromItem = (item) => {
  17. if (item.type in itemTemplates) {
  18. const actions = itemTemplates[item.type].defaultActions;
  19. if (typeof actions === "function") {
  20. return actions(item);
  21. }
  22. return actions;
  23. }
  24. return [];
  25. };
  26. const getAvailableActionsFromItem = (item) => {
  27. if (item.type in itemTemplates) {
  28. const actions = itemTemplates[item.type].availableActions;
  29. if (typeof actions === "function") {
  30. return actions(item);
  31. }
  32. return actions;
  33. }
  34. return [];
  35. };
  36. const ItemForm = ({ items }) => {
  37. const { t } = useTranslation();
  38. const { actionMap } = useGameItemActions();
  39. const [defaultActions] = React.useState(() =>
  40. getDefaultActionsFromItem(items[0])
  41. );
  42. const [availableActions] = React.useState(() =>
  43. getAvailableActionsFromItem(items[0])
  44. );
  45. let FieldsComponent;
  46. if (items.length === 1) {
  47. FieldsComponent = getFormFieldComponent(items[0].type);
  48. } else {
  49. const types = new Set(items.map(({ type }) => type));
  50. if (types.size === 1) {
  51. FieldsComponent = getFormFieldComponent(Array.from(types)[0]);
  52. } else {
  53. FieldsComponent = () => null;
  54. }
  55. }
  56. let initialValues;
  57. // Set initial values to item values if only one element selected
  58. // Empty object otherwise
  59. if (items.length === 1) {
  60. initialValues = { ...items[0] };
  61. initialValues.actions = initialValues.actions || defaultActions;
  62. } else {
  63. initialValues = {};
  64. }
  65. return (
  66. <>
  67. <Label>
  68. <Field
  69. name="locked"
  70. component="input"
  71. type="checkbox"
  72. initialValue={initialValues.locked}
  73. />
  74. <span className="checkable">{t("Locked?")}</span>
  75. <Hint>{t("Lock action help")}</Hint>
  76. </Label>
  77. <Label>
  78. {t("Rotation")}
  79. <Field name="rotation" initialValue={initialValues.rotation}>
  80. {({ input: { onChange, value } }) => {
  81. return (
  82. <Slider
  83. defaultValue={0}
  84. value={value}
  85. min={-180}
  86. max={180}
  87. step={5}
  88. included={false}
  89. marks={{
  90. "-180": -180,
  91. "-90": -90,
  92. "-45": -45,
  93. "-30": -30,
  94. 0: 0,
  95. 30: 30,
  96. 45: 45,
  97. 90: 90,
  98. 180: 180,
  99. }}
  100. onChange={onChange}
  101. className={"slider-rotation"}
  102. />
  103. );
  104. }}
  105. </Field>
  106. </Label>
  107. <Label>
  108. {t("Layer")}
  109. <Field name="layer" initialValue={initialValues.layer}>
  110. {({ input: { onChange, value } }) => {
  111. return (
  112. <Slider
  113. defaultValue={0}
  114. value={value}
  115. min={-3}
  116. max={3}
  117. step={1}
  118. included={false}
  119. marks={{
  120. "-3": -3,
  121. "-2": -2,
  122. "-1": -1,
  123. 0: 0,
  124. "1": 1,
  125. "2": 2,
  126. "3": 3,
  127. }}
  128. onChange={onChange}
  129. className={"slider-layer"}
  130. />
  131. );
  132. }}
  133. </Field>
  134. </Label>
  135. <FieldsComponent initialValues={initialValues} />
  136. <h3>{t("Snap to grid")}</h3>
  137. <Label>
  138. {t("Grid type")}
  139. <Field
  140. name="grid.type"
  141. initialValue={initialValues.grid?.type}
  142. component="select"
  143. >
  144. <option value="">{t("None")}</option>
  145. <option value="grid">{t("Grid")}</option>
  146. <option value="hexH">{t("Horizontal hexagons")}</option>
  147. <option value="hexV">{t("Vertical hexagons")}</option>
  148. </Field>
  149. </Label>
  150. <Label>
  151. {t("Size")}
  152. <Field
  153. name="grid.size"
  154. component="input"
  155. initialValue={initialValues.grid?.size}
  156. >
  157. {(props) => <input {...props.input} type="number" />}
  158. </Field>
  159. </Label>
  160. <h3>{t("Available actions")}</h3>
  161. <Label>
  162. <Field name="actions" initialValue={initialValues.actions}>
  163. {({ input: { onChange, value } }) => (
  164. <ActionsField
  165. onChange={onChange}
  166. value={value}
  167. availableActions={availableActions}
  168. actionMap={actionMap}
  169. />
  170. )}
  171. </Field>
  172. </Label>
  173. </>
  174. );
  175. };
  176. export default React.memo(ItemForm);