-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
[charts] Add VisibilityManager logic to allow managing series/items
#20571
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 4 commits
3485822
94740e7
8ebdb1f
7fde6c4
5b3868c
8e289c5
b1295f8
49963d2
54ca888
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,7 +11,7 @@ type BarDataset = DatasetType<number | null>; | |
| const barValueFormatter = ((v) => | ||
| v == null ? '' : v.toLocaleString()) as DefaultizedBarSeriesType['valueFormatter']; | ||
|
|
||
| const seriesProcessor: SeriesProcessor<'bar'> = (params, dataset) => { | ||
| const seriesProcessor: SeriesProcessor<'bar'> = (params, dataset, isIdentifierVisible) => { | ||
| const { seriesOrder, series } = params; | ||
| const stackingGroups = getStackingGroups(params); | ||
|
|
||
|
|
@@ -66,41 +66,101 @@ const seriesProcessor: SeriesProcessor<'bar'> = (params, dataset) => { | |
|
|
||
| const completedSeries: { | ||
| [id: string]: DefaultizedBarSeriesType & { | ||
| visibleStackedData: [number, number][]; | ||
| stackedData: [number, number][]; | ||
| }; | ||
| } = {}; | ||
|
|
||
| stackingGroups.forEach((stackingGroup) => { | ||
| const { ids, stackingOffset, stackingOrder } = stackingGroup; | ||
| // Get stacked values, and derive the domain | ||
| const stackedSeries = d3Stack<any, DatasetElementType<number | null>, SeriesId>() | ||
| .keys( | ||
| ids.map((id) => { | ||
| // Use dataKey if needed and available | ||
| const dataKey = series[id].dataKey; | ||
| return series[id].data === undefined && dataKey !== undefined ? dataKey : id; | ||
| }), | ||
| ) | ||
| const keys = ids.map((id) => { | ||
| // Use dataKey if needed and available | ||
| const dataKey = series[id].dataKey; | ||
| return series[id].data === undefined && dataKey !== undefined ? dataKey : id; | ||
| }); | ||
|
|
||
| const stackedData = d3Stack<any, DatasetElementType<number | null>, SeriesId>() | ||
| .keys(keys) | ||
| .value((d, key) => d[key] ?? 0) // defaultize null value to 0 | ||
| .order(stackingOrder) | ||
| .offset(stackingOffset)(d3Dataset); | ||
|
|
||
| // We sort the keys based on the original stacking order to ensure consistency | ||
| const idOrder = stackedData.sort((a, b) => a.index - b.index).map((s) => s.key); | ||
|
|
||
| // Compute visible stacked data | ||
| const visibleStackedData = d3Stack<any, DatasetElementType<number | null>, SeriesId>() | ||
| .keys(idOrder) | ||
| .value((d, key) => { | ||
| const keyIndex = keys.indexOf(key); | ||
| const seriesId = ids[keyIndex]; | ||
|
|
||
| if (!isIdentifierVisible?.(`${seriesId}`)) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assunme the
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, I could move it into the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't |
||
| // For hidden series, return 0 so they don't contribute to the stack | ||
| return 0; | ||
| } | ||
| return d[key] ?? 0; | ||
| }) | ||
| .offset(stackingOffset)(d3Dataset); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would assume the
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mentioned above
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would be happy if modifying "stack order" could transform France in a bicycle country :) But I need to face the reality, you've a mismatch between the series and their value ;)
https://deploy-preview-20404--material-ui-x.netlify.app/x/react-charts/stacking/#stack-order
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've create #20596 to test this case |
||
|
|
||
| // Post-process visibleStackedData to fix positions for hidden series | ||
| // Hidden series should collapse to the cumulative position of visible series before them | ||
|
||
| visibleStackedData.forEach((layer, layerIndex) => { | ||
| const key = idOrder[layerIndex]; | ||
| const keyIndex = keys.indexOf(key); | ||
| const seriesId = ids[keyIndex]; | ||
|
|
||
| if (!isIdentifierVisible?.(`${seriesId}`)) { | ||
| layer.forEach((point, pointIndex) => { | ||
| // Get the original value to determine if it's negative or positive | ||
| const originalValue = d3Dataset[pointIndex]?.[key] ?? 0; | ||
| const isNegative = originalValue < 0; | ||
|
|
||
| // Calculate the cumulative sum of all visible series before this one | ||
| // Only accumulate values with the same sign | ||
JCQuintas marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| let cumulativeSum = 0; | ||
| for (let i = 0; i < layerIndex; i += 1) { | ||
| const prevKey = idOrder[i]; | ||
| const prevKeyIndex = keys.indexOf(prevKey); | ||
| const prevSeriesId = ids[prevKeyIndex]; | ||
|
|
||
| if (isIdentifierVisible?.(`${prevSeriesId}`)) { | ||
| const value = d3Dataset[pointIndex]?.[prevKey] ?? 0; | ||
| const isPrevNegative = value < 0; | ||
|
|
||
| // Only accumulate if both have the same sign | ||
| if (isNegative === isPrevNegative) { | ||
| cumulativeSum += value; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Set both start and end to the cumulative position (zero height/width) | ||
| point[0] = cumulativeSum; | ||
| point[1] = cumulativeSum; | ||
| }); | ||
| } | ||
| }); | ||
|
|
||
| ids.forEach((id, index) => { | ||
| const dataKey = series[id].dataKey; | ||
| const data = dataKey | ||
| ? dataset!.map((d) => { | ||
| const value = d[dataKey]; | ||
| return typeof value === 'number' ? value : null; | ||
| }) | ||
| : series[id].data!; | ||
| const hidden = !isIdentifierVisible?.(`${id}`); | ||
| completedSeries[id] = { | ||
| layout: 'vertical', | ||
| labelMarkType: 'square', | ||
| minBarSize: 0, | ||
| valueFormatter: series[id].valueFormatter ?? barValueFormatter, | ||
| ...series[id], | ||
| data: dataKey | ||
| ? dataset!.map((data) => { | ||
| const value = data[dataKey]; | ||
|
|
||
| return typeof value === 'number' ? value : null; | ||
| }) | ||
| : series[id].data!, | ||
| stackedData: stackedSeries[index].map(([a, b]) => [a, b]), | ||
| data, | ||
| hidden, | ||
| stackedData: stackedData[index] as [number, number][], | ||
| visibleStackedData: visibleStackedData[index] as [number, number][], | ||
| }; | ||
| }); | ||
| }); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,6 +75,7 @@ export function useBarPlotData( | |
| dataIndex, | ||
| numberOfGroups: stackingGroups.length, | ||
| groupIndex, | ||
| isSeriesVisible: !series[seriesId].hidden, | ||
| }); | ||
|
|
||
| if (barDimensions == null) { | ||
|
|
@@ -86,6 +87,8 @@ export function useBarPlotData( | |
| const result = { | ||
| seriesId, | ||
| dataIndex, | ||
| layout: series[seriesId].layout, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need the layout here? It doesn't seem to be in the types.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think a few of your comments can be answered by the description 😆
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, makes sense, but I don't know which parts 😄 |
||
| hidden: series[seriesId].hidden, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this being used? Couldn't find it. It seems it's enough to provide it to |
||
| ...barDimensions, | ||
| color: colorGetter(dataIndex), | ||
| value: series[seriesId].data[dataIndex], | ||
|
|
||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about why this is needed. For me D3 takes care of the ordering with
stackingOrderThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have to use the exact same order as
stackedData.IIRC the issue was that some ordering functions change the order based on value, so if we have value
0for a hidden series it would be ordered first/last in the stack.