* Parse all c:ser elements from a chart type node into SeriesData[]. * Results are sorted by c:order to match the intended series sequence.
(chartTypeNode: SafeXmlNode, ctx: RenderContext)
| 571 | * Results are sorted by c:order to match the intended series sequence. |
| 572 | */ |
| 573 | function parseSeries(chartTypeNode: SafeXmlNode, ctx: RenderContext): SeriesData[] { |
| 574 | const seriesArr: SeriesData[] = [] |
| 575 | |
| 576 | for (const ser of chartTypeNode.children('ser')) { |
| 577 | const tx = ser.child('tx') |
| 578 | const name = extractSeriesName(tx) |
| 579 | const order = ser.child('order').numAttr('val') ?? seriesArr.length |
| 580 | |
| 581 | const cat = ser.child('cat') |
| 582 | const categories = extractStringValues(cat) |
| 583 | |
| 584 | const val = ser.child('val') |
| 585 | const values = extractNumericValues(val) |
| 586 | const formatCode = extractFormatCode(val) |
| 587 | |
| 588 | // Scatter charts use xVal / yVal instead of cat / val |
| 589 | const xValNode = ser.child('xVal') |
| 590 | const yValNode = ser.child('yVal') |
| 591 | let xValues: number[] | undefined |
| 592 | if (yValNode.exists()) { |
| 593 | // yVal overrides val for scatter |
| 594 | const yVals = extractNumericValues(yValNode) |
| 595 | if (yVals.length > 0) { |
| 596 | values.length = 0 |
| 597 | values.push(...yVals) |
| 598 | } |
| 599 | } |
| 600 | if (xValNode.exists()) { |
| 601 | xValues = extractNumericValues(xValNode) |
| 602 | // If categories are empty but xVal exists as strings, try that |
| 603 | if (categories.length === 0) { |
| 604 | const xCats = extractStringValues(xValNode) |
| 605 | if (xCats.length > 0) categories.push(...xCats) |
| 606 | } |
| 607 | } |
| 608 | |
| 609 | // Bubble chart sizes from c:bubbleSize |
| 610 | const bubbleSizeNode = ser.child('bubbleSize') |
| 611 | const bubbleSizes = bubbleSizeNode.exists() ? extractNumericValues(bubbleSizeNode) : undefined |
| 612 | |
| 613 | const colorHex = extractSeriesColor(ser, ctx) |
| 614 | const lineWidth = extractSeriesLineWidth(ser) |
| 615 | const dataPointColors = extractDataPointColors(ser, ctx) |
| 616 | |
| 617 | // Extract marker info (c:marker > c:symbol, c:size) |
| 618 | const marker = ser.child('marker') |
| 619 | const markerSymbol = marker.child('symbol').attr('val') |
| 620 | const markerSize = marker.child('size').numAttr('val') |
| 621 | const smooth = ser.child('smooth').attr('val') === '1' |
| 622 | |
| 623 | seriesArr.push({ |
| 624 | name, |
| 625 | order, |
| 626 | categories, |
| 627 | values, |
| 628 | xValues, |
| 629 | bubbleSizes, |
| 630 | colorHex, |
no test coverage detected