perspective_viewer/components/containers/
tab_list.rs

1// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
3// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
4// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
5// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
6// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
8// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9// ┃ This file is part of the Perspective library, distributed under the terms ┃
10// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
13use yew::{Callback, Children, Component, Html, Properties, classes, html};
14
15use crate::components::style::LocalStyle;
16use crate::css;
17
18pub trait Tab: PartialEq + std::fmt::Display + Clone + Default + 'static {}
19
20impl Tab for String {}
21
22impl Tab for &'static str {}
23
24#[derive(Properties, Debug, PartialEq)]
25pub struct TabListProps<T: Tab> {
26    // all possible tabs
27    pub tabs: Vec<T>,
28    pub on_tab_change: Callback<(usize, T)>,
29    pub selected_tab: Option<usize>,
30    // the curently instantiated tabs
31    pub children: Children,
32}
33
34pub enum TabListMsg {
35    SetSelected(usize),
36}
37
38pub struct TabList<T: Tab> {
39    t: std::marker::PhantomData<T>,
40    selected_idx: usize,
41}
42
43impl<T: Tab> Component for TabList<T> {
44    type Message = TabListMsg;
45    type Properties = TabListProps<T>;
46
47    fn create(_ctx: &yew::Context<Self>) -> Self {
48        Self {
49            t: std::marker::PhantomData,
50            selected_idx: 0,
51        }
52    }
53
54    fn update(&mut self, ctx: &yew::Context<Self>, msg: Self::Message) -> bool {
55        match msg {
56            TabListMsg::SetSelected(idx) => {
57                ctx.props()
58                    .on_tab_change
59                    .emit((idx, ctx.props().tabs[idx].clone()));
60                self.selected_idx = idx;
61                true
62            },
63        }
64    }
65
66    fn changed(&mut self, ctx: &yew::Context<Self>, _old_props: &Self::Properties) -> bool {
67        self.selected_idx = ctx.props().selected_tab.unwrap_or_default();
68        true
69    }
70
71    fn view(&self, ctx: &yew::Context<Self>) -> Html {
72        let p = ctx.props();
73        let gutter_tabs = p.tabs.iter().enumerate().map(|(idx, tab)| {
74            let mut class = classes!("tab");
75            if idx == self.selected_idx {
76                class.push("selected");
77            }
78
79            let onclick = ctx.link().callback(move |_| TabListMsg::SetSelected(idx));
80            html! {
81                <span {class} {onclick}>
82                    <div class="tab-title" id={tab.to_string()} />
83                    <div class="tab-border" />
84                </span>
85            }
86        });
87
88        html! {
89            <>
90                <LocalStyle href={css!("containers/tabs")} />
91                <div class="tab-gutter">
92                    { for gutter_tabs }
93                    <span class="tab tab-padding">
94                        <div class="tab-title">{ "\u{00a0}" }</div>
95                        <div class="tab-border" />
96                    </span>
97                </div>
98                <div id="format-tab" class="tab-content">
99                    { ctx.props().children.iter().nth(self.selected_idx) }
100                </div>
101            </>
102        }
103    }
104}