Commit fdf4522cfe42a780852e72d7f2054f708e5cc2a5

Authored by Georg Hopp
1 parent 5de3a1b2

Add simple component creation code for markdown

  1 +use crate::api::markdown::Markdown;
  2 +use mogwai::prelude::*;
  3 +use super::state::MarkdownState;
  4 +
  5 +#[derive(Clone)]
  6 +pub(super) enum MarkdownLogic {
  7 + Choose(Option<i32>),
  8 + Discard,
  9 + Select,
  10 + Store,
  11 + Toggle,
  12 + Update,
  13 +}
  14 +
  15 +pub(super) async fn markdown_logic( mut rx_logic: broadcast::Receiver<MarkdownLogic>
  16 + , tx_logic: broadcast::Sender<MarkdownLogic>
  17 + , tx_toggle: broadcast::Sender<bool>
  18 + , tx_patches: mpmc::Sender<ListPatch<ViewBuilder<Dom>>>
  19 + , mut rx_dom: broadcast::Receiver<Dom> )
  20 +{
  21 + let dom = rx_dom.next().await.unwrap();
  22 + let mut md = Markdown::new("md-example").await.unwrap();
  23 + let mut state = MarkdownState::new(dom).unwrap();
  24 +
  25 + while let Some(msg) = rx_logic.next().await {
  26 + match msg {
  27 + MarkdownLogic::Store => {
  28 + let new_md = state.get_md();
  29 + if md.json.content != new_md {
  30 + md.json.content = state.get_md();
  31 + md.save().await.unwrap();
  32 + }
  33 + },
  34 + MarkdownLogic::Update => state.update(),
  35 + MarkdownLogic::Toggle => {
  36 + state.toggle_show_edit();
  37 + tx_toggle.broadcast(state.show_edit).await.unwrap();
  38 + },
  39 + MarkdownLogic::Discard => {
  40 + state.set_md(Some(md.json.content.as_str()));
  41 + state.update();
  42 + },
  43 + MarkdownLogic::Select => {
  44 + state.toggle_show_patches();
  45 +
  46 + match state.show_patches {
  47 + true => {
  48 + let patches = md
  49 + . patches().await.unwrap()
  50 + . into_iter()
  51 + . map(|diff| {
  52 + let id = Some(diff.id);
  53 + let choose_filter = tx_logic
  54 + . sink()
  55 + . contra_map(move |_| MarkdownLogic::Choose(id));
  56 + builder! {
  57 + <li><button on:click=choose_filter
  58 + value=format!("{}", diff.id.to_owned())>
  59 + {diff.date_created.to_owned()}
  60 + </button></li>
  61 + }});
  62 + let all = vec![builder! {
  63 + <li><button on:click=tx_logic.sink().contra_map(|_| MarkdownLogic::Choose(None))>
  64 + "Current"
  65 + </button></li>
  66 + }].into_iter().chain(patches);
  67 +
  68 + let list_replace = ListPatch::splice(.., all);
  69 + tx_patches.send(list_replace).await.unwrap();
  70 + },
  71 + false => tx_patches.send(ListPatch::drain()).await.unwrap(),
  72 + }
  73 + },
  74 + MarkdownLogic::Choose(id) => {
  75 + md.read(id).await.unwrap();
  76 + state.set_md(Some(md.json.content.as_str()));
  77 + state.update();
  78 + tx_patches.send(ListPatch::drain()).await.unwrap();
  79 + state.reset_show_patches();
  80 + },
  81 + }
  82 + }
  83 +}
... ...
  1 +mod logic;
  2 +mod state;
  3 +mod view;
  4 +
  5 +use mogwai::prelude::*;
  6 +
  7 +use self::{logic::markdown_logic, view::markdown_view};
  8 +
  9 +pub(crate) async fn new() -> Component<Dom> {
  10 + let (tx_dom, rx_dom) = broadcast::bounded(1);
  11 + let (tx_logic, rx_logic) = broadcast::bounded(1);
  12 + let (tx_toggle, rx_toggle) = broadcast::bounded(1);
  13 + let (tx_patches, rx_patches) = mpmc::bounded(1);
  14 +
  15 + let view = markdown_view(tx_logic.clone(), rx_toggle, rx_patches, tx_dom);
  16 + let logic = markdown_logic(rx_logic, tx_logic, tx_toggle, tx_patches, rx_dom);
  17 + Component::from(view).with_logic(logic)
  18 +}
