//! El `counter` corriendo en un navegador: el consumidor de [`run_web`]. //! //! Misma app Elm que `counter.rs` —modelo `u32`, dos botones, `view` pura— //! con el único cambio que el web impone: en vez de `fn main()` que llama a //! `run::()`, un `#[wasm_bindgen(start)]` que llama a //! `run_web::(Some("llimphi"))`. **La app no sabe que está en un //! navegador**; eso es todo el punto. //! //! Es un `cdylib` (ver el `[[example]]` en Cargo.toml) porque wasm-bindgen //! necesita ese crate-type. En targets nativos queda como una biblioteca //! vacía, así que `cargo check --workspace --examples` lo cubre sin ruido. //! //! Construir y servir: //! //! ```sh //! ./scripts/build-llimphi-web.sh # → /tmp/llimphi-web //! python3 -m http.server 8000 -d /tmp/llimphi-web //! ``` #![cfg(target_arch = "wasm32")] use llimphi_ui::llimphi_layout::taffy::{ prelude::{length, percent, Dimension, FlexDirection, Size, Style}, AlignItems, JustifyContent, }; use llimphi_ui::llimphi_raster::peniko::Color; use llimphi_ui::{App, Handle, View}; use wasm_bindgen::prelude::wasm_bindgen; #[derive(Clone)] enum Msg { Increment, Reset, } struct Counter; impl App for Counter { type Model = u32; type Msg = Msg; fn title() -> &'static str { "llimphi · counter web" } fn init(_: &Handle) -> Self::Model { 0 } fn update(model: Self::Model, msg: Self::Msg, _: &Handle) -> Self::Model { match msg { Msg::Increment => model.saturating_add(1), Msg::Reset => 0, } } fn view(model: &Self::Model) -> View { let number = View::new(Style { size: Size { width: percent(1.0_f32), height: Dimension::auto(), }, flex_grow: 1.0, align_items: Some(AlignItems::CENTER), justify_content: Some(JustifyContent::CENTER), ..Default::default() }) .text(model.to_string(), 160.0, Color::from_rgba8(230, 240, 250, 255)); let boton = |etiqueta: &str, ancho: f32, fondo: Color, tinta: Color, msg: Msg| { View::new(Style { size: Size { width: length(ancho), height: length(56.0_f32), }, align_items: Some(AlignItems::CENTER), justify_content: Some(JustifyContent::CENTER), ..Default::default() }) .fill(fondo) .radius(12.0) .text(etiqueta.to_string(), 26.0, tinta) .on_click(msg) }; let botones = View::new(Style { flex_direction: FlexDirection::Row, size: Size { width: percent(1.0_f32), height: length(56.0_f32), }, gap: Size { width: length(16.0_f32), height: length(0.0_f32), }, justify_content: Some(JustifyContent::CENTER), ..Default::default() }) .children(vec![ boton( "+1", 160.0, Color::from_rgba8(60, 200, 130, 255), Color::from_rgba8(10, 30, 20, 255), Msg::Increment, ), boton( "reset", 120.0, Color::from_rgba8(220, 80, 80, 255), Color::from_rgba8(30, 10, 10, 255), Msg::Reset, ), ]); View::new(Style { flex_direction: FlexDirection::Column, size: Size { width: percent(1.0_f32), height: percent(1.0_f32), }, gap: Size { width: length(0.0_f32), height: length(24.0_f32), }, padding: llimphi_ui::llimphi_layout::taffy::Rect { left: length(32.0_f32), right: length(32.0_f32), top: length(32.0_f32), bottom: length(32.0_f32), }, ..Default::default() }) .fill(Color::from_rgba8(20, 24, 32, 255)) .children(vec![number, botones]) } } /// `start` de wasm-bindgen: el navegador lo llama al terminar de instanciar el /// módulo. Retorna en el acto — el bucle sigue vivo por `requestAnimationFrame` /// (ver [`llimphi_ui::run_web`]). #[wasm_bindgen(start)] pub fn arrancar() { llimphi_ui::run_web::(Some("llimphi")); }