mathquill_js_sys/
lib.rs

1#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
2#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/loading.md"))]
3
4use std::marker::PhantomData;
5
6use builder::ObjectBuilder;
7#[allow(unused_imports)]
8use tracing::{debug, error, info, trace, warn};
9
10use wasm_bindgen::prelude::*;
11use web_sys::js_sys;
12
13mod builder;
14
15/// API beginning
16#[wasm_bindgen]
17unsafe extern "C" {
18  /// <https://docs.mathquill.com/en/latest/Api_Methods/#api-methods>
19  pub type MathQuill;
20
21  #[wasm_bindgen(js_namespace = MathQuill)]
22  pub fn getInterface(version: u8) -> MathQuill;
23}
24
25impl MathQuill {
26  /// Don't call on a static field, see [`MathQuill::get_static_field`]
27  ///
28  /// IMPROVEME: Runtime-panic if accidentally called on a static field
29  pub fn get_field(&self, el: &web_sys::HtmlElement) -> Option<MathField> {
30    let api_as_fn: &js_sys::Function = self.unchecked_ref();
31    let res = api_as_fn.call1(&JsValue::NULL, el).unwrap();
32    if !res.is_truthy() {
33      return None;
34    }
35    // if res.is_instance_of::<MathField>() {
36    return Some(res.unchecked_into());
37    // } else {
38    //   warn!(
39    //     ?res,
40    //     "Called `MathQuill(el)` on an element that was not a MathField (maybe a StaticField)"
41    //   );
42    //   return None;
43    // }
44  }
45
46  /// Don't call on a mutable field, see [`MathQuill::get_field`]
47  ///
48  /// IMPROVEME: Runtime-panic if accidentally called on a mutable field
49  pub fn get_static_field(&self, el: &web_sys::HtmlElement) -> Option<StaticMath> {
50    let api_as_fn: &js_sys::Function = self.unchecked_ref();
51    let res = api_as_fn.call1(&JsValue::NULL, el).unwrap();
52    if !res.is_truthy() {
53      return None;
54    }
55    // if res.is_instance_of::<StaticMath>() {
56    return Some(res.unchecked_into());
57    // } else {
58    //   warn!(
59    //     ?res,
60    //     "Called `MathQuill(el)` on an element that was not a StaticMath (maybe a mutable MathField)"
61    //   );
62    //   return None;
63    // }
64  }
65}
66
67/// This is an owned, abstracted representation of the configuartion options
68/// supported by MathQuill.
69/// Dropping this struct invalidates callbacks on fields mounted by this config,
70/// see [`Handlers`].
71///
72/// Use [`Config::get_js_value`] to pass through the Wasm ABI interface
73pub struct Config<MathField> {
74  pub space_behaves_like_tab: Option<bool>,
75  pub handlers: Handlers<MathField>,
76}
77
78impl<T> Default for Config<T> {
79  fn default() -> Self {
80    Self {
81      space_behaves_like_tab: None,
82      handlers: Handlers::default(),
83    }
84  }
85}
86
87impl<T> std::fmt::Debug for Config<T> {
88  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89    f.debug_struct("Config")
90      .field("space_behaves_like_tab", &self.space_behaves_like_tab)
91      .field("handlers", &self.handlers)
92      .finish()
93  }
94}
95
96impl Config<MathField> {
97  pub fn get_js_value(&self) -> JsValue {
98    let mut obj = ObjectBuilder::new();
99    obj.set("spaceBehavesLikeTab", &self.space_behaves_like_tab.into());
100    obj.set("handlers", &self.handlers.get_js_value());
101    obj.build()
102  }
103}
104
105/// <https://docs.mathquill.com/en/latest/Config/#handlers>
106///
107/// You will have to think about manual memory management:
108/// <https://rustwasm.github.io/wasm-bindgen/reference/passing-rust-closures-to-js.html#heap-allocated-closures>
109pub struct Handlers<MathField> {
110  pub edit: Option<Closure<dyn FnMut()>>,
111  pub _phantom: PhantomData<MathField>,
112}
113
114impl<T> Default for Handlers<T> {
115  fn default() -> Self {
116    Self {
117      edit: None,
118      _phantom: PhantomData,
119    }
120  }
121}
122
123impl<T> std::fmt::Debug for Handlers<T> {
124  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125    f.debug_struct("Handlers")
126      .field("edit", &self.edit)
127      .finish()
128  }
129}
130
131impl Handlers<MathField> {
132  pub fn get_js_value(&self) -> JsValue {
133    let mut obj = ObjectBuilder::new();
134    obj.set(
135      "edit",
136      &self
137        .edit
138        .as_ref()
139        .map(|closure| closure.as_ref().clone())
140        .into(),
141    );
142    obj.build()
143  }
144}
145
146/// Mount
147#[wasm_bindgen]
148unsafe extern "C" {
149  pub type MathField;
150  /// <https://docs.mathquill.com/en/latest/Api_Methods/#mqstaticmathhtml_element>
151  #[wasm_bindgen(method)]
152  pub fn MathField(
153    this: &MathQuill,
154    html_element: &web_sys::HtmlElement,
155    config: JsValue,
156  ) -> MathField;
157
158  pub type StaticMath;
159  /// <https://docs.mathquill.com/en/latest/Api_Methods/#mqstaticmathhtml_element>
160  #[wasm_bindgen(method)]
161  pub fn StaticMath(this: &MathQuill, html_element: &web_sys::HtmlElement) -> StaticMath;
162}
163
164/// Syncing
165#[wasm_bindgen]
166unsafe extern "C" {
167  /// <https://docs.mathquill.com/en/latest/Api_Methods/#latex>
168  #[wasm_bindgen(method)]
169  pub fn latex(this: &StaticMath) -> String;
170
171  /// <https://docs.mathquill.com/en/latest/Api_Methods/#latex>
172  #[wasm_bindgen(method)]
173  pub fn latex(this: &MathField) -> String;
174
175  /// <https://docs.mathquill.com/en/latest/Api_Methods/#latexlatex_string>
176  #[wasm_bindgen(method, js_name = "latex")]
177  pub fn set_latex(this: &StaticMath, latex: &str);
178
179  /// <https://docs.mathquill.com/en/latest/Api_Methods/#latexlatex_string>
180  #[wasm_bindgen(method, js_name = "latex")]
181  pub fn set_latex(this: &MathField, latex: &str);
182
183  // https://docs.mathquill.com/en/latest/Api_Methods/#editable-mathfield-methods
184  // there are definitely more very interesting methods,
185  // like simulating user writing, focussing e.t.c
186  // open a PR to add these!
187}