summaryrefslogtreecommitdiff
path: root/packages/exchange-backoffice-ui/src/handlers/forms.ts
blob: 115127cc3dc1f2a95f674341959524b42c480a93 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import { TranslatedString } from "@gnu-taler/taler-util";
import { InputText } from "./InputText.js";
import { InputDate } from "./InputDate.js";
import { InputInteger } from "./InputInteger.js";
import { h as create, Fragment, VNode } from "preact";
import { InputChoiceStacked } from "./InputChoiceStacked.js";
import { InputArray } from "./InputArray.js";
import { InputSelectMultiple } from "./InputSelectMultiple.js";
import { InputTextArea } from "./InputTextArea.js";
import { InputFile } from "./InputFile.js";
import { Caption } from "./Caption.js";
import { Group } from "./Group.js";
import { InputSelectOne } from "./InputSelectOne.js";
import { FormProvider } from "./FormProvider.js";
import { InputLine } from "./InputLine.js";

export type DoubleColumnForm = DoubleColumnFormSection[];

type DoubleColumnFormSection = {
  title: TranslatedString;
  description?: TranslatedString;
  fields: UIFormField[];
};

/**
 * Constrain the type with the ui props
 */
type FieldType<T extends object = any, K extends keyof T = any> = {
  group: Parameters<typeof Group>[0];
  caption: Parameters<typeof Caption>[0];
  array: Parameters<typeof InputArray<T, K>>[0];
  file: Parameters<typeof InputFile<T, K>>[0];
  selectOne: Parameters<typeof InputSelectOne<T, K>>[0];
  selectMultiple: Parameters<typeof InputSelectMultiple<T, K>>[0];
  text: Parameters<typeof InputText<T, K>>[0];
  textArea: Parameters<typeof InputTextArea<T, K>>[0];
  choiceStacked: Parameters<typeof InputChoiceStacked<T, K>>[0];
  date: Parameters<typeof InputDate<T, K>>[0];
  integer: Parameters<typeof InputInteger<T, K>>[0];
};

/**
 * List all the form fields so typescript can type-check the form instance
 */
export type UIFormField =
  | { type: "group"; props: FieldType["group"] }
  | { type: "caption"; props: FieldType["caption"] }
  | { type: "array"; props: FieldType["array"] }
  | { type: "file"; props: FieldType["file"] }
  | { type: "selectOne"; props: FieldType["selectOne"] }
  | { type: "selectMultiple"; props: FieldType["selectMultiple"] }
  | { type: "text"; props: FieldType["text"] }
  | { type: "textArea"; props: FieldType["textArea"] }
  | { type: "choiceStacked"; props: FieldType["choiceStacked"] }
  | { type: "integer"; props: FieldType["integer"] }
  | { type: "date"; props: FieldType["date"] };

type FieldComponentFunction<key extends keyof FieldType> = (
  props: FieldType[key],
) => VNode;

type UIFormFieldMap = {
  [key in keyof FieldType]: FieldComponentFunction<key>;
};

/**
 * Maps input type with component implementation
 */
const UIFormConfiguration: UIFormFieldMap = {
  group: Group,
  caption: Caption,
  //@ts-ignore
  array: InputArray,
  text: InputText,
  //@ts-ignore
  file: InputFile,
  textArea: InputTextArea,
  //@ts-ignore
  date: InputDate,
  //@ts-ignore
  choiceStacked: InputChoiceStacked,
  integer: InputInteger,
  //@ts-ignore
  selectOne: InputSelectOne,
  //@ts-ignore
  selectMultiple: InputSelectMultiple,
};

export function RenderAllFieldsByUiConfig({
  fields,
}: {
  fields: UIFormField[];
}): VNode {
  return create(
    Fragment,
    {},
    fields.map((field, i) => {
      const Component = UIFormConfiguration[
        field.type
      ] as FieldComponentFunction<any>;
      return Component(field.props);
    }),
  );
}

type FormSet<T extends object, K extends keyof T = any> = {
  Provider: typeof FormProvider<T>;
  InputLine: typeof InputLine<T, K>;
};
export function createNewForm<T extends object>(): FormSet<T> {
  return {
    Provider: FormProvider,
    InputLine: InputLine,
  };
}