Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 111 additions & 1 deletion WebSharper.UI.Next.Formlets/Formlets.fs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ module Formlet =
let m = ListModel.Create fst []
let v =
m.View
|> View.ConvertBy m.Key snd
|> View.MapSeqCachedBy m.Key snd
|> View.Map Array.ofSeq
{
View =
Expand Down Expand Up @@ -404,6 +404,115 @@ module Formlet =

let Do = Builder()

module private ValidationView =

open WebSharper.UI.Next.Html

let validationMessage = function
| Success _ -> Doc.Empty
| Failure msg ->
let m = msg |> List.fold (fun a s -> a + "\n" + s) ""
text m

let inline respondResult (v : View<Result<_>>) (s, f) =
View.Map (
function
| Success _ -> s
| Failure _ -> f
) v

let toggleHidden (v : View<Result<_>>) =
respondResult v ("hidden", "visible")

let toggleError (v : View<Result<_>>) =
respondResult v ("", "errorFormlet")

let validationTooltipView res doc =
table [
tr [
tdAttr [attr.``class`` "tooltip"] [
doc
divAttr [attr.classDyn <| toggleHidden res] [
spanAttr
[attr.``class`` "tooltiptext"]
[Doc.BindView validationMessage res]
]
]
td [
divAttr [attr.classDyn <| toggleHidden res] [
divAttr [attr.``class`` "errorIcon"] []
]
]
]
] :> Doc

let validationMessageView res doc =
table [
tr [
tdAttr [attr.classDyn <| toggleError res] [
doc
]
td []
]
trAttr [attr.classDyn <| toggleHidden res] [
tdAttr [attr.``class`` "errorPanel"] [
Doc.BindView validationMessage res
]
td []
]
] :> Doc

let wrapIcon f v ls =
Layout.OfList ls
|> fun l ->
match l.Shape with
| Item x -> {l with Shape = Item (f v x)}
| _ -> l |> Layout.Wrap (f v)
|> fun x -> [x]

let withValidation f (flX : Formlet<'T>) =
let flx = flX.Data ()
Formlet (fun () ->
{
View = flx.View
Layout = wrapIcon f flx.View flx.Layout
})

let addTrigger l (sb : Submitter<_>) (x : Doc) =
match x with
| :? Elt as e ->
let itm = Item (DocExtensions.OnChange
(e, (fun _ _ -> sb.Trigger ())))
[{l with Shape = itm}]
| _ -> [l]

let addSubmitter sb flx =
match flx.Layout with
| [l] ->
match l.Shape with
| Item x -> addTrigger l sb x
| _ -> [l]
| l -> l

module V = ValidationView

let WithValidationIcon (flX : Formlet<'T>) =
V.withValidation V.validationTooltipView flX

let WithValidationMessage (flX : Formlet<'T>) =
V.withValidation V.validationMessageView flX

let ValidateOnChange init (flX : Formlet<'T>) =
let flx = flX.Data()
let sb = Submitter.Create flx.View (Success init)
Formlet (fun () ->
{
View = sb.View
Layout = V.addSubmitter sb flx
}
)


[<AutoOpen; JavaScript>]
module Pervasives =

Expand Down Expand Up @@ -507,3 +616,4 @@ module Validation =
let IsMatch regex msg flX =
let re = RegExp(regex)
Is re.Test msg flX

64 changes: 62 additions & 2 deletions WebSharper.UI.Next.Formlets/styles/Formlet.css
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,12 @@
.formlet select,
.formlet textarea
{
font-size: 14px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
padding : 4px;
margin : 5px;
border:solid 1px #aacfe4;
width:250px;
border: solid 1px #aacfe4;
width: 250px;
display: block;
}
.formlet select
Expand Down Expand Up @@ -190,12 +192,14 @@

.formlet .validIcon, .formlet .removeIcon, .formlet .addIcon, .formlet .infoIcon , .formlet .errorIcon
{
position: absolute;
display : block;
width : 24px;
height : 24px;
padding : 0;
margin:2px;
margin-left : 5px;
margin-top : -12px;
background-repeat : no-repeat;
background-position : center;
cursor : pointer;
Expand Down Expand Up @@ -225,3 +229,59 @@
{
background-image :url("InfoIcon.png");
}

@keyframes fadein {
from { opacity: 0; }
to { opacity: 1; }
}

@keyframes fadeout {
from { opacity: 0; }
to { opacity: 1; }
}

.formlet .visible {
opacity: 1;
animation: fadeout 0.3s ease-in;
}

.formlet .hidden {
opacity: 0;
animation: fadein 0.3s ease-out;
}

/* Tooltip container */
.formlet .tooltip {
position: relative;
display: inline-block;
opacity: 1;
}

/* Tooltip text */
.formlet .tooltip .tooltiptext {
bottom: 110%;
left: 50%;
width: 240px;
margin-left: -120px;
background-color: dimgray;
color: #fff;
text-align: center;
padding: 5px 0;
border-radius: 6px;
position: absolute;
z-index: 1;
opacity: inherit;
}

/* Tooltip arrow */
.formlet .tooltip .tooltiptext::after {
content: " ";
position: absolute;
top: 100%;
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: dimgray transparent transparent transparent;
opacity: inherit;
}