Simple form working end-to-end

This commit is contained in:
Ozzie Gooen 2020-02-09 20:44:59 +00:00
parent edaa7a7ca5
commit 54e645f252
10 changed files with 245 additions and 258 deletions

View File

@ -1,46 +1 @@
'use strict'; /* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */
var $$Array = require("bs-platform/lib/js/array.js");
var Curry = require("bs-platform/lib/js/curry.js");
var React = require("react");
var Belt_Option = require("bs-platform/lib/js/belt_Option.js");
var Belt_MapString = require("bs-platform/lib/js/belt_MapString.js");
var Model$ProbExample = require("../Model.bs.js");
var EAFunds_Model$ProbExample = require("./EAFunds_Model.bs.js");
function handleChange(handleChange$1, $$event) {
return Curry._1(handleChange$1, $$event.target.value);
}
var model = EAFunds_Model$ProbExample.Interface.model;
var initialMap = Model$ProbExample.toMaps(model);
function EAFunds_Form(Props) {
var match = React.useState((function () {
return Model$ProbExample.toMaps(model);
}));
var params = match[0];
return $$Array.map((function (parameter) {
var __x = Belt_MapString.get(params[/* inputs */1], parameter[/* id */0]);
var value = Belt_Option.flatMap(__x, (function (param) {
return param[1];
}));
return React.createElement(React.Fragment, undefined, parameter[/* name */1], parameter[/* id */0], React.createElement(Model$ProbExample.Input.Form.make, {
parameter: parameter,
value: value,
onChange: (function (r) {
console.log(r);
return /* () */0;
})
}));
}), $$Array.of_list(model[/* inputs */3]));
}
var make = EAFunds_Form;
exports.handleChange = handleChange;
exports.model = model;
exports.initialMap = initialMap;
exports.make = make;
/* initialMap Not a pure module */

View File

