@react-spectrum/list 3.0.0-alpha.0 → 3.0.0-alpha.11

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/src/ListView.tsx CHANGED
@@ -9,122 +9,275 @@
9
9
  * OF ANY KIND, either express or implied. See the License for the specific language
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
- import {AriaLabelingProps, CollectionBase, DOMProps, DOMRef, StyleProps} from '@react-types/shared';
13
12
  import {classNames, useDOMRef, useStyleProps} from '@react-spectrum/utils';
14
- import {GridCollection, useGridState} from '@react-stately/grid';
15
- import {GridKeyboardDelegate, useGrid} from '@react-aria/grid';
13
+ import {DOMRef, LoadingState} from '@react-types/shared';
14
+ import type {DraggableCollectionState, DroppableCollectionState} from '@react-stately/dnd';
15
+ import {DragHooks, DropHooks} from '@react-spectrum/dnd';
16
+ import type {DroppableCollectionResult} from '@react-aria/dnd';
17
+ import {filterDOMProps, useLayoutEffect} from '@react-aria/utils';
18
+ import InsertionIndicator from './InsertionIndicator';
16
19
  // @ts-ignore
17
20
  import intlMessages from '../intl/*.json';
18
21
  import {ListLayout} from '@react-stately/layout';
19
22
  import {ListState, useListState} from '@react-stately/list';
20
- import listStyles from './listview.css';
23
+ import listStyles from './styles.css';
21
24
  import {ListViewItem} from './ListViewItem';
25
+ import {mergeProps} from '@react-aria/utils';
22
26
  import {ProgressCircle} from '@react-spectrum/progress';
23
- import React, {ReactElement, useContext, useMemo} from 'react';
24
- import {useCollator, useLocale, useMessageFormatter} from '@react-aria/i18n';
27
+ import React, {Key, ReactElement, useContext, useMemo, useRef, useState} from 'react';
28
+ import {Rect} from '@react-stately/virtualizer';
29
+ import RootDropIndicator from './RootDropIndicator';
30
+ import {DragPreview as SpectrumDragPreview} from './DragPreview';
31
+ import {SpectrumListProps} from '@react-types/list';
32
+ import {useCollator, useMessageFormatter} from '@react-aria/i18n';
33
+ import {useList} from '@react-aria/list';
25
34
  import {useProvider} from '@react-spectrum/provider';
26
35
  import {Virtualizer} from '@react-aria/virtualizer';
27
36
 
37
+ interface ListViewContextValue<T> {
38
+ state: ListState<T>,
39
+ dragState: DraggableCollectionState,
40
+ dropState: DroppableCollectionState,
41
+ dragHooks: DragHooks,
42
+ dropHooks: DropHooks,
43
+ onAction:(key: Key) => void,
44
+ isListDraggable: boolean,
45
+ isListDroppable: boolean,
46
+ layout: ListLayout<T>,
47
+ loadingState: LoadingState
48
+ }
49
+
50
+ export const ListViewContext = React.createContext<ListViewContextValue<unknown>>(null);
28
51
 
29
- export const ListViewContext = React.createContext(null);
52
+ const ROW_HEIGHTS = {
53
+ compact: {
54
+ medium: 32,
55
+ large: 40
56
+ },
57
+ regular: {
58
+ medium: 40,
59
+ large: 50
60
+ },
61
+ spacious: {
62
+ medium: 48,
63
+ large: 60
64
+ }
65
+ };
30
66
 
