@@ -54,7 +54,7 @@ import {IconContext} from './Icon';
5454// @ts -ignore
5555import intlMessages from '../intl/*.json' ;
5656import { LayoutNode } from '@react-stately/layout' ;
57- import { Menu , MenuItem , MenuTrigger } from './Menu' ;
57+ import { Menu , MenuItem , MenuSection , MenuTrigger } from './Menu' ;
5858import { mergeStyles } from '../style/runtime' ;
5959import Nubbin from '../ui-icons/S2_MoveHorizontalTableWidget.svg' ;
6060import { ProgressCircle } from './ProgressCircle' ;
@@ -466,7 +466,7 @@ const columnStyles = style({
466466 } ,
467467 paddingX : {
468468 default : 16 ,
469- isColumnResizable : 0
469+ isMenu : 0
470470 } ,
471471 textAlign : {
472472 align : {
@@ -493,7 +493,7 @@ const columnStyles = style({
493493 borderStartWidth : 0 ,
494494 borderEndWidth : {
495495 default : 0 ,
496- isColumnResizable : 1
496+ isMenu : 1
497497 } ,
498498 borderStyle : 'solid' ,
499499 forcedColorAdjust : 'none'
@@ -510,7 +510,9 @@ export interface ColumnProps extends RACColumnProps {
510510 */
511511 align ?: 'start' | 'center' | 'end' ,
512512 /** The content to render as the column header. */
513- children : ReactNode
513+ children : ReactNode ,
514+ /** Menu fragment to be rendered inside the column header's menu. */
515+ UNSTABLE_menuItems ?: ReactNode
514516}
515517
516518/**
@@ -520,21 +522,22 @@ export const Column = forwardRef(function Column(props: ColumnProps, ref: DOMRef
520522 let { isQuiet} = useContext ( InternalTableContext ) ;
521523 let { allowsResizing, children, align = 'start' } = props ;
522524 let domRef = useDOMRef ( ref ) ;
523- let isColumnResizable = allowsResizing ;
525+ let isMenu = allowsResizing || ! ! props . UNSTABLE_menuItems ;
526+
524527
525528 return (
526- < RACColumn { ...props } ref = { domRef } style = { { borderInlineEndColor : 'transparent' } } className = { renderProps => columnStyles ( { ...renderProps , isColumnResizable , align, isQuiet} ) } >
529+ < RACColumn { ...props } ref = { domRef } style = { { borderInlineEndColor : 'transparent' } } className = { renderProps => columnStyles ( { ...renderProps , isMenu , align, isQuiet} ) } >
527530 { ( { allowsSorting, sortDirection, isFocusVisible, sort, startResize} ) => (
528531 < >
529532 { /* Note this is mainly for column's without a dropdown menu. If there is a dropdown menu, the button is styled to have a focus ring for simplicity
530533 (no need to juggle showing this focus ring if focus is on the menu button and not if it is on the resizer) */ }
531534 { /* Separate absolutely positioned element because appyling the ring on the column directly via outline means the ring's required borderRadius will cause the bottom gray border to curve as well */ }
532535 { isFocusVisible && < CellFocusRing /> }
533- { isColumnResizable ?
536+ { isMenu ?
534537 (
535- < ResizableColumnContents allowsSorting = { allowsSorting } sortDirection = { sortDirection } sort = { sort } startResize = { startResize } align = { align } >
538+ < ColumnWithMenu isColumnResizable = { allowsResizing } menuItems = { props . UNSTABLE_menuItems } allowsSorting = { allowsSorting } sortDirection = { sortDirection } sort = { sort } startResize = { startResize } align = { align } >
536539 { children }
537- </ ResizableColumnContents >
540+ </ ColumnWithMenu >
538541 ) : (
539542 < ColumnContents allowsSorting = { allowsSorting } sortDirection = { sortDirection } >
540543 { children }
@@ -704,10 +707,13 @@ const nubbin = style({
704707 }
705708} ) ;
706709
707- interface ResizableColumnContentProps extends Pick < ColumnRenderProps , 'allowsSorting' | 'sort' | 'sortDirection' | 'startResize' > , Pick < ColumnProps , 'align' | 'children' > { }
710+ interface ColumnWithMenuProps extends Pick < ColumnRenderProps , 'allowsSorting' | 'sort' | 'sortDirection' | 'startResize' > , Pick < ColumnProps , 'align' | 'children' > {
711+ isColumnResizable ?: boolean ,
712+ menuItems ?: ReactNode
713+ }
708714
709- function ResizableColumnContents ( props : ResizableColumnContentProps ) {
710- let { allowsSorting, sortDirection, sort, startResize, children, align} = props ;
715+ function ColumnWithMenu ( props : ColumnWithMenuProps ) {
716+ let { allowsSorting, sortDirection, sort, startResize, children, align, isColumnResizable , menuItems } = props ;
711717 let { setIsInResizeMode, isInResizeMode} = useContext ( InternalTableContext ) ;
712718 let stringFormatter = useLocalizedStringFormatter ( intlMessages , '@react-spectrum/s2' ) ;
713719 const onMenuSelect = ( key ) => {
@@ -726,12 +732,13 @@ function ResizableColumnContents(props: ResizableColumnContentProps) {
726732 } ;
727733
728734 let items = useMemo ( ( ) => {
729- let options = [
730- {
735+ let options : Array < { label : string , id : string } > = [ ] ;
736+ if ( isColumnResizable ) {
737+ options = [ {
731738 label : stringFormatter . format ( 'table.resizeColumn' ) ,
732739 id : 'resize'
733- }
734- ] ;
740+ } ] ;
741+ }
735742 if ( allowsSorting ) {
736743 options = [
737744 {
@@ -747,7 +754,7 @@ function ResizableColumnContents(props: ResizableColumnContentProps) {
747754 }
748755 return options ;
749756 // eslint-disable-next-line react-hooks/exhaustive-deps
750- } , [ allowsSorting ] ) ;
757+ } , [ allowsSorting , isColumnResizable ] ) ;
751758
752759 let buttonAlignment = 'start' ;
753760 let menuAlign = 'start' as 'start' | 'end' ;
@@ -779,20 +786,29 @@ function ResizableColumnContents(props: ResizableColumnContentProps) {
779786 </ div >
780787 < Chevron size = "M" className = { chevronIcon } />
781788 </ Button >
782- < Menu onAction = { onMenuSelect } items = { items } styles = { style ( { minWidth : 128 } ) } >
783- { ( item ) => < MenuItem > { item ?. label } </ MenuItem > }
789+ < Menu onAction = { onMenuSelect } styles = { style ( { minWidth : 128 } ) } >
790+ { items . length > 0 && (
791+ < MenuSection aria-label = { stringFormatter . format ( 'table.standardColumnMenu' ) } >
792+ < Collection items = { items } >
793+ { ( item ) => < MenuItem > { item ?. label } </ MenuItem > }
794+ </ Collection >
795+ </ MenuSection >
796+ ) }
797+ { menuItems }
784798 </ Menu >
785799 </ MenuTrigger >
786- < div data-react-aria-prevent-focus = "true" >
787- < ColumnResizer data-react-aria-prevent-focus = "true" className = { ( { resizableDirection, isResizing} ) => resizerHandleContainer ( { resizableDirection, isResizing, isInResizeMode} ) } >
788- { ( { isFocusVisible, isResizing} ) => (
789- < >
790- < ResizerIndicator isInResizeMode = { isInResizeMode } isFocusVisible = { isFocusVisible } isResizing = { isResizing } />
791- { ( isFocusVisible || isInResizeMode ) && isResizing && < div className = { nubbin } > < Nubbin /> </ div > }
792- </ >
793- ) }
794- </ ColumnResizer >
795- </ div >
800+ { isColumnResizable && (
801+ < div data-react-aria-prevent-focus = "true" >
802+ < ColumnResizer data-react-aria-prevent-focus = "true" className = { ( { resizableDirection, isResizing} ) => resizerHandleContainer ( { resizableDirection, isResizing, isInResizeMode} ) } >
803+ { ( { isFocusVisible, isResizing} ) => (
804+ < >
805+ < ResizerIndicator isInResizeMode = { isInResizeMode } isFocusVisible = { isFocusVisible } isResizing = { isResizing } />
806+ { ( isFocusVisible || isInResizeMode ) && isResizing && < div className = { nubbin } > < Nubbin /> </ div > }
807+ </ >
808+ ) }
809+ </ ColumnResizer >
810+ </ div >
811+ ) }
796812 </ >
797813 ) ;
798814}
0 commit comments