]> git.seodisparate.com - EN605.607.81.SP22_ASDM_Project/commitdiff
Reorganize front-end source into modules
authorStephen Seo <seo.disparate@gmail.com>
Wed, 2 Mar 2022 06:18:10 +0000 (15:18 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Wed, 2 Mar 2022 06:18:21 +0000 (15:18 +0900)
front_end/src/constants.rs [new file with mode: 0644]
front_end/src/main.rs
front_end/src/state.rs [new file with mode: 0644]
front_end/src/yew_components.rs [new file with mode: 0644]

diff --git a/front_end/src/constants.rs b/front_end/src/constants.rs
new file mode 100644 (file)
index 0000000..090bdbd
--- /dev/null
@@ -0,0 +1,5 @@
+//const ROWS: u8 = 8;
+pub const COLS: u8 = 7;
+
+pub const INFO_TEXT_MAX_ITEMS: u32 = 100;
+pub const INFO_TEXT_HEIGHT: i32 = 400;
index bee0e46d58397b844cd79926d20755832af4db7a..ae73e13e16eb2010665bcf932a0ff76d11daa846 100644 (file)
-use std::cell::{Cell, RefCell};
-use std::collections::VecDeque;
-use std::rc::Rc;
-use yew::prelude::*;
-
-//const ROWS: u8 = 8;
-const COLS: u8 = 7;
-
-const INFO_TEXT_MAX_ITEMS: u32 = 100;
-const INFO_TEXT_HEIGHT: i32 = 400;
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-enum BoardState {
-    Empty,
-    Cyan,
-    Magenta,
-}
-
-impl Default for BoardState {
-    fn default() -> Self {
-        Self::Empty
-    }
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-struct MessageBus {
-    queued: VecDeque<String>,
-}
-
-impl Default for MessageBus {
-    fn default() -> Self {
-        Self {
-            queued: VecDeque::new(),
-        }
-    }
-}
-
-impl MessageBus {
-    pub fn get_next_msg(&mut self) -> Option<String> {
-        self.queued.pop_front()
-    }
-
-    pub fn push_msg(&mut self, msg: String) -> Result<(), String> {
-        self.queued.push_back(msg);
-        Ok(())
-    }
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-enum Turn {
-    CyanPlayer,
-    MagentaPlayer,
-}
-
-#[derive(Clone, Debug, PartialEq)]
-struct SharedState {
-    board: [Rc<Cell<BoardState>>; 56],
-    bus: Rc<RefCell<MessageBus>>,
-    turn: Turn,
-    info_text_ref: NodeRef,
-}
-
-impl Default for SharedState {
-    fn default() -> Self {
-        Self {
-            // cannot use [<type>; 56] because Rc does not impl Copy
-            board: [
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-                Rc::new(Cell::new(BoardState::default())),
-            ],
-            bus: Rc::new(RefCell::new(MessageBus::default())),
-            turn: Turn::CyanPlayer,
-            info_text_ref: NodeRef::default(),
-        }
-    }
-}
-
-struct Slot {}
-
-enum SlotMessage {
-    Press(u8),
-}
+mod state;
+mod yew_components;
+mod constants;
 
-#[derive(Clone, PartialEq, Properties)]
-struct SlotProperties {
-    idx: u8,
-    state: Rc<Cell<BoardState>>,
-    bus: Rc<RefCell<MessageBus>>,
-}
-
-impl Component for Slot {
-    type Message = SlotMessage;
-    type Properties = SlotProperties;
-
-    fn create(_ctx: &Context<Self>) -> Self {
-        Self {}
-    }
-
-    fn view(&self, ctx: &Context<Self>) -> Html {
-        let idx = ctx.props().idx;
-        let state = match ctx.props().state.as_ref().get() {
-            BoardState::Empty => "open",
-            BoardState::Cyan => "cyan",
-            BoardState::Magenta => "magenta",
-        };
-        let idx_copy = idx;
-        let onclick = ctx.link().callback(move |_| SlotMessage::Press(idx_copy));
-        let col = idx % COLS;
-        let row = idx / COLS;
-        html! {
-            <button class={format!("slot {} r{} c{}", state, row, col)} id={format!("{}", idx)} onclick={onclick}>
-            </button>
-        }
-    }
-
-    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
-        match msg {
-            SlotMessage::Press(idx) => {
-                let (shared, _) = ctx
-                    .link()
-                    .context::<SharedState>(Callback::noop())
-                    .expect("shared to be set");
-
-                let result = shared.bus.borrow_mut().push_msg(format!("pressed {idx}"));
-                if let Err(e) = result {
-                    log::error!("Error pushing msg to bus: {}", e);
-                } else {
-                    // DEBUG
-                    //log::info!("Pushed \"pressed {idx}\" msg to bus");
-                }
-            }
-        }
-
-        // notify Wrapper with message
-        if let Some(p) = ctx.link().get_parent() {
-            p.clone().downcast::<Wrapper>().send_message(());
-        }
-
-        true
-    }
-}
-
-struct Wrapper {}
-
-impl Component for Wrapper {
-    type Message = ();
-    type Properties = ();
-
-    fn create(_ctx: &Context<Self>) -> Self {
-        Self {}
-    }
-
-    fn view(&self, ctx: &Context<Self>) -> Html {
-        let (shared, _) = ctx
-            .link()
-            .context::<SharedState>(Callback::noop())
-            .expect("state to be set");
-        html! {
-            <div class="wrapper">
-                <Slot idx=0 state={shared.board[0].clone()} bus={shared.bus.clone()} />
-                <Slot idx=1 state={shared.board[1].clone()} bus={shared.bus.clone()} />
-                <Slot idx=2 state={shared.board[2].clone()} bus={shared.bus.clone()} />
-                <Slot idx=3 state={shared.board[3].clone()} bus={shared.bus.clone()} />
-                <Slot idx=4 state={shared.board[4].clone()} bus={shared.bus.clone()} />
-                <Slot idx=5 state={shared.board[5].clone()} bus={shared.bus.clone()} />
-                <Slot idx=6 state={shared.board[6].clone()} bus={shared.bus.clone()} />
-                <Slot idx=7 state={shared.board[7].clone()} bus={shared.bus.clone()} />
-                <Slot idx=8 state={shared.board[8].clone()} bus={shared.bus.clone()} />
-                <Slot idx=9 state={shared.board[9].clone()} bus={shared.bus.clone()} />
-                <Slot idx=10 state={shared.board[10].clone()} bus={shared.bus.clone()} />
-                <Slot idx=11 state={shared.board[11].clone()} bus={shared.bus.clone()} />
-                <Slot idx=12 state={shared.board[12].clone()} bus={shared.bus.clone()} />
-                <Slot idx=13 state={shared.board[13].clone()} bus={shared.bus.clone()} />
-                <Slot idx=14 state={shared.board[14].clone()} bus={shared.bus.clone()} />
-                <Slot idx=15 state={shared.board[15].clone()} bus={shared.bus.clone()} />
-                <Slot idx=16 state={shared.board[16].clone()} bus={shared.bus.clone()} />
-                <Slot idx=17 state={shared.board[17].clone()} bus={shared.bus.clone()} />
-                <Slot idx=18 state={shared.board[18].clone()} bus={shared.bus.clone()} />
-                <Slot idx=19 state={shared.board[19].clone()} bus={shared.bus.clone()} />
-                <Slot idx=20 state={shared.board[20].clone()} bus={shared.bus.clone()} />
-                <Slot idx=21 state={shared.board[21].clone()} bus={shared.bus.clone()} />
-                <Slot idx=22 state={shared.board[22].clone()} bus={shared.bus.clone()} />
-                <Slot idx=23 state={shared.board[23].clone()} bus={shared.bus.clone()} />
-                <Slot idx=24 state={shared.board[24].clone()} bus={shared.bus.clone()} />
-                <Slot idx=25 state={shared.board[25].clone()} bus={shared.bus.clone()} />
-                <Slot idx=26 state={shared.board[26].clone()} bus={shared.bus.clone()} />
-                <Slot idx=27 state={shared.board[27].clone()} bus={shared.bus.clone()} />
-                <Slot idx=28 state={shared.board[28].clone()} bus={shared.bus.clone()} />
-                <Slot idx=29 state={shared.board[29].clone()} bus={shared.bus.clone()} />
-                <Slot idx=30 state={shared.board[30].clone()} bus={shared.bus.clone()} />
-                <Slot idx=31 state={shared.board[31].clone()} bus={shared.bus.clone()} />
-                <Slot idx=32 state={shared.board[32].clone()} bus={shared.bus.clone()} />
-                <Slot idx=33 state={shared.board[33].clone()} bus={shared.bus.clone()} />
-                <Slot idx=34 state={shared.board[34].clone()} bus={shared.bus.clone()} />
-                <Slot idx=35 state={shared.board[35].clone()} bus={shared.bus.clone()} />
-                <Slot idx=36 state={shared.board[36].clone()} bus={shared.bus.clone()} />
-                <Slot idx=37 state={shared.board[37].clone()} bus={shared.bus.clone()} />
-                <Slot idx=38 state={shared.board[38].clone()} bus={shared.bus.clone()} />
-                <Slot idx=39 state={shared.board[39].clone()} bus={shared.bus.clone()} />
-                <Slot idx=40 state={shared.board[40].clone()} bus={shared.bus.clone()} />
-                <Slot idx=41 state={shared.board[41].clone()} bus={shared.bus.clone()} />
-                <Slot idx=42 state={shared.board[42].clone()} bus={shared.bus.clone()} />
-                <Slot idx=43 state={shared.board[43].clone()} bus={shared.bus.clone()} />
-                <Slot idx=44 state={shared.board[44].clone()} bus={shared.bus.clone()} />
-                <Slot idx=45 state={shared.board[45].clone()} bus={shared.bus.clone()} />
-                <Slot idx=46 state={shared.board[46].clone()} bus={shared.bus.clone()} />
-                <Slot idx=47 state={shared.board[47].clone()} bus={shared.bus.clone()} />
-                <Slot idx=48 state={shared.board[48].clone()} bus={shared.bus.clone()} />
-                <Slot idx=49 state={shared.board[49].clone()} bus={shared.bus.clone()} />
-                <Slot idx=50 state={shared.board[50].clone()} bus={shared.bus.clone()} />
-                <Slot idx=51 state={shared.board[51].clone()} bus={shared.bus.clone()} />
-                <Slot idx=52 state={shared.board[52].clone()} bus={shared.bus.clone()} />
-                <Slot idx=53 state={shared.board[53].clone()} bus={shared.bus.clone()} />
-                <Slot idx=54 state={shared.board[54].clone()} bus={shared.bus.clone()} />
-                <Slot idx=55 state={shared.board[55].clone()} bus={shared.bus.clone()} />
-                <div class="info_text_wrapper">
-                    <InfoText />
-                </div>
-            </div> // wrapper
-        }
-    }
-
-    fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
-        let (shared, _) = ctx
-            .link()
-            .context::<SharedState>(Callback::noop())
-            .expect("state to be set");
-
-        loop {
-            if let Some(msg) = shared.bus.borrow_mut().get_next_msg() {
-                let split_str: Vec<&str> = msg.split_whitespace().collect();
-                if split_str.len() == 2 {
-                    if split_str[0] == "pressed" {
-                        if let Ok(idx) = split_str[1].parse::<u8>() {
-                            let output_str: String = format!("Got {idx} pressed.");
-                            // DEBUG
-                            //log::info!("{}", &output_str);
-                            if let Some(info_text) =
-                                shared.info_text_ref.cast::<web_sys::HtmlParagraphElement>()
-                            {
-                                // create the new text to be appended in the output
-                                let window = web_sys::window().expect("no window exists");
-                                let document =
-                                    window.document().expect("window should have a document");
-                                let p = document
-                                    .create_element("p")
-                                    .expect("document should be able to create <p>");
-                                p.set_text_content(Some(&output_str));
-
-                                // check if scrolled to bottom
-                                let at_bottom: bool = info_text.scroll_top() + INFO_TEXT_HEIGHT
-                                    >= info_text.scroll_height();
-
-                                // append text to output
-                                info_text
-                                    .append_with_node_1(&p)
-                                    .expect("should be able to append to info_text");
-                                while info_text.child_element_count() > INFO_TEXT_MAX_ITEMS {
-                                    info_text
-                                        .remove_child(&info_text.first_child().unwrap())
-                                        .expect("should be able to limit items in info_text");
-                                }
-
-                                // scroll to bottom only if at bottom
-                                if at_bottom {
-                                    info_text.set_scroll_top(info_text.scroll_height());
-                                }
-                            } else {
-                                log::warn!("Failed to get \"info_text\"");
-                            }
-                        }
-                    }
-                }
-            } else {
-                break;
-            }
-        }
-    }
-
-    fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
-        true
-    }
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-struct InfoText {}
-
-impl Component for InfoText {
-    type Message = ();
-    type Properties = ();
-
-    fn create(_ctx: &Context<Self>) -> Self {
-        Self {}
-    }
-
-    fn view(&self, ctx: &Context<Self>) -> Html {
-        let (shared, _) = ctx
-            .link()
-            .context::<SharedState>(Callback::noop())
-            .expect("state to be set");
-        html! {
-            <div ref={shared.info_text_ref} class="info_text">
-                {"Hello"}
-            </div>
-        }
-    }
-}
+use state::SharedState;
+use yew_components::Wrapper;
+use yew::prelude::*;
 
 #[function_component(App)]
-fn app() -> Html {
+pub fn app() -> Html {
     let ctx = use_state(SharedState::default);
     html! {
         <ContextProvider<SharedState> context={(*ctx).clone()}>
diff --git a/front_end/src/state.rs b/front_end/src/state.rs
new file mode 100644 (file)
index 0000000..0342f44
--- /dev/null
@@ -0,0 +1,124 @@
+use std::collections::VecDeque;
+use std::cell::{Cell, RefCell};
+use std::rc::Rc;
+use yew::prelude::*;
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct MessageBus {
+    queued: VecDeque<String>,
+}
+
+impl MessageBus {
+    pub fn get_next_msg(&mut self) -> Option<String> {
+        self.queued.pop_front()
+    }
+
+    pub fn push_msg(&mut self, msg: String) -> Result<(), String> {
+        self.queued.push_back(msg);
+        Ok(())
+    }
+}
+
+impl Default for MessageBus {
+    fn default() -> Self {
+        Self {
+            queued: VecDeque::new(),
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum BoardState {
+    Empty,
+    Cyan,
+    Magenta,
+}
+
+impl Default for BoardState {
+    fn default() -> Self {
+        Self::Empty
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum Turn {
+    CyanPlayer,
+    MagentaPlayer,
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct SharedState {
+    pub board: [Rc<Cell<BoardState>>; 56],
+    pub bus: Rc<RefCell<MessageBus>>,
+    pub turn: Turn,
+    pub info_text_ref: NodeRef,
+}
+
+impl Default for SharedState {
+    fn default() -> Self {
+        Self {
+            // cannot use [<type>; 56] because Rc does not impl Copy
+            board: [
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+                Rc::new(Cell::new(BoardState::default())),
+            ],
+            bus: Rc::new(RefCell::new(MessageBus::default())),
+            turn: Turn::CyanPlayer,
+            info_text_ref: NodeRef::default(),
+        }
+    }
+}
diff --git a/front_end/src/yew_components.rs b/front_end/src/yew_components.rs
new file mode 100644 (file)
index 0000000..34ddb5a
--- /dev/null
@@ -0,0 +1,236 @@
+use crate::state::{MessageBus, BoardState, Turn, SharedState};
+use crate::constants::{COLS, INFO_TEXT_HEIGHT, INFO_TEXT_MAX_ITEMS};
+use yew::prelude::*;
+use std::cell::{Cell, RefCell};
+use std::rc::Rc;
+
+pub struct Slot {}
+
+pub enum SlotMessage {
+    Press(u8),
+}
+
+#[derive(Clone, PartialEq, Properties)]
+pub struct SlotProperties {
+    idx: u8,
+    state: Rc<Cell<BoardState>>,
+    bus: Rc<RefCell<MessageBus>>,
+}
+
+impl Component for Slot {
+    type Message = SlotMessage;
+    type Properties = SlotProperties;
+
+    fn create(_ctx: &Context<Self>) -> Self {
+        Self {}
+    }
+
+    fn view(&self, ctx: &Context<Self>) -> Html {
+        let idx = ctx.props().idx;
+        let state = match ctx.props().state.as_ref().get() {
+            BoardState::Empty => "open",
+            BoardState::Cyan => "cyan",
+            BoardState::Magenta => "magenta",
+        };
+        let idx_copy = idx;
+        let onclick = ctx.link().callback(move |_| SlotMessage::Press(idx_copy));
+        let col = idx % COLS;
+        let row = idx / COLS;
+        html! {
+            <button class={format!("slot {} r{} c{}", state, row, col)} id={format!("{}", idx)} onclick={onclick}>
+            </button>
+        }
+    }
+
+    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
+        match msg {
+            SlotMessage::Press(idx) => {
+                let (shared, _) = ctx
+                    .link()
+                    .context::<SharedState>(Callback::noop())
+                    .expect("shared to be set");
+
+                let result = shared.bus.borrow_mut().push_msg(format!("pressed {idx}"));
+                if let Err(e) = result {
+                    log::error!("Error pushing msg to bus: {}", e);
+                } else {
+                    // DEBUG
+                    //log::info!("Pushed \"pressed {idx}\" msg to bus");
+                }
+            }
+        }
+
+        // notify Wrapper with message
+        if let Some(p) = ctx.link().get_parent() {
+            p.clone().downcast::<Wrapper>().send_message(());
+        }
+
+        true
+    }
+}
+
+pub struct Wrapper {}
+
+impl Component for Wrapper {
+    type Message = ();
+    type Properties = ();
+
+    fn create(_ctx: &Context<Self>) -> Self {
+        Self {}
+    }
+
+    fn view(&self, ctx: &Context<Self>) -> Html {
+        let (shared, _) = ctx
+            .link()
+            .context::<SharedState>(Callback::noop())
+            .expect("state to be set");
+        html! {
+            <div class="wrapper">
+                <Slot idx=0 state={shared.board[0].clone()} bus={shared.bus.clone()} />
+                <Slot idx=1 state={shared.board[1].clone()} bus={shared.bus.clone()} />
+                <Slot idx=2 state={shared.board[2].clone()} bus={shared.bus.clone()} />
+                <Slot idx=3 state={shared.board[3].clone()} bus={shared.bus.clone()} />
+                <Slot idx=4 state={shared.board[4].clone()} bus={shared.bus.clone()} />
+                <Slot idx=5 state={shared.board[5].clone()} bus={shared.bus.clone()} />
+                <Slot idx=6 state={shared.board[6].clone()} bus={shared.bus.clone()} />
+                <Slot idx=7 state={shared.board[7].clone()} bus={shared.bus.clone()} />
+                <Slot idx=8 state={shared.board[8].clone()} bus={shared.bus.clone()} />
+                <Slot idx=9 state={shared.board[9].clone()} bus={shared.bus.clone()} />
+                <Slot idx=10 state={shared.board[10].clone()} bus={shared.bus.clone()} />
+                <Slot idx=11 state={shared.board[11].clone()} bus={shared.bus.clone()} />
+                <Slot idx=12 state={shared.board[12].clone()} bus={shared.bus.clone()} />
+                <Slot idx=13 state={shared.board[13].clone()} bus={shared.bus.clone()} />
+                <Slot idx=14 state={shared.board[14].clone()} bus={shared.bus.clone()} />
+                <Slot idx=15 state={shared.board[15].clone()} bus={shared.bus.clone()} />
+                <Slot idx=16 state={shared.board[16].clone()} bus={shared.bus.clone()} />
+                <Slot idx=17 state={shared.board[17].clone()} bus={shared.bus.clone()} />
+                <Slot idx=18 state={shared.board[18].clone()} bus={shared.bus.clone()} />
+                <Slot idx=19 state={shared.board[19].clone()} bus={shared.bus.clone()} />
+                <Slot idx=20 state={shared.board[20].clone()} bus={shared.bus.clone()} />
+                <Slot idx=21 state={shared.board[21].clone()} bus={shared.bus.clone()} />
+                <Slot idx=22 state={shared.board[22].clone()} bus={shared.bus.clone()} />
+                <Slot idx=23 state={shared.board[23].clone()} bus={shared.bus.clone()} />
+                <Slot idx=24 state={shared.board[24].clone()} bus={shared.bus.clone()} />
+                <Slot idx=25 state={shared.board[25].clone()} bus={shared.bus.clone()} />
+                <Slot idx=26 state={shared.board[26].clone()} bus={shared.bus.clone()} />
+                <Slot idx=27 state={shared.board[27].clone()} bus={shared.bus.clone()} />
+                <Slot idx=28 state={shared.board[28].clone()} bus={shared.bus.clone()} />
+                <Slot idx=29 state={shared.board[29].clone()} bus={shared.bus.clone()} />
+                <Slot idx=30 state={shared.board[30].clone()} bus={shared.bus.clone()} />
+                <Slot idx=31 state={shared.board[31].clone()} bus={shared.bus.clone()} />
+                <Slot idx=32 state={shared.board[32].clone()} bus={shared.bus.clone()} />
+                <Slot idx=33 state={shared.board[33].clone()} bus={shared.bus.clone()} />
+                <Slot idx=34 state={shared.board[34].clone()} bus={shared.bus.clone()} />
+                <Slot idx=35 state={shared.board[35].clone()} bus={shared.bus.clone()} />
+                <Slot idx=36 state={shared.board[36].clone()} bus={shared.bus.clone()} />
+                <Slot idx=37 state={shared.board[37].clone()} bus={shared.bus.clone()} />
+                <Slot idx=38 state={shared.board[38].clone()} bus={shared.bus.clone()} />
+                <Slot idx=39 state={shared.board[39].clone()} bus={shared.bus.clone()} />
+                <Slot idx=40 state={shared.board[40].clone()} bus={shared.bus.clone()} />
+                <Slot idx=41 state={shared.board[41].clone()} bus={shared.bus.clone()} />
+                <Slot idx=42 state={shared.board[42].clone()} bus={shared.bus.clone()} />
+                <Slot idx=43 state={shared.board[43].clone()} bus={shared.bus.clone()} />
+                <Slot idx=44 state={shared.board[44].clone()} bus={shared.bus.clone()} />
+                <Slot idx=45 state={shared.board[45].clone()} bus={shared.bus.clone()} />
+                <Slot idx=46 state={shared.board[46].clone()} bus={shared.bus.clone()} />
+                <Slot idx=47 state={shared.board[47].clone()} bus={shared.bus.clone()} />
+                <Slot idx=48 state={shared.board[48].clone()} bus={shared.bus.clone()} />
+                <Slot idx=49 state={shared.board[49].clone()} bus={shared.bus.clone()} />
+                <Slot idx=50 state={shared.board[50].clone()} bus={shared.bus.clone()} />
+                <Slot idx=51 state={shared.board[51].clone()} bus={shared.bus.clone()} />
+                <Slot idx=52 state={shared.board[52].clone()} bus={shared.bus.clone()} />
+                <Slot idx=53 state={shared.board[53].clone()} bus={shared.bus.clone()} />
+                <Slot idx=54 state={shared.board[54].clone()} bus={shared.bus.clone()} />
+                <Slot idx=55 state={shared.board[55].clone()} bus={shared.bus.clone()} />
+                <div class="info_text_wrapper">
+                    <InfoText />
+                </div>
+            </div> // wrapper
+        }
+    }
+
+    fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
+        let (shared, _) = ctx
+            .link()
+            .context::<SharedState>(Callback::noop())
+            .expect("state to be set");
+
+        loop {
+            if let Some(msg) = shared.bus.borrow_mut().get_next_msg() {
+                let split_str: Vec<&str> = msg.split_whitespace().collect();
+                if split_str.len() == 2 {
+                    if split_str[0] == "pressed" {
+                        if let Ok(idx) = split_str[1].parse::<u8>() {
+                            let output_str: String = format!("Got {idx} pressed.");
+                            // DEBUG
+                            //log::info!("{}", &output_str);
+                            if let Some(info_text) =
+                                shared.info_text_ref.cast::<web_sys::HtmlParagraphElement>()
+                            {
+                                // create the new text to be appended in the output
+                                let window = web_sys::window().expect("no window exists");
+                                let document =
+                                    window.document().expect("window should have a document");
+                                let p = document
+                                    .create_element("p")
+                                    .expect("document should be able to create <p>");
+                                p.set_text_content(Some(&output_str));
+
+                                // check if scrolled to bottom
+                                let at_bottom: bool = info_text.scroll_top() + INFO_TEXT_HEIGHT
+                                    >= info_text.scroll_height();
+
+                                // append text to output
+                                info_text
+                                    .append_with_node_1(&p)
+                                    .expect("should be able to append to info_text");
+                                while info_text.child_element_count() > INFO_TEXT_MAX_ITEMS {
+                                    info_text
+                                        .remove_child(&info_text.first_child().unwrap())
+                                        .expect("should be able to limit items in info_text");
+                                }
+
+                                // scroll to bottom only if at bottom
+                                if at_bottom {
+                                    info_text.set_scroll_top(info_text.scroll_height());
+                                }
+                            } else {
+                                log::warn!("Failed to get \"info_text\"");
+                            }
+                        }
+                    }
+                }
+            } else {
+                break;
+            }
+        }
+    }
+
+    fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
+        true
+    }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct InfoText {}
+
+impl Component for InfoText {
+    type Message = ();
+    type Properties = ();
+
+    fn create(_ctx: &Context<Self>) -> Self {
+        Self {}
+    }
+
+    fn view(&self, ctx: &Context<Self>) -> Html {
+        let (shared, _) = ctx
+            .link()
+            .context::<SharedState>(Callback::noop())
+            .expect("state to be set");
+        html! {
+            <div ref={shared.info_text_ref} class="info_text">
+                {"Hello"}
+            </div>
+        }
+    }
+}