Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Jooseppi12 committed Apr 16, 2024
1 parent 507107d commit 70ee5df
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 101 deletions.
32 changes: 26 additions & 6 deletions WebSharper.UI.CSharp.Templating/CodeGenerator.fs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ let buildHoleMethods (typeName: string) (holeName: HoleName) (holeDef: HoleDefin
|]
| HoleKind.Var (ValTy.StringList, _) ->
[|
sv "Var<string array>" "VarStrList" "x"
sv "Var<string[]>" "VarStrList" "x"
|]
| HoleKind.Var (ValTy.Number, _) ->
[|
Expand Down Expand Up @@ -233,12 +233,32 @@ let finalMethodBody (ctx: Ctx) =
| None -> "FSharpOption<object>.None"
| Some d -> sprintf "FSharpOption<object>.Some(\"%s\")" (d :?> string)
yield sprintf """Tuple.Create("%s", ValTy.String, %s)""" holeName' (opt)
| HoleKind.Var (AST.ValTy.Number, d) -> yield sprintf """Tuple.Create("%s", ValTy.Number, FSharpOption<object>.None)""" holeName'
| HoleKind.Var (AST.ValTy.Bool, d) -> yield sprintf """Tuple.Create("%s", ValTy.Bool, FSharpOption<object>.None)""" holeName'
| HoleKind.Var (AST.ValTy.DateTime, d) -> yield sprintf """Tuple.Create("%s", ValTy.DateTime, FSharpOption<object>.None)""" holeName'
| HoleKind.Var (AST.ValTy.Number, d) ->
let opt =
match d with
| None -> "FSharpOption<object>.None"
| Some d -> sprintf "FSharpOption<object>.Some(%s)" (string (d :?> int))
yield sprintf """Tuple.Create("%s", ValTy.Number, %s)""" holeName' opt
| HoleKind.Var (AST.ValTy.Bool, d) ->
let opt =
match d with
| None -> "FSharpOption<object>.None"
| Some d -> sprintf "FSharpOption<object>.Some(%s)" (string (d :?> bool))
yield sprintf """Tuple.Create("%s", ValTy.Bool, %s)""" holeName' opt
| HoleKind.Var (AST.ValTy.DateTime, d) ->
let opt =
match d with
| None -> "FSharpOption<object>.None"
| Some d -> sprintf "FSharpOption<object>.Some(\"%s\")" (d :?> string)
yield sprintf """Tuple.Create("%s", ValTy.DateTime, %s)""" holeName' opt
| HoleKind.Var (AST.ValTy.File, d) -> yield sprintf """Tuple.Create("%s", ValTy.File, FSharpOption<object>.None)""" holeName'
| HoleKind.Var (AST.ValTy.DomElement, d) -> yield sprintf """Tuple.Create("%s", ValTy.DomElement, FSharpOption<object>.None)""" holeName'
| HoleKind.Var (AST.ValTy.StringList, d) -> yield sprintf """Tuple.Create("%s", ValTy.StringList, FSharpOption<object>.None)""" holeName'
| HoleKind.Var (AST.ValTy.StringList, d) ->
let opt =
match d with
| None -> "FSharpOption<object>.None"
| Some d -> sprintf "FSharpOption<object>.Some(new string[]{%s})" (d :?> string list |> List.map (fun v -> sprintf "\"%s\"" v) |> fun x -> String.Join(",", x))
yield sprintf """Tuple.Create("%s", ValTy.StringList, %s)""" holeName' opt
| _ -> ()
]
|> String.concat ", "
Expand Down Expand Up @@ -278,7 +298,7 @@ let varsClass (ctx: Ctx) =
| HoleKind.Var (AST.ValTy.File, _) ->
yield sprintf """[Inline] public Var<JavaScript.File array> %s => (Var<JavaScript.File array>)TemplateHole.Value((As<Instance>(this)).Hole("%s"));""" holeName holeName'
| HoleKind.Var (AST.ValTy.StringList, _) ->
yield sprintf """[Inline] public Var<string array> %s => (Var<string array>)TemplateHole.Value((As<Instance>(this)).Hole("%s"));""" holeName holeName'
yield sprintf """[Inline] public Var<string[]> %s => (Var<string[]>)TemplateHole.Value((As<Instance>(this)).Hole("%s"));""" holeName holeName'
| _ -> ()
]
yield "}"
Expand Down
2 changes: 1 addition & 1 deletion WebSharper.UI.CSharp.Tests/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ <h1>My TODO! list</h1>
<div class="form-group">
<label>New task</label>
<div class="input-group">
<input class="form-control" ws-var="NewTaskName" />
<input class="form-control" ws-var="NewTaskName" type="text" />
<input class="form-control" ws-var="NewTaskPriority" type="number" />
<span class="input-group-btn">
<button class="btn btn-primary" type="button" ws-onclick="Add">Add</button>
Expand Down
65 changes: 59 additions & 6 deletions WebSharper.UI.Templating.Common/Parsing.fs
Original file line number Diff line number Diff line change
Expand Up @@ -232,20 +232,73 @@ module Impl =
ValTy.String, defVal
| "select" ->
if node.Attributes.AttributesWithName "multiple" |> Seq.isEmpty |> not then
ValTy.StringList, None
let selectedNodes =
[|
for node in node.ChildNodes do
if node.Attributes.AttributesWithName "selected" |> Seq.isEmpty |> not then
if node.Attributes.AttributesWithName "value" |> Seq.isEmpty |> not then
yield (node.Attributes.AttributesWithName "value" |> Seq.head).Value
else
yield node.InnerText
|]
|> function
| [||] -> None
| v -> Some (v :> obj)
ValTy.StringList, selectedNodes
else
ValTy.Any, None
let selectedNodes =
[
for node in node.ChildNodes do
if node.Attributes.AttributesWithName "selected" |> Seq.isEmpty |> not then
if node.Attributes.AttributesWithName "value" |> Seq.isEmpty |> not then
yield (node.Attributes.AttributesWithName "value" |> Seq.head).Value
else
yield node.InnerText
]
|> function
| [v] -> Some (v :> obj)
| _ -> None
ValTy.Any, selectedNodes
| "input" ->
match node.GetAttributeValue("type", null) with
| ("number" | "range") -> ValTy.Number, None
| "checkbox" -> ValTy.Bool, None
| "datetime-local" -> ValTy.DateTime, None
| ("number" | "range") ->
let v =
if node.Attributes.AttributesWithName "value" |> Seq.isEmpty |> not then
(node.Attributes.AttributesWithName "value" |> Seq.head).Value
|> int
|> fun v -> Some (v :> obj)
else
None
ValTy.Number, v
| "checkbox" ->
let v =
if node.Attributes.AttributesWithName "checked" |> Seq.isEmpty |> not then
Some (true :> obj)
else
None
ValTy.Bool, v
| "datetime-local" ->
let v =
if node.Attributes.AttributesWithName "value" |> Seq.isEmpty |> not then
(node.Attributes.AttributesWithName "value" |> Seq.head).Value
|> DateTime.Parse
|> fun v -> Some (v :> obj)
else
None
ValTy.DateTime, v
| "file" -> ValTy.File, None
| ("button" | "submit") as t ->
failwithf "Using %s on a <input type=\"%s\"> node" VarAttr t
| "radio" ->
failwithf "Using %s on a <input type=\"radio\"> node is not supported yet" VarAttr
| _ -> ValTy.Any, None
| _ ->
let v =
if node.Attributes.AttributesWithName "value" |> Seq.isEmpty |> not then
(node.Attributes.AttributesWithName "value" |> Seq.head).Value
|> fun v -> Some (v :> obj)
else
None
ValTy.Any, v
| n -> failwithf "Using %s on a <%s> node" VarAttr n

