Skip to content

Commit 7991977

Browse files
committed
add autocomplete gridlist filtering
1 parent 77936ca commit 7991977

File tree

2 files changed

+37
-25
lines changed

2 files changed

+37
-25
lines changed

packages/react-aria-components/src/GridList.tsx

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ import {CollectionProps, CollectionRendererContext, DefaultCollectionRenderer, I
1717
import {ContextValue, DEFAULT_SLOT, Provider, RenderProps, SlotProps, StyleProps, StyleRenderProps, useContextProps, useRenderProps} from './utils';
1818
import {DragAndDropContext, DropIndicatorContext, DropIndicatorProps, useDndPersistedKeys, useRenderDropIndicator} from './DragAndDrop';
1919
import {DragAndDropHooks} from './useDragAndDrop';
20-
import {DraggableCollectionState, DroppableCollectionState, Collection as ICollection, ListState, Node, SelectionBehavior, useListState} from 'react-stately';
20+
import {DraggableCollectionState, DroppableCollectionState, Collection as ICollection, ListState, Node, SelectionBehavior, UNSTABLE_useFilteredListState, useListState} from 'react-stately';
2121
import {filterDOMProps, inertValue, LoadMoreSentinelProps, useLoadMoreSentinel, useObjectRef} from '@react-aria/utils';
2222
import {forwardRefType, GlobalDOMAttributes, HoverEvents, Key, LinkDOMProps, PressEvents, RefObject} from '@react-types/shared';
2323
import {ListStateContext} from './ListBox';
2424
import React, {createContext, ForwardedRef, forwardRef, HTMLAttributes, JSX, ReactNode, useContext, useEffect, useMemo, useRef} from 'react';
2525
import {TextContext} from './Text';
26+
import {UNSTABLE_InternalAutocompleteContext} from './Autocomplete';
2627

2728
export interface GridListRenderProps {
2829
/**
@@ -103,21 +104,25 @@ interface GridListInnerProps<T extends object> {
103104
}
104105

105106
function GridListInner<T extends object>({props, collection, gridListRef: ref}: GridListInnerProps<T>) {
107+
// TODO: for now, don't grab collection ref and collectionProps from the autocomplete, rely on the user tabbing to the gridlist
108+
// figure out if we want to support virtual focus for grids when wrapped in an autocomplete
109+
let {filter} = useContext(UNSTABLE_InternalAutocompleteContext) || {};
106110
let {dragAndDropHooks, keyboardNavigationBehavior = 'arrow', layout = 'stack'} = props;
107111
let {CollectionRoot, isVirtualized, layoutDelegate, dropTargetDelegate: ctxDropTargetDelegate} = useContext(CollectionRendererContext);
108-
let state = useListState({
112+
let gridlistState = useListState({
109113
...props,
110114
collection,
111115
children: undefined,
112116
layoutDelegate
113117
});
114118

119+
let filteredState = UNSTABLE_useFilteredListState(gridlistState, filter);
115120
let collator = useCollator({usage: 'search', sensitivity: 'base'});
116-
let {disabledBehavior, disabledKeys} = state.selectionManager;
121+
let {disabledBehavior, disabledKeys} = filteredState.selectionManager;
117122
let {direction} = useLocale();
118123
let keyboardDelegate = useMemo(() => (
119124
new ListKeyboardDelegate({
120-
collection,
125+
collection: filteredState.collection,
121126
collator,
122127
ref,
123128
disabledKeys,
@@ -126,7 +131,7 @@ function GridListInner<T extends object>({props, collection, gridListRef: ref}:
126131
layout,
127132
direction
128133
})
129-
), [collection, ref, layout, disabledKeys, disabledBehavior, layoutDelegate, collator, direction]);
134+
), [filteredState.collection, ref, layout, disabledKeys, disabledBehavior, layoutDelegate, collator, direction]);
130135

131136
let {gridProps} = useGridList({
132137
...props,
@@ -135,9 +140,9 @@ function GridListInner<T extends object>({props, collection, gridListRef: ref}:
135140
keyboardNavigationBehavior: layout === 'grid' ? 'tab' : keyboardNavigationBehavior,
136141
isVirtualized,
137142
shouldSelectOnPressUp: props.shouldSelectOnPressUp
138-
}, state, ref);
143+
}, filteredState, ref);
139144

140-
let selectionManager = state.selectionManager;
145+
let selectionManager = filteredState.selectionManager;
141146
let isListDraggable = !!dragAndDropHooks?.useDraggableCollectionState;
142147
let isListDroppable = !!dragAndDropHooks?.useDroppableCollectionState;
143148
let dragHooksProvided = useRef(isListDraggable);
@@ -163,7 +168,7 @@ function GridListInner<T extends object>({props, collection, gridListRef: ref}:
163168

164169
if (isListDraggable && dragAndDropHooks) {
165170
dragState = dragAndDropHooks.useDraggableCollectionState!({
166-
collection,
171+
collection: filteredState.collection,
167172
selectionManager,
168173
preview: dragAndDropHooks.renderDragPreview ? preview : undefined
169174
});
@@ -177,12 +182,12 @@ function GridListInner<T extends object>({props, collection, gridListRef: ref}:
177182

178183
if (isListDroppable && dragAndDropHooks) {
179184
dropState = dragAndDropHooks.useDroppableCollectionState!({
180-
collection,
185+
collection: filteredState.collection,
181186
selectionManager
182187
});
183188

184189
let keyboardDelegate = new ListKeyboardDelegate({
185-
collection,
190+
collection: filteredState.collection,
186191
disabledKeys: selectionManager.disabledKeys,
187192
disabledBehavior: selectionManager.disabledBehavior,
188193
ref
@@ -197,14 +202,14 @@ function GridListInner<T extends object>({props, collection, gridListRef: ref}:
197202
}
198203

199204
let {focusProps, isFocused, isFocusVisible} = useFocusRing();
200-
let isEmpty = state.collection.size === 0;
205+
let isEmpty = filteredState.collection.size === 0;
201206
let renderValues = {
202207
isDropTarget: isRootDropTarget,
203208
isEmpty,
204209
isFocused,
205210
isFocusVisible,
206211
layout,
207-
state
212+
state: filteredState
208213
};
209214
let renderProps = useRenderProps({
210215
className: props.className,
@@ -243,13 +248,13 @@ function GridListInner<T extends object>({props, collection, gridListRef: ref}:
243248
data-layout={layout}>
244249
<Provider
245250
values={[
246-
[ListStateContext, state],
251+
[ListStateContext, filteredState],
247252
[DragAndDropContext, {dragAndDropHooks, dragState, dropState}],
248253
[DropIndicatorContext, {render: GridListDropIndicatorWrapper}]
249254
]}>
250255
{isListDroppable && <RootDropIndicator />}
251256
<CollectionRoot
252-
collection={collection}
257+
collection={filteredState.collection}
253258
scrollRef={ref}
254259
persistedKeys={useDndPersistedKeys(selectionManager, dragAndDropHooks, dropState)}
255260
renderDropIndicator={useRenderDropIndicator(dragAndDropHooks, dropState)} />
@@ -279,13 +284,20 @@ export interface GridListItemProps<T = object> extends RenderProps<GridListItemR
279284
onAction?: () => void
280285
}
281286

282-
// TODO: add filter funct to this
287+
// TODO: reuse?
283288
class GridListNode extends CollectionNode<any> {
284289
static readonly type = 'item';
285290
constructor(key: Key) {
286291
super(GridListNode.type, key);
287292
}
288293

294+
filter(_, __, filterFn: (textValue: string) => boolean): CollectionNode<any> | null {
295+
if (filterFn(this.textValue)) {
296+
return this.clone();
297+
}
298+
299+
return null;
300+
}
289301
}
290302

291303
/**

packages/react-aria-components/stories/Autocomplete.stories.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -957,17 +957,17 @@ export const AutocompleteWithGridList = () => {
957957
</TextField>
958958
<GridList
959959
className={styles.menu}
960-
style={{height: 200}}
960+
style={{height: 200, width: 200}}
961961
aria-label="test gridlist">
962-
<MyGridListItem>1,1 <Button>Actions</Button></MyGridListItem>
963-
<MyGridListItem>1,2 <Button>Actions</Button></MyGridListItem>
964-
<MyGridListItem>1,3 <Button>Actions</Button></MyGridListItem>
965-
<MyGridListItem>2,1 <Button>Actions</Button></MyGridListItem>
966-
<MyGridListItem>2,2 <Button>Actions</Button></MyGridListItem>
967-
<MyGridListItem>2,3 <Button>Actions</Button></MyGridListItem>
968-
<MyGridListItem>3,1 <Button>Actions</Button></MyGridListItem>
969-
<MyGridListItem>3,2 <Button>Actions</Button></MyGridListItem>
970-
<MyGridListItem>3,3 <Button>Actions</Button></MyGridListItem>
962+
<MyGridListItem textValue="Foo">Foo <Button>Actions</Button></MyGridListItem>
963+
<MyGridListItem textValue="Bar">Bar <Button>Actions</Button></MyGridListItem>
964+
<MyGridListItem textValue="Baz">Baz <Button>Actions</Button></MyGridListItem>
965+
<MyGridListItem textValue="Charizard">Charizard<Button>Actions</Button></MyGridListItem>
966+
<MyGridListItem textValue="Blastoise">Blastoise <Button>Actions</Button></MyGridListItem>
967+
<MyGridListItem textValue="Pikachu">Pikachu <Button>Actions</Button></MyGridListItem>
968+
<MyGridListItem textValue="Venusaur">Venusaur<Button>Actions</Button></MyGridListItem>
969+
<MyGridListItem textValue="text value check">textValue is "text value check" <Button>Actions</Button></MyGridListItem>
970+
<MyGridListItem textValue="Blah">Blah <Button>Actions</Button></MyGridListItem>
971971
</GridList>
972972
</div>
973973
</AutocompleteWrapper>

0 commit comments

Comments
 (0)