Skip to content

Commit

Permalink
Add conditional shadow to sticky experiments column (#2062)
Browse files Browse the repository at this point in the history
* Add box-shadow to sticky experiments column
* Take off the experiment column's gray right border

Co-authored-by: mattseddon <37993418+mattseddon@users.noreply.github.com>
  • Loading branch information
julieg18 and mattseddon authored Jul 21, 2022
1 parent 42dad48 commit dc452aa
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 33 deletions.
14 changes: 12 additions & 2 deletions webview/src/experiments/components/table/MergeHeaderGroups.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
OnDragStart,
OnDrop
} from '../../../shared/components/dragDrop/DragDropWorkbench'
import { isFirstInArr } from '../../util/isFirstInArr'

export const MergedHeaderGroups: React.FC<{
headerGroup: HeaderGroup<Experiment>
Expand All @@ -20,6 +21,9 @@ export const MergedHeaderGroups: React.FC<{
onDragUpdate: OnDragOver
onDragStart: OnDragStart
onDragEnd: OnDrop
isFirst: boolean
setExpColumnNeedsShadow: (needsShadow: boolean) => void
root: HTMLElement | null
}> = ({
headerGroup,
sorts,
Expand All @@ -28,16 +32,21 @@ export const MergedHeaderGroups: React.FC<{
orderedColumns,
onDragUpdate,
onDragEnd,
onDragStart
onDragStart,
root,
isFirst,
setExpColumnNeedsShadow
}) => {
return (
<div
{...headerGroup.getHeaderGroupProps({
className: cx(styles.tr, styles.headRow)
})}
>
{headerGroup.headers.map((column: HeaderGroup<Experiment>) => (
{headerGroup.headers.map((column: HeaderGroup<Experiment>, ind) => (
<TableHeader
isFirst={isFirst && isFirstInArr(ind)}
setExpColumnNeedsShadow={setExpColumnNeedsShadow}
key={column.id}
orderedColumns={orderedColumns}
column={column}
Expand All @@ -47,6 +56,7 @@ export const MergedHeaderGroups: React.FC<{
onDragOver={onDragUpdate}
onDragStart={onDragStart}
onDrop={onDragEnd}
root={root}
/>
))}
</div>
Expand Down
11 changes: 9 additions & 2 deletions webview/src/experiments/components/table/Table.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useRef } from 'react'
import React, { useRef, useState } from 'react'
import cx from 'classnames'
import styles from './styles.module.scss'
import { TableHead } from './TableHead'
Expand Down Expand Up @@ -137,6 +137,7 @@ export const Table: React.FC<TableProps & WithChanges> = ({

const { clearSelectedRows, batchSelection, lastSelectedRow } =
React.useContext(RowSelectionContext)
const [expColumnNeedsShadow, setExpColumnNeedsShadow] = useState(false)

const tableRef = useRef<HTMLDivElement>(null)

Expand Down Expand Up @@ -178,7 +179,12 @@ export const Table: React.FC<TableProps & WithChanges> = ({
return (
<div className={styles.tableContainer}>
<div
{...getTableProps({ className: styles.table })}
{...getTableProps({
className: cx(
styles.table,
expColumnNeedsShadow && styles.withExpColumnShadow
)
})}
ref={tableRef}
tabIndex={0}
role="tree"
Expand All @@ -195,6 +201,7 @@ export const Table: React.FC<TableProps & WithChanges> = ({
filters={filters}
columns={columns}
root={tableRef.current}
setExpColumnNeedsShadow={setExpColumnNeedsShadow}
/>
{rows.map(row => (
<TableBody
Expand Down
10 changes: 8 additions & 2 deletions webview/src/experiments/components/table/TableHead.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
OnDragStart
} from '../../../shared/components/dragDrop/DragDropWorkbench'
import { getSelectedForPlotsCount } from '../../util/rows'
import { isFirstInArr } from '../../util/isFirstInArr'

interface TableHeadProps {
instance: TableInstance<Experiment>
Expand All @@ -25,6 +26,7 @@ interface TableHeadProps {
filteredCounts: FilteredCounts
filters: string[]
root: HTMLElement | null
setExpColumnNeedsShadow: (needsShadow: boolean) => void
}

export const TableHead = ({
Expand All @@ -39,7 +41,8 @@ export const TableHead = ({
filteredCounts,
filters,
columns,
sorts
sorts,
setExpColumnNeedsShadow
}: TableHeadProps) => {
const orderedColumns = useColumnOrder(columns, columnOrder)
const allHeaders: HeaderGroup<Experiment>[] = []
Expand Down Expand Up @@ -110,7 +113,7 @@ export const TableHead = ({
filters={filters}
filteredCounts={filteredCounts}
/>
{headerGroups.map(headerGroup => (
{headerGroups.map((headerGroup, ind) => (
// eslint-disable-next-line react/jsx-key
<MergedHeaderGroups
{...headerGroup.getHeaderGroupProps()}
Expand All @@ -122,6 +125,9 @@ export const TableHead = ({
onDragStart={onDragStart}
onDragUpdate={onDragUpdate}
onDragEnd={onDragEnd}
root={root}
isFirst={isFirstInArr(ind)}
setExpColumnNeedsShadow={setExpColumnNeedsShadow}
/>
))}
</div>
Expand Down
143 changes: 117 additions & 26 deletions webview/src/experiments/components/table/TableHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import {
Column,
ColumnType
} from 'dvc/src/experiments/webview/contract'
import React from 'react'
import React, { useEffect } from 'react'
import { HeaderGroup } from 'react-table'
import cx from 'classnames'
import { useInView } from 'react-intersection-observer'
import { MessageFromWebviewType } from 'dvc/src/webview/contract'
import { VSCodeDivider } from '@vscode/webview-ui-toolkit/react'
import styles from './styles.module.scss'
Expand Down Expand Up @@ -129,6 +130,76 @@ const getIconMenuItems = (
}
]

const FirstTableHeaderCellWrapper: React.FC<{
children: React.ReactNode
setExpColumnNeedsShadow: (needsShadow: boolean) => void
root: HTMLElement | null
}> = ({ root, setExpColumnNeedsShadow, children }) => {
const [ref, needsShadow] = useInView({
root,
rootMargin: '0px 0px 0px -15px',
threshold: 1
})

useEffect(() => {
setExpColumnNeedsShadow(needsShadow)
}, [needsShadow, setExpColumnNeedsShadow])

return <div ref={ref}>{children}</div>
}

const TableHeaderCellContents: React.FC<{
column: HeaderGroup<Experiment>
sortOrder: SortOrder
sortEnabled: boolean
hasFilter: boolean
isDraggable: boolean
menuSuppressed: boolean
onDragOver: OnDragOver
onDragStart: OnDragStart
onDrop: OnDrop
canResize: boolean
setMenuSuppressed: (menuSuppressed: boolean) => void
resizerHeight: string
}> = ({
column,
sortEnabled,
sortOrder,
hasFilter,
isDraggable,
menuSuppressed,
onDragOver,
onDragStart,
onDrop,
canResize,
setMenuSuppressed,
resizerHeight
}) => {
return (
<>
<div className={styles.iconMenu}>
<IconMenu items={getIconMenuItems(sortEnabled, sortOrder, hasFilter)} />
</div>
<ColumnDragHandle
column={column}
disabled={!isDraggable || menuSuppressed}
onDragOver={onDragOver}
onDragStart={onDragStart}
onDrop={onDrop}
/>
{canResize && (
<div
{...column.getResizerProps()}
onMouseEnter={() => setMenuSuppressed(true)}
onMouseLeave={() => setMenuSuppressed(false)}
className={styles.columnResizer}
style={{ height: resizerHeight }}
/>
)}
</>
)
}

const TableHeaderCell: React.FC<{
column: HeaderGroup<Experiment>
columns: HeaderGroup<Experiment>[]
Expand All @@ -141,6 +212,9 @@ const TableHeaderCell: React.FC<{
onDragOver: OnDragOver
onDragStart: OnDragStart
onDrop: OnDrop
isFirst: boolean
setExpColumnNeedsShadow: (needsShadow: boolean) => void
root: HTMLElement | null
}> = ({
column,
columns,
Expand All @@ -152,10 +226,12 @@ const TableHeaderCell: React.FC<{
menuDisabled,
onDragOver,
onDragStart,
onDrop
onDrop,
root,
isFirst,
setExpColumnNeedsShadow
}) => {
const [menuSuppressed, setMenuSuppressed] = React.useState<boolean>(false)

const isDraggable =
!column.placeholderOf && !['id', 'timestamp'].includes(column.id)

Expand All @@ -168,6 +244,23 @@ const TableHeaderCell: React.FC<{
columns
)

const cellContents = (
<TableHeaderCellContents
column={column}
sortOrder={sortOrder}
sortEnabled={sortEnabled}
hasFilter={hasFilter}
isDraggable={isDraggable}
menuSuppressed={menuSuppressed}
onDragOver={onDragOver}
onDragStart={onDragStart}
onDrop={onDrop}
canResize={canResize}
setMenuSuppressed={setMenuSuppressed}
resizerHeight={resizerHeight}
/>
)

return (
<ContextMenu
content={menuContent}
Expand All @@ -178,31 +271,20 @@ const TableHeaderCell: React.FC<{
{...column.getHeaderProps(
getHeaderPropsArgs(column, sortEnabled, sortOrder)
)}
key={column.id}
data-testid={`header-${column.id}`}
role={'columnheader'}
key={column.id}
role="columnheader"
tabIndex={0}
>
<div className={styles.iconMenu}>
<IconMenu
items={getIconMenuItems(sortEnabled, sortOrder, hasFilter)}
/>
</div>
<ColumnDragHandle
column={column}
disabled={!isDraggable || menuSuppressed}
onDragOver={onDragOver}
onDragStart={onDragStart}
onDrop={onDrop}
/>
{canResize && (
<div
{...column.getResizerProps()}
onMouseEnter={() => setMenuSuppressed(true)}
onMouseLeave={() => setMenuSuppressed(false)}
className={styles.columnResizer}
style={{ height: resizerHeight }}
/>
{isFirst ? (
<FirstTableHeaderCellWrapper
setExpColumnNeedsShadow={setExpColumnNeedsShadow}
root={root}
>
{cellContents}
</FirstTableHeaderCellWrapper>
) : (
cellContents
)}
</div>
</ContextMenu>
Expand All @@ -218,6 +300,9 @@ interface TableHeaderProps {
onDragOver: OnDragOver
onDragStart: OnDragStart
onDrop: OnDrop
isFirst: boolean
setExpColumnNeedsShadow: (needsShadow: boolean) => void
root: HTMLElement | null
}

export const TableHeader: React.FC<TableHeaderProps> = ({
Expand All @@ -228,7 +313,10 @@ export const TableHeader: React.FC<TableHeaderProps> = ({
orderedColumns,
onDragOver,
onDragStart,
onDrop
onDrop,
root,
isFirst,
setExpColumnNeedsShadow
}) => {
const baseColumn = column.placeholderOf || column
const sort = sorts.find(sort => sort.path === baseColumn.id)
Expand Down Expand Up @@ -278,6 +366,9 @@ export const TableHeader: React.FC<TableHeaderProps> = ({
onDragStart={onDragStart}
onDrop={onDrop}
menuDisabled={!isSortable && column.group !== ColumnType.PARAMS}
root={root}
isFirst={isFirst}
setExpColumnNeedsShadow={setExpColumnNeedsShadow}
menuContent={
<div>
<MessagesMenu options={contextMenuOptions} />
Expand Down
21 changes: 20 additions & 1 deletion webview/src/experiments/components/table/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,16 @@ $workspace-row-edge-margin: $edge-padding - $cell-padding;
position: sticky;
left: 0;
z-index: 3;
border-right: 2px solid var(--editor-foreground-transparency-4);

&:after {
content: '';
height: 100%;
position: absolute;
top: 0;
width: 6px;
right: 0;
transition: box-shadow 0.25s;
}
}

&.oddRow > *:first-child {
Expand All @@ -282,6 +291,12 @@ $workspace-row-edge-margin: $edge-padding - $cell-padding;
}
}

.table.withExpColumnShadow {
.tr:not(.rowSelected) > *:first-child:after {
box-shadow: 3px 0px 3px var(--vscode-widget-shadow);
}
}

.bodyRow {
&:not(.rowSelected) {
& > *:first-child {
Expand Down Expand Up @@ -311,6 +326,10 @@ $workspace-row-edge-margin: $edge-padding - $cell-padding;
background-color: $header-bg-color;
}

&:not(.rowSelected) > *:first-child:after {
right: -2px;
}

&:last-child,
.firstLevelHeader {
.paramHeaderCell,
Expand Down
1 change: 1 addition & 0 deletions webview/src/experiments/util/isFirstInArr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const isFirstInArr = (ind: number): boolean => ind === 0

0 comments on commit dc452aa

Please sign in to comment.