-
Notifications
You must be signed in to change notification settings - Fork 1.4k
feat: add highlight selection to S2 TableView #9811
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
base: main
Are you sure you want to change the base?
Changes from 7 commits
3b4b145
9adcfb2
6a79fa6
e38fa30
7ae2ed2
e59fe39
0cb910e
33cbc66
8646952
042ce49
a31ba79
54cf8ad
d7be568
8a2ea10
8f98eef
76da66f
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 | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -33,7 +33,6 @@ import { | |||||||||||||||||||||||||||||||||||||||||||||||
| TableHeaderProps as RACTableHeaderProps, | ||||||||||||||||||||||||||||||||||||||||||||||||
| TableProps as RACTableProps, | ||||||||||||||||||||||||||||||||||||||||||||||||
| ResizableTableContainer, | ||||||||||||||||||||||||||||||||||||||||||||||||
| RowRenderProps, | ||||||||||||||||||||||||||||||||||||||||||||||||
| TableBodyRenderProps, | ||||||||||||||||||||||||||||||||||||||||||||||||
| TableLayout, | ||||||||||||||||||||||||||||||||||||||||||||||||
| TableLoadMoreItem, | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -59,6 +58,7 @@ import {getOwnerDocument} from 'react-aria/private/utils/domHelpers'; | |||||||||||||||||||||||||||||||||||||||||||||||
| import {GridNode} from 'react-stately/private/grid/GridCollection'; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import {IconContext} from './Icon'; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import intlMessages from '../intl/*.json'; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import {isFirstItem, isNextSelected, useScale} from './utils'; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import {Key} from '@react-types/shared'; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import {LayoutNode} from 'react-stately/private/layout/ListLayout'; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import {Menu, MenuItem, MenuSection, MenuTrigger} from './Menu'; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -80,7 +80,6 @@ import {useLocale} from 'react-aria/I18nProvider'; | |||||||||||||||||||||||||||||||||||||||||||||||
| import {useLocalizedStringFormatter} from 'react-aria/useLocalizedStringFormatter'; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import {useMediaQuery} from './useMediaQuery'; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import {useObjectRef} from 'react-aria/useObjectRef'; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import {useScale} from './utils'; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import {useSpectrumContextProps} from './useSpectrumContextProps'; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import {Virtualizer} from 'react-aria-components/Virtualizer'; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import {VisuallyHidden} from 'react-aria/VisuallyHidden'; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -121,7 +120,8 @@ interface S2TableProps { | |||||||||||||||||||||||||||||||||||||||||||||||
| /** Handler that is called when more items should be loaded, e.g. while scrolling near the bottom. */ | ||||||||||||||||||||||||||||||||||||||||||||||||
| onLoadMore?: () => any, | ||||||||||||||||||||||||||||||||||||||||||||||||
| /** Provides the ActionBar to display when rows are selected in the TableView. */ | ||||||||||||||||||||||||||||||||||||||||||||||||
| renderActionBar?: (selectedKeys: 'all' | Set<Key>) => ReactElement | ||||||||||||||||||||||||||||||||||||||||||||||||
| renderActionBar?: (selectedKeys: 'all' | Set<Key>) => ReactElement, | ||||||||||||||||||||||||||||||||||||||||||||||||
| selectionStyle?: 'checkbox' | 'highlight' | ||||||||||||||||||||||||||||||||||||||||||||||||
yihuiliao marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Note that loadMore and loadingState are now on the Table instead of on the TableBody | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -130,7 +130,7 @@ export interface TableViewProps extends Omit<RACTableProps, 'style' | 'className | |||||||||||||||||||||||||||||||||||||||||||||||
| styles?: StylesPropWithHeight | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| let InternalTableContext = createContext<TableViewProps & {layout?: S2TableLayout<unknown>, setIsInResizeMode?:(val: boolean) => void, isInResizeMode?: boolean, selectionMode?: 'none' | 'single' | 'multiple'}>({}); | ||||||||||||||||||||||||||||||||||||||||||||||||
| let InternalTableContext = createContext<TableViewProps & {layout?: S2TableLayout<unknown>, setIsInResizeMode?:(val: boolean) => void, isInResizeMode?: boolean, selectionMode?: 'none' | 'single' | 'multiple', selectionStyle?: 'checkbox' | 'highlight'}>({}); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| const tableWrapper = style({ | ||||||||||||||||||||||||||||||||||||||||||||||||
| minHeight: 0, | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -300,6 +300,7 @@ export const TableView = forwardRef(function TableView(props: TableViewProps, re | |||||||||||||||||||||||||||||||||||||||||||||||
| onAction, | ||||||||||||||||||||||||||||||||||||||||||||||||
| onLoadMore, | ||||||||||||||||||||||||||||||||||||||||||||||||
| selectionMode = 'none', | ||||||||||||||||||||||||||||||||||||||||||||||||
| selectionStyle = 'checkbox', | ||||||||||||||||||||||||||||||||||||||||||||||||
| ...otherProps | ||||||||||||||||||||||||||||||||||||||||||||||||
| } = props; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -325,8 +326,9 @@ export const TableView = forwardRef(function TableView(props: TableViewProps, re | |||||||||||||||||||||||||||||||||||||||||||||||
| onLoadMore, | ||||||||||||||||||||||||||||||||||||||||||||||||
| isInResizeMode, | ||||||||||||||||||||||||||||||||||||||||||||||||
| setIsInResizeMode, | ||||||||||||||||||||||||||||||||||||||||||||||||
| selectionMode | ||||||||||||||||||||||||||||||||||||||||||||||||
| }), [isQuiet, density, overflowMode, loadingState, onLoadMore, isInResizeMode, setIsInResizeMode, selectionMode]); | ||||||||||||||||||||||||||||||||||||||||||||||||
| selectionMode, | ||||||||||||||||||||||||||||||||||||||||||||||||
| selectionStyle | ||||||||||||||||||||||||||||||||||||||||||||||||
| }), [isQuiet, density, overflowMode, loadingState, onLoadMore, isInResizeMode, setIsInResizeMode, selectionMode, selectionStyle]); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| let scrollRef = useRef<HTMLElement | null>(null); | ||||||||||||||||||||||||||||||||||||||||||||||||
| let isCheckboxSelection = selectionMode === 'multiple' || selectionMode === 'single'; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -371,7 +373,7 @@ export const TableView = forwardRef(function TableView(props: TableViewProps, re | |||||||||||||||||||||||||||||||||||||||||||||||
| isCheckboxSelection, | ||||||||||||||||||||||||||||||||||||||||||||||||
| isQuiet | ||||||||||||||||||||||||||||||||||||||||||||||||
| })} | ||||||||||||||||||||||||||||||||||||||||||||||||
| selectionBehavior="toggle" | ||||||||||||||||||||||||||||||||||||||||||||||||
| selectionBehavior={selectionStyle === 'highlight' ? 'replace' : 'toggle'} | ||||||||||||||||||||||||||||||||||||||||||||||||
| selectionMode={selectionMode} | ||||||||||||||||||||||||||||||||||||||||||||||||
| onRowAction={onAction} | ||||||||||||||||||||||||||||||||||||||||||||||||
| {...otherProps} | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -487,7 +489,7 @@ const cellFocus = { | |||||||||||||||||||||||||||||||||||||||||||||||
| } as const; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| function CellFocusRing() { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return <div role="presentation" className={style({...cellFocus, position: 'absolute', inset: 0, pointerEvents: 'none'})({isFocusVisible: true})} />; | ||||||||||||||||||||||||||||||||||||||||||||||||
| return <div role="presentation" className={style({...cellFocus, position: 'absolute', inset: 0, pointerEvents: 'none', top: 0, bottom: '[-1px]'})({isFocusVisible: true})} />; | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| const columnStyles = style({ | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -552,14 +554,14 @@ export interface ColumnProps extends Omit<RACColumnProps, 'style' | 'className' | |||||||||||||||||||||||||||||||||||||||||||||||
| * A column within a `<Table>`. | ||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||
| export const Column = forwardRef(function Column(props: ColumnProps, ref: DOMRef<HTMLDivElement>) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| let {isQuiet} = useContext(InternalTableContext); | ||||||||||||||||||||||||||||||||||||||||||||||||
| let {isQuiet, selectionStyle} = useContext(InternalTableContext); | ||||||||||||||||||||||||||||||||||||||||||||||||
| let {allowsResizing, children, align = 'start'} = props; | ||||||||||||||||||||||||||||||||||||||||||||||||
| let domRef = useDOMRef(ref); | ||||||||||||||||||||||||||||||||||||||||||||||||
| let isMenu = allowsResizing || !!props.menuItems; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||
| <RACColumn {...props} ref={domRef} style={{borderInlineEndColor: 'transparent'}} className={renderProps => columnStyles({...renderProps, isMenu, align, isQuiet})}> | ||||||||||||||||||||||||||||||||||||||||||||||||
| <RACColumn {...props} ref={domRef} style={{borderInlineEndColor: 'transparent'}} className={renderProps => columnStyles({...renderProps, isMenu, align, isQuiet, selectionStyle})}> | ||||||||||||||||||||||||||||||||||||||||||||||||
| {({allowsSorting, sortDirection, isFocusVisible, sort, startResize}) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||
| <> | ||||||||||||||||||||||||||||||||||||||||||||||||
| {/* 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 | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -903,7 +905,7 @@ export interface TableHeaderProps<T> extends Omit<RACTableHeaderProps<T>, 'style | |||||||||||||||||||||||||||||||||||||||||||||||
| export const TableHeader = /*#__PURE__*/ (forwardRef as forwardRefType)(function TableHeader<T extends object>({columns, dependencies, children}: TableHeaderProps<T>, ref: DOMRef<HTMLDivElement>) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| let scale = useScale(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| let {selectionBehavior, selectionMode} = useTableOptions(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| let {isQuiet} = useContext(InternalTableContext); | ||||||||||||||||||||||||||||||||||||||||||||||||
| let {isQuiet, selectionStyle} = useContext(InternalTableContext); | ||||||||||||||||||||||||||||||||||||||||||||||||
| let domRef = useDOMRef(ref); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -912,7 +914,7 @@ export const TableHeader = /*#__PURE__*/ (forwardRef as forwardRefType)(function | |||||||||||||||||||||||||||||||||||||||||||||||
| ref={domRef} | ||||||||||||||||||||||||||||||||||||||||||||||||
| className={tableHeader}> | ||||||||||||||||||||||||||||||||||||||||||||||||
| {/* Add extra columns for selection. */} | ||||||||||||||||||||||||||||||||||||||||||||||||
| {selectionBehavior === 'toggle' && ( | ||||||||||||||||||||||||||||||||||||||||||||||||
| {selectionBehavior === 'toggle' && selectionStyle === 'checkbox' && ( | ||||||||||||||||||||||||||||||||||||||||||||||||
| // Also isSticky prop is applied just for the layout, will decide what the RAC api should be later | ||||||||||||||||||||||||||||||||||||||||||||||||
| // @ts-ignore | ||||||||||||||||||||||||||||||||||||||||||||||||
| (<RACColumn isSticky width={scale === 'medium' ? 40 : 52} minWidth={scale === 'medium' ? 40 : 52} className={selectAllCheckboxColumn({isQuiet})}> | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -992,17 +994,10 @@ const cell = style<CellRenderProps & S2TableProps & {isDivider: boolean, isTreeC | |||||||||||||||||||||||||||||||||||||||||||||||
| fontSize: controlFont(), | ||||||||||||||||||||||||||||||||||||||||||||||||
| alignItems: 'center', | ||||||||||||||||||||||||||||||||||||||||||||||||
| display: 'flex', | ||||||||||||||||||||||||||||||||||||||||||||||||
| borderStyle: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| default: 'none', | ||||||||||||||||||||||||||||||||||||||||||||||||
| isDivider: 'solid' | ||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||
| borderEndWidth: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| default: 0, | ||||||||||||||||||||||||||||||||||||||||||||||||
| isDivider: 1 | ||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||
| borderColor: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| default: 'gray-300', | ||||||||||||||||||||||||||||||||||||||||||||||||
| forcedColors: 'ButtonBorder' | ||||||||||||||||||||||||||||||||||||||||||||||||
| boxShadow: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| isDivider: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| default: '[inset -1px 0 0 var(--borderColorGray)]' | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1065,6 +1060,7 @@ export const Cell = forwardRef(function Cell(props: CellProps, ref: DOMRef<HTMLD | |||||||||||||||||||||||||||||||||||||||||||||||
| let {children, isSticky, showDivider = false, align, textValue, ...otherProps} = props; | ||||||||||||||||||||||||||||||||||||||||||||||||
| let domRef = useDOMRef(ref); | ||||||||||||||||||||||||||||||||||||||||||||||||
| let tableVisualOptions = useContext(InternalTableContext); | ||||||||||||||||||||||||||||||||||||||||||||||||
| // let {isFirstItem} = useContext(InternalRowContext); | ||||||||||||||||||||||||||||||||||||||||||||||||
yihuiliao marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||
| textValue ||= typeof children === 'string' ? children : undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1503,11 +1499,24 @@ const rowTextColor = { | |||||||||||||||||||||||||||||||||||||||||||||||
| forcedColors: 'ButtonText' | ||||||||||||||||||||||||||||||||||||||||||||||||
| } as const; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| const row = style<RowRenderProps & S2TableProps>({ | ||||||||||||||||||||||||||||||||||||||||||||||||
| const row = style({ | ||||||||||||||||||||||||||||||||||||||||||||||||
| height: 'full', | ||||||||||||||||||||||||||||||||||||||||||||||||
| position: 'relative', | ||||||||||||||||||||||||||||||||||||||||||||||||
| boxSizing: 'border-box', | ||||||||||||||||||||||||||||||||||||||||||||||||
| backgroundColor: '--rowBackgroundColor', | ||||||||||||||||||||||||||||||||||||||||||||||||
| backgroundColor: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| default: '--rowBackgroundColor', | ||||||||||||||||||||||||||||||||||||||||||||||||
| selectionStyle: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| highlight: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| default: '--rowBackgroundColor', | ||||||||||||||||||||||||||||||||||||||||||||||||
| isSelected: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| default: colorMix('gray-25', 'blue-900', 10), | ||||||||||||||||||||||||||||||||||||||||||||||||
| isHovered: colorMix('gray-25', 'blue-900', 15), | ||||||||||||||||||||||||||||||||||||||||||||||||
| isPressed: colorMix('gray-25', 'blue-900', 15), | ||||||||||||||||||||||||||||||||||||||||||||||||
| forcedColors: 'Highlight' | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
yihuiliao marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||
| '--rowBackgroundColor': { | ||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'backgroundColor', | ||||||||||||||||||||||||||||||||||||||||||||||||
| value: rowBackgroundColor | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1549,17 +1558,83 @@ const row = style<RowRenderProps & S2TableProps>({ | |||||||||||||||||||||||||||||||||||||||||||||||
| // }, | ||||||||||||||||||||||||||||||||||||||||||||||||
| outlineStyle: 'none', | ||||||||||||||||||||||||||||||||||||||||||||||||
| borderTopWidth: 0, | ||||||||||||||||||||||||||||||||||||||||||||||||
| borderBottomWidth: 1, | ||||||||||||||||||||||||||||||||||||||||||||||||
| borderBottomWidth: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| selectionStyle: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| highlight: 0, | ||||||||||||||||||||||||||||||||||||||||||||||||
| checkbox: 1 | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||
| borderStartWidth: 0, | ||||||||||||||||||||||||||||||||||||||||||||||||
| borderEndWidth: 0, | ||||||||||||||||||||||||||||||||||||||||||||||||
| borderStyle: 'solid', | ||||||||||||||||||||||||||||||||||||||||||||||||
| borderColor: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| default: 'gray-300', | ||||||||||||||||||||||||||||||||||||||||||||||||
| forcedColors: 'ButtonBorder' | ||||||||||||||||||||||||||||||||||||||||||||||||
| selectionStyle: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| highlight: 'transparent', | ||||||||||||||||||||||||||||||||||||||||||||||||
| checkbox: 'gray-300' | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||
| '--borderColorGray': { | ||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'borderColor', | ||||||||||||||||||||||||||||||||||||||||||||||||
| value: 'gray-300' | ||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||
| '--borderColorBlue': { | ||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'borderColor', | ||||||||||||||||||||||||||||||||||||||||||||||||
| value: 'blue-900' | ||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||
| // In order to prevent layout shifts, we use box shadows to render the borders since we can't add an absolute position div (it messes up the cell count due to the way Table collections are built) | ||||||||||||||||||||||||||||||||||||||||||||||||
| // In highlight mode, selected groups also have gray borders between the items in addition to having a blue outer border | ||||||||||||||||||||||||||||||||||||||||||||||||
| // Having a border have two colors is possible, the issue is that the browser will render a diagonal line where the two borders meet | ||||||||||||||||||||||||||||||||||||||||||||||||
| // Using box shadows gives us a bit more control on how the border colors appear | ||||||||||||||||||||||||||||||||||||||||||||||||
| boxShadow: { | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1612
to
+1616
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. I'm not sure I entirely understand this part, at the moment each table row renders just the bottom border right so could we just change the color of that border based on if the row is selected and is the last/first row in its selection group?
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. Oh is it because of the top most row and if it is selected? Could we use a box shadow for that case only or something?
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. For highlight selection, the borders actually have a width of 0px. The reasoning for the borders having a width of 0px for highlight is for two reasons: 1) layout shifts 2) border rendering in browsers
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. The border for the first row is handled slightly differently due to issues with the CSS paint order. Initially, I also just used a box shadow to render the top border of the first item if it was selected, but if a cell has a divider, the divider gets rendered on top of it so you have a gray line on top of the blue line. So in order to fix that, I had to use a pseudo-element to basically force it on top of the divider. I tried isolation and z-indexes which didn't work
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. hm gotcha, I actually used a outline here (kinda like the commented out code) for the "on" drop indicators, could we do the same here instead then? react-spectrum/packages/@react-spectrum/s2/src/TableView.tsx Lines 1744 to 1766 in 083f4e5
If not, I'm happy to switch my implementation to use box shadows completely, but whichever way we choose should be used for both "on" drop and highlight selection for sure |
||||||||||||||||||||||||||||||||||||||||||||||||
| selectionStyle: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| highlight: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| default: '[inset 0px -1px 0px var(--borderColorGray)]', | ||||||||||||||||||||||||||||||||||||||||||||||||
| isNextSelected: '[inset 0px -1px 0px var(--borderColorBlue)]', | ||||||||||||||||||||||||||||||||||||||||||||||||
| isSelected: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| default: '[inset 0px -1px 0px var(--borderColorBlue), inset 1px 0px 0px var(--borderColorBlue), inset -1px 0px var(--borderColorBlue)]', | ||||||||||||||||||||||||||||||||||||||||||||||||
| isNextSelected: '[inset 1px 0px 0px var(--borderColorBlue), inset -1px 0px var(--borderColorBlue), inset 0px -1px 0px var(--borderColorGray)]', | ||||||||||||||||||||||||||||||||||||||||||||||||
| isFirstItem: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| default: '[inset 0px 1px 0px var(--borderColorBlue), inset 0px -1px 0px var(--borderColorBlue), inset 1px 0px 0px var(--borderColorBlue), inset -1px 0px var(--borderColorBlue)]', | ||||||||||||||||||||||||||||||||||||||||||||||||
| isNextSelected: '[inset 1px 0px 0px var(--borderColorBlue), inset -1px 0px 0px var(--borderColorBlue), inset 0px -1px 0px var(--borderColorGray)]' | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||
| '--focusIndicatorHeight': { | ||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'top', | ||||||||||||||||||||||||||||||||||||||||||||||||
| value: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| default: 'calc(self(height) - 1px)' | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||
| isolation: 'isolate', | ||||||||||||||||||||||||||||||||||||||||||||||||
| zIndex: 3, | ||||||||||||||||||||||||||||||||||||||||||||||||
| forcedColorAdjust: 'none' | ||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // Unlike the other items, the first item needs to render a border on top when it is selected in highlight mode | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // Unfortunately, we can't add a position: absolute div to Row because it messes up the cell count | ||||||||||||||||||||||||||||||||||||||||||||||||
| // As a result, we rely on adding a css pseudo element when the item is selected + first item + highlight selection | ||||||||||||||||||||||||||||||||||||||||||||||||
| const border = css( | ||||||||||||||||||||||||||||||||||||||||||||||||
| `&:after { | ||||||||||||||||||||||||||||||||||||||||||||||||
| content: ""; | ||||||||||||||||||||||||||||||||||||||||||||||||
| width: 100%; | ||||||||||||||||||||||||||||||||||||||||||||||||
| height: 100%; | ||||||||||||||||||||||||||||||||||||||||||||||||
| top: 0; | ||||||||||||||||||||||||||||||||||||||||||||||||
| inset-inline-start: 0; | ||||||||||||||||||||||||||||||||||||||||||||||||
| z-index: 3; | ||||||||||||||||||||||||||||||||||||||||||||||||
| position: absolute; | ||||||||||||||||||||||||||||||||||||||||||||||||
| box-sizing: border-box; | ||||||||||||||||||||||||||||||||||||||||||||||||
| border-top-width: 1px; | ||||||||||||||||||||||||||||||||||||||||||||||||
| border-bottom-width: 0px; | ||||||||||||||||||||||||||||||||||||||||||||||||
| border-inline-start-width: 0px; | ||||||||||||||||||||||||||||||||||||||||||||||||
| border-inline-end-width: 0px; | ||||||||||||||||||||||||||||||||||||||||||||||||
| border-style: solid; | ||||||||||||||||||||||||||||||||||||||||||||||||
| border-color: var(--borderColorBlue); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| ` | ||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| const selectionCheckbox = style({ | ||||||||||||||||||||||||||||||||||||||||||||||||
| visibility: { | ||||||||||||||||||||||||||||||||||||||||||||||||
| default: 'visible', | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1574,7 +1649,7 @@ export interface RowProps<T> extends Pick<RACRowProps<T>, 'id' | 'columns' | 'is | |||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||
| export const Row = /*#__PURE__*/ (forwardRef as forwardRefType)(function Row<T extends object>({id, columns, children, dependencies = [], ...otherProps}: RowProps<T>, ref: DOMRef<HTMLDivElement>) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| let {selectionBehavior, selectionMode} = useTableOptions(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| let tableVisualOptions = useContext(InternalTableContext); | ||||||||||||||||||||||||||||||||||||||||||||||||
| let {selectionStyle, ...tableVisualOptions} = useContext(InternalTableContext); | ||||||||||||||||||||||||||||||||||||||||||||||||
| let domRef = useDOMRef(ref); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1585,8 +1660,13 @@ export const Row = /*#__PURE__*/ (forwardRef as forwardRefType)(function Row<T e | |||||||||||||||||||||||||||||||||||||||||||||||
| dependencies={[...dependencies, columns]} | ||||||||||||||||||||||||||||||||||||||||||||||||
| className={renderProps => row({ | ||||||||||||||||||||||||||||||||||||||||||||||||
| ...renderProps, | ||||||||||||||||||||||||||||||||||||||||||||||||
| ...tableVisualOptions | ||||||||||||||||||||||||||||||||||||||||||||||||
| }) + (renderProps.isFocusVisible ? ' ' + css('&:before { content: ""; display: inline-block; position: sticky; inset-inline-start: 0; width: 3px; height: 100%; margin-inline-end: -3px; margin-block-end: 1px; z-index: 3; background-color: var(--rowFocusIndicatorColor)') : '')} | ||||||||||||||||||||||||||||||||||||||||||||||||
| ...tableVisualOptions, | ||||||||||||||||||||||||||||||||||||||||||||||||
| selectionStyle, | ||||||||||||||||||||||||||||||||||||||||||||||||
| isNextSelected: isNextSelected(id, renderProps.state), | ||||||||||||||||||||||||||||||||||||||||||||||||
| isFirstItem: isFirstItem(id, renderProps.state) | ||||||||||||||||||||||||||||||||||||||||||||||||
| }) + (renderProps.isFocusVisible ? ' ' + css('&:before { content: ""; display: inline-block; position: sticky; inset-inline-start: 0; width: 3px; height: var(--focusIndicatorHeight); margin-inline-end: -3px; margin-block-end: 1px; z-index: 3; background-color: var(--rowFocusIndicatorColor)') : '') | ||||||||||||||||||||||||||||||||||||||||||||||||
| + (isFirstItem(id, renderProps.state) && renderProps.isSelected && selectionStyle === 'highlight' ? ' ' + border : '') | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| {...otherProps}> | ||||||||||||||||||||||||||||||||||||||||||||||||
| {selectionMode !== 'none' && selectionBehavior === 'toggle' && ( | ||||||||||||||||||||||||||||||||||||||||||||||||
| // Not sure what we want to do with this className, in Cell it currently overrides the className that would have been applied. | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||


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.
moved to the utils file since it was being shared by ListView, TreeView, and TableView