Skip to content

Instantly share code, notes, and snippets.

@fakenickels
Created July 28, 2020 13:55
Show Gist options
  • Save fakenickels/600b60c1cae159e6abdd3d980fe048b2 to your computer and use it in GitHub Desktop.
Save fakenickels/600b60c1cae159e6abdd3d980fe048b2 to your computer and use it in GitHub Desktop.
// FormMaker experiment
module type Config = {
type field('a);
type state;
let set: (state, field('a), 'a) => state;
let get: (state, field('a)) => 'a;
};
module Make = (Config: Config) => {
module Form = BsReform.ReForm.Make(Config);
module GeneratorTypes = {
type field('inputType) = {
validator: Form.Validation.t,
field: Form.field,
inputType: 'inputType,
label: string,
};
let field = (~validator, ~inputType, ~label, field) => {
inputType,
label,
field: Field(field),
validator,
};
};
type fieldInterface('value) = {
handleChange: 'value => unit,
error: option(string),
state: BsReform.ReForm.fieldState,
validate: unit => unit,
value: 'value,
};
let useField = (field: Form.field) => {
let interface = Form.useFormContext();
interface->Belt.Option.map(
({handleChange, getFieldError, getFieldState, validateField, state}) => {
let unpack: type a. Form.field => fieldInterface(a) =
field => {
switch (field) {
| Form.ReSchema.Field(field) =>
// im dumb
let fieldUnpacked = Obj.magic(field);
{
handleChange: handleChange(fieldUnpacked),
error: getFieldError(Field(field)),
state: getFieldState(Field(field)),
validate: () => validateField(Field(field)),
value: state.values->Config.get(fieldUnpacked),
};
};
};
unpack(field);
});
};
module Field = {
[@react.component]
let make =
(
~field: Form.field,
~render: fieldInterface('a) => React.element,
~renderOnMissingContext=React.null,
(),
) => {
let fieldInterface = useField(field);
React.useMemo3(
() =>
fieldInterface
->Belt.Option.map(render)
->Belt.Option.getWithDefault(renderOnMissingContext),
(
Belt.Option.(
fieldInterface->flatMap(({error}) => error)->getWithDefault("")
),
Belt.Option.(fieldInterface->map(({value}) => value)),
Belt.Option.(fieldInterface->map(({state}) => state)),
),
);
};
};
module type GeneratorConfig = {
type inputType;
let renderer:
(
~props: fieldInterface('value),
~spec: GeneratorTypes.field(inputType)
) =>
'a;
module Wrapper: {
let makeProps:
(~key: string=?, ~reform: Form.api, ~children: React.element, unit) =>
{. "reform": 'a};
let make: 'props => React.element;
};
};
module Make = (Config: GeneratorConfig) => {
type field = GeneratorTypes.field(Config.inputType);
type fields = array(field);
module Wrapper = Config.Wrapper;
[@react.component]
let make = (~fields: fields, ~initialState, ~onSubmit) => {
let schema =
Form.Validation.Schema(
fields->Belt.Array.map(({validator}) => validator),
);
let reform = Form.use(~initialState, ~schema, ~onSubmit, ());
<Form.Provider value=reform>
<Wrapper reform>
{fields
->Belt.Array.map(spec => {
<Field
field={spec.field}
render={props => {Config.renderer(~props, ~spec)}}
/>
})
->React.array}
</Wrapper>
</Form.Provider>;
};
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment