tailwind_rs_core/utilities/
logical_properties.rs1use crate::classes::ClassBuilder;
7use crate::utilities::spacing::SpacingValue;
8use serde::{Deserialize, Serialize};
9use std::fmt;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub enum LogicalDirection {
14 InlineStart,
16 InlineEnd,
18 BlockStart,
20 BlockEnd,
22}
23
24impl fmt::Display for LogicalDirection {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 match self {
27 LogicalDirection::InlineStart => write!(f, "inline-start"),
28 LogicalDirection::InlineEnd => write!(f, "inline-end"),
29 LogicalDirection::BlockStart => write!(f, "block-start"),
30 LogicalDirection::BlockEnd => write!(f, "block-end"),
31 }
32 }
33}
34
35pub trait LogicalPropertiesUtilities {
37 fn margin_inline_start(self, value: SpacingValue) -> Self;
39 fn margin_inline_end(self, value: SpacingValue) -> Self;
41 fn margin_block_start(self, value: SpacingValue) -> Self;
43 fn margin_block_end(self, value: SpacingValue) -> Self;
45 fn padding_inline_start(self, value: SpacingValue) -> Self;
47 fn padding_inline_end(self, value: SpacingValue) -> Self;
49 fn padding_block_start(self, value: SpacingValue) -> Self;
51 fn padding_block_end(self, value: SpacingValue) -> Self;
53 fn border_inline_start(self, value: SpacingValue) -> Self;
55 fn border_inline_end(self, value: SpacingValue) -> Self;
57 fn border_block_start(self, value: SpacingValue) -> Self;
59 fn border_block_end(self, value: SpacingValue) -> Self;
61 fn inset_inline_start(self, value: SpacingValue) -> Self;
63 fn inset_inline_end(self, value: SpacingValue) -> Self;
65 fn inset_block_start(self, value: SpacingValue) -> Self;
67 fn inset_block_end(self, value: SpacingValue) -> Self;
69}
70
71impl LogicalPropertiesUtilities for ClassBuilder {
72 fn margin_inline_start(self, value: SpacingValue) -> Self {
73 let class_name = format!("ms-{}", value.to_string());
74 self.class(class_name)
75 }
76
77 fn margin_inline_end(self, value: SpacingValue) -> Self {
78 let class_name = format!("me-{}", value.to_string());
79 self.class(class_name)
80 }
81
82 fn margin_block_start(self, value: SpacingValue) -> Self {
83 let class_name = format!("mt-{}", value.to_string());
84 self.class(class_name)
85 }
86
87 fn margin_block_end(self, value: SpacingValue) -> Self {
88 let class_name = format!("mb-{}", value.to_string());
89 self.class(class_name)
90 }
91
92 fn padding_inline_start(self, value: SpacingValue) -> Self {
93 let class_name = format!("ps-{}", value.to_string());
94 self.class(class_name)
95 }
96
97 fn padding_inline_end(self, value: SpacingValue) -> Self {
98 let class_name = format!("pe-{}", value.to_string());
99 self.class(class_name)
100 }
101
102 fn padding_block_start(self, value: SpacingValue) -> Self {
103 let class_name = format!("pt-{}", value.to_string());
104 self.class(class_name)
105 }
106
107 fn padding_block_end(self, value: SpacingValue) -> Self {
108 let class_name = format!("pb-{}", value.to_string());
109 self.class(class_name)
110 }
111
112 fn border_inline_start(self, value: SpacingValue) -> Self {
113 let class_name = format!("border-s-{}", value.to_string());
114 self.class(class_name)
115 }
116
117 fn border_inline_end(self, value: SpacingValue) -> Self {
118 let class_name = format!("border-e-{}", value.to_string());
119 self.class(class_name)
120 }
121
122 fn border_block_start(self, value: SpacingValue) -> Self {
123 let class_name = format!("border-t-{}", value.to_string());
124 self.class(class_name)
125 }
126
127 fn border_block_end(self, value: SpacingValue) -> Self {
128 let class_name = format!("border-b-{}", value.to_string());
129 self.class(class_name)
130 }
131
132 fn inset_inline_start(self, value: SpacingValue) -> Self {
133 let class_name = format!("start-{}", value.to_string());
134 self.class(class_name)
135 }
136
137 fn inset_inline_end(self, value: SpacingValue) -> Self {
138 let class_name = format!("end-{}", value.to_string());
139 self.class(class_name)
140 }
141
142 fn inset_block_start(self, value: SpacingValue) -> Self {
143 let class_name = format!("top-{}", value.to_string());
144 self.class(class_name)
145 }
146
147 fn inset_block_end(self, value: SpacingValue) -> Self {
148 let class_name = format!("bottom-{}", value.to_string());
149 self.class(class_name)
150 }
151}
152
153pub trait LogicalPropertiesConvenience {
155 fn margin_inline_start_4(self) -> Self;
157 fn margin_inline_end_4(self) -> Self;
159 fn padding_inline_start_4(self) -> Self;
161 fn padding_inline_end_4(self) -> Self;
163 fn border_inline_start_2(self) -> Self;
165 fn border_inline_end_2(self) -> Self;
167 fn border_inline_start_1(self) -> Self;
169 fn border_inline_end_1(self) -> Self;
171}
172
173impl LogicalPropertiesConvenience for ClassBuilder {
174 fn margin_inline_start_4(self) -> Self {
175 self.margin_inline_start(SpacingValue::Integer(4))
176 }
177
178 fn margin_inline_end_4(self) -> Self {
179 self.margin_inline_end(SpacingValue::Integer(4))
180 }
181
182 fn padding_inline_start_4(self) -> Self {
183 self.padding_inline_start(SpacingValue::Integer(4))
184 }
185
186 fn padding_inline_end_4(self) -> Self {
187 self.padding_inline_end(SpacingValue::Integer(4))
188 }
189
190 fn border_inline_start_2(self) -> Self {
191 self.border_inline_start(SpacingValue::Integer(2))
192 }
193
194 fn border_inline_end_2(self) -> Self {
195 self.border_inline_end(SpacingValue::Integer(2))
196 }
197
198 fn border_inline_start_1(self) -> Self {
199 self.border_inline_start(SpacingValue::Integer(1))
200 }
201
202 fn border_inline_end_1(self) -> Self {
203 self.border_inline_end(SpacingValue::Integer(1))
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210 use crate::classes::ClassBuilder;
211 use crate::utilities::spacing::SpacingValue;
212
213 #[test]
214 fn test_logical_direction_enum_values() {
215 assert_eq!(LogicalDirection::InlineStart.to_string(), "inline-start");
216 assert_eq!(LogicalDirection::InlineEnd.to_string(), "inline-end");
217 assert_eq!(LogicalDirection::BlockStart.to_string(), "block-start");
218 assert_eq!(LogicalDirection::BlockEnd.to_string(), "block-end");
219 }
220
221 #[test]
222 fn test_logical_properties_utilities() {
223 let classes = ClassBuilder::new()
224 .margin_inline_start(SpacingValue::Integer(4))
225 .margin_inline_end(SpacingValue::Integer(4))
226 .padding_inline_start(SpacingValue::Integer(2))
227 .padding_inline_end(SpacingValue::Integer(2))
228 .border_inline_start(SpacingValue::Integer(1))
229 .border_inline_end(SpacingValue::Integer(1));
230
231 let result = classes.build();
232 assert!(result.contains("ms-4"));
233 assert!(result.contains("me-4"));
234 assert!(result.contains("ps-2"));
235 assert!(result.contains("pe-2"));
236 assert!(result.contains("border-s-1"));
237 assert!(result.contains("border-e-1"));
238 }
239
240 #[test]
241 fn test_logical_properties_convenience() {
242 let classes = ClassBuilder::new()
243 .margin_inline_start_4()
244 .margin_inline_end_4()
245 .padding_inline_start_4()
246 .padding_inline_end_4()
247 .border_inline_start_2()
248 .border_inline_end_2();
249
250 let result = classes.build();
251 assert!(result.contains("ms-4"));
252 assert!(result.contains("me-4"));
253 assert!(result.contains("ps-4"));
254 assert!(result.contains("pe-4"));
255 assert!(result.contains("border-s-2"));
256 assert!(result.contains("border-e-2"));
257 }
258
259 #[test]
260 fn test_logical_properties_serialization() {
261 let direction = LogicalDirection::InlineStart;
262 let serialized = serde_json::to_string(&direction).unwrap();
263 let deserialized: LogicalDirection = serde_json::from_str(&serialized).unwrap();
264 assert_eq!(direction, deserialized);
265 }
266
267 #[test]
268 fn test_logical_properties_comprehensive_usage() {
269 let classes = ClassBuilder::new()
270 .margin_inline_start_4()
271 .margin_inline_end_4()
272 .padding_inline_start_2()
273 .padding_inline_end_2()
274 .border_inline_start_1()
275 .border_inline_end_1();
276
277 let result = classes.build();
278 assert!(result.contains("ms-4"));
279 assert!(result.contains("me-4"));
280 assert!(result.contains("ps-2"));
281 assert!(result.contains("pe-2"));
282 assert!(result.contains("border-s-1"));
283 assert!(result.contains("border-e-1"));
284 }
285
286 #[test]
287 fn test_logical_properties_with_different_spacing_values() {
288 let classes = ClassBuilder::new()
289 .margin_inline_start(SpacingValue::Integer(8))
290 .margin_inline_end(SpacingValue::Integer(12))
291 .padding_inline_start(SpacingValue::Integer(6))
292 .padding_inline_end(SpacingValue::Integer(10));
293
294 let result = classes.build();
295 assert!(result.contains("ms-8"));
296 assert!(result.contains("me-12"));
297 assert!(result.contains("ps-6"));
298 assert!(result.contains("pe-10"));
299 }
300
301 #[test]
302 fn test_logical_properties_block_directions() {
303 let classes = ClassBuilder::new()
304 .margin_block_start(SpacingValue::Integer(4))
305 .margin_block_end(SpacingValue::Integer(4))
306 .padding_block_start(SpacingValue::Integer(2))
307 .padding_block_end(SpacingValue::Integer(2));
308
309 let result = classes.build();
310 assert!(result.contains("mt-4"));
311 assert!(result.contains("mb-4"));
312 assert!(result.contains("pt-2"));
313 assert!(result.contains("pb-2"));
314 }
315
316 #[test]
317 fn test_logical_properties_inset() {
318 let classes = ClassBuilder::new()
319 .inset_inline_start(SpacingValue::Integer(4))
320 .inset_inline_end(SpacingValue::Integer(4))
321 .inset_block_start(SpacingValue::Integer(2))
322 .inset_block_end(SpacingValue::Integer(2));
323
324 let result = classes.build();
325 assert!(result.contains("start-4"));
326 assert!(result.contains("end-4"));
327 assert!(result.contains("top-2"));
328 assert!(result.contains("bottom-2"));
329 }
330}