Skip to content

feat(table): add row detail sidebar on row click#329

Closed
ifeelBALANCED wants to merge 25 commits intomainfrom
feat/table-row-detail-sidebar
Closed

feat(table): add row detail sidebar on row click#329
ifeelBALANCED wants to merge 25 commits intomainfrom
feat/table-row-detail-sidebar

Conversation

@ifeelBALANCED
Copy link
Copy Markdown
Collaborator

@ifeelBALANCED ifeelBALANCED commented Feb 6, 2026

Description of Changes

  • Row click opens a right-hand sidebar that lists all row fields in a single column.
  • Column names are used as labels (with type in parentheses when it differs).
  • Null and empty string values are shown as “null” and “empty” in muted italic.
  • Added optional onRowClick in the table package and detailRowIndex in the table page store.
  • added pk,fk,unique key, maxLength, nullable, enums identifiers
  • added a collapsable size extend and close by dragabble
  • Why: To let users inspect every field of a row in one place without relying on horizontal scrolling or hidden columns.

Closes #60

Checklist

  • My changes are scoped and focused
  • I have tested the code locally

Screen Recording

Screen.Recording.2026-02-07.at.11.mp4

@railway-app
Copy link
Copy Markdown

railway-app Bot commented Feb 6, 2026

This PR was not deployed automatically as @ifeelBALANCED does not have access to the Railway project.

In order to get automatic PR deploys, please add @ifeelBALANCED to your workspace on Railway.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds row-click support to the shared table package and uses it in the desktop table page to open a right-hand “row details” sidebar for inspecting all fields of the clicked row.

Changes:

  • Extend @conar/table context/provider with optional onRowClick and wire row click handling in TableBody.
  • Add detailRowIndex to the table page store and reset logic.
  • Introduce RowDetailSidebar UI and render it alongside the table when a row is selected.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/table/src/table-context.ts Adds optional onRowClick callback to table context type.
packages/table/src/provider.tsx Accepts onRowClick prop and exposes it via context.
packages/table/src/body.tsx Hooks row click to call onRowClick and updates row styling/role.
apps/desktop/src/routes/_protected/database/$id/table/index.tsx Resets detailRowIndex when table/schema changes.
apps/desktop/src/routes/_protected/database/$id/table/-store.ts Adds detailRowIndex to store state and excludes it from persistence.
apps/desktop/src/routes/_protected/database/$id/table/-components/table/table.tsx Toggles detailRowIndex on row click and lays out table + sidebar.
apps/desktop/src/routes/_protected/database/$id/table/-components/table/row-detail-sidebar.tsx New sidebar component rendering column/value pairs for a row.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/table/src/body.tsx
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +5 to +6
import { RiCloseLine, RiSidebarFoldLine, RiSideBarLine } from '@remixicon/react'
import { getEditableValue } from '~/entities/connection/components/table/utils'
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RiSideBarLine looks like a mis-capitalized Remix Icon export (the library’s naming is typically RiSidebar*). If this named export doesn’t exist, the build will fail. Please verify the correct icon component name (likely RiSidebarLine) and update the import/usage accordingly.

Copilot uses AI. Check for mistakes.
Comment on lines +142 to +149
{column.maxLength && (
<span className="ml-1 text-primary">
(MAX
{' '}
{column.maxLength}
)
</span>
)}
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

column.maxLength && ... will skip rendering when maxLength is 0 (since it’s a falsy value) even though 0 is a valid number per the type (number | null). Prefer an explicit null/undefined check (maxLength != null) to match the declared type and avoid edge-case omissions.

Copilot uses AI. Check for mistakes.
…nts/table/table.tsx

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +388 to +416
<Panel
panelRef={rowDetailPanelRef}
defaultSize="25%"
minSize="5%"
maxSize="50%"
collapsible
collapsedSize={0}
onResize={() => {
const collapsed = rowDetailPanelRef.current?.isCollapsed() ?? false
setRowDetailCollapsed(collapsed)
if (collapsed)
store.setState(state => ({ ...state, detailRowIndex: null } satisfies typeof state))
}}
className="flex flex-col overflow-hidden border-l border-border bg-background shrink-0"
data-slot="resizable-panel"
>
<RowDetailSidebar
row={rows[detailRowIndex]!}
columns={columns}
onClose={() =>
store.setState(state => ({
...state,
detailRowIndex: null,
} satisfies typeof state))}
onExpand={() => rowDetailPanelRef.current?.expand()}
onCollapse={() => rowDetailPanelRef.current?.collapse()}
isCollapsed={rowDetailCollapsed}
/>
</Panel>
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code uses Panel imported directly from react-resizable-panels library instead of using ResizablePanel from the project's UI component library (@conar/ui/components/resizable). This is inconsistent with how other resizable panels are used throughout the codebase. All other files use ResizablePanel from the UI library wrapper. Using the raw Panel component bypasses any custom styling or behavior that the wrapper provides.

