Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file attr.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271open!BaseopenJs_of_ocaml(** This has 3 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)
- Those which we immediately convert into Js called Raw, which
in turn has two cases:
- Property for properties on the DOM
- Attribute for attributes on the DOM
- Hooks, which register callbacks on property addition and removal.
v}
Generally speaking one should avoid creating a property or attribute
for something for which we have a first class representation.
*)moduleHook:sigtypetvalcreate:init:(Dom_html.elementJs.t->'state)->?update:('state->Dom_html.elementJs.t->'state)->?destroy:('state->Dom_html.elementJs.t->unit)->id:'stateType_equal.Id.t->tvalpack:t->Js.Unsafe.anyend=structtypet=Js.Unsafe.anyletgeneric_hook=lazyJs.Unsafe.(getglobal(Js.string"GenericHook"))letcreate~init?update?destroy~id=letwrapa=a|>Js.wrap_callback|>Js.Unsafe.injectinletinit=wrapinitinletupdate=update|>Option.map~f:Js.wrap_callback|>Js.Opt.option|>Js.Unsafe.injectinletdestroy=destroy|>Option.value~default:(fun__->())|>wrapinletgeneric_hook=Lazy.forcegeneric_hookinJs.Unsafe.fun_callgeneric_hook[|init;update;destroy;id|>Js.Unsafe.inject|];;letpack=Fn.idendmoduleRaw:sigtypet(** {2 Attribute creation functions *)valcreate:string->string->tvalcreate_float:string->float->tvalcreate_hook:string->Hook.t->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.any|Hookofstring*Hook.tletcreatenamevalue=Attribute(name,Js.Unsafe.inject(Js.stringvalue))letcreate_floatnamevalue=Attribute(name,Js.Unsafe.inject(Dom_float.to_js_stringvalue));;letpropertynamevalue=Property(name,value)letstring_propertynamevalue=Property(name,Js.Unsafe.inject(Js.stringvalue))letbool_propertynamevalue=Property(name,Js.Unsafe.inject(Js.boolvalue))letcreate_hooknamehook=Hook(name,hook)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|Hook(name,hook)->Js.Unsafe.setattrs_obj(Js.stringname)(Hook.packhook)|Property("value",value)->letvalue=softSetHookvalueinJs.Unsafe.setattrs_obj(Js.string"value")value|Property(name,value)->Js.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)lettitlex=create"title"xletsrcx=create"src"xletminx=create_float"min"xletmaxx=create_float"max"xletoneventconvert_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)moduleExpert=structletcreate_basic_hookname?hook?unhook()=lethook=Option.valuehook~default:(Fn.const())inletunhook=Option.mapunhook~f:(funf()->f)inletid=Type_equal.Id.create~name:"placeholder"[%sexp_of:unit]inRaw(Raw.create_hookname(Hook.create~init:hook?update:None?destroy:unhook~id));;letcreate_stateful_hookname~hook~unhook~id=Raw(Raw.create_hookname(Hook.create~init:hook?update:None~destroy:unhook~id));;letcreate_persistent_hookname~init~update~destroy~id=Raw(Raw.create_hookname(Hook.create~init~update~destroy~id));;end