breakpoints.py 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. """Breakpoints utility."""
  2. from __future__ import annotations
  3. from typing import Dict, Tuple, TypeVar, Union
  4. breakpoints_values = ["30em", "48em", "62em", "80em", "96em"]
  5. breakpoint_names = ["xs", "sm", "md", "lg", "xl"]
  6. def set_breakpoints(values: Tuple[str, str, str, str, str]):
  7. """Overwrite default breakpoint values.
  8. Args:
  9. values: CSS values in order defining the breakpoints of responsive layouts
  10. """
  11. breakpoints_values.clear()
  12. breakpoints_values.extend(values)
  13. K = TypeVar("K")
  14. V = TypeVar("V")
  15. class Breakpoints(Dict[K, V]):
  16. """A responsive styling helper."""
  17. def factorize(self):
  18. """Removes references to breakpoints names and instead replaces them with their corresponding values.
  19. Returns:
  20. The factorized breakpoints.
  21. """
  22. return Breakpoints(
  23. {
  24. (
  25. breakpoints_values[breakpoint_names.index(k)]
  26. if k in breakpoint_names
  27. else ("0px" if k == "initial" else k)
  28. ): v
  29. for k, v in self.items()
  30. if v is not None
  31. }
  32. )
  33. @classmethod
  34. def create(
  35. cls,
  36. custom: Dict[K, V] | None = None,
  37. initial: V | None = None,
  38. xs: V | None = None,
  39. sm: V | None = None,
  40. md: V | None = None,
  41. lg: V | None = None,
  42. xl: V | None = None,
  43. ):
  44. """Create a new instance of the helper. Only provide a custom component OR use named props.
  45. Args:
  46. custom: Custom mapping using CSS values or variables.
  47. initial: Styling when in the inital width
  48. xs: Styling when in the extra-small width
  49. sm: Styling when in the small width
  50. md: Styling when in the medium width
  51. lg: Styling when in the large width
  52. xl: Styling when in the extra-large width
  53. Raises:
  54. ValueError: If both custom and any other named parameters are provided.
  55. Returns:
  56. The responsive mapping.
  57. """
  58. thresholds = [initial, xs, sm, md, lg, xl]
  59. if custom is not None:
  60. if any((threshold is not None for threshold in thresholds)):
  61. raise ValueError("Named props cannot be used with custom thresholds")
  62. return Breakpoints(custom)
  63. else:
  64. return Breakpoints(
  65. {
  66. k: v
  67. for k, v in zip(
  68. ["initial", *breakpoint_names], thresholds, strict=False
  69. )
  70. if v is not None
  71. }
  72. )
  73. breakpoints = Breakpoints.create
  74. T = TypeVar("T")
  75. Responsive = Union[T, Breakpoints[str, T]]