1- import React , {
2- Children ,
3- forwardRef ,
4- HTMLAttributes ,
5- ReactNode ,
6- useCallback ,
7- useRef ,
8- useState ,
9- } from "react" ;
10- import cn from "classnames" ;
1+ import React , { Children , forwardRef , HTMLAttributes , ReactNode } from "react" ;
112
12- import applyRef from "../applyRef" ;
13- import bem from "../bem" ;
14- import { useResizeObserver } from "../sizing/useResizeObserver" ;
153import GridListCell from "./GridListCell" ;
16- import getScrollbarSize from "./scrollbarSize" ;
17- import { GridListSizeProvider , GridListSize } from "./context" ;
18-
19- /**
20- * This is the css variable that is used store the current size of each cell.
21- */
22- export const CELL_SIZE_VAR = "--rmd-cell-size" ;
23-
24- /**
25- * This is the css variable that is used store the current margin of each cell.
26- */
27- export const CELL_MARGIN_VAR = "--rmd-cell-margin" ;
4+ import {
5+ DEFAULT_GRID_LIST_MAX_CELL_SIZE ,
6+ DEFAULT_GRID_LIST_PADDING ,
7+ useGridList ,
8+ UseGridListOptions ,
9+ GridListSize ,
10+ GridListSizeProvider ,
11+ } from "./useGridList" ;
2812
2913/**
3014 * The children render function that will be provided the current grid list size
@@ -40,47 +24,9 @@ export const CELL_MARGIN_VAR = "--rmd-cell-margin";
4024 */
4125export type RenderGridListChildren = ( size : GridListSize ) => ReactNode ;
4226
43- export interface GridListProps extends HTMLAttributes < HTMLDivElement > {
44- /**
45- * An optional margin to apply to each cell as the `CELL_MARGIN_VAR` css
46- * variable only when it is defined. This has to be a number string with a
47- * `px`, `em`, `rem` or `%` suffix or else the grid will break.
48- */
49- cellMargin ?: string ;
50-
51- /**
52- * The max size that each cell can be.
53- */
54- maxCellSize ?: number ;
55-
56- /**
57- * Since the `GridList` requires being fully rendered in the DOM to be able to
58- * correctly calculate the number of `columns` and `cellWidth`, this _might_
59- * cause problems when server-side rendering when using the children renderer
60- * to create a grid list dynamically based on the number of columns. If the
61- * number of columns and default `cellWidth` can be guessed server-side, you
62- * should provide this prop. Otherwise it will be: `{ cellSize; maxCellSize,
63- * columns: -1 }`
64- */
65- defaultSize ?: GridListSize | ( ( ) => GridListSize ) ;
66-
67- /**
68- * This is _normally_ the amount of padding on the grid list item itself to
69- * subtract from the `offsetWidth` since `padding`, `border`, and vertical
70- * scrollbars will be included. If you add a border or change the padding or
71- * add borders to this component, you'll need to update the `containerPadding`
72- * to be the new number.
73- */
74- containerPadding ?: number ;
75-
76- /**
77- * Boolean if the current scrollbar width should no longer be subtracted from
78- * the total width of the grid list. This should only be disabled if your
79- * `containerPadding` is updated to include scrollbar width as well since
80- * it'll mess up the grid on OSes that display scrollbars.
81- */
82- disableScrollbarWidth ?: boolean ;
83-
27+ export interface GridListProps
28+ extends HTMLAttributes < HTMLDivElement > ,
29+ UseGridListOptions {
8430 /**
8531 * Boolean if the resize observer should stop tracking width changes within
8632 * the `GridList`. This should normally stay as `false` since tracking width
@@ -120,12 +66,6 @@ export interface GridListProps extends HTMLAttributes<HTMLDivElement> {
12066 wrapOnly ?: boolean ;
12167}
12268
123- type CSSProperties = React . CSSProperties & {
124- [ CELL_SIZE_VAR ] : string ;
125- [ CELL_MARGIN_VAR ] ?: string ;
126- } ;
127-
128- const block = bem ( "rmd-grid-list" ) ;
12969const isRenderFunction = (
13070 children : GridListProps [ "children" ]
13171) : children is RenderGridListChildren => typeof children === "function" ;
@@ -148,63 +88,25 @@ const GridList = forwardRef<HTMLDivElement, GridListProps>(function GridList(
14888 wrapOnly = false ,
14989 cellMargin,
15090 defaultSize,
151- maxCellSize = 150 ,
152- containerPadding = 16 ,
91+ maxCellSize = DEFAULT_GRID_LIST_MAX_CELL_SIZE ,
92+ containerPadding = DEFAULT_GRID_LIST_PADDING ,
15393 disableHeightObserver = false ,
15494 disableWidthObserver = false ,
15595 ...props
15696 } ,
15797 forwardedRef
15898) {
159- const [ gridSize , setGridSize ] = useState (
160- defaultSize || { columns : - 1 , cellWidth : maxCellSize }
161- ) ;
162- const ref = useRef < HTMLDivElement | null > ( null ) ;
163- const recalculate = useCallback ( ( ) => {
164- if ( ! ref . current ) {
165- return ;
166- }
167-
168- // need to use rect instead of offsetWidth since we need decimal precision
169- // for the width since offsetWidth is basically Math.ceil(width). the
170- // calculations for max columns will be off on high-pixel-density monitors
171- // or some zoom levels.
172- let { width } = ref . current . getBoundingClientRect ( ) ;
173- width -= containerPadding ;
174-
175- // just need to see if there is a scrollbar visible and subtract that width.
176- // don't need decimal precision here since both values will be rounded
177- if ( ref . current . offsetHeight < ref . current . scrollHeight ) {
178- width -= getScrollbarSize ( "width" ) ;
179- }
180-
181- const columns = Math . ceil ( width / maxCellSize ) ;
182- setGridSize ( { cellWidth : width / columns , columns } ) ;
183- } , [ maxCellSize , containerPadding ] ) ;
184-
185- const refHandler = useCallback (
186- ( instance : HTMLDivElement | null ) => {
187- applyRef ( instance , forwardedRef ) ;
188- ref . current = instance ;
189-
190- if ( instance ) {
191- recalculate ( ) ;
192- }
193- } ,
194- [ forwardedRef , recalculate ]
195- ) ;
196-
197- useResizeObserver ( {
99+ const [ gridListProps , gridSize ] = useGridList ( {
100+ ref : forwardedRef ,
101+ style,
102+ className,
103+ cellMargin,
104+ defaultSize,
105+ maxCellSize,
106+ containerPadding,
198107 disableHeight : disableHeightObserver ,
199108 disableWidth : disableWidthObserver ,
200- onResize : recalculate ,
201- target : ref ,
202109 } ) ;
203- const mergedStyle : CSSProperties = {
204- ...style ,
205- [ CELL_SIZE_VAR ] : `${ gridSize . cellWidth } px` ,
206- [ CELL_MARGIN_VAR ] : cellMargin || undefined ,
207- } ;
208110
209111 let content : ReactNode = null ;
210112 if ( isRenderFunction ( children ) ) {
@@ -220,12 +122,7 @@ const GridList = forwardRef<HTMLDivElement, GridListProps>(function GridList(
220122
221123 return (
222124 < GridListSizeProvider value = { gridSize } >
223- < div
224- { ...props }
225- ref = { refHandler }
226- style = { mergedStyle }
227- className = { cn ( block ( ) , className ) }
228- >
125+ < div { ...props } { ...gridListProps } >
229126 { content }
230127 </ div >
231128 </ GridListSizeProvider >
0 commit comments