-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Tableview col resizing #3210
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
Tableview col resizing #3210
Changes from all commits
b84cb25
0aab040
b17d290
33fad86
1b458fd
3ed6a54
2d782d1
16cb689
d736e9b
870e897
ac56840
390e3cf
e9e1e6c
8dcc7e7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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'; | ||
|
|
@@ -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'; | ||
|
|
@@ -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); | ||
|
|
@@ -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}) => { | ||
|
|
@@ -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) { | ||
|
|
@@ -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, | ||
|
|
@@ -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 ( | ||
|
|
@@ -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} | ||
|
|
@@ -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 | ||
| }, | ||
|
|
@@ -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> | ||
| ); | ||
| } | ||
|
|
@@ -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); | ||
|
|
||
| state.setVisibleRect(rect); | ||
|
|
||
|
|
@@ -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 | ||
|
|
@@ -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 | ||
|
|
@@ -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; | ||
| }, []); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| }, [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} /> | ||
| </> | ||
| ); | ||
| } | ||
|
|
@@ -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}; | ||
There was a problem hiding this comment.
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?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
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.