Skip to main content

revue/runtime/style/properties/
types.rs

1//! CSS property type definitions
2//!
3//! This module contains all enums, structs, and type aliases for CSS properties.
4
5/// Display mode for layout
6#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
7pub enum Display {
8    /// Flexbox layout (default)
9    #[default]
10    Flex,
11    /// Block layout
12    Block,
13    /// CSS Grid layout
14    Grid,
15    /// Hidden - element takes no space
16    None,
17}
18
19/// Position mode for layout
20#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
21pub enum Position {
22    /// Static positioning (default, normal flow)
23    #[default]
24    Static,
25    /// Relative to normal position
26    Relative,
27    /// Positioned relative to nearest positioned ancestor
28    Absolute,
29    /// Positioned relative to viewport
30    Fixed,
31}
32
33/// Grid track sizing
34#[derive(Debug, Clone, Default, PartialEq)]
35pub enum GridTrack {
36    /// Fixed size in cells
37    Fixed(u16),
38    /// Fractional unit (fr)
39    Fr(f32),
40    /// Automatic sizing
41    #[default]
42    Auto,
43    /// Minimum content
44    MinContent,
45    /// Maximum content
46    MaxContent,
47}
48
49/// Grid template (columns or rows)
50#[derive(Debug, Clone, Default, PartialEq)]
51pub struct GridTemplate {
52    /// Track definitions
53    pub tracks: Vec<GridTrack>,
54}
55
56impl GridTemplate {
57    /// Create a new grid template
58    pub fn new(tracks: Vec<GridTrack>) -> Self {
59        Self { tracks }
60    }
61
62    /// Create a template with repeated tracks
63    pub fn repeat(count: usize, track: GridTrack) -> Self {
64        Self {
65            tracks: vec![track; count],
66        }
67    }
68
69    /// Create from fr values
70    pub fn fr(values: &[f32]) -> Self {
71        Self {
72            tracks: values.iter().map(|&v| GridTrack::Fr(v)).collect(),
73        }
74    }
75
76    /// Create from fixed values
77    pub fn fixed(values: &[u16]) -> Self {
78        Self {
79            tracks: values.iter().map(|&v| GridTrack::Fixed(v)).collect(),
80        }
81    }
82}
83
84/// Grid placement for a single axis
85#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
86pub struct GridPlacement {
87    /// Start line (1-based, 0 = auto)
88    pub start: i16,
89    /// End line (1-based, 0 = auto)
90    pub end: i16,
91}
92
93impl GridPlacement {
94    /// Create auto placement
95    pub fn auto() -> Self {
96        Self { start: 0, end: 0 }
97    }
98
99    /// Place at a specific line
100    pub fn line(line: i16) -> Self {
101        Self {
102            start: line,
103            end: 0,
104        }
105    }
106
107    /// Span a number of tracks
108    pub fn span(count: i16) -> Self {
109        Self {
110            start: 0,
111            end: -count,
112        } // Negative end means span
113    }
114
115    /// Place from start to end line
116    pub fn from_to(start: i16, end: i16) -> Self {
117        Self { start, end }
118    }
119}
120
121/// Flex direction for flexbox layout
122#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
123pub enum FlexDirection {
124    /// Horizontal layout (default)
125    #[default]
126    Row,
127    /// Vertical layout
128    Column,
129}
130
131/// Main axis alignment for flexbox
132#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
133pub enum JustifyContent {
134    /// Align to start (default)
135    #[default]
136    Start,
137    /// Center alignment
138    Center,
139    /// Align to end
140    End,
141    /// Space between items
142    SpaceBetween,
143    /// Space around items
144    SpaceAround,
145}
146
147/// Cross axis alignment for flexbox
148#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
149pub enum AlignItems {
150    /// Align to start (default)
151    #[default]
152    Start,
153    /// Center alignment
154    Center,
155    /// Align to end
156    End,
157    /// Stretch to fill
158    Stretch,
159}
160
161/// Spacing values for padding and margin
162#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
163pub struct Spacing {
164    /// Top spacing
165    pub top: u16,
166    /// Right spacing
167    pub right: u16,
168    /// Bottom spacing
169    pub bottom: u16,
170    /// Left spacing
171    pub left: u16,
172}
173
174impl Spacing {
175    /// Create spacing with same value on all sides
176    pub fn all(value: u16) -> Self {
177        Self {
178            top: value,
179            right: value,
180            bottom: value,
181            left: value,
182        }
183    }
184
185    /// Create spacing with vertical (top/bottom) values only
186    pub fn vertical(value: u16) -> Self {
187        Self {
188            top: value,
189            right: 0,
190            bottom: value,
191            left: 0,
192        }
193    }
194
195    /// Create spacing with horizontal (left/right) values only
196    pub fn horizontal(value: u16) -> Self {
197        Self {
198            top: 0,
199            right: value,
200            bottom: 0,
201            left: value,
202        }
203    }
204
205    /// Create spacing with individual values
206    pub fn new(top: u16, right: u16, bottom: u16, left: u16) -> Self {
207        Self {
208            top,
209            right,
210            bottom,
211            left,
212        }
213    }
214}
215
216/// Size constraint for width/height
217#[derive(Debug, Clone, Copy, Default, PartialEq)]
218pub enum Size {
219    /// Automatic sizing (default)
220    #[default]
221    Auto,
222    /// Fixed size in cells
223    Fixed(u16),
224    /// Percentage of parent
225    Percent(f32),
226}
227
228/// Border style options
229#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
230pub enum BorderStyle {
231    /// No border (default)
232    #[default]
233    None,
234    /// Solid line border
235    Solid,
236    /// Dashed line border
237    Dashed,
238    /// Double line border
239    Double,
240    /// Rounded corner border
241    Rounded,
242}
243
244/// RGBA color value with alpha channel
245///
246/// Alpha channel: 0 = fully transparent, 255 = fully opaque.
247/// Default color has alpha=0 to indicate "unset" state.
248#[derive(Debug, Clone, Copy, PartialEq, Eq)]
249pub struct Color {
250    /// Red component (0-255)
251    pub r: u8,
252    /// Green component (0-255)
253    pub g: u8,
254    /// Blue component (0-255)
255    pub b: u8,
256    /// Alpha component (0=transparent, 255=opaque)
257    pub a: u8,
258}
259
260impl Default for Color {
261    /// Default color is transparent black (unset state)
262    fn default() -> Self {
263        Self {
264            r: 0,
265            g: 0,
266            b: 0,
267            a: 0,
268        }
269    }
270}
271
272/// CSS property enum for dynamic property handling
273#[derive(Debug, Clone)]
274pub enum Property {
275    /// Display property value
276    Display(Display),
277    /// Flex direction property value
278    FlexDirection(FlexDirection),
279    /// Padding property value
280    Padding(Spacing),
281    /// Margin property value
282    Margin(Spacing),
283    /// Color property value
284    Color(Color),
285    /// Background property value
286    Background(Color),
287    /// Border style property value
288    BorderStyle(BorderStyle),
289    /// Border color property value
290    BorderColor(Color),
291}
292
293#[cfg(test)]
294mod tests {
295    use super::*;
296
297    // Display tests
298    #[test]
299    fn test_display_default() {
300        assert_eq!(Display::default(), Display::Flex);
301    }
302
303    #[test]
304    fn test_display_variants() {
305        assert_eq!(Display::Flex, Display::Flex);
306        assert_eq!(Display::Block, Display::Block);
307        assert_eq!(Display::Grid, Display::Grid);
308        assert_eq!(Display::None, Display::None);
309    }
310
311    // Position tests
312    #[test]
313    fn test_position_default() {
314        assert_eq!(Position::default(), Position::Static);
315    }
316
317    #[test]
318    fn test_position_variants() {
319        assert_eq!(Position::Static, Position::Static);
320        assert_eq!(Position::Relative, Position::Relative);
321        assert_eq!(Position::Absolute, Position::Absolute);
322        assert_eq!(Position::Fixed, Position::Fixed);
323    }
324
325    // GridTrack tests
326    #[test]
327    fn test_grid_track_default() {
328        assert_eq!(GridTrack::default(), GridTrack::Auto);
329    }
330
331    #[test]
332    fn test_grid_track_fixed() {
333        let track = GridTrack::Fixed(10);
334        assert_eq!(track, GridTrack::Fixed(10));
335    }
336
337    #[test]
338    fn test_grid_track_fr() {
339        let track = GridTrack::Fr(1.5);
340        assert_eq!(track, GridTrack::Fr(1.5));
341    }
342
343    #[test]
344    fn test_grid_track_min_content() {
345        let track = GridTrack::MinContent;
346        assert_eq!(track, GridTrack::MinContent);
347    }
348
349    #[test]
350    fn test_grid_track_max_content() {
351        let track = GridTrack::MaxContent;
352        assert_eq!(track, GridTrack::MaxContent);
353    }
354
355    // GridTemplate tests
356    #[test]
357    fn test_grid_template_default() {
358        let template = GridTemplate::default();
359        assert!(template.tracks.is_empty());
360    }
361
362    #[test]
363    fn test_grid_template_new() {
364        let template = GridTemplate::new(vec![GridTrack::Fixed(10), GridTrack::Fr(1.0)]);
365        assert_eq!(template.tracks.len(), 2);
366    }
367
368    #[test]
369    fn test_grid_template_repeat() {
370        let template = GridTemplate::repeat(3, GridTrack::Fixed(10));
371        assert_eq!(template.tracks.len(), 3);
372        assert!(template.tracks.iter().all(|t| t == &GridTrack::Fixed(10)));
373    }
374
375    #[test]
376    fn test_grid_template_fr() {
377        let template = GridTemplate::fr(&[1.0, 2.0, 1.5]);
378        assert_eq!(template.tracks.len(), 3);
379        assert_eq!(template.tracks[0], GridTrack::Fr(1.0));
380        assert_eq!(template.tracks[1], GridTrack::Fr(2.0));
381        assert_eq!(template.tracks[2], GridTrack::Fr(1.5));
382    }
383
384    #[test]
385    fn test_grid_template_fixed() {
386        let template = GridTemplate::fixed(&[10, 20, 30]);
387        assert_eq!(template.tracks.len(), 3);
388        assert_eq!(template.tracks[0], GridTrack::Fixed(10));
389        assert_eq!(template.tracks[1], GridTrack::Fixed(20));
390        assert_eq!(template.tracks[2], GridTrack::Fixed(30));
391    }
392
393    // GridPlacement tests
394    #[test]
395    fn test_grid_placement_default() {
396        let placement = GridPlacement::default();
397        assert_eq!(placement.start, 0);
398        assert_eq!(placement.end, 0);
399    }
400
401    #[test]
402    fn test_grid_placement_auto() {
403        let placement = GridPlacement::auto();
404        assert_eq!(placement.start, 0);
405        assert_eq!(placement.end, 0);
406    }
407
408    #[test]
409    fn test_grid_placement_line() {
410        let placement = GridPlacement::line(5);
411        assert_eq!(placement.start, 5);
412        assert_eq!(placement.end, 0);
413    }
414
415    #[test]
416    fn test_grid_placement_span() {
417        let placement = GridPlacement::span(3);
418        assert_eq!(placement.start, 0);
419        assert_eq!(placement.end, -3);
420    }
421
422    #[test]
423    fn test_grid_placement_from_to() {
424        let placement = GridPlacement::from_to(2, 5);
425        assert_eq!(placement.start, 2);
426        assert_eq!(placement.end, 5);
427    }
428
429    // FlexDirection tests
430    #[test]
431    fn test_flex_direction_default() {
432        assert_eq!(FlexDirection::default(), FlexDirection::Row);
433    }
434
435    #[test]
436    fn test_flex_direction_variants() {
437        assert_eq!(FlexDirection::Row, FlexDirection::Row);
438        assert_eq!(FlexDirection::Column, FlexDirection::Column);
439    }
440
441    // JustifyContent tests
442    #[test]
443    fn test_justify_content_default() {
444        assert_eq!(JustifyContent::default(), JustifyContent::Start);
445    }
446
447    #[test]
448    fn test_justify_content_variants() {
449        assert_eq!(JustifyContent::Start, JustifyContent::Start);
450        assert_eq!(JustifyContent::Center, JustifyContent::Center);
451        assert_eq!(JustifyContent::End, JustifyContent::End);
452        assert_eq!(JustifyContent::SpaceBetween, JustifyContent::SpaceBetween);
453        assert_eq!(JustifyContent::SpaceAround, JustifyContent::SpaceAround);
454    }
455
456    // AlignItems tests
457    #[test]
458    fn test_align_items_default() {
459        assert_eq!(AlignItems::default(), AlignItems::Start);
460    }
461
462    #[test]
463    fn test_align_items_variants() {
464        assert_eq!(AlignItems::Start, AlignItems::Start);
465        assert_eq!(AlignItems::Center, AlignItems::Center);
466        assert_eq!(AlignItems::End, AlignItems::End);
467        assert_eq!(AlignItems::Stretch, AlignItems::Stretch);
468    }
469
470    // Spacing tests
471    #[test]
472    fn test_spacing_default() {
473        let spacing = Spacing::default();
474        assert_eq!(spacing.top, 0);
475        assert_eq!(spacing.right, 0);
476        assert_eq!(spacing.bottom, 0);
477        assert_eq!(spacing.left, 0);
478    }
479
480    #[test]
481    fn test_spacing_all() {
482        let spacing = Spacing::all(10);
483        assert_eq!(spacing.top, 10);
484        assert_eq!(spacing.right, 10);
485        assert_eq!(spacing.bottom, 10);
486        assert_eq!(spacing.left, 10);
487    }
488
489    #[test]
490    fn test_spacing_vertical() {
491        let spacing = Spacing::vertical(10);
492        assert_eq!(spacing.top, 10);
493        assert_eq!(spacing.right, 0);
494        assert_eq!(spacing.bottom, 10);
495        assert_eq!(spacing.left, 0);
496    }
497
498    #[test]
499    fn test_spacing_horizontal() {
500        let spacing = Spacing::horizontal(10);
501        assert_eq!(spacing.top, 0);
502        assert_eq!(spacing.right, 10);
503        assert_eq!(spacing.bottom, 0);
504        assert_eq!(spacing.left, 10);
505    }
506
507    #[test]
508    fn test_spacing_new() {
509        let spacing = Spacing::new(10, 20, 30, 40);
510        assert_eq!(spacing.top, 10);
511        assert_eq!(spacing.right, 20);
512        assert_eq!(spacing.bottom, 30);
513        assert_eq!(spacing.left, 40);
514    }
515
516    // Size tests
517    #[test]
518    fn test_size_default() {
519        assert_eq!(Size::default(), Size::Auto);
520    }
521
522    #[test]
523    fn test_size_auto() {
524        assert_eq!(Size::Auto, Size::Auto);
525    }
526
527    #[test]
528    fn test_size_fixed() {
529        let size = Size::Fixed(100);
530        assert_eq!(size, Size::Fixed(100));
531    }
532
533    #[test]
534    fn test_size_percent() {
535        let size = Size::Percent(50.0);
536        assert_eq!(size, Size::Percent(50.0));
537    }
538
539    // BorderStyle tests
540    #[test]
541    fn test_border_style_default() {
542        assert_eq!(BorderStyle::default(), BorderStyle::None);
543    }
544
545    #[test]
546    fn test_border_style_variants() {
547        assert_eq!(BorderStyle::None, BorderStyle::None);
548        assert_eq!(BorderStyle::Solid, BorderStyle::Solid);
549        assert_eq!(BorderStyle::Dashed, BorderStyle::Dashed);
550        assert_eq!(BorderStyle::Double, BorderStyle::Double);
551        assert_eq!(BorderStyle::Rounded, BorderStyle::Rounded);
552    }
553
554    // Color tests
555    #[test]
556    fn test_color_default() {
557        let color = Color::default();
558        assert_eq!(color.r, 0);
559        assert_eq!(color.g, 0);
560        assert_eq!(color.b, 0);
561        assert_eq!(color.a, 0);
562    }
563
564    #[test]
565    fn test_color_partial_eq() {
566        let color1 = Color::rgb(255, 0, 0);
567        let color2 = Color::rgb(255, 0, 0);
568        assert_eq!(color1, color2);
569    }
570
571    #[test]
572    fn test_color_partial_ne() {
573        let color1 = Color::rgb(255, 0, 0);
574        let color2 = Color::rgb(0, 255, 0);
575        assert_ne!(color1, color2);
576    }
577
578    #[test]
579    fn test_color_copy_trait() {
580        let color1 = Color::rgb(255, 0, 0);
581        let color2 = color1;
582        assert_eq!(color1.r, 255);
583        assert_eq!(color2.r, 255);
584    }
585}