@react-aria/virtualizer 3.10.2-nightly.4674 → 3.10.2-nightly.4683

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,6 +15,7 @@ import {flushSync} from 'react-dom';
15
15
  import {getScrollLeft} from './utils';
16
16
  import React, {
17
17
  CSSProperties,
18
+ ForwardedRef,
18
19
  HTMLAttributes,
19
20
  ReactNode,
20
21
  RefObject,
@@ -24,13 +25,13 @@ import React, {
24
25
  useState
25
26
  } from 'react';
26
27
  import {Rect, Size} from '@react-stately/virtualizer';
27
- import {useEffectEvent, useLayoutEffect, useResizeObserver} from '@react-aria/utils';
28
+ import {useEffectEvent, useEvent, useLayoutEffect, useObjectRef, useResizeObserver} from '@react-aria/utils';
28
29
  import {useLocale} from '@react-aria/i18n';
29
30
 
30
31
  interface ScrollViewProps extends HTMLAttributes<HTMLElement> {
31
32
  contentSize: Size,
32
33
  onVisibleRectChange: (rect: Rect) => void,
33
- children: ReactNode,
34
+ children?: ReactNode,
34
35
  innerStyle?: CSSProperties,
35
36
  sizeToFit?: 'width' | 'height',
36
37
  onScrollStart?: () => void,
@@ -38,11 +39,26 @@ interface ScrollViewProps extends HTMLAttributes<HTMLElement> {
38
39
  scrollDirection?: 'horizontal' | 'vertical' | 'both'
39
40
  }
40
41
 
41
- function ScrollView(props: ScrollViewProps, ref: RefObject<HTMLDivElement>) {
42
+ function ScrollView(props: ScrollViewProps, ref: ForwardedRef<HTMLDivElement | null>) {
43
+ ref = useObjectRef(ref);
44
+ let {scrollViewProps, contentProps} = useScrollView(props, ref);
45
+
46
+ return (
47
+ <div role="presentation" {...scrollViewProps} ref={ref}>
48
+ <div role="presentation" {...contentProps}>
49
+ {props.children}
50
+ </div>
51
+ </div>
52
+ );
53
+ }
54
+
55
+ const ScrollViewForwardRef = React.forwardRef(ScrollView);
56
+ export {ScrollViewForwardRef as ScrollView};
57
+
58
+ export function useScrollView(props: ScrollViewProps, ref: RefObject<HTMLElement | null>) {
42
59
  let {
43
60
  contentSize,
44
61
  onVisibleRectChange,
45
- children,
46
62
  innerStyle,
47
63
  sizeToFit,
48
64
  onScrollStart,
@@ -51,8 +67,6 @@ function ScrollView(props: ScrollViewProps, ref: RefObject<HTMLDivElement>) {
51
67
  ...otherProps
52
68
  } = props;
53
69
 
54
- let defaultRef = useRef();
55
- ref = ref || defaultRef;
56
70
  let state = useRef({
57
71
  scrollTop: 0,
58
72
  scrollLeft: 0,
@@ -114,6 +128,9 @@ function ScrollView(props: ScrollViewProps, ref: RefObject<HTMLDivElement>) {
114
128
  });
115
129
  }, [props, direction, state, contentSize, onVisibleRectChange, onScrollStart, onScrollEnd]);
116
130
 
131
+ // Attach event directly to ref so RAC Virtualizer doesn't need to send props upward.
132
+ useEvent(ref, 'scroll', onScroll);
133
+
117
134
  // eslint-disable-next-line arrow-body-style
118
135
  useEffect(() => {
119
136
  return () => {
@@ -166,16 +183,29 @@ function ScrollView(props: ScrollViewProps, ref: RefObject<HTMLDivElement>) {
166
183
  }
167
184
  });
168
185
 
186
+ let didUpdateSize = useRef(false);
169
187
  useLayoutEffect(() => {
170
- // React doesn't allow flushSync inside effects so pass an identity function instead.
171
- // This only happens on initial render. The resize observer will also call updateSize
172
- // once it initializes, but we need earlier initialization in a layout effect to avoid
173
- // a flash of missing content.
174
- updateSize(fn => fn());
188
+ // React doesn't allow flushSync inside effects, so queue a microtask.
189
+ // We also need to wait until all refs are set (e.g. when passing a ref down from a parent).
190
+ queueMicrotask(() => {
191
+ if (!didUpdateSize.current) {
192
+ didUpdateSize.current = true;
193
+ updateSize(flushSync);
194
+ }
195
+ });
196
+ }, [updateSize]);
197
+ useEffect(() => {
198
+ if (!didUpdateSize.current) {
199
+ // If useEffect ran before the above microtask, we are in a synchronous render (e.g. act).
200
+ // Update the size here so that you don't need to mock timers in tests.
201
+ didUpdateSize.current = true;
202
+ updateSize(fn => fn());
203
+ }
175
204
  }, [updateSize]);
176
205
  let onResize = useCallback(() => {
177
206
  updateSize(flushSync);
178
207
  }, [updateSize]);
208
+
179
209
  // Watch border-box instead of of content-box so that we don't go into
180
210
  // an infinite loop when scrollbars appear or disappear.
181
211
  useResizeObserver({ref, box: 'border-box', onResize});
@@ -207,14 +237,13 @@ function ScrollView(props: ScrollViewProps, ref: RefObject<HTMLDivElement>) {
207
237
  ...innerStyle
208
238
  };
209
239
 
210
- return (
211
- <div role="presentation" {...otherProps} style={style} ref={ref} onScroll={onScroll}>
212
- <div role="presentation" style={innerStyle}>
213
- {children}
214
- </div>
215
- </div>
216
- );
240
+ return {
241
+ scrollViewProps: {
242
+ ...otherProps,
243
+ style
244
+ },
245
+ contentProps: {
246
+ style: innerStyle
247
+ }
248
+ };
217
249
  }
218
-
219
- const ScrollViewForwardRef = React.forwardRef(ScrollView);
220
- export {ScrollViewForwardRef as ScrollView};
@@ -37,7 +37,7 @@ interface VirtualizerProps<T extends object, V, O> extends Omit<HTMLAttributes<H
37
37
 
38
38
  export const VirtualizerContext = createContext<VirtualizerState<any, any, any> | null>(null);
39
39
 
40
- function Virtualizer<T extends object, V extends ReactNode, O>(props: VirtualizerProps<T, V, O>, ref: RefObject<HTMLDivElement>) {
40
+ function Virtualizer<T extends object, V extends ReactNode, O>(props: VirtualizerProps<T, V, O>, ref: RefObject<HTMLDivElement | null>) {
41
41
  let {
42
42
  children: renderView,
43
43
  renderWrapper,
@@ -54,7 +54,7 @@ function Virtualizer<T extends object, V extends ReactNode, O>(props: Virtualize
54
54
  ...otherProps
55
55
  } = props;
56
56
 
57
- let fallbackRef = useRef<HTMLDivElement>();
57
+ let fallbackRef = useRef<HTMLDivElement>(undefined);
58
58
  ref = ref || fallbackRef;
59
59
 
60
60
  let state = useVirtualizerState({
@@ -66,7 +66,7 @@ function Virtualizer<T extends object, V extends ReactNode, O>(props: Virtualize
66
66
  ref.current.scrollLeft = rect.x;
67
67
  ref.current.scrollTop = rect.y;
68
68
  },
69
- persistedKeys: useMemo(() => focusedKey ? new Set([focusedKey]) : new Set(), [focusedKey]),
69
+ persistedKeys: useMemo(() => focusedKey != null ? new Set([focusedKey]) : new Set(), [focusedKey]),
70
70
  layoutOptions
71
71
  });
72
72
 
@@ -96,7 +96,7 @@ interface VirtualizerOptions {
96
96
  }
97
97
 
98
98
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
99
- export function useVirtualizer<T extends object, V extends ReactNode, W>(props: VirtualizerOptions, state: VirtualizerState<T, V, W>, ref: RefObject<HTMLElement>) {
99
+ export function useVirtualizer<T extends object, V extends ReactNode, W>(props: VirtualizerOptions, state: VirtualizerState<T, V, W>, ref: RefObject<HTMLElement | null>) {
100
100
  let {isLoading, onLoadMore} = props;
101
101
  let {setVisibleRect, virtualizer} = state;
102
102
 
@@ -150,7 +150,7 @@ export function useVirtualizer<T extends object, V extends ReactNode, W>(props:
150
150
 
151
151
  // forwardRef doesn't support generic parameters, so cast the result to the correct type
152
152
  // https://stackoverflow.com/questions/58469229/react-with-typescript-generics-while-using-react-forwardref
153
- const _Virtualizer = React.forwardRef(Virtualizer) as <T extends object, V, O>(props: VirtualizerProps<T, V, O> & {ref?: RefObject<HTMLDivElement>}) => ReactElement;
153
+ const _Virtualizer = React.forwardRef(Virtualizer) as <T extends object, V, O>(props: VirtualizerProps<T, V, O> & {ref?: RefObject<HTMLDivElement | null>}) => ReactElement;
154
154
  export {_Virtualizer as Virtualizer};
155
155
 
156
156
  function defaultRenderWrapper<T extends object, V extends ReactNode>(
@@ -17,7 +17,7 @@ import {useLocale} from '@react-aria/i18n';
17
17
  import {useVirtualizerItem, VirtualizerItemOptions} from './useVirtualizerItem';
18
18
 
19
19
  interface VirtualizerItemProps extends Omit<VirtualizerItemOptions, 'ref'> {
20
- parent?: LayoutInfo,
20
+ parent?: LayoutInfo | null,
21
21
  className?: string,
22
22
  children: ReactNode
23
23
  }
@@ -25,7 +25,7 @@ interface VirtualizerItemProps extends Omit<VirtualizerItemOptions, 'ref'> {
25
25
  export function VirtualizerItem(props: VirtualizerItemProps) {
26
26
  let {className, layoutInfo, virtualizer, parent, children} = props;
27
27
  let {direction} = useLocale();
28
- let ref = useRef();
28
+ let ref = useRef(undefined);
29
29
  useVirtualizerItem({
30
30
  layoutInfo,
31
31
  virtualizer,
@@ -74,7 +74,9 @@ export function layoutInfoToStyle(layoutInfo: LayoutInfo, dir: Direction, parent
74
74
  position: layoutInfo.isSticky ? 'sticky' : 'absolute',
75
75
  // Sticky elements are positioned in normal document flow. Display inline-block so that they don't push other sticky columns onto the following rows.
76
76
  display: layoutInfo.isSticky ? 'inline-block' : undefined,
77
- overflow: layoutInfo.allowOverflow ? 'visible' : 'hidden',
77
+ // Use clip instead of hidden to avoid creating an implicit generic container in the accessibility tree in Firefox.
78
+ // Hidden still allows programmatic scrolling whereas clip does not.
79
+ overflow: layoutInfo.allowOverflow ? 'visible' : 'clip',
78
80
  opacity: layoutInfo.opacity,
79
81
  zIndex: layoutInfo.zIndex,
80
82
  transform: layoutInfo.transform,
package/src/index.ts CHANGED
@@ -15,5 +15,5 @@ export type {VirtualizerItemOptions} from './useVirtualizerItem';
15
15
  export {useVirtualizer, Virtualizer, VirtualizerContext} from './Virtualizer';
16
16
  export {useVirtualizerItem} from './useVirtualizerItem';
17
17
  export {VirtualizerItem, layoutInfoToStyle} from './VirtualizerItem';
18
- export {ScrollView} from './ScrollView';
18
+ export {ScrollView, useScrollView} from './ScrollView';
19
19
  export {getRTLOffsetType, getScrollLeft, setScrollLeft} from './utils';
@@ -22,7 +22,7 @@ interface IVirtualizer {
22
22
  export interface VirtualizerItemOptions {
23
23
  layoutInfo: LayoutInfo,
24
24
  virtualizer: IVirtualizer,
25
- ref: RefObject<HTMLElement>
25
+ ref: RefObject<HTMLElement | null>
26
26
  }
27
27
 
28
28
  export function useVirtualizerItem(options: VirtualizerItemOptions) {