diff --git a/examples/dom/WebReconciler.re b/examples/dom/WebReconciler.re index 83b8c03..86f7ea3 100644 --- a/examples/dom/WebReconciler.re +++ b/examples/dom/WebReconciler.re @@ -5,16 +5,20 @@ * This is just an example but you could use this to create interesting * CLI apps, with a react-like functional API! */ + +exception InvalidNodePrimitiveMatchInUpdateInstance; + +let str = string_of_int; + module Reconciler = { /* Step 1: Define primitives */ - type imageProps = {src: string}; - type buttonProps = {src: string}; type primitives = - | Div - | Span(string) - | Image(imageProps); + | View + | Text(string) + | Image(string) /* img src */ + | Button(unit => unit, string); /* onPress, title */ /* Step 2: Define node type @@ -23,6 +27,7 @@ module Reconciler = { | Div(Js.t(Dom_html.divElement)) | Span(Js.t(Dom_html.element)) | Image(Js.t(Dom_html.imageElement)) + | Button(Js.t(Dom_html.buttonElement)) | Container(Js.t(Dom_html.element)); let document = Dom_html.window##.document; @@ -32,15 +37,27 @@ module Reconciler = { let createInstance: primitives => node = primitive => switch (primitive) { - | Div => Div(Dom_html.createDiv(document)) - | Span(s) => + | View => Div(Dom_html.createDiv(document)) + | Text(s) => let e = Dom_html.createSpan(document); e##.innerHTML := Js.string(s); Span(e); | Image(p) => let img = Dom_html.createImg(document); - img##.src := Js.string(p.src); + img##.src := Js.string(p); Image(img); + | Button(onPress, title) => + let button = + Dom_html.createButton(~_type=Js.string("button"), document); + let t = Js.string(title); + button##.title := t; + button##.innerHTML := t; + button##.onclick := + Dom_html.handler(_e => { + onPress(); + Js.bool(false); + }); + Button(button); }; /* @@ -52,15 +69,26 @@ module Reconciler = { | Div(e) => e |> Dom_html.element | Span(e) => e |> Dom_html.element | Image(e) => e |> Dom_html.element + | Button(e) => e |> Dom_html.element | Container(e) => e |> Dom_html.element }; let updateInstance = (node: node, _oldPrimitive: primitives, newPrimitive: primitives) => switch (newPrimitive, node) { - /* The only update operation we handle today is updating src for an image! */ - | (Image({src}), Image(e)) => e##.src := Js.string(src) - | _ => () + | (View, Div(_e)) => () + | (Text(s), Span(e)) => e##.innerHTML := Js.string(s) + | (Image(src), Image(e)) => e##.src := Js.string(src) + | (Button(onPress, title), Button(e)) => + let t = Js.string(title); + e##.title := t; + e##.innerHTML := t; + e##.onclick := + Dom_html.handler(_e => { + onPress(); + Js.bool(false); + }); + | _ => raise(InvalidNodePrimitiveMatchInUpdateInstance) }; let appendChild = (parentNode: node, childNode: node) => { @@ -69,6 +97,7 @@ module Reconciler = { | Div(e) => Dom.appendChild(e, innerNode) | Span(e) => Dom.appendChild(e, innerNode) | Image(e) => Dom.appendChild(e, innerNode) + | Button(e) => Dom.appendChild(e, innerNode) | Container(e) => Dom.appendChild(e, innerNode) }; }; @@ -79,6 +108,7 @@ module Reconciler = { | Div(e) => Dom.removeChild(e, innerNode) | Span(e) => Dom.removeChild(e, innerNode) | Image(e) => Dom.removeChild(e, innerNode) + | Button(e) => Dom.removeChild(e, innerNode) | Container(e) => Dom.removeChild(e, innerNode) }; }; @@ -90,6 +120,7 @@ module Reconciler = { | Div(e) => Dom.replaceChild(e, newInnerNode, oldInnerNode) | Span(e) => Dom.replaceChild(e, newInnerNode, oldInnerNode) | Image(e) => Dom.replaceChild(e, newInnerNode, oldInnerNode) + | Button(e) => Dom.replaceChild(e, newInnerNode, oldInnerNode) | Container(e) => Dom.replaceChild(e, newInnerNode, oldInnerNode) }; }; @@ -97,16 +128,44 @@ module Reconciler = { /* Step 5: Hook it up! */ module JsooReact = Reactify.Make(Reconciler); +open JsooReact; /* Define our primitive components */ -let div = (~children, ()) => JsooReact.primitiveComponent(Div, ~children); - -let span = (~text, ~children, ()) => - JsooReact.primitiveComponent(Span(text), ~children); +let view = (~children, ()) => JsooReact.primitiveComponent(View, ~children); -let image = (~children, ~src, ()) => +let image = (~children, ~src="", ()) => JsooReact.primitiveComponent(Image(src), ~children); +let text = (~children: list(string), ()) => + JsooReact.primitiveComponent(Text(List.hd(children)), ~children=[]); + +let button = (~children, ~onPress, ~title, ()) => + JsooReact.primitiveComponent(Button(onPress, title), ~children); + +type action = + | Increment + | Decrement; + +let reducer = (state, action) => + switch (action) { + | Increment => state + 1 + | Decrement => state - 1 + }; + +let renderCounter = () => { + let (count, dispatch) = useReducer(reducer, 0); + + +