Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions packages/@react-aria/grid/src/GridKeyboardDelegate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,11 +268,11 @@ export class GridKeyboardDelegate<T, C extends GridCollection<T>> implements Key
return key;
}

private getItem(key: Key): HTMLElement {
protected getItem(key: Key): HTMLElement {
return this.ref.current.querySelector(`[data-key="${key}"]`);
}

private getItemRect(key: Key): Rect {
protected getItemRect(key: Key): Rect {
if (this.layout) {
return this.layout.getLayoutInfo(key)?.rect;
}
Expand Down Expand Up @@ -381,4 +381,3 @@ export class GridKeyboardDelegate<T, C extends GridCollection<T>> implements Key
return null;
}
}

25 changes: 20 additions & 5 deletions packages/@react-aria/grid/src/useGridCell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ interface GridCellProps {
* Please use onCellAction at the collection level instead.
* @deprecated
**/
onAction?: () => void
onAction?: () => void,
// TODO: check naming convention
skipCell?: boolean
}

interface GridCellAria {
Expand All @@ -56,17 +58,25 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
isVirtualized,
focusMode = 'child',
shouldSelectOnPressUp,
onAction
onAction,
skipCell
} = props;

let {direction} = useLocale();
let {keyboardDelegate, actions: {onCellAction}} = gridMap.get(state);

// Handles focusing the cell. If there is a focusable child,
// it is focused, otherwise the cell itself is focused.
// it is focused, otherwise the cell itself is focused. If skipCell is
// true, always attempt to put focus on a focusable child if any or back on the parent
// row.
let focus = () => {
let treeWalker = getFocusableTreeWalker(ref.current);
if (focusMode === 'child') {
if (focusMode === 'child' || skipCell) {
// If focus is already on a focusable child within the cell, early return so we don't shift focus
if (ref.current.contains(document.activeElement) && ref.current !== document.activeElement) {
return;
}

let focusable = state.selectionManager.childFocusStrategy === 'last'
? last(treeWalker)
: treeWalker.firstChild() as HTMLElement;
Expand All @@ -76,8 +86,13 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
}
}

if (!ref.current.contains(document.activeElement)) {
if (!ref.current.contains(document.activeElement) && !skipCell) {
focusSafely(ref.current);
return;
}

if (node.parentKey != null && skipCell) {
state.selectionManager.setFocusedKey(node.parentKey);
}
};

Expand Down
3 changes: 3 additions & 0 deletions packages/@react-aria/list/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @react-aria/list

This package is part of [react-spectrum](https://github.com/adobe/react-spectrum). See the repo for more details.
13 changes: 13 additions & 0 deletions packages/@react-aria/list/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright 2022 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.
*/

export * from './src';
32 changes: 32 additions & 0 deletions packages/@react-aria/list/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "@react-aria/list",
"version": "3.0.0-alpha.1",
"private": true,
"description": "Spectrum UI components in React",
"license": "Apache-2.0",
"main": "dist/main.js",
"module": "dist/module.js",
"types": "dist/types.d.ts",
"source": "src/index.ts",
"files": [
"dist",
"src"
],
"sideEffects": false,
"repository": {
"type": "git",
"url": "https://github.com/adobe/react-spectrum"
},
"dependencies": {
"@babel/runtime": "^7.6.2",
"@react-aria/grid": "^3.2.3",
"@react-stately/virtualizer": "^3.1.7",
"@react-types/grid": "^3.0.2"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1"
},
"publishConfig": {
"access": "public"
}
}
74 changes: 74 additions & 0 deletions packages/@react-aria/list/src/ListGridKeyboardDelegate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2022 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 {GridCollection} from '@react-types/grid';
import {GridKeyboardDelegate, GridKeyboardDelegateOptions} from '@react-aria/grid';
import {Key} from 'react';
import {Rect} from '@react-stately/virtualizer';

// TODO: Open to feedback about name, ListKeyboardDelegate already exists
export class ListGridKeyboardDelegate<T> extends GridKeyboardDelegate<T, GridCollection<T>> {
constructor(options: Omit<GridKeyboardDelegateOptions<T, GridCollection<T>>, 'focusMode'>) {
super({...options, focusMode: 'row'});
}

private getRowKey(key: Key) {
let startItem = key != null ? this.collection.getItem(key) : null;
if (!startItem) {
return;
}

// If focus was on a cell, start searching from the parent row
if (this.isCell(startItem)) {
key = startItem.parentKey;
}

return key;
}

// TODO: think about whether or not focus should be moved to the row or to the children in the cell
// Feels kinda weird to move it to the first child in the cell when focus used to be on the last child in the
// cell above...
getKeyBelow(key: Key) {
key = this.getRowKey(key);
return key != null ? this.findNextKey(key) : null;
}

getKeyAbove(key: Key) {
key = this.getRowKey(key);
return this.findPreviousKey(key);
}

getFirstKey() {
return this.collection.getFirstKey();
}

getLastKey() {
return this.collection.getLastKey();
}

protected getItemRect(key: Key): Rect {
key = this.getRowKey(key);
if (key == null) {
return;
}

if (this.layout) {
return this.layout.getLayoutInfo(key)?.rect;
}

let item = this.getItem(key);
if (item) {
return new Rect(item.offsetLeft, item.offsetTop, item.offsetWidth, item.offsetHeight);
}
}
}
13 changes: 13 additions & 0 deletions packages/@react-aria/list/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright 2022 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.
*/

export * from './ListGridKeyboardDelegate';
1 change: 1 addition & 0 deletions packages/@react-spectrum/list/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"@react-aria/grid": "^3.2.4",
"@react-aria/i18n": "^3.3.7",
"@react-aria/interactions": "^3.8.2",
"@react-aria/list": "3.0.0-alpha.1",
"@react-aria/listbox": "^3.4.3",
"@react-aria/separator": "^3.1.6",
"@react-aria/utils": "^3.11.3",
Expand Down
13 changes: 6 additions & 7 deletions packages/@react-spectrum/list/src/ListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ import {Content} from '@react-spectrum/view';
import type {DraggableCollectionState} from '@react-stately/dnd';
import {DragHooks} from '@react-spectrum/dnd';
import {GridCollection, GridState, useGridState} from '@react-stately/grid';
import {GridKeyboardDelegate, useGrid, useGridSelectionCheckbox} from '@react-aria/grid';
// @ts-ignore
import intlMessages from '../intl/*.json';
import {ListGridKeyboardDelegate} from '@react-aria/list';
import ListGripper from '@spectrum-icons/ui/ListGripper';
import {ListLayout} from '@react-stately/layout';
import {ListState, useListState} from '@react-stately/list';
Expand All @@ -38,11 +38,12 @@ import {ProgressCircle} from '@react-spectrum/progress';
import {Provider, useProvider} from '@react-spectrum/provider';
import React, {ReactElement, useContext, useMemo, useRef} from 'react';
import {useCollator, useLocale, useMessageFormatter} from '@react-aria/i18n';
import {useGrid, useGridSelectionCheckbox} from '@react-aria/grid';
import {Virtualizer} from '@react-aria/virtualizer';

interface ListViewContextValue {
state: GridState<object, GridCollection<any>>,
keyboardDelegate: GridKeyboardDelegate<unknown, GridCollection<any>>,
keyboardDelegate: ListGridKeyboardDelegate<unknown>,
dragState: DraggableCollectionState,
onAction:(key: string) => void,
isListDraggable: boolean
Expand Down Expand Up @@ -141,19 +142,17 @@ function ListView<T extends object>(props: ListViewProps<T>, ref: DOMRef<HTMLDiv
let state = useGridState({
...props,
collection: gridCollection,
focusMode: 'cell',
focusMode: 'row',
selectionBehavior: props.selectionStyle === 'highlight' ? 'replace' : 'toggle'
});
let layout = useListLayout(state, props.density || 'regular');
let keyboardDelegate = useMemo(() => new GridKeyboardDelegate({
let keyboardDelegate = useMemo(() => new ListGridKeyboardDelegate({
collection: state.collection,
disabledKeys: state.disabledKeys,
ref: domRef,
direction,
collator,
// Focus the ListView cell instead of the row so that focus doesn't change with left/right arrow keys when there aren't any
// focusable children in the cell.
focusMode: 'cell'
layout
}), [state, domRef, direction, collator]);

let provider = useProvider();
Expand Down
15 changes: 10 additions & 5 deletions packages/@react-spectrum/list/src/ListViewItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,18 @@ export function ListViewItem(props) {
}, state, rowRef);
let {gridCellProps} = useGridCell({
node: cellNode,
focusMode: 'cell'
skipCell: true,
isVirtualized: true
}, state, cellRef);
let draggableItem: DraggableItemResult;
if (isListDraggable) {
// eslint-disable-next-line react-hooks/rules-of-hooks
draggableItem = dragHooks.useDraggableItem({key: item.key}, dragState);
}
const mergedProps = mergeProps(
gridCellProps,
rowProps,
pressProps,
isDraggable && draggableItem?.dragProps,
hoverProps,
focusWithinProps,
focusProps
Expand Down Expand Up @@ -99,9 +102,11 @@ export function ListViewItem(props) {
let {visuallyHiddenProps} = useVisuallyHidden();
return (
<div
{...mergeProps(rowProps, pressProps, isDraggable && draggableItem?.dragProps)}
ref={rowRef}>
{...mergedProps}
ref={rowRef}
aria-label={item.textValue}>
<div
// TODO: refactor the css here now that we are focusing the row?
className={
classNames(
listStyles,
Expand All @@ -119,7 +124,7 @@ export function ListViewItem(props) {
)
}
ref={cellRef}
{...mergedProps}>
{...gridCellProps}>
<Grid UNSAFE_className={listStyles['react-spectrum-ListViewItem-grid']}>
{isListDraggable &&
<div className={listStyles['react-spectrum-ListViewItem-draghandle-container']}>
Expand Down
Loading