Coverage for tdom/classnames.py: 100%

19 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-17 19:54 +0000

1def classnames(*args: object) -> str: 

2 """ 

3 Construct a space-separated class string from various inputs. 

4 

5 Accepts strings, lists/tuples of strings, and dicts mapping class names to 

6 boolean values. Ignores None and False values. 

7 

8 Examples: 

9 classnames("btn", "btn-primary") -> "btn btn-primary" 

10 classnames("btn", {"btn-primary": True, "disabled": False}) -> "btn btn-primary" 

11 classnames(["btn", "btn-primary"], {"disabled": True}) -> "btn btn-primary disabled" 

12 classnames("btn", None, False, "active") -> "btn active" 

13 

14 Args: 

15 *args: Variable length argument list containing strings, lists/tuples, 

16 or dicts. 

17 

18 Returns: 

19 A single string with class names separated by spaces. 

20 """ 

21 classes: list[str] = [] 

22 # Use a queue to process arguments iteratively, preserving order. 

23 queue = list(args) 

24 

25 while queue: 

26 arg = queue.pop(0) 

27 

28 if not arg: # Handles None, False, empty strings/lists/dicts 

29 continue 

30 

31 if isinstance(arg, str): 

32 classes.append(arg) 

33 elif isinstance(arg, dict): 

34 for key, value in arg.items(): 

35 if value: 

36 classes.append(key) 

37 elif isinstance(arg, (list, tuple)): 

38 # Add items to the front of the queue to process them next, in order. 

39 queue[0:0] = arg 

40 elif isinstance(arg, bool): 

41 pass # Explicitly ignore booleans not in a dict 

42 else: 

43 raise ValueError(f"Invalid class argument type: {type(arg).__name__}") 

44 

45 # Filter out empty strings and join the result. 

46 return " ".join(stripped for c in classes if (stripped := c.strip()))