1use 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#[cfg(feature = "parse")]
14use crate::util::parse::{
15 from_str_from_css, parse_css_str_entirely, CssParseResult, FromCss, ParseError, Parser, Token,
16};
17
18#[derive(Debug, Clone, PartialEq)]
20#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21pub struct GridTemplateArea<CustomIdent: CheapCloneStr> {
22 #[cfg_attr(feature = "serde", serde(deserialize_with = "crate::util::deserialize_from_str"))]
24 pub name: CustomIdent,
25 pub row_start: u16,
27 pub row_end: u16,
29 pub column_start: u16,
31 pub column_end: u16,
33}
34
35#[derive(Debug, Clone, PartialEq)]
37#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
38pub struct NamedGridLine<CustomIdent: CheapCloneStr> {
39 #[cfg_attr(feature = "serde", serde(deserialize_with = "crate::util::deserialize_from_str"))]
41 pub name: CustomIdent,
42 pub index: u16,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq)]
48pub(crate) enum GridAreaAxis {
49 Row,
51 Column,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq)]
57pub(crate) enum GridAreaEnd {
58 Start,
60 End,
62}
63
64pub trait GenericRepetition {
66 type CustomIdent: CheapCloneStr;
68 type RepetitionTrackList<'a>: Iterator<Item = TrackSizingFunction> + ExactSizeIterator + Clone
70 where
71 Self: 'a;
72
73 type TemplateLineNames<'a>: TemplateLineNames<'a, Self::CustomIdent>
75 where
76 Self: 'a;
77 fn count(&self) -> RepetitionCount;
79 fn tracks(&self) -> Self::RepetitionTrackList<'_>;
81 fn track_count(&self) -> u16 {
83 self.tracks().len() as u16
84 }
85 fn lines_names(&self) -> Self::TemplateLineNames<'_>;
87}
88
89#[rustfmt::skip]
92pub trait TemplateLineNames<'a, S: CheapCloneStr> : Iterator<Item = Self::LineNameSet<'a>> + ExactSizeIterator + Clone where Self: 'a {
93 type LineNameSet<'b>: Iterator<Item = &'b S> + ExactSizeIterator + Clone where Self: 'b;
96}
97
98impl<'a, S: CheapCloneStr> TemplateLineNames<'a, S>
99 for core::iter::Map<core::slice::Iter<'a, Vec<S>>, fn(&Vec<S>) -> core::slice::Iter<'_, S>>
100{
101 type LineNameSet<'b>
102 = core::slice::Iter<'b, S>
103 where
104 Self: 'b;
105}
106
107#[derive(Copy, Clone)]
108pub enum GenericGridTemplateComponent<S, Repetition>
111where
112 S: CheapCloneStr,
113 Repetition: GenericRepetition<CustomIdent = S>,
114{
115 Single(TrackSizingFunction),
117 Repeat(Repetition),
119}
120
121impl<S, Repetition> GenericGridTemplateComponent<S, Repetition>
122where
123 S: CheapCloneStr,
124 Repetition: GenericRepetition<CustomIdent = S>,
125{
126 pub fn is_auto_repetition(&self) -> bool {
128 match self {
129 Self::Single(_) => false,
130 Self::Repeat(repeat) => matches!(repeat.count(), RepetitionCount::AutoFit | RepetitionCount::AutoFill),
131 }
132 }
133}
134
135pub trait GridContainerStyle: CoreStyle {
137 type Repetition<'a>: GenericRepetition<CustomIdent = Self::CustomIdent>
139 where
140 Self: 'a;
141
142 type TemplateTrackList<'a>: Iterator<Item = GenericGridTemplateComponent<Self::CustomIdent, Self::Repetition<'a>>>
144 + ExactSizeIterator
145 + Clone
146 where
147 Self: 'a;
148
149 type AutoTrackList<'a>: Iterator<Item = TrackSizingFunction> + ExactSizeIterator + Clone
151 where
152 Self: 'a;
153
154 type TemplateLineNames<'a>: TemplateLineNames<'a, Self::CustomIdent>
157 where
158 Self: 'a;
159
160 type GridTemplateAreas<'a>: IntoIterator<Item = GridTemplateArea<Self::CustomIdent>>
162 where
163 Self: 'a;
164
165 fn grid_template_rows(&self) -> Option<Self::TemplateTrackList<'_>>;
170 fn grid_template_columns(&self) -> Option<Self::TemplateTrackList<'_>>;
172 fn grid_auto_rows(&self) -> Self::AutoTrackList<'_>;
174 fn grid_auto_columns(&self) -> Self::AutoTrackList<'_>;
176
177 fn grid_template_areas(&self) -> Option<Self::GridTemplateAreas<'_>>;
179 fn grid_template_column_names(&self) -> Option<Self::TemplateLineNames<'_>>;
181 fn grid_template_row_names(&self) -> Option<Self::TemplateLineNames<'_>>;
183
184 #[inline(always)]
186 fn grid_auto_flow(&self) -> GridAutoFlow {
187 Style::<Self::CustomIdent>::DEFAULT.grid_auto_flow
188 }
189
190 #[inline(always)]
192 fn gap(&self) -> Size<LengthPercentage> {
193 Style::<Self::CustomIdent>::DEFAULT.gap
194 }
195
196 #[inline(always)]
200 fn align_content(&self) -> Option<AlignContent> {
201 Style::<Self::CustomIdent>::DEFAULT.align_content
202 }
203 #[inline(always)]
205 fn justify_content(&self) -> Option<JustifyContent> {
206 Style::<Self::CustomIdent>::DEFAULT.justify_content
207 }
208 #[inline(always)]
210 fn align_items(&self) -> Option<AlignItems> {
211 Style::<Self::CustomIdent>::DEFAULT.align_items
212 }
213 #[inline(always)]
215 fn justify_items(&self) -> Option<AlignItems> {
216 Style::<Self::CustomIdent>::DEFAULT.justify_items
217 }
218
219 #[inline(always)]
221 fn grid_template_tracks(&self, axis: AbsoluteAxis) -> Option<Self::TemplateTrackList<'_>> {
222 match axis {
223 AbsoluteAxis::Horizontal => self.grid_template_columns(),
224 AbsoluteAxis::Vertical => self.grid_template_rows(),
225 }
226 }
227
228 #[inline(always)]
230 fn grid_align_content(&self, axis: AbstractAxis) -> AlignContent {
231 match axis {
232 AbstractAxis::Inline => self.justify_content().unwrap_or(AlignContent::Stretch),
233 AbstractAxis::Block => self.align_content().unwrap_or(AlignContent::Stretch),
234 }
235 }
236}
237
238pub trait GridItemStyle: CoreStyle {
240 #[inline(always)]
242 fn grid_row(&self) -> Line<GridPlacement<Self::CustomIdent>> {
243 Default::default()
244 }
245 #[inline(always)]
247 fn grid_column(&self) -> Line<GridPlacement<Self::CustomIdent>> {
248 Default::default()
249 }
250
251 #[inline(always)]
254 fn align_self(&self) -> Option<AlignSelf> {
255 Style::<Self::CustomIdent>::DEFAULT.align_self
256 }
257 #[inline(always)]
260 fn justify_self(&self) -> Option<AlignSelf> {
261 Style::<Self::CustomIdent>::DEFAULT.justify_self
262 }
263
264 #[inline(always)]
266 fn grid_placement(&self, axis: AbsoluteAxis) -> Line<GridPlacement<Self::CustomIdent>> {
267 match axis {
268 AbsoluteAxis::Horizontal => self.grid_column(),
269 AbsoluteAxis::Vertical => self.grid_row(),
270 }
271 }
272}
273
274#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
282#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
283pub enum GridAutoFlow {
284 #[default]
286 Row,
287 Column,
289 RowDense,
291 ColumnDense,
293}
294
295#[cfg(feature = "parse")]
296impl FromCss for GridAutoFlow {
297 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
298 let mut axis: Option<&'static str> = None;
299 let mut dense = false;
300
301 for _ in 0..2 {
302 if let Ok(ident) = parser.try_parse(|parser| parser.expect_ident_cloned()) {
303 match &*ident {
304 "row" => {
305 axis = Some("row");
306 }
307 "column" => {
308 axis = Some("column");
309 }
310 "dense" => dense = true,
311 _ => {
312 return Err(parser.new_unexpected_token_error(Token::Ident(ident)));
313 }
314 }
315 } else {
316 break;
317 }
318 }
319
320 match (axis, dense) {
321 (Some("row"), false) => Ok(Self::Row),
322 (Some("row") | None, true) => Ok(Self::RowDense),
323 (Some("column"), false) => Ok(Self::Column),
324 (Some("column"), true) => Ok(Self::ColumnDense),
325 (None, false) => {
326 let token = parser.next().cloned()?;
327 Err(parser.new_unexpected_token_error(token))
328 }
329 _ => unreachable!(),
330 }
331 }
332}
333#[cfg(feature = "parse")]
334from_str_from_css!(GridAutoFlow);
335
336impl GridAutoFlow {
337 pub const fn is_dense(&self) -> bool {
340 match self {
341 Self::Row | Self::Column => false,
342 Self::RowDense | Self::ColumnDense => true,
343 }
344 }
345
346 pub const fn primary_axis(&self) -> AbsoluteAxis {
349 match self {
350 Self::Row | Self::RowDense => AbsoluteAxis::Horizontal,
351 Self::Column | Self::ColumnDense => AbsoluteAxis::Vertical,
352 }
353 }
354}
355
356#[derive(Copy, Clone, PartialEq, Eq, Debug)]
362#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
363pub enum GenericGridPlacement<LineType: GridCoordinate> {
364 Auto,
366 Line(LineType),
368 Span(u16),
370}
371
372pub(crate) type OriginZeroGridPlacement = GenericGridPlacement<OriginZeroLine>;
374
375pub(crate) type NonNamedGridPlacement = GenericGridPlacement<GridLine>;
379
380#[derive(Clone, PartialEq, Debug, Default)]
386#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
387pub enum GridPlacement<S: CheapCloneStr = DefaultCheapStr> {
388 #[default]
390 Auto,
391 Line(GridLine),
393 NamedLine(S, i16),
395 Span(u16),
397 NamedSpan(S, u16),
402}
403impl<S: CheapCloneStr> TaffyAuto for GridPlacement<S> {
404 const AUTO: Self = Self::Auto;
405}
406impl<S: CheapCloneStr> TaffyGridLine for GridPlacement<S> {
407 fn from_line_index(index: i16) -> Self {
408 GridPlacement::<S>::Line(GridLine::from(index))
409 }
410}
411impl<S: CheapCloneStr> TaffyGridLine for Line<GridPlacement<S>> {
412 fn from_line_index(index: i16) -> Self {
413 Line { start: GridPlacement::<S>::from_line_index(index), end: GridPlacement::<S>::Auto }
414 }
415}
416impl<S: CheapCloneStr> TaffyGridSpan for GridPlacement<S> {
417 fn from_span(span: u16) -> Self {
418 GridPlacement::<S>::Span(span)
419 }
420}
421impl<S: CheapCloneStr> TaffyGridSpan for Line<GridPlacement<S>> {
422 fn from_span(span: u16) -> Self {
423 Line { start: GridPlacement::<S>::from_span(span), end: GridPlacement::<S>::Auto }
424 }
425}
426
427#[cfg(feature = "parse")]
428impl<S: CheapCloneStr> FromCss for GridPlacement<S> {
429 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
430 let mut span = false;
431 let mut number = None;
432 let mut ident = None;
433
434 while !parser.is_exhausted() {
435 let token = parser.next()?.clone();
436 match &token {
437 Token::Ident(s) => match s.as_ref() {
438 "auto" => {
439 if span || number.is_some() || ident.is_some() {
440 return Err(parser.new_unexpected_token_error(token));
441 }
442 parser.expect_exhausted()?;
443 return Ok(Self::Auto);
444 }
445 "span" => {
446 if span {
447 return Err(parser.new_unexpected_token_error(token));
448 }
449 span = true;
450 }
451 other => {
452 if ident.is_some() {
453 return Err(parser.new_unexpected_token_error(token));
454 }
455 ident = Some(S::from(other));
456 }
457 },
458 Token::Number { int_value: Some(value), .. } if *value != 0 => {
459 if number.is_some() {
460 return Err(parser.new_unexpected_token_error(token));
461 }
462 number = Some(*value);
463 }
464 _ => return Err(parser.new_unexpected_token_error(token)),
465 };
466 }
467
468 match (span, number, ident) {
469 (true, None, None) => Ok(Self::Span(0)),
470 (true, Some(number), None) => Ok(Self::Span(number as u16)),
471 (true, None, Some(ident)) => Ok(Self::NamedSpan(ident, 0)),
472 (true, Some(number), Some(ident)) => Ok(Self::NamedSpan(ident, number as u16)),
473 (false, Some(number), None) => Ok(Self::Line(GridLine::from(number as i16))),
474 (false, Some(number), Some(ident)) => Ok(Self::NamedLine(ident, number as i16)),
475 (false, None, Some(ident)) => Ok(Self::NamedLine(ident, 0)),
476 (false, None, None) => Err(parser.new_error(cssparser::BasicParseErrorKind::EndOfInput)),
477 }
478 }
479}
480
481#[cfg(feature = "parse")]
482impl<S: CheapCloneStr> core::str::FromStr for GridPlacement<S> {
483 type Err = ParseError;
484 fn from_str(input: &str) -> Result<Self, Self::Err> {
485 parse_css_str_entirely(input)
486 }
487}
488
489impl<S: CheapCloneStr> GridPlacement<S> {
490 pub fn into_origin_zero_placement_ignoring_named(&self, explicit_track_count: u16) -> OriginZeroGridPlacement {
492 match self {
493 Self::Auto => OriginZeroGridPlacement::Auto,
494 Self::Span(span) => OriginZeroGridPlacement::Span(*span),
495 Self::Line(line) => match line.as_i16() {
498 0 => OriginZeroGridPlacement::Auto,
499 _ => OriginZeroGridPlacement::Line(line.into_origin_zero_line(explicit_track_count)),
500 },
501 Self::NamedLine(_, _) => OriginZeroGridPlacement::Auto,
502 Self::NamedSpan(_, _) => OriginZeroGridPlacement::Auto,
503 }
504 }
505}
506
507impl<S: CheapCloneStr> Line<GridPlacement<S>> {
508 pub fn into_origin_zero_ignoring_named(&self, explicit_track_count: u16) -> Line<OriginZeroGridPlacement> {
510 Line {
511 start: self.start.into_origin_zero_placement_ignoring_named(explicit_track_count),
512 end: self.end.into_origin_zero_placement_ignoring_named(explicit_track_count),
513 }
514 }
515}
516
517impl NonNamedGridPlacement {
518 pub fn into_origin_zero_placement(
520 &self,
521 explicit_track_count: u16,
522 ) -> OriginZeroGridPlacement {
524 match self {
525 Self::Auto => OriginZeroGridPlacement::Auto,
526 Self::Span(span) => OriginZeroGridPlacement::Span(*span),
527 Self::Line(line) => match line.as_i16() {
530 0 => OriginZeroGridPlacement::Auto,
531 _ => OriginZeroGridPlacement::Line(line.into_origin_zero_line(explicit_track_count)),
532 },
533 }
534 }
535}
536
537impl<T: GridCoordinate> Line<GenericGridPlacement<T>> {
538 pub const fn indefinite_span(&self) -> u16 {
541 use GenericGridPlacement as GP;
542 match (self.start, self.end) {
543 (GP::Line(_), GP::Auto) => 1,
544 (GP::Auto, GP::Line(_)) => 1,
545 (GP::Auto, GP::Auto) => 1,
546 (GP::Line(_), GP::Span(span)) => span,
547 (GP::Span(span), GP::Line(_)) => span,
548 (GP::Span(span), GP::Auto) => span,
549 (GP::Auto, GP::Span(span)) => span,
550 (GP::Span(span), GP::Span(_)) => span,
551 (GP::Line(_), GP::Line(_)) => panic!("indefinite_span should only be called on indefinite grid tracks"),
552 }
553 }
554}
555
556impl<S: CheapCloneStr> Line<GridPlacement<S>> {
557 #[inline]
558 pub fn is_definite(&self) -> bool {
562 match (&self.start, &self.end) {
563 (GridPlacement::Line(line), _) if line.as_i16() != 0 => true,
564 (_, GridPlacement::Line(line)) if line.as_i16() != 0 => true,
565 (GridPlacement::NamedLine(_, _), _) => true,
566 (_, GridPlacement::NamedLine(_, _)) => true,
567 _ => false,
568 }
569 }
570}
571
572impl Line<NonNamedGridPlacement> {
573 #[inline]
574 pub fn is_definite(&self) -> bool {
578 match (&self.start, &self.end) {
579 (GenericGridPlacement::Line(line), _) if line.as_i16() != 0 => true,
580 (_, GenericGridPlacement::Line(line)) if line.as_i16() != 0 => true,
581 _ => false,
582 }
583 }
584
585 pub fn into_origin_zero(&self, explicit_track_count: u16) -> Line<OriginZeroGridPlacement> {
587 Line {
588 start: self.start.into_origin_zero_placement(explicit_track_count),
589 end: self.end.into_origin_zero_placement(explicit_track_count),
590 }
591 }
592}
593
594impl Line<OriginZeroGridPlacement> {
595 #[inline]
596 pub const fn is_definite(&self) -> bool {
599 matches!((self.start, self.end), (GenericGridPlacement::Line(_), _) | (_, GenericGridPlacement::Line(_)))
600 }
601
602 pub fn resolve_definite_grid_lines(&self) -> Line<OriginZeroLine> {
605 use OriginZeroGridPlacement as GP;
606 match (self.start, self.end) {
607 (GP::Line(line1), GP::Line(line2)) => {
608 if line1 == line2 {
609 Line { start: line1, end: line1 + 1 }
610 } else {
611 Line { start: min(line1, line2), end: max(line1, line2) }
612 }
613 }
614 (GP::Line(line), GP::Span(span)) => Line { start: line, end: line + span },
615 (GP::Line(line), GP::Auto) => Line { start: line, end: line + 1 },
616 (GP::Span(span), GP::Line(line)) => Line { start: line - span, end: line },
617 (GP::Auto, GP::Line(line)) => Line { start: line - 1, end: line },
618 _ => panic!("resolve_definite_grid_tracks should only be called on definite grid tracks"),
619 }
620 }
621
622 pub fn resolve_absolutely_positioned_grid_tracks(&self) -> Line<Option<OriginZeroLine>> {
632 use OriginZeroGridPlacement as GP;
633 match (self.start, self.end) {
634 (GP::Line(track1), GP::Line(track2)) => {
635 if track1 == track2 {
636 Line { start: Some(track1), end: Some(track1 + 1) }
637 } else {
638 Line { start: Some(min(track1, track2)), end: Some(max(track1, track2)) }
639 }
640 }
641 (GP::Line(track), GP::Span(span)) => Line { start: Some(track), end: Some(track + span) },
642 (GP::Line(track), GP::Auto) => Line { start: Some(track), end: None },
643 (GP::Span(span), GP::Line(track)) => Line { start: Some(track - span), end: Some(track) },
644 (GP::Auto, GP::Line(track)) => Line { start: None, end: Some(track) },
645 _ => Line { start: None, end: None },
646 }
647 }
648
649 pub fn resolve_indefinite_grid_tracks(&self, start: OriginZeroLine) -> Line<OriginZeroLine> {
652 use OriginZeroGridPlacement as GP;
653 match (self.start, self.end) {
654 (GP::Auto, GP::Auto) => Line { start, end: start + 1 },
655 (GP::Span(span), GP::Auto) => Line { start, end: start + span },
656 (GP::Auto, GP::Span(span)) => Line { start, end: start + span },
657 (GP::Span(span), GP::Span(_)) => Line { start, end: start + span },
658 _ => panic!("resolve_indefinite_grid_tracks should only be called on indefinite grid tracks"),
659 }
660 }
661}
662
663impl<S: CheapCloneStr> Default for Line<GridPlacement<S>> {
665 fn default() -> Self {
666 Line { start: GridPlacement::<S>::Auto, end: GridPlacement::<S>::Auto }
667 }
668}
669
670#[derive(Copy, Clone, PartialEq, Debug)]
676#[cfg_attr(feature = "serde", derive(Serialize))]
677pub struct MaxTrackSizingFunction(pub(crate) CompactLength);
678impl TaffyZero for MaxTrackSizingFunction {
679 const ZERO: Self = Self(CompactLength::ZERO);
680}
681impl TaffyAuto for MaxTrackSizingFunction {
682 const AUTO: Self = Self(CompactLength::AUTO);
683}
684impl TaffyMinContent for MaxTrackSizingFunction {
685 const MIN_CONTENT: Self = Self(CompactLength::MIN_CONTENT);
686}
687impl TaffyMaxContent for MaxTrackSizingFunction {
688 const MAX_CONTENT: Self = Self(CompactLength::MAX_CONTENT);
689}
690impl FromLength for MaxTrackSizingFunction {
691 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
692 Self::length(value.into())
693 }
694}
695impl FromPercent for MaxTrackSizingFunction {
696 fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
697 Self::percent(value.into())
698 }
699}
700impl TaffyFitContent for MaxTrackSizingFunction {
701 fn fit_content(argument: LengthPercentage) -> Self {
702 Self(CompactLength::fit_content(argument))
703 }
704}
705impl FromFr for MaxTrackSizingFunction {
706 fn from_fr<Input: Into<f32> + Copy>(value: Input) -> Self {
707 Self::fr(value.into())
708 }
709}
710impl From<LengthPercentage> for MaxTrackSizingFunction {
711 fn from(input: LengthPercentage) -> Self {
712 Self(input.0)
713 }
714}
715impl From<LengthPercentageAuto> for MaxTrackSizingFunction {
716 fn from(input: LengthPercentageAuto) -> Self {
717 Self(input.0)
718 }
719}
720impl From<Dimension> for MaxTrackSizingFunction {
721 fn from(input: Dimension) -> Self {
722 Self(input.0)
723 }
724}
725impl From<MinTrackSizingFunction> for MaxTrackSizingFunction {
726 fn from(input: MinTrackSizingFunction) -> Self {
727 Self(input.0)
728 }
729}
730
731#[cfg(feature = "parse")]
732impl FromCss for MaxTrackSizingFunction {
733 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
734 let token = parser.next()?.clone();
735 match token {
736 Token::Percentage { unit_value, .. } => Ok(Self::percent(unit_value)),
737 Token::Dimension { unit, value, .. } if unit == "px" => Ok(Self::length(value)),
738 Token::Dimension { unit, value, .. } if unit == "fr" && value.is_sign_positive() => Ok(Self::fr(value)),
739 Token::Ident(ref ident) => match ident.as_ref() {
740 "auto" => Ok(Self::auto()),
741 "min-content" => Ok(Self::min_content()),
742 "max-content" => Ok(Self::max_content()),
743 _ => Err(parser.new_unexpected_token_error(token))?,
744 },
745 Token::Function(ref name) if name.as_ref() == "fit-content" => parser.parse_nested_block(|parser| {
746 let token = parser.next()?.clone();
747 match token {
748 Token::Percentage { unit_value, .. } => Ok(Self::fit_content_percent(unit_value)),
749 Token::Dimension { unit, value, .. } if unit == "px" => Ok(Self::fit_content_px(value)),
750 token => Err(parser.new_unexpected_token_error(token))?,
751 }
752 }),
753 token => Err(parser.new_unexpected_token_error(token))?,
754 }
755 }
756}
757
758#[cfg(feature = "parse")]
759from_str_from_css!(MaxTrackSizingFunction);
760
761#[cfg(feature = "serde")]
762impl<'de> serde::Deserialize<'de> for MaxTrackSizingFunction {
763 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
764 where
765 D: serde::Deserializer<'de>,
766 {
767 let inner = CompactLength::deserialize(deserializer)?;
768 if matches!(
770 inner.tag(),
771 CompactLength::LENGTH_TAG
772 | CompactLength::PERCENT_TAG
773 | CompactLength::AUTO_TAG
774 | CompactLength::MIN_CONTENT_TAG
775 | CompactLength::MAX_CONTENT_TAG
776 | CompactLength::FIT_CONTENT_PX_TAG
777 | CompactLength::FIT_CONTENT_PERCENT_TAG
778 | CompactLength::FR_TAG
779 ) {
780 Ok(Self(inner))
781 } else {
782 Err(serde::de::Error::custom("Invalid tag"))
783 }
784 }
785}
786
787impl MaxTrackSizingFunction {
788 #[inline(always)]
791 pub const fn length(val: f32) -> Self {
792 Self(CompactLength::length(val))
793 }
794
795 #[inline(always)]
799 pub const fn percent(val: f32) -> Self {
800 Self(CompactLength::percent(val))
801 }
802
803 #[inline(always)]
806 pub const fn auto() -> Self {
807 Self(CompactLength::auto())
808 }
809
810 #[inline(always)]
813 pub const fn min_content() -> Self {
814 Self(CompactLength::min_content())
815 }
816
817 #[inline(always)]
820 pub const fn max_content() -> Self {
821 Self(CompactLength::max_content())
822 }
823
824 #[inline(always)]
834 pub const fn fit_content_px(limit: f32) -> Self {
835 Self(CompactLength::fit_content_px(limit))
836 }
837
838 #[inline(always)]
848 pub const fn fit_content_percent(limit: f32) -> Self {
849 Self(CompactLength::fit_content_percent(limit))
850 }
851
852 #[inline(always)]
856 pub const fn fr(val: f32) -> Self {
857 Self(CompactLength::fr(val))
858 }
859
860 #[inline]
865 #[cfg(feature = "calc")]
866 pub fn calc(ptr: *const ()) -> Self {
867 Self(CompactLength::calc(ptr))
868 }
869
870 #[allow(unsafe_code)]
874 pub unsafe fn from_raw(val: CompactLength) -> Self {
875 Self(val)
876 }
877
878 pub fn into_raw(self) -> CompactLength {
880 self.0
881 }
882
883 #[inline(always)]
885 pub fn is_intrinsic(&self) -> bool {
886 self.0.is_intrinsic()
887 }
888
889 #[inline(always)]
893 pub fn is_max_content_alike(&self) -> bool {
894 self.0.is_max_content_alike()
895 }
896
897 #[inline(always)]
899 pub fn is_fr(&self) -> bool {
900 self.0.is_fr()
901 }
902
903 #[inline(always)]
905 pub fn is_auto(&self) -> bool {
906 self.0.is_auto()
907 }
908
909 #[inline(always)]
911 pub fn is_min_content(&self) -> bool {
912 self.0.is_min_content()
913 }
914
915 #[inline(always)]
917 pub fn is_max_content(&self) -> bool {
918 self.0.is_max_content()
919 }
920
921 #[inline(always)]
923 pub fn is_fit_content(&self) -> bool {
924 self.0.is_fit_content()
925 }
926
927 #[inline(always)]
929 pub fn is_max_or_fit_content(&self) -> bool {
930 self.0.is_max_or_fit_content()
931 }
932
933 #[inline(always)]
935 pub fn has_definite_value(self, parent_size: Option<f32>) -> bool {
936 match self.0.tag() {
937 CompactLength::LENGTH_TAG => true,
938 CompactLength::PERCENT_TAG => parent_size.is_some(),
939 #[cfg(feature = "calc")]
940 _ if self.0.is_calc() => parent_size.is_some(),
941 _ => false,
942 }
943 }
944
945 #[inline(always)]
949 pub fn definite_value(
950 self,
951 parent_size: Option<f32>,
952 calc_resolver: impl Fn(*const (), f32) -> f32,
953 ) -> Option<f32> {
954 match self.0.tag() {
955 CompactLength::LENGTH_TAG => Some(self.0.value()),
956 CompactLength::PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
957 #[cfg(feature = "calc")]
958 _ if self.0.is_calc() => parent_size.map(|size| calc_resolver(self.0.calc_value(), size)),
959 _ => None,
960 }
961 }
962
963 #[inline(always)]
970 pub fn definite_limit(
971 self,
972 parent_size: Option<f32>,
973 calc_resolver: impl Fn(*const (), f32) -> f32,
974 ) -> Option<f32> {
975 match self.0.tag() {
976 CompactLength::FIT_CONTENT_PX_TAG => Some(self.0.value()),
977 CompactLength::FIT_CONTENT_PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
978 _ => self.definite_value(parent_size, calc_resolver),
979 }
980 }
981
982 #[inline(always)]
985 pub fn resolved_percentage_size(
986 self,
987 parent_size: f32,
988 calc_resolver: impl Fn(*const (), f32) -> f32,
989 ) -> Option<f32> {
990 self.0.resolved_percentage_size(parent_size, calc_resolver)
991 }
992
993 #[inline(always)]
995 pub fn uses_percentage(self) -> bool {
996 self.0.uses_percentage()
997 }
998}
999
1000#[derive(Copy, Clone, PartialEq, Debug)]
1006#[cfg_attr(feature = "serde", derive(Serialize))]
1007pub struct MinTrackSizingFunction(pub(crate) CompactLength);
1008impl TaffyZero for MinTrackSizingFunction {
1009 const ZERO: Self = Self(CompactLength::ZERO);
1010}
1011impl TaffyAuto for MinTrackSizingFunction {
1012 const AUTO: Self = Self(CompactLength::AUTO);
1013}
1014impl TaffyMinContent for MinTrackSizingFunction {
1015 const MIN_CONTENT: Self = Self(CompactLength::MIN_CONTENT);
1016}
1017impl TaffyMaxContent for MinTrackSizingFunction {
1018 const MAX_CONTENT: Self = Self(CompactLength::MAX_CONTENT);
1019}
1020impl FromLength for MinTrackSizingFunction {
1021 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
1022 Self::length(value.into())
1023 }
1024}
1025impl FromPercent for MinTrackSizingFunction {
1026 fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
1027 Self::percent(value.into())
1028 }
1029}
1030impl From<LengthPercentage> for MinTrackSizingFunction {
1031 fn from(input: LengthPercentage) -> Self {
1032 Self(input.0)
1033 }
1034}
1035impl From<LengthPercentageAuto> for MinTrackSizingFunction {
1036 fn from(input: LengthPercentageAuto) -> Self {
1037 Self(input.0)
1038 }
1039}
1040impl From<Dimension> for MinTrackSizingFunction {
1041 fn from(input: Dimension) -> Self {
1042 Self(input.0)
1043 }
1044}
1045
1046impl From<MaxTrackSizingFunction> for MinTrackSizingFunction {
1047 fn from(input: MaxTrackSizingFunction) -> Self {
1048 if input.is_fr() || input.is_fit_content() {
1049 return Self::auto();
1050 }
1051 Self(input.0)
1052 }
1053}
1054
1055#[cfg(feature = "parse")]
1056impl FromCss for MinTrackSizingFunction {
1057 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
1058 let token = parser.next()?.clone();
1059 match token {
1060 Token::Percentage { unit_value, .. } => Ok(Self::percent(unit_value)),
1061 Token::Dimension { unit, value, .. } if unit == "px" => Ok(Self::length(value)),
1062 Token::Ident(ref ident) => match ident.as_ref() {
1063 "auto" => Ok(Self::auto()),
1064 "min-content" => Ok(Self::min_content()),
1065 "max-content" => Ok(Self::max_content()),
1066 _ => Err(parser.new_unexpected_token_error(token))?,
1067 },
1068 token => Err(parser.new_unexpected_token_error(token))?,
1069 }
1070 }
1071}
1072
1073#[cfg(feature = "parse")]
1074from_str_from_css!(MinTrackSizingFunction);
1075
1076#[cfg(feature = "serde")]
1077impl<'de> serde::Deserialize<'de> for MinTrackSizingFunction {
1078 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1079 where
1080 D: serde::Deserializer<'de>,
1081 {
1082 let inner = CompactLength::deserialize(deserializer)?;
1083 if matches!(
1085 inner.tag(),
1086 CompactLength::LENGTH_TAG
1087 | CompactLength::PERCENT_TAG
1088 | CompactLength::AUTO_TAG
1089 | CompactLength::MIN_CONTENT_TAG
1090 | CompactLength::MAX_CONTENT_TAG
1091 | CompactLength::FIT_CONTENT_PX_TAG
1092 | CompactLength::FIT_CONTENT_PERCENT_TAG
1093 ) {
1094 Ok(Self(inner))
1095 } else {
1096 Err(serde::de::Error::custom("Invalid tag"))
1097 }
1098 }
1099}
1100
1101impl MinTrackSizingFunction {
1102 #[inline(always)]
1105 pub const fn length(val: f32) -> Self {
1106 Self(CompactLength::length(val))
1107 }
1108
1109 #[inline(always)]
1113 pub const fn percent(val: f32) -> Self {
1114 Self(CompactLength::percent(val))
1115 }
1116
1117 #[inline(always)]
1120 pub const fn auto() -> Self {
1121 Self(CompactLength::auto())
1122 }
1123
1124 #[inline(always)]
1127 pub const fn min_content() -> Self {
1128 Self(CompactLength::min_content())
1129 }
1130
1131 #[inline(always)]
1134 pub const fn max_content() -> Self {
1135 Self(CompactLength::max_content())
1136 }
1137
1138 #[inline]
1143 #[cfg(feature = "calc")]
1144 pub fn calc(ptr: *const ()) -> Self {
1145 Self(CompactLength::calc(ptr))
1146 }
1147
1148 #[allow(unsafe_code)]
1152 pub unsafe fn from_raw(val: CompactLength) -> Self {
1153 Self(val)
1154 }
1155
1156 pub fn into_raw(self) -> CompactLength {
1158 self.0
1159 }
1160
1161 #[inline(always)]
1163 pub fn is_intrinsic(&self) -> bool {
1164 self.0.is_intrinsic()
1165 }
1166
1167 #[inline(always)]
1169 pub fn is_min_or_max_content(&self) -> bool {
1170 self.0.is_min_or_max_content()
1171 }
1172
1173 #[inline(always)]
1175 pub fn is_fr(&self) -> bool {
1176 self.0.is_fr()
1177 }
1178
1179 #[inline(always)]
1181 pub fn is_auto(&self) -> bool {
1182 self.0.is_auto()
1183 }
1184
1185 #[inline(always)]
1187 pub fn is_min_content(&self) -> bool {
1188 self.0.is_min_content()
1189 }
1190
1191 #[inline(always)]
1193 pub fn is_max_content(&self) -> bool {
1194 self.0.is_max_content()
1195 }
1196
1197 #[inline(always)]
1201 pub fn definite_value(
1202 self,
1203 parent_size: Option<f32>,
1204 calc_resolver: impl Fn(*const (), f32) -> f32,
1205 ) -> Option<f32> {
1206 match self.0.tag() {
1207 CompactLength::LENGTH_TAG => Some(self.0.value()),
1208 CompactLength::PERCENT_TAG => parent_size.map(|size| self.0.value() * size),
1209 #[cfg(feature = "calc")]
1210 _ if self.0.is_calc() => parent_size.map(|size| calc_resolver(self.0.calc_value(), size)),
1211 _ => None,
1212 }
1213 }
1214
1215 #[inline(always)]
1218 pub fn resolved_percentage_size(
1219 self,
1220 parent_size: f32,
1221 calc_resolver: impl Fn(*const (), f32) -> f32,
1222 ) -> Option<f32> {
1223 self.0.resolved_percentage_size(parent_size, calc_resolver)
1224 }
1225
1226 #[inline(always)]
1228 pub fn uses_percentage(self) -> bool {
1229 #[cfg(feature = "calc")]
1230 {
1231 matches!(self.0.tag(), CompactLength::PERCENT_TAG) || self.0.is_calc()
1232 }
1233 #[cfg(not(feature = "calc"))]
1234 {
1235 matches!(self.0.tag(), CompactLength::PERCENT_TAG)
1236 }
1237 }
1238}
1239
1240pub type TrackSizingFunction = MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>;
1245impl TrackSizingFunction {
1246 pub fn min_sizing_function(&self) -> MinTrackSizingFunction {
1248 self.min
1249 }
1250 pub fn max_sizing_function(&self) -> MaxTrackSizingFunction {
1252 self.max
1253 }
1254 pub fn has_fixed_component(&self) -> bool {
1256 self.min.0.is_length_or_percentage() || self.max.0.is_length_or_percentage()
1257 }
1258}
1259impl TaffyAuto for TrackSizingFunction {
1260 const AUTO: Self = Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::AUTO };
1261}
1262impl TaffyMinContent for TrackSizingFunction {
1263 const MIN_CONTENT: Self =
1264 Self { min: MinTrackSizingFunction::MIN_CONTENT, max: MaxTrackSizingFunction::MIN_CONTENT };
1265}
1266impl TaffyMaxContent for TrackSizingFunction {
1267 const MAX_CONTENT: Self =
1268 Self { min: MinTrackSizingFunction::MAX_CONTENT, max: MaxTrackSizingFunction::MAX_CONTENT };
1269}
1270impl TaffyFitContent for TrackSizingFunction {
1271 fn fit_content(argument: LengthPercentage) -> Self {
1272 Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::fit_content(argument) }
1273 }
1274}
1275impl TaffyZero for TrackSizingFunction {
1276 const ZERO: Self = Self { min: MinTrackSizingFunction::ZERO, max: MaxTrackSizingFunction::ZERO };
1277}
1278impl FromLength for TrackSizingFunction {
1279 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
1280 Self { min: MinTrackSizingFunction::from_length(value), max: MaxTrackSizingFunction::from_length(value) }
1281 }
1282}
1283impl FromPercent for TrackSizingFunction {
1284 fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
1285 Self { min: MinTrackSizingFunction::from_percent(percent), max: MaxTrackSizingFunction::from_percent(percent) }
1286 }
1287}
1288impl FromFr for TrackSizingFunction {
1289 fn from_fr<Input: Into<f32> + Copy>(flex: Input) -> Self {
1290 Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::from_fr(flex) }
1291 }
1292}
1293impl From<LengthPercentage> for TrackSizingFunction {
1294 fn from(input: LengthPercentage) -> Self {
1295 Self { min: input.into(), max: input.into() }
1296 }
1297}
1298impl From<LengthPercentageAuto> for TrackSizingFunction {
1299 fn from(input: LengthPercentageAuto) -> Self {
1300 Self { min: input.into(), max: input.into() }
1301 }
1302}
1303impl From<Dimension> for TrackSizingFunction {
1304 fn from(input: Dimension) -> Self {
1305 Self { min: input.into(), max: input.into() }
1306 }
1307}
1308
1309#[cfg(feature = "parse")]
1310impl FromCss for TrackSizingFunction {
1311 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
1312 if let Ok(value) = parser.try_parse(|parser| {
1314 parser.expect_function_matching("minmax")?;
1315 parser.parse_nested_block(|parser| {
1316 let min = MinTrackSizingFunction::from_css(parser)?;
1317 parser.expect_comma()?;
1318 let max = MaxTrackSizingFunction::from_css(parser)?;
1319
1320 Ok(Self { min, max })
1321 })
1322 }) {
1323 return Ok(value);
1324 }
1325
1326 let max = MaxTrackSizingFunction::from_css(parser)?;
1328 let min = max.into();
1329 Ok(Self { min, max })
1330 }
1331}
1332
1333#[cfg(feature = "parse")]
1334from_str_from_css!(TrackSizingFunction);
1335
1336#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1341#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1342pub enum RepetitionCount {
1343 AutoFill,
1346 AutoFit,
1349 Count(u16),
1351}
1352impl From<u16> for RepetitionCount {
1353 fn from(value: u16) -> Self {
1354 Self::Count(value)
1355 }
1356}
1357
1358#[derive(Debug)]
1361pub struct InvalidStringRepetitionValue;
1362#[cfg(feature = "std")]
1363impl std::error::Error for InvalidStringRepetitionValue {}
1364impl core::fmt::Display for InvalidStringRepetitionValue {
1365 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1366 f.write_str("&str can only be converted to GridTrackRepetition if it's value is 'auto-fit' or 'auto-fill'")
1367 }
1368}
1369impl TryFrom<&str> for RepetitionCount {
1370 type Error = InvalidStringRepetitionValue;
1371 fn try_from(value: &str) -> Result<Self, InvalidStringRepetitionValue> {
1372 match value {
1373 "auto-fit" => Ok(Self::AutoFit),
1374 "auto-fill" => Ok(Self::AutoFill),
1375 _ => Err(InvalidStringRepetitionValue),
1376 }
1377 }
1378}
1379
1380#[cfg(feature = "parse")]
1381impl FromCss for RepetitionCount {
1382 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
1383 match parser.next()?.clone() {
1384 Token::Number { int_value: Some(value), .. } if value.is_positive() => Ok(Self::Count(value as _)),
1385 Token::Ident(ident) if ident == "auto-fit" => Ok(Self::AutoFit),
1386 Token::Ident(ident) if ident == "auto-fill" => Ok(Self::AutoFill),
1387 token => Err(parser.new_unexpected_token_error(token))?,
1388 }
1389 }
1390}
1391#[cfg(feature = "parse")]
1392from_str_from_css!(RepetitionCount);
1393
1394#[derive(Clone, PartialEq, Debug)]
1396#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1397pub struct GridTemplateRepetition<S: CheapCloneStr> {
1398 pub count: RepetitionCount,
1400 pub tracks: Vec<TrackSizingFunction>,
1402 pub line_names: Vec<Vec<S>>,
1404}
1405
1406#[rustfmt::skip]
1407impl<S: CheapCloneStr> GenericRepetition for &'_ GridTemplateRepetition<S> {
1408 type CustomIdent = S;
1409 type RepetitionTrackList<'a> = core::iter::Copied<core::slice::Iter<'a, TrackSizingFunction>> where Self: 'a;
1410 type TemplateLineNames<'a> = core::iter::Map<core::slice::Iter<'a, Vec<S>>, fn(&Vec<S>) -> core::slice::Iter<'_, S>> where Self: 'a;
1411 #[inline(always)]
1412 fn count(&self) -> RepetitionCount {
1413 self.count
1414 }
1415 #[inline(always)]
1416 fn track_count(&self) -> u16 {
1417 self.tracks.len() as u16
1418 }
1419 #[inline(always)]
1420 fn tracks(&self) -> Self::RepetitionTrackList<'_> {
1421 self.tracks.iter().copied()
1422 }
1423 #[inline(always)]
1424 fn lines_names(&self) -> Self::TemplateLineNames<'_> {
1425 self.line_names.iter().map(|names| names.iter())
1426 }
1427}
1428
1429#[derive(Clone, PartialEq, Debug)]
1434#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1435pub enum GridTemplateComponent<S: CheapCloneStr> {
1436 Single(TrackSizingFunction),
1438 Repeat(GridTemplateRepetition<S>),
1441}
1442
1443impl<S: CheapCloneStr> GridTemplateComponent<S> {
1444 pub fn as_component_ref(&self) -> GenericGridTemplateComponent<S, &GridTemplateRepetition<S>> {
1446 match self {
1447 GridTemplateComponent::Single(size) => GenericGridTemplateComponent::Single(*size),
1448 GridTemplateComponent::Repeat(repetition) => GenericGridTemplateComponent::Repeat(repetition),
1449 }
1450 }
1451}
1452
1453impl<S: CheapCloneStr> GridTemplateComponent<S> {
1454 pub fn is_auto_repetition(&self) -> bool {
1456 matches!(
1457 self,
1458 Self::Repeat(GridTemplateRepetition { count: RepetitionCount::AutoFit | RepetitionCount::AutoFill, .. })
1459 )
1460 }
1461}
1462impl<S: CheapCloneStr> TaffyAuto for GridTemplateComponent<S> {
1463 const AUTO: Self = Self::Single(TrackSizingFunction::AUTO);
1464}
1465impl<S: CheapCloneStr> TaffyMinContent for GridTemplateComponent<S> {
1466 const MIN_CONTENT: Self = Self::Single(TrackSizingFunction::MIN_CONTENT);
1467}
1468impl<S: CheapCloneStr> TaffyMaxContent for GridTemplateComponent<S> {
1469 const MAX_CONTENT: Self = Self::Single(TrackSizingFunction::MAX_CONTENT);
1470}
1471impl<S: CheapCloneStr> TaffyFitContent for GridTemplateComponent<S> {
1472 fn fit_content(argument: LengthPercentage) -> Self {
1473 Self::Single(TrackSizingFunction::fit_content(argument))
1474 }
1475}
1476impl<S: CheapCloneStr> TaffyZero for GridTemplateComponent<S> {
1477 const ZERO: Self = Self::Single(TrackSizingFunction::ZERO);
1478}
1479impl<S: CheapCloneStr> FromLength for GridTemplateComponent<S> {
1480 fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
1481 Self::Single(TrackSizingFunction::from_length(value))
1482 }
1483}
1484impl<S: CheapCloneStr> FromPercent for GridTemplateComponent<S> {
1485 fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
1486 Self::Single(TrackSizingFunction::from_percent(percent))
1487 }
1488}
1489impl<S: CheapCloneStr> FromFr for GridTemplateComponent<S> {
1490 fn from_fr<Input: Into<f32> + Copy>(flex: Input) -> Self {
1491 Self::Single(TrackSizingFunction::from_fr(flex))
1492 }
1493}
1494impl<S: CheapCloneStr> From<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>> for GridTemplateComponent<S> {
1495 fn from(input: MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>) -> Self {
1496 Self::Single(input)
1497 }
1498}
1499
1500#[cfg(feature = "parse")]
1501impl<S: CheapCloneStr> FromCss for GridTemplateComponent<S> {
1502 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
1503 if let Ok(value) = parser.try_parse(|parser| {
1505 parser.expect_function_matching("repeat")?;
1506 parser.parse_nested_block(|parser| {
1507 let count = RepetitionCount::from_css(parser)?;
1508 parser.expect_comma()?;
1509 let tracks = GridTemplateTracks::<S, TrackSizingFunction>::from_css(parser)?;
1510
1511 Ok(Self::Repeat(GridTemplateRepetition { count, tracks: tracks.tracks, line_names: tracks.line_names }))
1512 })
1513 }) {
1514 return Ok(value);
1515 }
1516
1517 let track_sizing_function = TrackSizingFunction::from_css(parser)?;
1519 Ok(Self::Single(track_sizing_function))
1520 }
1521}
1522#[cfg(feature = "parse")]
1523impl<S: CheapCloneStr> core::str::FromStr for GridTemplateComponent<S> {
1524 type Err = ParseError;
1525 fn from_str(input: &str) -> Result<Self, Self::Err> {
1526 parse_css_str_entirely(input)
1527 }
1528}
1529
1530#[derive(Clone, PartialEq, Debug)]
1531#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1532#[doc(hidden)]
1533pub struct GridTemplateTracks<S: CheapCloneStr, Track> {
1534 pub tracks: Vec<Track>,
1536 pub line_names: Vec<Vec<S>>,
1538}
1539
1540impl<S: CheapCloneStr, Track> Default for GridTemplateTracks<S, Track> {
1541 fn default() -> Self {
1542 Self { tracks: Vec::new(), line_names: Vec::new() }
1543 }
1544}
1545
1546#[cfg(feature = "parse")]
1547impl<S: CheapCloneStr, Track: FromCss + Debug> FromCss for GridTemplateTracks<S, Track> {
1548 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
1549 fn try_parse_line_names<'i, S: CheapCloneStr>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Vec<S>> {
1550 parser.try_parse(|parser| {
1551 parser.expect_square_bracket_block()?;
1552 parser.parse_nested_block(|parser| {
1553 let mut line_names = Vec::new();
1554 while !parser.is_exhausted() {
1555 line_names.push(S::from(parser.expect_ident_cloned()?.as_ref()));
1556 }
1557 Ok(line_names)
1558 })
1559 })
1560 }
1561
1562 let mut tracks = Self::default();
1563 if let Ok(line_names) = try_parse_line_names(parser) {
1564 tracks.line_names.push(line_names);
1565 }
1566
1567 while !parser.is_exhausted() {
1568 tracks.tracks.push(Track::from_css(parser)?);
1569 if let Ok(line_names) = try_parse_line_names(parser) {
1570 tracks.line_names.push(line_names);
1571 }
1572 }
1573
1574 if tracks.tracks.is_empty() {
1575 return Err(parser.new_error(cssparser::BasicParseErrorKind::EndOfInput));
1576 }
1577
1578 Ok(tracks)
1579 }
1580}
1581#[cfg(feature = "parse")]
1582impl<S: CheapCloneStr, Track: FromCss + Debug> core::str::FromStr for GridTemplateTracks<S, Track> {
1583 type Err = ParseError;
1584 fn from_str(input: &str) -> Result<Self, Self::Err> {
1585 parse_css_str_entirely(input)
1586 }
1587}
1588
1589#[derive(Default)]
1590#[doc(hidden)]
1591pub struct GridAutoTracks(pub Vec<TrackSizingFunction>);
1592
1593#[cfg(feature = "parse")]
1594impl FromCss for GridAutoTracks {
1595 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
1596 let mut tracks = Self::default();
1597 while !parser.is_exhausted() {
1598 tracks.0.push(TrackSizingFunction::from_css(parser)?);
1599 }
1600 if tracks.0.is_empty() {
1601 return Err(parser.new_error(cssparser::BasicParseErrorKind::EndOfInput));
1602 }
1603 Ok(tracks)
1604 }
1605}
1606#[cfg(feature = "parse")]
1607from_str_from_css!(GridAutoTracks);