add more table configuration

- alternate row colors
- row hover highlight
- vertical borders
- horizontal borders
This commit is contained in:
jeffvli
2025-10-09 13:12:53 -07:00
parent c5c9311d00
commit 33b0cda2a0
3 changed files with 68 additions and 42 deletions
@@ -41,12 +41,30 @@
padding: var(--theme-spacing-xs); padding: var(--theme-spacing-xs);
} }
.container.with-row-border { .container.with-horizontal-border {
border-bottom: 1px solid var(--theme-colors-border); border-bottom: 1px solid var(--theme-colors-border);
} }
.container.data-row.row-hover-enabled:hover::before, .container.with-vertical-border {
.container.data-row.row-hover-enabled.row-hovered::before { border-right: 1px solid var(--theme-colors-border);
}
.container.alternate-row-even {
background-color: initial;
}
.container.alternate-row-odd {
@mixin dark {
background-color: darken(var(--theme-colors-background), 30%);
}
@mixin light {
background-color: darken(var(--theme-colors-background), 2%);
}
}
.container.data-row.row-hover-highlight-enabled:hover::before,
.container.data-row.row-hover-highlight-enabled.row-hovered::before {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
@@ -154,7 +154,7 @@ export const TableColumnTextContainer = (
const dataIndex = isDataRow ? props.rowIndex - 1 : props.rowIndex; const dataIndex = isDataRow ? props.rowIndex - 1 : props.rowIndex;
useEffect(() => { useEffect(() => {
if (!isDataRow || !containerRef.current || !props.enableRowHover) return; if (!isDataRow || !containerRef.current) return;
const container = containerRef.current; const container = containerRef.current;
const rowIndex = props.rowIndex; const rowIndex = props.rowIndex;
@@ -178,19 +178,24 @@ export const TableColumnTextContainer = (
container.removeEventListener('mouseenter', handleMouseEnter); container.removeEventListener('mouseenter', handleMouseEnter);
container.removeEventListener('mouseleave', handleMouseLeave); container.removeEventListener('mouseleave', handleMouseLeave);
}; };
}, [isDataRow, props.rowIndex, props.enableRowHover]); }, [isDataRow, props.rowIndex, props.enableRowHoverHighlight]);
return ( return (
<div <div
className={clsx(styles.container, props.containerClassName, { className={clsx(styles.container, props.containerClassName, {
[styles.alternateRowEven]:
props.enableAlternateRowColors && isDataRow && dataIndex % 2 === 0,
[styles.alternateRowOdd]:
props.enableAlternateRowColors && isDataRow && dataIndex % 2 === 1,
[styles.center]: props.columns[props.columnIndex].align === 'center', [styles.center]: props.columns[props.columnIndex].align === 'center',
[styles.compact]: props.size === 'compact', [styles.compact]: props.size === 'compact',
[styles.dataRow]: isDataRow, [styles.dataRow]: isDataRow,
[styles.left]: props.columns[props.columnIndex].align === 'start', [styles.left]: props.columns[props.columnIndex].align === 'start',
[styles.right]: props.columns[props.columnIndex].align === 'end', [styles.right]: props.columns[props.columnIndex].align === 'end',
[styles.rowHoverEnabled]: isDataRow && props.enableRowHover, [styles.rowHoverHighlightEnabled]: isDataRow && props.enableRowHoverHighlight,
[styles.withRowBorder]: [styles.withHorizontalBorder]:
props.enableRowBorders && props.enableHeader && props.rowIndex > 0, props.enableHorizontalBorders && props.enableHeader && props.rowIndex > 0,
[styles.withVerticalBorder]: props.enableVerticalBorders,
})} })}
data-row-index={isDataRow ? props.rowIndex : undefined} data-row-index={isDataRow ? props.rowIndex : undefined}
ref={containerRef} ref={containerRef}
@@ -223,7 +228,7 @@ export const TableColumnContainer = (
const dataIndex = isDataRow ? props.rowIndex - 1 : props.rowIndex; const dataIndex = isDataRow ? props.rowIndex - 1 : props.rowIndex;
useEffect(() => { useEffect(() => {
if (!isDataRow || !containerRef.current || !props.enableRowHover) return; if (!isDataRow || !containerRef.current) return;
const container = containerRef.current; const container = containerRef.current;
const rowIndex = props.rowIndex; const rowIndex = props.rowIndex;
@@ -247,19 +252,24 @@ export const TableColumnContainer = (
container.removeEventListener('mouseenter', handleMouseEnter); container.removeEventListener('mouseenter', handleMouseEnter);
container.removeEventListener('mouseleave', handleMouseLeave); container.removeEventListener('mouseleave', handleMouseLeave);
}; };
}, [isDataRow, props.rowIndex, props.enableRowHover]); }, [isDataRow, props.rowIndex, props.enableRowHoverHighlight]);
return ( return (
<div <div
className={clsx(styles.container, props.className, { className={clsx(styles.container, props.className, {
[styles.alternateRowEven]:
props.enableAlternateRowColors && isDataRow && dataIndex % 2 === 0,
[styles.alternateRowOdd]:
props.enableAlternateRowColors && isDataRow && dataIndex % 2 === 1,
[styles.center]: props.columns[props.columnIndex].align === 'center', [styles.center]: props.columns[props.columnIndex].align === 'center',
[styles.compact]: props.size === 'compact', [styles.compact]: props.size === 'compact',
[styles.dataRow]: isDataRow, [styles.dataRow]: isDataRow,
[styles.left]: props.columns[props.columnIndex].align === 'start', [styles.left]: props.columns[props.columnIndex].align === 'start',
[styles.right]: props.columns[props.columnIndex].align === 'end', [styles.right]: props.columns[props.columnIndex].align === 'end',
[styles.rowHoverEnabled]: isDataRow && props.enableRowHover, [styles.rowHoverHighlightEnabled]: isDataRow && props.enableRowHoverHighlight,
[styles.withRowBorder]: [styles.withHorizontalBorder]:
props.enableRowBorders && props.enableHeader && props.rowIndex > 0, props.enableHorizontalBorders && props.enableHeader && props.rowIndex > 0,
[styles.withVerticalBorder]: props.enableVerticalBorders,
})} })}
data-row-index={isDataRow ? props.rowIndex : undefined} data-row-index={isDataRow ? props.rowIndex : undefined}
ref={containerRef} ref={containerRef}
@@ -2,7 +2,7 @@
import { useMergedRef } from '@mantine/hooks'; import { useMergedRef } from '@mantine/hooks';
import clsx from 'clsx'; import clsx from 'clsx';
import { AnimatePresence, motion } from 'motion/react'; import { AnimatePresence } from 'motion/react';
import { useOverlayScrollbars } from 'overlayscrollbars-react'; import { useOverlayScrollbars } from 'overlayscrollbars-react';
import { import {
type JSXElementConstructor, type JSXElementConstructor,
@@ -24,18 +24,21 @@ import {
ItemListStateActions, ItemListStateActions,
useItemListState, useItemListState,
} from '/@/renderer/components/item-list/helpers/item-list-state'; } from '/@/renderer/components/item-list/helpers/item-list-state';
import { sortTableColumns } from '/@/renderer/components/item-list/helpers/sort-table-columns'; import { parseTableColumns } from '/@/renderer/components/item-list/helpers/parse-table-columns';
import { ItemListHandle, ItemTableListColumnConfig } from '/@/renderer/components/item-list/types'; import { ItemListHandle, ItemTableListColumnConfig } from '/@/renderer/components/item-list/types';
import { LibraryItem } from '/@/shared/types/domain-types'; import { LibraryItem } from '/@/shared/types/domain-types';
export interface TableItemProps { export interface TableItemProps {
columns: ItemTableListColumnConfig[]; columns: ItemTableListColumnConfig[];
data: unknown[]; data: unknown[];
enableAlternateRowColors?: boolean;
enableExpansion?: boolean; enableExpansion?: boolean;
enableHeader?: boolean; enableHeader?: boolean;
enableRowBorders?: boolean; enableHorizontalBorders?: boolean;
enableRowHover?: boolean; enableRowHoverHighlight?: boolean;
enableSelection?: boolean; enableSelection?: boolean;
enableVerticalBorders?: boolean;
getRowHeight: (index: number, cellProps: TableItemProps) => number;
internalState: ItemListStateActions; internalState: ItemListStateActions;
itemType: LibraryItem; itemType: LibraryItem;
size?: 'compact' | 'default'; size?: 'compact' | 'default';
@@ -45,11 +48,13 @@ interface ItemTableListProps {
CellComponent: JSXElementConstructor<CellComponentProps<TableItemProps>>; CellComponent: JSXElementConstructor<CellComponentProps<TableItemProps>>;
columns: ItemTableListColumnConfig[]; columns: ItemTableListColumnConfig[];
data: unknown[]; data: unknown[];
enableAlternateRowColors?: boolean;
enableExpansion?: boolean; enableExpansion?: boolean;
enableHeader?: boolean; enableHeader?: boolean;
enableRowBorders?: boolean; enableHorizontalBorders?: boolean;
enableRowHover?: boolean; enableRowHoverHighlight?: boolean;
enableSelection?: boolean; enableSelection?: boolean;
enableVerticalBorders?: boolean;
headerHeight?: number; headerHeight?: number;
initialTop?: { initialTop?: {
behavior?: 'auto' | 'smooth'; behavior?: 'auto' | 'smooth';
@@ -74,11 +79,13 @@ export const ItemTableList = ({
CellComponent, CellComponent,
columns, columns,
data, data,
enableExpansion = false, enableAlternateRowColors = false,
enableExpansion = true,
enableHeader = true, enableHeader = true,
enableRowBorders = false, enableHorizontalBorders = false,
enableRowHover = false, enableRowHoverHighlight = true,
enableSelection = false, enableSelection = true,
enableVerticalBorders = false,
headerHeight = 40, headerHeight = 40,
initialTop, initialTop,
itemType, itemType,
@@ -92,7 +99,7 @@ export const ItemTableList = ({
size = 'default', size = 'default',
}: ItemTableListProps) => { }: ItemTableListProps) => {
const totalItemCount = data.length; const totalItemCount = data.length;
const sortedColumns = useMemo(() => sortTableColumns(columns), [columns]); const sortedColumns = useMemo(() => parseTableColumns(columns), [columns]);
const columnCount = sortedColumns.length; const columnCount = sortedColumns.length;
const [centerContainerWidth, setCenterContainerWidth] = useState(0); const [centerContainerWidth, setCenterContainerWidth] = useState(0);
@@ -130,7 +137,7 @@ export const ItemTableList = ({
sortedColumns.forEach((col, idx) => { sortedColumns.forEach((col, idx) => {
if (col.pinned === null) { if (col.pinned === null) {
unpinnedIndices.push(idx); unpinnedIndices.push(idx);
if (col.autoWidth) { if (col.autoSize) {
autoUnpinnedIndices.push(idx); autoUnpinnedIndices.push(idx);
} }
} }
@@ -238,7 +245,6 @@ export const ItemTableList = ({
autoHideDelay: 500, autoHideDelay: 500,
pointers: ['mouse', 'pen', 'touch'], pointers: ['mouse', 'pen', 'touch'],
theme: 'feishin-os-scrollbar', theme: 'feishin-os-scrollbar',
visibility: 'visible',
}, },
}, },
}); });
@@ -521,7 +527,7 @@ export const ItemTableList = ({
const getRowHeight = useCallback( const getRowHeight = useCallback(
(index: number, cellProps: TableItemProps) => { (index: number, cellProps: TableItemProps) => {
const height = size === 'compact' ? 40 : 68; const height = size === 'compact' ? 40 : 64;
const baseHeight = const baseHeight =
typeof rowHeight === 'number' ? rowHeight : rowHeight?.(index, cellProps) || height; typeof rowHeight === 'number' ? rowHeight : rowHeight?.(index, cellProps) || height;
@@ -653,11 +659,14 @@ export const ItemTableList = ({
const itemProps: TableItemProps = { const itemProps: TableItemProps = {
columns: sortedColumns, columns: sortedColumns,
data, data,
enableAlternateRowColors,
enableExpansion, enableExpansion,
enableHeader, enableHeader,
enableRowBorders, enableHorizontalBorders,
enableRowHover, enableRowHoverHighlight,
enableSelection, enableSelection,
enableVerticalBorders,
getRowHeight,
internalState, internalState,
itemType, itemType,
size, size,
@@ -704,18 +713,7 @@ export const ItemTableList = ({
}, [imperativeHandle]); }, [imperativeHandle]);
return ( return (
<motion.div <div className={styles.itemTableListContainer}>
animate={{
height: '100%',
opacity: 1,
transition: {
duration: 1,
ease: 'backInOut',
},
}}
className={styles.itemTableListContainer}
initial={{ opacity: 0 }}
>
<div className={styles.itemTableContainer}> <div className={styles.itemTableContainer}>
<div <div
className={styles.itemTablePinnedColumnsGridContainer} className={styles.itemTablePinnedColumnsGridContainer}
@@ -903,6 +901,6 @@ export const ItemTableList = ({
</ExpandedListContainer> </ExpandedListContainer>
)} )}
</AnimatePresence> </AnimatePresence>
</motion.div> </div>
); );
}; };