diff --git a/packages/dev/s2-docs/pages/s2/NumberField.mdx b/packages/dev/s2-docs/pages/s2/NumberField.mdx
index 733673f2c0c..b8837eb2dbc 100644
--- a/packages/dev/s2-docs/pages/s2/NumberField.mdx
+++ b/packages/dev/s2-docs/pages/s2/NumberField.mdx
@@ -12,9 +12,10 @@ import docs from 'docs:@react-spectrum/s2';
component={NumberField}
docs={docs.exports.NumberField}
links={docs.links}
- props={['label', 'size', 'labelPosition', 'description', 'contextualHelp', 'hideStepper', 'isDisabled']}
+ props={['label', 'placeholder', 'size', 'labelPosition', 'description', 'contextualHelp', 'hideStepper', 'isDisabled']}
initialProps={{
label: 'Width',
+ placeholder: 'Enter a number',
defaultValue: 1024,
minValue: 0
}}
@@ -36,6 +37,7 @@ function Example() {
-
+
```
@@ -103,7 +110,7 @@ import {Provider, NumberField} from '@react-spectrum/s2';
Use the `name` prop to submit the raw number value (not a formatted string) to the server. Set the `isRequired` prop to validate that the user enters a value, or implement custom client or server-side validation. See the Forms guide to learn more.
-```tsx render docs={docs.exports.NumberField} links={docs.links} props={['isRequired', 'necessityIndicator']} initialProps={{isRequired: true}} type="s2"
+```tsx render docs={docs.exports.NumberField} links={docs.links} props={['isRequired', 'necessityIndicator']} initialProps={{isRequired: true, placeholder: 'Enter a number'}} type="s2"
"use client";
import {NumberField, Button, Form} from '@react-spectrum/s2';
@@ -126,4 +133,4 @@ function Example(props) {
## API
-
\ No newline at end of file
+
diff --git a/packages/dev/s2-docs/pages/s2/SearchField.mdx b/packages/dev/s2-docs/pages/s2/SearchField.mdx
index b73936a05b2..b935438595c 100644
--- a/packages/dev/s2-docs/pages/s2/SearchField.mdx
+++ b/packages/dev/s2-docs/pages/s2/SearchField.mdx
@@ -12,9 +12,10 @@ import docs from 'docs:@react-spectrum/s2';
component={SearchField}
docs={docs.exports.SearchField}
links={docs.links}
- props={['label', 'size', 'labelPosition', 'description', 'contextualHelp', 'isDisabled']}
+ props={['label', 'placeholder', 'size', 'labelPosition', 'description', 'contextualHelp', 'isDisabled']}
initialProps={{
- label: 'Search'
+ label: 'Search',
+ placeholder: 'Search documents'
}}
importSource="@react-spectrum/s2"
type="s2" />
@@ -31,11 +32,12 @@ import {useState} from 'react';
function Example() {
let [search, setSearch] = useState('');
let [submittedSearch, setSubmittedSearch] = useState('');
-
+
return (
@@ -30,11 +31,12 @@ import {useState} from 'react';
function Example() {
let [name, setName] = useState('');
-
+
return (
<>
@@ -59,6 +61,7 @@ function Example(props) {
@@ -30,11 +31,12 @@ import {useState} from 'react';
function Example() {
let [name, setName] = useState('');
-
+
return (
<>
@@ -59,6 +61,7 @@ function Example(props) {
-
+
{item => }
diff --git a/packages/dev/s2-docs/src/IllustrationCards.tsx b/packages/dev/s2-docs/src/IllustrationCards.tsx
index 56b1bcb6fbd..b91c11add1d 100644
--- a/packages/dev/s2-docs/src/IllustrationCards.tsx
+++ b/packages/dev/s2-docs/src/IllustrationCards.tsx
@@ -60,7 +60,7 @@ export function IllustrationCards() {
Linear
-
+
{variant === 'gradient' && (
{result}{result ? '\n ' + indent : null}{badge}>;
}
-
+
return result;
- } else if (children?.text) {
+ } else if (children?.text != null) {
return children.text;
} else if (Array.isArray(children)) {
return children.map((c, i) => {i > 0 ? '\n ' + indent : null}{c});
@@ -379,13 +379,13 @@ function renderImports(name: string, importSource: string, props: Props) {
if (props.contextualHelp) {
components.push('ContextualHelp', 'Heading', 'Content');
}
-
+
imports.push(renderImport(components.join(', '), importSource));
if (props.children?.icon && !props.children?.avatar) {
imports.push('\n', renderImport(props.children.icon.replace(/^(\d)/, '_$1'), `@react-spectrum/s2/icons/${props.children.icon}`, true));
}
-
+
imports.push('\n\n');
return imports;
}
@@ -474,7 +474,7 @@ function UnionControl({control, value, onChange, isPicker = false}) {
let length = control.value.elements.reduce((p, v) => p + v.value, '').length;
if (isPicker || control.options?.control === 'picker' || length > 18) {
return (
- }
selectedKey={value == null && control.optional && !control.default ? '__none' : value}
@@ -567,6 +567,7 @@ function NumberControl({control, value, onChange}: ControlProps) {
return (
}
value={value}
onChange={onChange}
@@ -688,6 +689,7 @@ function StringControl({control, value, onChange}: ControlProps) {
return (
}
value={value || ''}
onChange={onChange}
@@ -704,6 +706,7 @@ function ChildrenControl({control, value, onChange}: ControlProps) {
onChange({...objectValue, text})}
styles={style({width: 80, flexGrow: 1})} />
@@ -714,14 +717,14 @@ function ChildrenControl({control, value, onChange}: ControlProps) {
)}
{(control.slots.avatar || control.slots.badge) &&
- {control.slots.avatar &&
+ {control.slots.avatar &&
onChange({...objectValue, avatar})}>
Avatar
}
- {control.slots.badge &&
+ {control.slots.badge &&
onChange({...objectValue, badge})}>
@@ -848,7 +851,7 @@ function LocaleControl({control, value, onChange}: ControlProps) {
let updateLocale = locale => {
let calendar, numberingSystem;
if (extension === 'calendar') {
- calendar = (preferences.find(p => p.value === locale)?.ordering || 'gregory').split(' ')[0];
+ calendar = (preferences.find(p => p.value === locale)?.ordering || 'gregory').split(' ')[0];
} else if (extension === 'numberingSystem') {
numberingSystem = new Intl.NumberFormat(locale).resolvedOptions().numberingSystem;
if (numberingSystem === 'arabext') {
@@ -930,6 +933,7 @@ function DurationControl({control, value, onChange}: ControlProps) {
return (
}
value={value.months}
minValue={1}
@@ -983,7 +987,7 @@ function ColorSpaceControl({control, value}) {
if (props.channel) {
props.channel = getColorChannels(colorSpace)[0];
}
-
+
delete props.xChannel;
delete props.yChannel;
return props;
@@ -1034,7 +1038,7 @@ function PlacementControl({control, value, onChange}) {
function PlacementControlItem(props) {
return (
- (null);
return (
- {value.length === 0 &&
+ {value.length === 0 &&
onChange(new Size(width, value?.height ?? 0))}
styles={style({flexShrink: 1, flexGrow: 1})}
hideStepper />
onChange(new Size(value?.width ?? 0, height))}
styles={style({flexShrink: 1, flexGrow: 1})}
diff --git a/packages/react-aria-components/src/Autocomplete.tsx b/packages/react-aria-components/src/Autocomplete.tsx
index 11370b2363a..34dc9c46f44 100644
--- a/packages/react-aria-components/src/Autocomplete.tsx
+++ b/packages/react-aria-components/src/Autocomplete.tsx
@@ -12,7 +12,7 @@
import {AriaAutocompleteProps, useAutocomplete} from '@react-aria/autocomplete';
import {AutocompleteState, useAutocompleteState} from '@react-stately/autocomplete';
-import {FieldInputContext, SelectableCollectionContext} from './context';
+import {FieldInputContext, SelectableCollectionContext} from './RSPContexts';
import {mergeProps} from '@react-aria/utils';
import {Provider, removeDataAttributes, SlotProps, SlottedContextValue, useSlottedContext} from './utils';
import React, {createContext, JSX, useRef} from 'react';
diff --git a/packages/react-aria-components/src/GridList.tsx b/packages/react-aria-components/src/GridList.tsx
index 5229595ea62..6c087d25295 100644
--- a/packages/react-aria-components/src/GridList.tsx
+++ b/packages/react-aria-components/src/GridList.tsx
@@ -11,17 +11,15 @@
*/
import {AriaGridListProps, DraggableItemResult, DragPreviewRenderer, DropIndicatorAria, DroppableCollectionResult, FocusScope, ListKeyboardDelegate, mergeProps, useCollator, useFocusRing, useGridList, useGridListItem, useGridListSection, useGridListSelectionCheckbox, useHover, useLocale, useVisuallyHidden} from 'react-aria';
import {ButtonContext} from './Button';
-import {CheckboxContext} from './RSPContexts';
+import {CheckboxContext, FieldInputContext, SelectableCollectionContext, SelectableCollectionContextValue} from './RSPContexts';
import {Collection, CollectionBuilder, createBranchComponent, createLeafComponent, HeaderNode, ItemNode, LoaderNode, SectionNode} from '@react-aria/collections';
import {CollectionProps, CollectionRendererContext, DefaultCollectionRenderer, ItemRenderProps, SectionProps} from './Collection';
import {ContextValue, DEFAULT_SLOT, Provider, RenderProps, SlotProps, StyleProps, StyleRenderProps, useContextProps, useRenderProps} from './utils';
import {DragAndDropContext, DropIndicatorContext, DropIndicatorProps, useDndPersistedKeys, useRenderDropIndicator} from './DragAndDrop';
import {DragAndDropHooks} from './useDragAndDrop';
import {DraggableCollectionState, DroppableCollectionState, Collection as ICollection, ListState, Node, SelectionBehavior, UNSTABLE_useFilteredListState, useListState} from 'react-stately';
-import {FieldInputContext, SelectableCollectionContext} from './context';
import {filterDOMProps, inertValue, LoadMoreSentinelProps, useLoadMoreSentinel, useObjectRef} from '@react-aria/utils';
import {forwardRefType, GlobalDOMAttributes, HoverEvents, Key, LinkDOMProps, PressEvents, RefObject} from '@react-types/shared';
-import {HeaderContext} from './Header';
import {ListStateContext} from './ListBox';
import React, {createContext, ForwardedRef, forwardRef, HTMLAttributes, JSX, ReactNode, useContext, useEffect, useMemo, useRef} from 'react';
import {SelectionIndicatorContext} from './SelectionIndicator';
@@ -101,29 +99,25 @@ export const GridList = /*#__PURE__*/ (forwardRef as forwardRefType)(function Gr
});
interface GridListInnerProps {
- props: GridListProps,
+ props: GridListProps & SelectableCollectionContextValue,
collection: ICollection>,
- gridListRef: RefObject
+ gridListRef: RefObject
}
function GridListInner({props, collection, gridListRef: ref}: GridListInnerProps) {
- // TODO: for now, don't grab collection ref and collectionProps from the autocomplete, rely on the user tabbing to the gridlist
- // figure out if we want to support virtual focus for grids when wrapped in an autocomplete
- let contextProps;
- [contextProps] = useContextProps({}, null, SelectableCollectionContext);
- let {filter, ...collectionProps} = contextProps;
+ [props, ref] = useContextProps(props, ref, SelectableCollectionContext);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- let {shouldUseVirtualFocus, disallowTypeAhead, ...DOMCollectionProps} = collectionProps || {};
+ let {shouldUseVirtualFocus, filter, disallowTypeAhead, ...DOMCollectionProps} = props;
let {dragAndDropHooks, keyboardNavigationBehavior = 'arrow', layout = 'stack'} = props;
let {CollectionRoot, isVirtualized, layoutDelegate, dropTargetDelegate: ctxDropTargetDelegate} = useContext(CollectionRendererContext);
let gridlistState = useListState({
- ...props,
+ ...DOMCollectionProps,
collection,
children: undefined,
layoutDelegate
});
- let filteredState = UNSTABLE_useFilteredListState(gridlistState, filter);
+ let filteredState = UNSTABLE_useFilteredListState(gridlistState as ListState, filter);
let collator = useCollator({usage: 'search', sensitivity: 'base'});
let {disabledBehavior, disabledKeys} = filteredState.selectionManager;
let {direction} = useLocale();
@@ -141,7 +135,6 @@ function GridListInner({props, collection, gridListRef: ref}:
), [filteredState.collection, ref, layout, disabledKeys, disabledBehavior, layoutDelegate, collator, direction]);
let {gridProps} = useGridList({
- ...props,
...DOMCollectionProps,
keyboardDelegate,
// Only tab navigation is supported in grid layout.
@@ -246,7 +239,7 @@ function GridListInner({props, collection, gridListRef: ref}:
}
slot={props.slot || undefined}
onScroll={props.onScroll}
data-drop-target={isRootDropTarget || undefined}
@@ -585,11 +578,11 @@ export interface GridListSectionProps
extends SectionProps {}
/**
* A GridListSection represents a section within a GridList.
*/
-export const GridListSection = /*#__PURE__*/ createBranchComponent(SectionNode, (props: GridListSectionProps, ref: ForwardedRef, item: Node) => {
+export const GridListSection = /*#__PURE__*/ createBranchComponent(SectionNode, (props: GridListSectionProps, ref: ForwardedRef, item: Node) => {
let state = useContext(ListStateContext)!;
let {CollectionBranch} = useContext(CollectionRendererContext);
let headingRef = useRef(null);
- ref = useObjectRef(ref);
+ ref = useObjectRef(ref);
let {rowHeaderProps, rowProps, rowGroupProps} = useGridListSection({
'aria-label': props['aria-label'] ?? undefined
}, state, ref);
@@ -604,33 +597,34 @@ export const GridListSection = /*#__PURE__*/ createBranchComponent(SectionNode,
delete DOMProps.id;
return (
-
+
);
});
-const GridListHeaderContext = createContext | null>(null);
+export const GridListHeaderContext = createContext, HTMLDivElement>>({});
+const GridListHeaderInnerContext = createContext | null>(null);
-export const GridListHeader = /*#__PURE__*/ createLeafComponent(HeaderNode, function Header(props: HTMLAttributes, ref: ForwardedRef) {
- [props, ref] = useContextProps(props, ref, HeaderContext);
- let rowHeaderProps = useContext(GridListHeaderContext);
+export const GridListHeader = /*#__PURE__*/ createLeafComponent(HeaderNode, function Header(props: HTMLAttributes, ref: ForwardedRef) {
+ [props, ref] = useContextProps(props, ref, GridListHeaderContext);
+ let rowHeaderProps = useContext(GridListHeaderInnerContext);
return (
-
+
);
});
diff --git a/packages/react-aria-components/src/Input.tsx b/packages/react-aria-components/src/Input.tsx
index 9cd46c17feb..c3f4b00a862 100644
--- a/packages/react-aria-components/src/Input.tsx
+++ b/packages/react-aria-components/src/Input.tsx
@@ -43,7 +43,13 @@ export interface InputRenderProps {
isInvalid: boolean
}
-export interface InputProps extends Omit, 'className' | 'style'>, HoverEvents, StyleRenderProps {}
+export interface InputProps extends Omit, 'className' | 'style'>, HoverEvents, StyleRenderProps {
+ /**
+ * Temporary text that occupies the text input when it is empty.
+ * See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/placeholder).
+ */
+ placeholder?: string
+}
export const InputContext = createContext>({});
diff --git a/packages/react-aria-components/src/ListBox.tsx b/packages/react-aria-components/src/ListBox.tsx
index 5e0ba0d5e22..36464a35bd5 100644
--- a/packages/react-aria-components/src/ListBox.tsx
+++ b/packages/react-aria-components/src/ListBox.tsx
@@ -21,7 +21,7 @@ import {filterDOMProps, inertValue, LoadMoreSentinelProps, useLoadMoreSentinel,
import {forwardRefType, GlobalDOMAttributes, HoverEvents, Key, LinkDOMProps, PressEvents, RefObject} from '@react-types/shared';
import {HeaderContext} from './Header';
import React, {createContext, ForwardedRef, forwardRef, JSX, ReactNode, useContext, useEffect, useMemo, useRef} from 'react';
-import {SelectableCollectionContext, SelectableCollectionContextValue} from './context';
+import {SelectableCollectionContext, SelectableCollectionContextValue} from './RSPContexts';
import {SelectionIndicatorContext} from './SelectionIndicator';
import {SeparatorContext} from './Separator';
import {SharedElementTransition} from './SharedElementTransition';
diff --git a/packages/react-aria-components/src/Menu.tsx b/packages/react-aria-components/src/Menu.tsx
index c9eac9b1c93..635e9ed3ac5 100644
--- a/packages/react-aria-components/src/Menu.tsx
+++ b/packages/react-aria-components/src/Menu.tsx
@@ -15,7 +15,7 @@ import {BaseCollection, Collection, CollectionBuilder, CollectionNode, createBra
import {MenuTriggerProps as BaseMenuTriggerProps, Collection as ICollection, Node, RootMenuTriggerState, TreeState, useMenuTriggerState, useSubmenuTriggerState, useTreeState} from 'react-stately';
import {CollectionProps, CollectionRendererContext, ItemRenderProps, SectionContext, SectionProps, usePersistedKeys} from './Collection';
import {ContextValue, DEFAULT_SLOT, Provider, RenderProps, SlotProps, StyleRenderProps, useContextProps, useRenderProps, useSlot, useSlottedContext} from './utils';
-import {FieldInputContext, SelectableCollectionContext, SelectableCollectionContextValue} from './context';
+import {FieldInputContext, SelectableCollectionContext, SelectableCollectionContextValue} from './RSPContexts';
import {filterDOMProps, useObjectRef, useResizeObserver} from '@react-aria/utils';
import {FocusStrategy, forwardRefType, GlobalDOMAttributes, HoverEvents, Key, LinkDOMProps, MultipleSelection, PressEvents} from '@react-types/shared';
import {HeaderContext} from './Header';
diff --git a/packages/react-aria-components/src/RSPContexts.ts b/packages/react-aria-components/src/RSPContexts.ts
index 2b5db6a7462..1e45acc3e11 100644
--- a/packages/react-aria-components/src/RSPContexts.ts
+++ b/packages/react-aria-components/src/RSPContexts.ts
@@ -10,6 +10,8 @@
* governing permissions and limitations under the License.
*/
+import {AriaLabelingProps, DOMProps, FocusableElement, FocusEvents, KeyboardEvents, Node, ValueBase} from '@react-types/shared';
+import {AriaTextFieldProps} from '@react-aria/textfield';
import {CheckboxProps} from './Checkbox';
import {ColorAreaProps} from './ColorArea';
import {ColorFieldProps} from './ColorField';
@@ -31,3 +33,20 @@ export const ColorFieldContext = createContext, HTMLDivElement>>(null);
export const ColorWheelContext = createContext, HTMLDivElement>>(null);
export const HeadingContext = createContext>({});
+
+export interface SelectableCollectionContextValue extends DOMProps, AriaLabelingProps {
+ filter?: (nodeTextValue: string, node: Node) => boolean,
+ /** Whether the collection items should use virtual focus instead of being focused directly. */
+ shouldUseVirtualFocus?: boolean,
+ /** Whether typeahead is disabled. */
+ disallowTypeAhead?: boolean
+}
+interface FieldInputContextValue extends
+ DOMProps,
+ FocusEvents,
+ KeyboardEvents,
+ Pick, 'onChange' | 'value'>,
+ Pick {}
+
+export const SelectableCollectionContext = createContext, HTMLElement>>(null);
+export const FieldInputContext = createContext>(null);
diff --git a/packages/react-aria-components/src/SearchField.tsx b/packages/react-aria-components/src/SearchField.tsx
index db871dc545a..43c708c2575 100644
--- a/packages/react-aria-components/src/SearchField.tsx
+++ b/packages/react-aria-components/src/SearchField.tsx
@@ -15,7 +15,7 @@ import {ButtonContext} from './Button';
import {ContextValue, Provider, RACValidation, removeDataAttributes, RenderProps, SlotProps, useContextProps, useRenderProps, useSlot, useSlottedContext} from './utils';
import {createHideableComponent} from '@react-aria/collections';
import {FieldErrorContext} from './FieldError';
-import {FieldInputContext} from './context';
+import {FieldInputContext} from './RSPContexts';
import {filterDOMProps} from '@react-aria/utils';
import {FormContext} from './Form';
import {GlobalDOMAttributes} from '@react-types/shared';
diff --git a/packages/react-aria-components/src/Table.tsx b/packages/react-aria-components/src/Table.tsx
index 19d2ae1a46b..9d97a2bf3c7 100644
--- a/packages/react-aria-components/src/Table.tsx
+++ b/packages/react-aria-components/src/Table.tsx
@@ -2,7 +2,7 @@ import {AriaLabelingProps, GlobalDOMAttributes, HoverEvents, Key, LinkDOMProps,
import {BaseCollection, Collection, CollectionBuilder, CollectionNode, createBranchComponent, createLeafComponent, FilterableNode, LoaderNode, useCachedChildren} from '@react-aria/collections';
import {buildHeaderRows, TableColumnResizeState} from '@react-stately/table';
import {ButtonContext} from './Button';
-import {CheckboxContext} from './RSPContexts';
+import {CheckboxContext, FieldInputContext, SelectableCollectionContext, SelectableCollectionContextValue} from './RSPContexts';
import {CollectionProps, CollectionRendererContext, DefaultCollectionRenderer, ItemRenderProps} from './Collection';
import {ColumnSize, ColumnStaticSize, TableCollection as ITableCollection, TableProps as SharedTableProps} from '@react-types/table';
import {ContextValue, DEFAULT_SLOT, DOMProps, Provider, RenderProps, SlotProps, StyleProps, StyleRenderProps, useContextProps, useRenderProps} from './utils';
@@ -10,7 +10,6 @@ import {DisabledBehavior, DraggableCollectionState, DroppableCollectionState, Mu
import {DragAndDropContext, DropIndicatorContext, DropIndicatorProps, useDndPersistedKeys, useRenderDropIndicator} from './DragAndDrop';
import {DragAndDropHooks} from './useDragAndDrop';
import {DraggableItemResult, DragPreviewRenderer, DropIndicatorAria, DroppableCollectionResult, FocusScope, ListKeyboardDelegate, mergeProps, useFocusRing, useHover, useLocale, useLocalizedStringFormatter, useTable, useTableCell, useTableColumnHeader, useTableColumnResize, useTableHeaderRow, useTableRow, useTableRowGroup, useTableSelectAllCheckbox, useTableSelectionCheckbox, useVisuallyHidden} from 'react-aria';
-import {FieldInputContext, SelectableCollectionContext} from './context';
import {filterDOMProps, inertValue, isScrollable, LoadMoreSentinelProps, mergeRefs, useLayoutEffect, useLoadMoreSentinel, useObjectRef, useResizeObserver} from '@react-aria/utils';
import {GridNode} from '@react-types/grid';
// @ts-ignore
@@ -357,23 +356,21 @@ export const Table = forwardRef(function Table(props: TableProps, ref: Forwarded
});
interface TableInnerProps {
- props: TableProps,
- forwardedRef: ForwardedRef,
+ props: TableProps & SelectableCollectionContextValue,
+ forwardedRef: ForwardedRef,
selectionState: MultipleSelectionState,
collection: ITableCollection>
}
function TableInner({props, forwardedRef: ref, selectionState, collection}: TableInnerProps) {
- let contextProps;
- [contextProps] = useContextProps({}, null, SelectableCollectionContext);
- let {filter, ...collectionProps} = contextProps;
+ [props, ref] = useContextProps(props, ref, SelectableCollectionContext);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- let {shouldUseVirtualFocus, disallowTypeAhead, ...DOMCollectionProps} = collectionProps || {};
+ let {shouldUseVirtualFocus, disallowTypeAhead, filter, ...DOMCollectionProps} = props;
let tableContainerContext = useContext(ResizableTableContainerContext);
ref = useObjectRef(useMemo(() => mergeRefs(ref, tableContainerContext?.tableRef), [ref, tableContainerContext?.tableRef]));
let tableState = useTableState({
- ...props,
+ ...DOMCollectionProps,
collection,
children: undefined,
UNSAFE_selectionState: selectionState
@@ -383,7 +380,6 @@ function TableInner({props, forwardedRef: ref, selectionState, collection}: Tabl
let {isVirtualized, layoutDelegate, dropTargetDelegate: ctxDropTargetDelegate, CollectionRoot} = useContext(CollectionRendererContext);
let {dragAndDropHooks} = props;
let {gridProps} = useTable({
- ...props,
...DOMCollectionProps,
layoutDelegate,
isVirtualized
@@ -495,7 +491,7 @@ function TableInner({props, forwardedRef: ref, selectionState, collection}: Tabl
}
slot={props.slot || undefined}
onScroll={props.onScroll}
data-allows-dragging={isListDraggable || undefined}
diff --git a/packages/react-aria-components/src/TagGroup.tsx b/packages/react-aria-components/src/TagGroup.tsx
index 3fd30b4455f..03d5857224f 100644
--- a/packages/react-aria-components/src/TagGroup.tsx
+++ b/packages/react-aria-components/src/TagGroup.tsx
@@ -16,12 +16,12 @@ import {Collection, CollectionBuilder, createLeafComponent, ItemNode} from '@rea
import {CollectionProps, CollectionRendererContext, DefaultCollectionRenderer, ItemRenderProps, usePersistedKeys} from './Collection';
import {ContextValue, DOMProps, Provider, RenderProps, SlotProps, StyleRenderProps, useContextProps, useRenderProps, useSlot} from './utils';
import {filterDOMProps, mergeProps, useObjectRef} from '@react-aria/utils';
-import {forwardRefType, GlobalDOMAttributes, HoverEvents, Key, LinkDOMProps, PressEvents} from '@react-types/shared';
+import {forwardRefType, GlobalDOMAttributes, HoverEvents, Key, LinkDOMProps, PressEvents, RefObject} from '@react-types/shared';
import {LabelContext} from './Label';
import {ListState, Node, UNSTABLE_useFilteredListState, useListState} from 'react-stately';
import {ListStateContext} from './ListBox';
import React, {createContext, ForwardedRef, forwardRef, JSX, ReactNode, useContext, useEffect, useRef} from 'react';
-import {SelectableCollectionContext} from './context';
+import {SelectableCollectionContext, SelectableCollectionContextValue} from './RSPContexts';
import {SelectionIndicatorContext} from './SelectionIndicator';
import {SharedElementTransition} from './SharedElementTransition';
import {TextContext} from './Text';
@@ -72,48 +72,48 @@ export const TagGroup = /*#__PURE__*/ (forwardRef as forwardRefType)(function Ta
);
});
-interface TagGroupInnerProps {
- props: TagGroupProps,
+interface TagGroupInnerProps {
+ props: TagGroupProps & SelectableCollectionContextValue,
forwardedRef: ForwardedRef,
collection
}
-function TagGroupInner({props, forwardedRef: ref, collection}: TagGroupInnerProps) {
- let contextProps;
- [contextProps] = useContextProps({}, null, SelectableCollectionContext);
- let {filter, ...collectionProps} = contextProps;
+function TagGroupInner({props, forwardedRef: ref, collection}: TagGroupInnerProps) {
+ let tagListRef = useRef(null);
+ // Extract the user provided id so it doesn't clash with the collection id provided by Autocomplete
+ let {id, ...otherProps} = props;
+ [otherProps, tagListRef] = useContextProps(otherProps, tagListRef, SelectableCollectionContext);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- let {shouldUseVirtualFocus, disallowTypeAhead, ...DOMCollectionProps} = collectionProps || {};
- let tagListRef = useRef(null);
+ let {filter, shouldUseVirtualFocus, ...DOMCollectionProps} = otherProps;
let [labelRef, label] = useSlot(
!props['aria-label'] && !props['aria-labelledby']
);
let tagGroupState = useListState({
- ...props,
+ ...DOMCollectionProps,
children: undefined,
collection
});
- let filteredState = UNSTABLE_useFilteredListState(tagGroupState, filter);
+ let filteredState = UNSTABLE_useFilteredListState(tagGroupState as ListState, filter);
// Prevent DOM props from going to two places.
- let domProps = filterDOMProps(props, {global: true});
- let domPropOverrides = Object.fromEntries(Object.entries(domProps).map(([k]) => [k, undefined]));
+ let domProps = filterDOMProps(otherProps, {global: true});
+ let domPropOverrides = Object.fromEntries(Object.entries(domProps).map(([k, val]) => [k, k === 'id' ? val : undefined]));
let {
gridProps,
labelProps,
descriptionProps,
errorMessageProps
} = useTagGroup({
- ...props,
- ...domPropOverrides,
...DOMCollectionProps,
+ ...domPropOverrides,
label
}, filteredState, tagListRef);
return (
}],
[ListStateContext, filteredState],
[TextContext, {
slots: {
diff --git a/packages/react-aria-components/src/TextField.tsx b/packages/react-aria-components/src/TextField.tsx
index 3899bf22b07..5be86cef6d3 100644
--- a/packages/react-aria-components/src/TextField.tsx
+++ b/packages/react-aria-components/src/TextField.tsx
@@ -14,7 +14,7 @@ import {AriaTextFieldProps, useTextField} from 'react-aria';
import {ContextValue, DOMProps, Provider, RACValidation, removeDataAttributes, RenderProps, SlotProps, useContextProps, useRenderProps, useSlot, useSlottedContext} from './utils';
import {createHideableComponent} from '@react-aria/collections';
import {FieldErrorContext} from './FieldError';
-import {FieldInputContext} from './context';
+import {FieldInputContext} from './RSPContexts';
import {filterDOMProps} from '@react-aria/utils';
import {FormContext} from './Form';
import {GlobalDOMAttributes} from '@react-types/shared';
diff --git a/packages/react-aria-components/src/context.tsx b/packages/react-aria-components/src/context.tsx
deleted file mode 100644
index 4ce7a97be30..00000000000
--- a/packages/react-aria-components/src/context.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2025 Adobe. All rights reserved.
- * This file is licensed to you under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License. You may obtain a copy
- * of the License at http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
- * OF ANY KIND, either express or implied. See the License for the specific language
- * governing permissions and limitations under the License.
- */
-
-import {AriaLabelingProps, DOMProps, FocusableElement, FocusEvents, KeyboardEvents, Node, ValueBase} from '@react-types/shared';
-import {AriaTextFieldProps} from '@react-aria/textfield';
-import {ContextValue} from './utils';
-import {createContext} from 'react';
-
-export interface SelectableCollectionContextValue
extends DOMProps, AriaLabelingProps {
- filter?: (nodeTextValue: string, node: Node) => boolean,
- /** Whether the collection items should use virtual focus instead of being focused directly. */
- shouldUseVirtualFocus?: boolean,
- /** Whether typeahead is disabled. */
- disallowTypeAhead?: boolean
-}
-
-interface FieldInputContextValue extends
- DOMProps,
- FocusEvents,
- KeyboardEvents,
- Pick, 'onChange' | 'value'>,
- Pick {}
-
-export const SelectableCollectionContext = createContext, HTMLElement>>(null);
-export const FieldInputContext = createContext>(null);
diff --git a/packages/react-aria-components/src/index.ts b/packages/react-aria-components/src/index.ts
index a76ea9c6e8c..dc9bdfb3909 100644
--- a/packages/react-aria-components/src/index.ts
+++ b/packages/react-aria-components/src/index.ts
@@ -14,7 +14,7 @@
// to import it from a React Server Component in a framework like Next.js.
import 'client-only';
-export {CheckboxContext, ColorAreaContext, ColorFieldContext, ColorSliderContext, ColorWheelContext, HeadingContext} from './RSPContexts';
+export {CheckboxContext, ColorAreaContext, ColorFieldContext, ColorSliderContext, ColorWheelContext, HeadingContext, SelectableCollectionContext, FieldInputContext} from './RSPContexts';
export {Autocomplete, AutocompleteContext, AutocompleteStateContext} from './Autocomplete';
export {Breadcrumbs, BreadcrumbsContext, Breadcrumb} from './Breadcrumbs';
@@ -39,7 +39,7 @@ export {DropZone, DropZoneContext} from './DropZone';
export {FieldError, FieldErrorContext} from './FieldError';
export {FileTrigger} from './FileTrigger';
export {Form, FormContext} from './Form';
-export {GridListLoadMoreItem, GridList, GridListItem, GridListContext, GridListHeader, GridListSection} from './GridList';
+export {GridListLoadMoreItem, GridList, GridListItem, GridListContext, GridListHeader, GridListHeaderContext, GridListSection} from './GridList';
export {Group, GroupContext} from './Group';
export {Header, HeaderContext} from './Header';
export {Heading} from './Heading';
@@ -156,3 +156,4 @@ export type {CalendarState, CheckboxGroupState, Color, ColorAreaState, ColorFiel
export type {AutocompleteState} from '@react-stately/autocomplete';
export type {ListLayoutOptions, GridLayoutOptions, WaterfallLayoutOptions} from '@react-stately/layout';
export type {ValidationResult, RouterConfig} from '@react-types/shared';
+export type {SelectableCollectionContextValue} from './RSPContexts';