Skip to content

feat(datagrid): highlight rules that color rows or cells by value - #2738

Merged
datlechin merged 1 commit into
mainfrom
feat/conditional-formatting-2723
Sep 11, 2026
Merged

feat(datagrid): highlight rules that color rows or cells by value#2738
datlechin merged 1 commit into
mainfrom
feat/conditional-formatting-2723

Conversation

@datlechin

Copy link
Copy Markdown
Member

Summary

Highlight rules color data grid rows, or single cells, by their values (#2723).

  • Right-click a cell and open Highlight. A color under Rows Where status = “paid” tints every row holding that value; a color under Cells Where … tints only that cell. The palette marks the color an identical rule already uses, and Remove Highlight takes it away. The quick menu uses macOS 14 palette menus and section headers.
  • View > Highlight Rules…, the highlighter button in the status bar, and Highlight Values… in the column header menu open a popover for everything else: a column, an operator from the filter bar, a value, a color, and Row or Cell. Rules run top to bottom and the first match colors the row (Numbers' model). Reorder by drag or with the Move Rule Up / Down accessibility actions. Edits apply live. A rule closed without a value is dropped.
  • Rules belong to the table: stored per connection, database, schema and table in Application Support/TablePro/HighlightRules/<connection>.json, moved on a table, database or schema rename, and removed when the connection is deleted. They are device-local and never synced. A query result that resolved one table uses that table's rules; any other query result keeps its rules on the tab only.
  • Colors are the Finder tag set as dynamic system colors at 20% alpha, fill only, the way Numbers and Mail color rows. Label contrast measured at 11:1 or better in light and 7.8:1 or better in dark. A pending insert or delete keeps its own tint over a rule, a modified cell keeps the modified tint, and selection draws as usual.
  • VoiceOver reads "…, highlighted where status = “paid”" on a cell whose highlight is actually drawn.

Why the row view changed

Row color was one derived value (RowVisualState) read at TableViewCoordinator.visualState(for:), but DataGridRowView painted a copy pushed into it while the pinned gutter and the cells read the live value. Any path that rebuilt the index without pushing split the wash from the gutter. One shipped case: delete an existing row together with a new, unsaved row, and the existing row lost its red wash across the data columns while its gutter stayed red. A value rule would make this routine, because an edit can flip a row's highlight. So the row view now reads its state live, every state change invalidates the whole row and its gutter band, and highlights are composed inside the same chokepoint. Structure and Create Table grids supply their own state and are never highlighted.

Matching

HighlightCondition evaluates the FilterOperator vocabulary in memory against raw stored values, mirroring FilterSQLGenerator: NULL fails every comparison, = NULL means IS NULL on a non-text column, "is empty" is NULL or '', numeric columns compare as numbers, boolean columns accept true/1/t/yes, and a value is compared exactly as stored so a padded CHAR cell matches its own quick rule. Searches and regex patterns are capped at 10,000 characters. Results are cached per row id alongside the formatted-text cache, so they share its lifetime (a page change or edit drops them) and are evaluated only for rows that draw.

What I tested

  • New suites: HighlightConditionTests, HighlightRuleSetTests, HighlightRuleDescriptionTests, HighlightRuleStorageTests, TableViewCoordinatorHighlightTests, plus new cases in DataGridCellAppearanceTests, DataGridRowTintThemeTests (rewritten over a coordinator, including the live-state regression), DataGridUpdateSnapshotTests, RowDisplayCacheTests, ResultStatusModelTests, MainMenuValidationTests.
  • Every suite that owns a touched type, 272 cases, passing after rebasing on main.
  • HighlightRulesUITests: add a rule from the status bar, close with Escape, reopen and find it; an empty rule is dropped on close; the cell menu offers Highlight and opens the rules.
  • Driven by hand on a Debug build against the Chinook sample: quick rules, live repaint, light and dark.
  • Codex could not review this change (its account hit a usage limit), so the diff went through the code-review skill instead; its four findings are fixed.
  • ColumnJumpUITests fails, identically, on origin/main too, so it is not from this change.
  • MainMenuValidationTests.onlyConnectedPhaseEnablesCommands was red on main after feat(plugins): add a Cloudflare R2 SQL driver #2030 gated the page commands on canNavigatePages without setting it in the test's all-capable context. This PR sets it, one line in a file it already edits.

Not in this PR

Text color and bold styles, custom colors, AND/OR rules, iCloud sync, undo for rule edits, and highlighting in exports, JSON view, Compare panes or iOS.

Screenshots

There is no before: the grid had no value highlighting. After, light and dark:

Highlight rules popover over an Invoice grid, light
Highlight rules popover over an Invoice grid, dark

Fixes #2723

https://claude.ai/code/session_01WA4qjtY9TiRD6QuqhhW5Yr

@mintlify

mintlify Bot commented Sep 11, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
TablePro 🟢 Ready View Preview Sep 11, 2026, 2:47 PM

💡 Tip: Enable Automations to automatically generate PRs for you.

@datlechin
datlechin merged commit 8a9ed73 into main Sep 11, 2026
8 of 9 checks passed
@datlechin
datlechin deleted the feat/conditional-formatting-2723 branch September 11, 2026 14:57
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.

Conditional coloring of rows

1 participant