gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5364Rust 86Markdown 12TOML 91841 Definitions

⎇ 439a292e9a6bff9c9a9736d37494354f852dbd05 src/style/grid.rs Rust · 713 lines · 30.1 KB History Who touched what raw

Create style traits 439a292e9 · Nico Burns ·

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