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