ItemForm.jsx 4.9 KB

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