@@ -29,7 +29,7 @@ import {FormValidationState, useFormValidationState} from '../form/useFormValida
2929import { ListState , useListState } from '../list/useListState' ;
3030import { OverlayTriggerState , useOverlayTriggerState } from '../overlays/useOverlayTriggerState' ;
3131import { useControlledState } from '../utils/useControlledState' ;
32- import { useMemo , useState } from 'react' ;
32+ import { useMemo , useRef , useState } from 'react' ;
3333
3434export type SelectionMode = 'single' | 'multiple' ;
3535export type ValueType < M extends SelectionMode > = M extends 'single' ? Key | null : readonly Key [ ] ;
@@ -196,12 +196,27 @@ export function useSelectState<T, M extends SelectionMode = 'single'>(
196196 }
197197 } ;
198198
199+ // Preserve the selection's anchor (anchorKey/currentKey) across renders. The
200+ // multiple-selection `value` is a plain Key[], so without this the listbox
201+ // would rebuild an anchorless Selection on every render and range selection
202+ // (shift+click / shift+arrow) would collapse to just the clicked item. We keep
203+ // the last Selection produced internally and feed it back while its membership
204+ // still matches `value`.
205+ let lastSelection = useRef < Set < Key > | null > ( null ) ;
206+
199207 let listState = useListState ( {
200208 ...props ,
201209 selectionMode,
202210 disallowEmptySelection : selectionMode === 'single' ,
203211 allowDuplicateSelectionEvents : true ,
204- selectedKeys : useMemo ( ( ) => convertValue ( displayValue ) , [ displayValue ] ) ,
212+ selectedKeys : useMemo ( ( ) => {
213+ let selectedKeys = convertValue ( displayValue ) ;
214+ let last = lastSelection . current ;
215+ if ( last != null && Array . isArray ( selectedKeys ) && isSameSelection ( last , selectedKeys ) ) {
216+ return last ;
217+ }
218+ return selectedKeys ;
219+ } , [ displayValue ] ) ,
205220 onSelectionChange : ( keys : Selection ) => {
206221 // impossible, but TS doesn't know that
207222 if ( keys === 'all' ) {
@@ -212,6 +227,9 @@ export function useSelectState<T, M extends SelectionMode = 'single'>(
212227 let key = keys . values ( ) . next ( ) . value ?? null ;
213228 setValue ( key ) ;
214229 } else {
230+ // Remember the Selection (with its anchor) so it survives the round-trip
231+ // through the plain `value` array on the next render.
232+ lastSelection . current = keys ;
215233 setValue ( [ ...keys ] ) ;
216234 }
217235 if ( shouldCloseOnSelect ) {
@@ -278,3 +296,15 @@ function convertValue(value: Key | Key[] | null | undefined) {
278296 }
279297 return Array . isArray ( value ) ? value : [ value ] ;
280298}
299+
300+ function isSameSelection ( selection : Set < Key > , keys : Key [ ] ) : boolean {
301+ if ( selection . size !== keys . length ) {
302+ return false ;
303+ }
304+ for ( let key of keys ) {
305+ if ( ! selection . has ( key ) ) {
306+ return false ;
307+ }
308+ }
309+ return true ;
310+ }
0 commit comments