Skip to content

Commit 56ecc19

Browse files
committed
feat(utils): added useGridList hook
It's helpful to be able to use the grid list API without needing to use the `GridList` component so this hook was created. This also ended up updating to the new `useResizeObserver` API as well.
1 parent b8f638a commit 56ecc19

5 files changed

Lines changed: 357 additions & 171 deletions

File tree

packages/utils/src/layout/GridList.tsx

Lines changed: 23 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,14 @@
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";
153
import 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
*/
4125
export 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");
12969
const 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

Comments
 (0)