... ...
  1 +use mogwai::prelude::*;
  2 +use wasm_bindgen::JsValue;
  3 +
  4 +pub(super) struct MarkdownState {
  5 + editor: HtmlElement,
  6 + display: HtmlElement,
  7 + pub(super) show_edit: bool,
  8 + pub(super) show_patches: bool,
  9 +}
  10 +
  11 +impl MarkdownState {
  12 + pub(super) fn new(dom: Dom) -> Result<Self, JsValue> {
  13 + let (editor, display) = match dom.inner_read() {
  14 + Either::Left(dom_js) => {
  15 + let node = &dom_js.to_owned().dyn_into::<Node>()?;
  16 + ( node . first_child().unwrap()
  17 + . first_child().unwrap()
  18 + . dyn_into::<HtmlElement>()?
  19 + , node . child_nodes().get(1).unwrap()
  20 + . child_nodes().get(1).unwrap()
  21 + . dyn_into::<HtmlElement>()? )
  22 + },
  23 + _ => Err(JsValue::UNDEFINED)?,
  24 + };
  25 + let show_edit = false;
  26 + let show_patches = false;
  27 +
  28 + Ok(Self { editor, display, show_edit, show_patches })
  29 + }
  30 +
  31 + pub(super) fn get_md(&self) -> String {
  32 + self.editor.inner_text()
  33 + }
  34 +
  35 + pub(super) fn set_md(&self, md :Option<&str>) {
  36 + self.editor.set_text_content(md)
  37 + }
  38 +
  39 + pub(super) fn update(&self) {
  40 + use pulldown_cmark::{Parser, Options, html};
  41 +
  42 + let mut html_out = String::new();
  43 + let md = self.get_md();
  44 + let parser = Parser::new_ext(&md, Options::all());
  45 +
  46 + html::push_html(&mut html_out, parser);
  47 + self.display.set_inner_html(&html_out)
  48 + }
  49 +
  50 + pub(super) fn toggle_show_edit(&mut self) {
  51 + self.show_edit = ! self.show_edit;
  52 + }
  53 +
  54 + #[allow(dead_code)]
  55 + pub(super) fn set_show_edit(&mut self) {
  56 + self.show_edit = true;
  57 + }
  58 +
  59 + #[allow(dead_code)]
  60 + pub(super) fn reset_show_edit(&mut self) {
  61 + self.show_edit = false;
  62 + }
  63 +
  64 + pub(super) fn toggle_show_patches(&mut self) {
  65 + self.show_patches = ! self.show_patches;
  66 + }
  67 +
  68 + #[allow(dead_code)]
  69 + pub(super) fn set_show_patches(&mut self) {
  70 + self.show_patches = true;
  71 + }
  72 +
  73 + pub(super) fn reset_show_patches(&mut self) {
  74 + self.show_patches = false;
  75 + }
  76 +}
... ...
  1 +use mogwai::prelude::*;
  2 +
  3 +use crate::data::icons::*;
  4 +use super::logic::MarkdownLogic;
  5 +
  6 +pub(super) fn markdown_view( tx_logic: broadcast::Sender<MarkdownLogic>
  7 + , rx_toggle: broadcast::Receiver<bool>
  8 + , rx_patches: mpmc::Receiver<ListPatch<ViewBuilder<Dom>>>
  9 + , tx_dom: broadcast::Sender<Dom>
  10 + ) -> ViewBuilder<Dom>
  11 +{
  12 + let input_filter = tx_logic
  13 + . sink()
  14 + . contra_map(|_| MarkdownLogic::Update);
  15 + let store_filter = tx_logic
  16 + . sink()
  17 + . contra_map(|_| MarkdownLogic::Store);
  18 + let toggle_filter = tx_logic
  19 + . sink()
  20 + . contra_map(|_| MarkdownLogic::Toggle);
  21 + let select_filter = tx_logic
  22 + . sink()
  23 + . contra_map(|_e| MarkdownLogic::Select);
  24 + let discard_filter = tx_logic
  25 + . sink()
  26 + . contra_map(|_| MarkdownLogic::Discard);
  27 +
  28 + let toggle_map = rx_toggle
  29 + . map(|t| match t {
  30 + true => String::from("block"),
  31 + false => String::from("none") });
  32 +
  33 + builder! {
  34 + <div class="input"
  35 + style:width="33%"
  36 + post:build=move |_: &mut Dom| {
  37 + tx_logic.try_broadcast(MarkdownLogic::Choose(None)).unwrap();
  38 + }
  39 + capture:view=tx_dom.sink()>
  40 + <div contenteditable="true"
  41 + style:cursor="text"
  42 + on:input=input_filter
  43 + style:display=("none", toggle_map)>
  44 + <pre></pre>
  45 + </div>
  46 + <div>
  47 + <div>
  48 + <div>
  49 + <button on:click=select_filter>{select_icon()}</button>
  50 + <ul patch:children=rx_patches>
  51 + </ul>
  52 + </div>
  53 + <button on:click=store_filter>{save_icon()}</button>
  54 + <button on:click=discard_filter>{undo_point_icon()}</button>
  55 + <button on:click=toggle_filter>{edit_icon()}</button>
  56 + </div>
  57 + <div></div>
  58 + <div></div>
  59 + </div>
  60 + </div>
  61 + }
  62 +}
