Archived
add el-style, el-layout, el-i18n, el-config, el-secrets: responsive by default, theme-driven, zero breakpoints
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
/// Spacing scale — 4px base grid.
|
||||
///
|
||||
/// Use named scale values, not raw numbers. This ensures visual consistency
|
||||
/// and makes it easy to tweak the entire system by changing the base unit.
|
||||
|
||||
/// Named spacing scale values.
|
||||
///
|
||||
/// The base unit is 4dp/pt. All values are multiples of 4.
|
||||
/// Use these for padding, margin, gap, and any other spatial measurement.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Spacing {
|
||||
/// 0 — no spacing
|
||||
None,
|
||||
/// 4dp — hairline gap, tight list items
|
||||
Xs,
|
||||
/// 8dp — default tight padding, icon margins
|
||||
Sm,
|
||||
/// 12dp — compact component padding
|
||||
Md,
|
||||
/// 16dp — standard component padding (the workhorse)
|
||||
Lg,
|
||||
/// 24dp — generous padding, card internal spacing
|
||||
Xl,
|
||||
/// 32dp — section separation
|
||||
Xxl,
|
||||
/// 48dp — hero sections, major layout gaps
|
||||
Xxxl,
|
||||
/// 64dp — page-level margins, maximum separation
|
||||
Max,
|
||||
/// Custom value in dp (escape hatch)
|
||||
Custom(u32),
|
||||
}
|
||||
|
||||
impl Spacing {
|
||||
/// Resolve to a concrete dp/pt value.
|
||||
pub fn dp(&self) -> u32 {
|
||||
match self {
|
||||
Spacing::None => 0,
|
||||
Spacing::Xs => 4,
|
||||
Spacing::Sm => 8,
|
||||
Spacing::Md => 12,
|
||||
Spacing::Lg => 16,
|
||||
Spacing::Xl => 24,
|
||||
Spacing::Xxl => 32,
|
||||
Spacing::Xxxl => 48,
|
||||
Spacing::Max => 64,
|
||||
Spacing::Custom(v) => *v,
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve to a concrete CSS pixel string.
|
||||
pub fn to_css(&self) -> String {
|
||||
format!("{}px", self.dp())
|
||||
}
|
||||
}
|
||||
|
||||
/// The spacing scale exposed by a theme.
|
||||
/// Provides the mapping from scale names to concrete values.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SpacingScale {
|
||||
/// Base unit in dp (default: 4).
|
||||
pub base: u32,
|
||||
}
|
||||
|
||||
impl SpacingScale {
|
||||
pub fn default() -> Self {
|
||||
Self { base: 4 }
|
||||
}
|
||||
|
||||
/// Resolve a Spacing token to dp.
|
||||
pub fn resolve(&self, spacing: &Spacing) -> u32 {
|
||||
match spacing {
|
||||
Spacing::Custom(v) => *v,
|
||||
other => {
|
||||
let multiplier = match other {
|
||||
Spacing::None => 0,
|
||||
Spacing::Xs => 1,
|
||||
Spacing::Sm => 2,
|
||||
Spacing::Md => 3,
|
||||
Spacing::Lg => 4,
|
||||
Spacing::Xl => 6,
|
||||
Spacing::Xxl => 8,
|
||||
Spacing::Xxxl => 12,
|
||||
Spacing::Max => 16,
|
||||
Spacing::Custom(_) => unreachable!(),
|
||||
};
|
||||
self.base * multiplier
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn spacing_none_is_zero() {
|
||||
assert_eq!(Spacing::None.dp(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spacing_lg_is_16() {
|
||||
assert_eq!(Spacing::Lg.dp(), 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spacing_max_is_64() {
|
||||
assert_eq!(Spacing::Max.dp(), 64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spacing_custom() {
|
||||
assert_eq!(Spacing::Custom(20).dp(), 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spacing_scale_resolves_base_unit() {
|
||||
let scale = SpacingScale { base: 4 };
|
||||
assert_eq!(scale.resolve(&Spacing::Xs), 4);
|
||||
assert_eq!(scale.resolve(&Spacing::Sm), 8);
|
||||
assert_eq!(scale.resolve(&Spacing::Lg), 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spacing_to_css() {
|
||||
assert_eq!(Spacing::Lg.to_css(), "16px");
|
||||
assert_eq!(Spacing::None.to_css(), "0px");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spacing_scale_custom_scale() {
|
||||
let scale = SpacingScale { base: 8 };
|
||||
assert_eq!(scale.resolve(&Spacing::Xs), 8);
|
||||
assert_eq!(scale.resolve(&Spacing::Sm), 16);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user