CSS Grid: named lines and areas (#840)
1//! Style types for CSS Grid layout
2use super::{
3 AlignContent, AlignItems, AlignSelf, CheapCloneStr, CompactLength, CoreStyle, Dimension, JustifyContent,
4 LengthPercentage, LengthPercentageAuto, Style,
5};
6use crate::compute::grid::{GridCoordinate, GridLine, OriginZeroLine};
7use crate::geometry::{AbsoluteAxis, AbstractAxis, Line, MinMax, Size};
8use crate::style_helpers::*;
9use crate::sys::{DefaultCheapStr, Vec};
10use core::cmp::{max, min};
11use core::fmt::Debug;
12
13/// Defines a grid area
14#[derive(Debug, Clone, PartialEq)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16pub struct GridTemplateArea<CustomIdent: CheapCloneStr> {
17 /// The name of the grid area which
18 #[cfg_attr(feature = "serde", serde(deserialize_with = "crate::util::deserialize_from_str"))]
19 pub name: CustomIdent,
20 /// The index of the row at which the grid area starts in grid coordinates.
21 pub row_start: u16,
22 /// The index of the row at which the grid area ends in grid coordinates.
23 pub row_end: u16,
24 /// The index of the column at which the grid area starts in grid coordinates.
25 pub column_start: u16,
26 /// The index of the column at which the grid area end in grid coordinates.
27 pub column_end: u16,
28}
29
30/// Defines a named grid line
31#[derive(Debug, Clone, PartialEq)]
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33pub struct NamedGridLine<CustomIdent: CheapCloneStr> {
34 /// The name of the grid area which
35 #[cfg_attr(feature = "serde", serde(deserialize_with = "crate::util::deserialize_from_str"))]
36 pub name: CustomIdent,
37 /// The index of the row at which the grid area starts in grid coordinates.
38 pub index: u16,
39}
40
41/// Axis as `Row` or `Column`
42#[derive(Debug, Clone, Copy, PartialEq)]
43pub(crate) enum GridAreaAxis {
44 /// The `Row` axis
45 Row,
46 /// The `Column` axis
47 Column,
48}
49
50/// Logical end (`Start` or `End`)
51#[derive(Debug, Clone, Copy, PartialEq)]
52pub(crate) enum GridAreaEnd {
53 /// The `Start` end
54 Start,
55 /// The `End` end
56 End,
57}
58
59/// A trait to represent a `repeat()` clause in a `grid-template-*` definition
60pub trait GenericRepetition {
61 /// The type that represents `<custom-ident>`s (for named lines)
62 type CustomIdent: CheapCloneStr;
63 /// The type which represents an iterator over the list of repeated tracks
64 type RepetitionTrackList<'a>: Iterator<Item = TrackSizingFunction> + ExactSizeIterator + Clone
65 where
66 Self: 'a;
67
68 /// A nested iterator of line names (nested because each line may have multiple associated names)
69 type TemplateLineNames<'a>: TemplateLineNames<'a, Self::CustomIdent>
70 where
71 Self: 'a;
72 /// The repetition count (integer, auto-fill, or auto-fit)
73 fn count(&self) -> RepetitionCount;
74 /// Get an iterator over the repeated tracks
75 fn tracks(&self) -> Self::RepetitionTrackList<'_>;
76 /// Returns the number of repeated tracks
77 fn track_count(&self) -> u16 {
78 self.tracks().len() as u16
79 }
80 /// Returns an iterator over the lines names
81 fn lines_names(&self) -> Self::TemplateLineNames<'_>;
82}
83
84/// A nested list of line names. This is effectively a generic representation of `Vec<Vec<String>>` that allows
85/// both the collection and string type to be customised.
86#[rustfmt::skip]
87pub trait TemplateLineNames<'a, S: CheapCloneStr> : Iterator<Item = Self::LineNameSet<'a>> + ExactSizeIterator + Clone where Self: 'a {
88 /// A simple list line names. This is effectively a generic representation of `VecString>` that allows
89 /// both the collection and string type to be customised.
90 type LineNameSet<'b>: Iterator<Item = &'b S> + ExactSizeIterator + Clone where Self: 'b;
91}
92
93impl<'a, S: CheapCloneStr> TemplateLineNames<'a, S>
94 for core::iter::Map<core::slice::Iter<'a, Vec<S>>, fn(&Vec<S>) -> core::slice::Iter<'_, S>>
95{
96 type LineNameSet<'b>
97 = core::slice::Iter<'b, S>
98 where
99 Self: 'b;
100}
101
102#[derive(Copy, Clone)]
103/// A type representing a component in a `grid-template-*` defintion where the type
104/// representing `repeat()`s is generic
105pub enum GenericGridTemplateComponent<S, Repetition>
106where
107 S: CheapCloneStr,
108 Repetition: GenericRepetition<CustomIdent = S>,
109{
110 /// A single track sizing function
111 Single(TrackSizingFunction),
112 /// A `repeat()`
113 Repeat(Repetition),
114}
115
116impl<S, Repetition> GenericGridTemplateComponent<S, Repetition>
117where
118 S: CheapCloneStr,
119 Repetition: GenericRepetition<CustomIdent = S>,
120{
121 /// Whether the track definition is a auto-repeated fragment
122 pub fn is_auto_repetition(&self) -> bool {
123 match self {
124 Self::Single(_) => false,
125 Self::Repeat(repeat) => matches!(repeat.count(), RepetitionCount::AutoFit | RepetitionCount::AutoFill),
126 }
127 }
128}
129
130/// The set of styles required for a CSS Grid container
131pub trait GridContainerStyle: CoreStyle {
132 /// The type for a `repeat()` within a grid_template_rows or grid_template_columns
133 type Repetition<'a>: GenericRepetition<CustomIdent = Self::CustomIdent>
134 where
135 Self: 'a;
136
137 /// The type returned by grid_template_rows and grid_template_columns
138 type TemplateTrackList<'a>: Iterator<Item = GenericGridTemplateComponent<Self::CustomIdent, Self::Repetition<'a>>>
139 + ExactSizeIterator
140 + Clone
141 where
142 Self: 'a;
143
144 /// The type returned by grid_auto_rows and grid_auto_columns
145 type AutoTrackList<'a>: Iterator<Item = TrackSizingFunction> + ExactSizeIterator + Clone
146 where
147 Self: 'a;
148
149 /// The type returned by grid_template_row_names and grid_template_column_names
150 //IntoIterator<Item = &'a Self::LineNameSet<'a>>
151 type TemplateLineNames<'a>: TemplateLineNames<'a, Self::CustomIdent>
152 where
153 Self: 'a;
154
155 /// The type of custom identifiers used to identify named grid lines and areas
156 type GridTemplateAreas<'a>: IntoIterator<Item = GridTemplateArea<Self::CustomIdent>>
157 where
158 Self: 'a;
159
160 // FIXME: re-add default implemenations for grid_{template,auto}_{rows,columns} once the
161 // associated_type_defaults feature (https://github.com/rust-lang/rust/issues/29661) is stabilised.
162
163 /// Defines the track sizing functions (heights) of the grid rows
164 fn grid_template_rows(&self) -> Option<Self::TemplateTrackList<'_>>;
165 /// Defines the track sizing functions (widths) of the grid columns
166 fn grid_template_columns(&self) -> Option<Self::TemplateTrackList<'_>>;
167 /// Defines the size of implicitly created rows
168 fn grid_auto_rows(&self) -> Self::AutoTrackList<'_>;
169 /// Defined the size of implicitly created columns
170 fn grid_auto_columns(&self) -> Self::AutoTrackList<'_>;
171
172 /// Named grid areas
173 fn grid_template_areas(&self) -> Option<Self::GridTemplateAreas<'_>>;
174 /// Defines the line names for row lines
175 fn grid_template_column_names(&self) -> Option<Self::TemplateLineNames<'_>>;
176 /// Defines the size of implicitly created rows
177 fn grid_template_row_names(&self) -> Option<Self::TemplateLineNames<'_>>;
178
179 /// Controls how items get placed into the grid for auto-placed items
180 #[inline(always)]
181 fn grid_auto_flow(&self) -> GridAutoFlow {
182 Style::<Self::CustomIdent>::DEFAULT.grid_auto_flow
183 }
184
185 /// How large should the gaps between items in a grid or flex container be?
186 #[inline(always)]
187 fn gap(&self) -> Size<LengthPercentage> {
188 Style::<Self::CustomIdent>::DEFAULT.gap
189 }
190
191 // Alignment properties
192
193 /// How should content contained within this item be aligned in the cross/block axis
194 #[inline(always)]
195 fn align_content(&self) -> Option<AlignContent> {
196 Style::<Self::CustomIdent>::DEFAULT.align_content
197 }
198 /// How should contained within this item be aligned in the main/inline axis
199 #[inline(always)]
200 fn justify_content(&self) -> Option<JustifyContent> {
201 Style::<Self::CustomIdent>::DEFAULT.justify_content
202 }
203 /// How this node's children aligned in the cross/block axis?
204 #[inline(always)]
205 fn align_items(&self) -> Option<AlignItems> {
206 Style::<Self::CustomIdent>::DEFAULT.align_items
207 }
208 /// How this node's children should be aligned in the inline axis
209 #[inline(always)]
210 fn justify_items(&self) -> Option<AlignItems> {
211 Style::<Self::CustomIdent>::DEFAULT.justify_items
212 }
213
214 /// Get a grid item's row or column placement depending on the axis passed
215 #[inline(always)]
216 fn grid_template_tracks(&self, axis: AbsoluteAxis) -> Option<Self::TemplateTrackList<'_>> {
217 match axis {
218 AbsoluteAxis::Horizontal => self.grid_template_columns(),
219 AbsoluteAxis::Vertical => self.grid_template_rows(),
220 }
221 }
222
223 /// Get a grid container's align-content or justify-content alignment depending on the axis passed
224 #[inline(always)]
225 fn grid_align_content(&self, axis: AbstractAxis) -> AlignContent {
226 match axis {
227 AbstractAxis::Inline => self.justify_content().unwrap_or(AlignContent::Stretch),
228 AbstractAxis::Block => self.align_content().unwrap_or(AlignContent::Stretch),
229 }
230 }
231}
232
233/// The set of styles required for a CSS Grid item (child of a CSS Grid container)
234pub trait GridItemStyle: CoreStyle {
235 /// Defines which row in the grid the item should start and end at
236 #[inline(always)]
237 fn grid_row(&self) -> Line<GridPlacement<Self::CustomIdent>> {
238 Default::default()
239 }
240 /// Defines which column in the grid the item should start and end at
241 #[inline(always)]
242 fn grid_column(&self) -> Line<GridPlacement<Self::CustomIdent>> {
243 Default::default()
244 }
245
246 /// How this node should be aligned in the cross/block axis
247 /// Falls back to the parents [`AlignItems`] if not set
248 #[inline(always)]
249 fn align_self(&self) -> Option<AlignSelf> {
250 Style::<Self::CustomIdent>::DEFAULT.align_self
251 }
252 /// How this node should be aligned in the inline axis
253 /// Falls back to the parents [`super::JustifyItems`] if not set
254 #[inline(always)]
255 fn justify_self(&self) -> Option<AlignSelf> {
256 Style::<Self::CustomIdent>::DEFAULT.justify_self
257 }
258
259 /// Get a grid item's row or column placement depending on the axis passed
260 #[inline(always)]
261 fn grid_placement(&self, axis: AbsoluteAxis) -> Line<GridPlacement<Self::CustomIdent>> {
262 match axis {
263 AbsoluteAxis::Horizontal => self.grid_column(),
264 AbsoluteAxis::Vertical => self.grid_row(),
265 }
266 }
267}
268
269/// Controls whether grid items are placed row-wise or column-wise. And whether the sparse or dense packing algorithm is used.
270///
271/// 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.
272///
273/// Defaults to [`GridAutoFlow::Row`]
274///
275/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow)
276#[derive(Copy, Clone, PartialEq, Eq, Debug)]
277#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
278pub enum GridAutoFlow {
279 /// Items are placed by filling each row in turn, adding new rows as necessary
280 Row,
281 /// Items are placed by filling each column in turn, adding new columns as necessary.
282 Column,
283 /// Combines `Row` with the dense packing algorithm.
284 RowDense,
285 /// Combines `Column` with the dense packing algorithm.
286 ColumnDense,
287}
288
289impl Default for GridAutoFlow {
290 fn default() -> Self {
291 Self::Row
292 }
293}
294
295impl GridAutoFlow {
296 /// Whether grid auto placement uses the sparse placement algorithm or the dense placement algorithm
297 /// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow#values>
298 pub fn is_dense(&self) -> bool {
299 match self {
300 Self::Row | Self::Column => false,
301 Self::RowDense | Self::ColumnDense => true,
302 }
303 }
304
305 /// Whether grid auto placement fills areas row-wise or column-wise
306 /// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow#values>
307 pub fn primary_axis(&self) -> AbsoluteAxis {
308 match self {
309 Self::Row | Self::RowDense => AbsoluteAxis::Horizontal,
310 Self::Column | Self::ColumnDense => AbsoluteAxis::Vertical,
311 }
312 }
313}
314
315/// A grid line placement specification which is generic over the coordinate system that it uses to define
316/// grid line positions.
317///
318/// GenericGridPlacement<GridLine> is aliased as GridPlacement and is exposed to users of Taffy to define styles.
319/// GenericGridPlacement<OriginZeroLine> is aliased as OriginZeroGridPlacement and is used internally for placement computations.
320///
321/// See [`crate::compute::grid::type::coordinates`] for documentation on the different coordinate systems.
322#[derive(Copy, Clone, PartialEq, Eq, Debug)]
323#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
324pub enum GenericGridPlacement<LineType: GridCoordinate> {
325 /// Place item according to the auto-placement algorithm, and the parent's grid_auto_flow property
326 Auto,
327 /// Place item at specified line (column or row) index
328 Line(LineType),
329 /// Item should span specified number of tracks (columns or rows)
330 Span(u16),
331}
332
333/// A grid line placement using the normalized OriginZero coordinates to specify line positions.
334pub(crate) type OriginZeroGridPlacement = GenericGridPlacement<OriginZeroLine>;
335
336/// A grid line placement using CSS grid line coordinates to specify line positions. This uses the same coordinate
337/// system as the public `GridPlacement` type but doesn't support named lines (these are expected to have already
338/// been resolved by the time values of this type are constructed).
339pub(crate) type NonNamedGridPlacement = GenericGridPlacement<GridLine>;
340
341/// A grid line placement specification. Used for grid-[row/column]-[start/end]. Named tracks are not implemented.
342///
343/// Defaults to `GridPlacement::Auto`
344///
345/// [Specification](https://www.w3.org/TR/css3-grid-layout/#typedef-grid-row-start-grid-line)
346#[derive(Clone, PartialEq, Debug)]
347#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
348pub enum GridPlacement<S: CheapCloneStr = DefaultCheapStr> {
349 /// Place item according to the auto-placement algorithm, and the parent's grid_auto_flow property
350 Auto,
351 /// Place item at specified line (column or row) index
352 Line(GridLine),
353 /// Place item at specified named line (column or row)
354 NamedLine(S, i16),
355 /// Item should span specified number of tracks (columns or rows)
356 Span(u16),
357 /// Item should span until the nth line named <name>.
358 ///
359 /// If there are less than n lines named <name> in the specified direction then
360 /// all implicit lines will be counted.
361 NamedSpan(S, u16),
362}
363impl<S: CheapCloneStr> TaffyAuto for GridPlacement<S> {
364 const AUTO: Self = Self::Auto;
365}
366impl<S: CheapCloneStr> TaffyGridLine for GridPlacement<S> {
367 fn from_line_index(index: i16) -> Self {
368 GridPlacement::<S>::Line(GridLine::from(index))
369 }
370}
371impl<S: CheapCloneStr> TaffyGridLine for Line<GridPlacement<S>> {
372 fn from_line_index(index: i16) -> Self {
373 Line { start: GridPlacement::<S>::from_line_index(index), end: GridPlacement::<S>::Auto }
374 }
375}
376impl<S: CheapCloneStr> TaffyGridSpan for GridPlacement<S> {
377 fn from_span(span: u16) -> Self {
378 GridPlacement::<S>::Span(span)
379 }
380}
381impl<S: CheapCloneStr> TaffyGridSpan for Line<GridPlacement<S>> {
382 fn from_span(span: u16) -> Self {
383 Line { start: GridPlacement::<S>::from_span(span), end: GridPlacement::<S>::Auto }
384 }
385}
386
387impl<S: CheapCloneStr> Default for GridPlacement<S> {
388 fn default() -> Self {
389 Self::Auto
390 }
391}
392
393impl<S: CheapCloneStr> GridPlacement<S> {
394 /// Apply a mapping function if the [`GridPlacement`] is a `Line`. Otherwise return `self` unmodified.
395 pub fn into_origin_zero_placement_ignoring_named(&self, explicit_track_count: u16) -> OriginZeroGridPlacement {
396 match self {
397 Self::Auto => OriginZeroGridPlacement::Auto,
398 Self::Span(span) => OriginZeroGridPlacement::Span(*span),
399 // Grid line zero is an invalid index, so it gets treated as Auto
400 // See: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-row-start#values
401 Self::Line(line) => match line.as_i16() {
402 0 => OriginZeroGridPlacement::Auto,
403 _ => OriginZeroGridPlacement::Line(line.into_origin_zero_line(explicit_track_count)),
404 },
405 Self::NamedLine(_, _) => OriginZeroGridPlacement::Auto,
406 Self::NamedSpan(_, _) => OriginZeroGridPlacement::Auto,
407 }
408 }
409}
410
411impl<S: CheapCloneStr> Line<GridPlacement<S>> {
412 /// Apply a mapping function if the [`GridPlacement`] is a `Line`. Otherwise return `self` unmodified.
413 pub fn into_origin_zero_ignoring_named(&self, explicit_track_count: u16) -> Line<OriginZeroGridPlacement> {
414 Line {
415 start: self.start.into_origin_zero_placement_ignoring_named(explicit_track_count),
416 end: self.end.into_origin_zero_placement_ignoring_named(explicit_track_count),
417 }
418 }
419}
420
421impl NonNamedGridPlacement {
422 /// Apply a mapping function if the [`GridPlacement`] is a `Track`. Otherwise return `self` unmodified.
423 pub fn into_origin_zero_placement(
424 &self,
425 explicit_track_count: u16,
426 // resolve_named: impl Fn(&str) -> Option<GridLine>
427 ) -> OriginZeroGridPlacement {
428 match self {
429 Self::Auto => OriginZeroGridPlacement::Auto,
430 Self::Span(span) => OriginZeroGridPlacement::Span(*span),
431 // Grid line zero is an invalid index, so it gets treated as Auto
432 // See: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-row-start#values
433 Self::Line(line) => match line.as_i16() {
434 0 => OriginZeroGridPlacement::Auto,
435 _ => OriginZeroGridPlacement::Line(line.into_origin_zero_line(explicit_track_count)),
436 },
437 }
438 }
439}
440
441impl<T: GridCoordinate> Line<GenericGridPlacement<T>> {
442 /// Resolves the span for an indefinite placement (a placement that does not consist of two `Track`s).
443 /// Panics if called on a definite placement
444 pub fn indefinite_span(&self) -> u16 {
445 use GenericGridPlacement as GP;
446 match (self.start, self.end) {
447 (GP::Line(_), GP::Auto) => 1,
448 (GP::Auto, GP::Line(_)) => 1,
449 (GP::Auto, GP::Auto) => 1,
450 (GP::Line(_), GP::Span(span)) => span,
451 (GP::Span(span), GP::Line(_)) => span,
452 (GP::Span(span), GP::Auto) => span,
453 (GP::Auto, GP::Span(span)) => span,
454 (GP::Span(span), GP::Span(_)) => span,
455 (GP::Line(_), GP::Line(_)) => panic!("indefinite_span should only be called on indefinite grid tracks"),
456 }
457 }
458}
459
460impl<S: CheapCloneStr> Line<GridPlacement<S>> {
461 #[inline]
462 /// Whether the track position is definite in this axis (or the item will need auto placement)
463 /// The track position is definite if least one of the start and end positions is a NON-ZERO track index
464 /// (0 is an invalid line in GridLine coordinates, and falls back to "auto" which is indefinite)
465 pub fn is_definite(&self) -> bool {
466 match (&self.start, &self.end) {
467 (GridPlacement::Line(line), _) if line.as_i16() != 0 => true,
468 (_, GridPlacement::Line(line)) if line.as_i16() != 0 => true,
469 (GridPlacement::NamedLine(_, _), _) => true,
470 (_, GridPlacement::NamedLine(_, _)) => true,
471 _ => false,
472 }
473 }
474}
475
476impl Line<NonNamedGridPlacement> {
477 #[inline]
478 /// Whether the track position is definite in this axis (or the item will need auto placement)
479 /// The track position is definite if least one of the start and end positions is a NON-ZERO track index
480 /// (0 is an invalid line in GridLine coordinates, and falls back to "auto" which is indefinite)
481 pub fn is_definite(&self) -> bool {
482 match (&self.start, &self.end) {
483 (GenericGridPlacement::Line(line), _) if line.as_i16() != 0 => true,
484 (_, GenericGridPlacement::Line(line)) if line.as_i16() != 0 => true,
485 _ => false,
486 }
487 }
488
489 /// Apply a mapping function if the [`GridPlacement`] is a `Track`. Otherwise return `self` unmodified.
490 pub fn into_origin_zero(&self, explicit_track_count: u16) -> Line<OriginZeroGridPlacement> {
491 Line {
492 start: self.start.into_origin_zero_placement(explicit_track_count),
493 end: self.end.into_origin_zero_placement(explicit_track_count),
494 }
495 }
496}
497
498impl Line<OriginZeroGridPlacement> {
499 #[inline]
500 /// Whether the track position is definite in this axis (or the item will need auto placement)
501 /// The track position is definite if least one of the start and end positions is a track index
502 pub fn is_definite(&self) -> bool {
503 matches!((self.start, self.end), (GenericGridPlacement::Line(_), _) | (_, GenericGridPlacement::Line(_)))
504 }
505
506 /// If at least one of the of the start and end positions is a track index then the other end can be resolved
507 /// into a track index purely based on the information contained with the placement specification
508 pub fn resolve_definite_grid_lines(&self) -> Line<OriginZeroLine> {
509 use OriginZeroGridPlacement as GP;
510 match (self.start, self.end) {
511 (GP::Line(line1), GP::Line(line2)) => {
512 if line1 == line2 {
513 Line { start: line1, end: line1 + 1 }
514 } else {
515 Line { start: min(line1, line2), end: max(line1, line2) }
516 }
517 }
518 (GP::Line(line), GP::Span(span)) => Line { start: line, end: line + span },
519 (GP::Line(line), GP::Auto) => Line { start: line, end: line + 1 },
520 (GP::Span(span), GP::Line(line)) => Line { start: line - span, end: line },
521 (GP::Auto, GP::Line(line)) => Line { start: line - 1, end: line },
522 _ => panic!("resolve_definite_grid_tracks should only be called on definite grid tracks"),
523 }
524 }
525
526 /// For absolutely positioned items:
527 /// - Tracks resolve to definite tracks
528 /// - For Spans:
529 /// - If the other position is a Track, they resolve to a definite track relative to the other track
530 /// - Else resolve to None
531 /// - Auto resolves to None
532 ///
533 /// When finally positioning the item, a value of None means that the item's grid area is bounded by the grid
534 /// container's border box on that side.
535 pub fn resolve_absolutely_positioned_grid_tracks(&self) -> Line<Option<OriginZeroLine>> {
536 use OriginZeroGridPlacement as GP;
537 match (self.start, self.end) {
538 (GP::Line(track1), GP::Line(track2)) => {
539 if track1 == track2 {
540 Line { start: Some(track1), end: Some(track1 + 1) }
541 } else {
542 Line { start: Some(min(track1, track2)), end: Some(max(track1, track2)) }
543 }
544 }
545 (GP::Line(track), GP::Span(span)) => Line { start: Some(track), end: Some(track + span) },
546 (GP::Line(track), GP::Auto) => Line { start: Some(track), end: None },
547 (GP::Span(span), GP::Line(track)) => Line { start: Some(track - span), end: Some(track) },
548 (GP::Auto, GP::Line(track)) => Line { start: None, end: Some(track) },
549 _ => Line { start: None, end: None },
550 }
551 }
552
553 /// If neither of the start and end positions is a track index then the other end can be resolved
554 /// into a track index if a definite start position is supplied externally
555 pub fn resolve_indefinite_grid_tracks(&self, start: OriginZeroLine) -> Line<OriginZeroLine> {
556 use OriginZeroGridPlacement as GP;
557 match (self.start, self.end) {
558 (GP::Auto, GP::Auto) => Line { start, end: start + 1 },
559 (GP::Span(span), GP::Auto) => Line { start, end: start + span },
560 (GP::Auto, GP::Span(span)) => Line { start, end: start + span },
561 (GP::Span(span), GP::Span(_)) => Line { start, end: start + span },
562 _ => panic!("resolve_indefinite_grid_tracks should only be called on indefinite grid tracks"),
563 }
564 }
565}
566
567/// Represents the start and end points of a GridItem within a given axis
568impl<S: CheapCloneStr> Default for Line<GridPlacement<S>> {
569 fn default() -> Self {
570 Line { start: GridPlacement::<S>::Auto, end: GridPlacement::<S>::Auto }
571 }
572}
573
574/// Maximum track sizing function
575///
576/// Specifies the maximum size of a grid track. A grid track will automatically size between it's minimum and maximum size based
577/// on the size of it's contents, the amount of available space, and the sizing constraint the grid is being size under.
578/// See <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns>
579#[derive(Copy, Clone, PartialEq, Debug)]
580#[cfg_attr(feature = "serde", derive(Serialize))]
581pub struct MaxTrackSizingFunction(pub(crate) CompactLength);
582impl TaffyZero for MaxTrackSizingFunction {
583 const ZERO: Self = Self(CompactLength::ZERO);
584}
585impl TaffyAuto for MaxTrackSizingFunction {
586 const AUTO: Self = Self(CompactLength::AUTO);
587}
588impl TaffyMinContent for MaxTrackSizingFunction {
589 const MIN_CONTENT: Self = Self(CompactLength::MIN_CONTENT);
590}
591impl TaffyMaxContent for MaxTrackSizingFunction {
592 const MAX_CONTENT: Self = Self(CompactLength::MAX_CONTENT);
593}
594impl FromLength for MaxTrackSizingFunction {
595 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
596 Self::length(value.into())
597 }
598}
599impl FromPercent for MaxTrackSizingFunction {
600 fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
601 Self::percent(value.into())
602 }
603}
604impl TaffyFitContent for MaxTrackSizingFunction {
605 fn fit_content(argument: LengthPercentage) -> Self {
606 Self(CompactLength::fit_content(argument))
607 }
608}
609impl FromFr for MaxTrackSizingFunction {
610 fn from_fr<Input: Into<f32> + Copy>(value: Input) -> Self {
611 Self::fr(value.into())
612 }
613}
614impl From<LengthPercentage> for MaxTrackSizingFunction {
615 fn from(input: LengthPercentage) -> Self {
616 Self(input.0)
617 }
618}
619impl From<LengthPercentageAuto> for MaxTrackSizingFunction {
620 fn from(input: LengthPercentageAuto) -> Self {
621 Self(input.0)
622 }
623}
624impl From<Dimension> for MaxTrackSizingFunction {
625 fn from(input: Dimension) -> Self {
626 Self(input.0)
627 }
628}
629impl From<MinTrackSizingFunction> for MaxTrackSizingFunction {
630 fn from(input: MinTrackSizingFunction) -> Self {
631 Self(input.0)
632 }
633}
634#[cfg(feature = "serde")]
635impl<'de> serde::Deserialize<'de> for MaxTrackSizingFunction {
636 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
637 where
638 D: serde::Deserializer<'de>,
639 {
640 let inner = CompactLength::deserialize(deserializer)?;
641 // Note: validation intentionally excludes the CALC_TAG as deserializing calc() values is not supported
642 if matches!(
643 inner.tag(),
644 CompactLength::LENGTH_TAG
645 | CompactLength::PERCENT_TAG
646 | CompactLength::AUTO_TAG
647 | CompactLength::MIN_CONTENT_TAG
648 | CompactLength::MAX_CONTENT_TAG
649 | CompactLength::FIT_CONTENT_PX_TAG
650 | CompactLength::FIT_CONTENT_PERCENT_TAG
651 | CompactLength::FR_TAG
652 ) {
653 Ok(Self(inner))
654 } else {
655 Err(serde::de::Error::custom("Invalid tag"))
656 }
657 }
658}
659
660impl MaxTrackSizingFunction {
661 /// An absolute length in some abstract units. Users of Taffy may define what they correspond
662 /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
663 #[inline(always)]
664 pub const fn length(val: f32) -> Self {
665 Self(CompactLength::length(val))
666 }
667
668 /// A percentage length relative to the size of the containing block.
669 ///
670 /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]**
671 #[inline(always)]
672 pub const fn percent(val: f32) -> Self {
673 Self(CompactLength::percent(val))
674 }
675
676 /// The dimension should be automatically computed according to algorithm-specific rules
677 /// regarding the default size of boxes.
678 #[inline(always)]
679 pub const fn auto() -> Self {
680 Self(CompactLength::auto())
681 }
682
683 /// The size should be the "min-content" size.
684 /// This is the smallest size that can fit the item's contents with ALL soft line-wrapping opportunities taken
685 #[inline(always)]
686 pub const fn min_content() -> Self {
687 Self(CompactLength::min_content())
688 }
689
690 /// The size should be the "max-content" size.
691 /// This is the smallest size that can fit the item's contents with NO soft line-wrapping opportunities taken
692 #[inline(always)]
693 pub const fn max_content() -> Self {
694 Self(CompactLength::max_content())
695 }
696
697 /// The size should be computed according to the "fit content" formula:
698 /// `max(min_content, min(max_content, limit))`
699 /// where:
700 /// - `min_content` is the [min-content](Self::min_content) size
701 /// - `max_content` is the [max-content](Self::max_content) size
702 /// - `limit` is a LENGTH value passed to this function
703 ///
704 /// The effect of this is that the item takes the size of `limit` clamped
705 /// by the min-content and max-content sizes.
706 #[inline(always)]
707 pub const fn fit_content_px(limit: f32) -> Self {
708 Self(CompactLength::fit_content_px(limit))
709 }
710
711 /// The size should be computed according to the "fit content" formula:
712 /// `max(min_content, min(max_content, limit))`
713 /// where:
714 /// - `min_content` is the [min-content](Self::min_content) size
715 /// - `max_content` is the [max-content](Self::max_content) size
716 /// - `limit` is a PERCENTAGE value passed to this function
717 ///
718 /// The effect of this is that the item takes the size of `limit` clamped
719 /// by the min-content and max-content sizes.
720 #[inline(always)]
721 pub const fn fit_content_percent(limit: f32) -> Self {
722 Self(CompactLength::fit_content_percent(limit))
723 }
724
725 /// The dimension as a fraction of the total available grid space (`fr` units in CSS)
726 /// Specified value is the numerator of the fraction. Denominator is the sum of all fraction specified in that grid dimension
727 /// Spec: <https://www.w3.org/TR/css3-grid-layout/#fr-unit>
728 #[inline(always)]
729 pub const fn fr(val: f32) -> Self {
730 Self(CompactLength::fr(val))
731 }
732
733 /// A `calc()` value. The value passed here is treated as an opaque handle to
734 /// the actual calc representation and may be a pointer, index, etc.
735 ///
736 /// The low 3 bits are used as a tag value and will be returned as 0.
737 #[inline]
738 #[cfg(feature = "calc")]
739 pub fn calc(ptr: *const ()) -> Self {
740 Self(CompactLength::calc(ptr))
741 }
742
743 /// Create a LengthPercentageAuto from a raw `CompactLength`.
744 /// # Safety
745 /// CompactLength must represent a valid variant for LengthPercentageAuto
746 #[allow(unsafe_code)]
747 pub unsafe fn from_raw(val: CompactLength) -> Self {
748 Self(val)
749 }
750
751 /// Get the underlying `CompactLength` representation of the value
752 pub fn into_raw(self) -> CompactLength {
753 self.0
754 }
755
756 /// Returns true if the max track sizing function is `MinContent`, `MaxContent`, `FitContent` or `Auto`, else false.
757 #[inline(always)]
758 pub fn is_intrinsic(&self) -> bool {
759 self.0.is_intrinsic()
760 }
761
762 /// Returns true if the max track sizing function is `MaxContent`, `FitContent` or `Auto` else false.
763 /// "In all cases, treat auto and fit-content() as max-content, except where specified otherwise for fit-content()."
764 /// See: <https://www.w3.org/TR/css-grid-1/#algo-terms>
765 #[inline(always)]
766 pub fn is_max_content_alike(&self) -> bool {
767 self.0.is_max_content_alike()
768 }
769
770 /// Returns true if the an Fr value, else false.
771 #[inline(always)]
772 pub fn is_fr(&self) -> bool {
773 self.0.is_fr()
774 }
775
776 /// Returns true if the is `Auto`, else false.
777 #[inline(always)]
778 pub fn is_auto(&self) -> bool {
779 self.0.is_auto()
780 }
781
782 /// Returns true if value is MinContent
783 #[inline(always)]
784 pub fn is_min_content(&self) -> bool {
785 self.0.is_min_content()
786 }
787
788 /// Returns true if value is MaxContent
789 #[inline(always)]
790 pub fn is_max_content(&self) -> bool {
791 self.0.is_max_content()
792 }
793
794 /// Returns true if value is FitContent(...)
795 #[inline(always)]
796 pub fn is_fit_content(&self) -> bool {
797 self.0.is_fit_content()
798 }
799
800 /// Returns true if value is MaxContent or FitContent(...)
801 #[inline(always)]
802 pub fn is_max_or_fit_content(&self) -> bool {
803 self.0.is_max_or_fit_content()
804 }
805
806 /// Returns whether the value can be resolved using `Self::definite_value`
807 #[inline(always)]
808 pub fn has_definite_value(self, parent_size: Option<f32>) -> bool {
809 match self.0.tag() {
810 CompactLength::LENGTH_TAG => true,
811 CompactLength::PERCENT_TAG => parent_size.is_some(),
812 #[cfg(feature = "calc")]
813 _ if self.0.is_calc() => parent_size.is_some(),
814 _ => false,
815 }
816 }
817
818 /// Returns fixed point values directly. Attempts to resolve percentage values against
819 /// the passed available_space and returns if this results in a concrete value (which it
820 /// will if the available_space is `Some`). Otherwise returns None.
821 #[inline(always)]
822 pub fn definite_value(
823 self,
824 parent_size: Option<f32>,
825 calc_resolver: impl Fn(*const (), f32) -> f32,
826 ) -> Option<f32> {
827 match self.0.tag() {
828 CompactLength::LENGTH_TAG => Some(self.0.value()),
829 CompactLength::PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
830 #[cfg(feature = "calc")]
831 _ if self.0.is_calc() => parent_size.map(|size| calc_resolver(self.0.calc_value(), size)),
832 _ => None,
833 }
834 }
835
836 /// Resolve the maximum size of the track as defined by either:
837 /// - A fixed track sizing function
838 /// - A percentage track sizing function (with definite available space)
839 /// - A fit-content sizing function with fixed argument
840 /// - A fit-content sizing function with percentage argument (with definite available space)
841 /// All other kinds of track sizing function return None.
842 #[inline(always)]
843 pub fn definite_limit(
844 self,
845 parent_size: Option<f32>,
846 calc_resolver: impl Fn(*const (), f32) -> f32,
847 ) -> Option<f32> {
848 match self.0.tag() {
849 CompactLength::FIT_CONTENT_PX_TAG => Some(self.0.value()),
850 CompactLength::FIT_CONTENT_PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
851 _ => self.definite_value(parent_size, calc_resolver),
852 }
853 }
854
855 /// Resolve percentage values against the passed parent_size, returning Some(value)
856 /// Non-percentage values always return None.
857 #[inline(always)]
858 pub fn resolved_percentage_size(
859 self,
860 parent_size: f32,
861 calc_resolver: impl Fn(*const (), f32) -> f32,
862 ) -> Option<f32> {
863 self.0.resolved_percentage_size(parent_size, calc_resolver)
864 }
865
866 /// Whether the track sizing functions depends on the size of the parent node
867 #[inline(always)]
868 pub fn uses_percentage(self) -> bool {
869 self.0.uses_percentage()
870 }
871}
872
873/// Minimum track sizing function
874///
875/// Specifies the minimum size of a grid track. A grid track will automatically size between it's minimum and maximum size based
876/// on the size of it's contents, the amount of available space, and the sizing constraint the grid is being size under.
877/// See <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns>
878#[derive(Copy, Clone, PartialEq, Debug)]
879#[cfg_attr(feature = "serde", derive(Serialize))]
880pub struct MinTrackSizingFunction(pub(crate) CompactLength);
881impl TaffyZero for MinTrackSizingFunction {
882 const ZERO: Self = Self(CompactLength::ZERO);
883}
884impl TaffyAuto for MinTrackSizingFunction {
885 const AUTO: Self = Self(CompactLength::AUTO);
886}
887impl TaffyMinContent for MinTrackSizingFunction {
888 const MIN_CONTENT: Self = Self(CompactLength::MIN_CONTENT);
889}
890impl TaffyMaxContent for MinTrackSizingFunction {
891 const MAX_CONTENT: Self = Self(CompactLength::MAX_CONTENT);
892}
893impl FromLength for MinTrackSizingFunction {
894 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
895 Self::length(value.into())
896 }
897}
898impl FromPercent for MinTrackSizingFunction {
899 fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
900 Self::percent(value.into())
901 }
902}
903impl From<LengthPercentage> for MinTrackSizingFunction {
904 fn from(input: LengthPercentage) -> Self {
905 Self(input.0)
906 }
907}
908impl From<LengthPercentageAuto> for MinTrackSizingFunction {
909 fn from(input: LengthPercentageAuto) -> Self {
910 Self(input.0)
911 }
912}
913impl From<Dimension> for MinTrackSizingFunction {
914 fn from(input: Dimension) -> Self {
915 Self(input.0)
916 }
917}
918#[cfg(feature = "serde")]
919impl<'de> serde::Deserialize<'de> for MinTrackSizingFunction {
920 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
921 where
922 D: serde::Deserializer<'de>,
923 {
924 let inner = CompactLength::deserialize(deserializer)?;
925 // Note: validation intentionally excludes the CALC_TAG as deserializing calc() values is not supported
926 if matches!(
927 inner.tag(),
928 CompactLength::LENGTH_TAG
929 | CompactLength::PERCENT_TAG
930 | CompactLength::AUTO_TAG
931 | CompactLength::MIN_CONTENT_TAG
932 | CompactLength::MAX_CONTENT_TAG
933 | CompactLength::FIT_CONTENT_PX_TAG
934 | CompactLength::FIT_CONTENT_PERCENT_TAG
935 ) {
936 Ok(Self(inner))
937 } else {
938 Err(serde::de::Error::custom("Invalid tag"))
939 }
940 }
941}
942
943impl MinTrackSizingFunction {
944 /// An absolute length in some abstract units. Users of Taffy may define what they correspond
945 /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
946 #[inline(always)]
947 pub const fn length(val: f32) -> Self {
948 Self(CompactLength::length(val))
949 }
950
951 /// A percentage length relative to the size of the containing block.
952 ///
953 /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]**
954 #[inline(always)]
955 pub const fn percent(val: f32) -> Self {
956 Self(CompactLength::percent(val))
957 }
958
959 /// The dimension should be automatically computed according to algorithm-specific rules
960 /// regarding the default size of boxes.
961 #[inline(always)]
962 pub const fn auto() -> Self {
963 Self(CompactLength::auto())
964 }
965
966 /// The size should be the "min-content" size.
967 /// This is the smallest size that can fit the item's contents with ALL soft line-wrapping opportunities taken
968 #[inline(always)]
969 pub const fn min_content() -> Self {
970 Self(CompactLength::min_content())
971 }
972
973 /// The size should be the "max-content" size.
974 /// This is the smallest size that can fit the item's contents with NO soft line-wrapping opportunities taken
975 #[inline(always)]
976 pub const fn max_content() -> Self {
977 Self(CompactLength::max_content())
978 }
979
980 /// A `calc()` value. The value passed here is treated as an opaque handle to
981 /// the actual calc representation and may be a pointer, index, etc.
982 ///
983 /// The low 3 bits are used as a tag value and will be returned as 0.
984 #[inline]
985 #[cfg(feature = "calc")]
986 pub fn calc(ptr: *const ()) -> Self {
987 Self(CompactLength::calc(ptr))
988 }
989
990 /// Create a LengthPercentageAuto from a raw `CompactLength`.
991 /// # Safety
992 /// CompactLength must represent a valid variant for LengthPercentageAuto
993 #[allow(unsafe_code)]
994 pub unsafe fn from_raw(val: CompactLength) -> Self {
995 Self(val)
996 }
997
998 /// Get the underlying `CompactLength` representation of the value
999 pub fn into_raw(self) -> CompactLength {
1000 self.0
1001 }
1002
1003 /// Returns true if the min track sizing function is `MinContent`, `MaxContent` or `Auto`, else false.
1004 #[inline(always)]
1005 pub fn is_intrinsic(&self) -> bool {
1006 self.0.is_intrinsic()
1007 }
1008
1009 /// Returns true if the min track sizing function is `MinContent` or `MaxContent`, else false.
1010 #[inline(always)]
1011 pub fn is_min_or_max_content(&self) -> bool {
1012 self.0.is_min_or_max_content()
1013 }
1014
1015 /// Returns true if the value is an fr value
1016 #[inline(always)]
1017 pub fn is_fr(&self) -> bool {
1018 self.0.is_fr()
1019 }
1020
1021 /// Returns true if the is `Auto`, else false.
1022 #[inline(always)]
1023 pub fn is_auto(&self) -> bool {
1024 self.0.is_auto()
1025 }
1026
1027 /// Returns true if value is MinContent
1028 #[inline(always)]
1029 pub fn is_min_content(&self) -> bool {
1030 self.0.is_min_content()
1031 }
1032
1033 /// Returns true if value is MaxContent
1034 #[inline(always)]
1035 pub fn is_max_content(&self) -> bool {
1036 self.0.is_max_content()
1037 }
1038
1039 /// Returns fixed point values directly. Attempts to resolve percentage values against
1040 /// the passed available_space and returns if this results in a concrete value (which it
1041 /// will if the available_space is `Some`). Otherwise returns `None`.
1042 #[inline(always)]
1043 pub fn definite_value(
1044 self,
1045 parent_size: Option<f32>,
1046 calc_resolver: impl Fn(*const (), f32) -> f32,
1047 ) -> Option<f32> {
1048 match self.0.tag() {
1049 CompactLength::LENGTH_TAG => Some(self.0.value()),
1050 CompactLength::PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
1051 #[cfg(feature = "calc")]
1052 _ if self.0.is_calc() => parent_size.map(|size| calc_resolver(self.0.calc_value(), size)),
1053 _ => None,
1054 }
1055 }
1056
1057 /// Resolve percentage values against the passed parent_size, returning Some(value)
1058 /// Non-percentage values always return None.
1059 #[inline(always)]
1060 pub fn resolved_percentage_size(
1061 self,
1062 parent_size: f32,
1063 calc_resolver: impl Fn(*const (), f32) -> f32,
1064 ) -> Option<f32> {
1065 self.0.resolved_percentage_size(parent_size, calc_resolver)
1066 }
1067
1068 /// Whether the track sizing functions depends on the size of the parent node
1069 #[inline(always)]
1070 pub fn uses_percentage(self) -> bool {
1071 #[cfg(feature = "calc")]
1072 {
1073 matches!(self.0.tag(), CompactLength::PERCENT_TAG) || self.0.is_calc()
1074 }
1075 #[cfg(not(feature = "calc"))]
1076 {
1077 matches!(self.0.tag(), CompactLength::PERCENT_TAG)
1078 }
1079 }
1080}
1081
1082/// The sizing function for a grid track (row/column)
1083///
1084/// May either be a MinMax variant which specifies separate values for the min-/max- track sizing functions
1085/// or a scalar value which applies to both track sizing functions.
1086pub type TrackSizingFunction = MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>;
1087impl TrackSizingFunction {
1088 /// Extract the min track sizing function
1089 pub fn min_sizing_function(&self) -> MinTrackSizingFunction {
1090 self.min
1091 }
1092 /// Extract the max track sizing function
1093 pub fn max_sizing_function(&self) -> MaxTrackSizingFunction {
1094 self.max
1095 }
1096 /// Determine whether at least one of the components ("min" and "max") are fixed sizing function
1097 pub fn has_fixed_component(&self) -> bool {
1098 self.min.0.is_length_or_percentage() || self.max.0.is_length_or_percentage()
1099 }
1100}
1101impl TaffyAuto for TrackSizingFunction {
1102 const AUTO: Self = Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::AUTO };
1103}
1104impl TaffyMinContent for TrackSizingFunction {
1105 const MIN_CONTENT: Self =
1106 Self { min: MinTrackSizingFunction::MIN_CONTENT, max: MaxTrackSizingFunction::MIN_CONTENT };
1107}
1108impl TaffyMaxContent for TrackSizingFunction {
1109 const MAX_CONTENT: Self =
1110 Self { min: MinTrackSizingFunction::MAX_CONTENT, max: MaxTrackSizingFunction::MAX_CONTENT };
1111}
1112impl TaffyFitContent for TrackSizingFunction {
1113 fn fit_content(argument: LengthPercentage) -> Self {
1114 Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::fit_content(argument) }
1115 }
1116}
1117impl TaffyZero for TrackSizingFunction {
1118 const ZERO: Self = Self { min: MinTrackSizingFunction::ZERO, max: MaxTrackSizingFunction::ZERO };
1119}
1120impl FromLength for TrackSizingFunction {
1121 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
1122 Self { min: MinTrackSizingFunction::from_length(value), max: MaxTrackSizingFunction::from_length(value) }
1123 }
1124}
1125impl FromPercent for TrackSizingFunction {
1126 fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
1127 Self { min: MinTrackSizingFunction::from_percent(percent), max: MaxTrackSizingFunction::from_percent(percent) }
1128 }
1129}
1130impl FromFr for TrackSizingFunction {
1131 fn from_fr<Input: Into<f32> + Copy>(flex: Input) -> Self {
1132 Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::from_fr(flex) }
1133 }
1134}
1135impl From<LengthPercentage> for TrackSizingFunction {
1136 fn from(input: LengthPercentage) -> Self {
1137 Self { min: input.into(), max: input.into() }
1138 }
1139}
1140impl From<LengthPercentageAuto> for TrackSizingFunction {
1141 fn from(input: LengthPercentageAuto) -> Self {
1142 Self { min: input.into(), max: input.into() }
1143 }
1144}
1145impl From<Dimension> for TrackSizingFunction {
1146 fn from(input: Dimension) -> Self {
1147 Self { min: input.into(), max: input.into() }
1148 }
1149}
1150
1151/// The first argument to a repeated track definition. This type represents the type of automatic repetition to perform.
1152///
1153/// See <https://www.w3.org/TR/css-grid-1/#auto-repeat> for an explanation of how auto-repeated track definitions work
1154/// and the difference between AutoFit and AutoFill.
1155#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1156#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1157pub enum RepetitionCount {
1158 /// Auto-repeating tracks should be generated to fit the container
1159 /// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fill>
1160 AutoFill,
1161 /// Auto-repeating tracks should be generated to fit the container
1162 /// See: <https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fit>
1163 AutoFit,
1164 /// The specified tracks should be repeated exacts N times
1165 Count(u16),
1166}
1167impl From<u16> for RepetitionCount {
1168 fn from(value: u16) -> Self {
1169 Self::Count(value)
1170 }
1171}
1172
1173/// Error returned when trying to convert a string to a GridTrackRepetition and that string is not
1174/// either "auto-fit" or "auto-fill"
1175#[derive(Debug)]
1176pub struct InvalidStringRepetitionValue;
1177#[cfg(feature = "std")]
1178impl std::error::Error for InvalidStringRepetitionValue {}
1179impl core::fmt::Display for InvalidStringRepetitionValue {
1180 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1181 f.write_str("&str can only be converted to GridTrackRepetition if it's value is 'auto-fit' or 'auto-fill'")
1182 }
1183}
1184impl TryFrom<&str> for RepetitionCount {
1185 type Error = InvalidStringRepetitionValue;
1186 fn try_from(value: &str) -> Result<Self, InvalidStringRepetitionValue> {
1187 match value {
1188 "auto-fit" => Ok(Self::AutoFit),
1189 "auto-fill" => Ok(Self::AutoFill),
1190 _ => Err(InvalidStringRepetitionValue),
1191 }
1192 }
1193}
1194
1195/// A typed representation of a `repeat(..)` in `grid-template-*` value
1196#[derive(Clone, PartialEq, Debug)]
1197#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1198pub struct GridTemplateRepetition<S: CheapCloneStr> {
1199 /// The number of the times the repeat is repeated
1200 pub count: RepetitionCount,
1201 /// The tracks to repeat
1202 pub tracks: Vec<TrackSizingFunction>,
1203 /// The line names for the repeated tracks
1204 pub line_names: Vec<Vec<S>>,
1205}
1206
1207#[rustfmt::skip]
1208impl<S: CheapCloneStr> GenericRepetition for &'_ GridTemplateRepetition<S> {
1209 type CustomIdent = S;
1210 type RepetitionTrackList<'a> = core::iter::Copied<core::slice::Iter<'a, TrackSizingFunction>> where Self: 'a;
1211 type TemplateLineNames<'a> = core::iter::Map<core::slice::Iter<'a, Vec<S>>, fn(&Vec<S>) -> core::slice::Iter<'_, S>> where Self: 'a;
1212 #[inline(always)]
1213 fn count(&self) -> RepetitionCount {
1214 self.count
1215 }
1216 #[inline(always)]
1217 fn track_count(&self) -> u16 {
1218 self.tracks.len() as u16
1219 }
1220 #[inline(always)]
1221 fn tracks(&self) -> Self::RepetitionTrackList<'_> {
1222 self.tracks.iter().copied()
1223 }
1224 #[inline(always)]
1225 fn lines_names(&self) -> Self::TemplateLineNames<'_> {
1226 self.line_names.iter().map(|names| names.iter())
1227 }
1228}
1229
1230/// An element in a `grid-template-columns` or `grid-template-rows` definition.
1231/// Either a track sizing function or a repeat().
1232///
1233/// See <https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns>
1234#[derive(Clone, PartialEq, Debug)]
1235#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1236pub enum GridTemplateComponent<S: CheapCloneStr> {
1237 /// A single non-repeated track
1238 Single(TrackSizingFunction),
1239 /// Automatically generate grid tracks to fit the available space using the specified definite track lengths
1240 /// Only valid if every track in template (not just the repetition) has a fixed size.
1241 Repeat(GridTemplateRepetition<S>),
1242}
1243
1244impl<S: CheapCloneStr> GridTemplateComponent<S> {
1245 /// Convert a `GridTemplateComponent` into a `GridTemplateComponentRef`
1246 pub fn as_component_ref(&self) -> GenericGridTemplateComponent<S, &GridTemplateRepetition<S>> {
1247 match self {
1248 GridTemplateComponent::Single(size) => GenericGridTemplateComponent::Single(*size),
1249 GridTemplateComponent::Repeat(repetition) => GenericGridTemplateComponent::Repeat(repetition),
1250 }
1251 }
1252}
1253
1254impl<S: CheapCloneStr> GridTemplateComponent<S> {
1255 /// Whether the track definition is a auto-repeated fragment
1256 pub fn is_auto_repetition(&self) -> bool {
1257 matches!(
1258 self,
1259 Self::Repeat(GridTemplateRepetition { count: RepetitionCount::AutoFit | RepetitionCount::AutoFill, .. })
1260 )
1261 }
1262}
1263impl<S: CheapCloneStr> TaffyAuto for GridTemplateComponent<S> {
1264 const AUTO: Self = Self::Single(TrackSizingFunction::AUTO);
1265}
1266impl<S: CheapCloneStr> TaffyMinContent for GridTemplateComponent<S> {
1267 const MIN_CONTENT: Self = Self::Single(TrackSizingFunction::MIN_CONTENT);
1268}
1269impl<S: CheapCloneStr> TaffyMaxContent for GridTemplateComponent<S> {
1270 const MAX_CONTENT: Self = Self::Single(TrackSizingFunction::MAX_CONTENT);
1271}
1272impl<S: CheapCloneStr> TaffyFitContent for GridTemplateComponent<S> {
1273 fn fit_content(argument: LengthPercentage) -> Self {
1274 Self::Single(TrackSizingFunction::fit_content(argument))
1275 }
1276}
1277impl<S: CheapCloneStr> TaffyZero for GridTemplateComponent<S> {
1278 const ZERO: Self = Self::Single(TrackSizingFunction::ZERO);
1279}
1280impl<S: CheapCloneStr> FromLength for GridTemplateComponent<S> {
1281 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
1282 Self::Single(TrackSizingFunction::from_length(value))
1283 }
1284}
1285impl<S: CheapCloneStr> FromPercent for GridTemplateComponent<S> {
1286 fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
1287 Self::Single(TrackSizingFunction::from_percent(percent))
1288 }
1289}
1290impl<S: CheapCloneStr> FromFr for GridTemplateComponent<S> {
1291 fn from_fr<Input: Into<f32> + Copy>(flex: Input) -> Self {
1292 Self::Single(TrackSizingFunction::from_fr(flex))
1293 }
1294}
1295impl<S: CheapCloneStr> From<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>> for GridTemplateComponent<S> {
1296 fn from(input: MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>) -> Self {
1297 Self::Single(input)
1298 }
1299}