breakpoints.py 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  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 initial 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(["initial", *breakpoint_names], thresholds)
  68. if v is not None
  69. }
  70. )
  71. breakpoints = Breakpoints.create
  72. T = TypeVar("T")
  73. Responsive = Union[T, Breakpoints[str, T]]