use std::cell::RefCell;
use std::collections::HashMap;
use web_sys::Element;
#[derive(Clone, Debug)]
pub struct Phase {
pub base: &'static str,
pub from: &'static str,
pub to: &'static str,
}
#[derive(Clone, Debug)]
pub struct Preset {
pub enter: Phase,
pub leave: Phase,
}
impl Preset {
pub const fn symmetric(base: &'static str, from: &'static str, to: &'static str) -> Self {
Self {
enter: Phase { base, from, to },
leave: Phase {
base,
from: to,
to: from,
},
}
}
}
thread_local! {
static REGISTRY: RefCell<Option<HashMap<&'static str, Preset>>> =
const { RefCell::new(None) };
}
fn with_registry<R>(f: impl FnOnce(&mut HashMap<&'static str, Preset>) -> R) -> R {
REGISTRY.with(|cell| {
let mut slot = cell.borrow_mut();
let map = slot.get_or_insert_with(built_ins);
f(map)
})
}
fn built_ins() -> HashMap<&'static str, Preset> {
let mut m = HashMap::new();
m.insert(
"fade",
Preset::symmetric("pp-tx-fade-base", "pp-tx-fade-from", "pp-tx-fade-to"),
);
m.insert(
"scale",
Preset::symmetric("pp-tx-scale-base", "pp-tx-scale-from", "pp-tx-scale-to"),
);
m.insert(
"fade-scale",
Preset::symmetric(
"pp-tx-fade-scale-base",
"pp-tx-fade-scale-from",
"pp-tx-fade-scale-to",
),
);
m.insert(
"zoom",
Preset::symmetric("pp-tx-zoom-base", "pp-tx-zoom-from", "pp-tx-zoom-to"),
);
m.insert(
"slide-up",
Preset::symmetric(
"pp-tx-slide-up-base",
"pp-tx-slide-up-from",
"pp-tx-slide-up-to",
),
);
m.insert(
"slide-down",
Preset::symmetric(
"pp-tx-slide-down-base",
"pp-tx-slide-down-from",
"pp-tx-slide-down-to",
),
);
m.insert(
"slide-left",
Preset::symmetric(
"pp-tx-slide-left-base",
"pp-tx-slide-left-from",
"pp-tx-slide-left-to",
),
);
m.insert(
"slide-right",
Preset::symmetric(
"pp-tx-slide-right-base",
"pp-tx-slide-right-from",
"pp-tx-slide-right-to",
),
);
m.insert(
"collapse",
Preset::symmetric(
"pp-tx-collapse-base",
"pp-tx-collapse-from",
"pp-tx-collapse-to",
),
);
m.insert("none", Preset::symmetric("", "", ""));
m
}
pub fn register_preset(name: &'static str, preset: Preset) -> Result<(), &'static str> {
with_registry(|m| {
if m.contains_key(name) {
Err("preset name already registered")
} else {
m.insert(name, preset);
Ok(())
}
})
}
pub fn lookup(name: &str) -> Option<Preset> {
with_registry(|m| m.iter().find(|(k, _)| **k == name).map(|(_, p)| p.clone()))
}
pub fn apply_preset(el: &Element, in_name: &str, out_name: &str) {
let in_preset = lookup(in_name).unwrap_or_else(|| lookup("none").unwrap());
let out_preset = lookup(out_name).unwrap_or_else(|| lookup("none").unwrap());
let set_or_clear = |attr: &str, value: &str| {
if value.is_empty() {
let _ = el.remove_attribute(attr);
} else {
let _ = el.set_attribute(attr, value);
}
};
set_or_clear("pp-transition:enter", in_preset.enter.base);
set_or_clear("pp-transition:enter-start", in_preset.enter.from);
set_or_clear("pp-transition:enter-end", in_preset.enter.to);
set_or_clear("pp-transition:leave", out_preset.leave.base);
set_or_clear("pp-transition:leave-start", out_preset.leave.from);
set_or_clear("pp-transition:leave-end", out_preset.leave.to);
}
pub(crate) fn inject_atoms_stylesheet() {
crate::styles::inject_style("pocopine-animate-atoms", include_str!("atoms.css"));
}