1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#![deny(missing_docs)]
#[macro_use]
extern crate sunbeam;
use self::app::PercyPreviewApp;
use self::render::render_app;
use std::sync::{Arc, Mutex};
pub use self::app::async_task_spawner;
use crate::app::AppConfig;
pub use crate::config::WebClientConfig;
use crate::window_messenger::WindowMessenger;
use percy_dom::single_page_app::{intercept_relative_links, set_onpopstate_handler};
use percy_dom::{render::create_render_scheduler, PercyDom, VirtualNode};
use routes::create_router;
use wasm_bindgen::prelude::*;
mod app;
mod config;
mod render;
mod routes;
mod view;
mod window_messenger;
pub mod all_sunbeam_css;
#[wasm_bindgen]
pub struct PercyPreviewWebClient {
#[wasm_bindgen(skip)]
pub rerender: Arc<Mutex<Box<dyn FnMut() -> ()>>>,
}
impl PercyPreviewWebClient {
pub fn new_append_to_mount(config: WebClientConfig, dom_selector_of_mount: &str) -> Self {
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
let render: Arc<Mutex<Box<dyn FnMut() -> ()>>> = Arc::new(Mutex::new(Box::new(|| {})));
let render_clone = Arc::clone(&render);
let app = create_app(config, render);
let window_messenger = WindowMessenger::new(app.world.clone());
setup_single_page_application(window_messenger);
let pdom = create_percy_dom(dom_selector_of_mount, render_app(&app.world));
let render = move || render_app(&app.world);
let render = create_render_scheduler(pdom, render);
*render_clone.lock().unwrap() = render;
Self {
rerender: render_clone,
}
}
}
fn create_app(
config: WebClientConfig,
render: Arc<Mutex<Box<dyn FnMut() -> ()>>>,
) -> PercyPreviewApp {
PercyPreviewApp::new(
AppConfig {
async_task_spawner: config.async_task_spawner,
after_path_change: Box::new(|new_path| {
history()
.push_state_with_url(&JsValue::null(), "Percy Preview App", Some(new_path))
.unwrap();
}),
previews: config.previews,
},
render,
)
}
fn setup_single_page_application(window_messenger: WindowMessenger) {
window_messenger.msg_set_path(window().location().pathname().unwrap().to_string());
let wm1 = window_messenger.clone();
let wm2 = window_messenger;
intercept_relative_links(move |new_path| {
wm1.msg_set_path(new_path);
});
set_onpopstate_handler(move |new_path| {
wm2.msg_set_path(new_path);
});
}
fn create_percy_dom(dom_selector_of_mount: &str, start_view: VirtualNode) -> PercyDom {
let mount = document()
.query_selector(dom_selector_of_mount)
.unwrap()
.unwrap();
let pdom = PercyDom::new_append_to_mount(start_view, &mount);
pdom
}
fn window() -> web_sys::Window {
web_sys::window().unwrap()
}
fn document() -> web_sys::Document {
window().document().unwrap()
}
fn history() -> web_sys::History {
window().history().unwrap()
}