Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for grid edit mode w/ nested interactive widgets #7277

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
8 changes: 7 additions & 1 deletion packages/@react-aria/collections/src/CollectionBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {BaseCollection} from './BaseCollection';
import {BaseNode, Document, ElementNode} from './Document';
import {CachedChildrenOptions, useCachedChildren} from './useCachedChildren';
import {createPortal} from 'react-dom';
import {forwardRefType, Node} from '@react-types/shared';
import {forwardRefType, Key, Node} from '@react-types/shared';
import {Hidden} from './Hidden';
import React, {createContext, ForwardedRef, forwardRef, JSX, ReactElement, ReactNode, useCallback, useContext, useMemo, useRef, useState} from 'react';
import {useIsSSR} from '@react-aria/ssr';
Expand All @@ -25,6 +25,7 @@ const ShallowRenderContext = createContext(false);
const CollectionDocumentContext = createContext<Document<any, BaseCollection<any>> | null>(null);

export interface CollectionBuilderProps<C extends BaseCollection<object>> {
id?: Key,
content: ReactNode,
children: (collection: C) => ReactNode,
createCollection?: () => C
Expand All @@ -51,6 +52,11 @@ export function CollectionBuilder<C extends BaseCollection<object>>(props: Colle
// This is fine. CollectionDocumentContext never changes after mounting.
// eslint-disable-next-line react-hooks/rules-of-hooks
let {collection, document} = useCollectionDocument(props.createCollection);

if (props.id) {
document.key = props.id;
}
nwidynski marked this conversation as resolved.
Show resolved Hide resolved

return (
<>
<Hidden>
Expand Down
5 changes: 3 additions & 2 deletions packages/@react-aria/collections/src/Document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import {BaseCollection, CollectionNode, Mutable} from './BaseCollection';
import {ForwardedRef, ReactElement} from 'react';
import {Node} from '@react-types/shared';
import {Key, Node} from '@react-types/shared';

// This Collection implementation is perhaps a little unusual. It works by rendering the React tree into a
// Portal to a fake DOM implementation. This gives us efficient access to the tree of rendered objects, and
Expand Down Expand Up @@ -226,7 +226,7 @@ export class ElementNode<T> extends BaseNode<T> {

constructor(type: string, ownerDocument: Document<T, any>) {
super(ownerDocument);
this.node = new CollectionNode(type, `react-aria-${++ownerDocument.nodeId}`);
this.node = new CollectionNode(type, `${ownerDocument.key}-${++ownerDocument.nodeId}`);
// Start a transaction so that no updates are emitted from the collection
// until the props for this node are set. We don't know the real id for the
// node until then, so we need to avoid emitting collections in an inconsistent state.
Expand Down Expand Up @@ -304,6 +304,7 @@ export class ElementNode<T> extends BaseNode<T> {
* which is lazily copied on write during updates.
*/
export class Document<T, C extends BaseCollection<T> = BaseCollection<T>> extends BaseNode<T> {
key: Key = 'react-aria';
nodeType = 11; // DOCUMENT_FRAGMENT_NODE
ownerDocument = this;
dirtyNodes: Set<BaseNode<T>> = new Set();
Expand Down
1 change: 1 addition & 0 deletions packages/@react-aria/grid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@react-aria/utils": "^3.25.3",
"@react-stately/collections": "^3.11.0",
"@react-stately/grid": "^3.9.3",
"@react-stately/group": "3.0.0-alpha.1",
"@react-stately/selection": "^3.17.0",
"@react-types/checkbox": "^3.8.4",
"@react-types/grid": "^3.2.9",
Expand Down
11 changes: 9 additions & 2 deletions packages/@react-aria/grid/src/useGrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ export interface GridProps extends DOMProps, AriaLabelingProps {
* @default (key) => state.collection.getItem(key)?.textValue
*/
getRowText?: (key: Key) => string,
/**
* Whether keyboard navigation to focusable elements within the grid is
* via the left/right arrow keys or the tab key.
* @default 'arrow'
*/
keyboardNavigationBehavior?: 'arrow' | 'tab',
/**
* The ref attached to the scrollable body. Used to provided automatic scrolling on item focus for non-virtualized grids.
*/
Expand Down Expand Up @@ -71,7 +77,8 @@ export function useGrid<T>(props: GridProps, state: GridState<T, GridCollection<
scrollRef,
getRowText,
onRowAction,
onCellAction
onCellAction,
keyboardNavigationBehavior = 'arrow'
} = props;
let {selectionManager: manager} = state;

Expand Down Expand Up @@ -103,7 +110,7 @@ export function useGrid<T>(props: GridProps, state: GridState<T, GridCollection<
});

let id = useId(props.id);
gridMap.set(state, {keyboardDelegate: delegate, actions: {onRowAction, onCellAction}});
gridMap.set(state, {id, keyboardDelegate: delegate, keyboardNavigationBehavior, actions: {onRowAction, onCellAction}});

let descriptionProps = useHighlightSelectionDescription({
selectionManager: manager,
Expand Down
Loading