react-aria-components 3.0.0-nightly-412a51816-250219 → 3.0.0-nightly-a792c1ad5-250222
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.
- package/dist/Autocomplete.main.js +20 -10
- package/dist/Autocomplete.main.js.map +1 -1
- package/dist/Autocomplete.mjs +20 -10
- package/dist/Autocomplete.module.js +20 -10
- package/dist/Autocomplete.module.js.map +1 -1
- package/dist/GridList.main.js.map +1 -1
- package/dist/GridList.module.js.map +1 -1
- package/dist/Group.main.js.map +1 -1
- package/dist/Group.module.js.map +1 -1
- package/dist/ListBox.main.js +16 -16
- package/dist/ListBox.main.js.map +1 -1
- package/dist/ListBox.mjs +17 -17
- package/dist/ListBox.module.js +17 -17
- package/dist/ListBox.module.js.map +1 -1
- package/dist/Menu.main.js +72 -7
- package/dist/Menu.main.js.map +1 -1
- package/dist/Menu.mjs +72 -8
- package/dist/Menu.module.js +72 -8
- package/dist/Menu.module.js.map +1 -1
- package/dist/Popover.main.js +38 -14
- package/dist/Popover.main.js.map +1 -1
- package/dist/Popover.mjs +39 -15
- package/dist/Popover.module.js +39 -15
- package/dist/Popover.module.js.map +1 -1
- package/dist/SearchField.main.js +3 -1
- package/dist/SearchField.main.js.map +1 -1
- package/dist/SearchField.mjs +4 -2
- package/dist/SearchField.module.js +4 -2
- package/dist/SearchField.module.js.map +1 -1
- package/dist/Table.main.js +10 -38
- package/dist/Table.main.js.map +1 -1
- package/dist/Table.mjs +10 -38
- package/dist/Table.module.js +10 -38
- package/dist/Table.module.js.map +1 -1
- package/dist/TableLayout.main.js.map +1 -1
- package/dist/TableLayout.module.js.map +1 -1
- package/dist/TextField.main.js +3 -1
- package/dist/TextField.main.js.map +1 -1
- package/dist/TextField.mjs +4 -2
- package/dist/TextField.module.js +4 -2
- package/dist/TextField.module.js.map +1 -1
- package/dist/Toast.main.js +150 -0
- package/dist/Toast.main.js.map +1 -0
- package/dist/Toast.mjs +139 -0
- package/dist/Toast.module.js +139 -0
- package/dist/Toast.module.js.map +1 -0
- package/dist/Tree.main.js +26 -25
- package/dist/Tree.main.js.map +1 -1
- package/dist/Tree.mjs +21 -20
- package/dist/Tree.module.js +21 -20
- package/dist/Tree.module.js.map +1 -1
- package/dist/Virtualizer.main.js +4 -1
- package/dist/Virtualizer.main.js.map +1 -1
- package/dist/Virtualizer.mjs +4 -1
- package/dist/Virtualizer.module.js +4 -1
- package/dist/Virtualizer.module.js.map +1 -1
- package/dist/import.mjs +11 -5
- package/dist/main.js +32 -13
- package/dist/main.js.map +1 -1
- package/dist/module.js +11 -5
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +136 -28
- package/dist/types.d.ts.map +1 -1
- package/i18n/index.js +1 -1
- package/i18n/index.mjs +1 -1
- package/package.json +34 -32
- package/src/Autocomplete.tsx +14 -11
- package/src/GridList.tsx +5 -0
- package/src/Group.tsx +1 -0
- package/src/ListBox.tsx +9 -8
- package/src/Menu.tsx +68 -10
- package/src/Popover.tsx +47 -21
- package/src/SearchField.tsx +3 -3
- package/src/Table.tsx +10 -40
- package/src/TableLayout.ts +1 -1
- package/src/TextField.tsx +3 -3
- package/src/Toast.tsx +185 -0
- package/src/Tree.tsx +50 -37
- package/src/Virtualizer.tsx +18 -3
- package/src/index.ts +13 -7
package/src/Menu.tsx
CHANGED
|
@@ -15,12 +15,12 @@ import {BaseCollection, Collection, CollectionBuilder, createBranchComponent, cr
|
|
|
15
15
|
import {MenuTriggerProps as BaseMenuTriggerProps, Collection as ICollection, Node, TreeState, useMenuTriggerState, useTreeState} from 'react-stately';
|
|
16
16
|
import {CollectionProps, CollectionRendererContext, ItemRenderProps, SectionContext, SectionProps, usePersistedKeys} from './Collection';
|
|
17
17
|
import {ContextValue, DEFAULT_SLOT, Provider, RenderProps, ScrollableProps, SlotProps, StyleProps, useContextProps, useRenderProps, useSlot, useSlottedContext} from './utils';
|
|
18
|
+
import {DialogContext, OverlayTriggerStateContext} from './Dialog';
|
|
18
19
|
import {filterDOMProps, mergeRefs, useObjectRef, useResizeObserver} from '@react-aria/utils';
|
|
19
20
|
import {FocusStrategy, forwardRefType, HoverEvents, Key, LinkDOMProps, MultipleSelection} from '@react-types/shared';
|
|
20
21
|
import {HeaderContext} from './Header';
|
|
21
22
|
import {KeyboardContext} from './Keyboard';
|
|
22
23
|
import {MultipleSelectionState, SelectionManager, useMultipleSelectionState} from '@react-stately/selection';
|
|
23
|
-
import {OverlayTriggerStateContext} from './Dialog';
|
|
24
24
|
import {PopoverContext} from './Popover';
|
|
25
25
|
import {PressResponder, useHover} from '@react-aria/interactions';
|
|
26
26
|
import React, {
|
|
@@ -70,7 +70,6 @@ export function MenuTrigger(props: MenuTriggerProps) {
|
|
|
70
70
|
ref: ref,
|
|
71
71
|
onResize: onResize
|
|
72
72
|
});
|
|
73
|
-
|
|
74
73
|
let scrollRef = useRef(null);
|
|
75
74
|
|
|
76
75
|
return (
|
|
@@ -106,7 +105,7 @@ export interface SubmenuTriggerProps {
|
|
|
106
105
|
delay?: number
|
|
107
106
|
}
|
|
108
107
|
|
|
109
|
-
const SubmenuTriggerContext = createContext<{parentMenuRef: RefObject<HTMLElement | null
|
|
108
|
+
const SubmenuTriggerContext = createContext<{parentMenuRef: RefObject<HTMLElement | null>, shouldUseVirtualFocus?: boolean} | null>(null);
|
|
110
109
|
|
|
111
110
|
/**
|
|
112
111
|
* A submenu trigger is used to wrap a submenu's trigger item and the submenu itself.
|
|
@@ -120,11 +119,12 @@ export const SubmenuTrigger = /*#__PURE__*/ createBranchComponent('submenutrigg
|
|
|
120
119
|
let submenuTriggerState = useSubmenuTriggerState({triggerKey: item.key}, rootMenuTriggerState);
|
|
121
120
|
let submenuRef = useRef<HTMLDivElement>(null);
|
|
122
121
|
let itemRef = useObjectRef(ref);
|
|
123
|
-
let {parentMenuRef} = useContext(SubmenuTriggerContext)!;
|
|
122
|
+
let {parentMenuRef, shouldUseVirtualFocus} = useContext(SubmenuTriggerContext)!;
|
|
124
123
|
let {submenuTriggerProps, submenuProps, popoverProps} = useSubmenuTrigger({
|
|
125
124
|
parentMenuRef,
|
|
126
125
|
submenuRef,
|
|
127
|
-
delay: props.delay
|
|
126
|
+
delay: props.delay,
|
|
127
|
+
shouldUseVirtualFocus
|
|
128
128
|
}, submenuTriggerState, itemRef);
|
|
129
129
|
|
|
130
130
|
return (
|
|
@@ -138,9 +138,62 @@ export const SubmenuTrigger = /*#__PURE__*/ createBranchComponent('submenutrigg
|
|
|
138
138
|
trigger: 'SubmenuTrigger',
|
|
139
139
|
triggerRef: itemRef,
|
|
140
140
|
placement: 'end top',
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
...popoverProps
|
|
142
|
+
}]
|
|
143
|
+
]}>
|
|
144
|
+
<CollectionBranch collection={state.collection} parent={item} />
|
|
145
|
+
{props.children[1]}
|
|
146
|
+
</Provider>
|
|
147
|
+
);
|
|
148
|
+
}, props => props.children[0]);
|
|
149
|
+
|
|
150
|
+
// TODO: make SubdialogTrigger unstable
|
|
151
|
+
export interface SubDialogTriggerProps {
|
|
152
|
+
/**
|
|
153
|
+
* The contents of the SubDialogTrigger. The first child should be an Item (the trigger) and the second child should be the Popover (for the subdialog).
|
|
154
|
+
*/
|
|
155
|
+
children: ReactElement[],
|
|
156
|
+
/**
|
|
157
|
+
* The delay time in milliseconds for the subdialog to appear after hovering over the trigger.
|
|
158
|
+
* @default 200
|
|
159
|
+
*/
|
|
160
|
+
delay?: number
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* A subdialog trigger is used to wrap a subdialog's trigger item and the subdialog itself.
|
|
165
|
+
*
|
|
166
|
+
* @version alpha
|
|
167
|
+
*/
|
|
168
|
+
export const SubDialogTrigger = /*#__PURE__*/ createBranchComponent('subdialogtrigger', (props: SubDialogTriggerProps, ref: ForwardedRef<HTMLDivElement>, item) => {
|
|
169
|
+
let {CollectionBranch} = useContext(CollectionRendererContext);
|
|
170
|
+
let state = useContext(MenuStateContext)!;
|
|
171
|
+
let rootMenuTriggerState = useContext(RootMenuTriggerStateContext)!;
|
|
172
|
+
let submenuTriggerState = useSubmenuTriggerState({triggerKey: item.key}, rootMenuTriggerState);
|
|
173
|
+
let subdialogRef = useRef<HTMLDivElement>(null);
|
|
174
|
+
let itemRef = useObjectRef(ref);
|
|
175
|
+
let {parentMenuRef, shouldUseVirtualFocus} = useContext(SubmenuTriggerContext)!;
|
|
176
|
+
let {submenuTriggerProps, submenuProps, popoverProps} = useSubmenuTrigger({
|
|
177
|
+
parentMenuRef,
|
|
178
|
+
submenuRef: subdialogRef,
|
|
179
|
+
type: 'dialog',
|
|
180
|
+
delay: props.delay,
|
|
181
|
+
shouldUseVirtualFocus
|
|
182
|
+
// TODO: might need to have something like isUnavailable like we do for ContextualHelpTrigger
|
|
183
|
+
}, submenuTriggerState, itemRef);
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<Provider
|
|
187
|
+
values={[
|
|
188
|
+
[MenuItemContext, {...submenuTriggerProps, onAction: undefined, ref: itemRef}],
|
|
189
|
+
[DialogContext, {'aria-labelledby': submenuProps['aria-labelledby']}],
|
|
190
|
+
[MenuContext, submenuProps],
|
|
191
|
+
[OverlayTriggerStateContext, submenuTriggerState],
|
|
192
|
+
[PopoverContext, {
|
|
193
|
+
ref: subdialogRef,
|
|
194
|
+
trigger: 'SubDialogTrigger',
|
|
195
|
+
triggerRef: itemRef,
|
|
196
|
+
placement: 'end top',
|
|
144
197
|
...popoverProps
|
|
145
198
|
}]
|
|
146
199
|
]}>
|
|
@@ -206,9 +259,14 @@ function MenuInner<T extends object>({props, collection, menuRef: ref}: MenuInne
|
|
|
206
259
|
[MenuStateContext, state],
|
|
207
260
|
[SeparatorContext, {elementType: 'div'}],
|
|
208
261
|
[SectionContext, {name: 'MenuSection', render: MenuSectionInner}],
|
|
209
|
-
[SubmenuTriggerContext, {parentMenuRef: ref}],
|
|
262
|
+
[SubmenuTriggerContext, {parentMenuRef: ref, shouldUseVirtualFocus: autocompleteMenuProps?.shouldUseVirtualFocus}],
|
|
210
263
|
[MenuItemContext, null],
|
|
211
|
-
[
|
|
264
|
+
[UNSTABLE_InternalAutocompleteContext, null],
|
|
265
|
+
[SelectionManagerContext, state.selectionManager],
|
|
266
|
+
/* Ensure root MenuTriggerState is defined, in case Menu is rendered outside a MenuTrigger. */
|
|
267
|
+
/* We assume the context can never change between defined and undefined. */
|
|
268
|
+
/* eslint-disable-next-line react-hooks/rules-of-hooks */
|
|
269
|
+
[RootMenuTriggerStateContext, triggerState ?? useMenuTriggerState({})]
|
|
212
270
|
]}>
|
|
213
271
|
<CollectionRoot
|
|
214
272
|
collection={state.collection}
|
package/src/Popover.tsx
CHANGED
|
@@ -20,7 +20,7 @@ import {OverlayTriggerStateContext} from './Dialog';
|
|
|
20
20
|
import React, {createContext, ForwardedRef, forwardRef, useContext, useRef, useState} from 'react';
|
|
21
21
|
import {useIsHidden} from '@react-aria/collections';
|
|
22
22
|
|
|
23
|
-
export interface PopoverProps extends Omit<PositionProps, 'isOpen'>, Omit<AriaPopoverProps, 'popoverRef' | 'triggerRef' | 'offset' | 'arrowSize'>, OverlayTriggerProps, RenderProps<PopoverRenderProps>, SlotProps {
|
|
23
|
+
export interface PopoverProps extends Omit<PositionProps, 'isOpen'>, Omit<AriaPopoverProps, 'popoverRef' | 'triggerRef' | 'groupRef' | 'offset' | 'arrowSize'>, OverlayTriggerProps, RenderProps<PopoverRenderProps>, SlotProps {
|
|
24
24
|
/**
|
|
25
25
|
* The name of the component that triggered the popover. This is reflected on the element
|
|
26
26
|
* as the `data-trigger` attribute, and can be used to provide specific
|
|
@@ -80,6 +80,9 @@ export interface PopoverRenderProps {
|
|
|
80
80
|
|
|
81
81
|
export const PopoverContext = createContext<ContextValue<PopoverProps, HTMLElement>>(null);
|
|
82
82
|
|
|
83
|
+
// Stores a ref for the portal container for a group of popovers (e.g. submenus).
|
|
84
|
+
const PopoverGroupContext = createContext<RefObject<Element | null> | null>(null);
|
|
85
|
+
|
|
83
86
|
/**
|
|
84
87
|
* A popover is an overlay element positioned relative to a trigger.
|
|
85
88
|
*/
|
|
@@ -137,6 +140,9 @@ function PopoverInner({state, isExiting, UNSTABLE_portalContainer, ...props}: Po
|
|
|
137
140
|
// Referenced from: packages/@react-spectrum/tooltip/src/TooltipTrigger.tsx
|
|
138
141
|
let arrowRef = useRef<HTMLDivElement>(null);
|
|
139
142
|
let [arrowWidth, setArrowWidth] = useState(0);
|
|
143
|
+
let containerRef = useRef<HTMLDivElement | null>(null);
|
|
144
|
+
let groupCtx = useContext(PopoverGroupContext);
|
|
145
|
+
let isSubPopover = groupCtx && (props.trigger === 'SubmenuTrigger' || props.trigger === 'SubDialogTrigger');
|
|
140
146
|
useLayoutEffect(() => {
|
|
141
147
|
if (arrowRef.current && state.isOpen) {
|
|
142
148
|
setArrowWidth(arrowRef.current.getBoundingClientRect().width);
|
|
@@ -146,7 +152,10 @@ function PopoverInner({state, isExiting, UNSTABLE_portalContainer, ...props}: Po
|
|
|
146
152
|
let {popoverProps, underlayProps, arrowProps, placement} = usePopover({
|
|
147
153
|
...props,
|
|
148
154
|
offset: props.offset ?? 8,
|
|
149
|
-
arrowSize: arrowWidth
|
|
155
|
+
arrowSize: arrowWidth,
|
|
156
|
+
// If this is a submenu/subdialog, use the root popover's container
|
|
157
|
+
// to detect outside interaction and add aria-hidden.
|
|
158
|
+
groupRef: isSubPopover ? groupCtx! : containerRef
|
|
150
159
|
}, state);
|
|
151
160
|
|
|
152
161
|
let ref = props.popoverRef as RefObject<HTMLDivElement | null>;
|
|
@@ -163,27 +172,44 @@ function PopoverInner({state, isExiting, UNSTABLE_portalContainer, ...props}: Po
|
|
|
163
172
|
});
|
|
164
173
|
|
|
165
174
|
let style = {...popoverProps.style, ...renderProps.style};
|
|
175
|
+
let overlay = (
|
|
176
|
+
<div
|
|
177
|
+
{...mergeProps(filterDOMProps(props as any), popoverProps)}
|
|
178
|
+
{...renderProps}
|
|
179
|
+
ref={ref}
|
|
180
|
+
slot={props.slot || undefined}
|
|
181
|
+
style={style}
|
|
182
|
+
dir={props.dir}
|
|
183
|
+
data-trigger={props.trigger}
|
|
184
|
+
data-placement={placement}
|
|
185
|
+
data-entering={isEntering || undefined}
|
|
186
|
+
data-exiting={isExiting || undefined}>
|
|
187
|
+
{!props.isNonModal && <DismissButton onDismiss={state.close} />}
|
|
188
|
+
<OverlayArrowContext.Provider value={{...arrowProps, placement, ref: arrowRef}}>
|
|
189
|
+
{renderProps.children}
|
|
190
|
+
</OverlayArrowContext.Provider>
|
|
191
|
+
<DismissButton onDismiss={state.close} />
|
|
192
|
+
</div>
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
// If this is a root popover, render an extra div to act as the portal container for submenus/subdialogs.
|
|
196
|
+
if (!isSubPopover) {
|
|
197
|
+
return (
|
|
198
|
+
<Overlay {...props} isExiting={isExiting} portalContainer={UNSTABLE_portalContainer}>
|
|
199
|
+
{!props.isNonModal && state.isOpen && <div data-testid="underlay" {...underlayProps} style={{position: 'fixed', inset: 0}} />}
|
|
200
|
+
<div ref={containerRef} style={{display: 'contents'}}>
|
|
201
|
+
<PopoverGroupContext.Provider value={containerRef}>
|
|
202
|
+
{overlay}
|
|
203
|
+
</PopoverGroupContext.Provider>
|
|
204
|
+
</div>
|
|
205
|
+
</Overlay>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
166
208
|
|
|
209
|
+
// Submenus/subdialogs are mounted into the root popover's container.
|
|
167
210
|
return (
|
|
168
|
-
<Overlay {...props} isExiting={isExiting} portalContainer={UNSTABLE_portalContainer}>
|
|
169
|
-
{
|
|
170
|
-
<div
|
|
171
|
-
{...mergeProps(filterDOMProps(props as any), popoverProps)}
|
|
172
|
-
{...renderProps}
|
|
173
|
-
ref={ref}
|
|
174
|
-
slot={props.slot || undefined}
|
|
175
|
-
style={style}
|
|
176
|
-
dir={props.dir}
|
|
177
|
-
data-trigger={props.trigger}
|
|
178
|
-
data-placement={placement}
|
|
179
|
-
data-entering={isEntering || undefined}
|
|
180
|
-
data-exiting={isExiting || undefined}>
|
|
181
|
-
{!props.isNonModal && <DismissButton onDismiss={state.close} />}
|
|
182
|
-
<OverlayArrowContext.Provider value={{...arrowProps, placement, ref: arrowRef}}>
|
|
183
|
-
{renderProps.children}
|
|
184
|
-
</OverlayArrowContext.Provider>
|
|
185
|
-
<DismissButton onDismiss={state.close} />
|
|
186
|
-
</div>
|
|
211
|
+
<Overlay {...props} isExiting={isExiting} portalContainer={UNSTABLE_portalContainer ?? groupCtx?.current ?? undefined}>
|
|
212
|
+
{overlay}
|
|
187
213
|
</Overlay>
|
|
188
214
|
);
|
|
189
215
|
}
|
package/src/SearchField.tsx
CHANGED
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
import {AriaSearchFieldProps, useSearchField} from 'react-aria';
|
|
14
14
|
import {ButtonContext} from './Button';
|
|
15
15
|
import {ContextValue, Provider, RACValidation, removeDataAttributes, RenderProps, SlotProps, useContextProps, useRenderProps, useSlot, useSlottedContext} from './utils';
|
|
16
|
+
import {createHideableComponent} from '@react-aria/collections';
|
|
16
17
|
import {FieldErrorContext} from './FieldError';
|
|
17
18
|
import {filterDOMProps, mergeProps} from '@react-aria/utils';
|
|
18
19
|
import {FormContext} from './Form';
|
|
19
|
-
import {forwardRefType} from '@react-types/shared';
|
|
20
20
|
import {GroupContext} from './Group';
|
|
21
21
|
import {InputContext} from './Input';
|
|
22
22
|
import {LabelContext} from './Label';
|
|
23
|
-
import React, {createContext, ForwardedRef,
|
|
23
|
+
import React, {createContext, ForwardedRef, useRef} from 'react';
|
|
24
24
|
import {SearchFieldState, useSearchFieldState} from 'react-stately';
|
|
25
25
|
import {TextContext} from './Text';
|
|
26
26
|
|
|
@@ -53,7 +53,7 @@ export const SearchFieldContext = createContext<ContextValue<SearchFieldProps, H
|
|
|
53
53
|
/**
|
|
54
54
|
* A search field allows a user to enter and clear a search query.
|
|
55
55
|
*/
|
|
56
|
-
export const SearchField = /*#__PURE__*/ (
|
|
56
|
+
export const SearchField = /*#__PURE__*/ createHideableComponent(function SearchField(props: SearchFieldProps, ref: ForwardedRef<HTMLDivElement>) {
|
|
57
57
|
[props, ref] = useContextProps(props, ref, SearchFieldContext);
|
|
58
58
|
let {validationBehavior: formValidationBehavior} = useSlottedContext(FormContext) || {};
|
|
59
59
|
let validationBehavior = props.validationBehavior ?? formValidationBehavior ?? 'native';
|
package/src/Table.tsx
CHANGED
|
@@ -41,51 +41,21 @@ class TableCollection<T> extends BaseCollection<T> implements ITableCollection<T
|
|
|
41
41
|
|
|
42
42
|
commit(firstKey: Key, lastKey: Key, isSSR = false) {
|
|
43
43
|
this.updateColumns(isSSR);
|
|
44
|
-
this.updateRows(isSSR);
|
|
45
|
-
super.commit(firstKey, lastKey, isSSR);
|
|
46
|
-
}
|
|
47
44
|
|
|
48
|
-
private updateRows(isSSR: boolean) {
|
|
49
45
|
this.rows = [];
|
|
50
|
-
let
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
let
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (rowHasCellWithColSpan) {
|
|
62
|
-
let last: GridNode<T> | null = null;
|
|
63
|
-
for (let child of childNodes) {
|
|
64
|
-
child.colSpan = child.props?.colSpan;
|
|
65
|
-
child.colspan = child.props?.colSpan;
|
|
66
|
-
child.colIndex = !last ? child.index : (last.colIndex ?? last.index) + (last.colSpan ?? 1);
|
|
67
|
-
last = child;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
let lastColIndex = last?.colIndex ?? 0 + 1; // internally colIndex is 0 based
|
|
71
|
-
let lastColSpan = last?.colSpan ?? 1;
|
|
72
|
-
let numberOfCellsInRow = lastColIndex + lastColSpan;
|
|
73
|
-
|
|
74
|
-
if (numberOfCellsInRow !== this.columns.length && !isSSR) {
|
|
75
|
-
throw new Error(`Cell count must match column count. Found ${numberOfCellsInRow} cells and ${this.columns.length} columns.`);
|
|
76
|
-
}
|
|
77
|
-
} else {
|
|
78
|
-
let numberOfCellsInRow = [...childNodes].length;
|
|
79
|
-
if (numberOfCellsInRow !== this.columns.length && !isSSR) {
|
|
80
|
-
throw new Error(`Cell count must match column count. Found ${numberOfCellsInRow} cells and ${this.columns.length} columns.`);
|
|
81
|
-
}
|
|
46
|
+
for (let row of this.getChildren(this.body.key)) {
|
|
47
|
+
let lastChildKey = (row as CollectionNode<T>).lastChildKey;
|
|
48
|
+
if (lastChildKey != null) {
|
|
49
|
+
let lastCell = this.getItem(lastChildKey) as GridNode<T>;
|
|
50
|
+
let numberOfCellsInRow = (lastCell.colIndex ?? lastCell.index) + (lastCell.colSpan ?? 1);
|
|
51
|
+
if (numberOfCellsInRow !== this.columns.length && !isSSR) {
|
|
52
|
+
throw new Error(`Cell count must match column count. Found ${numberOfCellsInRow} cells and ${this.columns.length} columns.`);
|
|
82
53
|
}
|
|
83
54
|
}
|
|
84
|
-
this.rows.push(
|
|
85
|
-
};
|
|
86
|
-
for (let child of this.getChildren(this.body.key)) {
|
|
87
|
-
visit(child);
|
|
55
|
+
this.rows.push(row);
|
|
88
56
|
}
|
|
57
|
+
|
|
58
|
+
super.commit(firstKey, lastKey, isSSR);
|
|
89
59
|
}
|
|
90
60
|
|
|
91
61
|
private updateColumns(isSSR: boolean) {
|
package/src/TableLayout.ts
CHANGED
|
@@ -15,7 +15,7 @@ import {LayoutOptionsDelegate} from './Virtualizer';
|
|
|
15
15
|
import {TableColumnResizeStateContext} from './Table';
|
|
16
16
|
import {useContext, useMemo} from 'react';
|
|
17
17
|
|
|
18
|
-
export class TableLayout<T> extends BaseTableLayout<T> implements LayoutOptionsDelegate<TableLayoutProps> {
|
|
18
|
+
export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> extends BaseTableLayout<T, O> implements LayoutOptionsDelegate<TableLayoutProps> {
|
|
19
19
|
// Invalidate the layout whenever the column widths change.
|
|
20
20
|
useLayoutOptions() {
|
|
21
21
|
// This is not a React class component, just a regular class.
|
package/src/TextField.tsx
CHANGED
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
|
|
13
13
|
import {AriaTextFieldProps, useTextField} from 'react-aria';
|
|
14
14
|
import {ContextValue, DOMProps, Provider, RACValidation, removeDataAttributes, RenderProps, SlotProps, useContextProps, useRenderProps, useSlot, useSlottedContext} from './utils';
|
|
15
|
+
import {createHideableComponent} from '@react-aria/collections';
|
|
15
16
|
import {FieldErrorContext} from './FieldError';
|
|
16
17
|
import {filterDOMProps, mergeProps} from '@react-aria/utils';
|
|
17
18
|
import {FormContext} from './Form';
|
|
18
|
-
import {forwardRefType} from '@react-types/shared';
|
|
19
19
|
import {InputContext} from './Input';
|
|
20
20
|
import {LabelContext} from './Label';
|
|
21
|
-
import React, {createContext, ForwardedRef,
|
|
21
|
+
import React, {createContext, ForwardedRef, useCallback, useRef, useState} from 'react';
|
|
22
22
|
import {TextAreaContext} from './TextArea';
|
|
23
23
|
import {TextContext} from './Text';
|
|
24
24
|
|
|
@@ -55,7 +55,7 @@ export const TextFieldContext = createContext<ContextValue<TextFieldProps, HTMLD
|
|
|
55
55
|
/**
|
|
56
56
|
* A text field allows a user to enter a plain text value with a keyboard.
|
|
57
57
|
*/
|
|
58
|
-
export const TextField = /*#__PURE__*/ (
|
|
58
|
+
export const TextField = /*#__PURE__*/ createHideableComponent(function TextField(props: TextFieldProps, ref: ForwardedRef<HTMLDivElement>) {
|
|
59
59
|
[props, ref] = useContextProps(props, ref, TextFieldContext);
|
|
60
60
|
let {validationBehavior: formValidationBehavior} = useSlottedContext(FormContext) || {};
|
|
61
61
|
let validationBehavior = props.validationBehavior ?? formValidationBehavior ?? 'native';
|
package/src/Toast.tsx
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {AriaToastProps, AriaToastRegionProps, useToast, useToastRegion} from '@react-aria/toast';
|
|
14
|
+
import {ButtonContext} from './Button';
|
|
15
|
+
import {ContextValue, DEFAULT_SLOT, Provider, RenderProps, StyleRenderProps, useContextProps, useRenderProps} from './utils';
|
|
16
|
+
import {createPortal} from 'react-dom';
|
|
17
|
+
import {forwardRefType} from '@react-types/shared';
|
|
18
|
+
import {mergeProps, useFocusRing} from 'react-aria';
|
|
19
|
+
import {QueuedToast, ToastQueue, ToastState, useToastQueue} from '@react-stately/toast';
|
|
20
|
+
import React, {createContext, ForwardedRef, forwardRef, HTMLAttributes, JSX, ReactElement, useContext} from 'react';
|
|
21
|
+
import {TextContext} from './Text';
|
|
22
|
+
import {useObjectRef} from '@react-aria/utils';
|
|
23
|
+
|
|
24
|
+
const ToastStateContext = createContext<ToastState<any> | null>(null);
|
|
25
|
+
|
|
26
|
+
export interface ToastRegionRenderProps<T> {
|
|
27
|
+
/** A list of all currently visible toasts. */
|
|
28
|
+
visibleToasts: QueuedToast<T>[],
|
|
29
|
+
/**
|
|
30
|
+
* Whether the toast region is currently focused.
|
|
31
|
+
* @selector [data-focused]
|
|
32
|
+
*/
|
|
33
|
+
isFocused: boolean,
|
|
34
|
+
/**
|
|
35
|
+
* Whether the toast region is keyboard focused.
|
|
36
|
+
* @selector [data-focus-visible]
|
|
37
|
+
*/
|
|
38
|
+
isFocusVisible: boolean
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ToastRegionProps<T> extends AriaToastRegionProps, StyleRenderProps<ToastRegionRenderProps<T>> {
|
|
42
|
+
/** The queue of toasts to display. */
|
|
43
|
+
queue: ToastQueue<T>,
|
|
44
|
+
/** A function to render each toast. */
|
|
45
|
+
children: (renderProps: {toast: QueuedToast<T>}) => ReactElement
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A ToastRegion displays one or more toast notifications.
|
|
50
|
+
*/
|
|
51
|
+
export const ToastRegion = /*#__PURE__*/ (forwardRef as forwardRefType)(function ToastRegion<T>(props: ToastRegionProps<T>, ref: ForwardedRef<HTMLDivElement>): JSX.Element | null {
|
|
52
|
+
let state = useToastQueue(props.queue);
|
|
53
|
+
let objectRef = useObjectRef(ref);
|
|
54
|
+
let {regionProps} = useToastRegion(props, state, objectRef);
|
|
55
|
+
|
|
56
|
+
let {focusProps, isFocused, isFocusVisible} = useFocusRing();
|
|
57
|
+
let renderProps = useRenderProps({
|
|
58
|
+
...props,
|
|
59
|
+
children: undefined,
|
|
60
|
+
defaultClassName: 'react-aria-ToastRegion',
|
|
61
|
+
values: {
|
|
62
|
+
visibleToasts: state.visibleToasts,
|
|
63
|
+
isFocused,
|
|
64
|
+
isFocusVisible
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
let region = (
|
|
69
|
+
<ToastStateContext.Provider value={state}>
|
|
70
|
+
<div
|
|
71
|
+
{...renderProps}
|
|
72
|
+
{...mergeProps(regionProps, focusProps)}
|
|
73
|
+
ref={objectRef}
|
|
74
|
+
data-focused={isFocused || undefined}
|
|
75
|
+
data-focus-visible={isFocusVisible || undefined}>
|
|
76
|
+
{typeof props.children === 'function' ? <ToastList {...props} style={{display: 'contents'}} /> : props.children}
|
|
77
|
+
</div>
|
|
78
|
+
</ToastStateContext.Provider>
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
return state.visibleToasts.length > 0 && typeof document !== 'undefined'
|
|
82
|
+
? createPortal(region, document.body)
|
|
83
|
+
: null;
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// TODO: possibly export this so additional children can be added to the region, outside the list.
|
|
87
|
+
const ToastList = /*#__PURE__*/ (forwardRef as forwardRefType)(function ToastList<T>(props: ToastRegionProps<T>, ref: ForwardedRef<HTMLOListElement>) {
|
|
88
|
+
let state = useContext(ToastStateContext)!;
|
|
89
|
+
return (
|
|
90
|
+
// @ts-ignore
|
|
91
|
+
<ol ref={ref} style={props.style} className={props.className}>
|
|
92
|
+
{state.visibleToasts.map((toast) => (
|
|
93
|
+
<li key={toast.key} style={{display: 'contents'}}>
|
|
94
|
+
{props.children({toast})}
|
|
95
|
+
</li>
|
|
96
|
+
))}
|
|
97
|
+
</ol>
|
|
98
|
+
);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
export interface ToastRenderProps<T> {
|
|
102
|
+
/**
|
|
103
|
+
* The toast object to display.
|
|
104
|
+
*/
|
|
105
|
+
toast: QueuedToast<T>,
|
|
106
|
+
/**
|
|
107
|
+
* Whether the toast is currently focused.
|
|
108
|
+
* @selector [data-focused]
|
|
109
|
+
*/
|
|
110
|
+
isFocused: boolean,
|
|
111
|
+
/**
|
|
112
|
+
* Whether the toast is keyboard focused.
|
|
113
|
+
* @selector [data-focus-visible]
|
|
114
|
+
*/
|
|
115
|
+
isFocusVisible: boolean
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface ToastProps<T> extends AriaToastProps<T>, RenderProps<ToastRenderProps<T>> {}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* A Toast displays a brief, temporary notification of actions, errors, or other events in an application.
|
|
122
|
+
*/
|
|
123
|
+
export const Toast = /*#__PURE__*/ (forwardRef as forwardRefType)(function Toast<T>(props: ToastProps<T>, ref: ForwardedRef<HTMLDivElement>) {
|
|
124
|
+
let state = useContext(ToastStateContext)!;
|
|
125
|
+
let objectRef = useObjectRef(ref);
|
|
126
|
+
let {toastProps, contentProps, titleProps, descriptionProps, closeButtonProps} = useToast(
|
|
127
|
+
props,
|
|
128
|
+
state,
|
|
129
|
+
objectRef
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
let {focusProps, isFocused, isFocusVisible} = useFocusRing();
|
|
133
|
+
let renderProps = useRenderProps({
|
|
134
|
+
...props,
|
|
135
|
+
defaultClassName: 'react-aria-Toast',
|
|
136
|
+
values: {
|
|
137
|
+
toast: props.toast,
|
|
138
|
+
isFocused,
|
|
139
|
+
isFocusVisible
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<div
|
|
145
|
+
{...renderProps}
|
|
146
|
+
{...mergeProps(toastProps, focusProps)}
|
|
147
|
+
ref={objectRef}
|
|
148
|
+
data-focused={isFocused || undefined}
|
|
149
|
+
data-focus-visible={isFocusVisible || undefined}>
|
|
150
|
+
<Provider
|
|
151
|
+
values={[
|
|
152
|
+
[ToastContentContext, contentProps],
|
|
153
|
+
[TextContext, {
|
|
154
|
+
slots: {
|
|
155
|
+
[DEFAULT_SLOT]: {},
|
|
156
|
+
title: titleProps,
|
|
157
|
+
description: descriptionProps
|
|
158
|
+
}
|
|
159
|
+
}],
|
|
160
|
+
[ButtonContext, {
|
|
161
|
+
slots: {
|
|
162
|
+
[DEFAULT_SLOT]: {},
|
|
163
|
+
close: closeButtonProps
|
|
164
|
+
}
|
|
165
|
+
}]
|
|
166
|
+
]}>
|
|
167
|
+
{renderProps.children}
|
|
168
|
+
</Provider>
|
|
169
|
+
</div>
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
export const ToastContentContext = createContext<ContextValue<HTMLAttributes<HTMLElement>, HTMLDivElement>>({});
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* ToastContent wraps the main content of a toast notification.
|
|
177
|
+
*/
|
|
178
|
+
export const ToastContent = /*#__PURE__*/ forwardRef(function ToastContent(props: HTMLAttributes<HTMLElement>, ref: ForwardedRef<HTMLDivElement>) {
|
|
179
|
+
[props, ref] = useContextProps(props, ref, ToastContentContext);
|
|
180
|
+
return (
|
|
181
|
+
<div className="react-aria-ToastContent" {...props} ref={ref}>
|
|
182
|
+
{props.children}
|
|
183
|
+
</div>
|
|
184
|
+
);
|
|
185
|
+
});
|