list.js 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import Field from './_field';
  2. import { Input, NeoButton } from 'app/design/controls'
  3. import { useEffect, useState } from 'react';
  4. import { View, Row } from 'app/design/view'
  5. import { strToObj } from 'app/lib/util';
  6. import { useFormField } from 'app/lib/form/use-form-field';
  7. import { listInitialRows } from 'app/lib/form/field-initial-values';
  8. import { useTranslation } from 'react-i18next';
  9. export default function FormFieldList(props) {
  10. const { t } = useTranslation();
  11. const params = strToObj(props.params) || {};
  12. const minCount = params.minCount || 1;
  13. const maxCount = params.maxCount || 10;
  14. const createEmpty = () => {
  15. const keys = (params.fields || []).map((f) => f.name);
  16. return keys.reduce((acc, key) => {
  17. acc[key] = '';
  18. return acc;
  19. }, {});
  20. };
  21. const initedValue = listInitialRows(props);
  22. const { field } = useFormField(props, {
  23. defaultValue: JSON.stringify(initedValue),
  24. syncValue: false,
  25. });
  26. const [values, setValues] = useState(initedValue);
  27. useEffect(() => {
  28. // Rows start equal to the form default — write user edits only.
  29. const next = JSON.stringify(values);
  30. if (next !== field.value) field.onChange(next);
  31. }, [values]);
  32. const addNew = () => {
  33. setValues((prev) => [...prev, createEmpty()]);
  34. };
  35. const deleteValue = (index) => {
  36. setValues((prev) => {
  37. const next = [...prev];
  38. next.splice(index, 1);
  39. return next;
  40. });
  41. };
  42. const setValues2 = (index, fieldName, value) => {
  43. setValues((prev) => {
  44. const next = [...prev];
  45. next[index] = { ...next[index], [fieldName]: value };
  46. return next;
  47. });
  48. };
  49. return (
  50. <Field {...props}>
  51. <View className="gap-y-2 sm:gap-y-3 w-full">
  52. {values.map((value, index) => (
  53. <Row key={`vls${index}`} className="gap-x-2">
  54. {(params.fields || []).map((fld) => (
  55. <Input
  56. key={`fld-${fld.name}`}
  57. placeholder={fld.title}
  58. value={values[index]?.[fld.name]}
  59. onChangeText={(text) => {
  60. setValues2(index, fld.name, text);
  61. }}
  62. />
  63. ))}
  64. {index >= minCount && (
  65. <View>
  66. <NeoButton
  67. controlSize="large"
  68. image="X"
  69. accessibilityLabel={t('Remove')}
  70. onPress={() => deleteValue(index)}
  71. />
  72. </View>
  73. )}
  74. {index == 0 && values.length < maxCount && (
  75. <View>
  76. <NeoButton
  77. controlSize="large"
  78. image="Plus"
  79. accessibilityLabel={t('Add')}
  80. onPress={addNew}
  81. />
  82. </View>
  83. )}
  84. </Row>
  85. ))}
  86. </View>
  87. </Field>
  88. );
  89. }