Skip to content

List view: left align numbers while keeping decimals aligned (#918) - #940

Open
arvinsingla wants to merge 1 commit into
masterfrom
claude/2026-09-23-issue-918-numeric-left-align
Open

arvinsingla wants to merge 1 commit into
masterfrom
claude/2026-09-23-issue-918-numeric-left-align

Conversation

@arvinsingla

Copy link
Copy Markdown
Collaborator

Closes #918

Implements the plan approved on the issue: #918 (comment)

The approach

Numbers in list-of-entries cells were flush-right. This returns them to ordinary left alignment while still lining up the decimal points down a column.

The key observation from the plan: number formatting is fixed per element, not per value. formulize_numberFormat() always emits the same decimal count, separators, prefix and suffix for every value in a column, so only the integer part varies in width. That means the decimals line up as soon as every number in a column sits in an equal-width box, with the number pushed to the box's right edge and the box itself placed at the left of the cell. No per-value splitting of the decimal is needed.

What changed

modules/formulize/include/entriesdisplay.php — a pre-pass in drawEntries(), immediately before the row loop, measures each numeric column once off the page of results already in memory. It formats only the column's smallest and largest value (with a fixed format, the longest string comes from whichever end has the greater magnitude; both ends are measured because the minus sign and thousands separators are part of the string) and takes the longer result's character length. Only the element types formulize_numberFormat() actually formats — number, text, derived — are measured; anything else (a select whose stored value happens to be numeric, say) displays text rather than a formatted number, so it is left alone. Result goes in $GLOBALS['formulize_numericColumnWidths'].

modules/formulize/include/functions.php — getHTMLForList() emits the measurement, not the styling: the numeric span gains style="--fz-num-width: {N}ch". Where no measurement exists (subform listings, the XHR inline-edit redraw path, custom code calling the function directly) the property is simply absent and the value degrades to plain left alignment — no guessed width. Also adds a small formulize_numericStringWidth() helper (multibyte-aware visible-character count, so a € prefix counts as one).

themes/Lyris/css/style.css and themes/Anari/css/style.css — the old .formulize-numeric { text-align: right } rules become an inline-block box:

display: inline-block;
text-align: right;
min-width: var(--fz-num-width, 0);
max-width: 100%;
font-variant-numeric: tabular-nums;

Two notes on the selectors: they are scoped to span (multi-value cells render lis, which stay list items), and they need an #listofentries prefix plus a second class to outweigh formulize.css's #listofentries div.main-cell-div > span { display: block } — without that the box stays block-level and the numbers stay flush-right. (I caught this on Anari during verification; the first selector I wrote lost the specificity contest.)

Lyris's .formulize-display-element-contents:has(.formulize-numeric) { flex: 1 } rule is removed. It existed only so a right-aligned number could reach the cell's right edge, and formulize.css already gives .formulize-display-element-contents flex: 1 1 auto regardless.

Out of scope per the approved plan, and untouched: subform listings (.right-align-text), calculation footers, and form display (.numeric-text). No per-column opt-out setting.

Verification

Manual/scripted browser check against the dev Docker stack (no e2e suite run), Lyris theme, before/after with the same data.

Artifacts list (sid 8), Height / Width / Depth / Year:

before after
computed display block inline-block
--fz-num-width absent 5ch (Height/Width/Depth), 4ch (Year) — one distinct value per column
Depth decimal-point x spread 1356.44 – 1361.11 px, 4 distinct positions 1172.23 – 1172.25 px (sub-pixel)
box left edge 1139.58 (box spanned the full cell, number pinned right at 1367.69) 1139.58, ends at 1182.67 — cell left edge is 1127.58, i.e. box starts at the cell's left padding

Line Items list (sid 1005), Quantity / Unit Price / Line Total: widths measured as 3ch / 10ch / 13ch, which match the data exactly — max quantity 999, max unit price $12,000.00 (10 chars), max line total $1,233,325.44 (13 chars). Unit Price decimal points went from 6 distinct x positions (1367.19 – 1371.08) to one (1247.50 – 1247.52).

Inline edit: verified on the Kitchen Sink list (sid 1010), whose percent column is inline-editable. The numeric box renders inline-block at 5ch next to the pencil, left-placed in the cell, and clicking the pencil still swaps in the number input correctly.

Anari: the dev site is pinned to Lyris (theme_set_allowed contains only Anari but the session switch does not take on this install), so Anari's list view could not be loaded directly. Instead the shipped Anari rule was injected verbatim into the live list DOM with the Lyris numeric rules disabled — it resolves to display: inline-block, min-width: 86.19px (10ch), with all box left and right edges equal down the column. That is the check that surfaced the specificity bug above.

No new PHP notices or warnings from the added code in the container error log across all of these renders.

🤖 Generated with Claude Code

Numbers in list-of-entries cells were flush-right. This returns them to
ordinary left alignment while still lining up the decimal points down a
column.

Number formatting is fixed per element rather than per value:
formulize_numberFormat always emits the same decimal count, separators,
prefix and suffix for every value in a column, so only the integer part
varies in width. The decimals therefore line up as soon as every number
in a column sits in an equal-width box with the number pushed to the
box's right edge and the box itself placed at the left of the cell. No
per-value splitting of the decimal is needed.

- drawEntries() measures each numeric column once, off the page of
  results already in memory, by formatting the column's smallest and
  largest value and taking the longer string's character length. Only
  the element types formulize_numberFormat actually formats (number,
  text, derived) are measured. The result goes in
  $GLOBALS['formulize_numericColumnWidths'].
- getHTMLForList() emits that measurement as a --fz-num-width custom
  property on the numeric span; it does no styling itself. Where no
  measurement exists (subform listings, the XHR inline-edit redraw
  path, custom code) the property is absent and the value degrades to
  plain left alignment.
- Lyris and Anari turn --fz-num-width into an inline-block box with
  text-align: right and tabular-nums. Lyris's old
  `:has(.formulize-numeric) { flex: 1 }` rule is dropped: it existed
  only so a right-aligned number could reach the cell's right edge, and
  .formulize-display-element-contents already carries flex: 1 1 auto
  from formulize.css.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

List view left align numbers in table cells but still ensure decimals are aligned

1 participant