diff --git a/v3/src/components/case-table/case-table-types.ts b/v3/src/components/case-table/case-table-types.ts index 98934de88a..b82154d9f3 100644 --- a/v3/src/components/case-table/case-table-types.ts +++ b/v3/src/components/case-table/case-table-types.ts @@ -1,6 +1,6 @@ import { - CalculatedColumn, CellClickArgs, ColSpanArgs, Column, RenderEditCellProps, RenderCellProps, RenderHeaderCellProps, - RenderRowProps, RowsChangeData + CalculatedColumn, CellClickArgs, ColSpanArgs, Column, RenderCellProps, RenderEditCellProps, Renderers, + RenderHeaderCellProps, RenderRowProps, RowsChangeData } from "react-data-grid" import { IGroupedCase, symFirstChild } from "../../models/data/data-set-types" @@ -18,6 +18,7 @@ export interface TRow extends IGroupedCase { export interface TRowsChangeData extends RowsChangeData {} export interface TColumn extends Column {} export interface TCalculatedColumn extends CalculatedColumn {} +export interface TRenderers extends Renderers {} export interface TRenderEditCellProps extends RenderEditCellProps {} export interface TRenderCellProps extends RenderCellProps {} export interface TRenderHeaderCellProps extends RenderHeaderCellProps {} diff --git a/v3/src/components/case-table/case-table.scss b/v3/src/components/case-table/case-table.scss index cce2f4fa0a..64bacde0ed 100644 --- a/v3/src/components/case-table/case-table.scss +++ b/v3/src/components/case-table/case-table.scss @@ -255,19 +255,17 @@ $table-body-font-size: 8pt; } } - .cell-span { - .cell-color-swatch { - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; + .cell-color-swatch { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; - .cell-color-swatch-interior { - width: calc(100% - 4px); - height: calc(100% - 8px); - margin-top: 0.5px; - } + .cell-color-swatch-interior { + width: calc(100% - 4px); + height: calc(100% - 8px); + margin-top: 0.5px; } } diff --git a/v3/src/components/case-table/collection-table.tsx b/v3/src/components/case-table/collection-table.tsx index 347c23f69b..6dc681bca9 100644 --- a/v3/src/components/case-table/collection-table.tsx +++ b/v3/src/components/case-table/collection-table.tsx @@ -3,9 +3,10 @@ import { observer } from "mobx-react-lite" import React, { useCallback, useEffect, useRef } from "react" import DataGrid, { DataGridHandle } from "react-data-grid" import { kCollectionTableBodyDropZoneBaseId } from "./case-table-drag-drop" -import { OnScrollClosestRowIntoViewFn, OnTableScrollFn, TRow } from "./case-table-types" +import { OnScrollClosestRowIntoViewFn, OnTableScrollFn, TRenderers, TRow } from "./case-table-types" import { CollectionTableSpacer } from "./collection-table-spacer" import { CollectionTitle } from "./collection-title" +import { customRenderRow } from "./custom-row" import { useColumns } from "./use-columns" import { useIndexColumn } from "./use-index-column" import { useRows } from "./use-rows" @@ -25,6 +26,9 @@ import styles from "./case-table-shared.scss" type OnNewCollectionDropFn = (dataSet: IDataSet, attrId: string, beforeCollectionId: string) => void +// custom renderers for use with RDG +const renderers: TRenderers = { renderRow: customRenderRow } + interface IProps { onMount: (collectionId: string) => void onNewCollectionDrop: OnNewCollectionDropFn @@ -130,7 +134,7 @@ export const CollectionTable = observer(function CollectionTable(props: IProps)
- (null) diff --git a/v3/src/components/case-table/custom-row.tsx b/v3/src/components/case-table/custom-row.tsx new file mode 100644 index 0000000000..5cea253044 --- /dev/null +++ b/v3/src/components/case-table/custom-row.tsx @@ -0,0 +1,50 @@ +import { colord } from "colord" +import { observer } from "mobx-react-lite" +import React, { useEffect, useRef } from "react" +import { Row } from "react-data-grid" +import { TRenderRowProps } from "./case-table-types" +import { useCollectionContext } from "../../hooks/use-collection-context" +import { useDataSetContext } from "../../hooks/use-data-set-context" +import { IDataSet } from "../../models/data/data-set" +import { parseColorToHex } from "../../utilities/color-utils" + +// have to balance background visibility vs. visibility/accessibility of rendered text +const kAlphaForRowBackground = 0.1 + +function getRowColor(data: IDataSet | undefined, collectionId: string, caseId: string) { + if (!data) return + // check first attribute of this collection or parent collections for a color attribute + const collectionIndex = data?.getCollectionIndex(collectionId) ?? -1 + for (let i = collectionIndex; i >= 0; --i) { + const collection = i < data.collections.length ? data.collections[i] : undefined + const firstAttribute = collection?.attributes[0] ?? data.attributes[0] + if (firstAttribute?.userType === "color") { + const firstValue = data.getStrValue(caseId, firstAttribute.id) + const firstColor = firstValue && parseColorToHex(firstValue, { colorNames: true }) + if (firstColor) { + // reduce alpha for text contrast/visibility + return colord(firstColor).alpha(kAlphaForRowBackground).toHex() + } + } + } +} + +export const CustomRow = observer(function CustomRow(props: TRenderRowProps) { + const data = useDataSetContext() + const collectionId = useCollectionContext() + const rowRef = useRef(null) + const { row: { __id__: caseId } } = props + const rowColor = getRowColor(data, collectionId, caseId) ?? "" + + useEffect(() => { + if (rowRef.current) { + rowRef.current.style.backgroundColor = rowColor + } + }, [rowColor]) + + return +}) + +export function customRenderRow(key: React.Key, props: TRenderRowProps) { + return +} diff --git a/v3/src/components/case-table/use-columns.tsx b/v3/src/components/case-table/use-columns.tsx index e4ac4ad75c..090f43c3f8 100644 --- a/v3/src/components/case-table/use-columns.tsx +++ b/v3/src/components/case-table/use-columns.tsx @@ -28,27 +28,33 @@ export const getNumFormatter = (formatStr: string) => { return formatter } -export function renderValue(str = "", num = NaN, attr?: IAttribute) { +export function renderValue(str = "", num = NaN, attr?: IAttribute, key?: number) { const { type, userType } = attr || {} // colors const color = type === "color" || !userType ? parseColor(str, { colorNames: type === "color" }) : "" if (color) { - return ( -
-
-
- ) + return { + value: color, + content: ( +
+
+
+ ) + } } // numbers if (isFinite(num)) { const formatStr = attr?.format ?? kDefaultFormatStr const formatter = getNumFormatter(formatStr) - if (formatter) return formatter(num) + if (formatter) str = formatter(num) } - return str + return { + value: str, + content: {str} + } } interface IUseColumnsProps { @@ -63,20 +69,20 @@ export const useColumns = ({ data, indexColumn }: IUseColumnsProps) => { // cell renderer const RenderCell = useCallback(function({ column, row }: TRenderCellProps) { - const str = (data?.getStrValue(row.__id__, column.key) ?? "").trim() - const isParentCollapsed = row[symParent] ? caseMetadata?.isCollapsed(row[symParent]) : false - const output = isParentCollapsed - ? "" - : renderValue(str, data?.getNumeric(row.__id__, column.key), data?.attrFromID(column.key)) - const tooltip = typeof output === "string" ? output : str + const strValue = (data?.getStrValue(row.__id__, column.key) ?? "").trim() + const numValue = data?.getNumeric(row.__id__, column.key) // if this is the first React render after performance rendering, add a // random key to force React to render the contents for synchronization const key = row[symDom]?.has(column.key) ? Math.random() : undefined row[symDom]?.delete(column.key) + const isParentCollapsed = row[symParent] ? caseMetadata?.isCollapsed(row[symParent]) : false + const { value, content } = isParentCollapsed + ? { value: "", content: null } + : renderValue(strValue, numValue, data?.attrFromID(column.key), key) return ( - - {output} + {content} ) }, [caseMetadata, data])