mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
Reimplement App type based on scope
Add prelude module
This commit is contained in:
parent
b5949c349f
commit
ecf364173f
@ -1,4 +1,4 @@
|
||||
use yew::html::*;
|
||||
use yew::prelude::*;
|
||||
use button::Button;
|
||||
|
||||
pub struct Barrier {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use yew::html::*;
|
||||
use yew::prelude::*;
|
||||
|
||||
pub struct Button {
|
||||
title: String,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use yew::html::*;
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub enum Color {
|
||||
|
||||
@ -5,7 +5,7 @@ mod counter;
|
||||
mod button;
|
||||
mod barrier;
|
||||
|
||||
use yew::html::*;
|
||||
use yew::prelude::*;
|
||||
use yew::services::console::ConsoleService;
|
||||
use counter::{Counter, Color};
|
||||
use barrier::Barrier;
|
||||
@ -14,7 +14,7 @@ struct Context {
|
||||
console: ConsoleService,
|
||||
}
|
||||
|
||||
impl counter::Printer for Context {
|
||||
impl counter::Printer for AppContext<Context, Model, Msg> {
|
||||
fn print(&mut self, data: &str) {
|
||||
self.console.log(data);
|
||||
}
|
||||
@ -29,47 +29,40 @@ enum Msg {
|
||||
ChildClicked(u32),
|
||||
}
|
||||
|
||||
|
||||
impl Component<Context> for Model {
|
||||
type Msg = Msg;
|
||||
type Properties = ();
|
||||
|
||||
fn create(_: &mut ScopeRef<Context, Self>) -> Self {
|
||||
Model { color: Color::Red }
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Msg, context: &mut ScopeRef<Context, Self>) -> ShouldUpdate {
|
||||
match msg {
|
||||
Msg::Repaint => {
|
||||
self.color = Color::Blue;
|
||||
true
|
||||
}
|
||||
Msg::ChildClicked(value) => {
|
||||
context.console.log(&format!("child clicked: {}", value));
|
||||
false
|
||||
}
|
||||
fn update(context: &mut Context, model: &mut Model, msg: Msg) -> ShouldUpdate {
|
||||
match msg {
|
||||
Msg::Repaint => {
|
||||
model.color = Color::Blue;
|
||||
true
|
||||
}
|
||||
Msg::ChildClicked(value) => {
|
||||
context.console.log(&format!("child clicked: {}", value));
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self) -> Html<Context, Self> {
|
||||
let counter = |_| html! {
|
||||
<Counter: color=&self.color, onclick=Msg::ChildClicked,/>
|
||||
};
|
||||
html! {
|
||||
<div>
|
||||
<Barrier: limit=10, onsignal=|_| Msg::Repaint, />
|
||||
{ for (0..1000).map(counter) }
|
||||
</div>
|
||||
}
|
||||
fn view(model: &Model) -> AppHtml<Context, Model, Msg> {
|
||||
let counter = |_| html! {
|
||||
<Counter: color=&model.color, onclick=Msg::ChildClicked,/>
|
||||
};
|
||||
html! {
|
||||
<div>
|
||||
<Barrier: limit=10, onsignal=|_| Msg::Repaint, />
|
||||
{ for (0..1000).map(counter) }
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
yew::initialize();
|
||||
let app = App::new();
|
||||
let context = Context {
|
||||
console: ConsoleService,
|
||||
};
|
||||
let app: Scope<Context, Model> = Scope::new(context);
|
||||
app.mount_to_body();
|
||||
let model = Model {
|
||||
color: Color::Red,
|
||||
};
|
||||
app.mount(context, model, update, view);
|
||||
yew::run_loop();
|
||||
}
|
||||
|
||||
144
src/app.rs
Normal file
144
src/app.rs
Normal file
@ -0,0 +1,144 @@
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use stdweb::web::document;
|
||||
use html::{Component, ComponentUpdate, Html, ScopeBuilder, ScopeSender, ScopeRef, ShouldUpdate};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
pub struct App<CTX, MOD, MSG>
|
||||
where
|
||||
CTX: 'static,
|
||||
MOD: 'static,
|
||||
MSG: 'static,
|
||||
{
|
||||
builder: ScopeBuilder<AppContext<CTX, MOD, MSG>, AppImpl<CTX, MOD, MSG>>,
|
||||
}
|
||||
|
||||
pub struct AppSender<CTX, MOD, MSG>
|
||||
where
|
||||
CTX: 'static,
|
||||
MOD: 'static,
|
||||
MSG: 'static,
|
||||
{
|
||||
sender: ScopeSender<AppContext<CTX, MOD, MSG>, AppImpl<CTX, MOD, MSG>>,
|
||||
}
|
||||
|
||||
impl<CTX, MOD, MSG> AppSender<CTX, MOD, MSG> {
|
||||
pub fn send(&mut self, msg: MSG) {
|
||||
self.sender.send(ComponentUpdate::Message(msg))
|
||||
}
|
||||
}
|
||||
|
||||
impl<CTX, MOD, MSG> App<CTX, MOD, MSG> {
|
||||
|
||||
pub fn new() -> Self {
|
||||
App {
|
||||
builder: ScopeBuilder::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sender(&mut self) -> AppSender<CTX, MOD, MSG> {
|
||||
let sender = self.builder.sender();
|
||||
AppSender {
|
||||
sender,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mount<U, V>(self, context: CTX, model: MOD, update: U, view: V)
|
||||
where
|
||||
U: Fn(&mut CTX, &mut MOD, MSG) -> ShouldUpdate + 'static,
|
||||
V: Fn(&MOD) -> Html<AppContext<CTX, MOD, MSG>, AppImpl<CTX, MOD, MSG>> + 'static,
|
||||
{
|
||||
self.mount_to("body", context, model, update, view)
|
||||
}
|
||||
|
||||
pub fn mount_to<U, V>(self, selector: &str, context: CTX, model: MOD, update: U, view: V)
|
||||
where
|
||||
U: Fn(&mut CTX, &mut MOD, MSG) -> ShouldUpdate + 'static,
|
||||
V: Fn(&MOD) -> Html<AppContext<CTX, MOD, MSG>, AppImpl<CTX, MOD, MSG>> + 'static,
|
||||
{
|
||||
let element = document().query_selector(selector)
|
||||
.expect(format!("can't get node with selector `{}` for rendering", selector).as_str());
|
||||
let app_impl = AppImpl {
|
||||
model: model,
|
||||
update: Box::new(update),
|
||||
view: Box::new(view),
|
||||
};
|
||||
let context_impl = AppContext {
|
||||
app: Some(app_impl),
|
||||
context: context,
|
||||
};
|
||||
let context = Rc::new(RefCell::new(context_impl));
|
||||
let scope = self.builder.build(context);
|
||||
scope.mount(element);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AppContext<CTX, MOD, MSG>
|
||||
where
|
||||
CTX: 'static,
|
||||
MOD: 'static,
|
||||
MSG: 'static,
|
||||
{
|
||||
app: Option<AppImpl<CTX, MOD, MSG>>,
|
||||
context: CTX,
|
||||
}
|
||||
|
||||
impl<CTX, MOD, MSG> AsRef<CTX> for AppContext<CTX, MOD, MSG> {
|
||||
fn as_ref(&self) -> &CTX {
|
||||
&self.context
|
||||
}
|
||||
}
|
||||
|
||||
impl<CTX, MOD, MSG> AsMut<CTX> for AppContext<CTX, MOD, MSG> {
|
||||
fn as_mut(&mut self) -> &mut CTX {
|
||||
&mut self.context
|
||||
}
|
||||
}
|
||||
|
||||
impl<CTX, MOD, MSG> Deref for AppContext<CTX, MOD, MSG> {
|
||||
type Target = CTX;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.context
|
||||
}
|
||||
}
|
||||
|
||||
impl<CTX, MOD, MSG> DerefMut for AppContext<CTX, MOD, MSG> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.context
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AppImpl<CTX, MOD, MSG>
|
||||
where
|
||||
CTX: 'static,
|
||||
MOD: 'static,
|
||||
MSG: 'static,
|
||||
{
|
||||
model: MOD,
|
||||
update: Box<Fn(&mut CTX, &mut MOD, MSG) -> ShouldUpdate>,
|
||||
view: Box<Fn(&MOD) -> Html<AppContext<CTX, MOD, MSG>, AppImpl<CTX, MOD, MSG>>>,
|
||||
}
|
||||
|
||||
impl<CTX, MOD, MSG> Component<AppContext<CTX, MOD, MSG>> for AppImpl<CTX, MOD, MSG>
|
||||
where
|
||||
CTX: 'static,
|
||||
MOD: 'static,
|
||||
MSG: 'static,
|
||||
{
|
||||
type Msg = MSG;
|
||||
type Properties = ();
|
||||
|
||||
fn create(context: &mut ScopeRef<AppContext<CTX, MOD, MSG>, Self>) -> Self {
|
||||
context.app.take().expect("tried to unpack app impl twice")
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Msg, context: &mut ScopeRef<AppContext<CTX, MOD, MSG>, Self>) -> ShouldUpdate {
|
||||
(self.update)(&mut context.context, &mut self.model, msg)
|
||||
}
|
||||
|
||||
fn view(&self) -> Html<AppContext<CTX, MOD, MSG>, Self> {
|
||||
(self.view)(&self.model)
|
||||
}
|
||||
}
|
||||
|
||||
pub type AppHtml<CTX, MOD, MSG> = Html<AppContext<CTX, MOD, MSG>, AppImpl<CTX, MOD, MSG>>;
|
||||
15
src/html.rs
15
src/html.rs
@ -12,13 +12,6 @@ use stdweb::web::{Element, INode, EventListenerHandle, document};
|
||||
use stdweb::web::event::{IMouseEvent, IKeyboardEvent};
|
||||
use virtual_dom::{VNode, Listener};
|
||||
|
||||
/// Removes anything from the given element.
|
||||
fn clear_element(element: &Element) {
|
||||
while let Some(child) = element.last_child() {
|
||||
element.remove_child(&child).expect("can't remove a child");
|
||||
}
|
||||
}
|
||||
|
||||
/// This type indicates that component should be rendered again.
|
||||
pub type ShouldUpdate = bool;
|
||||
|
||||
@ -89,7 +82,6 @@ impl<IN> Callback<IN> {
|
||||
pub type SharedContext<CTX> = Rc<RefCell<CTX>>;
|
||||
|
||||
/// Local reference to application internals: messages sender and context.
|
||||
// TODO Rename to Context
|
||||
pub struct ScopeRef<'a, CTX: 'a, COMP: Component<CTX>> {
|
||||
context: &'a mut CTX,
|
||||
tx: &'a mut ComponentSender<CTX, COMP>,
|
||||
@ -337,6 +329,13 @@ impl<CTX: 'static, COMP: Component<CTX> + 'static> Scope<CTX, COMP> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes anything from the given element.
|
||||
fn clear_element(element: &Element) {
|
||||
while let Some(child) = element.last_child() {
|
||||
element.remove_child(&child).expect("can't remove a child");
|
||||
}
|
||||
}
|
||||
|
||||
/// A type which expected as a result of `view` function implementation.
|
||||
pub type Html<CTX, MSG> = VNode<CTX, MSG>;
|
||||
|
||||
|
||||
@ -58,6 +58,8 @@ extern crate stdweb;
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
pub mod html;
|
||||
pub mod app;
|
||||
pub mod prelude;
|
||||
pub mod services;
|
||||
pub mod format;
|
||||
pub mod virtual_dom;
|
||||
|
||||
2
src/prelude.rs
Normal file
2
src/prelude.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub use html::{Component, Html, ScopeRef, ShouldUpdate, Callback};
|
||||
pub use app::{App, AppContext, AppHtml};
|
||||
Loading…
x
Reference in New Issue
Block a user