Skip to content
Merged
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
17 changes: 14 additions & 3 deletions packages/@react-aria/table/src/useTableColumnResize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,24 @@
* governing permissions and limitations under the License.
*/

import {ColumnResizeState} from '@react-stately/table';
import {focusSafely, useFocusable} from '@react-aria/focus';
import {GridNode} from '@react-types/grid';
import {HTMLAttributes, RefObject, useRef} from 'react';
import {mergeProps} from '@react-aria/utils';
import {useKeyboard, useMove} from '@react-aria/interactions';
import {useLocale} from '@react-aria/i18n';
import {useRef} from 'react';

export function useTableColumnResize(state, item, ref): any {
interface ResizerAria {
resizerProps: HTMLAttributes<HTMLElement>
}

interface ResizerProps<T> {
column: GridNode<T>
}

export function useTableColumnResize<T>(props: ResizerProps<T>, state: ColumnResizeState<T>, ref: RefObject<HTMLDivElement>): ResizerAria {
let {column: item} = props;
const stateRef = useRef(null);
// keep track of what the cursor on the body is so it can be restored back to that when done resizing
const cursor = useRef(null);
Expand All @@ -32,7 +43,7 @@ export function useTableColumnResize(state, item, ref): any {
}
if (e.key === 'Escape' || e.key === 'Enter' || e.key === ' ') {
// switch focus back to the column header on escape
const columnHeader = ref.current.previousSibling;
const columnHeader = ref.current.previousSibling as HTMLElement;
if (columnHeader) {
focusSafely(columnHeader);
}
Expand Down
14 changes: 7 additions & 7 deletions packages/@react-spectrum/table/src/Resizer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import {useTableContext} from './TableView';


function Resizer(props, ref) {
const {item} = props;
let {state} = useTableContext();
let {resizerProps} = useTableColumnResize(state, item, ref);
const {column} = props;
let {columnState} = useTableContext();
let {resizerProps} = useTableColumnResize({column}, columnState, ref);
return (
<FocusRing focusRingClass={classNames(styles, 'focus-ring')}>
<div
Expand All @@ -20,10 +20,10 @@ function Resizer(props, ref) {
role="separator"
aria-orientation="vertical"
aria-label="Resize column"
aria-labelledby={item.key}
aria-valuenow={state.getColumnWidth(item.key)}
aria-valuemin={state.getColumnMinWidth(item.key)}
aria-valuemax={state.getColumnMaxWidth(item.key)} />
aria-labelledby={column.key}
aria-valuenow={columnState.getColumnWidth(column.key)}
aria-valuemin={columnState.getColumnMinWidth(column.key)}
aria-valuemax={columnState.getColumnMaxWidth(column.key)} />
</FocusRing>
);
}
Expand Down
115 changes: 54 additions & 61 deletions packages/@react-spectrum/table/src/TableView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import ArrowDownSmall from '@spectrum-icons/ui/ArrowDownSmall';
import {chain, mergeProps, useLayoutEffect} from '@react-aria/utils';
import {Checkbox} from '@react-spectrum/checkbox';
import {classNames, useDOMRef, useStyleProps} from '@react-spectrum/utils';
import {ColumnResizeState, TableState, useTableColumnResizeState, useTableState} from '@react-stately/table';
import {DOMRef} from '@react-types/shared';
import {FocusRing, focusSafely, useFocusRing} from '@react-aria/focus';
import {GridNode} from '@react-types/grid';
Expand All @@ -29,8 +30,6 @@ import {SpectrumColumnProps, SpectrumTableProps} from '@react-types/table';
import styles from '@adobe/spectrum-css-temp/components/table/vars.css';
import stylesOverrides from './table.css';
import {TableLayout} from '@react-stately/layout';
import {TableState, useTableState} from '@react-stately/table';
import {TableView_DEPRECATED} from './TableView_DEPRECATED';
import {Tooltip, TooltipTrigger} from '@react-spectrum/tooltip';
import {useButton} from '@react-aria/button';
import {useHover} from '@react-aria/interactions';
Expand Down Expand Up @@ -81,7 +80,8 @@ const SELECTION_CELL_DEFAULT_WIDTH = {

interface TableContextValue<T> {
state: TableState<T>,
layout: TableLayout<T>
layout: TableLayout<T>,
columnState: ColumnResizeState<T>
}

const TableContext = React.createContext<TableContextValue<unknown>>(null);
Expand All @@ -95,6 +95,7 @@ function TableView<T extends object>(props: SpectrumTableProps<T>, ref: DOMRef<H
let {styleProps} = useStyleProps(props);

let [showSelectionCheckboxes, setShowSelectionCheckboxes] = useState(props.selectionStyle !== 'highlight');
let {direction} = useLocale();
let {scale} = useProvider();

const getDefaultWidth = useCallback(({hideHeader, isSelectionCell, showDivider}) => {
Expand All @@ -109,10 +110,11 @@ function TableView<T extends object>(props: SpectrumTableProps<T>, ref: DOMRef<H
let state = useTableState({
...props,
showSelectionCheckboxes,
selectionBehavior: props.selectionStyle === 'highlight' ? 'replace' : 'toggle',
getDefaultWidth
selectionBehavior: props.selectionStyle === 'highlight' ? 'replace' : 'toggle'
});

const columnState = useTableColumnResizeState({...props, columns: state.collection.columns, getDefaultWidth, onColumnResize: props.onColumnResize, onColumnResizeEnd: props.onColumnResizeEnd});

// If the selection behavior changes in state, we need to update showSelectionCheckboxes here due to the circular dependency...
let shouldShowCheckboxes = state.selectionManager.selectionBehavior !== 'replace';
if (shouldShowCheckboxes !== showSelectionCheckboxes) {
Expand Down Expand Up @@ -141,9 +143,8 @@ function TableView<T extends object>(props: SpectrumTableProps<T>, ref: DOMRef<H
}),
[props.overflowMode, scale, density]
);
let {direction} = useLocale();
layout.collection = state.collection;
layout.getColumnWidth = state.getColumnWidth;
layout.getColumnWidth = columnState.getColumnWidth;

let {gridProps} = useTable({
...props,
Expand Down Expand Up @@ -259,7 +260,7 @@ function TableView<T extends object>(props: SpectrumTableProps<T>, ref: DOMRef<H
}

if (item.props.allowsResizing) {
return <ResizableTableColumnHeader item={item} state={state} />;
return <ResizableTableColumnHeader column={item} />;
}

return (
Expand Down Expand Up @@ -303,7 +304,7 @@ function TableView<T extends object>(props: SpectrumTableProps<T>, ref: DOMRef<H
}, []);

return (
<TableContext.Provider value={{state, layout}}>
<TableContext.Provider value={{state, layout, columnState}}>
<TableVirtualizer
{...gridProps}
{...styleProps}
Expand All @@ -316,7 +317,7 @@ function TableView<T extends object>(props: SpectrumTableProps<T>, ref: DOMRef<H
'spectrum-Table--quiet': isQuiet,
'spectrum-Table--wrap': props.overflowMode === 'wrap',
'spectrum-Table--loadingMore': state.collection.body.props.loadingState === 'loadingMore',
'spectrum-Table--resizingColumn': state.isResizingColumn,
'spectrum-Table--resizingColumn': columnState.isResizingColumn,
'spectrum-Table--isVerticalScrollbarVisible': isVerticalScrollbarVisible,
'spectrum-Table--isHorizontalScrollbarVisible': isHorizontalScrollbarVisible
},
Expand All @@ -331,11 +332,11 @@ function TableView<T extends object>(props: SpectrumTableProps<T>, ref: DOMRef<H
focusedKey={state.selectionManager.focusedKey}
renderView={renderView}
renderWrapper={renderWrapper}
setTableWidth={state.setTableWidth}
setTableWidth={columnState.setTableWidth}
onVisibleRectChange={onVisibleRectChange}
domRef={domRef}
bodyRef={bodyRef}
getColumnWidth={state.getColumnWidth} />
getColumnWidth={columnState.getColumnWidth} />
</TableContext.Provider>
);
}
Expand Down Expand Up @@ -391,10 +392,7 @@ function TableVirtualizer({layout, collection, focusedKey, renderView, renderWra
}, [bodyRef]);

let onVisibleRectChange = useCallback((rect: Rect) => {
// setting the table width will recalculate column widths which we only want to do once the virtualizer is done initializing
if (state.virtualizer.contentSize.height > 0) {
setTableWidth(rect.width);
}
setTableWidth(rect.width);
Comment on lines -394 to +395
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this part of the spooky column code? Or an optimization?

Copy link
Copy Markdown
Member Author

@snowystinger snowystinger Jun 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was that bit of code that you and I looked at together when we were fixing the tests. I don't recall the exact reason, but we didn't need to wait for initialization. It's not related to the spooky column though. I'll try to find more of the reason for the change.


state.setVisibleRect(rect);

Expand Down Expand Up @@ -477,12 +475,14 @@ function TableColumnHeader(props) {
}

let {hoverProps, isHovered} = useHover({});

const allProps = [columnHeaderProps, hoverProps];
if (columnProps.allowsResizing) {
allProps.push(buttonProps);
// if we allow resizing, override the usePress that useTableColumnHeader generates so that clicking brings up the menu
// instead of sorting the column immediately
columnHeaderProps = {...columnHeaderProps, ...buttonProps};
}

const allProps = [columnHeaderProps, hoverProps];

return (
<FocusRing focusRingClass={classNames(styles, 'focus-ring')}>
<div
Expand Down Expand Up @@ -522,17 +522,18 @@ function TableColumnHeader(props) {
);
}

function ResizableTableColumnHeader({item, state}) {
function ResizableTableColumnHeader({column}) {
let ref = useRef();
let {state} = useTableContext();
let formatMessage = useMessageFormatter(intlMessages);

const onMenuSelect = (key) => {
switch (key) {
case 'sort-asc':
state.sort(item.key, 'ascending');
state.sort(column.key, 'ascending');
break;
case 'sort-desc':
state.sort(item.key, 'descending');
state.sort(column.key, 'descending');
break;
case 'resize':
// focusResizer, needs timeout so that it happens after the animation timeout for menu close
Expand All @@ -542,28 +543,43 @@ function ResizableTableColumnHeader({item, state}) {
break;
}
};
let allowsSorting = column.props?.allowsSorting;
let items = useMemo(() => {
let options = {
sortAscending: allowsSorting && {
label: formatMessage('sortAscending'),
id: 'sort-asc'
},
sortDescending: allowsSorting && {
label: formatMessage('sortDescending'),
id: 'sort-desc'
},
resize: {
label: formatMessage('resizeColumn'),
id: 'resize'
}
};
return Object.keys(options).reduce((acc, key) => {
if (options[key]) {
acc.push(options[key]);
}
return acc;
}, []);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this written like this rather than just coding it as an array in the first place?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we had some issues with null children in Menu's at one point, I might've been defensively coding against that memory
I'll update in my next PR

}, [allowsSorting]);

return (
<>
<MenuTrigger>
<TableColumnHeader column={item} />
<Menu onAction={onMenuSelect} minWidth="size-2000">
{item.props?.allowsSorting &&
<Item key="sort-asc">
{formatMessage('sortAscending')}
<TableColumnHeader column={column} />
<Menu onAction={onMenuSelect} minWidth="size-2000" items={items}>
{(item) => (
<Item>
{item.label}
</Item>
}
{item.props?.allowsSorting &&
<Item key="sort-desc">
{formatMessage('sortDescending')}
</Item>
}
<Item key="resize">
{formatMessage('resizeColumn')}
</Item>
)}
</Menu>
</MenuTrigger>
<Resizer ref={ref} item={item} />
<Resizer ref={ref} column={column} />
</>
);
}
Expand Down Expand Up @@ -815,27 +831,4 @@ function CenteredWrapper({children}) {
*/
const _TableView = React.forwardRef(TableView) as <T>(props: SpectrumTableProps<T> & {ref?: DOMRef<HTMLDivElement>}) => ReactElement;

/*
When ready to remove this feature flag, you can remove this whole section of code, delete the _DEPRECATED files, and just replace the export with the _TableView above.
*/
function FeatureFlaggedTableView<T extends object>(props: SpectrumTableProps<T>, ref: DOMRef<HTMLDivElement>) {
let state = useTableState({
...props
});

const someColumnsAllowResizing = state.collection.columns.some(c => c.props?.allowsResizing);

if (someColumnsAllowResizing) {
return <_TableView {...props} ref={ref} />;
} else {
return <TableView_DEPRECATED {...props} ref={ref} />;
}
}

/**
* Tables are containers for displaying information. They allow users to quickly scan, sort, compare, and take action on large amounts of data.
*/
const _FeatureFlaggedTableView = React.forwardRef(FeatureFlaggedTableView) as <T>(props: SpectrumTableProps<T> & {ref?: DOMRef<HTMLDivElement>}) => ReactElement;


export {_FeatureFlaggedTableView as TableView};
export {_TableView as TableView};
Loading