Skip to main content

tablo_core/panel/shell/
environment.rs

1//! [`Panel::environment`]: the deployment a panel runs in, named on every page so nobody mistakes
2//! staging for production.
3
4use topcoat::{
5    context::Cx,
6    view::{BoxView, ViewExt, view},
7};
8
9use super::super::Panel;
10use crate::{Color, Text};
11
12/// The deployment a panel runs in, such as staging: every page of the panel, its sign-in pages
13/// included, carries a stripe and a badge in its color, and its browser tab title starts with its
14/// name.
15///
16/// ```rust
17/// # use tablo_core::{Color, Environment, Panel};
18/// let panel = Panel::new("admin");
19/// let panel = match std::env::var("APP_ENV").as_deref() {
20///     Ok("production") => panel,
21///     Ok(name) => panel.environment(Environment::new(name.to_owned()).color(Color::Info)),
22///     Err(_) => panel.environment(Environment::new("Local")),
23/// };
24/// ```
25#[derive(Debug, Clone)]
26pub struct Environment {
27    name: Text,
28    color: Color,
29}
30
31impl Environment {
32    /// An environment named `name`, in [`Color::Warning`].
33    pub fn new(name: impl Into<Text>) -> Self {
34        Self {
35            name: name.into(),
36            color: Color::Warning,
37        }
38    }
39
40    /// Paint the stripe and the badge in `color`.
41    #[must_use]
42    pub fn color(mut self, color: Color) -> Self {
43        self.color = color;
44        self
45    }
46
47    /// The name, in the request's locale.
48    pub(crate) fn name(&self, cx: &Cx) -> String {
49        self.name.localize(cx)
50    }
51
52    /// The stripe along the top of the page and the badge at its bottom: fixed, so neither moves
53    /// the page's layout, and the badge lets clicks through to what it covers.
54    pub(crate) fn ribbon<'a>(&self, cx: &'a Cx) -> BoxView<'a> {
55        let name = self.name(cx);
56        let stripe = format!(
57            "pointer-events-none fixed inset-x-0 top-0 z-50 h-0.5 {}",
58            self.color.fill_class()
59        );
60        let badge = format!(
61            "pointer-events-none fixed bottom-3 left-1/2 z-50 -translate-x-1/2 rounded-full px-3 \
62             py-0.5 text-xs font-medium text-white shadow-sm {}",
63            self.color.fill_class()
64        );
65        view! {
66            <div class=(stripe) aria-hidden="true"></div>
67            <div class=(badge) role="note" data-environment=(name.clone())>(name)</div>
68        }
69        .boxed()
70    }
71}
72
73impl Panel {
74    /// Names the deployment the panel runs in on every page: a stripe and a badge, and the name
75    /// at the start of the browser tab title. Production usually names none.
76    #[must_use]
77    pub fn environment(mut self, environment: Environment) -> Self {
78        self.environment = Some(environment);
79        self
80    }
81}