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