Skip to main content

perspective_viewer/ui/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 std::fmt::Display;
14
15use yew::{Callback, Children, Component, Html, Properties, classes, html};
16
17use crate::ui::form::intl_label::{intl_content_style, intl_slug};
18
19pub trait TabItem: PartialEq + Display + Clone + Default + 'static {}
20
21impl TabItem for String {}
22
23impl TabItem for &'static str {}
24
25#[derive(Properties, Debug, PartialEq)]
26pub struct TabListProps<T: TabItem> {
27    pub tabs: Vec<T>,
28
29    pub on_tab_change: Callback<(usize, T)>,
30
31    pub selected_tab: Option<usize>,
32
33    pub children: Children,
34}
35
36pub enum TabListMsg {
37    SetSelected(usize),
38}
39
40pub struct TabList<T: TabItem> {
41    t: std::marker::PhantomData<T>,
42    selected_idx: usize,
43}
44
45impl<T: TabItem> Component for TabList<T> {
46    type Message = TabListMsg;
47    type Properties = TabListProps<T>;
48
49    fn create(_ctx: &yew::Context<Self>) -> Self {
50        Self {
51            t: std::marker::PhantomData,
52            selected_idx: 0,
53        }
54    }
55
56    fn update(&mut self, ctx: &yew::Context<Self>, msg: Self::Message) -> bool {
57        match msg {
58            TabListMsg::SetSelected(idx) => {
59                ctx.props()
60                    .on_tab_change
61                    .emit((idx, ctx.props().tabs[idx].clone()));
62                self.selected_idx = idx;
63                true
64            },
65        }
66    }
67
68    fn changed(&mut self, ctx: &yew::Context<Self>, _old_props: &Self::Properties) -> bool {
69        self.selected_idx = ctx.props().selected_tab.unwrap_or_default();
70        true
71    }
72
73    fn view(&self, ctx: &yew::Context<Self>) -> Html {
74        let p = ctx.props();
75        let gutter_tabs = p.tabs.iter().enumerate().map(|(idx, tab)| {
76            let mut class = classes!("settings_tab");
77            if idx == self.selected_idx {
78                class.push("selected_tab");
79            }
80
81            let onclick = ctx.link().callback(move |_| TabListMsg::SetSelected(idx));
82            let title = tab.to_string();
83            let style = intl_content_style(&format!("{}-tab", intl_slug(&title)));
84            html! {
85                <span {class} {onclick}>
86                    <div class="tab-title" id={title} {style} />
87                    <div class="tab-border" />
88                </span>
89            }
90        });
91
92        html! {
93            <>
94                <div id="settings_tab_bar">{ for gutter_tabs }</div>
95                <div id="format-tab" class="tab-content scrollable">
96                    { ctx.props().children.iter().nth(self.selected_idx) }
97                </div>
98            </>
99        }
100    }
101}