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}