mirror of
https://github.com/supabase/supabase.git
synced 2026-10-10 03:45:06 +03:00
fix(ui): opaque sticky table column hover (#47668)
## Problem
On tables with a sticky last column (Database Extensions, OAuth Apps,
Custom Auth Providers), hovering a row lets scrolled cell content show
through the sticky column.
Root cause: row hover uses `bg-surface-200`, which maps to `--muted`
(alpha). That works for normal cells (nothing behind them) but sticky
cells sit above horizontally scrolled content, so the tint is
see-through.
## Solution
In `ShadowScrollArea` when `stickyLastColumn` is set:
- Keep an opaque `bg-surface-100` base on sticky cells
- Composite the muted hover tint via `background-image` so hover matches
`TableRow` without becoming transparent
Also:
- Extensions: use `Table containerProps={{ stickyLastColumn: true }}`
instead of nesting a second `ShadowScrollArea` (same pattern as OAuth /
Custom Auth)
- OAuth Apps and Custom Auth Providers: drop redundant per-cell
`bg-surface-100` / `hover:bg-surface-200` so sticky hover comes only
from `ShadowScrollArea`
| Before | After |
| --- | --- |
| <img width="1042" height="98" alt="27097"
src="https://github.com/user-attachments/assets/204a7dc4-d5ac-40b7-87fb-fdb03982eaad"
/> | <img width="1084" height="101" alt="94456"
src="https://github.com/user-attachments/assets/c6db3607-6bbe-4488-a0a5-81f5a0436d66"
/> |
## Review instructions
1. Open Database Extensions. Narrow the viewport or scroll the table
horizontally so the Enabled column is sticky.
2. Hover a row while Links or description content sits under the Enabled
column. The sticky cell should stay opaque and match the row hover tint
(light and dark).
3. Optionally repeat on Auth → OAuth Apps and Auth → Custom Providers
(actions column).
Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com>
This commit is contained in:
1 parent
a76b861b50
commit
692b14d47b
5 files changed
+48
-52
No files matched your search
+1
-1
@@ -468,7 +468,7 @@ export const CustomAuthProvidersList = () => {
|
||||
{provider.enabled ? 'Enabled' : 'Disabled'}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="max-w-20 bg-surface-100 @[944px]:hover:bg-surface-200 px-6">
|
||||
<TableCell className="max-w-20 relative px-6">
|
||||
<div className="absolute top-0 right-0 left-0 bottom-0 flex items-center justify-center border-l @[944px]:border-l-0">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
||||
@@ -403,7 +403,7 @@ export const OAuthAppsList = () => {
|
||||
labelFormat="D MMM, YYYY"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className="max-w-20 bg-surface-100 @[944px]:hover:bg-surface-200 px-6">
|
||||
<TableCell className="max-w-20 relative px-6">
|
||||
<div className="absolute top-0 right-0 left-0 bottom-0 flex items-center justify-center border-l @[944px]:border-l-0">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
||||
@@ -149,10 +149,10 @@ export const ExtensionRow = ({ extension }: ExtensionRowProps) => {
|
||||
</TableCell>
|
||||
|
||||
{/*
|
||||
[Joshen] The div child here and all these classes is to properly add a left border
|
||||
to make the sticky column more distinct
|
||||
Left border on the absolute child makes the sticky column more distinct.
|
||||
Sticky positioning/background come from ShadowScrollArea's stickyLastColumn.
|
||||
*/}
|
||||
<TableCell className="w-20 sticky bg-surface-100 right-0 relative">
|
||||
<TableCell className="w-20 relative">
|
||||
<div className="absolute top-0 right-0 left-0 bottom-0 flex items-center justify-center border-l">
|
||||
{isDisabling ? (
|
||||
<Loader2 className="animate-spin" size={16} />
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
InputGroup,
|
||||
InputGroupAddon,
|
||||
InputGroupInput,
|
||||
ShadowScrollArea,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
@@ -107,51 +106,43 @@ export const Extensions = () => {
|
||||
<GenericSkeletonLoader />
|
||||
) : (
|
||||
<Card>
|
||||
<ShadowScrollArea stickyLastColumn>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<Table containerProps={{ stickyLastColumn: true }}>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead key="name">Name</TableHead>
|
||||
<TableHead key="version" className="w-28">
|
||||
Version
|
||||
</TableHead>
|
||||
<TableHead key="schema">Schema</TableHead>
|
||||
<TableHead key="description" className="min-w-80">
|
||||
Description
|
||||
</TableHead>
|
||||
<TableHead key="used-by">Used by</TableHead>
|
||||
<TableHead key="links">Links</TableHead>
|
||||
<TableHead key="enabled" className="px-0">
|
||||
<div className="bg-200! px-4 w-full h-full flex items-center border-l">
|
||||
Enabled
|
||||
</div>
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{[...enabledExtensions, ...disabledExtensions].map((extension) => (
|
||||
<ExtensionRow key={extension.name} extension={extension} />
|
||||
))}
|
||||
{extensions.length === 0 && (
|
||||
<TableRow>
|
||||
<TableHead key="name">Name</TableHead>
|
||||
<TableHead key="version" className="w-28">
|
||||
Version
|
||||
</TableHead>
|
||||
<TableHead key="schema">Schema</TableHead>
|
||||
<TableHead key="description" className="min-w-80">
|
||||
Description
|
||||
</TableHead>
|
||||
<TableHead key="used-by">Used by</TableHead>
|
||||
<TableHead key="links">Links</TableHead>
|
||||
{/*
|
||||
[Joshen] All these classes are just to make the last column sticky
|
||||
I reckon we can pull these out into the Table component where we can declare
|
||||
sticky columns via props, but we can do that if we start to have more tables
|
||||
in the dashboard with sticky columns
|
||||
*/}
|
||||
<TableHead key="enabled" className="px-0">
|
||||
<div className="bg-200! px-4 w-full h-full flex items-center border-l">
|
||||
Enabled
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableCell colSpan={7}>
|
||||
<NoSearchResults
|
||||
className="border-none p-0! bg-transparent"
|
||||
searchString={filterString}
|
||||
onResetFilter={() => setFilterString('')}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{[...enabledExtensions, ...disabledExtensions].map((extension) => (
|
||||
<ExtensionRow key={extension.name} extension={extension} />
|
||||
))}
|
||||
{extensions.length === 0 && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={7}>
|
||||
<NoSearchResults
|
||||
className="border-none p-0! bg-transparent"
|
||||
searchString={filterString}
|
||||
onResetFilter={() => setFilterString('')}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</ShadowScrollArea>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -57,10 +57,15 @@ const ShadowScrollArea = React.forwardRef<HTMLDivElement, ShadowScrollAreaProps>
|
||||
'w-full overflow-auto',
|
||||
stickyLastColumn && [
|
||||
'[&_tr>*:last-child]:sticky [&_tr>*:last-child]:z-38 [&_tr>*:last-child]:right-0',
|
||||
'[&_tr:hover>*:last-child]:bg-transparent',
|
||||
'[&_th>*:last-child]:bg-surface-100',
|
||||
// Sticky cells need an opaque base. `bg-surface-200` / `--muted` is alpha-based,
|
||||
// so using it alone lets scrolled row content show through on hover. Keep the
|
||||
// opaque surface-100 base and composite muted via background-image so hover
|
||||
// matches TableRow without becoming transparent.
|
||||
'[&_td:last-child]:bg-surface-100',
|
||||
'[&_tr:hover>td:last-child]:!bg-surface-100',
|
||||
'[&_tr:hover>td:last-child]:![background-image:linear-gradient(var(--background-surface-200),var(--background-surface-200))]',
|
||||
'[&_th:last-child]:bg-200',
|
||||
stickyColumnShadow,
|
||||
hasHorizontalScroll && '[&_tr:hover>td:last-child]:!bg-surface-200',
|
||||
],
|
||||
canScrollRight &&
|
||||
'[&_td]:before:opacity-100 [&_tr>*:last-child]:before:opacity-100 [&_th:last-child]:before:opacity-100',
|
||||
|
||||
Reference in new issue
Block a user