Document the component
This commit is contained in:
		
					parent
					
						
							
								8d46687a54
							
						
					
				
			
			
				commit
				
					
						d852e33413
					
				
			
		
					 1 changed files with 60 additions and 42 deletions
				
			
		| 
						 | 
					@ -148,18 +148,27 @@ interface DragState {
 | 
				
			||||||
  cursorY: number;
 | 
					  cursorY: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * An interactive, animated grid of video tiles.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
export const NewVideoGrid: FC<Props> = ({
 | 
					export const NewVideoGrid: FC<Props> = ({
 | 
				
			||||||
  items,
 | 
					  items,
 | 
				
			||||||
  disableAnimations,
 | 
					  disableAnimations,
 | 
				
			||||||
  children,
 | 
					  children,
 | 
				
			||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
 | 
					  // Overview: This component lays out tiles by rendering an invisible template
 | 
				
			||||||
 | 
					  // grid of "slots" for tiles to go in. Once rendered, it uses the DOM API to
 | 
				
			||||||
 | 
					  // get the dimensions of each slot, feeding these numbers back into
 | 
				
			||||||
 | 
					  // react-spring to let the actual tiles move freely atop the template.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // To know when the rendered grid becomes consistent with the layout we've
 | 
				
			||||||
 | 
					  // requested, we give it a data-generation attribute which holds the ID of the
 | 
				
			||||||
 | 
					  // most recently rendered generation of the grid, and watch it with a
 | 
				
			||||||
 | 
					  // MutationObserver.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [slotGrid, setSlotGrid] = useState<HTMLDivElement | null>(null);
 | 
					  const [slotGrid, setSlotGrid] = useState<HTMLDivElement | null>(null);
 | 
				
			||||||
  const [slotGridGeneration, setSlotGridGeneration] = useState(0);
 | 
					  const [slotGridGeneration, setSlotGridGeneration] = useState(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [gridRef1, gridBounds] = useMeasure();
 | 
					 | 
				
			||||||
  const gridRef2 = useRef<HTMLDivElement | null>(null);
 | 
					 | 
				
			||||||
  const gridRef = useMergedRefs(gridRef1, gridRef2);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    if (slotGrid !== null) {
 | 
					    if (slotGrid !== null) {
 | 
				
			||||||
      setSlotGridGeneration(
 | 
					      setSlotGridGeneration(
 | 
				
			||||||
| 
						 | 
					@ -179,6 +188,10 @@ export const NewVideoGrid: FC<Props> = ({
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }, [slotGrid, setSlotGridGeneration]);
 | 
					  }, [slotGrid, setSlotGridGeneration]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [gridRef1, gridBounds] = useMeasure();
 | 
				
			||||||
 | 
					  const gridRef2 = useRef<HTMLDivElement | null>(null);
 | 
				
			||||||
 | 
					  const gridRef = useMergedRefs(gridRef1, gridRef2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const slotRects = useMemo(() => {
 | 
					  const slotRects = useMemo(() => {
 | 
				
			||||||
    if (slotGrid === null) return [];
 | 
					    if (slotGrid === null) return [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -214,7 +227,7 @@ export const NewVideoGrid: FC<Props> = ({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [tiles] = useReactiveState<Tile[]>(
 | 
					  const [tiles] = useReactiveState<Tile[]>(
 | 
				
			||||||
    (prevTiles) => {
 | 
					    (prevTiles) => {
 | 
				
			||||||
      // If React hasn't yet rendered the current generation of the layout, skip
 | 
					      // If React hasn't yet rendered the current generation of the grid, skip
 | 
				
			||||||
      // the update, because grid and slotRects will be out of sync
 | 
					      // the update, because grid and slotRects will be out of sync
 | 
				
			||||||
      if (slotGridGeneration !== grid?.generation) return prevTiles ?? [];
 | 
					      if (slotGridGeneration !== grid?.generation) return prevTiles ?? [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -264,43 +277,6 @@ export const NewVideoGrid: FC<Props> = ({
 | 
				
			||||||
    // react-spring's types are bugged and can't infer the spring type
 | 
					    // react-spring's types are bugged and can't infer the spring type
 | 
				
			||||||
  ) as unknown as [TransitionFn<Tile, TileSpring>, SpringRef<TileSpring>];
 | 
					  ) as unknown as [TransitionFn<Tile, TileSpring>, SpringRef<TileSpring>];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const slotGridStyle = useMemo(() => {
 | 
					 | 
				
			||||||
    if (grid === null) return {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const areas = new Array<(number | null)[]>(
 | 
					 | 
				
			||||||
      Math.ceil(grid.cells.length / grid.columns)
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
    for (let i = 0; i < areas.length; i++)
 | 
					 | 
				
			||||||
      areas[i] = new Array<number | null>(grid.columns).fill(null);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let slotId = 0;
 | 
					 | 
				
			||||||
    for (let i = 0; i < grid.cells.length; i++) {
 | 
					 | 
				
			||||||
      const cell = grid.cells[i];
 | 
					 | 
				
			||||||
      if (cell?.origin) {
 | 
					 | 
				
			||||||
        const slotEnd = i + cell.columns - 1 + grid.columns * (cell.rows - 1);
 | 
					 | 
				
			||||||
        forEachCellInArea(
 | 
					 | 
				
			||||||
          i,
 | 
					 | 
				
			||||||
          slotEnd,
 | 
					 | 
				
			||||||
          grid,
 | 
					 | 
				
			||||||
          (_c, j) => (areas[row(j, grid)][column(j, grid)] = slotId)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        slotId++;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return {
 | 
					 | 
				
			||||||
      gridTemplateAreas: areas
 | 
					 | 
				
			||||||
        .map(
 | 
					 | 
				
			||||||
          (row) =>
 | 
					 | 
				
			||||||
            `'${row
 | 
					 | 
				
			||||||
              .map((slotId) => (slotId === null ? "." : `s${slotId}`))
 | 
					 | 
				
			||||||
              .join(" ")}'`
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        .join(" "),
 | 
					 | 
				
			||||||
      gridTemplateColumns: `repeat(${columns}, 1fr)`,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }, [grid, columns]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const animateDraggedTile = (endOfGesture: boolean) => {
 | 
					  const animateDraggedTile = (endOfGesture: boolean) => {
 | 
				
			||||||
    const { tileId, tileX, tileY, cursorX, cursorY } = dragState.current!;
 | 
					    const { tileId, tileX, tileY, cursorX, cursorY } = dragState.current!;
 | 
				
			||||||
    const tile = tiles.find((t) => t.item.id === tileId)!;
 | 
					    const tile = tiles.find((t) => t.item.id === tileId)!;
 | 
				
			||||||
| 
						 | 
					@ -357,6 +333,11 @@ export const NewVideoGrid: FC<Props> = ({
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Callback for useDrag. We could call useDrag here, but the default
 | 
				
			||||||
 | 
					  // pattern of spreading {...bind()} across the children to bind the gesture
 | 
				
			||||||
 | 
					  // ends up breaking memoization and ruining this component's performance.
 | 
				
			||||||
 | 
					  // Instead, we pass this callback to each tile via a ref, to let them bind the
 | 
				
			||||||
 | 
					  // gesture using the much more sensible ref-based method.
 | 
				
			||||||
  const onTileDrag = (
 | 
					  const onTileDrag = (
 | 
				
			||||||
    tileId: string,
 | 
					    tileId: string,
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -411,6 +392,43 @@ export const NewVideoGrid: FC<Props> = ({
 | 
				
			||||||
    { target: gridRef2 }
 | 
					    { target: gridRef2 }
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const slotGridStyle = useMemo(() => {
 | 
				
			||||||
 | 
					    if (grid === null) return {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const areas = new Array<(number | null)[]>(
 | 
				
			||||||
 | 
					      Math.ceil(grid.cells.length / grid.columns)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    for (let i = 0; i < areas.length; i++)
 | 
				
			||||||
 | 
					      areas[i] = new Array<number | null>(grid.columns).fill(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let slotId = 0;
 | 
				
			||||||
 | 
					    for (let i = 0; i < grid.cells.length; i++) {
 | 
				
			||||||
 | 
					      const cell = grid.cells[i];
 | 
				
			||||||
 | 
					      if (cell?.origin) {
 | 
				
			||||||
 | 
					        const slotEnd = i + cell.columns - 1 + grid.columns * (cell.rows - 1);
 | 
				
			||||||
 | 
					        forEachCellInArea(
 | 
				
			||||||
 | 
					          i,
 | 
				
			||||||
 | 
					          slotEnd,
 | 
				
			||||||
 | 
					          grid,
 | 
				
			||||||
 | 
					          (_c, j) => (areas[row(j, grid)][column(j, grid)] = slotId)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        slotId++;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      gridTemplateAreas: areas
 | 
				
			||||||
 | 
					        .map(
 | 
				
			||||||
 | 
					          (row) =>
 | 
				
			||||||
 | 
					            `'${row
 | 
				
			||||||
 | 
					              .map((slotId) => (slotId === null ? "." : `s${slotId}`))
 | 
				
			||||||
 | 
					              .join(" ")}'`
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .join(" "),
 | 
				
			||||||
 | 
					      gridTemplateColumns: `repeat(${columns}, 1fr)`,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }, [grid, columns]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const slots = useMemo(() => {
 | 
					  const slots = useMemo(() => {
 | 
				
			||||||
    const slots = new Array<ReactNode>(items.length);
 | 
					    const slots = new Array<ReactNode>(items.length);
 | 
				
			||||||
    for (let i = 0; i < items.length; i++)
 | 
					    for (let i = 0; i < items.length; i++)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue