123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338open!CoreopenBonsai_webopenBonsai.Let_syntaxmoduleView=structmodulePrivate=ViewincludeViewletto_vdom?(custom=View.to_vdom)?on_submit?editablet=custom?on_submit?editablet;;endmoduleT=structtype('read,'write)unbalanced={value:'readOr_error.t;view:View.t;set:'write->unitVdom.Effect.t}[@@derivingfields]type'at=('a,'a)unbalancedletvalue_or_defaultt~default=t|>value|>Or_error.ok|>Option.value~defaultletnormalize{value;set;view=_}=matchvaluewith|Okvalue->setvalue|Error_->Ui_effect.Ignore;;endincludeTmoduleSubmit=structtype'at={f:'a->unitUi_effect.t;handle_enter:bool;button_text:stringoption}letcreate?(handle_enter=true)?(button=Some"submit")~f()={f;handle_enter;button_text=button};;endletview_as_vdom?on_submit?(editable=`Yes_always)t=leton_submit=Option.mapon_submit~f:(fun{Submit.f;handle_enter;button_text}->leton_submit=t.value|>Result.ok|>Option.map~fin{View.on_submit;handle_enter;button_text})inView.to_vdom?on_submit~editablet.view;;letis_validt=Or_error.is_okt.valueletreturnvalue={value=Okvalue;view=View.Empty;set=(fun_->Ui_effect.Ignore)};;letreturn_errorerror={value=Errorerror;view=View.Empty;set=(fun_->Ui_effect.Ignore)};;letmap_errort~f={twithvalue=t.value|>Result.map_error~f}letmapt~f=letvalue=Or_error.mapt.value~fin{value;view=t.view;set=t.set};;letcontra_mapt~f=letseta=t.set(fa)in{value=t.value;view=t.view;set};;letbothab=letvalue=Or_error.botha.valueb.valueinletview=View.concata.viewb.viewinletset(ea,eb)=Ui_effect.Many[a.setea;b.seteb]in{value;view;set};;letboth_for_profunctorab=letvalue=Or_error.botha.valueb.valueinletview=View.concata.viewb.viewinletsetv=Ui_effect.Many[a.setv;b.setv]in{value;view;set};;letlabel'labelt={twithview=View.set_labellabelt.view}letlabeltext=label'(Vdom.Node.texttext)lettooltip'tooltipt={twithview=View.set_tooltiptooltipt.view}lettooltiptext=tooltip'(Vdom.Node.texttext)letgroup'labelt={twithview=View.grouplabelt.view}letgrouptext=group'(Vdom.Node.texttext)letproject't~parse~unparse=letvalue=Or_error.bindt.value~f:(funa->Or_error.try_with_join(fun()->parsea))inletseta=t.set(unparsea)in{value;view=t.view;set};;letvalidatet~f=project't~parse:(funa->fa|>Or_error.map~f:(fun()->a))~unparse:Fn.id;;letprojectt~parse_exn~unparse=project't~parse:(funa->Ok(parse_exna))~unparseletoptional'(typeab)(t:at)~parse~unparse~none:boptiont=project't~parse~unparse:(Option.value_map~default:none~f:unparse);;letoptionalt~is_some~none=letparsea=ifis_someathenOk(Somea)elseOkNoneinoptional't~parse~unparse:Fn.id~none;;moduleRecord_builder=structincludeProfunctor.Record_builder(structtype('read,'write)t=('read,'write)unbalancedletboth=both_for_profunctorletmap=mapletcontra_map=contra_mapend)letlabel_of_fieldfieldslib_field=fieldslib_field|>Fieldslib.Field.name|>String.map~f:(function|'_'->' '|other->other)|>Vdom.Node.text;;letattach_fieldname_to_errortfieldslib_field=Result.map_errort.value~f:(Error.tag~tag:(sprintf"in field %s"(Fieldslib.Field.namefieldslib_field)));;(* This function "overrides" the [field] function inside of Record_builder
by adding a label *)letfieldtfieldslib_field=letlabel=label_of_fieldfieldslib_fieldinletvalue=attach_fieldname_to_errortfieldslib_fieldinletview=View.suggest_labellabelt.viewinletwith_label={twithview;value}infieldwith_labelfieldslib_field;;letbuild_for_recorda=lett=build_for_recordain{twithview=View.Group{label=None;tooltip=None;view=t.view}};;endmoduleDynamic=structletwith_defaultdefaultform=letopenBonsai.Let_syntaxinlet%subis_loaded,set_is_loaded=Bonsai.state[%here](moduleBool)~default_model:falseinlet%sub()=Bonsai.Edge.lifecycle~on_activate:(let%mapdefault=defaultandis_loaded=is_loadedandset_is_loaded=set_is_loadedandform=forminifnotis_loadedthenUi_effect.Many[setformdefault;set_is_loadedtrue]elseUi_effect.Ignore)()inreturnform;;leterror_hintt=letfview~error=letif_not_nonevalue~f=Option.value_mapvalue~default:Fn.id~finview|>if_not_noneerror~f:View.suggest_errorinlett=let%suberror_hovered=Bonsai.state[%here](moduleBool)~default_model:falseinlet%suberror_clicked=Bonsai.state[%here](moduleBool)~default_model:falseinBonsai.read@@let%mapt=tandis_error_hovered,set_error_hovered=error_hoveredandis_clicked,set_clicked=error_clickedinleton_click=set_clicked(notis_clicked)inleterror_details=Option.map(Result.errort.value)~f:(funerror->{View.Error_details.is_viewing=is_error_hovered||is_clicked;error;on_mouse_over=set_error_hoveredtrue;on_mouse_out=set_error_hoveredfalse;on_click;is_toggled=is_clicked})in{twithview=ft.view~error:error_details}int;;letcollapsible_group?(starts_open=true)labelt=let%subopen_state=Bonsai.state[%here](moduleBool)~default_model:starts_openinBonsai.read@@let%mapis_open,set_is_open=open_stateandlabel=labelandt=tinletlabel=Vdom.Node.div~attr:(Vdom.Attr.many_without_merge[Vdom.Attr.on_click(fun_->set_is_open(notis_open));Vdom.Attr.styleCss_gen.(user_select`None@>Css_gen.create~field:"cursor"~value:"pointer")])[Vdom.Node.text(ifis_openthen"▾ "^labelelse"► "^label)]inletform=group'labeltinletview=matchis_open,form.viewwith|false,Group{label;tooltip;_}->View.Group{label;tooltip;view=Empty}|_,other->otherin{formwithview};;leton_change(typea)?(on_error=Value.return(Fn.constUi_effect.Ignore))(moduleM:Bonsai.Modelwithtypet=a)~fvalue_to_watch=letmoduleM_or_error=structtypet=M.tOr_error.t[@@derivingequal,sexp]endinletcallback=let%mapf=fandon_error=on_errorinfunction|Errore->on_errore|Oknew_value->fnew_valueinBonsai.Edge.on_change[%here](moduleM_or_error)(value_to_watch>>|value)~callback;;letvalidate_via_effect(typea)(moduleInput:Bonsai.Modelwithtypet=a)(t:atBonsai.Value.t)~f=letmoduleValidated=structtypet=Input.tOr_error.t[@@derivingsexp,equal]endinmatch%subt>>|valuewith|Error_->Bonsai.readt|Okvalue->let%subvalidation=Bonsai.Edge.Poll.(effect_on_change[%here](moduleInput)(moduleValidated)Starting.emptyvalue~effect:(let%mapf=finfuna->match%map.Effectfawith|Ok()->Oka|Errore->Errore))inBonsai.read@@let%mapt=tandvalidation=validationinvalidatet~f:(funa->matchvalidationwith|Some(Okx)whenInput.equalax->Ok()|None|Some(Ok_)->Error(Error.of_string"validating...")|Some(Errore)->Errore);;moduleRecord_builder=structincludeProfunctor.Record_builder(structtype('read,'write)t=('read,'write)unbalancedValue.tletbothab=Value.map2ab~f:both_for_profunctorletmapa~f=Value.mapa~f:(map~f)letcontra_mapa~f=Value.mapa~f:(contra_map~f)end)letfield?(group_lists=true)tfieldslib_field=letlabel=Record_builder.label_of_fieldfieldslib_fieldinletwith_label=let%mapt=tinletview=(ifgroup_liststhenView.group_listt.viewelset.view)|>View.suggest_labellabelinletvalue=Record_builder.attach_fieldname_to_errortfieldslib_fieldin{twithview;value}infieldwith_labelfieldslib_field;;letbuild_for_recordcreator=Bonsai.read@@let%mapt=build_for_recordcreatorin{twithview=View.Group{label=None;tooltip=None;view=t.view}};;endendmoduleExpert=structletcreate=Fields_of_unbalanced.createendmodulePrivate=structletgroup_listt={twithview=View.group_listt.view}letsuggest_labellabelt={twithview=View.suggest_label(Vdom.Node.textlabel)t.view};;end