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