open Signatures open MethodView @send external closest: ({..}, string) => Nullable.t = "closest" @send external focus: {..} => unit = "focus" module Make = ( Term: TERM, Judgment: JUDGMENT with module Term := Term, JudgmentView: JUDGMENT_VIEW with module Term := Term and module Judgment := Judgment, MethodView: METHOD_VIEW with module Term := Term and module Judgment := Judgment, ) => { module Rule = Rule.Make(Term, Judgment) module ScopeView = ScopeView.Make(Term, JudgmentView.TermView) module Proof = Proof.Make(Term, Judgment, MethodView.Method) module Results = Method.MethodResults(Term) module ResultsView = { type menuState<'a> = { history: list>>>, // Stack of previous menus current: array>>, // What is currently visible } type props = { initialNodes: array>>, onApply: (MethodView.Method.t, Term.subst) => (), onBlur: (ReactEvent.Focus.t) => () } @react.componentWithProps let make = (props: props) => { let (state, setState) = React.useState(_ => { history: list{}, current: props.initialNodes, }) let goBack = _ => { setState(prev => { switch prev.history { | list{parent, ...rest} => {current: parent, history: rest} | list{} => prev // Already at the root } }) } let drillDown = (newNodes: array>) => { setState(prev => { history: list{prev.current, ...prev.history}, current: newNodes, }) }
{state.history != list{} ? : React.null}
{state.current->Array.mapWithIndex((node, i) => { switch node { | Action(label, nextTree, subst) => | Group(label, children) => | Delay(label, getChildren) => } })->React.array}
} } type props = { proof: Proof.checked, scope: array, ruleStyle: RuleView.style, gen: Term.gen, onChange: (Proof.checked, Term.subst) => unit, } module RuleView = RuleView.Make(Term, Judgment, JudgmentView) @react.componentWithProps let rec make = (props: props) => { let {sidebarRef} = React.useContext(SidebarContext.context) let (isFocused, setFocused) = React.useState(() => false) let onBlur = e => { let leavingProof = ReactEvent.Focus.relatedTarget(e) ->Option.flatMap(el => el->closest(".sidebar")->Nullable.toOption) ->Option.isNone if leavingProof { setFocused(_ => false) } } switch props.proof { | Proof.Checked({fixes, assumptions, method, rule}) => { let scope = Array.concat(fixes, props.scope) <>
    {Belt.Array.zipBy(assumptions, rule.premises, (n, r) => {
  • {React.string(n)}
  • })->React.array}
{switch method { | Goal(options) => let portal = switch sidebarRef.current->Nullable.toOption { | None => React.null | Some(node) => let res = options(props.gen); Portal.createPortal( <> { props.onChange( Proof.Checked({fixes, assumptions, method: Do(opt), rule}), subst, )} > } , node, ) }
{ setFocused(_ => true) ReactEvent.Focus.stopPropagation(e) }} > {if isFocused { <> {portal} } else { }}
| Do(method) => React.createElement( MethodView.make(p => make({ proof: p["proof"], scope: p["scope"], ruleStyle: p["ruleStyle"], gen: p["gen"], onChange: p["onChange"], }) ), { method, scope, ruleStyle: props.ruleStyle, gen: props.gen, onChange: (newm, subst) => { props.onChange( Proof.Checked({fixes, assumptions, method: Do(newm), rule}), subst, ) }, }, ) }}
} | Proof.ProofError({raw: _, rule: _, msg}) =>
{React.string(msg)}
} } }