123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213openBaseopenJs_of_ocaml(** This has 2 kinds of constructors. {v
- First class constructors for properties / attributes for which we
have written first class ocaml representations (so far only Style
and Class)
- And those which we immediatly convert into Js called Raw, which
in turn has to cases:
- Property for properties on the DOM
- Attribute for attributes on the DOM
v}
Generally speaking one should avoid creating a property or attribute
for something for which we have a first class representation.
*)moduleRaw:sigtypet(** {2 Attribute creation functions *)valcreate:string->string->tvalcreate_float:string->float->t(** {2 Property creation functions *)valproperty:string->Js.Unsafe.any->tvalstring_property:string->string->tvalbool_property:string->bool->tvallist_to_obj:tlist-><>Js.tend=structtypet=|Propertyofstring*Js.Unsafe.any|Attributeofstring*Js.Unsafe.anyletcreatenamevalue=Attribute(name,Js.Unsafe.inject(Js.stringvalue))letcreate_floatnamevalue=Attribute(name,Js.Unsafe.inject(Js.number_of_floatvalue)##toString);;letpropertynamevalue=Property(name,value)letstring_propertynamevalue=Property(name,Js.Unsafe.inject(Js.stringvalue))letbool_propertynamevalue=Property(name,Js.Unsafe.inject(Js.boolvalue))letlist_to_objattrs=(* When input elements have their value set to what it already is
the cursor gets moved to the end of the field even when the user
is editing in the middle. SoftSetHook (from ./soft-set-hook.js)
compares before setting, avoiding the problem just like in
https://github.com/Matt-Esch/virtual-dom/blob/947ecf92b67d25bb693a0f625fa8e90c099887d5/virtual-hyperscript/index.js#L43-L51
note that Elm's virtual-dom includes a workaround for this so
if we switch to that the workaround here will be unnecessary.
https://github.com/elm-lang/virtual-dom/blob/17b30fb7de48672565d6227d33c0176f075786db/src/Native/VirtualDom.js#L434-L439
*)letsoftSetHookx=Js.Unsafe.global##SoftSetHookxinletattrs_obj=Js.Unsafe.obj[||]inList.iter~f:(function|Property(name,value)->letvalue=ifString.(=)name"value"thensoftSetHookvalueelsevalueinJs.Unsafe.setattrs_obj(Js.stringname)value|Attribute(name,value)->ifnot(Js.Optdef.testattrs_obj##.attributes)thenattrs_obj##.attributes:=Js.Unsafe.obj[||];Js.Unsafe.setattrs_obj##.attributes(Js.stringname)value)attrs;attrs_obj;;endtypet=|StyleofCss_gen.t|Classof(string,String.comparator_witness)Set.t|RawofRaw.tletto_style=function|Styles->Somes|Class_|Raw_->None;;letstylecss=Stylecssletstyle_to_rawcss=letprops=Css_gen.to_string_listcssinletobj=Js.Unsafe.obj[||]inList.iter~f:(fun(k,v)->Js.Unsafe.setobj(Js.stringk)(Js.stringv))props;Raw.property"style"obj;;letvalid_class_names=letinvalid=String.is_emptys||String.existss~f:Char.is_whitespaceinnotinvalid;;let%test"valid"=valid_class_name"foo-bar"let%test"invalid-empty"=not(valid_class_name"")let%test"invalid-space"=not(valid_class_name"foo bar")letclass_classname=ifnot(valid_class_nameclassname)thenraise_s[%message"invalid classname"(classname:string)];Class(Set.singleton(moduleString)classname);;letclasses'classes=Classclassesletclassesclassnames=ifnot(List.for_all~f:valid_class_nameclassnames)thenraise_s[%message"invalid classnames"(classnames:stringlist)];classes'(Set.of_list(moduleString)classnames);;letto_class=function|Classcs->Somecs|Style_|Raw_->None;;letclass_to_rawclasses=Raw.create"class"(String.concat(Set.to_listclasses)~sep:" ");;letcreatenamevalue=Raw(Raw.createnamevalue)letcreate_floatnamevalue=Raw(Raw.create_floatnamevalue)letpropertynamevalue=Raw(Raw.propertynamevalue)letstring_propertynamevalue=Raw(Raw.string_propertynamevalue)letbool_propertynamevalue=Raw(Raw.bool_propertynamevalue)letids=create"id"sletnames=create"name"slethrefr=create"href"rletchecked=create"checked"""letselected=create"selected"""lethidden=create"hidden"""letdisabled=create"disabled"""letplaceholderx=create"placeholder"xletautofocusb=create"autofocus"(Bool.to_stringb)letfor_x=create"for"xlettype_x=create"type"xletvaluex=create"value"xlettabindexx=create"tabindex"(Int.to_stringx)letoneventconvert_to_vdom_event:t=letfe=Event.Expert.handlee(convert_to_vdom_evente);Js._trueinproperty("on"^event)(Js.Unsafe.inject(Dom.handlerf));;leton_focus=on"focus"leton_blur=on"blur"leton_click=on"click"leton_contextmenu=on"contextmenu"leton_double_click=on"dblclick"leton_mousemove=on"mousemove"leton_mouseup=on"mouseup"leton_mousedown=on"mousedown"leton_mouseenter=on"mouseenter"leton_mouseleave=on"mouseleave"leton_mouseover=on"mouseover"leton_mouseout=on"mouseout"leton_keyup=on"keyup"leton_keypress=on"keypress"leton_keydown=on"keydown"letconst_ignore_=Event.Ignoreclasstypevalue_element=objectinheritDom_html.elementmethodvalue:Js.js_stringJs.tJs.propendtypevalue_coercion=Dom_html.elementJs.t->value_elementJs.tJs.optletrun_coercioncoerciontargetprev=matchprevwith|Some_->prev|None->Js.Opt.to_option(coerciontarget);;letcoerce_value_elementtarget=letopenDom_html.CoerceToinNone|>run_coercion(input:>value_coercion)target|>run_coercion(select:>value_coercion)target|>run_coercion(textarea:>value_coercion)target;;leton_input_eventeventhandler=onevent(funev->Js.Opt.caseev##.targetconst_ignore(funtarget->Option.value_map(coerce_value_elementtarget)~default:Event.Ignore~f:(funtarget->lettext=Js.to_stringtarget##.valueinhandlerevtext)));;leton_change=on_input_event"change"leton_input=on_input_event"input"letto_raw=function|Rawr->r|Stylecss->style_to_rawcss|Classclasses->class_to_rawclasses;;letlist_to_objl=Raw.list_to_obj(List.mapl~f:to_raw)