form-initial-values.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import { components, coreFormFields } from 'app/components/registry';
  2. import { FILES_FIELD_MIRRORS, resolveFormFieldType } from 'app/lib/form/form-helpers';
  3. import {
  4. datetimeInitialValue,
  5. doubleRangeInitialValue,
  6. filesFieldValue,
  7. listInitialRows,
  8. locationFormValues,
  9. locationStringOf,
  10. multiFieldFormValues,
  11. multiFieldInitialRows,
  12. normalizePriceValue,
  13. ownInitialValue,
  14. selectorInitialValue,
  15. stringArrayInitialValue,
  16. switcherInitialValue,
  17. syncedInitialValue,
  18. } from 'app/lib/form/field-initial-values';
  19. /**
  20. * Form's default values, from the per-type initial values in
  21. * field-initial-values. Kept apart from those helpers because the field
  22. * components import them, and this part pulls in form-helpers and the
  23. * registry, which lead back to the fields (a require cycle).
  24. */
  25. type Input = Record<string, any>;
  26. type InitialValues = Record<string, any>;
  27. function locationInitialValues(input: Input, syncName: boolean) {
  28. const { name, value } = input;
  29. const base = { [name]: ownInitialValue(value) };
  30. if (!locationStringOf(value)) return base;
  31. return { ...base, ...locationFormValues(name, value, syncName) };
  32. }
  33. const single = (fn: (input: Input) => any) => (input: Input) => ({ [input.name]: fn(input) });
  34. const synced = single((input) => syncedInitialValue(input.value));
  35. const own = single((input) => ownInitialValue(input.value));
  36. const INITIAL_VALUES_BY_TYPE: Record<string, (input: Input) => InitialValues> = {
  37. text: synced,
  38. password: synced,
  39. hidden: synced,
  40. submit: synced,
  41. button: synced,
  42. phone: synced,
  43. value: synced,
  44. select: synced,
  45. radio_set: synced,
  46. suggestion: synced,
  47. visibility: synced,
  48. initial_members: synced,
  49. labels: synced,
  50. mood: synced,
  51. embed: synced,
  52. polls: own,
  53. textarea: (input) =>
  54. // Rich text (html 1-3) goes through the editor engines: no props.value sync.
  55. ({ [input.name]: [1, 2, 3].includes(Number(input.html)) ? ownInitialValue(input.value) : syncedInitialValue(input.value) }),
  56. editor: (input) =>
  57. ({ [input.name]: [1, 2, 3].includes(Number(input.html)) ? ownInitialValue(input.value) : syncedInitialValue(input.value) }),
  58. textarea_markdown: single((input) => input.value ?? ''),
  59. markdown: single((input) => input.value ?? ''),
  60. switcher: single(switcherInitialValue),
  61. checkbox: single(switcherInitialValue),
  62. price: single((input) => normalizePriceValue(input.value)),
  63. checkbox_set: single((input) => stringArrayInitialValue(input.value)),
  64. selector: single((input) => selectorInitialValue(input.value)),
  65. select_multiple: single((input) => selectorInitialValue(input.value)),
  66. doublerange: single(doubleRangeInitialValue),
  67. datetime: single(datetimeInitialValue),
  68. datepicker: single(datetimeInitialValue),
  69. list: single((input) => JSON.stringify(listInitialRows(input))),
  70. multi_field: (input) => multiFieldFormValues(input.name, multiFieldInitialRows(input)),
  71. location: (input) => locationInitialValues(input, true),
  72. location_radius: (input) => locationInitialValues(input, false),
  73. files: (input) => {
  74. const images = input.values_src;
  75. const ids = images ? filesFieldValue(images) : '';
  76. const mirror = images && FILES_FIELD_MIRRORS[input.name];
  77. return mirror ? { [input.name]: ids, [mirror]: ids } : { [input.name]: ids };
  78. },
  79. };
  80. /**
  81. * Initial values of every core field in `inputs` (UNA inputs map after Form's
  82. * processing). Files mirrors (`covers` → `thumb`) win over the mirrored input,
  83. * as the files field writes them on mount.
  84. */
  85. export function getFormInitialValues(inputs: Record<string, Input> | null | undefined): InitialValues {
  86. if (!inputs) return {};
  87. const values: InitialValues = {};
  88. const mirrors: InitialValues = {};
  89. for (const input of Object.values(inputs)) {
  90. if (!input?.name) continue;
  91. const type = resolveFormFieldType(input);
  92. const getValues = INITIAL_VALUES_BY_TYPE[type];
  93. const registered = (components as any)['form-field'][type];
  94. if (!getValues || registered !== (coreFormFields as any)[type]) continue;
  95. const fieldValues = getValues(input);
  96. Object.assign(type === 'files' ? mirrors : values, fieldValues);
  97. }
  98. return { ...values, ...mirrors };
  99. }