From ff3002143430bd1a124b23b6b2cc12369f5c6a76 Mon Sep 17 00:00:00 2001 From: Swrup Date: Tue, 16 Apr 2024 02:15:55 +0200 Subject: [PATCH] add text/color input --- examples/slider.ml | 14 +-- examples/style.css | 2 +- lib/slippery_slidy.ml | 202 +++++++++++++++++++++++++++++++---------- lib/slippery_slidy.mli | 65 +++++++++---- 4 files changed, 210 insertions(+), 73 deletions(-) diff --git a/examples/slider.ml b/examples/slider.ml index ea5b849..6440ce9 100644 --- a/examples/slider.ml +++ b/examples/slider.ml @@ -1,6 +1,7 @@ (* This add a slidder to into
moving the slider print its value to the console *) open Brr +open Slippery_slidy let append_el_to_main el = let main = @@ -13,21 +14,20 @@ let append_el_to_main el = let () = let datalist = - Slippery_slidy.mk_datalist + mk_datalist (List.map Jstr.of_float [ 2.; 3.; 5.; 8.; 13. ]) "my-slider-datalist" in let slider = - Slippery_slidy.mk_slider ~min:0. ~max:100. ~step:Any ~value:50. - ~id:"my-slider" ~label:"Important slider, slide carefully" + mk_slider ~min:0. ~max:100. ~step:Any ~value:50. ~id:"my-slider" + ~label:"Important slider, slide carefully" ~datalist_id:(Some datalist.datalist_id) in - append_el_to_main slider.el; + append_el_to_main (el slider); (* don't forget to add datalist to document too! *) append_el_to_main datalist.datalist_el; (* add listener on slider change *) - Slippery_slidy.add_slider_input_listener slider (fun x -> - Printf.printf "slider value: %f !!@." x ); - Slippery_slidy.mk_dragable slider; + add_input_listener slider (fun x -> Printf.printf "slider value: %f !!@." x); + mk_dragable slider; () diff --git a/examples/style.css b/examples/style.css index a1dd884..7f81773 100644 --- a/examples/style.css +++ b/examples/style.css @@ -1,4 +1,4 @@ -.slippery-slidy-container { +.brridget-container { display: flex; flex-direction: column; position: fixed; diff --git a/lib/slippery_slidy.ml b/lib/slippery_slidy.ml index 3a42549..dcc2eb4 100644 --- a/lib/slippery_slidy.ml +++ b/lib/slippery_slidy.ml @@ -5,20 +5,53 @@ type datalist = ; datalist_el : El.t } -type slider = +type widget = { id : string ; el : El.t - ; actual_slider_el : El.t + ; input_el : El.t } +type _ t = + | Text : widget -> string t + | Slider : widget -> float t + | Color : widget -> string t + (* Any is for continuous slider *) type step_kind = | Any | Step_value of float -let container_class = "slippery-slidy-container" +let slider_class = "brridget-slider" -let slider_class = "slippery-slidy-slider" +let slider_container_class = slider_class ^ "-container" + +let text_class = "brridget-text" + +let text_container_class = text_class ^ "-container" + +let color_class = "brridget-color" + +let color_container_class = color_class ^ "-container" + +let container_class = "brridget-container" + +let to_widget : type a. a t -> widget = function + | Slider r | Text r | Color r -> r + +let el : type a. a t -> El.t = + fun o -> + let r = to_widget o in + r.el + +let input_el : type a. a t -> El.t = + fun o -> + let r = to_widget o in + r.input_el + +let id : type a. a t -> string = + fun o -> + let r = to_widget o in + r.id let step_to_string step = match step with Any -> "any" | Step_value f -> Float.to_string f @@ -33,6 +66,18 @@ let mk_datalist : Jstr.t list -> string -> datalist = in { datalist_id = id; datalist_el = datalist } +let mk_widget ~label ~id ~at ~container_at = + let input_el = El.input ~d:G.document ~at () in + let label = + El.label ~d:G.document + ~at:[ At.for' (Jstr.v id) ] + [ El.txt' ~d:G.document label ] + in + let container_el = + El.div ~d:G.document ~at:container_at [ label; input_el ] + in + { id; el = container_el; input_el } + (* make a div of class slippery-slidy-container containing a slider of class slippery-slidy-slider *) let mk_slider : @@ -43,7 +88,7 @@ let mk_slider : -> id:string -> label:string -> datalist_id:string option - -> slider = + -> float t = fun ~min ~max ~step ~value ~id ~label ~datalist_id -> (* some checks *) let b = min <= max && value <= max && value >= min in @@ -55,33 +100,96 @@ let mk_slider : | None -> [] | Some datalist_id -> [ At.v (Jstr.v "list") (Jstr.v datalist_id) ] in - let input_el = - El.input ~d:G.document - ~at: - ( datalist - @ [ At.type' (Jstr.v "range") - ; At.float (Jstr.v "min") min - ; At.float (Jstr.v "max") max - ; At.v (Jstr.v "step") (Jstr.v (step_to_string step)) - ; At.value (Jstr.of_float value) - ; At.id (Jstr.v id) - ; At.class' (Jstr.v slider_class) - ] ) - () + let at = + datalist + @ [ At.type' (Jstr.v "range") + ; At.float (Jstr.v "min") min + ; At.float (Jstr.v "max") max + ; At.v (Jstr.v "step") (Jstr.v (step_to_string step)) + ; At.value (Jstr.of_float value) + ; At.id (Jstr.v id) + ; At.class' (Jstr.v slider_class) + ] in - let label = - El.label ~d:G.document - ~at:[ At.for' (Jstr.v id) ] - [ El.txt' ~d:G.document label ] + let container_at = + [ At.class' (Jstr.v slider_container_class) + ; At.class' (Jstr.v container_class) + ] in - let slider_container_el = - El.div ~d:G.document - ~at:[ At.class' (Jstr.v container_class) ] - [ label; input_el ] - in - { id; el = slider_container_el; actual_slider_el = input_el } + Slider (mk_widget ~at ~container_at ~label ~id) -let add_slider_input_listener slider f = +let mk_text : + min:int option + -> max:int option + -> size:int option + -> datalist_id:string option + -> value:string + -> id:string + -> label:string + -> string t = + fun ~min ~max ~size ~datalist_id ~value ~id ~label -> + let min = Option.map (At.int (Jstr.v "min")) min in + let max = Option.map (At.int (Jstr.v "max")) max in + let size = Option.map (At.int (Jstr.v "size")) size in + let datalist = + Option.map (fun id -> At.v (Jstr.v "list") (Jstr.v id)) datalist_id + in + let opts = List.filter_map Fun.id [ min; max; size; datalist ] in + let at = + opts + @ [ At.type' (Jstr.v "text") + ; At.value (Jstr.v value) + ; At.id (Jstr.v id) + ; At.class' (Jstr.v text_class) + ] + in + let container_at = + [ At.class' (Jstr.v text_container_class) + ; At.class' (Jstr.v container_class) + ] + in + Text (mk_widget ~at ~container_at ~label ~id) + +let color_regex = + let regexp = Jv.get Jv.global "RegExp" in + let re = Jv.new' regexp [| Jv.of_string "^#[0-9a-fA-F]{6}$" |] in + re + +let validate_color s = + let r = Jv.call color_regex "test" [| Jv.of_string s |] in + Jv.to_bool r + +(* The value must be in seven-character hexadecimal notation, meaning the "#" character followed by two digits each representing red, green, and blue, like this: #rrggbb. *) +let mk_color : value:string -> id:string -> label:string -> string t = + fun ~value ~id ~label -> + if not (validate_color value) then + raise + (Invalid_argument + (Printf.sprintf "mk_color: `%s` is not a valid hexadecimal color" value) + ) + else + let at = + [ At.type' (Jstr.v "color") + ; At.value (Jstr.v value) + ; At.id (Jstr.v id) + ; At.class' (Jstr.v color_class) + ] + in + let container_at = + [ At.class' (Jstr.v color_container_class) + ; At.class' (Jstr.v container_class) + ] + in + Color (mk_widget ~at ~container_at ~label ~id) + +let conv : type a. a t -> Jv.t -> a = + fun w -> + match w with + | Text _ -> Jv.to_string + | Slider _ -> Jv.to_float + | Color _ -> (* TODO validate here and make a color type? *) Jv.to_string + +let add_input_listener t f = (* we do not use Ev.input because it makes a Ev.Input.t but: "For