Browse Source

fix: improve how table addresses even/odd rows

pull/604/head
Dan Ditomaso 1 year ago
parent
commit
39dc88d788
  1. 149
      src/components/generic/Table/index.tsx

149
src/components/generic/Table/index.tsx

@ -1,9 +1,10 @@
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"; import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import React from "react";
export interface TableProps { export interface TableProps {
headings: Heading[]; headings: Heading[];
rows: [][]; rows: React.ReactNode[][];
} }
export interface Heading { export interface Heading {
@ -12,18 +13,15 @@ export interface Heading {
sortable: boolean; sortable: boolean;
} }
/** function numericHops(hopsAway: string | unknown): number {
* @param hopsAway String describing the number of hops away the node is from the current node if (typeof hopsAway !== "string") {
* @returns number of hopsAway or `0` if hopsAway is 'Direct' return Number.MAX_SAFE_INTEGER;
*/ }
function numericHops(hopsAway: string): number {
if (hopsAway.match(/direct/i)) { if (hopsAway.match(/direct/i)) {
return 0; return 0;
} }
if (hopsAway.match(/\d+\s+hop/gi)) { const match = hopsAway.match(/(\d+)\s+hop/i);
return Number(hopsAway.match(/(\d+)\s+hop/i)?.[1]); return Number(match?.[1] ?? Number.MAX_SAFE_INTEGER);
}
return Number.MAX_SAFE_INTEGER;
} }
export const Table = ({ headings, rows }: TableProps) => { export const Table = ({ headings, rows }: TableProps) => {
@ -39,49 +37,63 @@ export const Table = ({ headings, rows }: TableProps) => {
} }
}; };
const getElement = (cell: React.ReactNode): React.ReactElement | null => {
if (!React.isValidElement(cell)) {
return null;
}
if (cell.type === React.Fragment) {
const childrenArray = React.Children.toArray(cell.props.children);
const firstElement = childrenArray.find((child) =>
React.isValidElement(child)
);
return (firstElement as React.ReactElement) ?? null;
}
// If not a fragment, return the element itself
return cell;
};
const sortedRows = rows.slice().sort((a, b) => { const sortedRows = rows.slice().sort((a, b) => {
if (!sortColumn) return 0; if (!sortColumn) return 0;
const columnIndex = headings.findIndex((h) => h.title === sortColumn); const columnIndex = headings.findIndex((h) => h.title === sortColumn);
const aValue = a[columnIndex].props.children; if (columnIndex === -1) return 0;
const bValue = b[columnIndex].props.children;
const elementA = getElement(a[columnIndex]);
const elementB = getElement(b[columnIndex]);
if (sortColumn === "Last Heard") { if (sortColumn === "Last Heard") {
const aTimestamp = aValue.props.timestamp ?? 0; const aTimestamp = elementA?.props?.timestamp ?? 0;
const bTimestamp = bValue.props.timestamp ?? 0; const bTimestamp = elementB?.props?.timestamp ?? 0;
if (aTimestamp < bTimestamp) return sortOrder === "asc" ? -1 : 1;
if (aTimestamp < bTimestamp) { if (aTimestamp > bTimestamp) return sortOrder === "asc" ? 1 : -1;
return sortOrder === "asc" ? -1 : 1;
}
if (aTimestamp > bTimestamp) {
return sortOrder === "asc" ? 1 : -1;
}
return 0; return 0;
} }
if (sortColumn === "Connection") { if (sortColumn === "Connection") {
const aNumHops = numericHops( const aHopsStr = elementA?.props?.children;
aValue instanceof Array ? aValue[0] : aValue, const bHopsStr = elementB?.props?.children;
); const aNumHops = numericHops(aHopsStr);
const bNumHops = numericHops( const bNumHops = numericHops(bHopsStr);
bValue instanceof Array ? bValue[0] : bValue, if (aNumHops < bNumHops) return sortOrder === "asc" ? -1 : 1;
); if (aNumHops > bNumHops) return sortOrder === "asc" ? 1 : -1;
if (aNumHops < bNumHops) {
return sortOrder === "asc" ? -1 : 1;
}
if (aNumHops > bNumHops) {
return sortOrder === "asc" ? 1 : -1;
}
return 0; return 0;
} }
if (aValue < bValue) { const aValue = elementA?.props?.children;
return sortOrder === "asc" ? -1 : 1; const bValue = elementB?.props?.children;
} const valA = aValue ?? "";
if (aValue > bValue) { const valB = bValue ?? "";
return sortOrder === "asc" ? 1 : -1;
} // Ensure consistent comparison for potentially different types
const compareA = typeof valA === "string" || typeof valA === "number"
? valA
: String(valA);
const compareB = typeof valB === "string" || typeof valB === "number"
? valB
: String(valB);
if (compareA < compareB) return sortOrder === "asc" ? -1 : 1;
if (compareA > compareB) return sortOrder === "asc" ? 1 : -1;
return 0; return 0;
}); });
@ -99,37 +111,52 @@ export const Table = ({ headings, rows }: TableProps) => {
: "" : ""
}`} }`}
onClick={() => heading.sortable && headingSort(heading.title)} onClick={() => heading.sortable && headingSort(heading.title)}
onKeyUp={() => heading.sortable && headingSort(heading.title)} onKeyUp={(e) => {
if (heading.sortable && (e.key === "Enter" || e.key === " ")) {
headingSort(heading.title);
}
}}
tabIndex={heading.sortable ? 0 : -1}
role="columnheader"
aria-sort={sortColumn === heading.title
? sortOrder === "asc" ? "ascending" : "descending"
: "none"}
> >
<div className="flex gap-2"> <div className="flex items-center gap-2">
{heading.title} {heading.title}
{sortColumn === heading.title && {heading.sortable && sortColumn === heading.title && (
(sortOrder === "asc" sortOrder === "asc"
? <ChevronUpIcon size={16} /> ? <ChevronUpIcon size={16} aria-hidden="true" />
: <ChevronDownIcon size={16} />)} : <ChevronDownIcon size={16} aria-hidden="true" />
)}
</div> </div>
</th> </th>
))} ))}
</tr> </tr>
</thead> </thead>
<tbody className="max-w-fit"> <tbody className="max-w-fit">
{sortedRows.map((row, index) => { {sortedRows.map((row) => {
// biome-ignore lint/suspicious/noArrayIndexKey: TODO: Once this table is sortable, this should get fixed. const firstCellKey =
(React.isValidElement(row[0]) && row[0].key !== null)
? String(row[0].key)
: null;
const rowKey = firstCellKey ?? Math.random().toString(); // Use random only as last resort
return ( return (
<tr <tr
key={index} key={rowKey}
className={`${ className={`
index % 2 bg-white dark:bg-white/10
? "bg-white dark:bg-white/2" odd:bg-slate-800/70 dark:even:bg-slate-900/70
: "bg-slate-50/50 dark:bg-slate-50/5" `}
} border-b-1 border-slate-200 dark:border-slate-900`}
> >
{row.map((item, index) => { {row.map((item, cellIndex) => {
return (index === 0 const cellKey = `${rowKey}_${cellIndex}`;
return cellIndex === 0
? ( ? (
<th <th
key={item.key ?? index} key={cellKey}
className="whitespace-nowrap py-2 text-sm text-text-secondary first:pl-2" className="whitespace-nowrap px-3 py-2 text-sm text-left text-text-secondary"
scope="row" scope="row"
> >
{item} {item}
@ -137,12 +164,12 @@ export const Table = ({ headings, rows }: TableProps) => {
) )
: ( : (
<td <td
key={item.key ?? index} key={cellKey}
className="whitespace-nowrap py-2 text-sm text-text-secondary first:pl-2" className="whitespace-nowrap px-3 py-2 text-sm text-text-secondary"
> >
{item} {item}
</td> </td>
)); );
})} })}
</tr> </tr>
); );

Loading…
Cancel
Save