Copilot uses AI. Check for mistakes.
import { useStore } from '@tanstack/react-store'
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Panel, useDefaultLayout } from 'react-resizable-panels'
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When using the Panel component directly from react-resizable-panels, the import of Panel at line 11 should be removed since the convention is to use ResizablePanel from the UI library. However, if Panel needs to be used for access to imperative methods via panelRef, then the type import at line 2 (PanelImperativeHandle) should remain, but the component import should be changed to use ResizablePanel with a ref.

Suggested change
import { Panel, useDefaultLayout } from 'react-resizable-panels'
import { useDefaultLayout } from 'react-resizable-panels'

Copilot uses AI. Check for mistakes.
Comment on lines +56 to +94
return (
<aside
className={cn(
'flex min-w-0 w-full shrink-0 flex-col border-l border-border bg-background',
className,
)}
aria-label="Row details"
>
<div className="
flex shrink-0 items-center justify-between border-b border-border px-3
py-2
"
>
<span className="text-sm font-medium text-muted-foreground">
Row details
</span>
<div className="flex items-center gap-1">
{onCollapse && (
<Button
variant="ghost"
size="icon"
className="size-7"
onClick={onCollapse}
aria-label="Collapse row details"
>
<RiSidebarFoldLine className="size-4" />
</Button>
)}
<Button
variant="ghost"
size="icon"
className="size-7"
onClick={onClose}
aria-label="Close row details"
>
<RiCloseLine className="size-4" />
</Button>
</div>
</div>
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The row detail sidebar lacks keyboard accessibility. Users should be able to close the sidebar using the Escape key, which is a common pattern for dismissible UI elements. Consider adding an onKeyDown handler to the sidebar (or its parent container) that closes the sidebar when the Escape key is pressed.

Copilot uses AI. Check for mistakes.
if (detailRowIndex === null)
setRowDetailCollapsed(false)
}, [detailRowIndex])

Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When filters or sorting are applied, the row at detailRowIndex may no longer exist or may be a different row. The sidebar should be closed when filters or orderBy change to prevent showing details for the wrong row. Consider adding a useEffect that resets detailRowIndex when filters or orderBy change, similar to how it's reset when table/schema changes.

Suggested change
useEffect(() => {
if (detailRowIndex !== null) {
store.setState(state => ({
...state,
detailRowIndex: null,
} satisfies typeof state))
setRowDetailCollapsed(true)
}
}, [filters, orderBy, store, detailRowIndex])

Copilot uses AI. Check for mistakes.
<span className="ml-1 text-secondary-foreground">NULLABLE</span>
)}
{column.enum && (
<span className="rounded-xl ml-1 text-secondary-foreground p-2 bg-secondary">Enum</span>
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Enum badge has inconsistent styling compared to other badges in the same row. It has p-2 padding which is much larger than needed for inline text, and uses rounded-xl which creates a pill shape. The other badges (PK, FK, UQ, etc.) use simpler inline text without background. For consistency, either remove the background styling from the Enum badge or apply similar badge styling to all indicators. The current p-2 padding (0.5rem = 8px) is especially excessive for inline text.

Suggested change
<span className="rounded-xl ml-1 text-secondary-foreground p-2 bg-secondary">Enum</span>
<span className="ml-1 text-secondary-foreground">Enum</span>

Copilot uses AI. Check for mistakes.
@ifeelBALANCED ifeelBALANCED force-pushed the feat/table-row-detail-sidebar branch from a88f7bb to 89dc561 Compare February 27, 2026 21:45
@Rudra-Sankha-Sinhamahapatra Rudra-Sankha-Sinhamahapatra force-pushed the feat/table-row-detail-sidebar branch from 0a6fb9f to a88f7bb Compare March 7, 2026 09:19
…t/table-row-detail-sidebar

Made-with: Cursor

# Conflicts:
#	apps/desktop/src/routes/_protected/connection/$resourceId/table/-components/table/table.tsx
@letstri letstri closed this Apr 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add sidebar-like block after row click to see all fields in one column

4 participants