open Brr type datalist = { datalist_id : string ; datalist_el : El.t } type widget = { id : string ; 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 slider_class = "brridget-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 let mk_datalist : Jstr.t list -> string -> datalist = fun l id -> let option_el_l = List.map (fun s -> El.option ~d:G.document ~at:[ At.value s ] []) l in let datalist = El.datalist ~d:G.document ~at:[ At.id (Jstr.v id) ] option_el_l 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 : min:float -> max:float -> step:step_kind -> value:float -> id:string -> label:string -> datalist_id:string option -> float t = fun ~min ~max ~step ~value ~id ~label ~datalist_id -> (* some checks *) let b = min <= max && value <= max && value >= min in if not b then raise (Invalid_argument "mk_slider: parameters did not pass validation") else let datalist = match datalist_id with | None -> [] | Some datalist_id -> [ At.v (Jstr.v "list") (Jstr.v datalist_id) ] in 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 container_at = [ At.class' (Jstr.v slider_container_class) ; At.class' (Jstr.v container_class) ] in Slider (mk_widget ~at ~container_at ~label ~id) 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