Grid.tsx 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. import React from 'react'
  2. import styled from '@emotion/styled'
  3. import useResizeObserver from 'use-resize-observer'
  4. import { sizes, breakpoints } from '../../theme'
  5. import { MIN_VIDEO_PREVIEW_WIDTH } from '../VideoPreview'
  6. const toPx = (n: number | string) => (typeof n === 'number' ? `${n}px` : n)
  7. type ContainerProps = Required<Pick<GridProps, 'gap' | 'maxColumns' | 'minWidth' | 'repeat'>>
  8. const Container = styled.div<ContainerProps>`
  9. display: grid;
  10. gap: ${(props) => toPx(props.gap)};
  11. grid-template-columns: repeat(
  12. auto-${(props) => props.repeat},
  13. minmax(min(${(props) => toPx(props.minWidth)}, 100%), 1fr)
  14. );
  15. @media (min-width: ${toPx(breakpoints.xlarge)}) {
  16. grid-template-columns: repeat(${(props) => props.maxColumns}, 1fr);
  17. }
  18. `
  19. type GridProps = {
  20. gap?: number | string
  21. className?: string
  22. maxColumns?: number
  23. minWidth?: number | string
  24. repeat?: 'fit' | 'fill'
  25. onResize?: (sizes: number[]) => void
  26. }
  27. const Grid: React.FC<GridProps> = ({
  28. className,
  29. gap = sizes(6),
  30. onResize,
  31. repeat = 'fill',
  32. maxColumns = 6,
  33. minWidth = MIN_VIDEO_PREVIEW_WIDTH,
  34. ...props
  35. }) => {
  36. const { ref: gridRef } = useResizeObserver<HTMLDivElement>({
  37. onResize: () => {
  38. if (onResize && gridRef.current) {
  39. const computedStyles = window.getComputedStyle(gridRef.current)
  40. const columnSizes = computedStyles.gridTemplateColumns.split(' ').map(parseFloat)
  41. onResize(columnSizes)
  42. }
  43. },
  44. })
  45. return (
  46. <Container
  47. {...props}
  48. className={className}
  49. ref={gridRef}
  50. gap={gap}
  51. minWidth={minWidth}
  52. maxColumns={maxColumns}
  53. repeat={repeat}
  54. />
  55. )
  56. }
  57. type BreakpointsToMatchGridArg = {
  58. breakpoints: number
  59. minItemWidth: number
  60. gridColumnGap?: number
  61. viewportContainerDifference?: number
  62. }
  63. // This will generate the Array of breakpoints at which the Grid adds one element
  64. export function breakpointsOfGrid({
  65. breakpoints = 0,
  66. minItemWidth,
  67. gridColumnGap = 0,
  68. viewportContainerDifference = 0,
  69. }: BreakpointsToMatchGridArg) {
  70. return Array(breakpoints)
  71. .fill(null)
  72. .map((_, n) => (n + 1) * minItemWidth + n * gridColumnGap + viewportContainerDifference)
  73. }
  74. export default Grid