index.tsx80 lines · main
| 1 | 'use client' |
| 2 | |
| 3 | import * as React from 'react' |
| 4 | |
| 5 | import { cn } from '../../lib/utils/cn' |
| 6 | import { useHorizontalScroll } from '../hooks/use-horizontal-scroll' |
| 7 | |
| 8 | interface 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 | |
| 21 | const 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 | |
| 78 | ShadowScrollArea.displayName = 'ShadowScrollArea' |
| 79 | |
| 80 | export { ShadowScrollArea } |