@@ -54,7 +54,7 @@ import {IconContext} from './Icon';
54
54
// @ts -ignore
55
55
import intlMessages from '../intl/*.json' ;
56
56
import { LayoutNode } from '@react-stately/layout' ;
57
- import { Menu , MenuItem , MenuTrigger } from './Menu' ;
57
+ import { Menu , MenuItem , MenuSection , MenuTrigger } from './Menu' ;
58
58
import { mergeStyles } from '../style/runtime' ;
59
59
import Nubbin from '../ui-icons/S2_MoveHorizontalTableWidget.svg' ;
60
60
import { ProgressCircle } from './ProgressCircle' ;
@@ -466,7 +466,7 @@ const columnStyles = style({
466
466
} ,
467
467
paddingX : {
468
468
default : 16 ,
469
- isColumnResizable : 0
469
+ isMenu : 0
470
470
} ,
471
471
textAlign : {
472
472
align : {
@@ -493,7 +493,7 @@ const columnStyles = style({
493
493
borderStartWidth : 0 ,
494
494
borderEndWidth : {
495
495
default : 0 ,
496
- isColumnResizable : 1
496
+ isMenu : 1
497
497
} ,
498
498
borderStyle : 'solid' ,
499
499
forcedColorAdjust : 'none'
@@ -510,7 +510,9 @@ export interface ColumnProps extends RACColumnProps {
510
510
*/
511
511
align ?: 'start' | 'center' | 'end' ,
512
512
/** 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
514
516
}
515
517
516
518
/**
@@ -520,21 +522,22 @@ export const Column = forwardRef(function Column(props: ColumnProps, ref: DOMRef
520
522
let { isQuiet} = useContext ( InternalTableContext ) ;
521
523
let { allowsResizing, children, align = 'start' } = props ;
522
524
let domRef = useDOMRef ( ref ) ;
523
- let isColumnResizable = allowsResizing ;
525
+ let isMenu = allowsResizing || ! ! props . UNSTABLE_menuItems ;
526
+
524
527
525
528
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} ) } >
527
530
{ ( { allowsSorting, sortDirection, isFocusVisible, sort, startResize} ) => (
528
531
< >
529
532
{ /* 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
530
533
(no need to juggle showing this focus ring if focus is on the menu button and not if it is on the resizer) */ }
531
534
{ /* 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 */ }
532
535
{ isFocusVisible && < CellFocusRing /> }
533
- { isColumnResizable ?
536
+ { isMenu ?
534
537
(
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 } >
536
539
{ children }
537
- </ ResizableColumnContents >
540
+ </ ColumnWithMenu >
538
541
) : (
539
542
< ColumnContents allowsSorting = { allowsSorting } sortDirection = { sortDirection } >
540
543
{ children }
@@ -704,10 +707,13 @@ const nubbin = style({
704
707
}
705
708
} ) ;
706
709
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
+ }
708
714
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 ;
711
717
let { setIsInResizeMode, isInResizeMode} = useContext ( InternalTableContext ) ;
712
718
let stringFormatter = useLocalizedStringFormatter ( intlMessages , '@react-spectrum/s2' ) ;
713
719
const onMenuSelect = ( key ) => {
@@ -726,12 +732,13 @@ function ResizableColumnContents(props: ResizableColumnContentProps) {
726
732
} ;
727
733
728
734
let items = useMemo ( ( ) => {
729
- let options = [
730
- {
735
+ let options : Array < { label : string , id : string } > = [ ] ;
736
+ if ( isColumnResizable ) {
737
+ options = [ {
731
738
label : stringFormatter . format ( 'table.resizeColumn' ) ,
732
739
id : 'resize'
733
- }
734
- ] ;
740
+ } ] ;
741
+ }
735
742
if ( allowsSorting ) {
736
743
options = [
737
744
{
@@ -747,7 +754,7 @@ function ResizableColumnContents(props: ResizableColumnContentProps) {
747
754
}
748
755
return options ;
749
756
// eslint-disable-next-line react-hooks/exhaustive-deps
750
- } , [ allowsSorting ] ) ;
757
+ } , [ allowsSorting , isColumnResizable ] ) ;
751
758
752
759
let buttonAlignment = 'start' ;
753
760
let menuAlign = 'start' as 'start' | 'end' ;
@@ -779,20 +786,29 @@ function ResizableColumnContents(props: ResizableColumnContentProps) {
779
786
</ div >
780
787
< Chevron size = "M" className = { chevronIcon } />
781
788
</ 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 }
784
798
</ Menu >
785
799
</ 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
+ ) }
796
812
</ >
797
813
) ;
798
814
}
0 commit comments