/
GridList.tsx
132 lines (121 loc) · 4.5 KB
/
GridList.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import type { HTMLAttributes, ReactNode } from "react";
import { Children, forwardRef } from "react";
import { GridListCell } from "./GridListCell";
import type { GridListSize, UseGridListOptions } from "./useGridList";
import {
DEFAULT_GRID_LIST_MAX_CELL_SIZE,
DEFAULT_GRID_LIST_PADDING,
GridListSizeProvider,
useGridList,
} from "./useGridList";
/**
* The children render function that will be provided the current grid list size
* object and should return renderable elements.
*
* Note: The first time this is called, the `columns` and `cellWidth` will be
* the `defaultSize`. Once the `GridList` has been fully mounted in the DOM, it
* will begin the sizing calculations and update with the "real" values. This
* doesn't cause any problems if you are only rendering client side, but it
* might mess up server-side rendering, so it is recommended to update the
* `defaultSize` when server-side rendering if this can be "known" service-side
* in your app.
*/
export type RenderGridListChildren = (size: GridListSize) => ReactNode;
export interface GridListProps
extends HTMLAttributes<HTMLDivElement>,
UseGridListOptions {
/**
* Boolean if the resize observer should stop tracking width changes within
* the `GridList`. This should normally stay as `false` since tracking width
* changes will allow for dynamic content being added to the list to not mess
* up the grid calculation when the user is on an OS that shows scrollbars.
*/
disableHeightObserver?: boolean;
/**
* Boolean if the resize observer should stop tracking width changes within
* the `GridList`. This should normally stay as `false` since tracking width
* changes will allow for dynamic content being added to the list to not mess
* up the grid calculation when the user is on an OS that shows scrollbars.
*/
disableWidthObserver?: boolean;
/**
* The children to display within the grid list. This can either be a callback
* function that will provide the current calculated width for each cell that
* should return renderable elements or any renderable elements that are sized
* with the `--rmd-cell-width` css variable.
*/
children: ReactNode | RenderGridListChildren;
/**
* Boolean if the current cell sizing should automatically be cloned into each
* child. This will only work if the `children` is renderable element or a
* list of renderable elements that accept the `style` and `className` props.
*/
clone?: boolean;
/**
* Boolean if each child within the `GridList` should be wrapped with the
* `GridListCell` component. This will only work if the `children` is not a
* `function`.
*/
wrapOnly?: boolean;
}
const isRenderFunction = (
children: GridListProps["children"]
): children is RenderGridListChildren => typeof children === "function";
/**
* The `GridList` component is a different way to render a list of data where
* the number of columns is dynamic and based on the max-width for each cell.
* Instead of setting a percentage width to each cell based on the number of
* columns, this will dynamically add columns to fill up the remaining space and
* have each cell grow up to a set max-width. A really good use-case for this is
* displaying a list of images or thumbnails and allowing the user to see a full
* screen preview once selected/clicked.
*/
export const GridList = forwardRef<HTMLDivElement, GridListProps>(
function GridList(
{
style,
className,
children,
clone = false,
wrapOnly = false,
cellMargin,
defaultSize,
maxCellSize = DEFAULT_GRID_LIST_MAX_CELL_SIZE,
containerPadding = DEFAULT_GRID_LIST_PADDING,
disableHeightObserver = false,
disableWidthObserver = false,
...props
},
forwardedRef
) {
const [gridListProps, gridSize] = useGridList({
ref: forwardedRef,
style,
className,
cellMargin,
defaultSize,
maxCellSize,
containerPadding,
disableHeight: disableHeightObserver,
disableWidth: disableWidthObserver,
});
let content: ReactNode = null;
if (isRenderFunction(children)) {
content = children(gridSize);
} else if (clone || wrapOnly) {
content = Children.map(
children,
(child) => child && <GridListCell clone={clone}>{child}</GridListCell>
);
} else {
content = children;
}
return (
<GridListSizeProvider value={gridSize}>
<div {...props} {...gridListProps}>
{content}
</div>
</GridListSizeProvider>
);
}
);