1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
// ┃ This file is part of the Perspective library, distributed under the terms ┃
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

use yew::{classes, html, Callback, Children, Component, Html, Properties};

use crate::components::style::LocalStyle;
use crate::css;

pub trait Tab: PartialEq + std::fmt::Display + Clone + Default + 'static {}

impl Tab for String {}

impl Tab for &'static str {}

#[derive(Properties, Debug, PartialEq)]
pub struct TabListProps<T: Tab> {
    // all possible tabs
    pub tabs: Vec<T>,
    pub on_tab_change: Callback<(usize, T)>,
    pub selected_tab: Option<usize>,
    // the curently instantiated tabs
    pub children: Children,
}

pub enum TabListMsg {
    SetSelected(usize),
}

pub struct TabList<T: Tab> {
    t: std::marker::PhantomData<T>,
    selected_idx: usize,
}

impl<T: Tab> Component for TabList<T> {
    type Message = TabListMsg;
    type Properties = TabListProps<T>;

    fn create(_ctx: &yew::Context<Self>) -> Self {
        Self {
            t: std::marker::PhantomData,
            selected_idx: 0,
        }
    }

    fn update(&mut self, ctx: &yew::Context<Self>, msg: Self::Message) -> bool {
        match msg {
            TabListMsg::SetSelected(idx) => {
                ctx.props()
                    .on_tab_change
                    .emit((idx, ctx.props().tabs[idx].clone()));
                self.selected_idx = idx;
                true
            },
        }
    }

    fn changed(&mut self, ctx: &yew::Context<Self>, _old_props: &Self::Properties) -> bool {
        self.selected_idx = ctx.props().selected_tab.unwrap_or_default();
        true
    }

    fn view(&self, ctx: &yew::Context<Self>) -> Html {
        let p = ctx.props();
        let gutter_tabs = p.tabs.iter().enumerate().map(|(idx, tab)| {
            let mut class = classes!("tab");
            if idx == self.selected_idx {
                class.push("selected");
            }

            let onclick = ctx.link().callback(move |_| TabListMsg::SetSelected(idx));
            html! {
                <span {class} {onclick}>
                    <div class="tab-title" id={tab.to_string()} />
                    <div class="tab-border" />
                </span>
            }
        });

        html! {
            <>
                <LocalStyle href={css!("containers/tabs")} />
                <div class="tab-gutter">
                    { for gutter_tabs }
                    <span class="tab tab-padding">
                        <div class="tab-title">{ "\u{00a0}" }</div>
                        <div class="tab-border" />
                    </span>
                </div>
                <div id="format-tab" class="tab-content">
                    { ctx.props().children.iter().nth(self.selected_idx) }
                </div>
            </>
        }
    }
}