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, }))