From d17d17261faccb3eb42e91f88ca035e5b1730c66 Mon Sep 17 00:00:00 2001 From: Sébastien Dailly Date: Sun, 31 Jan 2021 04:21:01 +0100 Subject: Bindings to prosemirror --- editor/editor.ml | 263 +++++++++++++++++++++++++++---------------------------- 1 file changed, 130 insertions(+), 133 deletions(-) (limited to 'editor/editor.ml') diff --git a/editor/editor.ml b/editor/editor.ml index aeb96c1..be15718 100755 --- a/editor/editor.ml +++ b/editor/editor.ml @@ -1,132 +1,11 @@ +open Js_of_ocaml open Brr open Note -module Prop - : sig - type ('a, 'b) prop - - val prop - : string -> ('a, 'b) prop - - val get - : 'a -> ('a, 'b) prop -> 'b option - - val set - : 'a -> ('a, 'b) prop -> 'b -> unit - end - -= struct - - type ('a, 'b) prop = Jv.prop' - - let prop - : string -> ('a, 'b) prop - = Jstr.of_string - - let get - : 'a -> ('a, 'b) prop -> 'b option - = fun obj prop -> - Jv.get' (Jv.Id.to_jv obj) prop - |> Jv.to_option Jv.Id.of_jv - - let set - : 'a -> ('a, 'b) prop -> 'b -> unit - = fun obj prop value -> - Jv.set' - (Jv.Id.to_jv obj) - prop - (Jv.Id.to_jv value) - -end - -module Quill = struct - - type t = Jv.t - - type options - - let bounds - : (options, El.t) Prop.prop - = Prop.prop "bounds" - - let debug - : (options, Jstr.t) Prop.prop - = Prop.prop "debug" - - let placeholder - : (options, Jstr.t) Prop.prop - = Prop.prop "placeholder" - - let readonly - : (options, Jstr.t) Prop.prop - = Prop.prop "readonly" - - let theme - : (options, Jstr.t) Prop.prop - = Prop.prop "theme" - - let scrollingContainer - : (options, El.t) Prop.prop - = Prop.prop "scrollingContainer" - - let options - : unit -> options - = Jv.Id.of_jv @@ Jv.obj' [||] - - (* Constructor. - - [quill element] will create the editor inside the given element - - *) - let quill - : ?options:options -> El.t -> (t, Jv.Error.t) Result.t - = fun ?options element -> - let quill = Jv.get Jv.global "Quill" in - - let options = Jv.of_option ~none:Jv.undefined Jv.Id.to_jv options in - - match Jv.new' quill Jv.Id.[| to_jv element; options |] with - | exception Jv.Error e -> Error e - | v -> Ok v - - - type delta = Jv.t - - (* Operations is an array *) - type operations = Jv.t - - let ops - : (delta, operations) Prop.prop - = Prop.prop "ops" - - - (** Return the editor content *) - let get_contents - : t -> delta - = fun t -> - Jv.call t "getContents" [||] - - let set_contents - : t -> delta -> unit - = fun t contents -> - ignore @@ Jv.call t "setContents" [|contents|] - - (** [extract_content index length] return the content starting from index, with length elements *) - let extract_contents - : t -> int -> int -> delta - = fun t index length -> - Jv.call t "getContents" [|Jv.of_int index; Jv.of_int length|] - - let on_text_change - : t -> (string -> string -> string -> unit) -> unit - = fun t callback -> - ignore @@ Jv.call t "on" [|Jv.Id.to_jv @@ Jstr.v "text-change" ; Jv.repr callback|] - - -end let storage_key = (Jstr.v "content") +(** Save the text into the local storage *) let save_contents : Quill.t -> unit = fun editor -> @@ -135,9 +14,12 @@ let save_contents Brr_io.Storage.set_item storage storage_key - (Json.encode @@ Jv.Id.of_jv @@ contents) + (Json.encode @@ Quill.delta_to_json @@ contents) |> Console.log_if_error ~use:() + + +(** Load the content from the cache storage *) let load_contents : Quill.t -> unit = fun editor -> @@ -146,24 +28,21 @@ let load_contents storage storage_key |> Option.iter (fun contents -> - - Json.decode contents |> Result.map (fun json -> - Quill.set_contents editor json + Quill.delta_of_json json + |> Quill.set_contents editor ) |> Console.log_if_error ~use:() ) - - -let page_main id = +let quill id = begin match (Jv.is_none id) with | true -> Console.(error [str "No element with id '%s' found"; id]) | false -> let options = Quill.options () in - Prop.set options Quill.placeholder (Jstr.v "Nouvelle note…"); - Prop.set options Quill.theme (Jstr.v "snow"); + J.set options Quill.placeholder (Jstr.v "Nouvelle note…"); + J.set options Quill.theme (Jstr.v "snow"); (* Create the editor with the configuration *) Quill.quill ~options (Jv.Id.of_jv id) @@ -193,11 +72,129 @@ let page_main id = ) end +let create_new_state pm pm_model pm_state mySchema content = + let module PM = Prosemirror in + + let doc = PM.Model.( + DOMParser.parse + (DOMParser.from_schema pm_model mySchema) + (Jv.Id.of_jv content)) in + + let props = PM.State.creation_prop () in + props##.doc := Js.some doc; + props##.plugins := Js.some (PM.example_setup pm mySchema); + + PM.State.create + pm_state + props + +let storage_key = (Jstr.v "editor") +let prosemirror id content = + begin match (Jv.is_none id), (Jv.is_none content) with + | false, false -> + + let module PM = Prosemirror in + let pm = PM.v () in + + let ( let+ ) o f = Option.iter f o + and ( and+ ) a b = + match a, b with + | Some a, Some b -> Some (a, b) + | _ -> None + + in + + let+ pm_state = J.get pm PM.state + and+ pm_view = J.get pm PM.view + and+ pm_model = J.get pm PM.model + and+ schema_basic = J.get pm PM.schema_basic + and+ schema_list = J.get pm PM.schema_list + + in + + let _ = schema_basic + and _ = schema_list in + + let mySchema = Js_of_ocaml.Js.Unsafe.eval_string {|new PM.model.Schema({ + nodes: PM.schema_list.addListNodes(PM.schema_basic.schema.spec.nodes, "paragraph block*", "block"), + marks: PM.schema_basic.schema.spec.marks + })|} in + + + (* Create the initial state *) + let storage = Brr_io.Storage.local G.window in + let opt_data = Brr_io.Storage.get_item storage storage_key in + let state = match opt_data with + | None -> create_new_state pm pm_model pm_state mySchema content + | Some contents -> + (* Try to load from the storage *) + begin match Json.decode contents with + | Error _ -> create_new_state pm pm_model pm_state mySchema content + | Ok json -> + Console.(log [Jstr.v "Loading json"]); + + let history = PM.History.(history pm (history_prop ()) ) in + Console.(log [history]); + let _ = history in + + let obj = PM.State.configuration_prop () in + obj##.plugins := Js.some (PM.example_setup pm mySchema); + obj##.schema := mySchema; + PM.State.fromJSON pm_state obj json + end + in + + let props = PM.View.direct_editor_props () in + props##.dispatchTransaction := (Js.wrap_meth_callback (fun view transaction -> + Console.(log [ Jstr.v "Document size went from" + ; transaction##.before##.content##.size ]); + let state = view##.state##apply transaction in + view##updateState state + )); + props##.state := state; + + + let view = PM.View.editor_view + pm_view + (Jv.Id.of_jv id) + props in + + + view##setProps props; + + (* Attach an event on focus out *) + let _out_event = Brr_note.Evr.on_el + (Ev.focusout) + (fun _ -> + let contents = view##.state##toJSON () in + + let storage = Brr_io.Storage.local G.window in + Brr_io.Storage.set_item + storage + storage_key + (Json.encode @@ contents) + |> Console.log_if_error ~use:() + + + + ) + (Jv.Id.of_jv id) in + () + + | _, _-> Console.(error [str "No element with id '%s' '%s' found"; id ; content]) + + + + end + + + let () = let open Jv in let editor = obj - [| "attach", (repr page_main) + [| "attach_quill", (repr quill) + ; "attach_prosemirror", (repr prosemirror) |] in set global "editor" editor -- cgit v1.2.3