Perplexica/ui/components/Markdown/renderer/table.tsx

150 lines
4.2 KiB
TypeScript
Raw Normal View History

2024-07-10 16:17:23 +08:00
import { css, cx } from "@emotion/css";
import { isEqual } from "@guanghechen/equal";
import type { Table } from "@yozora/ast";
import React from "react";
import { astClasses } from "../context";
import { NodesRenderer } from "../NodesRenderer";
/**
* Render yozora `table`, `tableRow` and `tableCell`.
*
* @see https://www.npmjs.com/package/@yozora/ast#table
* @see https://www.npmjs.com/package/@yozora/ast#tablecell
* @see https://www.npmjs.com/package/@yozora/ast#tablerow
* @see https://www.npmjs.com/package/@yozora/tokenizer-table
* @see https://www.npmjs.com/package/@yozora/tokenizer-table-row
* @see https://www.npmjs.com/package/@yozora/tokenizer-table-cell
*/
export class TableRenderer extends React.Component<Table> {
2024-07-10 16:34:28 +08:00
public override shouldComponentUpdate(nextProperties: Readonly<Table>): boolean {
const properties = this.props;
return (
!isEqual(properties.columns, nextProperties.columns) || !isEqual(properties.children, nextProperties.children)
);
2024-07-10 16:17:23 +08:00
}
public override render(): React.ReactElement {
const { columns, children: rows } = this.props;
const aligns = columns.map(col => col.align ?? undefined);
const [ths, ...tds] = rows.map(row =>
2024-07-10 16:34:28 +08:00
row.children.map((cell, index) => <NodesRenderer key={index} nodes={cell.children} />),
2024-07-10 16:17:23 +08:00
);
return (
<table className={cls}>
<thead>
<tr>
2024-07-10 16:34:28 +08:00
{ths.map((children, index) => (
<Th key={index} align={aligns[index]}>
2024-07-10 16:17:23 +08:00
{children}
</Th>
))}
</tr>
</thead>
<tbody>
{tds.map((row, rowIndex) => (
<tr key={rowIndex}>
2024-07-10 16:34:28 +08:00
{row.map((children, index) => (
<td key={index} align={aligns[index]}>
2024-07-10 16:17:23 +08:00
{children}
</td>
))}
</tr>
))}
</tbody>
</table>
);
}
}
2024-07-10 16:34:28 +08:00
interface IThProperties {
2024-07-10 16:17:23 +08:00
align: "left" | "center" | "right" | undefined;
children: React.ReactNode;
}
2024-07-10 16:34:28 +08:00
class Th extends React.Component<IThProperties> {
2024-07-10 16:17:23 +08:00
protected readonly ref: React.RefObject<HTMLTableCellElement>;
2024-07-10 16:34:28 +08:00
constructor(properties: IThProperties) {
super(properties);
2024-07-10 16:17:23 +08:00
this.ref = { current: null };
}
2024-07-10 16:34:28 +08:00
public override shouldComponentUpdate(nextProperties: Readonly<IThProperties>): boolean {
const properties = this.props;
return properties.align !== nextProperties.align || properties.children !== nextProperties.children;
2024-07-10 16:17:23 +08:00
}
public override render(): React.ReactElement {
const { align, children } = this.props;
return (
<th ref={this.ref} align={align}>
{children}
</th>
);
}
public override componentDidMount(): void {
const th = this.ref.current;
if (th) {
2024-07-10 16:34:28 +08:00
// eslint-disable-next-line unicorn/prefer-dom-node-text-content
2024-07-10 16:17:23 +08:00
th.setAttribute("title", th.innerText);
}
}
public override componentDidUpdate(): void {
const th = this.ref.current;
if (th) {
2024-07-10 16:34:28 +08:00
// eslint-disable-next-line unicorn/prefer-dom-node-text-content
2024-07-10 16:17:23 +08:00
th.setAttribute("title", th.innerText);
}
}
}
const cls: string = cx(
astClasses.table,
css({
display: "block",
overflow: "auto",
width: "max-content",
maxWidth: "100%",
padding: 0,
borderCollapse: "collapse",
borderRadius: "6px",
borderSpacing: "0px",
border: "1px solid var(--colorBorderTable)",
margin: "0 auto 1.25em",
lineHeight: "1.6",
"> thead": {
backgroundColor: "var(--colorBgTableHead)",
borderBottom: "1px solid #f0f0f0",
th: {
padding: "0.5rem 1rem",
borderLeft: "1px solid var(--colorBorderTable)",
wordBreak: "normal",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
"&:first-child": {
borderLeft: "none",
},
},
},
"> tbody": {
tr: {
borderTop: "1px solid var(--colorBorderTable)",
backgroundColor: "var(--colorBgTableOddRow)",
},
"tr:nth-child(2n)": {
backgroundColor: "var(--colorBgTableEvenRow)",
},
td: {
padding: "0.5rem 1rem",
borderLeft: "1px solid var(--colorBorderTable)",
"&:first-child": {
borderLeft: "none",
},
},
},
}),
);