tailwind_rs_core/responsive/
flexbox.rs1use super::breakpoints::Breakpoint;
6use super::responsive_values::ResponsiveValue;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub enum FlexDirection {
12 Row,
14 RowReverse,
16 Column,
18 ColumnReverse,
20}
21
22impl FlexDirection {
23 pub fn to_class(&self) -> &'static str {
25 match self {
26 FlexDirection::Row => "flex-row",
27 FlexDirection::RowReverse => "flex-row-reverse",
28 FlexDirection::Column => "flex-col",
29 FlexDirection::ColumnReverse => "flex-col-reverse",
30 }
31 }
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
36pub enum FlexWrap {
37 NoWrap,
39 Wrap,
41 WrapReverse,
43}
44
45impl FlexWrap {
46 pub fn to_class(&self) -> &'static str {
48 match self {
49 FlexWrap::NoWrap => "flex-nowrap",
50 FlexWrap::Wrap => "flex-wrap",
51 FlexWrap::WrapReverse => "flex-wrap-reverse",
52 }
53 }
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
58pub enum JustifyContent {
59 Start,
61 End,
63 Center,
65 Between,
67 Around,
69 Evenly,
71}
72
73impl JustifyContent {
74 pub fn to_class(&self) -> &'static str {
76 match self {
77 JustifyContent::Start => "justify-start",
78 JustifyContent::End => "justify-end",
79 JustifyContent::Center => "justify-center",
80 JustifyContent::Between => "justify-between",
81 JustifyContent::Around => "justify-around",
82 JustifyContent::Evenly => "justify-evenly",
83 }
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
89pub enum AlignItems {
90 Start,
92 End,
94 Center,
96 Baseline,
98 Stretch,
100}
101
102impl AlignItems {
103 pub fn to_class(&self) -> &'static str {
105 match self {
106 AlignItems::Start => "items-start",
107 AlignItems::End => "items-end",
108 AlignItems::Center => "items-center",
109 AlignItems::Baseline => "items-baseline",
110 AlignItems::Stretch => "items-stretch",
111 }
112 }
113}
114
115#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
117pub struct ResponsiveFlex {
118 pub direction: ResponsiveValue<FlexDirection>,
120 pub wrap: ResponsiveValue<FlexWrap>,
122 pub justify: ResponsiveValue<JustifyContent>,
124 pub align: ResponsiveValue<AlignItems>,
126 pub gap: ResponsiveValue<u32>,
128}
129
130impl ResponsiveFlex {
131 pub fn new() -> Self {
133 Self::default()
134 }
135
136 pub fn with_base(direction: FlexDirection, wrap: FlexWrap, justify: JustifyContent, align: AlignItems, gap: u32) -> Self {
138 Self {
139 direction: ResponsiveValue::with_base(direction),
140 wrap: ResponsiveValue::with_base(wrap),
141 justify: ResponsiveValue::with_base(justify),
142 align: ResponsiveValue::with_base(align),
143 gap: ResponsiveValue::with_base(gap),
144 }
145 }
146
147 pub fn set_direction(&mut self, breakpoint: Breakpoint, direction: FlexDirection) {
149 self.direction.set_breakpoint(breakpoint, direction);
150 }
151
152 pub fn set_wrap(&mut self, breakpoint: Breakpoint, wrap: FlexWrap) {
154 self.wrap.set_breakpoint(breakpoint, wrap);
155 }
156
157 pub fn set_justify(&mut self, breakpoint: Breakpoint, justify: JustifyContent) {
159 self.justify.set_breakpoint(breakpoint, justify);
160 }
161
162 pub fn set_align(&mut self, breakpoint: Breakpoint, align: AlignItems) {
164 self.align.set_breakpoint(breakpoint, align);
165 }
166
167 pub fn set_gap(&mut self, breakpoint: Breakpoint, gap: u32) {
169 self.gap.set_breakpoint(breakpoint, gap);
170 }
171
172 pub fn get_direction(&self, breakpoint: Breakpoint) -> Option<FlexDirection> {
174 self.direction.get_breakpoint(breakpoint).copied()
175 }
176
177 pub fn get_wrap(&self, breakpoint: Breakpoint) -> Option<FlexWrap> {
179 self.wrap.get_breakpoint(breakpoint).copied()
180 }
181
182 pub fn get_justify(&self, breakpoint: Breakpoint) -> Option<JustifyContent> {
184 self.justify.get_breakpoint(breakpoint).copied()
185 }
186
187 pub fn get_align(&self, breakpoint: Breakpoint) -> Option<AlignItems> {
189 self.align.get_breakpoint(breakpoint).copied()
190 }
191
192 pub fn get_gap(&self, breakpoint: Breakpoint) -> Option<u32> {
194 self.gap.get_breakpoint(breakpoint).copied()
195 }
196
197 pub fn to_css_classes(&self) -> String {
199 let mut classes = Vec::new();
200
201 let direction_classes = self.direction.to_css_classes(|d| d.to_class().to_string());
203 if !direction_classes.is_empty() {
204 classes.push(direction_classes);
205 }
206
207 let wrap_classes = self.wrap.to_css_classes(|w| w.to_class().to_string());
209 if !wrap_classes.is_empty() {
210 classes.push(wrap_classes);
211 }
212
213 let justify_classes = self.justify.to_css_classes(|j| j.to_class().to_string());
215 if !justify_classes.is_empty() {
216 classes.push(justify_classes);
217 }
218
219 let align_classes = self.align.to_css_classes(|a| a.to_class().to_string());
221 if !align_classes.is_empty() {
222 classes.push(align_classes);
223 }
224
225 let gap_classes = self.gap.to_css_classes(|g| {
227 if *g == 0 {
228 "gap-0".to_string()
229 } else {
230 format!("gap-{}", g)
231 }
232 });
233 if !gap_classes.is_empty() {
234 classes.push(gap_classes);
235 }
236
237 classes.join(" ")
238 }
239
240 pub fn to_css_classes_for_width(&self, screen_width: u32) -> String {
242 let mut classes = Vec::new();
243
244 if let Some(direction) = self.direction.get_for_width(screen_width) {
246 classes.push(direction.to_class().to_string());
247 }
248
249 if let Some(wrap) = self.wrap.get_for_width(screen_width) {
251 classes.push(wrap.to_class().to_string());
252 }
253
254 if let Some(justify) = self.justify.get_for_width(screen_width) {
256 classes.push(justify.to_class().to_string());
257 }
258
259 if let Some(align) = self.align.get_for_width(screen_width) {
261 classes.push(align.to_class().to_string());
262 }
263
264 if let Some(gap) = self.gap.get_for_width(screen_width) {
266 if *gap == 0 {
267 classes.push("gap-0".to_string());
268 } else {
269 classes.push(format!("gap-{}", gap));
270 }
271 }
272
273 classes.join(" ")
274 }
275}
276
277impl Default for ResponsiveFlex {
278 fn default() -> Self {
279 Self {
280 direction: ResponsiveValue::with_base(FlexDirection::Row),
281 wrap: ResponsiveValue::with_base(FlexWrap::NoWrap),
282 justify: ResponsiveValue::with_base(JustifyContent::Start),
283 align: ResponsiveValue::with_base(AlignItems::Stretch),
284 gap: ResponsiveValue::with_base(0),
285 }
286 }
287}
288
289#[cfg(test)]
290mod tests {
291 use super::*;
292
293 #[test]
294 fn test_flex_direction_to_class() {
295 assert_eq!(FlexDirection::Row.to_class(), "flex-row");
296 assert_eq!(FlexDirection::RowReverse.to_class(), "flex-row-reverse");
297 assert_eq!(FlexDirection::Column.to_class(), "flex-col");
298 assert_eq!(FlexDirection::ColumnReverse.to_class(), "flex-col-reverse");
299 }
300
301 #[test]
302 fn test_flex_wrap_to_class() {
303 assert_eq!(FlexWrap::NoWrap.to_class(), "flex-nowrap");
304 assert_eq!(FlexWrap::Wrap.to_class(), "flex-wrap");
305 assert_eq!(FlexWrap::WrapReverse.to_class(), "flex-wrap-reverse");
306 }
307
308 #[test]
309 fn test_justify_content_to_class() {
310 assert_eq!(JustifyContent::Start.to_class(), "justify-start");
311 assert_eq!(JustifyContent::End.to_class(), "justify-end");
312 assert_eq!(JustifyContent::Center.to_class(), "justify-center");
313 assert_eq!(JustifyContent::Between.to_class(), "justify-between");
314 assert_eq!(JustifyContent::Around.to_class(), "justify-around");
315 assert_eq!(JustifyContent::Evenly.to_class(), "justify-evenly");
316 }
317
318 #[test]
319 fn test_align_items_to_class() {
320 assert_eq!(AlignItems::Start.to_class(), "items-start");
321 assert_eq!(AlignItems::End.to_class(), "items-end");
322 assert_eq!(AlignItems::Center.to_class(), "items-center");
323 assert_eq!(AlignItems::Baseline.to_class(), "items-baseline");
324 assert_eq!(AlignItems::Stretch.to_class(), "items-stretch");
325 }
326
327 #[test]
328 fn test_responsive_flex_new() {
329 let flex = ResponsiveFlex::new();
330 assert_eq!(flex.get_direction(Breakpoint::Base), Some(FlexDirection::Row));
331 assert_eq!(flex.get_wrap(Breakpoint::Base), Some(FlexWrap::NoWrap));
332 assert_eq!(flex.get_justify(Breakpoint::Base), Some(JustifyContent::Start));
333 assert_eq!(flex.get_align(Breakpoint::Base), Some(AlignItems::Stretch));
334 assert_eq!(flex.get_gap(Breakpoint::Base), Some(0));
335 }
336
337 #[test]
338 fn test_responsive_flex_with_base() {
339 let flex = ResponsiveFlex::with_base(
340 FlexDirection::Column,
341 FlexWrap::Wrap,
342 JustifyContent::Center,
343 AlignItems::Center,
344 4,
345 );
346
347 assert_eq!(flex.get_direction(Breakpoint::Base), Some(FlexDirection::Column));
348 assert_eq!(flex.get_wrap(Breakpoint::Base), Some(FlexWrap::Wrap));
349 assert_eq!(flex.get_justify(Breakpoint::Base), Some(JustifyContent::Center));
350 assert_eq!(flex.get_align(Breakpoint::Base), Some(AlignItems::Center));
351 assert_eq!(flex.get_gap(Breakpoint::Base), Some(4));
352 }
353
354 #[test]
355 fn test_responsive_flex_set_get() {
356 let mut flex = ResponsiveFlex::new();
357
358 flex.set_direction(Breakpoint::Sm, FlexDirection::Column);
359 flex.set_wrap(Breakpoint::Md, FlexWrap::Wrap);
360 flex.set_justify(Breakpoint::Lg, JustifyContent::Between);
361 flex.set_align(Breakpoint::Xl, AlignItems::Center);
362 flex.set_gap(Breakpoint::Xl2, 8);
363
364 assert_eq!(flex.get_direction(Breakpoint::Sm), Some(FlexDirection::Column));
365 assert_eq!(flex.get_wrap(Breakpoint::Md), Some(FlexWrap::Wrap));
366 assert_eq!(flex.get_justify(Breakpoint::Lg), Some(JustifyContent::Between));
367 assert_eq!(flex.get_align(Breakpoint::Xl), Some(AlignItems::Center));
368 assert_eq!(flex.get_gap(Breakpoint::Xl2), Some(8));
369 }
370
371 #[test]
372 fn test_responsive_flex_to_css_classes() {
373 let mut flex = ResponsiveFlex::new();
374 flex.set_direction(Breakpoint::Sm, FlexDirection::Column);
375 flex.set_justify(Breakpoint::Md, JustifyContent::Center);
376 flex.set_gap(Breakpoint::Lg, 4);
377
378 let classes = flex.to_css_classes();
379 assert!(classes.contains("flex-row"));
380 assert!(classes.contains("sm:flex-col"));
381 assert!(classes.contains("md:justify-center"));
382 assert!(classes.contains("lg:gap-4"));
383 }
384
385 #[test]
386 fn test_responsive_flex_to_css_classes_for_width() {
387 let mut flex = ResponsiveFlex::new();
388 flex.set_direction(Breakpoint::Sm, FlexDirection::Column);
389 flex.set_justify(Breakpoint::Md, JustifyContent::Center);
390 flex.set_gap(Breakpoint::Lg, 4);
391
392 let classes_0 = flex.to_css_classes_for_width(0);
394 assert!(classes_0.contains("flex-row"));
395 assert!(!classes_0.contains("flex-col"));
396 assert!(!classes_0.contains("justify-center"));
397 assert!(!classes_0.contains("gap-4"));
398
399 let classes_640 = flex.to_css_classes_for_width(640);
401 assert!(classes_640.contains("flex-col"));
402 assert!(!classes_640.contains("justify-center"));
403 assert!(!classes_640.contains("gap-4"));
404
405 let classes_768 = flex.to_css_classes_for_width(768);
407 assert!(classes_768.contains("flex-col"));
408 assert!(classes_768.contains("justify-center"));
409 assert!(!classes_768.contains("gap-4"));
410
411 let classes_1024 = flex.to_css_classes_for_width(1024);
413 assert!(classes_1024.contains("flex-col"));
414 assert!(classes_1024.contains("justify-center"));
415 assert!(classes_1024.contains("gap-4"));
416 }
417}