index.tsx80 lines · main
1'use client'
2
3import * as React from 'react'
4
5import { cn } from '../../lib/utils/cn'
6import { useHorizontalScroll } from '../hooks/use-horizontal-scroll'
7
8interface ShadowScrollAreaProps extends React.HTMLAttributes<HTMLDivElement> {
9 children: React.ReactNode
10 containerClassName?: string
11 stickyLastColumn?: boolean
12 outerContainerRef?: React.Ref<HTMLDivElement>
13}
14
15/**
16 * [Joshen] Leaving feedback here to address in the future
17 We should pull out all the sticky column logic, shift them to Table component where we can declare
18 sticky columns via a prop. ShadowScrollArea here should purely handle the shadow styling
19 */
20
21const ShadowScrollArea = React.forwardRef<HTMLDivElement, ShadowScrollAreaProps>(
22 (
23 { className, containerClassName, children, stickyLastColumn, outerContainerRef, ...props },
24 _ref
25 ) => {
26 const containerRef = React.useRef<HTMLDivElement | null>(null)
27 const { hasHorizontalScroll, canScrollLeft, canScrollRight } = useHorizontalScroll(containerRef)
28
29 const stickyColumnShadow = cn(
30 '[&_td:last-child]:before:absolute [&_td:last-child]:before:top-0 [&_td:last-child]:before:-left-6',
31 '[&_td:last-child]:before:bottom-0 [&_td:last-child]:before:w-6 [&_td:last-child]:before:bg-linear-to-l',
32 '[&_td:last-child]:before:from-black/5 dark:[&_td:last-child]:before:from-black/20 [&_td:last-child]:before:to-transparent',
33 '[&_td:last-child]:before:opacity-0 [&_td:last-child]:before:transition-all [&_td:last-child]:before:duration-400',
34 '[&_td:last-child]:before:easing-[0.24, 0.25, 0.05, 1] [&_td:last-child]:before:z-39',
35 '[&_th:last-child]:before:absolute [&_th:last-child]:before:top-0 [&_th:last-child]:before:-left-6',
36 '[&_th:last-child]:before:bottom-0 [&_th:last-child]:before:w-6 [&_th:last-child]:before:bg-linear-to-l',
37 '[&_th:last-child]:before:from-black/5 dark:[&_th:last-child]:before:from-black/20 [&_th:last-child]:before:to-transparent',
38 '[&_th:last-child]:before:opacity-0 [&_th:last-child]:before:transition-all [&_th:last-child]:before:duration-400',
39 '[&_th:last-child]:before:easing-[0.24, 0.25, 0.05, 1] [&_th:last-child]:before:z-39'
40 )
41
42 return (
43 <div ref={outerContainerRef} className={cn(containerClassName, 'relative')}>
44 <div
45 className={cn(
46 'absolute inset-0 pointer-events-none z-38',
47 'before:absolute before:top-0 before:right-0 before:bottom-0 before:w-6 before:bg-linear-to-l before:from-black/5 dark:before:from-black/20 before:to-transparent before:opacity-0 before:transition-all before:duration-400 before:easing-[0.24, 0.25, 0.05, 1]',
48 'after:absolute after:top-0 after:left-0 after:bottom-0 after:w-6 after:bg-linear-to-r after:from-black/5 dark:after:from-black/20 after:to-transparent after:opacity-0 after:transition-all after:duration-400 after:easing-[0.24, 0.25, 0.05, 1]',
49 hasHorizontalScroll && 'hover:before:opacity-100 hover:after:opacity-100',
50 canScrollRight && 'before:opacity-100',
51 canScrollLeft && 'after:opacity-100'
52 )}
53 />
54 <div
55 ref={containerRef}
56 className={cn(
57 'w-full overflow-auto',
58 stickyLastColumn && [
59 '[&_tr>*:last-child]:sticky [&_tr>*:last-child]:z-38 [&_tr>*:last-child]:right-0',
60 '[&_tr:hover>*:last-child]:bg-transparent',
61 '[&_th>*:last-child]:bg-surface-100',
62 stickyColumnShadow,
63 ],
64 hasHorizontalScroll && '[&_tr:hover>td:last-child]:!bg-surface-200',
65 canScrollRight &&
66 '[&_td]:before:opacity-100 [&_tr>*:last-child]:before:opacity-100 [&_th:last-child]:before:opacity-100',
67 className
68 )}
69 {...props}
70 >
71 {children}
72 </div>
73 </div>
74 )
75 }
76)
77
78ShadowScrollArea.displayName = 'ShadowScrollArea'
79
80export { ShadowScrollArea }