11import { ChevronRightIcon } from "@heroicons/react/24/solid" ;
22import { Link } from "@remix-run/react" ;
3- import { ReactNode , forwardRef , useState } from "react" ;
3+ import React , { ReactNode , forwardRef , useState , useContext , createContext } from "react" ;
44import { cn } from "~/utils/cn" ;
55import { Popover , PopoverContent , PopoverVerticalEllipseTrigger } from "./Popover" ;
66import { InfoIconTooltip } from "./Tooltip" ;
77
8+ const variants = {
9+ bright : {
10+ header : "bg-background-bright" ,
11+ cell : "group-hover/table-row:bg-charcoal-750 group-has-[[tabindex='0']:focus]/table-row:bg-charcoal-750" ,
12+ stickyCell : "bg-background-bright group-hover/table-row:bg-charcoal-750" ,
13+ menuButton :
14+ "bg-background-bright group-hover/table-row:bg-charcoal-750 group-hover/table-row:ring-charcoal-600/70 group-has-[[tabindex='0']:focus]/table-row:bg-charcoal-750" ,
15+ menuButtonDivider : "group-hover/table-row:border-charcoal-600/70" ,
16+ rowSelected : "bg-charcoal-750 group-hover/table-row:bg-charcoal-750" ,
17+ } ,
18+ dimmed : {
19+ header : "bg-background-dimmed" ,
20+ cell : "group-hover/table-row:bg-charcoal-800 group-has-[[tabindex='0']:focus]/table-row:bg-background-bright" ,
21+ stickyCell : "group-hover/table-row:bg-charcoal-800" ,
22+ menuButton :
23+ "bg-background-dimmed group-hover/table-row:bg-charcoal-800 group-hover/table-row:ring-grid-bright group-has-[[tabindex='0']:focus]/table-row:bg-background-bright" ,
24+ menuButtonDivider : "group-hover/table-row:border-grid-dimmed" ,
25+ rowSelected : "bg-charcoal-750 group-hover/table-row:bg-charcoal-750" ,
26+ } ,
27+ } as const ;
28+
29+ export type TableVariant = keyof typeof variants ;
30+
831type TableProps = {
932 containerClassName ?: string ;
1033 className ?: string ;
1134 children : ReactNode ;
1235 fullWidth ?: boolean ;
1336} ;
1437
15- export const Table = forwardRef < HTMLTableElement , TableProps > (
16- ( { className, containerClassName, children, fullWidth } , ref ) => {
38+ // Add TableContext
39+ const TableContext = createContext < { variant : TableVariant } > ( { variant : "dimmed" } ) ;
40+
41+ export const Table = forwardRef < HTMLTableElement , TableProps & { variant ?: TableVariant } > (
42+ ( { className, containerClassName, children, fullWidth, variant = "dimmed" } , ref ) => {
1743 return (
18- < div
19- className = { cn (
20- "overflow-x-auto whitespace-nowrap border-t border-grid-bright scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600" ,
21- containerClassName ,
22- fullWidth && "w-full"
23- ) }
24- >
25- < table ref = { ref } className = { cn ( "w-full" , className ) } >
26- { children }
27- </ table >
28- </ div >
44+ < TableContext . Provider value = { { variant } } >
45+ < div
46+ className = { cn (
47+ "overflow-x-auto whitespace-nowrap border-t scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600" ,
48+ containerClassName ,
49+ fullWidth && "w-full"
50+ ) }
51+ >
52+ < table ref = { ref } className = { cn ( "w-full" , className ) } >
53+ { children }
54+ </ table >
55+ </ div >
56+ </ TableContext . Provider >
2957 ) ;
3058 }
3159) ;
@@ -37,11 +65,13 @@ type TableHeaderProps = {
3765
3866export const TableHeader = forwardRef < HTMLTableSectionElement , TableHeaderProps > (
3967 ( { className, children } , ref ) => {
68+ const { variant } = useContext ( TableContext ) ;
4069 return (
4170 < thead
4271 ref = { ref }
4372 className = { cn (
44- "sticky top-0 z-10 bg-background-dimmed after:absolute after:bottom-0 after:left-0 after:right-0 after:h-px after:bg-grid-bright" ,
73+ "sticky top-0 z-10 after:absolute after:bottom-0 after:left-0 after:right-0 after:h-px after:bg-grid-bright" ,
74+ variants [ variant ] . header ,
4575 className
4676 ) }
4777 >
@@ -75,13 +105,14 @@ type TableRowProps = {
75105
76106export const TableRow = forwardRef < HTMLTableRowElement , TableRowProps > (
77107 ( { className, disabled, isSelected, children } , ref ) => {
108+ const { variant } = useContext ( TableContext ) ;
78109 return (
79110 < tr
80111 ref = { ref }
81112 className = { cn (
82- "group/table-row relative w-full after:absolute after:bottom-0 after:left-3 after:right-0 after:h-px after:bg-grid-dimmed" ,
113+ "group/table-row relative w-full outline-none after:absolute after:bottom-0 after:left-3 after:right-0 after:h-px after:bg-grid-dimmed" ,
114+ isSelected && variants [ variant ] . rowSelected ,
83115 disabled && "opacity-50" ,
84- isSelected && isSelectedStyle ,
85116 className
86117 ) }
87118 >
@@ -94,7 +125,7 @@ export const TableRow = forwardRef<HTMLTableRowElement, TableRowProps>(
94125type TableCellBasicProps = {
95126 className ?: string ;
96127 alignment ?: "left" | "center" | "right" ;
97- children : ReactNode ;
128+ children ? : ReactNode ;
98129 colSpan ?: number ;
99130} ;
100131
@@ -125,6 +156,7 @@ export const TableHeaderCell = forwardRef<HTMLTableCellElement, TableHeaderCellP
125156 className
126157 ) }
127158 colSpan = { colSpan }
159+ tabIndex = { - 1 }
128160 >
129161 { hiddenLabel ? (
130162 < span className = "sr-only" > { children } </ span >
@@ -147,24 +179,12 @@ type TableCellProps = TableCellBasicProps & {
147179 hasAction ?: boolean ;
148180 isSticky ?: boolean ;
149181 actionClassName ?: string ;
150- rowHoverStyle ?: keyof typeof rowHoverStyles ;
182+ rowHoverStyle ?: string ;
151183 isSelected ?: boolean ;
184+ isTabbableCell ?: boolean ;
185+ children ?: ReactNode ;
152186} ;
153187
154- const rowHoverStyles = {
155- default :
156- "group-hover/table-row:bg-charcoal-800 group-hover/table-row:before:absolute group-hover/table-row:before:bg-charcoal-750 group-hover/table-row:before:top-[-1px] group-hover/table-row:before:left-0 group-hover/table-row:before:h-px group-hover/table-row:before:w-3 group-hover/table-row:after:absolute group-hover/table-row:after:bg-charcoal-750 group-hover/table-row:after:bottom-0 group-hover/table-row:after:left-0 group-hover/table-row:after:h-px group-hover/table-row:after:w-3" ,
157- dimmed :
158- "group-hover/table-row:bg-charcoal-850 group-hover/table-row:before:absolute group-hover/table-row:before:bg-charcoal-800 group-hover/table-row:before:top-[-1px] group-hover/table-row:before:left-0 group-hover/table-row:before:h-px group-hover/table-row:before:w-3 group-hover/table-row:after:absolute group-hover/table-row:after:bg-charcoal-800 group-hover/table-row:after:bottom-0 group-hover/table-row:after:left-0 group-hover/table-row:after:h-px group-hover/table-row:after:w-3" ,
159- bright :
160- "group-hover/table-row:bg-charcoal-750 group-hover/table-row:before:absolute group-hover/table-row:before:bg-charcoal-700 group-hover/table-row:before:top-[-1px] group-hover/table-row:before:left-0 group-hover/table-row:before:h-px group-hover/table-row:before:w-3 group-hover/table-row:after:absolute group-hover/table-row:after:bg-charcoal-700 group-hover/table-row:after:bottom-0 group-hover/table-row:after:left-0 group-hover/table-row:after:h-px group-hover/table-row:after:w-3" ,
161- } ;
162-
163- const stickyStyles =
164- "sticky right-0 bg-background-dimmed group-hover/table-row:bg-charcoal-750 w-[--sticky-width] [&:has(.group-hover\\/table-row\\:block)]:w-auto" ;
165-
166- const isSelectedStyle = "bg-charcoal-750 group-hover:bg-charcoal-750" ;
167-
168188export const TableCell = forwardRef < HTMLTableCellElement , TableCellProps > (
169189 (
170190 {
@@ -177,8 +197,8 @@ export const TableCell = forwardRef<HTMLTableCellElement, TableCellProps>(
177197 onClick,
178198 hasAction = false ,
179199 isSticky = false ,
180- rowHoverStyle = "default" ,
181200 isSelected,
201+ isTabbableCell = false ,
182202 } ,
183203 ref
184204 ) => {
@@ -193,34 +213,47 @@ export const TableCell = forwardRef<HTMLTableCellElement, TableCellProps>(
193213 }
194214
195215 const flexClasses = cn (
196- "flex w-full whitespace-nowrap px-3 py-3 text-xs text-text-dimmed" ,
216+ "flex w-full whitespace-nowrap px-3 py-3 items-center text-xs text-text-dimmed" ,
197217 alignment === "left"
198218 ? "justify-start text-left"
199219 : alignment === "center"
200220 ? "justify-center text-center"
201221 : "justify-end text-right"
202222 ) ;
223+ const { variant } = useContext ( TableContext ) ;
203224
204225 return (
205226 < td
206227 ref = { ref }
207228 className = { cn (
208- "text-xs text-charcoal-400" ,
209- to || onClick || hasAction ? "cursor-pointer" : "px-3 py-3 align-middle" ,
229+ "text-xs text-charcoal-400 has-[[tabindex='0']:focus]:before:absolute has-[[tabindex='0']:focus]:before:-top-px has-[[tabindex='0']:focus]:before:left-0 has-[[tabindex='0']:focus]:before:h-px has-[[tabindex='0']:focus]:before:w-3 has-[[tabindex='0']:focus]:before:bg-grid-dimmed has-[[tabindex='0']:focus]:after:absolute has-[[tabindex='0']:focus]:after:bottom-0 has-[[tabindex='0']:focus]:after:left-0 has-[[tabindex='0']:focus]:after:right-0 has-[[tabindex='0']:focus]:after:h-px has-[[tabindex='0']:focus]:after:bg-grid-dimmed" ,
230+ variants [ variant ] . cell ,
231+ to || onClick || hasAction ? "cursor-pointer" : "cursor-default px-3 py-3 align-middle" ,
210232 ! to && ! onClick && alignmentClassName ,
211- isSticky && stickyStyles ,
212- isSelected && isSelectedStyle ,
213- ! isSelected && rowHoverStyles [ rowHoverStyle ] ,
233+ isSticky &&
234+ "[&:has(.group-hover/table-row:block)]:w-auto sticky right-0 bg-background-dimmed" ,
235+ isSticky && variants [ variant ] . stickyCell ,
236+ isSelected && variants [ variant ] . rowSelected ,
237+ ! isSelected &&
238+ "group-hover/table-row:before:absolute group-hover/table-row:before:left-0 group-hover/table-row:before:top-[-1px] group-hover/table-row:before:h-px group-hover/table-row:before:w-3 group-hover/table-row:before:bg-charcoal-750 group-hover/table-row:after:absolute group-hover/table-row:after:bottom-0 group-hover/table-row:after:left-0 group-hover/table-row:after:h-px group-hover/table-row:after:w-3 group-hover/table-row:after:bg-charcoal-750 group-focus-visible/table-row:bg-background-bright" ,
214239 className
215240 ) }
216241 colSpan = { colSpan }
217242 >
218243 { to ? (
219- < Link to = { to } className = { cn ( "focus-custom" , flexClasses , actionClassName ) } >
244+ < Link
245+ to = { to }
246+ className = { cn ( "cursor-pointer focus:outline-none" , flexClasses , actionClassName ) }
247+ tabIndex = { isTabbableCell ? 0 : - 1 }
248+ >
220249 { children }
221250 </ Link >
222251 ) : onClick ? (
223- < button onClick = { onClick } className = { cn ( "focus-custom" , flexClasses , actionClassName ) } >
252+ < button
253+ onClick = { onClick }
254+ className = { cn ( "cursor-pointer focus:outline-none" , flexClasses , actionClassName ) }
255+ tabIndex = { isTabbableCell ? 0 : - 1 }
256+ >
224257 { children }
225258 </ button >
226259 ) : (
@@ -258,7 +291,7 @@ export const TableCellChevron = forwardRef<
258291
259292export const TableCellMenu = forwardRef <
260293 HTMLTableCellElement ,
261- {
294+ TableCellProps & {
262295 className ?: string ;
263296 isSticky ?: boolean ;
264297 onClick ?: ( event : React . MouseEvent < HTMLButtonElement , MouseEvent > ) => void ;
@@ -283,6 +316,7 @@ export const TableCellMenu = forwardRef<
283316 ref
284317 ) => {
285318 const [ isOpen , setIsOpen ] = useState ( false ) ;
319+ const { variant } = useContext ( TableContext ) ;
286320
287321 return (
288322 < TableCell
@@ -297,15 +331,18 @@ export const TableCellMenu = forwardRef<
297331 < div className = "relative h-full p-1" >
298332 < div
299333 className = { cn (
300- "absolute right-0 top-1/2 mr-1 flex -translate-y-1/2 items-center justify-end gap-0.5 rounded-[0.25rem] bg-background-dimmed p-0.5 group-hover/table-row:bg-background-bright group-hover/table-row:ring-1 group-hover/table-row:ring-grid-bright" ,
301- isSelected && isSelectedStyle ,
302- isSelected &&
303- "group-hover/table-row:bg-charcoal-750 group-hover/table-row:ring-charcoal-600/50"
334+ "absolute right-0 top-1/2 mr-1 flex -translate-y-1/2 items-center justify-end gap-0.5 rounded-[0.25rem] p-0.5 group-hover/table-row:ring-1" ,
335+ variants [ variant ] . menuButton
304336 ) }
305337 >
306338 { /* Hidden buttons that show on hover */ }
307339 { hiddenButtons && (
308- < div className = "hidden pr-0.5 group-hover/table-row:block group-hover/table-row:border-r group-hover/table-row:border-grid-dimmed" >
340+ < div
341+ className = { cn (
342+ "hidden pr-0.5 group-hover/table-row:block group-hover/table-row:border-r" ,
343+ variants [ variant ] . menuButtonDivider
344+ ) }
345+ >
309346 { hiddenButtons }
310347 </ div >
311348 ) }
0 commit comments