@mui/x-charts 9.4.0 → 9.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/ChartsRadialAxisHighlight/index.d.mts +6 -1
  3. package/ChartsRadialAxisHighlight/index.d.ts +6 -1
  4. package/ChartsRadialAxisHighlight/index.js +9 -2
  5. package/ChartsRadialAxisHighlight/index.mjs +8 -1
  6. package/ChartsRadialDataProvider/ChartsRadialDataProvider.d.mts +1 -1
  7. package/ChartsRadialDataProvider/ChartsRadialDataProvider.d.ts +1 -1
  8. package/ChartsRadialDataProvider/ChartsRadialDataProvider.js +1 -1
  9. package/ChartsRadialDataProvider/ChartsRadialDataProvider.mjs +1 -1
  10. package/ChartsRadialDataProvider/index.d.mts +5 -0
  11. package/ChartsRadialDataProvider/index.d.ts +5 -0
  12. package/ChartsRadialDataProvider/index.js +12 -1
  13. package/ChartsRadialDataProvider/index.mjs +7 -0
  14. package/ChartsRadialGrid/index.d.mts +6 -1
  15. package/ChartsRadialGrid/index.d.ts +6 -1
  16. package/ChartsRadialGrid/index.js +8 -2
  17. package/ChartsRadialGrid/index.mjs +8 -1
  18. package/ChartsRadiusAxis/index.d.mts +6 -1
  19. package/ChartsRadiusAxis/index.d.ts +6 -1
  20. package/ChartsRadiusAxis/index.js +10 -2
  21. package/ChartsRadiusAxis/index.mjs +7 -1
  22. package/ChartsRotationAxis/index.d.mts +6 -1
  23. package/ChartsRotationAxis/index.d.ts +6 -1
  24. package/ChartsRotationAxis/index.js +10 -2
  25. package/ChartsRotationAxis/index.mjs +7 -1
  26. package/ChartsTooltip/ChartsTooltipContainer.js +11 -4
  27. package/ChartsTooltip/ChartsTooltipContainer.mjs +11 -4
  28. package/LineChart/AreaElement.js +1 -1
  29. package/LineChart/AreaElement.mjs +1 -1
  30. package/LineChart/CircleMarkElement.js +1 -1
  31. package/LineChart/CircleMarkElement.mjs +1 -1
  32. package/LineChart/LineElement.js +1 -1
  33. package/LineChart/LineElement.mjs +1 -1
  34. package/LineChart/MarkElement.js +1 -1
  35. package/LineChart/MarkElement.mjs +1 -1
  36. package/ScatterChart/ScatterChart.js +7 -3
  37. package/ScatterChart/ScatterChart.mjs +7 -3
  38. package/ScatterChart/ScatterChart.plugins.d.mts +2 -1
  39. package/ScatterChart/ScatterChart.plugins.d.ts +2 -1
  40. package/ScatterChart/ScatterChart.plugins.js +2 -1
  41. package/ScatterChart/ScatterChart.plugins.mjs +2 -1
  42. package/ScatterChart/ScatterPlot.d.mts +4 -2
  43. package/ScatterChart/ScatterPlot.d.ts +4 -2
  44. package/ScatterChart/ScatterPlot.js +25 -3
  45. package/ScatterChart/ScatterPlot.mjs +25 -3
  46. package/ScatterChart/async/ScatterAsync.d.mts +9 -0
  47. package/ScatterChart/async/ScatterAsync.d.ts +9 -0
  48. package/ScatterChart/async/ScatterAsync.js +71 -0
  49. package/ScatterChart/async/ScatterAsync.mjs +67 -0
  50. package/ScatterChart/async/ScatterAsyncBatch.d.mts +24 -0
  51. package/ScatterChart/async/ScatterAsyncBatch.d.ts +24 -0
  52. package/ScatterChart/async/ScatterAsyncBatch.js +112 -0
  53. package/ScatterChart/async/ScatterAsyncBatch.mjs +106 -0
  54. package/ScatterChart/async/scatterRenderData.selectors.d.mts +38 -0
  55. package/ScatterChart/async/scatterRenderData.selectors.d.ts +38 -0
  56. package/ScatterChart/async/scatterRenderData.selectors.js +93 -0
  57. package/ScatterChart/async/scatterRenderData.selectors.mjs +87 -0
  58. package/SparkLineChart/SparkLineChart.js +2 -1
  59. package/SparkLineChart/SparkLineChart.mjs +2 -1
  60. package/index.js +1 -1
  61. package/index.mjs +1 -1
  62. package/internals/getSeriesColorFn.d.mts +5 -5
  63. package/internals/getSeriesColorFn.d.ts +5 -5
  64. package/internals/index.d.mts +1 -0
  65. package/internals/index.d.ts +1 -0
  66. package/internals/index.js +12 -0
  67. package/internals/index.mjs +1 -0
  68. package/internals/plugins/allPlugins.d.mts +4 -3
  69. package/internals/plugins/allPlugins.d.ts +4 -3
  70. package/internals/plugins/allPlugins.js +2 -1
  71. package/internals/plugins/allPlugins.mjs +2 -1
  72. package/internals/plugins/corePlugins/useChartExperimentalFeature/useChartExperimentalFeature.selectors.d.mts +12 -3
  73. package/internals/plugins/corePlugins/useChartExperimentalFeature/useChartExperimentalFeature.selectors.d.ts +12 -3
  74. package/internals/plugins/corePlugins/useChartExperimentalFeature/useChartExperimentalFeature.selectors.js +10 -1
  75. package/internals/plugins/corePlugins/useChartExperimentalFeature/useChartExperimentalFeature.selectors.mjs +10 -1
  76. package/internals/plugins/corePlugins/useChartExperimentalFeature/useChartExperimentalFeature.types.d.mts +15 -1
  77. package/internals/plugins/corePlugins/useChartExperimentalFeature/useChartExperimentalFeature.types.d.ts +15 -1
  78. package/internals/plugins/featurePlugins/useChartCartesianAxis/getAxisScale.js +27 -2
  79. package/internals/plugins/featurePlugins/useChartCartesianAxis/getAxisScale.mjs +27 -2
  80. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxis.js +1 -3
  81. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxis.mjs +1 -3
  82. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxisRendering.selectors.js +1 -1
  83. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxisRendering.selectors.mjs +1 -1
  84. package/internals/plugins/featurePlugins/useProgressiveRendering/index.d.mts +3 -0
  85. package/internals/plugins/featurePlugins/useProgressiveRendering/index.d.ts +3 -0
  86. package/internals/plugins/featurePlugins/useProgressiveRendering/index.js +27 -0
  87. package/internals/plugins/featurePlugins/useProgressiveRendering/index.mjs +2 -0
  88. package/internals/plugins/featurePlugins/useProgressiveRendering/useProgressiveRendering.d.mts +13 -0
  89. package/internals/plugins/featurePlugins/useProgressiveRendering/useProgressiveRendering.d.ts +13 -0
  90. package/internals/plugins/featurePlugins/useProgressiveRendering/useProgressiveRendering.js +136 -0
  91. package/internals/plugins/featurePlugins/useProgressiveRendering/useProgressiveRendering.mjs +128 -0
  92. package/internals/plugins/featurePlugins/useProgressiveRendering/useProgressiveRendering.selectors.d.mts +42 -0
  93. package/internals/plugins/featurePlugins/useProgressiveRendering/useProgressiveRendering.selectors.d.ts +42 -0
  94. package/internals/plugins/featurePlugins/useProgressiveRendering/useProgressiveRendering.selectors.js +166 -0
  95. package/internals/plugins/featurePlugins/useProgressiveRendering/useProgressiveRendering.selectors.mjs +159 -0
  96. package/internals/plugins/featurePlugins/useProgressiveRendering/useProgressiveRendering.types.d.mts +34 -0
  97. package/internals/plugins/featurePlugins/useProgressiveRendering/useProgressiveRendering.types.d.ts +34 -0
  98. package/internals/plugins/featurePlugins/useProgressiveRendering/useProgressiveRendering.types.js +5 -0
  99. package/internals/plugins/featurePlugins/useProgressiveRendering/useProgressiveRendering.types.mjs +1 -0
  100. package/internals/scales/scaleBand.js +70 -43
  101. package/internals/scales/scaleBand.mjs +71 -44
  102. package/models/seriesType/bar.d.mts +1 -1
  103. package/models/seriesType/bar.d.ts +1 -1
  104. package/models/seriesType/common.d.mts +4 -4
  105. package/models/seriesType/common.d.ts +4 -4
  106. package/models/seriesType/config.d.mts +3 -3
  107. package/models/seriesType/config.d.ts +3 -3
  108. package/models/seriesType/line.d.mts +1 -1
  109. package/models/seriesType/line.d.ts +1 -1
  110. package/models/seriesType/pie.d.mts +1 -1
  111. package/models/seriesType/pie.d.ts +1 -1
  112. package/models/seriesType/radar.d.mts +1 -1
  113. package/models/seriesType/radar.d.ts +1 -1
  114. package/models/seriesType/scatter.d.mts +1 -1
  115. package/models/seriesType/scatter.d.ts +1 -1
  116. package/package.json +3 -3
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.sameSeriesIds = sameSeriesIds;
7
+ exports.selectorShouldUseProgressiveRenderer = exports.selectorProgressiveTotalRounds = exports.selectorProgressiveSeriesRevealedBatches = exports.selectorProgressiveRevealedRounds = exports.selectorProgressivePlans = exports.selectorProgressiveBatchSize = exports.selectorProgressiveAggregate = void 0;
8
+ var _store = require("@mui/x-internals/store");
9
+ var _useChartSeries = require("../../corePlugins/useChartSeries");
10
+ var _useChartExperimentalFeature = require("../../corePlugins/useChartExperimentalFeature");
11
+ /**
12
+ * Total point count above which the auto (`renderer` unset) mode switches to
13
+ * the progressive renderer. Below it the synchronous renderer is used, since
14
+ * the progressive paint's overhead is not worth it for small datasets.
15
+ */
16
+ const PROGRESSIVE_POINT_THRESHOLD = 20000;
17
+
18
+ /**
19
+ * Target number of reveal commits. Each commit repaints every already-painted
20
+ * circle, so the total progressive wall time is roughly `(C + 1) / 2` times a
21
+ * single synchronous render (where `C` is the number of commits). Targeting
22
+ * `5` commits keeps the progressive paint at roughly 2–3× the sync render time.
23
+ */
24
+ const TARGET_PROGRESSIVE_COMMITS = 5;
25
+
26
+ /**
27
+ * Lower bound for the per-tick reveal budget (total points across every
28
+ * series). Prevents tiny commits whose React overhead would dominate.
29
+ */
30
+ const MIN_BATCH_TOTAL = 1000;
31
+
32
+ /**
33
+ * Upper bound for the per-tick reveal budget (total points across every
34
+ * series). Prevents a single commit from blocking the main thread for too
35
+ * long; very large datasets simply use more commits.
36
+ */
37
+ const MAX_BATCH_TOTAL = 10000;
38
+
39
+ /**
40
+ * Per-series points revealed per tick, derived from the total point count
41
+ * across visible series and the number of visible series. The total per-tick
42
+ * budget aims for {@link TARGET_PROGRESSIVE_COMMITS} commits, clamped by
43
+ * {@link MIN_BATCH_TOTAL} / {@link MAX_BATCH_TOTAL}, then split evenly across
44
+ * the visible series so every series progresses together.
45
+ */
46
+ const getEffectiveBatchSize = (nSeries, totalPoints) => {
47
+ const safeSeries = Math.max(1, nSeries);
48
+ const safePoints = Math.max(1, totalPoints);
49
+ const totalPerTick = Math.min(MAX_BATCH_TOTAL, Math.max(MIN_BATCH_TOTAL, Math.ceil(safePoints / TARGET_PROGRESSIVE_COMMITS)));
50
+ return Math.max(1, Math.floor(totalPerTick / safeSeries));
51
+ };
52
+ const selectorProgressiveState = state => state.progressiveRendering;
53
+
54
+ /** Map of registered plots → their set of series ids. */
55
+ const selectorProgressivePlans = exports.selectorProgressivePlans = (0, _store.createSelector)(selectorProgressiveState, s => s?.plans);
56
+
57
+ /** Total number of rounds revealed across all plots so far. */
58
+ const selectorProgressiveRevealedRounds = exports.selectorProgressiveRevealedRounds = (0, _store.createSelector)(selectorProgressiveState, s => s?.revealedRounds ?? 0);
59
+
60
+ /** Point count of a series, looked up across every processed series type. */
61
+ function getSeriesPointCount(processedSeries, seriesId) {
62
+ for (const type in processedSeries) {
63
+ if (!Object.hasOwn(processedSeries, type)) {
64
+ continue;
65
+ }
66
+ const item = processedSeries[type]?.series[seriesId];
67
+ if (item) {
68
+ return Array.isArray(item.data) ? item.data.length : 0;
69
+ }
70
+ }
71
+ return 0;
72
+ }
73
+
74
+ /**
75
+ * Aggregated view of every registered plan: the per-series batch counts, the
76
+ * total number of rounds, and the per-series batch size (so every consumer
77
+ * sizes its batches the same way). Point counts are read straight from the
78
+ * processed series rather than carried by the registration.
79
+ */
80
+ const selectorProgressiveAggregate = exports.selectorProgressiveAggregate = (0, _store.createSelectorMemoized)(selectorProgressivePlans, _useChartSeries.selectorChartSeriesProcessed, function selectorProgressiveAggregate(plans, processedSeries) {
81
+ const nBatchesBySeries = new Map();
82
+ if (!plans || plans.size === 0) {
83
+ return {
84
+ nBatchesBySeries,
85
+ totalRounds: 0,
86
+ batchSize: MIN_BATCH_TOTAL
87
+ };
88
+ }
89
+ let nSeries = 0;
90
+ let totalPoints = 0;
91
+ const pointCounts = new Map();
92
+ plans.forEach(seriesIds => {
93
+ seriesIds.forEach(seriesId => {
94
+ const nPoints = getSeriesPointCount(processedSeries, seriesId);
95
+ pointCounts.set(seriesId, nPoints);
96
+ nSeries += 1;
97
+ totalPoints += nPoints;
98
+ });
99
+ });
100
+ const batchSize = getEffectiveBatchSize(nSeries, totalPoints);
101
+ let totalRounds = 0;
102
+ pointCounts.forEach((nPoints, seriesId) => {
103
+ const n = Math.max(1, Math.ceil(nPoints / batchSize));
104
+ nBatchesBySeries.set(seriesId, n);
105
+ if (n > totalRounds) {
106
+ totalRounds = n;
107
+ }
108
+ });
109
+ return {
110
+ nBatchesBySeries,
111
+ totalRounds,
112
+ batchSize
113
+ };
114
+ });
115
+ const selectorProgressiveBatchSize = exports.selectorProgressiveBatchSize = (0, _store.createSelector)(selectorProgressiveAggregate, a => a.batchSize);
116
+ const selectorProgressiveTotalRounds = exports.selectorProgressiveTotalRounds = (0, _store.createSelector)(selectorProgressiveAggregate, a => a.totalRounds);
117
+
118
+ /**
119
+ * How many of `seriesId`'s own batches are revealed so far. Capped at that
120
+ * series' total batch count, so series with fewer batches simply stop
121
+ * progressing while longer ones keep filling in.
122
+ */
123
+ const selectorProgressiveSeriesRevealedBatches = exports.selectorProgressiveSeriesRevealedBatches = (0, _store.createSelector)(selectorProgressiveAggregate, selectorProgressiveRevealedRounds, function selectorProgressiveSeriesRevealedBatches(agg, revealed, seriesId) {
124
+ return Math.min(revealed, agg.nBatchesBySeries.get(seriesId) ?? 0);
125
+ });
126
+
127
+ /**
128
+ * Whether `seriesIds` should be rendered progressively given the requested
129
+ * `renderer`:
130
+ * - `svg-single` / `svg-batch`: never (those are non-progressive renderers).
131
+ * - `svg-progressive`: always.
132
+ * - unset (auto): only when the `progressiveRendering` experimental feature is
133
+ * enabled and the total point count is above
134
+ * {@link PROGRESSIVE_POINT_THRESHOLD}. The flag keeps the auto behavior
135
+ * opt-in so the default (`svg-single`) stays non-breaking.
136
+ */
137
+ const selectorProgressiveRenderingEnabled = state => (0, _useChartExperimentalFeature.selectorChartExperimentalFeaturesState)(state, 'progressiveRendering');
138
+ const selectorShouldUseProgressiveRenderer = exports.selectorShouldUseProgressiveRenderer = (0, _store.createSelector)(_useChartSeries.selectorChartSeriesProcessed, selectorProgressiveRenderingEnabled, (processedSeries, progressiveRenderingEnabled, seriesIds, renderer) => {
139
+ if (renderer === 'svg-single' || renderer === 'svg-batch') {
140
+ return false;
141
+ }
142
+ if (renderer === 'svg-progressive') {
143
+ return true;
144
+ }
145
+ if (!progressiveRenderingEnabled) {
146
+ return false;
147
+ }
148
+ let totalPoints = 0;
149
+ for (const seriesId of seriesIds) {
150
+ totalPoints += getSeriesPointCount(processedSeries, seriesId);
151
+ }
152
+ return totalPoints > PROGRESSIVE_POINT_THRESHOLD;
153
+ });
154
+
155
+ /** Order-sensitive equality between two registered series-id sets. */
156
+ function sameSeriesIds(a, b) {
157
+ if (!a || a.length !== b.length) {
158
+ return false;
159
+ }
160
+ for (let i = 0; i < a.length; i += 1) {
161
+ if (a[i] !== b[i]) {
162
+ return false;
163
+ }
164
+ }
165
+ return true;
166
+ }
@@ -0,0 +1,159 @@
1
+ import { createSelector, createSelectorMemoized } from '@mui/x-internals/store';
2
+ import { selectorChartSeriesProcessed } from "../../corePlugins/useChartSeries/index.mjs";
3
+ import { selectorChartExperimentalFeaturesState } from "../../corePlugins/useChartExperimentalFeature/index.mjs";
4
+ /**
5
+ * Total point count above which the auto (`renderer` unset) mode switches to
6
+ * the progressive renderer. Below it the synchronous renderer is used, since
7
+ * the progressive paint's overhead is not worth it for small datasets.
8
+ */
9
+ const PROGRESSIVE_POINT_THRESHOLD = 20000;
10
+
11
+ /**
12
+ * Target number of reveal commits. Each commit repaints every already-painted
13
+ * circle, so the total progressive wall time is roughly `(C + 1) / 2` times a
14
+ * single synchronous render (where `C` is the number of commits). Targeting
15
+ * `5` commits keeps the progressive paint at roughly 2–3× the sync render time.
16
+ */
17
+ const TARGET_PROGRESSIVE_COMMITS = 5;
18
+
19
+ /**
20
+ * Lower bound for the per-tick reveal budget (total points across every
21
+ * series). Prevents tiny commits whose React overhead would dominate.
22
+ */
23
+ const MIN_BATCH_TOTAL = 1000;
24
+
25
+ /**
26
+ * Upper bound for the per-tick reveal budget (total points across every
27
+ * series). Prevents a single commit from blocking the main thread for too
28
+ * long; very large datasets simply use more commits.
29
+ */
30
+ const MAX_BATCH_TOTAL = 10000;
31
+
32
+ /**
33
+ * Per-series points revealed per tick, derived from the total point count
34
+ * across visible series and the number of visible series. The total per-tick
35
+ * budget aims for {@link TARGET_PROGRESSIVE_COMMITS} commits, clamped by
36
+ * {@link MIN_BATCH_TOTAL} / {@link MAX_BATCH_TOTAL}, then split evenly across
37
+ * the visible series so every series progresses together.
38
+ */
39
+ const getEffectiveBatchSize = (nSeries, totalPoints) => {
40
+ const safeSeries = Math.max(1, nSeries);
41
+ const safePoints = Math.max(1, totalPoints);
42
+ const totalPerTick = Math.min(MAX_BATCH_TOTAL, Math.max(MIN_BATCH_TOTAL, Math.ceil(safePoints / TARGET_PROGRESSIVE_COMMITS)));
43
+ return Math.max(1, Math.floor(totalPerTick / safeSeries));
44
+ };
45
+ const selectorProgressiveState = state => state.progressiveRendering;
46
+
47
+ /** Map of registered plots → their set of series ids. */
48
+ export const selectorProgressivePlans = createSelector(selectorProgressiveState, s => s?.plans);
49
+
50
+ /** Total number of rounds revealed across all plots so far. */
51
+ export const selectorProgressiveRevealedRounds = createSelector(selectorProgressiveState, s => s?.revealedRounds ?? 0);
52
+
53
+ /** Point count of a series, looked up across every processed series type. */
54
+ function getSeriesPointCount(processedSeries, seriesId) {
55
+ for (const type in processedSeries) {
56
+ if (!Object.hasOwn(processedSeries, type)) {
57
+ continue;
58
+ }
59
+ const item = processedSeries[type]?.series[seriesId];
60
+ if (item) {
61
+ return Array.isArray(item.data) ? item.data.length : 0;
62
+ }
63
+ }
64
+ return 0;
65
+ }
66
+
67
+ /**
68
+ * Aggregated view of every registered plan: the per-series batch counts, the
69
+ * total number of rounds, and the per-series batch size (so every consumer
70
+ * sizes its batches the same way). Point counts are read straight from the
71
+ * processed series rather than carried by the registration.
72
+ */
73
+ export const selectorProgressiveAggregate = createSelectorMemoized(selectorProgressivePlans, selectorChartSeriesProcessed, function selectorProgressiveAggregate(plans, processedSeries) {
74
+ const nBatchesBySeries = new Map();
75
+ if (!plans || plans.size === 0) {
76
+ return {
77
+ nBatchesBySeries,
78
+ totalRounds: 0,
79
+ batchSize: MIN_BATCH_TOTAL
80
+ };
81
+ }
82
+ let nSeries = 0;
83
+ let totalPoints = 0;
84
+ const pointCounts = new Map();
85
+ plans.forEach(seriesIds => {
86
+ seriesIds.forEach(seriesId => {
87
+ const nPoints = getSeriesPointCount(processedSeries, seriesId);
88
+ pointCounts.set(seriesId, nPoints);
89
+ nSeries += 1;
90
+ totalPoints += nPoints;
91
+ });
92
+ });
93
+ const batchSize = getEffectiveBatchSize(nSeries, totalPoints);
94
+ let totalRounds = 0;
95
+ pointCounts.forEach((nPoints, seriesId) => {
96
+ const n = Math.max(1, Math.ceil(nPoints / batchSize));
97
+ nBatchesBySeries.set(seriesId, n);
98
+ if (n > totalRounds) {
99
+ totalRounds = n;
100
+ }
101
+ });
102
+ return {
103
+ nBatchesBySeries,
104
+ totalRounds,
105
+ batchSize
106
+ };
107
+ });
108
+ export const selectorProgressiveBatchSize = createSelector(selectorProgressiveAggregate, a => a.batchSize);
109
+ export const selectorProgressiveTotalRounds = createSelector(selectorProgressiveAggregate, a => a.totalRounds);
110
+
111
+ /**
112
+ * How many of `seriesId`'s own batches are revealed so far. Capped at that
113
+ * series' total batch count, so series with fewer batches simply stop
114
+ * progressing while longer ones keep filling in.
115
+ */
116
+ export const selectorProgressiveSeriesRevealedBatches = createSelector(selectorProgressiveAggregate, selectorProgressiveRevealedRounds, function selectorProgressiveSeriesRevealedBatches(agg, revealed, seriesId) {
117
+ return Math.min(revealed, agg.nBatchesBySeries.get(seriesId) ?? 0);
118
+ });
119
+
120
+ /**
121
+ * Whether `seriesIds` should be rendered progressively given the requested
122
+ * `renderer`:
123
+ * - `svg-single` / `svg-batch`: never (those are non-progressive renderers).
124
+ * - `svg-progressive`: always.
125
+ * - unset (auto): only when the `progressiveRendering` experimental feature is
126
+ * enabled and the total point count is above
127
+ * {@link PROGRESSIVE_POINT_THRESHOLD}. The flag keeps the auto behavior
128
+ * opt-in so the default (`svg-single`) stays non-breaking.
129
+ */
130
+ const selectorProgressiveRenderingEnabled = state => selectorChartExperimentalFeaturesState(state, 'progressiveRendering');
131
+ export const selectorShouldUseProgressiveRenderer = createSelector(selectorChartSeriesProcessed, selectorProgressiveRenderingEnabled, (processedSeries, progressiveRenderingEnabled, seriesIds, renderer) => {
132
+ if (renderer === 'svg-single' || renderer === 'svg-batch') {
133
+ return false;
134
+ }
135
+ if (renderer === 'svg-progressive') {
136
+ return true;
137
+ }
138
+ if (!progressiveRenderingEnabled) {
139
+ return false;
140
+ }
141
+ let totalPoints = 0;
142
+ for (const seriesId of seriesIds) {
143
+ totalPoints += getSeriesPointCount(processedSeries, seriesId);
144
+ }
145
+ return totalPoints > PROGRESSIVE_POINT_THRESHOLD;
146
+ });
147
+
148
+ /** Order-sensitive equality between two registered series-id sets. */
149
+ export function sameSeriesIds(a, b) {
150
+ if (!a || a.length !== b.length) {
151
+ return false;
152
+ }
153
+ for (let i = 0; i < a.length; i += 1) {
154
+ if (a[i] !== b[i]) {
155
+ return false;
156
+ }
157
+ }
158
+ return true;
159
+ }
@@ -0,0 +1,34 @@
1
+ import { type SeriesId } from "../../../../models/seriesType/common.mjs";
2
+ import type { RendererType } from "../../../../ScatterChart/index.mjs";
3
+ import { type ChartPluginSignature } from "../../models/index.mjs";
4
+ export interface UseProgressiveRenderingState {
5
+ progressiveRendering: {
6
+ /**
7
+ * Per-plot sets of series ids that should be progressively revealed, keyed
8
+ * by an opaque plot id. Each plot (e.g. a separate `ScatterPlot` composed
9
+ * into the same chart) registers its own set. The scheduler aggregates
10
+ * them and reads point counts straight from the processed series.
11
+ */
12
+ plans: ReadonlyMap<string, readonly SeriesId[]>;
13
+ /**
14
+ * Number of *rounds* revealed so far. A round adds one batch in every
15
+ * registered series simultaneously, so every series progresses together.
16
+ */
17
+ revealedRounds: number;
18
+ };
19
+ }
20
+ export interface UseProgressiveRenderingInstance {
21
+ /**
22
+ * Register the set of series ids that `plotId` wants progressively revealed,
23
+ * and return a cleanup function that unregisters them. Designed to be called
24
+ * from a `React.useEffect`: returning the unregister function makes the
25
+ * effect's cleanup tear the registration down on unmount or before the next
26
+ * call. Re-calling with the same `plotId` replaces the previous set; a no-op
27
+ * when the set is unchanged.
28
+ */
29
+ registerProgressivePlan(plotId: string, seriesIds: readonly SeriesId[], renderer: RendererType | 'svg-progressive' | undefined): (() => void) | undefined;
30
+ }
31
+ export type UseProgressiveRenderingSignature = ChartPluginSignature<{
32
+ state: UseProgressiveRenderingState;
33
+ instance: UseProgressiveRenderingInstance;
34
+ }>;
@@ -0,0 +1,34 @@
1
+ import { type SeriesId } from "../../../../models/seriesType/common.js";
2
+ import type { RendererType } from "../../../../ScatterChart/index.js";
3
+ import { type ChartPluginSignature } from "../../models/index.js";
4
+ export interface UseProgressiveRenderingState {
5
+ progressiveRendering: {
6
+ /**
7
+ * Per-plot sets of series ids that should be progressively revealed, keyed
8
+ * by an opaque plot id. Each plot (e.g. a separate `ScatterPlot` composed
9
+ * into the same chart) registers its own set. The scheduler aggregates
10
+ * them and reads point counts straight from the processed series.
11
+ */
12
+ plans: ReadonlyMap<string, readonly SeriesId[]>;
13
+ /**
14
+ * Number of *rounds* revealed so far. A round adds one batch in every
15
+ * registered series simultaneously, so every series progresses together.
16
+ */
17
+ revealedRounds: number;
18
+ };
19
+ }
20
+ export interface UseProgressiveRenderingInstance {
21
+ /**
22
+ * Register the set of series ids that `plotId` wants progressively revealed,
23
+ * and return a cleanup function that unregisters them. Designed to be called
24
+ * from a `React.useEffect`: returning the unregister function makes the
25
+ * effect's cleanup tear the registration down on unmount or before the next
26
+ * call. Re-calling with the same `plotId` replaces the previous set; a no-op
27
+ * when the set is unchanged.
28
+ */
29
+ registerProgressivePlan(plotId: string, seriesIds: readonly SeriesId[], renderer: RendererType | 'svg-progressive' | undefined): (() => void) | undefined;
30
+ }
31
+ export type UseProgressiveRenderingSignature = ChartPluginSignature<{
32
+ state: UseProgressiveRenderingState;
33
+ instance: UseProgressiveRenderingInstance;
34
+ }>;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
@@ -21,60 +21,57 @@ function keyof(value) {
21
21
  }
