1 | import React, { forwardRef, memo, type ReactNode, type CSSProperties } from "react";
|
2 | import { assert } from "tsafe/assert";
|
3 | import type { Equals } from "tsafe";
|
4 | import { fr } from "./fr";
|
5 | import { cx } from "./tools/cx";
|
6 | import { symToStr } from "tsafe/symToStr";
|
7 | import type { FrClassName } from "./fr/generatedFromCss/classNames";
|
8 | import { useAnalyticsId } from "./tools/useAnalyticsId";
|
9 |
|
10 | export type TableProps = {
|
11 | id?: string;
|
12 | data: ReactNode[][];
|
13 | className?: string;
|
14 | caption?: ReactNode;
|
15 | headers?: ReactNode[];
|
16 |
|
17 | fixed?: boolean;
|
18 |
|
19 | noScroll?: boolean;
|
20 |
|
21 | bordered?: boolean;
|
22 |
|
23 | noCaption?: boolean;
|
24 |
|
25 | bottomCaption?: boolean;
|
26 | style?: CSSProperties;
|
27 | colorVariant?: TableProps.ColorVariant;
|
28 | };
|
29 |
|
30 | export namespace TableProps {
|
31 | type ExtractColorVariant<FrClassName> = FrClassName extends `fr-table--${infer AccentColor}`
|
32 | ? Exclude<
|
33 | AccentColor,
|
34 | "no-scroll" | "no-caption" | "caption-bottom" | "layout-fixed" | "bordered"
|
35 | >
|
36 | : never;
|
37 |
|
38 | export type ColorVariant = ExtractColorVariant<FrClassName>;
|
39 | }
|
40 |
|
41 |
|
42 | export const Table = memo(
|
43 | forwardRef<HTMLDivElement, TableProps>((props, ref) => {
|
44 | const {
|
45 | id: id_props,
|
46 | data,
|
47 | headers,
|
48 | caption,
|
49 | bordered = false,
|
50 | noScroll = false,
|
51 | fixed = false,
|
52 | noCaption = false,
|
53 | bottomCaption = false,
|
54 | colorVariant,
|
55 | className,
|
56 | style,
|
57 | ...rest
|
58 | } = props;
|
59 |
|
60 | assert<Equals<keyof typeof rest, never>>();
|
61 |
|
62 | const id = useAnalyticsId({
|
63 | "defaultIdPrefix": "fr-table",
|
64 | "explicitlyProvidedId": id_props
|
65 | });
|
66 |
|
67 | return (
|
68 | <div
|
69 | id={id}
|
70 | ref={ref}
|
71 | style={style}
|
72 | className={cx(
|
73 | fr.cx(
|
74 | "fr-table",
|
75 | {
|
76 | "fr-table--bordered": bordered,
|
77 | "fr-table--no-scroll": noScroll,
|
78 | "fr-table--layout-fixed": fixed,
|
79 | "fr-table--no-caption": noCaption,
|
80 | "fr-table--caption-bottom": bottomCaption
|
81 | },
|
82 | colorVariant !== undefined && `fr-table--${colorVariant}`
|
83 | ),
|
84 | className
|
85 | )}
|
86 | >
|
87 | <table>
|
88 | {caption !== undefined && <caption>{caption}</caption>}
|
89 | {headers !== undefined && (
|
90 | <thead>
|
91 | <tr>
|
92 | {headers.map((header, i) => (
|
93 | <th key={i} scope="col">
|
94 | {header}
|
95 | </th>
|
96 | ))}
|
97 | </tr>
|
98 | </thead>
|
99 | )}
|
100 | <tbody>
|
101 | {data.map((row, i) => (
|
102 | <tr key={i}>
|
103 | {row.map((col, j) => (
|
104 | <td key={j}>{col}</td>
|
105 | ))}
|
106 | </tr>
|
107 | ))}
|
108 | </tbody>
|
109 | </table>
|
110 | </div>
|
111 | );
|
112 | })
|
113 | );
|
114 |
|
115 | Table.displayName = symToStr({ Table });
|
116 |
|
117 | export default Table;
|
118 |
|
\ | No newline at end of file |