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}