taffy/style/
grid.rs

1//! Style types for CSS Grid layout
2use super::{AlignContent, AlignItems, AlignSelf, CoreStyle, JustifyContent, LengthPercentage, Style};
3use crate::compute::grid::{GridCoordinate, GridLine, OriginZeroLine};
4use crate::geometry::{AbsoluteAxis, AbstractAxis, Line, MinMax, Size};
5use crate::style_helpers::*;
6use crate::util::sys::GridTrackVec;
7use core::borrow::Borrow;
8use core::cmp::{max, min};
9use core::convert::Infallible;
10
11/// The set of styles required for a CSS Grid container
12pub trait GridContainerStyle: CoreStyle {
13    /// The type returned by grid_template_rows and grid_template_columns
14    type TemplateTrackList<'a>: Borrow<[TrackSizingFunction]>
15    where
16        Self: 'a;
17    /// The type returned by grid_auto_rows and grid_auto_columns
18    type AutoTrackList<'a>: Borrow<[NonRepeatedTrackSizingFunction]>
19    where
20        Self: 'a;
21
22    // FIXME: re-add default implemenations for grid_{template,auto}_{rows,columns} once the
23    // associated_type_defaults feature (https://github.com/rust-lang/rust/issues/29661) is stabilised.
24
25    /// Defines the track sizing functions (heights) of the grid rows
26    fn grid_template_rows(&self) -> Self::TemplateTrackList<'_>;
27    /// Defines the track sizing functions (widths) of the grid columns
28    fn grid_template_columns(&self) -> Self::TemplateTrackList<'_>;
29    /// Defines the size of implicitly created rows
30    fn grid_auto_rows(&self) -> Self::AutoTrackList<'_>;
31    /// Defined the size of implicitly created columns
32    fn grid_auto_columns(&self) -> Self::AutoTrackList<'_>;
33
34    /// Controls how items get placed into the grid for auto-placed items
35    #[inline(always)]
36    fn grid_auto_flow(&self) -> GridAutoFlow {
37        Style::DEFAULT.grid_auto_flow
38    }
39
40    /// How large should the gaps between items in a grid or flex container be?
41    #[inline(always)]
42    fn gap(&self) -> Size<LengthPercentage> {
43        Style::DEFAULT.gap
44    }
45
46    // Alignment properties
47
48    /// How should content contained within this item be aligned in the cross/block axis
49    #[inline(always)]
50    fn align_content(&self) -> Option<AlignContent> {
51        Style::DEFAULT.align_content
52    }
53    /// How should contained within this item be aligned in the main/inline axis
54    #[inline(always)]
55    fn justify_content(&self) -> Option<JustifyContent> {
56        Style::DEFAULT.justify_content
57    }
58    /// How this node's children aligned in the cross/block axis?
59    #[inline(always)]
60    fn align_items(&self) -> Option<AlignItems> {
61        Style::DEFAULT.align_items
62    }
63    /// How this node's children should be aligned in the inline axis
64    #[inline(always)]
65    fn justify_items(&self) -> Option<AlignItems> {
66        Style::DEFAULT.justify_items
67    }
68
69    /// Get a grid item's row or column placement depending on the axis passed
70    #[inline(always)]
71    fn grid_template_tracks(&self, axis: AbsoluteAxis) -> Self::TemplateTrackList<'_> {
72        match axis {
73            AbsoluteAxis::Horizontal => self.grid_template_columns(),
74            AbsoluteAxis::Vertical => self.grid_template_rows(),
75        }
76    }
77
78    /// Get a grid container's align-content or justify-content alignment depending on the axis passed
79    #[inline(always)]
80    fn grid_align_content(&self, axis: AbstractAxis) -> AlignContent {
81        match axis {
82            AbstractAxis::Inline => self.justify_content().unwrap_or(AlignContent::Stretch),
83            AbstractAxis::Block => self.align_content().unwrap_or(AlignContent::Stretch),
84        }
85    }
86}
87
88/// The set of styles required for a CSS Grid item (child of a CSS Grid container)
89pub trait GridItemStyle: CoreStyle {
90    /// Defines which row in the grid the item should start and end at
91    #[inline(always)]
92    fn grid_row(&self) -> Line<GridPlacement> {
93        Style::DEFAULT.grid_row
94    }
95    /// Defines which column in the grid the item should start and end at
96    #[inline(always)]
97    fn grid_column(&self) -> Line<GridPlacement> {
98        Style::DEFAULT.grid_column
99    }
100
101    /// How this node should be aligned in the cross/block axis
102    /// Falls back to the parents [`AlignItems`] if not set
103    #[inline(always)]
104    fn align_self(&self) -> Option<AlignSelf> {
105        Style::DEFAULT.align_self
106    }
107    /// How this node should be aligned in the inline axis
108    /// Falls back to the parents [`super::JustifyItems`] if not set
109    #[inline(always)]
110    fn justify_self(&self) -> Option<AlignSelf> {
111        Style::DEFAULT.justify_self
112    }
113
114    /// Get a grid item's row or column placement depending on the axis passed
115    #[inline(always)]
116    fn grid_placement(&self, axis: AbsoluteAxis) -> Line<GridPlacement> {
117        match axis {
118            AbsoluteAxis::Horizontal => self.grid_column(),
119            AbsoluteAxis::Vertical => self.grid_row(),
120        }
121    }
122}
123
124/// Controls whether grid items are placed row-wise or column-wise. And whether the sparse or dense packing algorithm is used.
125///
126/// The "dense" packing algorithm attempts to fill in holes earlier in the grid, if smaller items come up later. This may cause items to appear out-of-order, when doing so would fill in holes left by larger items.
127///
128/// Defaults to [`GridAutoFlow::Row`]
129///
130/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow)
131#[derive(Copy, Clone, PartialEq, Eq, Debug)]
132#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
133pub enum GridAutoFlow {
134    /// Items are placed by filling each row in turn, adding new rows as necessary
135    Row,
136    /// Items are placed by filling each column in turn, adding new columns as necessary.
137    Column,
138    /// Combines `Row` with the dense packing algorithm.
139    RowDense,
140    /// Combines `Column` with the dense packing algorithm.
141    ColumnDense,
142}
143
144impl Default for GridAutoFlow {
145    fn default() -> Self {
146        Self::Row
147    }
148}
149
150impl GridAutoFlow {
151    /// Whether grid auto placement uses the sparse placement algorithm or the dense placement algorithm
152    /// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow#values>
153    pub fn is_dense(&self) -> bool {
154        match self {
155            Self::Row | Self::Column => false,
156            Self::RowDense | Self::ColumnDense => true,
157        }
158    }
159
160    /// Whether grid auto placement fills areas row-wise or column-wise
161    /// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow#values>
162    pub fn primary_axis(&self) -> AbsoluteAxis {
163        match self {
164            Self::Row | Self::RowDense => AbsoluteAxis::Horizontal,
165            Self::Column | Self::ColumnDense => AbsoluteAxis::Vertical,
166        }
167    }
168}
169
170/// A grid line placement specification which is generic over the coordinate system that it uses to define
171/// grid line positions.
172///
173/// GenericGridPlacement<GridLine> is aliased as GridPlacement and is exposed to users of Taffy to define styles.
174/// GenericGridPlacement<OriginZeroLine> is aliased as OriginZeroGridPlacement and is used internally for placement computations.
175///
176/// See [`crate::compute::grid::type::coordinates`] for documentation on the different coordinate systems.
177#[derive(Copy, Clone, PartialEq, Eq, Debug)]
178#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
179pub enum GenericGridPlacement<LineType: GridCoordinate> {
180    /// Place item according to the auto-placement algorithm, and the parent's grid_auto_flow property
181    Auto,
182    /// Place item at specified line (column or row) index
183    Line(LineType),
184    /// Item should span specified number of tracks (columns or rows)
185    Span(u16),
186}
187
188/// A grid line placement using the normalized OriginZero coordinates to specify line positions.
189pub(crate) type OriginZeroGridPlacement = GenericGridPlacement<OriginZeroLine>;
190
191/// A grid line placement specification. Used for grid-[row/column]-[start/end]. Named tracks are not implemented.
192///
193/// Defaults to `GridPlacement::Auto`
194///
195/// [Specification](https://www.w3.org/TR/css3-grid-layout/#typedef-grid-row-start-grid-line)
196pub type GridPlacement = GenericGridPlacement<GridLine>;
197impl TaffyAuto for GridPlacement {
198    const AUTO: Self = Self::Auto;
199}
200impl TaffyGridLine for GridPlacement {
201    fn from_line_index(index: i16) -> Self {
202        GridPlacement::Line(GridLine::from(index))
203    }
204}
205impl TaffyGridLine for Line<GridPlacement> {
206    fn from_line_index(index: i16) -> Self {
207        Line { start: GridPlacement::from_line_index(index), end: GridPlacement::Auto }
208    }
209}
210impl TaffyGridSpan for GridPlacement {
211    fn from_span(span: u16) -> Self {
212        GridPlacement::Span(span)
213    }
214}
215impl TaffyGridSpan for Line<GridPlacement> {
216    fn from_span(span: u16) -> Self {
217        Line { start: GridPlacement::from_span(span), end: GridPlacement::Auto }
218    }
219}
220
221impl Default for GridPlacement {
222    fn default() -> Self {
223        Self::Auto
224    }
225}
226
227impl GridPlacement {
228    /// Apply a mapping function if the [`GridPlacement`] is a `Track`. Otherwise return `self` unmodified.
229    pub fn into_origin_zero_placement(self, explicit_track_count: u16) -> OriginZeroGridPlacement {
230        match self {
231            Self::Auto => OriginZeroGridPlacement::Auto,
232            Self::Span(span) => OriginZeroGridPlacement::Span(span),
233            // Grid line zero is an invalid index, so it gets treated as Auto
234            // See: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-row-start#values
235            Self::Line(line) => match line.as_i16() {
236                0 => OriginZeroGridPlacement::Auto,
237                _ => OriginZeroGridPlacement::Line(line.into_origin_zero_line(explicit_track_count)),
238            },
239        }
240    }
241}
242
243impl<T: GridCoordinate> Line<GenericGridPlacement<T>> {
244    /// Resolves the span for an indefinite placement (a placement that does not consist of two `Track`s).
245    /// Panics if called on a definite placement
246    pub fn indefinite_span(&self) -> u16 {
247        use GenericGridPlacement as GP;
248        match (self.start, self.end) {
249            (GP::Line(_), GP::Auto) => 1,
250            (GP::Auto, GP::Line(_)) => 1,
251            (GP::Auto, GP::Auto) => 1,
252            (GP::Line(_), GP::Span(span)) => span,
253            (GP::Span(span), GP::Line(_)) => span,
254            (GP::Span(span), GP::Auto) => span,
255            (GP::Auto, GP::Span(span)) => span,
256            (GP::Span(span), GP::Span(_)) => span,
257            (GP::Line(_), GP::Line(_)) => panic!("indefinite_span should only be called on indefinite grid tracks"),
258        }
259    }
260}
261
262impl Line<GridPlacement> {
263    #[inline]
264    /// Whether the track position is definite in this axis (or the item will need auto placement)
265    /// The track position is definite if least one of the start and end positions is a NON-ZERO track index
266    /// (0 is an invalid line in GridLine coordinates, and falls back to "auto" which is indefinite)
267    pub fn is_definite(&self) -> bool {
268        match (self.start, self.end) {
269            (GenericGridPlacement::Line(line), _) if line.as_i16() != 0 => true,
270            (_, GenericGridPlacement::Line(line)) if line.as_i16() != 0 => true,
271            _ => false,
272        }
273    }
274
275    /// Apply a mapping function if the [`GridPlacement`] is a `Track`. Otherwise return `self` unmodified.
276    pub fn into_origin_zero(&self, explicit_track_count: u16) -> Line<OriginZeroGridPlacement> {
277        Line {
278            start: self.start.into_origin_zero_placement(explicit_track_count),
279            end: self.end.into_origin_zero_placement(explicit_track_count),
280        }
281    }
282}
283
284impl Line<OriginZeroGridPlacement> {
285    #[inline]
286    /// Whether the track position is definite in this axis (or the item will need auto placement)
287    /// The track position is definite if least one of the start and end positions is a track index
288    pub fn is_definite(&self) -> bool {
289        matches!((self.start, self.end), (GenericGridPlacement::Line(_), _) | (_, GenericGridPlacement::Line(_)))
290    }
291
292    /// If at least one of the of the start and end positions is a track index then the other end can be resolved
293    /// into a track index purely based on the information contained with the placement specification
294    pub fn resolve_definite_grid_lines(&self) -> Line<OriginZeroLine> {
295        use OriginZeroGridPlacement as GP;
296        match (self.start, self.end) {
297            (GP::Line(line1), GP::Line(line2)) => {
298                if line1 == line2 {
299                    Line { start: line1, end: line1 + 1 }
300                } else {
301                    Line { start: min(line1, line2), end: max(line1, line2) }
302                }
303            }
304            (GP::Line(line), GP::Span(span)) => Line { start: line, end: line + span },
305            (GP::Line(line), GP::Auto) => Line { start: line, end: line + 1 },
306            (GP::Span(span), GP::Line(line)) => Line { start: line - span, end: line },
307            (GP::Auto, GP::Line(line)) => Line { start: line - 1, end: line },
308            _ => panic!("resolve_definite_grid_tracks should only be called on definite grid tracks"),
309        }
310    }
311
312    /// For absolutely positioned items:
313    ///   - Tracks resolve to definite tracks
314    ///   - For Spans:
315    ///      - If the other position is a Track, they resolve to a definite track relative to the other track
316    ///      - Else resolve to None
317    ///   - Auto resolves to None
318    ///
319    /// When finally positioning the item, a value of None means that the item's grid area is bounded by the grid
320    /// container's border box on that side.
321    pub fn resolve_absolutely_positioned_grid_tracks(&self) -> Line<Option<OriginZeroLine>> {
322        use OriginZeroGridPlacement as GP;
323        match (self.start, self.end) {
324            (GP::Line(track1), GP::Line(track2)) => {
325                if track1 == track2 {
326                    Line { start: Some(track1), end: Some(track1 + 1) }
327                } else {
328                    Line { start: Some(min(track1, track2)), end: Some(max(track1, track2)) }
329                }
330            }
331            (GP::Line(track), GP::Span(span)) => Line { start: Some(track), end: Some(track + span) },
332            (GP::Line(track), GP::Auto) => Line { start: Some(track), end: None },
333            (GP::Span(span), GP::Line(track)) => Line { start: Some(track - span), end: Some(track) },
334            (GP::Auto, GP::Line(track)) => Line { start: None, end: Some(track) },
335            _ => Line { start: None, end: None },
336        }
337    }
338
339    /// If neither of the start and end positions is a track index then the other end can be resolved
340    /// into a track index if a definite start position is supplied externally
341    pub fn resolve_indefinite_grid_tracks(&self, start: OriginZeroLine) -> Line<OriginZeroLine> {
342        use OriginZeroGridPlacement as GP;
343        match (self.start, self.end) {
344            (GP::Auto, GP::Auto) => Line { start, end: start + 1 },
345            (GP::Span(span), GP::Auto) => Line { start, end: start + span },
346            (GP::Auto, GP::Span(span)) => Line { start, end: start + span },
347            (GP::Span(span), GP::Span(_)) => Line { start, end: start + span },
348            _ => panic!("resolve_indefinite_grid_tracks should only be called on indefinite grid tracks"),
349        }
350    }
351}
352
353/// Represents the start and end points of a GridItem within a given axis
354impl Default for Line<GridPlacement> {
355    fn default() -> Self {
356        Line { start: GridPlacement::Auto, end: GridPlacement::Auto }
357    }
358}
359
360/// Maximum track sizing function
361///
362/// Specifies the maximum size of a grid track. A grid track will automatically size between it's minimum and maximum size based
363/// on the size of it's contents, the amount of available space, and the sizing constraint the grid is being size under.
364/// See <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns>
365#[derive(Copy, Clone, PartialEq, Debug)]
366#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
367pub enum MaxTrackSizingFunction {
368    /// Track maximum size should be a fixed length or percentage value
369    Fixed(LengthPercentage),
370    /// Track maximum size should be content sized under a min-content constraint
371    MinContent,
372    /// Track maximum size should be content sized under a max-content constraint
373    MaxContent,
374    /// Track maximum size should be sized according to the fit-content formula
375    FitContent(LengthPercentage),
376    /// Track maximum size should be automatically sized
377    Auto,
378    /// The dimension as a fraction of the total available grid space (`fr` units in CSS)
379    /// Specified value is the numerator of the fraction. Denominator is the sum of all fraction specified in that grid dimension
380    /// Spec: <https://www.w3.org/TR/css3-grid-layout/#fr-unit>
381    Fraction(f32),
382}
383impl TaffyAuto for MaxTrackSizingFunction {
384    const AUTO: Self = Self::Auto;
385}
386impl TaffyMinContent for MaxTrackSizingFunction {
387    const MIN_CONTENT: Self = Self::MinContent;
388}
389impl TaffyMaxContent for MaxTrackSizingFunction {
390    const MAX_CONTENT: Self = Self::MaxContent;
391}
392impl TaffyFitContent for MaxTrackSizingFunction {
393    fn fit_content(argument: LengthPercentage) -> Self {
394        Self::FitContent(argument)
395    }
396}
397impl TaffyZero for MaxTrackSizingFunction {
398    const ZERO: Self = Self::Fixed(LengthPercentage::ZERO);
399}
400impl FromLength for MaxTrackSizingFunction {
401    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
402        Self::Fixed(LengthPercentage::from_length(value))
403    }
404}
405impl FromPercent for MaxTrackSizingFunction {
406    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
407        Self::Fixed(LengthPercentage::from_percent(percent))
408    }
409}
410impl FromFlex for MaxTrackSizingFunction {
411    fn from_flex<Input: Into<f32> + Copy>(flex: Input) -> Self {
412        Self::Fraction(flex.into())
413    }
414}
415
416impl MaxTrackSizingFunction {
417    /// Returns true if the max track sizing function is `MinContent`, `MaxContent`, `FitContent` or `Auto`, else false.
418    #[inline(always)]
419    pub fn is_intrinsic(&self) -> bool {
420        matches!(self, Self::MinContent | Self::MaxContent | Self::FitContent(_) | Self::Auto)
421    }
422
423    /// Returns true if the max track sizing function is `MaxContent`, `FitContent` or `Auto` else false.
424    /// "In all cases, treat auto and fit-content() as max-content, except where specified otherwise for fit-content()."
425    /// See: <https://www.w3.org/TR/css-grid-1/#algo-terms>
426    #[inline(always)]
427    pub fn is_max_content_alike(&self) -> bool {
428        matches!(self, Self::MaxContent | Self::FitContent(_) | Self::Auto)
429    }
430
431    /// Returns true if the max track sizing function is `Flex`, else false.
432    #[inline(always)]
433    pub fn is_flexible(&self) -> bool {
434        matches!(self, Self::Fraction(_))
435    }
436
437    /// Returns fixed point values directly. Attempts to resolve percentage values against
438    /// the passed available_space and returns if this results in a concrete value (which it
439    /// will if the available_space is `Some`). Otherwise returns None.
440    #[inline(always)]
441    pub fn definite_value(self, parent_size: Option<f32>) -> Option<f32> {
442        use MaxTrackSizingFunction::*;
443        match self {
444            Fixed(LengthPercentage::Length(size)) => Some(size),
445            Fixed(LengthPercentage::Percent(fraction)) => parent_size.map(|size| fraction * size),
446            MinContent | MaxContent | FitContent(_) | Auto | Fraction(_) => None,
447        }
448    }
449
450    /// Resolve the maximum size of the track as defined by either:
451    ///     - A fixed track sizing function
452    ///     - A percentage track sizing function (with definite available space)
453    ///     - A fit-content sizing function with fixed argument
454    ///     - A fit-content sizing function with percentage argument (with definite available space)
455    /// All other kinds of track sizing function return None.
456    #[inline(always)]
457    pub fn definite_limit(self, parent_size: Option<f32>) -> Option<f32> {
458        use MaxTrackSizingFunction::FitContent;
459        match self {
460            FitContent(LengthPercentage::Length(size)) => Some(size),
461            FitContent(LengthPercentage::Percent(fraction)) => parent_size.map(|size| fraction * size),
462            _ => self.definite_value(parent_size),
463        }
464    }
465
466    /// Resolve percentage values against the passed parent_size, returning Some(value)
467    /// Non-percentage values always return None.
468    #[inline(always)]
469    pub fn resolved_percentage_size(self, parent_size: f32) -> Option<f32> {
470        use MaxTrackSizingFunction::*;
471        match self {
472            Fixed(LengthPercentage::Percent(fraction)) => Some(fraction * parent_size),
473            Fixed(LengthPercentage::Length(_)) | MinContent | MaxContent | FitContent(_) | Auto | Fraction(_) => None,
474        }
475    }
476
477    /// Whether the track sizing functions depends on the size of the parent node
478    #[inline(always)]
479    pub fn uses_percentage(self) -> bool {
480        use MaxTrackSizingFunction::*;
481        matches!(self, Fixed(LengthPercentage::Percent(_)) | FitContent(LengthPercentage::Percent(_)))
482    }
483}
484
485/// Minimum track sizing function
486///
487/// Specifies the minimum size of a grid track. A grid track will automatically size between it's minimum and maximum size based
488/// on the size of it's contents, the amount of available space, and the sizing constraint the grid is being size under.
489/// See <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns>
490#[derive(Copy, Clone, PartialEq, Debug)]
491#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
492pub enum MinTrackSizingFunction {
493    /// Track minimum size should be a fixed length or percentage value
494    Fixed(LengthPercentage),
495    /// Track minimum size should be content sized under a min-content constraint
496    MinContent,
497    /// Track minimum size should be content sized under a max-content constraint
498    MaxContent,
499    /// Track minimum size should be automatically sized
500    Auto,
501}
502impl TaffyAuto for MinTrackSizingFunction {
503    const AUTO: Self = Self::Auto;
504}
505impl TaffyMinContent for MinTrackSizingFunction {
506    const MIN_CONTENT: Self = Self::MinContent;
507}
508impl TaffyMaxContent for MinTrackSizingFunction {
509    const MAX_CONTENT: Self = Self::MaxContent;
510}
511impl TaffyZero for MinTrackSizingFunction {
512    const ZERO: Self = Self::Fixed(LengthPercentage::ZERO);
513}
514impl FromLength for MinTrackSizingFunction {
515    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
516        Self::Fixed(LengthPercentage::from_length(value))
517    }
518}
519impl FromPercent for MinTrackSizingFunction {
520    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
521        Self::Fixed(LengthPercentage::from_percent(percent))
522    }
523}
524
525impl MinTrackSizingFunction {
526    /// Returns true if the min track sizing function is `MinContent`, `MaxContent` or `Auto`, else false.
527    #[inline(always)]
528    pub fn is_intrinsic(&self) -> bool {
529        matches!(self, Self::MinContent | Self::MaxContent | Self::Auto)
530    }
531
532    /// Returns fixed point values directly. Attempts to resolve percentage values against
533    /// the passed available_space and returns if this results in a concrete value (which it
534    /// will if the available_space is `Some`). Otherwise returns `None`.
535    #[inline(always)]
536    pub fn definite_value(self, parent_size: Option<f32>) -> Option<f32> {
537        use MinTrackSizingFunction::*;
538        match self {
539            Fixed(LengthPercentage::Length(size)) => Some(size),
540            Fixed(LengthPercentage::Percent(fraction)) => parent_size.map(|size| fraction * size),
541            MinContent | MaxContent | Auto => None,
542        }
543    }
544
545    /// Resolve percentage values against the passed parent_size, returning Some(value)
546    /// Non-percentage values always return None.
547    #[inline(always)]
548    pub fn resolved_percentage_size(self, parent_size: f32) -> Option<f32> {
549        use MinTrackSizingFunction::*;
550        match self {
551            Fixed(LengthPercentage::Percent(fraction)) => Some(fraction * parent_size),
552            Fixed(LengthPercentage::Length(_)) | MinContent | MaxContent | Auto => None,
553        }
554    }
555
556    /// Whether the track sizing functions depends on the size of the parent node
557    #[inline(always)]
558    pub fn uses_percentage(self) -> bool {
559        use MinTrackSizingFunction::*;
560        matches!(self, Fixed(LengthPercentage::Percent(_)))
561    }
562}
563
564/// The sizing function for a grid track (row/column)
565///
566/// May either be a MinMax variant which specifies separate values for the min-/max- track sizing functions
567/// or a scalar value which applies to both track sizing functions.
568pub type NonRepeatedTrackSizingFunction = MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>;
569impl NonRepeatedTrackSizingFunction {
570    /// Extract the min track sizing function
571    pub fn min_sizing_function(&self) -> MinTrackSizingFunction {
572        self.min
573    }
574    /// Extract the max track sizing function
575    pub fn max_sizing_function(&self) -> MaxTrackSizingFunction {
576        self.max
577    }
578    /// Determine whether at least one of the components ("min" and "max") are fixed sizing function
579    pub fn has_fixed_component(&self) -> bool {
580        matches!(self.min, MinTrackSizingFunction::Fixed(_)) || matches!(self.max, MaxTrackSizingFunction::Fixed(_))
581    }
582}
583impl TaffyAuto for NonRepeatedTrackSizingFunction {
584    const AUTO: Self = Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::AUTO };
585}
586impl TaffyMinContent for NonRepeatedTrackSizingFunction {
587    const MIN_CONTENT: Self =
588        Self { min: MinTrackSizingFunction::MIN_CONTENT, max: MaxTrackSizingFunction::MIN_CONTENT };
589}
590impl TaffyMaxContent for NonRepeatedTrackSizingFunction {
591    const MAX_CONTENT: Self =
592        Self { min: MinTrackSizingFunction::MAX_CONTENT, max: MaxTrackSizingFunction::MAX_CONTENT };
593}
594impl TaffyFitContent for NonRepeatedTrackSizingFunction {
595    fn fit_content(argument: LengthPercentage) -> Self {
596        Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::FitContent(argument) }
597    }
598}
599impl TaffyZero for NonRepeatedTrackSizingFunction {
600    const ZERO: Self = Self { min: MinTrackSizingFunction::ZERO, max: MaxTrackSizingFunction::ZERO };
601}
602impl FromLength for NonRepeatedTrackSizingFunction {
603    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
604        Self { min: MinTrackSizingFunction::from_length(value), max: MaxTrackSizingFunction::from_length(value) }
605    }
606}
607impl FromPercent for NonRepeatedTrackSizingFunction {
608    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
609        Self { min: MinTrackSizingFunction::from_percent(percent), max: MaxTrackSizingFunction::from_percent(percent) }
610    }
611}
612impl FromFlex for NonRepeatedTrackSizingFunction {
613    fn from_flex<Input: Into<f32> + Copy>(flex: Input) -> Self {
614        Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::from_flex(flex) }
615    }
616}
617
618/// The first argument to a repeated track definition. This type represents the type of automatic repetition to perform.
619///
620/// See <https://www.w3.org/TR/css-grid-1/#auto-repeat> for an explanation of how auto-repeated track definitions work
621/// and the difference between AutoFit and AutoFill.
622#[derive(Clone, Copy, Debug, PartialEq, Eq)]
623#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
624pub enum GridTrackRepetition {
625    /// Auto-repeating tracks should be generated to fit the container
626    /// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fill>
627    AutoFill,
628    /// Auto-repeating tracks should be generated to fit the container
629    /// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fit>
630    AutoFit,
631    /// The specified tracks should be repeated exacts N times
632    Count(u16),
633}
634impl TryFrom<u16> for GridTrackRepetition {
635    type Error = Infallible;
636    fn try_from(value: u16) -> Result<Self, Infallible> {
637        Ok(Self::Count(value))
638    }
639}
640
641/// Error returned when trying to convert a string to a GridTrackRepetition and that string is not
642/// either "auto-fit" or "auto-fill"
643#[derive(Debug)]
644pub struct InvalidStringRepetitionValue;
645#[cfg(feature = "std")]
646impl std::error::Error for InvalidStringRepetitionValue {}
647impl core::fmt::Display for InvalidStringRepetitionValue {
648    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
649        f.write_str("&str can only be converted to GridTrackRepetition if it's value is 'auto-fit' or 'auto-fill'")
650    }
651}
652impl TryFrom<&str> for GridTrackRepetition {
653    type Error = InvalidStringRepetitionValue;
654    fn try_from(value: &str) -> Result<Self, InvalidStringRepetitionValue> {
655        match value {
656            "auto-fit" => Ok(Self::AutoFit),
657            "auto-fill" => Ok(Self::AutoFill),
658            _ => Err(InvalidStringRepetitionValue),
659        }
660    }
661}
662
663/// The sizing function for a grid track (row/column)
664/// See <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns>
665#[derive(Clone, PartialEq, Debug)]
666#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
667pub enum TrackSizingFunction {
668    /// A single non-repeated track
669    Single(NonRepeatedTrackSizingFunction),
670    /// Automatically generate grid tracks to fit the available space using the specified definite track lengths
671    /// Only valid if every track in template (not just the repetition) has a fixed size.
672    Repeat(GridTrackRepetition, GridTrackVec<NonRepeatedTrackSizingFunction>),
673}
674impl TrackSizingFunction {
675    /// Whether the track definition is a auto-repeated fragment
676    pub fn is_auto_repetition(&self) -> bool {
677        matches!(self, Self::Repeat(GridTrackRepetition::AutoFit | GridTrackRepetition::AutoFill, _))
678    }
679}
680impl TaffyAuto for TrackSizingFunction {
681    const AUTO: Self = Self::Single(NonRepeatedTrackSizingFunction::AUTO);
682}
683impl TaffyMinContent for TrackSizingFunction {
684    const MIN_CONTENT: Self = Self::Single(NonRepeatedTrackSizingFunction::MIN_CONTENT);
685}
686impl TaffyMaxContent for TrackSizingFunction {
687    const MAX_CONTENT: Self = Self::Single(NonRepeatedTrackSizingFunction::MAX_CONTENT);
688}
689impl TaffyFitContent for TrackSizingFunction {
690    fn fit_content(argument: LengthPercentage) -> Self {
691        Self::Single(NonRepeatedTrackSizingFunction::fit_content(argument))
692    }
693}
694impl TaffyZero for TrackSizingFunction {
695    const ZERO: Self = Self::Single(NonRepeatedTrackSizingFunction::ZERO);
696}
697impl FromLength for TrackSizingFunction {
698    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
699        Self::Single(NonRepeatedTrackSizingFunction::from_length(value))
700    }
701}
702impl FromPercent for TrackSizingFunction {
703    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
704        Self::Single(NonRepeatedTrackSizingFunction::from_percent(percent))
705    }
706}
707impl FromFlex for TrackSizingFunction {
708    fn from_flex<Input: Into<f32> + Copy>(flex: Input) -> Self {
709        Self::Single(NonRepeatedTrackSizingFunction::from_flex(flex))
710    }
711}
712impl From<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>> for TrackSizingFunction {
713    fn from(input: MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>) -> Self {
714        Self::Single(input)
715    }
716}