Repository navigation
Conversation
🦋 Changeset detectedLatest commit: dfddb47 The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Thanks @hshoja, the requested cases are useful. I independently checked the layout helper at One performance improvement before this is ready: the capacity loop calls A private prototype selected the highest-priority I have not settled the separate-mark API choice yet, and the reported bundle costs and full validation still need independent verification. |
|
One more case from a rendered check of My fixture uses 20 rows in input order, All three engines kept labels inside the chart, with no label-to-label overlaps, at 640 × 320, 220 × 320 and 640 × 100. Visible label counts matched interaction-point counts. The connector routes still need a clear corridor beside the text when labels are spread vertically. I also measured the existing 114 consumer entries independently: 108 are byte-identical, six polar entries change by +0.026 kB minified, at most +0.009 kB gzip and +0.068 kB Brotli. No budget changes are approved by this review. |
|
Thanks for both reviews. Both points are fixed. 1. Faster selection when labels do not fit (8c41042)Before, the code removed the weakest label one at a time and scanned the whole list each time. Now it sorts by priority once, keeps the top labels that fit, and keeps them in slice order. Ties still keep the earlier row, and the Example: 5,000 labels on one side with room for 25 took 152.5 ms, and now take about 1 ms. New tests check that the result is the same as the old one-at-a-time removal (50 inputs), that slice order is kept, and the 5,000-label case. 2. Connectors no longer cross other labels (7d57e93)Before, labels followed a curve, so a slanted connector could pass through a neighbor's text. Now all labels on one side start at the same x, just outside the outermost connector. Every connector ends before the text starts, so it cannot cross another label. If the text does not fit the chart width, the mark hides either the label with the outermost connector or the widest label, whichever has the lower priority. On a tie it hides the outermost one, because that label pushes the column out. Your fixture, with text boxes grown by 2 px (red = connector crosses a label):
Your fixture is now a test. It checks every connector against every other label at all three sizes. On the old code it fails with Ideas for laterNot in this PR, until the API is decided:
|
|
Thanks @hshoja, I rechecked the rendered fixture at Firefox reports a slightly wider SVG text bounding box than the text advance. I compared screenshots with SVG overflow visible versus clipped, and there were zero changed pixels in all three engines for these fixtures. I do not see painted clipping here. The revised layout helper also passed 1,812 bounds, spacing and connector-column cases per engine, plus the 5,000-label case with capacity 25. That helper was checked at At the current head, 108 of the 114 existing consumer bundles remain byte-identical. The six changed polar bundles add 0.026 kB minified, at most 0.009 kB gzip and 0.073 kB Brotli. These measurements compare the reviewed source overlay against the current checkout, not a newly approved budget baseline. The two specific review concerns, repeated capacity scans and connector crossings in this fixture, are addressed. The separate-mark API choice, bundle acceptance and full CI remain open, so this is not merge approval. |
|
Thanks @hshoja, I extended verification at The checked-in bundle gate still fails three gzip ceilings: Polar arc, Polar gauge composition and Radial labels. I have not changed or approved those ceilings. The separate-mark API decision and packed-package verification also remain open, so this is additional test evidence, not merge approval. |
|
Thanks @hshoja, actual packed-package verification now passes at this same head, including bare React Native, Expo, the seven adapters and the unified artifact. The packed radial-label types pass four valid forms and reject four invalid option types; removing the export makes the negative control fail. The packed full-motion renderer also passes interpolation, reduced motion, interruption and cleanup in Chromium, Firefox and WebKit. Its 118-module packed Charts input closure is unchanged in the final unified artifact. This exact head predates motionLite, so these packed results cover full motion only. The three existing gzip failures, API discussion, draft status and required CI still need resolution. Verified head: |
Outside pie labels built from radialRule and radialText sit on their slice angle, so labels of small adjacent slices overlap. radialLabels stacks labels on each side, moves overlapping labels apart as little as possible, and hides the lowest-priority labels when they do not fit. maxShift optionally hides labels that would move too far from their slice.
Measure label text with the chart's text measurer and hide labels that would cross the left or right edge, so narrow charts and long labels stay inside the surface. Equal priorities now hide the later label first. Tests cover narrow and resized charts, long labels, maxShift removal, and stable keys.
radialLabels emits only polyline and label scene nodes. These tests confirm each renderer paints every visible connector and label.
Keyed labels and connectors keep their elements and interpolate to the new layout, and labels of removed slices fade out before removal.
Removing the lowest-priority label one at a time rescanned and copied the side for every removal, which is quadratic when many labels cannot fit. Sort once by priority, keep the labels that fit, and filter the angular order through that selection. The maxShift loop is unchanged.
Labels followed a curve, so a slanted connector to an outer label could pass through a neighbor's text when labels were spread vertically. Each side now places its text in one column outside the outermost visible elbow, so every connector stays on the inner side of all text. When the column does not fit, the lower-priority one of the outermost-elbow label and the widest label is hidden, preferring the outermost-elbow label on ties because it sets the column.
The API shape is not settled, so expose only options that change behavior: priority, maxShift, and the text and style options. Connector offset, elbow length, text padding, and line height become internal defaults; line height follows fontSize.
numericField duplicated the existing numberProperty helper that radialArc uses for its datum defaults. Document the mark with the option table used by the other polar marks, and shorten the friction entry to the format of recent entries.
Charts that do not use radialLabels should keep their exact polar bundle. Passing the chart text measurer through the polar container and adding the mark to polar.ts both changed every polar bundle. The mark now lives in polar-labels.ts and uses the built-in text estimate, polar.ts only re-exports it, and the shared option types move to a types-only module.
3797bfb to
dfddb47
Compare
|
Thanks for the extra checks. The branch is now rebased on What caused it: the polar container passed What changed (dfddb47):
Gzip bytes vs
The remaining +1 and +2 bytes are minifier name choices: the JS has the same length as Your fixture still has zero connector-to-label crossings at all three sizes, with 20, 10 and 6 labels. Trade-off: unusual fonts or a large React Native font scale can make the estimate a few pixels off. If you prefer real measurement, it is a one-line change in the polar container, at about 9 gzip bytes per polar chart. |
|
Thanks @hshoja, I checked dfddb47 against current main. The split removes the earlier arc and gauge budget failures. One remains on Node 24.18.0: Radial labels + static SVG is 24.315 decimal kB gzip against the unchanged 24.313 kB ceiling, a 2-byte difference. I have not changed the ceiling. Independent checks pass: 2,280 tests, typecheck, docs, packed React Native/Expo consumers, all seven framework adapters, and the final unified package. Full motion and motionLite pass interpolation, reduced-motion, resize, interruption and teardown checks through the final packed exports in Chromium, Firefox and WebKit. These checks are on the new head, not carried over from the old package. The estimate tradeoff is real even with ordinary fonts. In a 400 x 320 static SVG, radiusRatio 0.5, a right-side label at angle PI/2, 12px Arial, WWWWWWWW extends about 2.61px beyond the right edge in Chromium/WebKit. A 12px monospace Long category extends about 5.63px. The fixture sets the rendered font explicitly and uses a one-to-one viewport. Firefox also shows overflow. This is a bounded font test, not a claim about every layout. The docs should describe estimated, rather than guaranteed, edge containment and not limit the caveat to unusual fonts. The PR is still a draft, and required CI has not run. Keeping the text-measurement/API choice separate from the remaining 2-byte budget decision. |