@ -1,24 +1,21 @@
let model = EAFunds_Model.Interface.model; // let model = EAFunds_Model.Interface.model;
// let handleChange = (handleChange, event) =>
let handleChange = (handleChange, event) => // handleChange(ReactEvent.Form.target(event)##value);
handleChange(ReactEvent.Form.target(event)##value); // let model = EAFunds_Model.Interface.model;
// let initialMap = Model.toMaps(model);
let model = EAFunds_Model.Interface.model; // [@react.component]
let initialMap = Model.toMaps(model); // let make = () => {
// let (params, changeParams) = React.useState(() => Model.toMaps(model));
[@react.component] // model.inputs
let make = () => { // |> Array.of_list
let (params, changeParams) = React.useState(() => Model.toMaps(model)); // |> Array.map((parameter: Model.Input.parameter) => {
model.inputs // let value =
|> Array.of_list // params.inputs->Model.MS.get(parameter.id)
|> Array.map((parameter: Model.Input.parameter) => { // |> Belt.Option.flatMap(_, ((_, b)) => b);
let value = // <>
params.inputs->Model.MS.get(parameter.id) // {parameter.name |> ReasonReact.string}
|> Belt.Option.flatMap(_, ((_, b)) => b); // {parameter.id |> ReasonReact.string}
<> // <Model.Input.Form parameter value onChange={r => Js.log(r)} />
{parameter.name |> ReasonReact.string} // </>;
{parameter.id |> ReasonReact.string} // });
<Model.Input.Form parameter value onChange={r => Js.log(r)} /> /* }*/
</>;
});
};

View File

@ -1,52 +1,50 @@
open EAFunds_Data; // open EAFunds_Data;
// let handleChange = (handleChange, event) =>
let handleChange = (handleChange, event) => // handleChange(ReactEvent.Form.target(event)##value);
handleChange(ReactEvent.Form.target(event)##value); // [@react.component]
// let make = () => {
[@react.component] // let (year, setYear) = React.useState(() => 2021.);
let make = () => { // <>
let (year, setYear) = React.useState(() => 2021.); // <h1> {"EA Funds Forecasting Model 0.1" |> ReasonReact.string} </h1>
<> // <input
<h1> {"EA Funds Forecasting Model 0.1" |> ReasonReact.string} </h1> // type_="number"
<input // value={year |> Js.Float.toString}
type_="number" // onChange={handleChange(r =>
value={year |> Js.Float.toString} // switch (Js.Float.fromString(r)) {
onChange={handleChange(r => // | r when r >= 2020.0 && r <= 2050.0 => setYear(_ => r)
switch (Js.Float.fromString(r)) { // | _ => ()
| r when r >= 2020.0 && r <= 2050.0 => setYear(_ => r) // }
| _ => () // )}
} // />
)} // <table className="table-auto">
/> // <thead>
<table className="table-auto"> // <tr>
<thead> // <th className="px-4 py-2"> {"Fund Name" |> ReasonReact.string} </th>
<tr> // <th className="px-4 py-2"> {"Donations" |> ReasonReact.string} </th>
<th className="px-4 py-2"> {"Fund Name" |> ReasonReact.string} </th> // <th className="px-4 py-2"> {"Payouts" |> ReasonReact.string} </th>
<th className="px-4 py-2"> {"Donations" |> ReasonReact.string} </th> // </tr>
<th className="px-4 py-2"> {"Payouts" |> ReasonReact.string} </th> // </thead>
</tr> // <tbody>
</thead> // {funds
<tbody> // |> Belt.Array.map(_, r =>
{funds // <tr>
|> Belt.Array.map(_, r => // <th className="px-4 py-2 border ">
<tr> // {r.name |> ReasonReact.string}
<th className="px-4 py-2 border "> // </th>
{r.name |> ReasonReact.string} // <th className="px-4 py-2 border font-normal">
</th> // {EAFunds_Model.go(r.group, year, DONATIONS)
<th className="px-4 py-2 border font-normal"> // |> Model.InputTypes.to_string
{EAFunds_Model.go(r.group, year, DONATIONS) // |> ReasonReact.string}
|> Model.InputTypes.to_string // </th>
|> ReasonReact.string} // <th className="px-4 py-2 border font-normal">
</th> // {EAFunds_Model.go(r.group, year, PAYOUTS)
<th className="px-4 py-2 border font-normal"> // |> Model.InputTypes.to_string
{EAFunds_Model.go(r.group, year, PAYOUTS) // |> ReasonReact.string}
|> Model.InputTypes.to_string // </th>
|> ReasonReact.string} // </tr>
</th> // )
</tr> // |> ReasonReact.array}
) // </tbody>
|> ReasonReact.array} // </table>
</tbody> // </>;
</table> /* }*/
</>;
};

View File

@ -1,8 +1,10 @@
'use strict'; 'use strict';
var Block = require("bs-platform/lib/js/block.js"); var Block = require("bs-platform/lib/js/block.js");
var Curry = require("bs-platform/lib/js/curry.js");
var React = require("react");
var Math$ProbExample = require("../Math.bs.js"); var Math$ProbExample = require("../Math.bs.js");
var Model$ProbExample = require("../Model.bs.js"); var Prop$ProbExample = require("../Prop.bs.js");
function yearDiff(year) { function yearDiff(year) {
return year - 2020.0; return year - 2020.0;
@ -66,46 +68,38 @@ var PayoutsIfAround = {
}; };
function go(group, year, output) { function go(group, year, output) {
return /* FloatCdf */Block.__(3, [calculateDifference(currentValue(group, output), year, /* record */[ return /* FloatCdf */Block.__(2, [calculateDifference(currentValue(group, output), year, /* record */[
/* meanDiff */1.1, /* meanDiff */1.1,
/* stdDiff */1.1 /* stdDiff */1.1
])]); ])]);
} }
var model_002 = /* assumptions : :: */[ var model_002 = /* inputTypes : array */[
Model$ProbExample.Input.make("Yearly Growth Rate", /* FloatPoint */0, undefined, /* () */0), Prop$ProbExample.TypeWithMetadata.make("Fund", /* SelectSingle */Block.__(0, [/* record */[
/* :: */[
Model$ProbExample.Input.currentYear,
/* [] */0
]
];
var model_003 = /* inputs : :: */[
Model$ProbExample.Input.make("Fund", /* SingleChoice */Block.__(1, [/* record */[
/* options : :: */[ /* options : :: */[
/* tuple */[ /* record */[
"Animal Welfare Fund", /* id */"animal",
"animal" /* name */"Animal Welfare Fund"
], ],
/* :: */[ /* :: */[
/* tuple */[ /* record */[
"Global Health Fund", /* id */"globalHealth",
"globalHealth" /* name */"Global Health Fund"
], ],
/* :: */[ /* :: */[
/* tuple */[ /* record */[
"Long Term Future Fund", /* id */"longTerm",
"longTerm" /* name */"Long Term Future Fund"
], ],
/* :: */[ /* :: */[
/* tuple */[ /* record */[
"Meta Fund", /* id */"longterm",
"meta" /* name */"Meta Fund"
], ],
/* :: */[ /* :: */[
/* tuple */[ /* record */[
"All", /* id */"all",
"all" /* name */"All"
], ],
/* [] */0 /* [] */0
] ]
@ -114,32 +108,21 @@ var model_003 = /* inputs : :: */[
] ]
], ],
/* default */"total" /* default */"total"
]]), undefined, /* () */0), ]]), undefined, undefined, undefined, /* () */0),
/* :: */[ Prop$ProbExample.TypeWithMetadata.make("Year", /* Year */Block.__(2, [/* record */[
Model$ProbExample.Input.make("Year", /* Year */Block.__(0, [/* record */[ /* default */2030.0,
/* default */2030.0, /* min */2020.0,
/* min */2020.0, /* max */2050.0
/* max */2050.0 ]]), undefined, undefined, undefined, /* () */0)
]]), undefined, /* () */0),
/* [] */0
]
]; ];
var model_004 = /* outputs : :: */[ var model_003 = /* outputTypes : array */[];
Model$ProbExample.Output.make("Payments", /* FloatCdf */2, undefined, /* () */0),
/* :: */[
Model$ProbExample.Output.make("Payouts", /* FloatCdf */2, undefined, /* () */0),
/* [] */0
]
];
var model = /* record */[ var model = /* record */[
/* name */"Calculate the payments and payouts of EA Funds based on existing data.", /* name */"Calculate the payments and payouts of EA Funds based on existing data.",
/* author */"George Harrison", /* author */"George Harrison",
model_002, model_002,
model_003, model_003
model_004,
/* outputConfig : Single */0
]; ];
function convertChoice(s) { function convertChoice(s) {
@ -158,48 +141,54 @@ function convertChoice(s) {
} }
function run(p) { function run(p) {
var match = p[/* assumptions */0]; var partial_arg = p[/* inputValues */1];
var match$1 = p[/* inputs */1]; var partial_arg$1 = Prop$ProbExample.ValueMap.get;
if (match) { var get = function (param) {
var match$2 = match[0]; return partial_arg$1(partial_arg, param);
if (match$2 !== undefined) { };
var match$3 = match$2; var match = Curry._1(get, "Fund");
if (match$3.tag) { var match$1 = Curry._1(get, "Year");
return ; if (match !== undefined) {
} else { var match$2 = match;
var match$4 = match[1]; switch (match$2.tag | 0) {
if (match$4) { case /* SelectSingle */0 :
var match$5 = match$4[0]; if (match$1 !== undefined) {
if (match$5 !== undefined && !(match$5.tag || match$4[1] || !match$1)) { var match$3 = match$1;
var match$6 = match$1[0]; switch (match$3.tag | 0) {
if (match$6 !== undefined) { case /* FloatPoint */1 :
var match$7 = match$6; return go(convertChoice(match$2[0]), match$3[0], /* DONATIONS */0);
if (match$7.tag === /* SingleChoice */1 && !match$1[1]) { case /* SelectSingle */0 :
return go(convertChoice(match$7[0]), match$3[0], /* DONATIONS */0); case /* FloatCdf */2 :
} else { return ;
return ;
}
} else {
return ;
} }
} else { } else {
return ; return ;
} }
} else { case /* FloatPoint */1 :
case /* FloatCdf */2 :
return ; return ;
}
}
} else {
return ;
} }
} }
} }
function EAFunds_Model$Interface$Form(Props) {
return React.createElement(Prop$ProbExample.ModelForm.make, {
combo: Prop$ProbExample.Combo.fromModel(model)
});
}
var Form = {
make: EAFunds_Model$Interface$Form
};
var Interface = { var Interface = {
model: model, model: model,
convertChoice: convertChoice, convertChoice: convertChoice,
run: run run: run,
Form: Form
}; };
exports.PayoutsIfAround = PayoutsIfAround; exports.PayoutsIfAround = PayoutsIfAround;

View File

@ -44,7 +44,7 @@ module PayoutsIfAround = {
let go = (group: group, year: float, output: output) => { let go = (group: group, year: float, output: output) => {
PayoutsIfAround.( PayoutsIfAround.(
Model.InputTypes.FloatCdf( Prop.Value.FloatCdf(
calculateDifference( calculateDifference(
currentValue(group, output), currentValue(group, output),
year, year,
@ -55,34 +55,30 @@ let go = (group: group, year: float, output: output) => {
}; };
module Interface = { module Interface = {
open Model; open Prop;
let model = { let model: Model.t = {
name: "Calculate the payments and payouts of EA Funds based on existing data.", name: "Calculate the payments and payouts of EA Funds based on existing data.",
author: "George Harrison", author: "George Harrison",
assumptions: [ inputTypes: [|
Input.make(~name="Yearly Growth Rate", ~parameterType=FloatPoint, ()), TypeWithMetadata.make(
Input.currentYear,
],
inputs: [
Input.make(
~name="Fund", ~name="Fund",
~parameterType= ~type_=
SingleChoice({ SelectSingle({
default: Some("total"), default: Some("total"),
options: [ options: [
("Animal Welfare Fund", "animal"), {name: "Animal Welfare Fund", id: "animal"},
("Global Health Fund", "globalHealth"), {name: "Global Health Fund", id: "globalHealth"},
("Long Term Future Fund", "longTerm"), {name: "Long Term Future Fund", id: "longTerm"},
("Meta Fund", "meta"), {name: "Meta Fund", id: "longterm"},
("All", "all"), {name: "All", id: "all"},
], ],
}), }),
(), (),
), ),
Input.make( TypeWithMetadata.make(
~name="Year", ~name="Year",
~parameterType= ~type_=
Year({ Year({
default: Some(2030.0), default: Some(2030.0),
min: Some(2020.0), min: Some(2020.0),
@ -90,12 +86,8 @@ module Interface = {
}), }),
(), (),
), ),
], |],
outputs: [ outputTypes: [||],
Output.make(~name="Payments", ~parameterType=FloatCdf, ()),
Output.make(~name="Payouts", ~parameterType=FloatCdf, ()),
],
outputConfig: Single,
}; };
let convertChoice = (s: string) => let convertChoice = (s: string) =>
@ -107,14 +99,17 @@ module Interface = {
| _ => All | _ => All
}; };
let run = (p: Model.modelParams) => { let run = (p: Combo.t) => {
switch (p.assumptions, p.inputs) { let get = Prop.ValueMap.get(p.inputValues);
| ( switch (get("Fund"), get("Year")) {
[Some(Year(intendedYear)), Some(Year(currentYear))], | (Some(SelectSingle(fund)), Some(FloatPoint(intendedYear))) =>
[Some(SingleChoice(fund))],
) =>
Some(go(convertChoice(fund), intendedYear, DONATIONS)) Some(go(convertChoice(fund), intendedYear, DONATIONS))
| _ => None | _ => None
}; };
}; };
module Form = {
[@react.component]
let make = () => <Prop.ModelForm combo={Prop.Combo.fromModel(model)} />;
};
}; };

View File

@ -2,10 +2,10 @@
var React = require("react"); var React = require("react");
var ReactDOMRe = require("reason-react/src/ReactDOMRe.js"); var ReactDOMRe = require("reason-react/src/ReactDOMRe.js");
var EAFunds_Form2$ProbExample = require("./EAFunds/EAFunds_Form2.bs.js"); var EAFunds_Model$ProbExample = require("./EAFunds/EAFunds_Model.bs.js");
((import('./styles/index.css'))); ((import('./styles/index.css')));
ReactDOMRe.renderToElementWithId(React.createElement(EAFunds_Form2$ProbExample.make, { }), "app"); ReactDOMRe.renderToElementWithId(React.createElement(EAFunds_Model$ProbExample.Interface.Form.make, { }), "app");
/* Not a pure module */ /* Not a pure module */

View File

@ -1,2 +1,2 @@
[%bs.raw {|import('./styles/index.css')|}]; [%bs.raw {|import('./styles/index.css')|}];
ReactDOMRe.renderToElementWithId(<EAFunds_Form2 />, "app"); ReactDOMRe.renderToElementWithId(<EAFunds_Model.Interface.Form />, "app");

View File

@ -1,17 +0,0 @@
type yearAsFloat = {
min: option(float),
max: option(float),
};
type namedValue('a) = {
name: string,
value: 'a,
};
type choice('a) = list(namedValue('a));
type output =
| A
| B;
let nOutput: choice(output) = [{name: "sdfsdf", value: A}];

View File

@ -51,12 +51,12 @@ module Type = {
module ValueMap = { module ValueMap = {
module MS = Belt.Map.String; module MS = Belt.Map.String;
type t = MS.t(Value.t); type t = MS.t(Value.t);
let get = MS.get; let get = (t: t, s) => MS.get(t, s);
let keys = MS.keysToArray; let keys = MS.keysToArray;
let map = MS.map; let map = MS.map;
let fromArray = (r): t => MS.fromArray(r); let fromArray = (r): t => MS.fromArray(r);
let values = (t: t) => t |> MS.valuesToArray; let values = (t: t) => t |> MS.valuesToArray;
let update = MS.update; let update = (t, k, v) => MS.update(t, k, _ => v);
let toArray = MS.toArray; let toArray = MS.toArray;
let fromOptionalMap = (t: MS.t(option(Value.t))): t => let fromOptionalMap = (t: MS.t(option(Value.t))): t =>
MS.keep(t, (_, d) => E.O.isSome(d)) MS.keep(t, (_, d) => E.O.isSome(d))
@ -121,7 +121,7 @@ module Model = {
}; };
module Combo = { module Combo = {
type combo = { type t = {
model: Model.t, model: Model.t,
inputValues: ValueMap.t, inputValues: ValueMap.t,
outputValues: ValueMap.t, outputValues: ValueMap.t,
@ -135,20 +135,90 @@ module Combo = {
) )
|> ValueMap.fromOptionalArray; |> ValueMap.fromOptionalArray;
let isValid = (t: combo) => let isValid = t =>
t.model t.model
|> Model.InputTypes.keys |> Model.InputTypes.keys
|> E.A.fmap(ValueMap.get(t.inputValues)) |> E.A.fmap(ValueMap.get(t.inputValues))
|> Belt.Array.some(_, E.O.isNone); |> Belt.Array.some(_, E.O.isNone);
let update = let update = (t, key: string, onUpdate: option(Value.t)) =>
(
t: combo,
key: string,
onUpdate: option(Value.t) => option(Value.t),
) =>
ValueMap.update(t.inputValues, key, onUpdate); ValueMap.update(t.inputValues, key, onUpdate);
}; };
let run = (t: combo, f): ValueMap.t => f(t.inputValues); let updateInputValue = (t, k, u) => {
...t,
inputValues: InputValues.update(t, k, u),
};
let inputTypeValuePairs = (t: t) =>
t.model.inputTypes
|> E.A.fmap((i: TypeWithMetadata.t) =>
(i, ValueMap.get(t.inputValues, i.id))
);
let fromModel = (t: Model.t) => {
model: t,
inputValues: InputValues.defaults(t),
outputValues: InputValues.defaults(t),
};
let run = (t: t, f): ValueMap.t => f(t.inputValues);
};
module ValueForm = {
let handleChange = (handleChange, event) =>
handleChange(ReactEvent.Form.target(event)##value);
type onChange = option(Value.t) => unit;
[@react.component]
let make =
(
~type_: TypeWithMetadata.t,
~value: option(Value.t),
~onChange: onChange,
) => {
switch (type_.type_, value) {
| (Year(_), Some(FloatPoint(r))) =>
<input
type_="number"
value={r |> Js.Float.toString}
onChange={handleChange(r =>
switch (Js.Float.fromString(r)) {
| r => onChange(Some(Value.FloatPoint(r)))
}
)}
/>
| (FloatPoint(_), Some(FloatPoint(r))) =>
<input type_="number" value={r |> Js.Float.toString} />
| (Year(_), _)
| (FloatPoint(_), _) => <input type_="number" value="" />
| (SelectSingle(_), _) =>
<div> {"Single Choice" |> ReasonReact.string} </div>
};
};
};
module ModelForm = {
let handleChange = (handleChange, event) =>
handleChange(ReactEvent.Form.target(event)##value);
[@react.component]
let make = (~combo: Combo.t) => {
let (combo, setCombo) = React.useState(() => combo);
<div>
{Combo.inputTypeValuePairs(combo)
|> E.A.fmap(((type_, value)) =>
<ValueForm
type_
value
onChange={newValue =>
setCombo(_ =>
Combo.updateInputValue(combo, type_.id, newValue)
)
}
/>
)
|> ReasonReact.array}
</div>;
};
}; };