Skip to content

Commit 8f6724f

Browse files
committed
Merge branch 'main' of github.com:adobe/react-spectrum into subdialogs
# Conflicts: # packages/react-aria-components/stories/Autocomplete.stories.tsx
2 parents 3dee094 + 7967920 commit 8f6724f

File tree

78 files changed

+2840
-685
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+2840
-685
lines changed

.storybook-s2/docs/Intro.jsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,6 @@ import {ActionButton} from '@react-spectrum/s2';
202202
<li><Code>width</Code></li>
203203
<li><Code>minWidth</Code></li>
204204
<li><Code>maxWidth</Code></li>
205-
<li><Code>flex</Code></li>
206205
<li><Code>flexGrow</Code></li>
207206
<li><Code>flexShrink</Code></li>
208207
<li><Code>flexBasis</Code></li>

packages/@react-aria/collections/src/BaseCollection.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ export class CollectionNode<T> implements Node<T> {
3535
readonly lastChildKey: Key | null = null;
3636
readonly props: any = {};
3737
readonly render?: (node: Node<any>) => ReactElement;
38+
readonly colSpan: number | null = null;
39+
readonly colIndex: number | null = null;
3840

3941
constructor(type: string, key: Key) {
4042
this.type = type;
@@ -61,6 +63,8 @@ export class CollectionNode<T> implements Node<T> {
6163
node.lastChildKey = this.lastChildKey;
6264
node.props = this.props;
6365
node.render = this.render;
66+
node.colSpan = this.colSpan;
67+
node.colIndex = this.colIndex;
6468
return node;
6569
}
6670
}

packages/@react-aria/collections/src/CollectionBuilder.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ function useCollectionDocument<T extends object, C extends BaseCollection<T>>(cr
116116
useLayoutEffect(() => {
117117
document.isMounted = true;
118118
return () => {
119-
// Mark unmounted so we can skip all of the collection updates caused by
119+
// Mark unmounted so we can skip all of the collection updates caused by
120120
// React calling removeChild on every item in the collection.
121121
document.isMounted = false;
122122
};

packages/@react-aria/collections/src/Document.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,16 @@ export class ElementNode<T> extends BaseNode<T> {
260260
node.hasChildNodes = !!this.firstChild;
261261
node.firstChildKey = this.firstChild?.node.key ?? null;
262262
node.lastChildKey = this.lastChild?.node.key ?? null;
263+
264+
// Update the colIndex of sibling nodes if this node has a colSpan.
265+
if ((node.colSpan != null || node.colIndex != null) && this.nextSibling) {
266+
// This queues the next sibling for update, which means this happens recursively.
267+
let nextColIndex = (node.colIndex ?? node.index) + (node.colSpan ?? 1);
268+
if (nextColIndex !== this.nextSibling.node.colIndex) {
269+
let siblingNode = this.ownerDocument.getMutableNode(this.nextSibling);
270+
siblingNode.colIndex = nextColIndex;
271+
}
272+
}
263273
}
264274

265275
setProps<E extends Element>(obj: any, ref: ForwardedRef<E>, rendered?: any, render?: (node: Node<T>) => ReactElement) {
@@ -278,6 +288,10 @@ export class ElementNode<T> extends BaseNode<T> {
278288
node.key = id;
279289
}
280290

291+
if (props.colSpan != null) {
292+
node.colSpan = props.colSpan;
293+
}
294+
281295
// If this is the first time props have been set, end the transaction started in the constructor
282296
// so this node can be emitted.
283297
if (!this.hasSetProps) {
@@ -418,8 +432,8 @@ export class Document<T, C extends BaseCollection<T> = BaseCollection<T>> extend
418432
}
419433
}
420434

421-
collection.commit(this.firstChild?.node.key ?? null, this.lastChild?.node.key ?? null, this.isSSR);
422435
this.mutatedNodes.clear();
436+
collection.commit(this.firstChild?.node.key ?? null, this.lastChild?.node.key ?? null, this.isSSR);
423437
}
424438

425439
this.collectionMutated = false;

packages/@react-aria/collections/src/Hidden.tsx

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13-
import {createPortal} from 'react-dom';
1413
import {forwardRefType} from '@react-types/shared';
1514
import React, {createContext, forwardRef, ReactElement, ReactNode, useContext} from 'react';
16-
import {useIsSSR} from '@react-aria/ssr';
1715

1816
// React doesn't understand the <template> element, which doesn't have children like a normal element.
1917
// It will throw an error during hydration when it expects the firstChild to contain content rendered
@@ -37,12 +35,8 @@ if (typeof HTMLTemplateElement !== 'undefined') {
3735

3836
export const HiddenContext = createContext<boolean>(false);
3937

40-
// Portal to nowhere
41-
const hiddenFragment = typeof DocumentFragment !== 'undefined' ? new DocumentFragment() : null;
42-
4338
export function Hidden(props: {children: ReactNode}) {
4439
let isHidden = useContext(HiddenContext);
45-
let isSSR = useIsSSR();
4640
if (isHidden) {
4741
// Don't hide again if we are already hidden.
4842
return <>{props.children}</>;
@@ -54,12 +48,10 @@ export function Hidden(props: {children: ReactNode}) {
5448
</HiddenContext.Provider>
5549
);
5650

57-
// In SSR, portals are not supported by React. Instead, render into a <template>
51+
// In SSR, portals are not supported by React. Instead, always render into a <template>
5852
// element, which the browser will never display to the user. In addition, the
59-
// content is not part of the DOM tree, so it won't affect ids or other accessibility attributes.
60-
return isSSR
61-
? <template data-react-aria-hidden>{children}</template>
62-
: createPortal(children, hiddenFragment!);
53+
// content is not part of the accessible DOM tree, so it won't affect ids or other accessibility attributes.
54+
return <template data-react-aria-hidden>{children}</template>;
6355
}
6456

6557
/** Creates a component that forwards its ref and returns null if it is in a hidden subtree. */

packages/@react-aria/toast/src/useToastRegion.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,32 @@ export function useToastRegion<T>(props: AriaToastRegionProps, state: ToastState
4949
onHoverEnd: state.resumeAll
5050
});
5151

52+
let prevToastCount = useRef(state.visibleToasts.length);
53+
useEffect(() => {
54+
// Resume timers if the user's pointer left the region due to a toast being removed and the region shrinking.
55+
// Waits until the next pointermove after a toast is removed.
56+
let onPointerMove = (e: PointerEvent) => {
57+
if (!ref.current) {
58+
document.removeEventListener('pointermove', onPointerMove);
59+
return;
60+
}
61+
let regionRect = ref.current.getBoundingClientRect();
62+
const isPointerOverRegion = e.clientX >= regionRect.left && e.clientX <= regionRect.right && e.clientY >= regionRect.top && e.clientY <= regionRect.bottom;
63+
if (!isPointerOverRegion) {
64+
state.resumeAll();
65+
}
66+
document.removeEventListener('pointermove', onPointerMove);
67+
};
68+
69+
if (state.visibleToasts.length < prevToastCount.current && state.visibleToasts.length > 0) {
70+
document.addEventListener('pointermove', onPointerMove);
71+
}
72+
prevToastCount.current = state.visibleToasts.length;
73+
return () => {
74+
document.removeEventListener('pointermove', onPointerMove);
75+
};
76+
}, [state.visibleToasts, ref, state]);
77+
5278
// Manage focus within the toast region.
5379
// If a focused containing toast is removed, move focus to the next toast, or the previous toast if there is no next toast.
5480
// We might be making an assumption with how this works if someone implements the priority queue differently, or

packages/@react-aria/toast/stories/Example.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function ToastRegion() {
3232
let ref = useRef(null);
3333
let {regionProps} = useToastRegion({}, state, ref);
3434
return (
35-
<div {...regionProps} ref={ref} style={{position: 'fixed', bottom: 0, right: 0}}>
35+
<div {...regionProps} ref={ref} style={{position: 'fixed', bottom: 0, right: 0, display: 'flex', flexDirection: 'column', gap: 10}}>
3636
{state.visibleToasts.map(toast => (
3737
<Toast key={toast.key} toast={toast} />
3838
))}

packages/@react-aria/toast/stories/useToast.stories.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,14 @@ import {ToastContainer} from './Example';
1616
export default {
1717
title: 'useToast',
1818
args: {
19-
maxVisibleToasts: 1
19+
maxVisibleToasts: 1,
20+
timeout: null
21+
},
22+
argTypes: {
23+
timeout: {
24+
control: 'radio',
25+
options: [null, 5000]
26+
}
2027
}
2128
};
2229

@@ -25,9 +32,9 @@ let count = 0;
2532
export const Default = args => (
2633
<ToastContainer {...args}>
2734
{state => (<>
28-
<button onClick={() => state.add('High ' + ++count, {priority: 10})}>Add high priority toast</button>
29-
<button onClick={() => state.add('Medium ' + ++count, {priority: 5})}>Add medium priority toast</button>
30-
<button onClick={() => state.add('Low ' + ++count, {priority: 1})}>Add low priority toast</button>
35+
<button onClick={() => state.add('High ' + ++count, {priority: 10, timeout: args.timeout})}>Add high priority toast</button>
36+
<button onClick={() => state.add('Medium ' + ++count, {priority: 5, timeout: args.timeout})}>Add medium priority toast</button>
37+
<button onClick={() => state.add('Low ' + ++count, {priority: 1, timeout: args.timeout})}>Add low priority toast</button>
3138
</>)}
3239
</ToastContainer>
3340
);

packages/@react-aria/virtualizer/src/VirtualizerItem.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ export function layoutInfoToStyle(layoutInfo: LayoutInfo, dir: Direction, parent
8888
...rectStyles
8989
};
9090

91+
if (layoutInfo.isSticky) {
92+
if (style.top) {
93+
style.marginTop = style.top;
94+
}
95+
if (style[xProperty]) {
96+
style[dir === 'rtl' ? 'marginRight' : 'marginLeft'] = style[xProperty];
97+
}
98+
}
99+
91100
cache.set(layoutInfo, style);
92101
return style;
93102
}

packages/@react-spectrum/listbox/src/ListBoxBase.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export function useListBoxLayout<T>(): ListBoxLayout<T> {
5454
new ListBoxLayout<T>({
5555
estimatedRowHeight: scale === 'large' ? 48 : 32,
5656
estimatedHeadingHeight: scale === 'large' ? 33 : 26,
57-
padding: scale === 'large' ? 5 : 4, // TODO: get from DNA
57+
paddingY: scale === 'large' ? 5 : 4, // TODO: get from DNA
5858
placeholderHeight: scale === 'large' ? 48 : 32
5959
})
6060
, [scale]);

packages/@react-spectrum/listbox/src/ListBoxLayout.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,18 @@ interface ListBoxLayoutProps {
88

99
interface ListBoxLayoutOptions extends ListLayoutOptions {
1010
placeholderHeight: number,
11-
padding: number
11+
paddingY: number
1212
}
1313

1414
export class ListBoxLayout<T> extends ListLayout<T, ListBoxLayoutProps> {
1515
private isLoading: boolean = false;
1616
private placeholderHeight: number;
17-
private padding: number;
17+
private paddingY: number;
1818

1919
constructor(opts: ListBoxLayoutOptions) {
2020
super(opts);
2121
this.placeholderHeight = opts.placeholderHeight;
22-
this.padding = opts.padding;
22+
this.paddingY = opts.paddingY;
2323
}
2424

2525
update(invalidationContext: InvalidationContext<ListBoxLayoutProps>): void {
@@ -28,7 +28,7 @@ export class ListBoxLayout<T> extends ListLayout<T, ListBoxLayoutProps> {
2828
}
2929

3030
protected buildCollection(): LayoutNode[] {
31-
let nodes = super.buildCollection(this.padding);
31+
let nodes = super.buildCollection(this.paddingY);
3232
let y = this.contentSize.height;
3333

3434
if (this.isLoading) {
@@ -55,7 +55,7 @@ export class ListBoxLayout<T> extends ListLayout<T, ListBoxLayoutProps> {
5555
y = placeholder.rect.maxY;
5656
}
5757

58-
this.contentSize.height = y + this.padding;
58+
this.contentSize.height = y + this.paddingY;
5959
return nodes;
6060
}
6161

packages/@react-spectrum/s2/chromatic/TreeView.stories.tsx

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13-
import {ActionMenu, Collection, Content, Heading, IllustratedMessage, Link, MenuItem, Text, TreeItemContent, TreeView, TreeViewItem} from '../src';
13+
import {ActionMenu, Collection, Content, Heading, IllustratedMessage, Link, MenuItem, Text, TreeView, TreeViewItem, TreeViewItemContent} from '../src';
1414
import Delete from '../s2wf-icons/S2_Icon_Delete_20_N.svg';
1515
import Edit from '../s2wf-icons/S2_Icon_Edit_20_N.svg';
1616
import FileTxt from '../s2wf-icons/S2_Icon_FileText_20_N.svg';
@@ -38,7 +38,7 @@ function TreeExample(props) {
3838
expandedKeys={['projects']}>
3939

4040
<TreeViewItem id="Photos" textValue="Photos">
41-
<TreeItemContent>
41+
<TreeViewItemContent>
4242
<Text>Photos</Text>
4343
<Folder />
4444
<ActionMenu>
@@ -51,10 +51,10 @@ function TreeExample(props) {
5151
<Text>Delete</Text>
5252
</MenuItem>
5353
</ActionMenu>
54-
</TreeItemContent>
54+
</TreeViewItemContent>
5555
</TreeViewItem>
5656
<TreeViewItem id="projects" textValue="Projects">
57-
<TreeItemContent>
57+
<TreeViewItemContent>
5858
<Text>Projects</Text>
5959
<Folder />
6060
<ActionMenu>
@@ -67,9 +67,9 @@ function TreeExample(props) {
6767
<Text>Delete</Text>
6868
</MenuItem>
6969
</ActionMenu>
70-
</TreeItemContent>
70+
</TreeViewItemContent>
7171
<TreeViewItem id="projects-1" textValue="Projects-1">
72-
<TreeItemContent>
72+
<TreeViewItemContent>
7373
<Text>Projects-1</Text>
7474
<Folder />
7575
<ActionMenu>
@@ -82,9 +82,9 @@ function TreeExample(props) {
8282
<Text>Delete</Text>
8383
</MenuItem>
8484
</ActionMenu>
85-
</TreeItemContent>
85+
</TreeViewItemContent>
8686
<TreeViewItem id="projects-1A" textValue="Projects-1A">
87-
<TreeItemContent>
87+
<TreeViewItemContent>
8888
<Text>Projects-1A</Text>
8989
<FileTxt />
9090
<ActionMenu>
@@ -97,11 +97,11 @@ function TreeExample(props) {
9797
<Text>Delete</Text>
9898
</MenuItem>
9999
</ActionMenu>
100-
</TreeItemContent>
100+
</TreeViewItemContent>
101101
</TreeViewItem>
102102
</TreeViewItem>
103103
<TreeViewItem id="projects-2" textValue="Projects-2">
104-
<TreeItemContent>
104+
<TreeViewItemContent>
105105
<Text>Projects-2</Text>
106106
<FileTxt />
107107
<ActionMenu>
@@ -114,10 +114,10 @@ function TreeExample(props) {
114114
<Text>Delete</Text>
115115
</MenuItem>
116116
</ActionMenu>
117-
</TreeItemContent>
117+
</TreeViewItemContent>
118118
</TreeViewItem>
119119
<TreeViewItem id="projects-3" textValue="Projects-3">
120-
<TreeItemContent>
120+
<TreeViewItemContent>
121121
<Text>Projects-3</Text>
122122
<FileTxt />
123123
<ActionMenu>
@@ -130,7 +130,7 @@ function TreeExample(props) {
130130
<Text>Delete</Text>
131131
</MenuItem>
132132
</ActionMenu>
133-
</TreeItemContent>
133+
</TreeViewItemContent>
134134
</TreeViewItem>
135135
</TreeViewItem>
136136
</TreeView>
@@ -225,7 +225,7 @@ const DynamicTreeItem = (props) => {
225225
return (
226226
<>
227227
<TreeViewItem id={props.id} childItems={childItems} textValue={name} href={props.href}>
228-
<TreeItemContent>
228+
<TreeViewItemContent>
229229
<Text>{name}</Text>
230230
{icon}
231231
<ActionMenu>
@@ -238,7 +238,7 @@ const DynamicTreeItem = (props) => {
238238
<Text>Delete</Text>
239239
</MenuItem>
240240
</ActionMenu>
241-
</TreeItemContent>
241+
</TreeViewItemContent>
242242
<Collection items={childItems}>
243243
{(item: any) => (
244244
<DynamicTreeItem

0 commit comments

Comments
 (0)