import { Color } from '@o/color'
import { useReaction } from '@o/use-store'
import { Box, gloss, ThemeFn } from 'gloss'
import React, { memo } from 'react'
import { DataValue } from '../DataValue'
import { CheckBox } from '../forms/CheckBox'
import { getRowValues } from '../helpers/getRowValues'
import { SelectableStore } from '../lists/SelectableStore'
import { useScale } from '../Scale'
import { DataColumns, GenericDataRow } from '../types'
import FilterRow from './FilterRow'
import { guesses, guessTheme } from './guessTheme'
import { DEFAULT_ROW_HEIGHT, TableColumnKeys, TableColumnSizes, TableOnAddFilter } from './types'
import { normaliseColumnWidth } from './utils'
/**
* Copyright 2018-present Facebook.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* @format
*/
type TableRowProps = {
color?: Color
even?: boolean
background?: Color
highlightedBackgroundColor?: Color
columnKeys: TableColumnKeys
columnSizes: TableColumnSizes
columns: DataColumns
onMouseDown: (e: React.MouseEvent) => any
onMouseEnter?: (e: React.MouseEvent) => void
multiline?: boolean
rowLineHeight: number
highlighted?: boolean
row: GenericDataRow
index: number
style?: Object
onAddFilter?: TableOnAddFilter
zebra?: boolean
selectableStore?: SelectableStore
selectable?: boolean
rowKey?: any
}
const reactionOpts = {
name: 'TableRow.isHighlighted',
}
export const TableRow = memo(function TableRow({
index,
highlighted,
row,
multiline,
columns,
columnKeys,
columnSizes,
onAddFilter,
selectableStore,
rowKey,
rowLineHeight = DEFAULT_ROW_HEIGHT,
...props
}: TableRowProps) {
const lineHeight = rowLineHeight * useScale()
const isHighlighted = useReaction(() => {
return (selectableStore && selectableStore.active.has(rowKey)) || false
}, reactionOpts)
if (!columnKeys.length) {
console.warn('No columns')
}
return (
{columnKeys.map(key => {
const value = getRowValues(row)[key]
const col = columns[key]
// TODO we could let them configure but seems weird, when do you want an "unfilterable" row?
const isFilterable = true
if (col == null) {
throw new Error(
`Trying to access column "${key}" which does not exist on row. Make sure buildRow is returning a valid row.`,
)
}
let element: React.ReactNode = null
if (col.type === 'boolean') {
element = (
{
if (col.onChange) {
col.onChange(index, next)
} else {
console.warn(`No onChange event passed to table column ${key}`)
}
}}
/>
)
} else {
element =
}
if (isFilterable && onAddFilter != null) {
element = (
{element}
)
}
return (
{element}
)
})}
)
})
const backgroundTheme: ThemeFn = props => {
let background
if (props.background) {
background = props.background
} else {
const isZebra = props.even && props.zebra
if (props.highlighted) {
if (!props.background && props.row) {
const cat = props.row.category
if (cat && guesses[cat]) {
background = guessTheme(cat, props).background || 'transparent'
}
} else {
background = props.backgroundHighlight
}
} else if (isZebra) {
background = props.backgroundZebra
} else {
background = props.background.setAlpha(0.35)
}
}
return {
background,
hoverStyle: {
background: typeof background === 'string' ? background : background.lighten(0.075, true),
},
}
}
const colorTheme: ThemeFn = props => {
let color = props.color
if (props.highlighted) {
color = props.colorHighlight
} else if (props.row) {
const cat = props.row.category
if (guesses[cat]) {
color = color || guessTheme(cat, props).color
}
}
return { color: color || 'inherit' }
}
const TableBodyRowContainer = gloss(Box, {
flexDirection: 'row',
fontWeight: 'inherit',
overflow: 'hidden',
width: '100%',
userSelect: 'none',
}).theme(backgroundTheme, colorTheme, props => {
return {
boxShadow: props.zebra ? 'none' : ['inset', 0, -1, props.borderColorLight],
'& *': {
color: props.highlighted ? `${props.colorHighlight.toCSS()} !important` : null,
},
'& img': {
background: props.highlighted ? `${props.colorHighlight.toCSS()} !important` : 'none',
},
height: props.multiline ? 'auto' : props.rowLineHeight,
lineHeight: `${String(props.rowLineHeight)}px`,
flexShrink: 0,
}
})
const TableBodyColumnContainer = gloss(Box, {
overflow: 'hidden',
padding: [0, 8],
userSelect: 'none',
maxWidth: '100%',
justifyContent: 'center',
}).theme(props => ({
flexShrink: props.width === 'flex' ? 1 : 0,
whiteSpace: props.multiline ? 'normal' : 'nowrap',
wordWrap: props.multiline ? 'break-word' : 'normal',
width: props.width === 'flex' ? '100%' : props.width,
}))