Refs #227 - outside labels of small adjacent pie slices overlap, and there is no built-in way to spread or hide them.
radialLabelsmark in@tanstack/charts/polar: outside labels with elbow connectors, one text column per sidepriority(default: datumvalue);maxShiftalso hides labels that move too farScreenshot:

How to review: start at
polar-label-layout-internal.ts(pure layout), thenradialLabelsinpolar.ts; the rest is tests and docsHow to test:
pnpm vitest run packages/charts-core/src/polar-label-layout-internal.test.ts packages/charts-core/src/polar-radial-labels.test.tsMore screenshots
maxShift: 24hides labels that would move too far from their slice:Review fixture, before and after the text column (red = connector crosses a label):
Requested cases
maxShiftkey; connector keys add:connectormotion(), labels and connectors move to the new layout; hidden labels fade outlabelandpolylinenodes; tested in SVG, Canvas, and React Nativepolar.tsonly re-exports the mark, so polar charts withoutradialLabelskeep the same bundle asmain(at most 2 gzip bytes of minifier naming noise)Separate mark or
radialTextoptionradialTextoption: fewer exports, butradialTextwould need connector options, and hiding a label breaks its rule that every valid row renders.Local checks
Type check, docs check, and new and changed tests pass.
bundle:checkfails the same way on cleanmain;package:checkfails on a local Node version mismatch (24.7.0 vs.nvmrc24.18.0).