-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add WaterfallLayout to RAC, and update GridLayout #7729
base: virtualizer-docs
Are you sure you want to change the base?
Conversation
* is true, all rows will have equal heights. | ||
* @default false | ||
*/ | ||
preserveAspectRatio?: boolean, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we think variable row heights or fixed row heights are a better default?
let item = this.virtualizer!.collection.getItem(key); | ||
if (item?.index != null) { | ||
persistedIndices.push(item.index); | ||
if (node.type === 'skeleton') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Skeleton is a bit Spectrum specific for now, but maybe useful in RAC eventually? Hard to pull it out here though without forking the whole layout. Maybe fine to just leave it here unused for now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How much do you think non-spectrum people are likely to use these?
If someone doesn't want to use the skeleton code and it's too much in terms of size, they can also create their own layout, so I'm fine with it staying.
I feel like another common usecase, which would avoid this, is going to be using other virtualizer libraries, such as tanstack.
## API Changes
react-aria-components/react-aria-components:UNSTABLE_TableLayout-UNSTABLE_TableLayout <T> {
- constructor: (ListLayoutOptions) => void
- getContentSize: () => void
- getDropTargetFromPoint: (number, number, (DropTarget) => boolean) => DropTarget | null
- getDropTargetLayoutInfo: (ItemDropTarget) => LayoutInfo
- getItemRect: (Key) => Rect | null
- getLayoutInfo: (Key) => void
- getVisibleLayoutInfos: (Rect) => void
- getVisibleRect: () => Rect
- shouldInvalidate: (Rect, Rect) => boolean
- update: (InvalidationContext<TableLayoutProps>) => void
- updateItemSize: (Key, Size) => void
- useLayoutOptions: () => void
- virtualizer: Virtualizer<{}, any> | null
-} /react-aria-components:UNSTABLE_Virtualizer-UNSTABLE_Virtualizer <O> {
- children: ReactNode
- layout: ILayout<O>
- layoutOptions?: O
-} /react-aria-components:UNSTABLE_ListLayout-UNSTABLE_ListLayout <O = any, T> {
- constructor: (ListLayoutOptions) => void
- getContentSize: () => void
- getDropTargetFromPoint: (number, number, (DropTarget) => boolean) => DropTarget | null
- getDropTargetLayoutInfo: (ItemDropTarget) => LayoutInfo
- getItemRect: (Key) => Rect | null
- getLayoutInfo: (Key) => void
- getVisibleLayoutInfos: (Rect) => void
- getVisibleRect: () => Rect
- shouldInvalidate: (Rect, Rect) => boolean
- update: (InvalidationContext<O>) => void
- updateItemSize: (Key, Size) => void
- virtualizer: Virtualizer<{}, any> | null
-} /react-aria-components:UNSTABLE_GridLayout-UNSTABLE_GridLayout <O = any, T> {
- constructor: (GridLayoutOptions) => void
- getContentSize: () => Size
- getDropTargetFromPoint: (number, number, (DropTarget) => boolean) => DropTarget
- getDropTargetLayoutInfo: (ItemDropTarget) => LayoutInfo
- getItemRect: (Key) => Rect | null
- getLayoutInfo: (Key) => LayoutInfo | null
- getVisibleLayoutInfos: (Rect) => Array<LayoutInfo>
- getVisibleRect: () => Rect
- shouldInvalidate: (Rect, Rect) => boolean
- update: () => void
- updateItemSize: (Key, Size) => boolean
- virtualizer: Virtualizer<{}, any> | null
-} /react-aria-components:ListLayoutOptions ListLayoutOptions {
- dropIndicatorThickness?: number
+ dropIndicatorThickness?: number = 2
estimatedHeadingHeight?: number
estimatedRowHeight?: number
- headingHeight?: number
- loaderHeight?: number
- rowHeight?: number
+ gap?: number = 0
+ headingHeight?: number = 48
+ loaderHeight?: number = 48
+ padding?: number = 0
+ rowHeight?: number = 48
} /react-aria-components:GridLayoutOptions GridLayoutOptions {
dropIndicatorThickness?: number = 2
maxColumns?: number = Infinity
maxItemSize?: Size = Infinity
minItemSize?: Size = 200 x 200
minSpace?: Size = 18 x 18
+ preserveAspectRatio?: boolean = false
} /react-aria-components:TableLayout+TableLayout <T> {
+ constructor: (ListLayoutOptions) => void
+ getContentSize: () => void
+ getDropTargetFromPoint: (number, number, (DropTarget) => boolean) => DropTarget | null
+ getDropTargetLayoutInfo: (ItemDropTarget) => LayoutInfo
+ getLayoutInfo: (Key) => void
+ getVisibleLayoutInfos: (Rect) => void
+ shouldInvalidate: (Rect, Rect) => boolean
+ shouldInvalidateLayoutOptions: (TableLayoutProps, TableLayoutProps) => boolean
+ update: (InvalidationContext<TableLayoutProps>) => void
+ updateItemSize: (Key, Size) => void
+ useLayoutOptions: () => void
+ virtualizer: Virtualizer<{}, any> | null
+} /react-aria-components:Virtualizer+Virtualizer <O> {
+ children: ReactNode
+ layout: ILayout<O>
+ layoutOptions?: O
+} /react-aria-components:ListLayout+ListLayout <O extends ListLayoutOptions = ListLayoutOptions, T> {
+ constructor: (ListLayoutOptions) => void
+ getContentSize: () => void
+ getDropTargetFromPoint: (number, number, (DropTarget) => boolean) => DropTarget | null
+ getDropTargetLayoutInfo: (ItemDropTarget) => LayoutInfo
+ getLayoutInfo: (Key) => void
+ getVisibleLayoutInfos: (Rect) => void
+ shouldInvalidate: (Rect, Rect) => boolean
+ shouldInvalidateLayoutOptions: (ListLayoutOptions, ListLayoutOptions) => boolean
+ update: (InvalidationContext<ListLayoutOptions>) => void
+ updateItemSize: (Key, Size) => void
+ virtualizer: Virtualizer<{}, any> | null
+} /react-aria-components:GridLayout+GridLayout <O extends GridLayoutOptions = GridLayoutOptions, T> {
+ getContentSize: () => Size
+ getDropTargetFromPoint: (number, number, (DropTarget) => boolean) => DropTarget
+ getDropTargetLayoutInfo: (ItemDropTarget) => LayoutInfo
+ getLayoutInfo: (Key) => LayoutInfo
+ getVisibleLayoutInfos: (Rect) => Array<LayoutInfo>
+ shouldInvalidate: (Rect, Rect) => boolean
+ shouldInvalidateLayoutOptions: (GridLayoutOptions, GridLayoutOptions) => boolean
+ update: (InvalidationContext<GridLayoutOptions>) => void
+ updateItemSize: (Key, Size) => void
+ virtualizer: Virtualizer<{}, any> | null
+} /react-aria-components:WaterfallLayout+WaterfallLayout <O extends WaterfallLayoutOptions = WaterfallLayoutOptions, T extends {}> {
+ getContentSize: () => Size
+ getDropTargetFromPoint: (number, number) => DropTarget
+ getDropTargetLayoutInfo: (ItemDropTarget) => LayoutInfo
+ getKeyLeftOf: (Key) => Key | null
+ getKeyRange: (Key, Key) => Array<Key>
+ getKeyRightOf: (Key) => Key | null
+ getLayoutInfo: (Key) => LayoutInfo
+ getVisibleLayoutInfos: (Rect) => Array<LayoutInfo>
+ shouldInvalidate: (Rect, Rect) => boolean
+ shouldInvalidateLayoutOptions: (WaterfallLayoutOptions, WaterfallLayoutOptions) => boolean
+ update: (InvalidationContext<WaterfallLayoutOptions>) => void
+ updateItemSize: (Key, Size) => void
+ virtualizer: Virtualizer<{}, any> | null
+} /react-aria-components:Layout+Layout <O = any, T extends {} = Node<any>> {
+ getContentSize: () => Size
+ getDropTargetLayoutInfo: (ItemDropTarget) => LayoutInfo
+ getLayoutInfo: (Key) => LayoutInfo | null
+ getVisibleLayoutInfos: (Rect) => Array<LayoutInfo>
+ shouldInvalidate: (Rect, Rect) => boolean
+ shouldInvalidateLayoutOptions: (O, O) => boolean
+ update: (InvalidationContext<O>) => void
+ updateItemSize: (Key, Size) => boolean
+ virtualizer: Virtualizer<{}, any> | null
+} /react-aria-components:LayoutInfo+LayoutInfo {
+ allowOverflow: boolean = false
+ constructor: (string, Key, Rect) => void
+ content: any | null
+ copy: () => LayoutInfo
+ estimatedSize: boolean = false
+ isSticky: boolean = false
+ key: Key
+ opacity: number = 1
+ parentKey: Key | null
+ rect: Rect
+ transform: string | null
+ type: string
+ zIndex: number
+} /react-aria-components:Size+Size {
+ area: any
+ constructor: (any, any) => void
+ copy: () => Size
+ equals: (Size) => boolean
+ height: number
+ width: number
+} /react-aria-components:Rect+Rect {
+ area: number
+ bottomLeft: Point
+ bottomRight: Point
+ constructor: (any, any, any, any) => void
+ containsPoint: (Point) => boolean
+ containsRect: (Rect) => boolean
+ copy: () => Rect
+ equals: (Rect) => void
+ getCornerInRect: (Rect) => RectCorner | null
+ height: number
+ intersection: (Rect) => Rect
+ intersects: (Rect) => boolean
+ maxX: number
+ maxY: number
+ pointEquals: (Point | Rect) => void
+ sizeEquals: (Size | Rect) => void
+ topLeft: Point
+ topRight: Point
+ union: (Rect) => void
+ width: number
+ x: number
+ y: number
+} /react-aria-components:Point+Point {
+ constructor: (any, any) => void
+ copy: () => Point
+ equals: (Point) => boolean
+ isOrigin: () => boolean
+ x: number
+ y: number
+} /react-aria-components:WaterfallLayoutOptions+WaterfallLayoutOptions {
+ dropIndicatorThickness?: number = 2
+ maxColumns?: number = Infinity
+ maxItemSize?: Size = Infinity
+ minItemSize?: Size = 200 x 200
+ minSpace?: Size = 18 x 18
+} @react-spectrum/card/@react-spectrum/card:GalleryLayout GalleryLayout <T> {
_distributeWidths: (any) => void
_findClosest: (Rect, Rect) => void
_findClosestLayoutInfo: (Rect, Rect) => void
buildCollection: () => void
collection: GridCollection<T>
constructor: (GalleryLayoutOptions) => void
direction: Direction
disabledKeys: Set<Key>
getContentSize: () => void
getDropTargetLayoutInfo: (ItemDropTarget) => LayoutInfo
getFirstKey: () => void
- getItemRect: (Key) => Rect | null
getKeyAbove: (Key) => void
getKeyBelow: (Key) => void
getKeyForSearch: (string, Key) => void
getKeyLeftOf: (Key) => void
getKeyPageAbove: (Key) => void
getKeyPageBelow: (Key) => void
getKeyRightOf: (Key) => void
getLastKey: () => void
getLayoutInfo: (Key) => void
getVisibleLayoutInfos: (Rect, any) => void
- getVisibleRect: () => Rect
isLoading: boolean
isVisible: (LayoutInfo, Rect, boolean) => void
itemPadding: number
layoutType: any
margin: number
scale: Scale
shouldInvalidate: (Rect, Rect) => boolean
+ shouldInvalidateLayoutOptions: (O, O) => boolean
update: (InvalidationContext<CardViewLayoutOptions>) => void
updateItemSize: (Key, Size) => boolean
virtualizer: Virtualizer<{}, any> | null
} /@react-spectrum/card:GridLayout GridLayout <T> {
_findClosest: (Rect, Rect) => void
_findClosestLayoutInfo: (Rect, Rect) => void
buildChild: (Node<T>, number, number) => LayoutInfo
buildCollection: () => void
cardOrientation: Orientation
collection: GridCollection<T>
constructor: (GridLayoutOptions) => void
direction: Direction
disabledKeys: Set<Key>
getContentSize: () => void
getDropTargetLayoutInfo: (ItemDropTarget) => LayoutInfo
getFirstKey: () => void
getIndexAtPoint: (any, any, any) => void
- getItemRect: (Key) => Rect | null
getKeyAbove: (Key) => void
getKeyBelow: (Key) => void
getKeyForSearch: (string, Key) => void
getKeyLeftOf: (Key) => void
getKeyPageAbove: (Key) => void
getKeyPageBelow: (Key) => void
getKeyRightOf: (Key) => void
getLastKey: () => void
getLayoutInfo: (Key) => void
getVisibleLayoutInfos: (Rect, any) => void
- getVisibleRect: () => Rect
isLoading: boolean
isVisible: (LayoutInfo, Rect, boolean) => void
itemPadding: number
layoutType: any
margin: number
scale: Scale
shouldInvalidate: (Rect, Rect) => boolean
+ shouldInvalidateLayoutOptions: (O, O) => boolean
update: (InvalidationContext<CardViewLayoutOptions>) => void
updateItemSize: (Key, Size) => boolean
virtualizer: Virtualizer<{}, any> | null
} /@react-spectrum/card:WaterfallLayout WaterfallLayout <T> {
_findClosest: (Rect, Rect) => void
_findClosestLayoutInfo: (Rect, Rect) => void
buildCollection: (InvalidationContext) => void
collection: GridCollection<T>
constructor: (WaterfallLayoutOptions) => void
direction: Direction
disabledKeys: Set<Key>
getClosestLeft: (Key) => void
getClosestRight: (Key) => void
getContentSize: () => void
getDropTargetLayoutInfo: (ItemDropTarget) => LayoutInfo
getFirstKey: () => void
- getItemRect: (Key) => Rect | null
getKeyAbove: (Key) => void
getKeyBelow: (Key) => void
getKeyForSearch: (string, Key) => void
getKeyLeftOf: (Key) => void
getKeyPageAbove: (Key) => void
getKeyPageBelow: (Key) => void
getKeyRightOf: (Key) => void
getLastKey: () => void
getLayoutInfo: (Key) => void
getNextColumnIndex: (any) => void
getVisibleLayoutInfos: (Rect, any) => void
- getVisibleRect: () => Rect
isLoading: boolean
isVisible: (LayoutInfo, Rect, boolean) => void
layoutType: any
margin: number
scale: Scale
shouldInvalidate: (Rect, Rect) => boolean
+ shouldInvalidateLayoutOptions: (O, O) => boolean
update: (InvalidationContext<CardViewLayoutOptions>) => void
updateItemSize: (Key, Size) => void
virtualizer: Virtualizer<{}, any> | null
} @react-spectrum/s2/@react-spectrum/s2:ImageProps ImageProps {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
alt?: string
crossOrigin?: 'anonymous' | 'use-credentials'
decoding?: 'async' | 'auto' | 'sync'
- fetchPriority?: 'high' | 'low' | 'auto'
group?: ImageGroup
loading?: 'eager' | 'lazy'
referrerPolicy?: HTMLAttributeReferrerPolicy
renderError?: () => ReactNode
src?: string
styles?: StyleString
} @react-stately/layout/@react-stately/layout:GridLayoutOptions GridLayoutOptions {
dropIndicatorThickness?: number = 2
maxColumns?: number = Infinity
maxItemSize?: Size = Infinity
minItemSize?: Size = 200 x 200
minSpace?: Size = 18 x 18
+ preserveAspectRatio?: boolean = false
} /@react-stately/layout:ListLayoutOptions ListLayoutOptions {
- dropIndicatorThickness?: number
+ dropIndicatorThickness?: number = 2
estimatedHeadingHeight?: number
estimatedRowHeight?: number
- headingHeight?: number
- loaderHeight?: number
- rowHeight?: number
+ gap?: number = 0
+ headingHeight?: number = 48
+ loaderHeight?: number = 48
+ padding?: number = 0
+ rowHeight?: number = 48
} /@react-stately/layout:TableLayoutProps TableLayoutProps {
columnWidths?: Map<Key, number>
+ dropIndicatorThickness?: number = 2
+ estimatedHeadingHeight?: number
+ estimatedRowHeight?: number
+ gap?: number = 0
+ headingHeight?: number = 48
+ loaderHeight?: number = 48
+ padding?: number = 0
+ rowHeight?: number = 48
} /@react-stately/layout:GridLayout-GridLayout <O = any, T> {
+GridLayout <O extends GridLayoutOptions = GridLayoutOptions, T> {
- constructor: (GridLayoutOptions) => void
getContentSize: () => Size
getDropTargetFromPoint: (number, number, (DropTarget) => boolean) => DropTarget
getDropTargetLayoutInfo: (ItemDropTarget) => LayoutInfo
- getItemRect: (Key) => Rect | null
- getLayoutInfo: (Key) => LayoutInfo | null
+ getLayoutInfo: (Key) => LayoutInfo
getVisibleLayoutInfos: (Rect) => Array<LayoutInfo>
- getVisibleRect: () => Rect
shouldInvalidate: (Rect, Rect) => boolean
- update: () => void
- updateItemSize: (Key, Size) => boolean
+ shouldInvalidateLayoutOptions: (GridLayoutOptions, GridLayoutOptions) => boolean
+ update: (InvalidationContext<GridLayoutOptions>) => void
+ updateItemSize: (Key, Size) => void
virtualizer: Virtualizer<{}, any> | null
} /@react-stately/layout:ListLayout-ListLayout <O = any, T> {
+ListLayout <O extends ListLayoutOptions = ListLayoutOptions, T> {
constructor: (ListLayoutOptions) => void
getContentSize: () => void
getDropTargetFromPoint: (number, number, (DropTarget) => boolean) => DropTarget | null
getDropTargetLayoutInfo: (ItemDropTarget) => LayoutInfo
- getItemRect: (Key) => Rect | null
getLayoutInfo: (Key) => void
getVisibleLayoutInfos: (Rect) => void
- getVisibleRect: () => Rect
shouldInvalidate: (Rect, Rect) => boolean
- update: (InvalidationContext<O>) => void
+ shouldInvalidateLayoutOptions: (ListLayoutOptions, ListLayoutOptions) => boolean
+ update: (InvalidationContext<ListLayoutOptions>) => void
updateItemSize: (Key, Size) => void
virtualizer: Virtualizer<{}, any> | null
} /@react-stately/layout:TableLayout TableLayout <O extends TableLayoutProps = TableLayoutProps, T> {
constructor: (ListLayoutOptions) => void
getContentSize: () => void
getDropTargetFromPoint: (number, number, (DropTarget) => boolean) => DropTarget | null
getDropTargetLayoutInfo: (ItemDropTarget) => LayoutInfo
- getItemRect: (Key) => Rect | null
getLayoutInfo: (Key) => void
getVisibleLayoutInfos: (Rect) => void
- getVisibleRect: () => Rect
shouldInvalidate: (Rect, Rect) => boolean
+ shouldInvalidateLayoutOptions: (TableLayoutProps, TableLayoutProps) => boolean
update: (InvalidationContext<TableLayoutProps>) => void
updateItemSize: (Key, Size) => void
virtualizer: Virtualizer<{}, any> | null
} /@react-stately/layout:WaterfallLayoutOptions+WaterfallLayoutOptions {
+ dropIndicatorThickness?: number = 2
+ maxColumns?: number = Infinity
+ maxItemSize?: Size = Infinity
+ minItemSize?: Size = 200 x 200
+ minSpace?: Size = 18 x 18
+} /@react-stately/layout:WaterfallLayout+WaterfallLayout <O extends WaterfallLayoutOptions = WaterfallLayoutOptions, T extends {}> {
+ getContentSize: () => Size
+ getDropTargetFromPoint: (number, number) => DropTarget
+ getDropTargetLayoutInfo: (ItemDropTarget) => LayoutInfo
+ getKeyLeftOf: (Key) => Key | null
+ getKeyRange: (Key, Key) => Array<Key>
+ getKeyRightOf: (Key) => Key | null
+ getLayoutInfo: (Key) => LayoutInfo
+ getVisibleLayoutInfos: (Rect) => Array<LayoutInfo>
+ shouldInvalidate: (Rect, Rect) => boolean
+ shouldInvalidateLayoutOptions: (WaterfallLayoutOptions, WaterfallLayoutOptions) => boolean
+ update: (InvalidationContext<WaterfallLayoutOptions>) => void
+ updateItemSize: (Key, Size) => void
+ virtualizer: Virtualizer<{}, any> | null
+} @react-stately/virtualizer/@react-stately/virtualizer:InvalidationContext InvalidationContext <O = any> {
contentChanged?: boolean
itemSizeChanged?: boolean
layoutOptions?: O
+ layoutOptionsChanged?: boolean
offsetChanged?: boolean
sizeChanged?: boolean
} /@react-stately/virtualizer:Layout-Layout <O = any, T extends {}> {
+Layout <O = any, T extends {} = Node<any>> {
getContentSize: () => Size
getDropTargetLayoutInfo: (ItemDropTarget) => LayoutInfo
- getItemRect: (Key) => Rect | null
getLayoutInfo: (Key) => LayoutInfo | null
getVisibleLayoutInfos: (Rect) => Array<LayoutInfo>
- getVisibleRect: () => Rect
shouldInvalidate: (Rect, Rect) => boolean
+ shouldInvalidateLayoutOptions: (O, O) => boolean
update: (InvalidationContext<O>) => void
updateItemSize: (Key, Size) => boolean
virtualizer: Virtualizer<{}, any> | null
} /@react-stately/virtualizer:LayoutInfo LayoutInfo {
allowOverflow: boolean = false
constructor: (string, Key, Rect) => void
content: any | null
copy: () => LayoutInfo
- estimatedSize: boolean
- isSticky: boolean
+ estimatedSize: boolean = false
+ isSticky: boolean = false
key: Key
- opacity: number
+ opacity: number = 1
parentKey: Key | null
rect: Rect
transform: string | null
type: string
} |
items.push({id: i, name}); | ||
} | ||
|
||
let layout = useMemo(() => new WaterfallLayout(), []); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there any legitimate case for invalidating this and creating a new instance? if not, then should people just pass the layout class into the layout prop?
could use layoutOptions for any initial/setup values they wanted
I assume we'd want to discourage people from sending values to the constructor, instead making use of the layoutOptions
let item = this.virtualizer!.collection.getItem(key); | ||
if (item?.index != null) { | ||
persistedIndices.push(item.index); | ||
if (node.type === 'skeleton') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How much do you think non-spectrum people are likely to use these?
If someone doesn't want to use the skeleton code and it's too much in terms of size, they can also create their own layout, so I'm fine with it staying.
I feel like another common usecase, which would avoid this, is going to be using other virtualizer libraries, such as tanstack.
Depends on #7700
This moves the WaterfallLayout from S2 into RAC, and adds it to the documentation. It also updates GridLayout to support variable row heights like in S2 instead of only equal sized items.
Note that the GridLayout changes are breaking, especially in cases where it is subclassed. This was exported as
UNSTABLE
from RAC, but not directly from the@react-stately/layout
package so we need to decide how to release this.Also updated the docs to use the
layoutOptions
prop instead of passing options to theLayout
constructor. This allows options to be changed at runtime without invalidating the entire layout. Virtualizer will ask the layout if it needs to invalidate in response tolayoutOptions
changing, meaning it doesn't need to be manually memoized by the user.