Skip to content

Commit a08c834

Browse files
zeekayclaude
andcommitted
fix(base-admin): right-align + tabular figures for numeric grid columns
Pixel QA of the Twenty-grade record grid: numeric columns rendered as plain left-aligned text, so digits didn't line up column-wise. Number columns now right-align (header + cells) and number/date cells use tabular-nums, so amounts, counts and timestamps read as precise, column-aligned data — matching the grid's mono/tabular intent. Rebuilt the embedded dist (tsc -b && vite build, base=/_/). The grid already scrolls horizontally INSIDE its container on narrow viewports (verified 360/390/768/834/1440 — no body overflow), so mobile keeps readable column widths rather than compressing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 904deab commit a08c834

6 files changed

Lines changed: 20 additions & 10 deletions

File tree

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ui-react/dist/assets/index-CP7K0H8x.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ui-react/dist/assets/index-DCX6ykL9.css

Lines changed: 0 additions & 1 deletion
This file was deleted.

ui-react/dist/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
77
<meta name="color-scheme" content="dark" />
88
<title>Base Admin</title>
9-
<script type="module" crossorigin src="/_/assets/index-T-zYPzuY.js"></script>
10-
<link rel="stylesheet" crossorigin href="/_/assets/index-DCX6ykL9.css">
9+
<script type="module" crossorigin src="/_/assets/index-C4VLRqI_.js"></script>
10+
<link rel="stylesheet" crossorigin href="/_/assets/index-CP7K0H8x.css">
1111
</head>
1212
<body>
1313
<div id="root"></div>

ui-react/src/components/grid/EditableCell.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,22 @@ export function EditableCell(props: EditableCellProps) {
5757

5858
const editable = kind !== 'readonly' && kind !== 'file';
5959
const display = formatDisplay(value, field);
60+
// Numeric columns right-align; numeric + date use tabular figures so digits
61+
// line up column-wise (Twenty/Airtable-grade legibility).
62+
const numeric = kind === 'number';
63+
const tnum = numeric || kind === 'date';
6064

6165
return (
6266
<Popover open={ editing } onOpenChange={ (o) => { if (!o) onEditEnd(); } }>
6367
<PopoverAnchor asChild>
6468
<CellShell
6569
active={ active }
6670
editable={ editable }
71+
numeric={ numeric }
6772
onActivate={ onActivate }
6873
onEdit={ editable ? onEdit : undefined }
6974
>
70-
<span className={ cn('block truncate', kind === 'json' && 'font-mono text-xs') }>
75+
<span className={ cn('block truncate', tnum && 'tabular-nums', kind === 'json' && 'font-mono text-xs') }>
7176
{ display || <span className="text-muted-foreground/50"></span> }
7277
</span>
7378
</CellShell>
@@ -96,12 +101,14 @@ export function EditableCell(props: EditableCellProps) {
96101
function CellShell({
97102
active,
98103
editable,
104+
numeric,
99105
onActivate,
100106
onEdit,
101107
children,
102108
}: {
103109
active: boolean;
104110
editable?: boolean;
111+
numeric?: boolean;
105112
onActivate: () => void;
106113
onEdit?: () => void;
107114
children: React.ReactNode;
@@ -114,6 +121,7 @@ function CellShell({
114121
onDoubleClick={ onEdit }
115122
className={ cn(
116123
'flex h-9 items-center px-3 text-sm outline-none',
124+
numeric && 'justify-end',
117125
editable && 'cursor-text',
118126
active && 'ring-1 ring-inset ring-ring',
119127
) }

ui-react/src/components/grid/RecordGrid.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,14 @@ function SortHeader({
166166
}) {
167167
const asc = sort === field.name;
168168
const desc = sort === `-${field.name}`;
169+
// Numeric headers right-align to sit over their right-aligned tabular cells.
170+
const numeric = field.type === 'number';
169171
return (
170-
<th className="whitespace-nowrap px-3 py-2 text-left font-medium text-muted-foreground">
172+
<th className={ cn('whitespace-nowrap px-3 py-2 font-medium text-muted-foreground', numeric ? 'text-right' : 'text-left') }>
171173
<button
172174
type="button"
173175
onClick={ () => onSort(field.name) }
174-
className="inline-flex items-center gap-1 hover:text-foreground"
176+
className={ cn('inline-flex items-center gap-1 hover:text-foreground', numeric && 'flex-row-reverse') }
175177
>
176178
<span>{ field.name }</span>
177179
<span className="text-[10px] uppercase text-muted-foreground/60">{ field.type }</span>

0 commit comments

Comments
 (0)