... ...
  1 +pub(crate) mod markdown;
... ...
... ... @@ -2,242 +2,21 @@ mod api;
2 2 mod data;
3 3 mod error;
4 4 mod client;
  5 +mod component;
5 6
6   -use api::markdown::Markdown;
7   -use data::icons::*;
  7 +use std::panic;
  8 +
  9 +use crate::component::markdown;
8 10 use log::Level;
9 11 use mogwai::prelude::*;
10   -use std::panic;
11 12 use wasm_bindgen::prelude::*;
12 13
13   -#[derive(Clone)]
14   -enum AppLogic {
15   - Update,
16   - Toggle,
17   - Store,
18   - Select,
19   - Choose(Option<i32>),
20   - Discard,
21   -}
22   -
23   -async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic>
24   - , tx_logic: broadcast::Sender<AppLogic>
25   - , tx_toggle: broadcast::Sender<bool>
26   - , tx_patches: mpmc::Sender<ListPatch<ViewBuilder<Dom>>>
27   - , mut rx_dom: broadcast::Receiver<Dom> )
28   -{
29   - let dom = rx_dom.next().await.unwrap();
30   - let mut show_edit = false;
31   - let mut md = Markdown::new("md-example").await.unwrap();
32   -
33   - let container = match dom.inner_read() {
34   - Either::Left(dom_js) =>
35   - Some( ( dom_js
36   - . to_owned()
37   - . dyn_into::<Node>().unwrap()
38   - . first_child().unwrap()
39   - . first_child().unwrap()
40   - . dyn_into::<HtmlElement>().unwrap()
41   - , dom_js
42   - . to_owned()
43   - . dyn_into::<Node>().unwrap()
44   - . child_nodes().get(1).unwrap()
45   - . child_nodes().get(1).unwrap()
46   - . dyn_into::<HtmlElement>().unwrap() )),
47   - _ => None,
48   - };
49   -
50   - let cont_ref = container.as_ref();
51   -
52   - let get_md = move || {
53   - match cont_ref {
54   - Some((md_cont, _)) => md_cont.inner_text(),
55   - None => String::from(""),
56   - }
57   - };
58   -
59   - let set_md = move |md :&str| {
60   - match cont_ref {
61   - Some((md_cont, _)) => md_cont.set_text_content(Some(md)),
62   - None => (),
63   - }
64   - };
65   -
66   - let update = move || {
67   - match cont_ref {
68   - Some((_, view_cont)) => {
69   - use pulldown_cmark::{Parser, Options, html};
70   -
71   - let mut html_out = String::new();
72   - let md = get_md();
73   - let parser = Parser::new_ext(&md, Options::all());
74   -
75   - html::push_html(&mut html_out, parser);
76   - view_cont.set_inner_html(&html_out)
77   - },
78   - None => (),
79   - }
80   - };
81   -
82   - set_md(md.json.content.as_str());
83   - update();
84   -
85   - /* play with katex ==== */
86   - let opts = katex::Opts::builder()
87   - . output_type(katex::opts::OutputType::Mathml)
88   - . build().unwrap();
89   - let formula1 = katex::render_with_opts("E = mc^2", &opts).unwrap();
90   - let formula2 = katex::render_with_opts("e^{i*\\pi} +1 = 0", &opts).unwrap();
91   -
92   - if let Either::Left(dom_js) = dom.inner_read() {
93   - dom_js . to_owned()
94   - . dyn_into::<Node>().unwrap()
95   - . child_nodes().get(1).unwrap()
96   - . child_nodes().get(2).unwrap()
97   - . dyn_into::<HtmlElement>().unwrap()
98   - . set_inner_html(formula1.as_str())
99   - };
100   -
101   - if let Either::Left(dom_js) = dom.inner_read() {
102   - dom_js . to_owned()
103   - . dyn_into::<Node>().unwrap()
104   - . child_nodes().get(1).unwrap()
105   - . child_nodes().get(3).unwrap()
106   - . dyn_into::<HtmlElement>().unwrap()
107   - . set_inner_html(formula2.as_str())
108   - };
109   - /* =========== */
110   -
111   - while let Some(msg) = rx_logic.next().await {
112   - match msg {
113   - AppLogic::Store => {
114   - md.json.content = get_md();
115   - md.save().await.unwrap();
116   - },
117   - AppLogic::Update => update(),
118   - AppLogic::Toggle => {
119   - show_edit = ! show_edit;
120   - tx_toggle.broadcast(show_edit).await.unwrap();
121   - },
122   - AppLogic::Discard => {
123   - set_md(md.json.content.as_str());
124   - update();
125   - },
126   - AppLogic::Select => {
127   - let patches = md
128   - . patches().await.unwrap()
129   - . into_iter()
130   - . map(|diff| {
131   - let id = Some(diff.id);
132   - let choose_filter = tx_logic
133   - . sink()
134   - . contra_map(move |_| AppLogic::Choose(id));
135   - builder! {
136   - <li><button on:click=choose_filter
137   - value=format!("{}", diff.id.to_owned())>
138   - {diff.date_created.to_owned()}
139   - </button></li>
140   - }});
141   - let all = vec![builder! {
142   - <li><button on:click=tx_logic.sink().contra_map(|_| AppLogic::Choose(None))>
143   - "Current"
144   - </button></li>
145   - }].into_iter().chain(patches);
146   -
147   - let list_replace = ListPatch::splice(.., all);
148   - tx_patches.send(list_replace).await.unwrap();
149   - },
150   - AppLogic::Choose(id) => {
151   - md.read(id).await.unwrap();
152   - set_md(md.json.content.as_str());
153   - update();
154   - },
155   - }
156   - }
157   -}
158   -
159   -fn editor_view( tx_logic: broadcast::Sender<AppLogic>
160   - , rx_toggle: broadcast::Receiver<bool>
161   - , rx_patches: mpmc::Receiver<ListPatch<ViewBuilder<Dom>>>
162   - , tx_dom: broadcast::Sender<Dom>
163   - ) -> ViewBuilder<Dom>
164   -{
165   - let input_filter = tx_logic
166   - . sink()
167   - . contra_map(|_| AppLogic::Update);
168   - let store_filter = tx_logic
169   - . sink()
170   - . contra_map(|_| AppLogic::Store);
171   - /* keep as example how to handle concrete events. ==
172   - . contra_filter_map(|e :DomEvent| {
173   - if let Either::Left(e) = e.clone_inner() {
174   - let e = e.dyn_into::<MouseEvent>().unwrap();
175   - match e.alt_key() {
176   - true => Some(AppLogic::Store),
177   - false => None
178   - }
179   - } else {
180   - None
181   - }
182   - });
183   - == */
184   - let toggle_filter = tx_logic
185   - . sink()
186   - . contra_map(|_| AppLogic::Toggle);
187   - let select_filter = tx_logic
188   - . sink()
189   - . contra_map(|_e| AppLogic::Select);
190   - let discard_filter = tx_logic
191   - . sink()
192   - . contra_map(|_| AppLogic::Discard);
193   -
194   - let toggle_map = rx_toggle
195   - . map(|t| match t {
196   - true => String::from("block"),
197   - false => String::from("none") });
198   -
199   - builder! {
200   - <div class="input"
201   - style:width="33%"
202   - on:input=input_filter
203   - capture:view=tx_dom.sink()>
204   - <div contenteditable="true"
205   - style:cursor="text"
206   - style:display=("none", toggle_map)>
207   - <pre></pre>
208   - </div>
209   - <div>
210   - <div>
211   - <div>
212   - <button on:click=select_filter>{select_icon()}</button>
213   - <ul patch:children=rx_patches>
214   - </ul>
215   - </div>
216   - <button on:click=store_filter>{save_icon()}</button>
217   - <button on:click=discard_filter>{discard_icon()}</button>
218   - <button on:click=toggle_filter>{edit_icon()}</button>
219   - </div>
220   - <div></div>
221   - <div></div>
222   - <div></div>
223   - </div>
224   - </div>
225   - }
226   -}
227   -
228 14 #[wasm_bindgen(start)]
229 15 pub async fn main() -> Result<(), JsValue> {
230 16 panic::set_hook(Box::new(console_error_panic_hook::hook));
231 17 console_log::init_with_level(Level::Trace).unwrap();
232 18
233   - let (tx_dom, rx_dom) = broadcast::bounded(1);
234   - let (tx_logic, rx_logic) = broadcast::bounded(1);
235   - let (tx_toggle, rx_toggle) = broadcast::bounded(1);
236   - let (tx_patches, rx_patches) = mpmc::bounded(1);
237   -
238   - let view = editor_view(tx_logic.clone(), rx_toggle, rx_patches, tx_dom);
239   - let logic = editor_logic(rx_logic, tx_logic, tx_toggle, tx_patches, rx_dom);
240   - let comp = Component::from(view).with_logic(logic);
  19 + let comp = markdown::new().await;
241 20
242 21 let page = Component::from(builder! {{comp}});
243 22 page.build()?.run()
... ...
Please register or login to post a comment