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,242 +2,21 @@ mod api;
2 mod data; 2 mod data;
3 mod error; 3 mod error;
4 mod client; 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 use log::Level; 10 use log::Level;
9 use mogwai::prelude::*; 11 use mogwai::prelude::*;
10 -use std::panic;  
11 use wasm_bindgen::prelude::*; 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 #[wasm_bindgen(start)] 14 #[wasm_bindgen(start)]
229 pub async fn main() -> Result<(), JsValue> { 15 pub async fn main() -> Result<(), JsValue> {
230 panic::set_hook(Box::new(console_error_panic_hook::hook)); 16 panic::set_hook(Box::new(console_error_panic_hook::hook));
231 console_log::init_with_level(Level::Trace).unwrap(); 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 let page = Component::from(builder! {{comp}}); 21 let page = Component::from(builder! {{comp}});
243 page.build()?.run() 22 page.build()?.run()
Please register or login to post a comment