31
- export function useListLayout<T>(state: ListState<T>) {
67
+ function useListLayout<T>(state: ListState<T>, density: SpectrumListProps<T>['density'], allowDisabledKeyFocus: boolean) {
32
68
  let {scale} = useProvider();
33
69
  let collator = useCollator({usage: 'search', sensitivity: 'base'});
70
+ let isEmpty = state.collection.size === 0;
34
71
  let layout = useMemo(() =>
35
- new ListLayout<T>({
36
- estimatedRowHeight: scale === 'large' ? 48 : 32,
37
- padding: 0,
38
- collator
39
- })
40
- , [collator, scale]);
72
+ new ListLayout<T>({
73
+ estimatedRowHeight: ROW_HEIGHTS[density][scale],
74
+ padding: 0,
75
+ collator,
76
+ loaderHeight: isEmpty ? null : ROW_HEIGHTS[density][scale],
77
+ allowDisabledKeyFocus
78
+ })
79
+ , [collator, scale, density, isEmpty, allowDisabledKeyFocus]);
41
80
 
42
81
  layout.collection = state.collection;
43
82
  layout.disabledKeys = state.disabledKeys;
44
83
  return layout;
45
84
  }
46
85
 
47
- interface ListViewProps<T> extends CollectionBase<T>, DOMProps, AriaLabelingProps, StyleProps {
48
- isLoading?: boolean,
49
- renderEmptyState?: () => JSX.Element,
50
- transitionDuration?: number
51
- }
52
-
53
- function ListView<T extends object>(props: ListViewProps<T>, ref: DOMRef<HTMLDivElement>) {
86
+ function ListView<T extends object>(props: SpectrumListProps<T>, ref: DOMRef<HTMLDivElement>) {
54
87
  let {
55
- transitionDuration = 0
88
+ density = 'regular',
89
+ onLoadMore,
90
+ loadingState,
91
+ isQuiet,
92
+ overflowMode = 'truncate',
93
+ onAction,
94
+ dragHooks,
95
+ dropHooks,
96
+ ...otherProps
56
97
  } = props;
98
+ let isListDraggable = !!dragHooks;
99
+ let isListDroppable = !!dropHooks;
100
+ let dragHooksProvided = useRef(isListDraggable);
101
+ let dropHooksProvided = useRef(isListDroppable);
102
+ if (dragHooksProvided.current !== isListDraggable) {
103
+ console.warn('Drag hooks were provided during one render, but not another. This should be avoided as it may produce unexpected behavior.');
104
+ }
105
+ if (dropHooksProvided.current !== isListDroppable) {
106
+ console.warn('Drop hooks were provided during one render, but not another. This should be avoided as it may produce unexpected behavior.');
107
+ }
57
108
  let domRef = useDOMRef(ref);
58
- let {collection} = useListState(props);
109
+ let state = useListState({
110
+ ...props,
111
+ selectionBehavior: props.selectionStyle === 'highlight' ? 'replace' : 'toggle'
112
+ });
113
+ let {collection, selectionManager} = state;
59
114
  let formatMessage = useMessageFormatter(intlMessages);
115
+ let isLoading = loadingState === 'loading' || loadingState === 'loadingMore';
60
116
 
61
117
  let {styleProps} = useStyleProps(props);
62
- let {direction} = useLocale();
63
- let collator = useCollator({usage: 'search', sensitivity: 'base'});
64
- let gridCollection = useMemo(() => new GridCollection({
65
- columnCount: 1,
66
- items: [...collection].map(item => ({
67
- type: 'item',
68
- childNodes: [{
69
- ...item,
70
- index: 0,
71
- type: 'cell'
72
- }]
73
- }))
74
- }), [collection]);
75
- let state = useGridState({
76
- ...props,
77
- collection: gridCollection
78
- });
79
- let layout = useListLayout(state);
80
- let keyboardDelegate = useMemo(() => new GridKeyboardDelegate({
81
- collection: state.collection,
82
- disabledKeys: state.disabledKeys,
83
- ref: domRef,
84
- direction,
85
- collator,
86
- focusMode: 'cell'
87
- }), [state, domRef, direction, collator]);
88
- let {gridProps} = useGrid({
118
+ let layout = useListLayout(state, props.density || 'regular', state.selectionManager.disabledBehavior === 'selection');
119
+ let dragState: DraggableCollectionState;
120
+ let preview = useRef(null);
121
+ if (isListDraggable) {
122
+ dragState = dragHooks.useDraggableCollectionState({
123
+ collection,
124
+ selectionManager,
125
+ preview
126
+ });
127
+ }
128
+
129
+ let DragPreview = dragHooks?.DragPreview;
130
+ let dropState: DroppableCollectionState;
131
+ let droppableCollection: DroppableCollectionResult;
132
+ let isRootDropTarget: boolean;
133
+ if (isListDroppable) {
134
+ dropState = dropHooks.useDroppableCollectionState({
135
+ collection,
136
+ selectionManager
137
+ });
138
+ droppableCollection = dropHooks.useDroppableCollection({
139
+ keyboardDelegate: layout,
140
+ getDropTargetFromPoint(x, y) {
141
+ let closest = null;
142
+ let closestDistance = Infinity;
143
+ let closestDir = null;
144
+
145
+ x += domRef.current.scrollLeft;
146
+ y += domRef.current.scrollTop;
147
+
148
+ let visible = layout.getVisibleLayoutInfos(new Rect(x - 50, y - 50, x + 50, y + 50));
149
+
150
+ for (let layoutInfo of visible) {
151
+ let r = layoutInfo.rect;
152
+ let points: [number, number, string][] = [
153
+ [r.x, r.y + 4, 'before'],
154
+ [r.maxX, r.y + 4, 'before'],
155
+ [r.x, r.maxY - 8, 'after'],
156
+ [r.maxX, r.maxY - 8, 'after']
157
+ ];
158
+
159
+ for (let [px, py, dir] of points) {
160
+ let dx = px - x;
161
+ let dy = py - y;
162
+ let d = dx * dx + dy * dy;
163
+ if (d < closestDistance) {
164
+ closestDistance = d;
165
+ closest = layoutInfo;
166
+ closestDir = dir;
167
+ }
168
+ }
169
+
170
+ // TODO: Best way to implement only for when closest can be dropped on
171
+ // TODO: Figure out the typescript for this
172
+ // @ts-ignore
173
+ if (y >= r.y + 10 && y <= r.maxY - 10 && collection.getItem(closest.key).value.type === 'folder') {
174
+ closestDir = 'on';
175
+ }
176
+ }
177
+
178
+ let key = closest?.key;
179
+ if (key) {
180
+ return {
181
+ type: 'item',
182
+ key,
183
+ dropPosition: closestDir
184
+ };
185
+ }
186
+ }
187
+ }, dropState, domRef);
188
+
189
+ isRootDropTarget = dropState.isDropTarget({type: 'root'});
190
+ }
191
+
192
+ let {gridProps} = useList({
89
193
  ...props,
90
194
  isVirtualized: true,
91
- keyboardDelegate,
92
- ref: domRef
93
- }, state);
195
+ keyboardDelegate: layout,
196
+ onAction
197
+ }, state, domRef);
94
198
 
95
199
  // Sync loading state into the layout.
96
- layout.isLoading = props.isLoading;
200
+ layout.isLoading = isLoading;
201
+
202
+ let focusedKey = selectionManager.focusedKey;
203
+ if (dropState?.target?.type === 'item') {
204
+ focusedKey = dropState.target.key;
205
+ }
206
+
207
+ // wait for layout to get accurate measurements
208
+ let [isVerticalScrollbarVisible, setVerticalScollbarVisible] = useState(false);
209
+ let [isHorizontalScrollbarVisible, setHorizontalScollbarVisible] = useState(false);
210
+ useLayoutEffect(() => {
211
+ if (domRef.current) {
212
+ // 2 is the width of the border which is not part of the box size
213
+ setVerticalScollbarVisible(domRef.current.clientWidth + 2 < domRef.current.offsetWidth);
214
+ setHorizontalScollbarVisible(domRef.current.clientHeight + 2 < domRef.current.offsetHeight);
215
+ }
216
+ });
217
+
218
+ let hasAnyChildren = useMemo(() => [...collection].some(item => item.hasChildNodes), [collection]);
97
219
 
98
220
  return (
99
- <ListViewContext.Provider value={{state, keyboardDelegate}}>
221
+ <ListViewContext.Provider value={{state, dragState, dropState, dragHooks, dropHooks, onAction, isListDraggable, isListDroppable, layout, loadingState}}>
100
222
  <Virtualizer
223
+ {...mergeProps(isListDroppable && droppableCollection?.collectionProps, gridProps)}
224
+ {...filterDOMProps(otherProps)}
101
225
  {...gridProps}
102
226
  {...styleProps}
227
+ isLoading={isLoading}
228
+ onLoadMore={onLoadMore}
103
229
  ref={domRef}
104
- focusedKey={state.selectionManager.focusedKey}
105
- sizeToFit="height"
230
+ focusedKey={focusedKey}
106
231
  scrollDirection="vertical"
107
232
  className={
108
233
  classNames(
109
234
  listStyles,
110
235
  'react-spectrum-ListView',
236
+ `react-spectrum-ListView--${density}`,
237
+ 'react-spectrum-ListView--emphasized',
238
+ {
239
+ 'react-spectrum-ListView--quiet': isQuiet,
240
+ 'react-spectrum-ListView--loadingMore': loadingState === 'loadingMore',
241
+ 'react-spectrum-ListView--draggable': !!isListDraggable,
242
+ 'react-spectrum-ListView--dropTarget': !!isRootDropTarget,
243
+ 'react-spectrum-ListView--isVerticalScrollbarVisible': isVerticalScrollbarVisible,
244
+ 'react-spectrum-ListView--isHorizontalScrollbarVisible': isHorizontalScrollbarVisible,
245
+ 'react-spectrum-ListView--hasAnyChildren': hasAnyChildren,
246
+ 'react-spectrum-ListView--wrap': overflowMode === 'wrap'
247
+ },
111
248
  styleProps.className
112
249
  )
113
250
  }
114
251
  layout={layout}
115
252
  collection={collection}
116
- transitionDuration={transitionDuration}>
253
+ transitionDuration={isLoading ? 160 : 220}>
117
254
  {(type, item) => {
118
255
  if (type === 'item') {
119
256
  return (
120
- <ListViewItem item={item} />
257
+ <>
258
+ {isListDroppable && collection.getKeyBefore(item.key) == null &&
259
+ <RootDropIndicator key="root" />
260
+ }
261
+ {isListDroppable &&
262
+ <InsertionIndicator
263
+ key={`${item.key}-before`}
264
+ target={{key: item.key, type: 'item', dropPosition: 'before'}} />
265
+ }
266
+ <ListViewItem item={item} isEmphasized hasActions={!!onAction} />
267
+ {isListDroppable &&
268
+ <InsertionIndicator
269
+ key={`${item.key}-after`}
270
+ target={{key: item.key, type: 'item', dropPosition: 'after'}}
271
+ isPresentationOnly={collection.getKeyAfter(item.key) !== null} />
272
+ }
273
+ </>
121
274
  );
122
275
  } else if (type === 'loader') {
123
276
  return (
124
277
  <CenteredWrapper>
125
278
  <ProgressCircle
126
279
  isIndeterminate
127
- aria-label={state.collection.size > 0 ? formatMessage('loadingMore') : formatMessage('loading')} />
280
+ aria-label={collection.size > 0 ? formatMessage('loadingMore') : formatMessage('loading')} />
128
281
  </CenteredWrapper>
129
282
  );
130
283
  } else if (type === 'placeholder') {
@@ -142,18 +295,34 @@ function ListView<T extends object>(props: ListViewProps<T>, ref: DOMRef<HTMLDiv
142
295
 
143
296
  }}
144
297
  </Virtualizer>
298
+ {DragPreview && isListDraggable &&
299
+ <DragPreview ref={preview}>
300
+ {() => {
301
+ let item = state.collection.getItem(dragState.draggedKey);
302
+ let itemCount = dragState.draggingKeys.size;
303
+ let itemHeight = layout.getLayoutInfo(dragState.draggedKey).rect.height;
304
+ return <SpectrumDragPreview item={item} itemCount={itemCount} itemHeight={itemHeight} />;
305
+ }}
306
+ </DragPreview>
307
+ }
145
308
  </ListViewContext.Provider>
146
309
  );
147
310
  }
148
311
 
149
-
150
312
  function CenteredWrapper({children}) {
151
313
  let {state} = useContext(ListViewContext);
152
314
  return (
153
315
  <div
154
316
  role="row"
155
317
  aria-rowindex={state.collection.size + 1}
156
- className={classNames(listStyles, 'react-spectrum-ListView-centeredWrapper')}>
318
+ className={
319
+ classNames(
320
+ listStyles,
321
+ 'react-spectrum-ListView-centeredWrapper',
322
+ {
323
+ 'react-spectrum-ListView-centeredWrapper--loadingMore': state.collection.size > 0
324
+ }
325
+ )}>
157
326
  <div role="gridcell">
158
327
  {children}
159
328
  </div>
@@ -161,5 +330,8 @@ function CenteredWrapper({children}) {
161
330
  );
162
331
  }
163
332
 
164
- const _ListView = React.forwardRef(ListView) as <T>(props: ListViewProps<T> & {ref?: DOMRef<HTMLDivElement>}) => ReactElement;
333
+ /**
334
+ * Lists display a linear collection of data. They allow users to quickly scan, sort, compare, and take action on large amounts of data.
335
+ */
336
+ const _ListView = React.forwardRef(ListView) as <T>(props: SpectrumListProps<T> & {ref?: DOMRef<HTMLDivElement>}) => ReactElement;
165
337
  export {_ListView as ListView};
@@ -9,62 +9,256 @@
9
9
  * OF ANY KIND, either express or implied. See the License for the specific language
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
- import {classNames} from '@react-spectrum/utils';
13
- import listStyles from './listview.css';
12
+ import {Checkbox} from '@react-spectrum/checkbox';
13
+ import ChevronLeftMedium from '@spectrum-icons/ui/ChevronLeftMedium';
14
+ import ChevronRightMedium from '@spectrum-icons/ui/ChevronRightMedium';
15
+ import {classNames, ClearSlots, SlotProvider, useHasChild} from '@react-spectrum/utils';
16
+ import {CSSTransition} from 'react-transition-group';
17
+ import type {DraggableItemResult, DroppableItemResult} from '@react-aria/dnd';
18
+ import {DropTarget, Node} from '@react-types/shared';
19
+ import {FocusRing, useFocusRing} from '@react-aria/focus';
20
+ import {Grid} from '@react-spectrum/layout';
21
+ import {isFocusVisible as isGlobalFocusVisible, useHover} from '@react-aria/interactions';
22
+ import ListGripper from '@spectrum-icons/ui/ListGripper';
23
+ import listStyles from './styles.css';
14
24
  import {ListViewContext} from './ListView';
15
25
  import {mergeProps} from '@react-aria/utils';
26
+ import {Provider} from '@react-spectrum/provider';
16
27
  import React, {useContext, useRef} from 'react';
17
- import {useFocusRing} from '@react-aria/focus';
18
- import {useGridCell, useGridRow} from '@react-aria/grid';
19
- import {useHover} from '@react-aria/interactions';
28
+ import {Text} from '@react-spectrum/text';
29
+ import {useButton} from '@react-aria/button';
30
+ import {useListItem, useListSelectionCheckbox} from '@react-aria/list';
31
+ import {useLocale} from '@react-aria/i18n';
32
+ import {useVisuallyHidden} from '@react-aria/visually-hidden';
20
33
 
34
+ interface ListViewItemProps<T> {
35
+ item: Node<T>,
36
+ isEmphasized: boolean,
37
+ hasActions: boolean
38
+ }
21
39
 
22
- export function ListViewItem(props) {
40
+ export function ListViewItem<T>(props: ListViewItemProps<T>) {
23
41
  let {
24
- item
42
+ item,
43
+ isEmphasized
25
44
  } = props;
26
- let {state} = useContext(ListViewContext);
27
- let ref = useRef<HTMLDivElement>();
45
+ let {state, dragState, dropState, isListDraggable, isListDroppable, layout, dragHooks, dropHooks, loadingState} = useContext(ListViewContext);
46
+ let {direction} = useLocale();
47
+ let rowRef = useRef<HTMLDivElement>();
28
48
  let {
29
49
  isFocusVisible: isFocusVisibleWithin,
30
50
  focusProps: focusWithinProps
31
51
  } = useFocusRing({within: true});
32
52
  let {isFocusVisible, focusProps} = useFocusRing();
33
- let {hoverProps, isHovered} = useHover({});
34
- let {rowProps} = useGridRow({
53
+
54
+ let {
55
+ rowProps,
56
+ gridCellProps,
57
+ isPressed,
58
+ isSelected,
59
+ isDisabled,
60
+ allowsSelection,
61
+ hasAction
62
+ } = useListItem({
35
63
  node: item,
36
64
  isVirtualized: true,
37
- ref
38
- }, state);
39
- let {gridCellProps} = useGridCell({
40
- node: item,
41
- ref,
42
- focusMode: 'cell'
43
- }, state);
65
+ shouldSelectOnPressUp: isListDraggable
66
+ }, state, rowRef);
67
+ let isDroppable = isListDroppable && !isDisabled;
68
+ let {hoverProps, isHovered} = useHover({isDisabled: !allowsSelection && !hasAction});
69
+
70
+ let {checkboxProps} = useListSelectionCheckbox({key: item.key}, state);
71
+ let hasDescription = useHasChild(`.${listStyles['react-spectrum-ListViewItem-description']}`, rowRef);
72
+
73
+ let draggableItem: DraggableItemResult;
74
+ if (isListDraggable) {
75
+ // eslint-disable-next-line react-hooks/rules-of-hooks
76
+ draggableItem = dragHooks.useDraggableItem({key: item.key}, dragState);
77
+ if (isDisabled) {
78
+ draggableItem = null;
79
+ }
80
+ }
81
+ let droppableItem: DroppableItemResult;
82
+ let isDropTarget: boolean;
83
+ if (isListDroppable) {
84
+ let target = {type: 'item', key: item.key, dropPosition: 'on'} as DropTarget;
85
+ isDropTarget = dropState.isDropTarget(target);
86
+ // eslint-disable-next-line react-hooks/rules-of-hooks
87
+ droppableItem = dropHooks.useDroppableItem({target}, dropState, rowRef);
88
+ }
89
+
90
+ let dragButtonRef = React.useRef();
91
+ let {buttonProps} = useButton({
92
+ ...draggableItem?.dragButtonProps,
93
+ elementType: 'div'
94
+ }, dragButtonRef);
95
+
96
+ let chevron = direction === 'ltr'
97
+ ? (
98
+ <ChevronRightMedium
99
+ aria-hidden="true"
100
+ UNSAFE_className={
101
+ classNames(
102
+ listStyles,
103
+ 'react-spectrum-ListViewItem-parentIndicator',
104
+ {
105
+ 'react-spectrum-ListViewItem-parentIndicator--hasChildItems': item.props.hasChildItems,
106
+ 'is-disabled': !hasAction
107
+ }
108
+ )
109
+ } />
110
+ )
111
+ : (
112
+ <ChevronLeftMedium
113
+ aria-hidden="true"
114
+ UNSAFE_className={
115
+ classNames(
116
+ listStyles,
117
+ 'react-spectrum-ListViewItem-parentIndicator',
118
+ {
119
+ 'react-spectrum-ListViewItem-parentIndicator--hasChildItems': item.props.hasChildItems,
120
+ 'is-disabled': !hasAction
121
+ }
122
+ )
123
+ } />
124
+ );
125
+
126
+ let showCheckbox = state.selectionManager.selectionMode !== 'none' && state.selectionManager.selectionBehavior === 'toggle';
127
+ let {visuallyHiddenProps} = useVisuallyHidden();
128
+
44
129
  const mergedProps = mergeProps(
45
- gridCellProps,
130
+ rowProps,
131
+ draggableItem?.dragProps,
132
+ isDroppable && droppableItem?.dropProps,
46
133
  hoverProps,
47
134
  focusWithinProps,
48
135
  focusProps
49
136
  );
50
137
 
138
+ let isFirstRow = item.prevKey == null;
139
+ let isLastRow = item.nextKey == null;
140
+ // Figure out if the ListView content is equal or greater in height to the container. If so, we'll need to round the bottom
141
+ // border corners of the last row when selected and we can get rid of the bottom border if it isn't selected to avoid border overlap
142
+ // with bottom border
143
+ let isFlushWithContainerBottom = false;
144
+ if (isLastRow && loadingState !== 'loadingMore') {
145
+ if (layout.getContentSize()?.height >= layout.virtualizer?.getVisibleRect().height) {
146
+ isFlushWithContainerBottom = true;
147
+ }
148
+ }
149
+ // previous item isn't selected
150
+ // and the previous item isn't focused or, if it is focused, then if focus globally isn't visible or just focus isn't in the listview
151
+ let roundTops = (!state.selectionManager.isSelected(item.prevKey)
152
+ && (state.selectionManager.focusedKey !== item.prevKey || !(isGlobalFocusVisible() && state.selectionManager.isFocused)));
153
+ let roundBottoms = (!state.selectionManager.isSelected(item.nextKey)
154
+ && (state.selectionManager.focusedKey !== item.nextKey || !(isGlobalFocusVisible() && state.selectionManager.isFocused)));
155
+
156
+ let content = typeof item.rendered === 'string' ? <Text>{item.rendered}</Text> : item.rendered;
157
+ if (isDisabled) {
158
+ content = <Provider isDisabled>{content}</Provider>;
159
+ }
160
+
51
161
  return (
52
- <div {...rowProps}>
162
+ <div
163
+ {...mergedProps}
164
+ className={
165
+ classNames(
166
+ listStyles,
167
+ 'react-spectrum-ListView-row',
168
+ {
169
+ 'focus-ring': isFocusVisible,
170
+ 'round-tops':
171
+ roundTops || (isHovered && !isSelected && state.selectionManager.focusedKey !== item.key),
172
+ 'round-bottoms':
173
+ roundBottoms || (isHovered && !isSelected && state.selectionManager.focusedKey !== item.key)
174
+ }
175
+ )
176
+ }
177
+ ref={rowRef}>
53
178
  <div
179
+ // TODO: refactor the css here now that we are focusing the row?
54
180
  className={
55
181
  classNames(
56
182
  listStyles,
57
183
  'react-spectrum-ListViewItem',
58
184
  {
185
+ 'is-active': isPressed,
59
186
  'is-focused': isFocusVisibleWithin,
60
187
  'focus-ring': isFocusVisible,
61
- 'is-hovered': isHovered
188
+ 'is-hovered': isHovered,
189
+ 'is-selected': isSelected,
190
+ 'is-disabled': isDisabled,
191
+ 'is-prev-selected': state.selectionManager.isSelected(item.prevKey),
192
+ 'is-next-selected': state.selectionManager.isSelected(item.nextKey),
193
+ 'react-spectrum-ListViewItem--highlightSelection': state.selectionManager.selectionBehavior === 'replace' && (isSelected || state.selectionManager.isSelected(item.nextKey)),
194
+ 'react-spectrum-ListViewItem--dropTarget': !!isDropTarget,
195
+ 'react-spectrum-ListViewItem--firstRow': isFirstRow,
196
+ 'react-spectrum-ListViewItem--lastRow': isLastRow,
197
+ 'react-spectrum-ListViewItem--isFlushBottom': isFlushWithContainerBottom,
198
+ 'react-spectrum-ListViewItem--hasDescription': hasDescription
62
199
  }
63
200
  )
64
201
  }
65
- ref={ref}
66
- {...mergedProps}>
67
- {item.rendered}
202
+ {...gridCellProps}>
203
+ <Grid UNSAFE_className={listStyles['react-spectrum-ListViewItem-grid']}>
204
+ {isListDraggable &&
205
+ <div className={listStyles['react-spectrum-ListViewItem-draghandle-container']}>
206
+ {!isDisabled &&
207
+ <FocusRing focusRingClass={classNames(listStyles, 'focus-ring')}>
208
+ <div
209
+ {...buttonProps as React.HTMLAttributes<HTMLElement>}
210
+ className={
211
+ classNames(
212
+ listStyles,
213
+ 'react-spectrum-ListViewItem-draghandle-button'
214
+ )
215
+ }
216
+ style={!isFocusVisibleWithin ? {...visuallyHiddenProps.style} : {}}
217
+ ref={dragButtonRef}
218
+ draggable="true">
219
+ <ListGripper />
220
+ </div>
221
+ </FocusRing>
222
+ }
223
+ </div>
224
+ }
225
+ <CSSTransition
226
+ in={showCheckbox}
227
+ unmountOnExit
228
+ classNames={{
229
+ enter: listStyles['react-spectrum-ListViewItem-checkbox--enter'],
230
+ enterActive: listStyles['react-spectrum-ListViewItem-checkbox--enterActive'],
231
+ exit: listStyles['react-spectrum-ListViewItem-checkbox--exit'],
232
+ exitActive: listStyles['react-spectrum-ListViewItem-checkbox--exitActive']
233
+ }}
234
+ timeout={160} >
235
+ <div className={listStyles['react-spectrum-ListViewItem-checkboxWrapper']}>
236
+ <Checkbox
237
+ {...checkboxProps}
238
+ UNSAFE_className={listStyles['react-spectrum-ListViewItem-checkbox']}
239
+ isEmphasized={isEmphasized} />
240
+ </div>
241
+ </CSSTransition>
242
+ <SlotProvider
243
+ slots={{
244
+ text: {UNSAFE_className: listStyles['react-spectrum-ListViewItem-content']},
245
+ description: {UNSAFE_className: listStyles['react-spectrum-ListViewItem-description']},
246
+ icon: {UNSAFE_className: listStyles['react-spectrum-ListViewItem-icon'], size: 'M'},
247
+ image: {UNSAFE_className: listStyles['react-spectrum-ListViewItem-image']},
248
+ actionButton: {UNSAFE_className: listStyles['react-spectrum-ListViewItem-actions'], isQuiet: true},
249
+ actionGroup: {
250
+ UNSAFE_className: listStyles['react-spectrum-ListViewItem-actions'],
251
+ isQuiet: true,
252
+ density: 'compact'
253
+ },
254
+ actionMenu: {UNSAFE_className: listStyles['react-spectrum-ListViewItem-actionmenu'], isQuiet: true}
255
+ }}>
256
+ {content}
257
+ <ClearSlots>
258
+ {chevron}
259
+ </ClearSlots>
260
+ </SlotProvider>
261
+ </Grid>
68
262
  </div>
69
263
  </div>
70
264
  );