let addHole (holes: Dictionary<HoleName, HoleDefinition>) (name: HoleName) (def: HoleDefinition) =
Expand Down
8 changes: 4 additions & 4 deletions WebSharper.UI.Templating.Runtime/RuntimeClient.fs
Original file line number Diff line number Diff line change
Expand Up @@ -278,15 +278,15 @@ type private HandlerProxy =
| Server.ValTy.String ->
Server.TemplateInitializer.GetOrAddHoleFor(key, name, fun () -> TemplateHole.VarStr (name, Var.Create (d |> Option.map (fun x -> x :?> string) |> Option.defaultValue "")) :> TemplateHole)
| Server.ValTy.Number ->
Server.TemplateInitializer.GetOrAddHoleFor(key, name, fun () -> TemplateHole.VarFloatUnchecked (name, Var.Create 0.))
Server.TemplateInitializer.GetOrAddHoleFor(key, name, fun () -> TemplateHole.VarFloatUnchecked (name, Var.Create (d |> Option.map (fun x -> x :?> float) |> Option.defaultValue 0.)))
| Server.ValTy.Bool ->
Server.TemplateInitializer.GetOrAddHoleFor(key, name, fun () -> TemplateHole.VarBool (name, Var.Create false))
Server.TemplateInitializer.GetOrAddHoleFor(key, name, fun () -> TemplateHole.VarBool (name, Var.Create (d |> Option.map (fun x -> x :?> bool) |> Option.defaultValue false)))
| Server.ValTy.DateTime ->
Server.TemplateInitializer.GetOrAddHoleFor(key, name, fun () -> TemplateHole.VarDateTime (name, Var.Create DateTime.MinValue))
Server.TemplateInitializer.GetOrAddHoleFor(key, name, fun () -> TemplateHole.VarDateTime (name, Var.Create (d |> Option.map (fun x -> x :?> DateTime) |> Option.defaultValue DateTime.MinValue)))
| Server.ValTy.File ->
Server.TemplateInitializer.GetOrAddHoleFor(key, name, fun () -> TemplateHole.VarFile (name, Var.Create [||]))
| Server.ValTy.StringList ->
Server.TemplateInitializer.GetOrAddHoleFor(key, name, fun () -> TemplateHole.VarStrList (name, Var.Create [||]))
Server.TemplateInitializer.GetOrAddHoleFor(key, name, fun () -> TemplateHole.VarStrList (name, Var.Create (d |> Option.map (fun x -> x :?> string []) |> Option.defaultValue [||])))
| Server.ValTy.DomElement ->
Server.TemplateInitializer.GetOrAddHoleFor(key, name, fun () ->
let el = JavaScript.JS.Document.QuerySelector("[ws-dom=" + name + "]")
Expand Down
162 changes: 83 additions & 79 deletions WebSharper.UI.Templating.Tests/wwwroot/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,84 +22,88 @@
</style>
</head>
<body>
<h1 ws-anchor="Header">Template holes directly in the main file (<code>.Bind()</code>)</h1>
<p ws-hole="Hole">[FAIL] This should be replaced by a ws-hole.</p>
<p ws-replace="Replace">[FAIL] This should be replaced by a ws-replace.</p>
<p>${TextHole}</p>
<p class="fail" ws-attr="Attr">This is marked ok by ws-attr.</p>
<p ws-attr="MultiAttr">This is marked ok by ws-attr filled with ParamArray arguments.</p>
<p ws-onmouseenter="MouseEnter">This is marked ok by ws-onmouseenter.</p>
<p ws-onafterrender="AfterRender">[FAIL] This should be replaced by a ws-onafterrender.</p>
<p class="fail ${OkClass}">This is marked ok by a ${text} hole in an attribute.</p>
<p>
${Input} This is marked ok by a ws-var on the following input (set on mouseenter).
<input ws-var="Input" ws-onmouseenter="InputMouseEnter" />
</p>
<p>${OkView} This is marked ok by a View ${text} hole.</p>
<p class="fail ${OkClassView}">This is marked ok by a ${text} View hole in an attribute.</p>
<p>
${VarView} This is marked ok by a (View, setter) Var method on the following input.
<input ws-var="VarView" />
</p>
<div ws-replace="ExtraOkItems"></div>
<p>The following is a ws-instantiation of Form.</p>
<ws-Form Username Submit Welcome></ws-Form>
<div id="main"></div>
<div ws-template="Form">
<ws-FormInput Var="Username"><Type>text</Type></ws-FormInput>
<br />
<ws-FormInput Var="Password"><Type>password</Type></ws-FormInput>
<br />
<input type="submit" value="Submit" ws-onclick="Submit" />
<div>${Welcome}</div>
<div ws-hole="LeaveEmpty">THIS SHOULD NOT APPEAR</div>
<!-- Regression Test for #170: out-of-order use of nested subtemplate -->
<input ws-template="FormInput" type="${Type}" ws-var="Var" />
</div>
<div ws-preserve>
<!-- Testing comment -->
There should be an unfilled hole here: ${AHoleHere}
<div ws-replace="DoNotFillThisHole">
[OK] ws-replace inside ws-preserve is not removed
</div>
<div ws-hole="DoNotFillThisHole2">
[OK] ws-hole inside ws-preserve is not emptied
</div>
</div>
<div class="new-inputs" ws-onafterrender="SelectPrint">
<input type="datetime-local" ws-var="DateTimeVar" />
<input type="file" ws-var="FileVar" />
<input type="range" ws-var="RangeVar" />
<select ws-var="MultipleSelect" multiple>
<option value="a">A</option>
<option value="b">B</option>
<option value="c">C</option>
</select>
</div>
<template name="HTML5Template">
<slot name="ThisIsOk"></slot>
<slot></slot>
</template>
<div ws-replace="imAHtml5-Template"></div>
<div class="test-dom">
<button ws-onclick="ReplaceOnClick">Replace with an input</button>
</div>
<div ws-dom="asd"><input ws-var="x"/></div>
<div style="background: red; width: 80px; height: 80px" ws-dom="ReplaceMe"></div>
<div ws-template="WSDOMINSIDETEMPLATE" ws-onclick="ClickHandler">
<div ws-dom="DomNode"></div>
</div>
<div ws-template="FileUploader" ws-dom="MyNode">
<p ws-replace="HideMe">Is this going to show up?</p>
<form>
<input type="text" value="Text here" />
<input id="uploader" ws-var="Uploader" type="file" />
<button ws-onclick="OnSubmit">Submit</button>
</form>
</div>
<div ws-replace="TestWSDom"></div>
<div ws-replace="TestWSDom2"></div>
<div ws-replace="CheckBooleanAttr"></div>
<script type="module" src="Content/WebSharper.UI.Templating.Tests.js"></script>
<h1 ws-anchor="Header">Template holes directly in the main file (<code>.Bind()</code>)</h1>
<p ws-hole="Hole">[FAIL] This should be replaced by a ws-hole.</p>
<p ws-replace="Replace">[FAIL] This should be replaced by a ws-replace.</p>
<p>${TextHole}</p>
<p class="fail" ws-attr="Attr">This is marked ok by ws-attr.</p>
<p ws-attr="MultiAttr">This is marked ok by ws-attr filled with ParamArray arguments.</p>
<p ws-onmouseenter="MouseEnter">This is marked ok by ws-onmouseenter.</p>
<p ws-onafterrender="AfterRender">[FAIL] This should be replaced by a ws-onafterrender.</p>
<p class="fail ${OkClass}">This is marked ok by a ${text} hole in an attribute.</p>
<textarea ws-var="TestVarInitialization">My initial val</textarea>
<input ws-var="TestVarInitialization2" value="Init from html" />
<input ws-var="TestVarInitialization3" type="number" value="15" />
<input ws-var="TestVarInitialization4" type="checkbox" checked />
<p>
${Input} This is marked ok by a ws-var on the following input (set on mouseenter).
<input ws-var="Input" ws-onmouseenter="InputMouseEnter" />
</p>
<p>${OkView} This is marked ok by a View ${text} hole.</p>
<p class="fail ${OkClassView}">This is marked ok by a ${text} View hole in an attribute.</p>
<p>
${VarView} This is marked ok by a (View, setter) Var method on the following input.
<input ws-var="VarView" />
</p>
<div ws-replace="ExtraOkItems"></div>
<p>The following is a ws-instantiation of Form.</p>
<ws-Form Username Submit Welcome></ws-Form>
<div id="main"></div>
<div ws-template="Form">
<ws-FormInput Var="Username"><Type>text</Type></ws-FormInput>
<br />
<ws-FormInput Var="Password"><Type>password</Type></ws-FormInput>
<br />
<input type="submit" value="Submit" ws-onclick="Submit" />
<div>${Welcome}</div>
<div ws-hole="LeaveEmpty">THIS SHOULD NOT APPEAR</div>
<!-- Regression Test for #170: out-of-order use of nested subtemplate -->
<input ws-template="FormInput" type="${Type}" ws-var="Var" />
</div>
<div ws-preserve>
<!-- Testing comment -->
There should be an unfilled hole here: ${AHoleHere}
<div ws-replace="DoNotFillThisHole">
[OK] ws-replace inside ws-preserve is not removed
</div>
<div ws-hole="DoNotFillThisHole2">
[OK] ws-hole inside ws-preserve is not emptied
</div>
</div>
<div class="new-inputs" ws-onafterrender="SelectPrint">
<input type="datetime-local" ws-var="DateTimeVar" />
<input type="file" ws-var="FileVar" />
<input type="range" ws-var="RangeVar" />
<select ws-var="MultipleSelect" multiple>
<option value="a">A</option>
<option value="b">B</option>
<option value="c">C</option>
</select>
</div>
<template name="HTML5Template">
<slot name="ThisIsOk"></slot>
<slot></slot>
</template>
<div ws-replace="imAHtml5-Template"></div>
<div class="test-dom">
<button ws-onclick="ReplaceOnClick">Replace with an input</button>
</div>
<div ws-dom="asd"><input ws-var="x" /></div>
<div style="background: red; width: 80px; height: 80px" ws-dom="ReplaceMe"></div>
<div ws-template="WSDOMINSIDETEMPLATE" ws-onclick="ClickHandler">
<div ws-dom="DomNode"></div>
</div>
<div ws-template="FileUploader" ws-dom="MyNode">
<p ws-replace="HideMe">Is this going to show up?</p>
<form>
<input type="text" value="Text here" />
<input id="uploader" ws-var="Uploader" type="file" />
<button ws-onclick="OnSubmit">Submit</button>
</form>
</div>
<div ws-replace="TestWSDom"></div>
<div ws-replace="TestWSDom2"></div>
<div ws-replace="CheckBooleanAttr"></div>
<script type="module" src="Content/WebSharper.UI.Templating.Tests.js"></script>
</body>
</html>

0 comments on commit 70ee5df

Please sign in to comment.