Skip to content

Commit fb42e3a

Browse files
feat: create table component set with ShadCN
1 parent 62b5b3a commit fb42e3a

File tree

5 files changed

+469
-3
lines changed

5 files changed

+469
-3
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
"reading-time": "^1.5.0",
7979
"remark-gfm": "^3.0.1",
8080
"tailwind-merge": "^2.3.0",
81+
"tailwind-variants": "^0.2.1",
8182
"tailwindcss-animate": "^1.0.7",
8283
"usehooks-ts": "^3.1.0",
8384
"yaml-loader": "^0.8.0"
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import * as React from "react"
2+
import { Flex } from "@chakra-ui/react"
3+
import { Meta, StoryObj } from "@storybook/react"
4+
5+
import {
6+
MdxDemoData,
7+
MdxEnergyConsumpData,
8+
MdxTypesOfBridgesData,
9+
} from "./mockMdxData"
10+
import { Table as TableComponent } from "./Table"
11+
12+
const meta = {
13+
title: "Molecules / Display Content / ShadCN Tables",
14+
component: TableComponent,
15+
parameters: {
16+
// TODO: Remove this when this story file becomes the primary one
17+
chromatic: { disableSnapshot: true },
18+
},
19+
decorators: [
20+
(Story) => (
21+
<Flex flexDir="column" gap={16} maxW="container.md">
22+
<Story />
23+
</Flex>
24+
),
25+
],
26+
} satisfies Meta<typeof TableComponent>
27+
28+
export default meta
29+
30+
type Story = StoryObj<typeof meta>
31+
32+
export const Tables: Story = {
33+
args: {
34+
children: <MdxDemoData />,
35+
},
36+
render: (args) => (
37+
<>
38+
<TableComponent {...args} />
39+
<TableComponent {...args} variant="minimal" />
40+
<TableComponent {...args} variant="minimal-striped" />
41+
<TableComponent {...args} variant="simple-striped" />
42+
</>
43+
),
44+
}
45+
46+
export const MockDocContent: StoryObj = {
47+
render: () => (
48+
<>
49+
<TableComponent variant="simple">
50+
<MdxEnergyConsumpData />
51+
</TableComponent>
52+
<TableComponent>
53+
<MdxTypesOfBridgesData />
54+
</TableComponent>
55+
</>
56+
),
57+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import * as React from "react"
2+
import { createContext, useContext } from "react"
3+
import { tv, type VariantProps } from "tailwind-variants"
4+
5+
import { cn } from "@/lib/utils/cn"
6+
7+
/**
8+
* TODO: Currently, there are cell spacing issues with some table content.
9+
* Prefer `table-fixed` utility class in the future when content has been addressed
10+
* to provide equal cell widths.
11+
*/
12+
13+
const tableVariants = tv({
14+
slots: {
15+
table: "w-full",
16+
th: "text-start border-b-[1px] border-body text-body normal-case align-bottom p-4",
17+
tr: "not-[:last-of-type]:[&_th]:border-e-2 not-[:last-of-type]:[&_th]:border-e-background not-[:last-of-type]:[&_td]:border-e-2 not-[:last-of-type]:[&_td]:border-e-background",
18+
td: "p-4",
19+
tbody: "[&_tr]:align-top hover:[&_tr]:bg-background-highlight",
20+
// slot key with empty string to establish the key name for variants (TypeScript)
21+
thead: "",
22+
},
23+
variants: {
24+
variant: {
25+
simple: {
26+
thead: "bg-background-highlight",
27+
},
28+
"minimal-striped": {
29+
tbody: "even:[&_tr]:bg-background-highlight",
30+
},
31+
"simple-striped": {
32+
thead: "bg-background-highlight",
33+
tbody: "even:[&_tr]:bg-background-highlight",
34+
},
35+
minimal: {},
36+
},
37+
},
38+
defaultVariants: {
39+
variant: "simple",
40+
},
41+
})
42+
43+
type TableVariants = VariantProps<typeof tableVariants>
44+
45+
type TableVariantsReturnType = ReturnType<typeof tableVariants>
46+
47+
/**
48+
* For `TableCell` and `TableHead` only
49+
*
50+
* Applies the `align` prop in name for the `textAlign` CSS property
51+
* instead of the `align` attribute. (The `align` attribute is deprecated)
52+
*/
53+
type CellPropsWithAlign<C> = Omit<C, "align"> & {
54+
align?: React.CSSProperties["textAlign"]
55+
}
56+
57+
const TableStylesContext = createContext<{
58+
[P in keyof TableVariantsReturnType]: TableVariantsReturnType[P]
59+
}>(tableVariants())
60+
61+
const useTableStyles = () => useContext(TableStylesContext)
62+
63+
const Table = React.forwardRef<
64+
HTMLTableElement,
65+
React.HTMLAttributes<HTMLTableElement> & TableVariants
66+
>(({ className, variant, ...props }, ref) => {
67+
const tableVariantStyles = tableVariants({ variant })
68+
69+
return (
70+
<TableStylesContext.Provider value={tableVariantStyles}>
71+
<div className="relative w-full overflow-auto whitespace-normal">
72+
<table
73+
ref={ref}
74+
className={cn(tableVariantStyles.table(), className)}
75+
{...props}
76+
/>
77+
</div>
78+
</TableStylesContext.Provider>
79+
)
80+
})
81+
Table.displayName = "Table"
82+
83+
const TableHeader = React.forwardRef<
84+
HTMLTableSectionElement,
85+
React.HTMLAttributes<HTMLTableSectionElement>
86+
>(({ className, ...props }, ref) => {
87+
const { thead } = useTableStyles()
88+
89+
return <thead ref={ref} className={cn(thead(), className)} {...props} />
90+
})
91+
TableHeader.displayName = "TableHeader"
92+
93+
const TableBody = React.forwardRef<
94+
HTMLTableSectionElement,
95+
React.HTMLAttributes<HTMLTableSectionElement>
96+
>(({ className, ...props }, ref) => {
97+
const { tbody } = useTableStyles()
98+
return <tbody ref={ref} className={cn(tbody(), className)} {...props} />
99+
})
100+
TableBody.displayName = "TableBody"
101+
102+
const TableRow = React.forwardRef<
103+
HTMLTableRowElement,
104+
React.HTMLAttributes<HTMLTableRowElement>
105+
>(({ className, ...props }, ref) => {
106+
const { tr } = useTableStyles()
107+
return <tr ref={ref} className={cn(tr(), className)} {...props} />
108+
})
109+
TableRow.displayName = "TableRow"
110+
111+
const TableHead = React.forwardRef<
112+
HTMLTableCellElement,
113+
CellPropsWithAlign<React.ThHTMLAttributes<HTMLTableCellElement>>
114+
>(({ className, align, ...props }, ref) => {
115+
const { th } = useTableStyles()
116+
return (
117+
<th
118+
ref={ref}
119+
className={cn(th(), className)}
120+
style={{ textAlign: align }}
121+
{...props}
122+
/>
123+
)
124+
})
125+
TableHead.displayName = "TableHead"
126+
127+
const TableCell = React.forwardRef<
128+
HTMLTableCellElement,
129+
CellPropsWithAlign<React.TdHTMLAttributes<HTMLTableCellElement>>
130+
>(({ className, align, children, ...props }, ref) => {
131+
const { td } = useTableStyles()
132+
133+
return (
134+
<td
135+
ref={ref}
136+
className={cn(td(), className)}
137+
style={{ textAlign: align }}
138+
{...props}
139+
>
140+
{children}
141+
</td>
142+
)
143+
})
144+
TableCell.displayName = "TableCell"
145+
146+
const mdxTableComponents = {
147+
table: Table,
148+
td: ({ align, ...rest }) => <TableCell align={align} {...rest} />,
149+
th: ({ align, ...rest }) => <TableHead align={align} {...rest} />,
150+
tr: (props) => <TableRow {...props} />,
151+
tbody: (props) => <TableBody {...props} />,
152+
thead: (props) => <TableHeader {...props} />,
153+
}
154+
155+
export {
156+
mdxTableComponents,
157+
Table,
158+
TableBody,
159+
TableCell,
160+
TableHead,
161+
TableHeader,
162+
TableRow,
163+
}

0 commit comments

Comments
 (0)