22
22
 
23
23
  /**
24
- * Constructs a new band scale with the specified range, no padding, no rounding and center alignment.
25
- * The domain defaults to the empty domain.
26
- * If range is not specified, it defaults to the unit range [0, 1].
27
- *
28
- * The generic corresponds to the data type of domain elements.
29
- *
30
- * @param range A two-element array of numeric values.
31
- */
32
-
33
- /**
34
- * Constructs a new band scale with the specified domain and range, no padding, no rounding and center alignment.
35
- *
36
- * The generic corresponds to the data type of domain elements.
37
- *
38
- * @param domain Array of domain values.
39
- * @param range A two-element array of numeric values.
24
+ * The internal state shared when copying a scale. The domain index (`InternMap`) is the expensive
25
+ * part to build — `O(domain)` and is immutable once set (the domain setter replaces it rather than
26
+ * mutating it), so a copy can share the same reference instead of rebuilding it. This makes `copy()`
27
+ * `O(1)`, which matters because zooming copies the scale on every frame to apply a new range.
40
28
  */
41
29
 
42
- function scaleBand(...args) {
30
+ function createScaleBand(seed) {
43
31
  // @ts-expect-error, InternMap accepts two arguments, but its types are set as Map, which doesn't.
44
- let index = new _d3Array.InternMap(undefined, keyof);
45
- let domain = [];
46
- let ordinalRange = [];
47
- let r0 = 0;
48
- let r1 = 1;
32
+ let index = seed ? seed.index : new _d3Array.InternMap(undefined, keyof);
33
+ let domain = seed ? seed.domain : [];
34
+ let r0 = seed ? seed.r0 : 0;
35
+ let r1 = seed ? seed.r1 : 1;
36
+ let isRound = seed ? seed.isRound : false;
37
+ let paddingInner = seed ? seed.paddingInner : 0;
38
+ let paddingOuter = seed ? seed.paddingOuter : 0;
39
+ let align = seed ? seed.align : 0.5;
40
+
41
+ // Derived on every rescale. Positions are affine in the index, so they are computed on demand in
42
+ // `scale()` rather than materialized into an array — keeping rescale `O(1)` instead of `O(domain)`.
49
43
  let step;
50
44
  let bandwidth;
51
- let isRound = false;
52
- let paddingInner = 0;
53
- let paddingOuter = 0;
54
- let align = 0.5;
45
+ let reverse = false;
46
+ let start = 0;
55
47
  const scale = d => {
56
48
  const i = index.get(d);
57
49
  if (i === undefined) {
58
50
  return undefined;
59
51
  }
60
- return ordinalRange[i % ordinalRange.length];
52
+ const n = domain.length;
53
+ if (n === 0) {
54
+ return undefined;
55
+ }
56
+ const k = reverse ? n - 1 - i % n : i % n;
57
+ return start + step * k;
61
58
  };
62
59
  const rescale = () => {
63
60
  const n = domain.length;
64
- const reverse = r1 < r0;
65
- const start = reverse ? r1 : r0;
66
- const stop = reverse ? r0 : r1;
67
- step = (stop - start) / Math.max(1, n - paddingInner + paddingOuter * 2);
61
+ reverse = r1 < r0;
62
+ const lo = reverse ? r1 : r0;
63
+ const hi = reverse ? r0 : r1;
64
+ step = (hi - lo) / Math.max(1, n - paddingInner + paddingOuter * 2);
68
65
  if (isRound) {
69
66
  step = Math.floor(step);
70
67
  }
71
- const adjustedStart = start + (stop - start - step * (n - paddingInner)) * align;
68
+ let adjustedStart = lo + (hi - lo - step * (n - paddingInner)) * align;
72
69
  bandwidth = step * (1 - paddingInner);
73
- const finalStart = isRound ? Math.round(adjustedStart) : adjustedStart;
74
- const finalBandwidth = isRound ? Math.round(bandwidth) : bandwidth;
75
- bandwidth = finalBandwidth;
76
- const values = (0, _d3Array.range)(n).map(i => finalStart + step * i);
77
- ordinalRange = reverse ? values.reverse() : values;
70
+ if (isRound) {
71
+ adjustedStart = Math.round(adjustedStart);
72
+ bandwidth = Math.round(bandwidth);
73
+ }
74
+ start = adjustedStart;
78
75
  return scale;
79
76
  };
80
77
  scale.domain = function (_) {
@@ -149,19 +146,49 @@ function scaleBand(...args) {
149
146
  align = Math.max(0, Math.min(1, _));
150
147
  return rescale();
151
148
  };
152
- scale.copy = () => {
153
- return scaleBand(domain, [r0, r1]).round(isRound).paddingInner(paddingInner).paddingOuter(paddingOuter).align(align);
154
- };
155
149
 
156
- // Initialize from arguments
150
+ // Shares the immutable domain index with the copy, so copying does not rebuild it.
151
+ scale.copy = () => createScaleBand({
152
+ index,
153
+ domain,
154
+ r0,
155
+ r1,
156
+ isRound,
157
+ paddingInner,
158
+ paddingOuter,
159
+ align
160
+ });
161
+ rescale();
162
+ return scale;
163
+ }
164
+
165
+ /**
166
+ * Constructs a new band scale with the specified range, no padding, no rounding and center alignment.
167
+ * The domain defaults to the empty domain.
168
+ * If range is not specified, it defaults to the unit range [0, 1].
169
+ *
170
+ * The generic corresponds to the data type of domain elements.
171
+ *
172
+ * @param range A two-element array of numeric values.
173
+ */
174
+
175
+ /**
176
+ * Constructs a new band scale with the specified domain and range, no padding, no rounding and center alignment.
177
+ *
178
+ * The generic corresponds to the data type of domain elements.
179
+ *
180
+ * @param domain Array of domain values.
181
+ * @param range A two-element array of numeric values.
182
+ */
183
+
184
+ function scaleBand(...args) {
185
+ const scale = createScaleBand();
157
186
  const [arg0, arg1] = args;
158
187
  if (args.length > 1) {
159
188
  scale.domain(arg0);
160
189
  scale.range(arg1);
161
190
  } else if (arg0) {
162
191
  scale.range(arg0);
163
- } else {
164
- rescale();
165
192
  }
166
193
  return scale;
167
194
  }