use crate::css::Css;
use crate::data_type::LengthPercentage;
#[derive(Clone, Debug, PartialEq)]
pub struct Padding {
pub trbl: [LengthPercentage; 4],
}
impl<T: Into<LengthPercentage>> From<T> for Padding {
fn from(v: T) -> Self {
let v = v.into();
Self {
trbl: [v.clone(), v.clone(), v.clone(), v],
}
}
}
impl<A, B> From<(A, B)> for Padding
where
A: Into<LengthPercentage>,
B: Into<LengthPercentage>,
{
fn from((y, x): (A, B)) -> Self {
let y = y.into();
let x = x.into();
Self {
trbl: [y.clone(), x.clone(), y, x],
}
}
}
impl<A, B, C> From<(A, B, C)> for Padding
where
A: Into<LengthPercentage>,
B: Into<LengthPercentage>,
C: Into<LengthPercentage>,
{
fn from((t, x, b): (A, B, C)) -> Self {
let x = x.into();
Self {
trbl: [t.into(), x.clone(), b.into(), x],
}
}
}
impl<A, B, C, D> From<(A, B, C, D)> for Padding
where
A: Into<LengthPercentage>,
B: Into<LengthPercentage>,
C: Into<LengthPercentage>,
D: Into<LengthPercentage>,
{
fn from((t, r, b, l): (A, B, C, D)) -> Self {
Self {
trbl: [t.into(), r.into(), b.into(), l.into()],
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum MarginValue {
LengthPercentage(LengthPercentage),
Auto,
}
impl crate::to_css::ToCss for MarginValue {
fn to_css(&self, dest: &mut dyn core::fmt::Write) -> core::fmt::Result {
match self {
MarginValue::LengthPercentage(lp) => lp.to_css(dest),
MarginValue::Auto => dest.write_str("auto"),
}
}
}
impl From<LengthPercentage> for MarginValue {
fn from(v: LengthPercentage) -> Self {
Self::LengthPercentage(v)
}
}
impl From<crate::data_type::Length> for MarginValue {
fn from(v: crate::data_type::Length) -> Self {
Self::LengthPercentage(v.into())
}
}
impl From<crate::data_type::Percentage> for MarginValue {
fn from(v: crate::data_type::Percentage) -> Self {
Self::LengthPercentage(v.into())
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Margin {
pub trbl: [MarginValue; 4],
}
impl<T: Into<MarginValue>> From<T> for Margin {
fn from(v: T) -> Self {
let v = v.into();
Self {
trbl: [v.clone(), v.clone(), v.clone(), v],
}
}
}
impl<A, B> From<(A, B)> for Margin
where
A: Into<MarginValue>,
B: Into<MarginValue>,
{
fn from((y, x): (A, B)) -> Self {
let y = y.into();
let x = x.into();
Self {
trbl: [y.clone(), x.clone(), y, x],
}
}
}
impl<A, B, C> From<(A, B, C)> for Margin
where
A: Into<MarginValue>,
B: Into<MarginValue>,
C: Into<MarginValue>,
{
fn from((t, x, b): (A, B, C)) -> Self {
let x = x.into();
Self {
trbl: [t.into(), x.clone(), b.into(), x],
}
}
}
impl<A, B, C, D> From<(A, B, C, D)> for Margin
where
A: Into<MarginValue>,
B: Into<MarginValue>,
C: Into<MarginValue>,
D: Into<MarginValue>,
{
fn from((t, r, b, l): (A, B, C, D)) -> Self {
Self {
trbl: [t.into(), r.into(), b.into(), l.into()],
}
}
}
impl Css {
pub fn padding(self, v: impl Into<Padding>) -> Self {
let Padding { trbl: [t, r, b, l] } = v.into();
self.padding_top(t)
.padding_right(r)
.padding_bottom(b)
.padding_left(l)
}
pub fn margin(self, v: impl Into<Margin>) -> Self {
let Margin { trbl: [t, r, b, l] } = v.into();
self.push("margin-top", t)
.push("margin-right", r)
.push("margin-bottom", b)
.push("margin-left", l)
}
}
#[cfg(test)]
mod tests {
use crate::ext::*;
use crate::shorthand::padding_margin::MarginValue;
use crate::Css;
#[test]
fn padding_single_value_expands_to_four() {
let s = Css::new().padding(px(8));
assert_eq!(
s.to_string(),
"padding-top: 8px; padding-right: 8px; padding-bottom: 8px; padding-left: 8px;"
);
}
#[test]
fn padding_two_value_y_x() {
let s = Css::new().padding((px(8), px(16)));
assert_eq!(
s.to_string(),
"padding-top: 8px; padding-right: 16px; padding-bottom: 8px; padding-left: 16px;"
);
}
#[test]
fn padding_three_value_t_x_b() {
let s = Css::new().padding((px(8), px(16), px(4)));
assert_eq!(
s.to_string(),
"padding-top: 8px; padding-right: 16px; padding-bottom: 4px; padding-left: 16px;"
);
}
#[test]
fn padding_four_value_trbl() {
let s = Css::new().padding((px(2), px(4), px(6), px(8)));
assert_eq!(
s.to_string(),
"padding-top: 2px; padding-right: 4px; padding-bottom: 6px; padding-left: 8px;"
);
}
#[test]
fn padding_then_single_side_override() {
let s = Css::new().padding(px(8)).padding_top(px(0));
assert_eq!(
s.to_string(),
"padding-right: 8px; padding-bottom: 8px; padding-left: 8px; padding-top: 0px;"
);
}
#[test]
fn padding_percentages() {
let s = Css::new().padding(50.percent());
assert_eq!(
s.to_string(),
"padding-top: 50%; padding-right: 50%; padding-bottom: 50%; padding-left: 50%;"
);
}
#[test]
fn margin_single_value() {
let s = Css::new().margin(px(8));
assert_eq!(
s.to_string(),
"margin-top: 8px; margin-right: 8px; margin-bottom: 8px; margin-left: 8px;"
);
}
#[test]
fn margin_auto_centers() {
let s = Css::new().margin((px(0), MarginValue::Auto));
assert_eq!(
s.to_string(),
"margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto;"
);
}
#[test]
fn margin_four_value_with_negative() {
let s = Css::new().margin((px(-4), px(0), px(4), MarginValue::Auto));
assert_eq!(
s.to_string(),
"margin-top: -4px; margin-right: 0px; margin-bottom: 4px; margin-left: auto;"
);
}
#[test]
fn margin_three_value_t_x_b() {
let s = Css::new().margin((px(2), px(4), px(6)));
assert_eq!(
s.to_string(),
"margin-top: 2px; margin-right: 4px; margin-bottom: 6px; margin-left: 4px;"
);
}
#[test]
fn margin_value_from_length_percentage() {
let v: MarginValue = px(4).into();
let v2: MarginValue = 25.percent().into();
let v3: MarginValue =
crate::data_type::LengthPercentage::Length(crate::data_type::Length::Px(8.0)).into();
assert!(matches!(v, MarginValue::LengthPercentage(_)));
assert!(matches!(v2, MarginValue::LengthPercentage(_)));
assert!(matches!(v3, MarginValue::LengthPercentage(_)));
}
}