mirror of
				https://github.com/mermaid-js/mermaid.git
				synced 2025-11-03 20:34:20 +01:00 
			
		
		
		
	dynamically resizes vertical xychart bar
Co-authored-by: pranavm2109 <mishrap@dickinson.edu>
This commit is contained in:
		@@ -50,6 +50,16 @@ export const draw = (txt: string, id: string, _version: string, diagObj: Diagram
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const groups: Record<string, any> = {};
 | 
					  const groups: Record<string, any> = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  interface BarItem {
 | 
				
			||||||
 | 
					    data: {
 | 
				
			||||||
 | 
					      x: number;
 | 
				
			||||||
 | 
					      y: number;
 | 
				
			||||||
 | 
					      width: number;
 | 
				
			||||||
 | 
					      height: number;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    label: string;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function getGroup(gList: string[]) {
 | 
					  function getGroup(gList: string[]) {
 | 
				
			||||||
    let elem = group;
 | 
					    let elem = group;
 | 
				
			||||||
    let prefix = '';
 | 
					    let prefix = '';
 | 
				
			||||||
@@ -121,33 +131,70 @@ export const draw = (txt: string, id: string, _version: string, diagObj: Diagram
 | 
				
			|||||||
              .enter()
 | 
					              .enter()
 | 
				
			||||||
              .append('text')
 | 
					              .append('text')
 | 
				
			||||||
              .attr('x', (data) => data.x + data.width - longestTextWidth - 5)
 | 
					              .attr('x', (data) => data.x + data.width - longestTextWidth - 5)
 | 
				
			||||||
              .attr('y', (data) => data.y + data.height / 2 + 1)
 | 
					              .attr('y', (data) => data.y + data.height / 2 + 0.2 * data.height)
 | 
				
			||||||
              .attr('text-anchor', 'start')
 | 
					              .attr('text-anchor', 'start')
 | 
				
			||||||
              .attr('dominant-baseline', 'middle')
 | 
					              .attr('dominant-baseline', 'middle')
 | 
				
			||||||
              .attr('fill', 'black')
 | 
					              .attr('fill', 'black')
 | 
				
			||||||
              .attr('font-size', `${uniformFontSize}px`)
 | 
					              .attr('font-size', `${uniformFontSize}px`)
 | 
				
			||||||
              .text((data, index) => labelData[index]);
 | 
					              .text((data, index) => labelData[index]);
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            // Compute candidate font sizes for each bar using width only.
 | 
					            const yOffset = 10;
 | 
				
			||||||
            const candidateFontSizes = shape.data.map((data, index) => {
 | 
					
 | 
				
			||||||
              const label = labelData[index].toString();
 | 
					            // filter out bars that have zero width or height.
 | 
				
			||||||
              return data.width / (label.length * 0.6);
 | 
					            const validItems = shape.data
 | 
				
			||||||
 | 
					              .map((d, i) => ({ data: d, label: labelData[i].toString() }))
 | 
				
			||||||
 | 
					              .filter((item) => item.data.width > 0 && item.data.height > 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Helper function that checks if the text with a given fontSize fits within the bar boundaries.
 | 
				
			||||||
 | 
					            function fitsInBar(item: BarItem, fontSize: number, yOffset: number): boolean {
 | 
				
			||||||
 | 
					              const { data, label } = item;
 | 
				
			||||||
 | 
					              const charWidthFactor = 0.7;
 | 
				
			||||||
 | 
					              const textWidth = fontSize * label.length * charWidthFactor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              // Compute horizontal boundaries using the center.
 | 
				
			||||||
 | 
					              const centerX = data.x + data.width / 2;
 | 
				
			||||||
 | 
					              const leftEdge = centerX - textWidth / 2;
 | 
				
			||||||
 | 
					              const rightEdge = centerX + textWidth / 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              // Check that text doesn't overflow horizontally.
 | 
				
			||||||
 | 
					              const horizontalFits = leftEdge >= data.x && rightEdge <= data.x + data.width;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              // For vertical placement, we use 'dominant-baseline: hanging' so that y marks the top of the text.
 | 
				
			||||||
 | 
					              // Thus, the bottom edge is y + yOffset + fontSize.
 | 
				
			||||||
 | 
					              const verticalFits = data.y + yOffset + fontSize <= data.y + data.height;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              return horizontalFits && verticalFits;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // For each valid item, start with a candidate font size based on the width,
 | 
				
			||||||
 | 
					            // then reduce it until the text fits within both the horizontal and vertical boundaries.
 | 
				
			||||||
 | 
					            const candidateFontSizes = validItems.map((item) => {
 | 
				
			||||||
 | 
					              const { data, label } = item;
 | 
				
			||||||
 | 
					              let fontSize = data.width / (label.length * 0.7);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              // Decrease the font size until the text fits or fontSize reaches 0.
 | 
				
			||||||
 | 
					              while (!fitsInBar(item, fontSize, yOffset) && fontSize > 0) {
 | 
				
			||||||
 | 
					                fontSize -= 1;
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              return fontSize;
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Use the smallest font size for uniformity.
 | 
					            // Choose the smallest candidate across all valid bars for uniformity.
 | 
				
			||||||
            const uniformFontSize = Math.floor(Math.min(...candidateFontSizes));
 | 
					            const uniformFontSize = Math.floor(Math.min(...candidateFontSizes));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Render text only for valid items.
 | 
				
			||||||
            shapeGroup
 | 
					            shapeGroup
 | 
				
			||||||
              .selectAll('text')
 | 
					              .selectAll('text')
 | 
				
			||||||
              .data(shape.data)
 | 
					              .data(validItems)
 | 
				
			||||||
              .enter()
 | 
					              .enter()
 | 
				
			||||||
              .append('text')
 | 
					              .append('text')
 | 
				
			||||||
              .attr('x', (data) => data.x + data.width / 2)
 | 
					              .attr('x', (item) => item.data.x + item.data.width / 2)
 | 
				
			||||||
              .attr('y', (data) => data.y + 25)
 | 
					              .attr('y', (item) => item.data.y + yOffset)
 | 
				
			||||||
              .attr('text-anchor', 'middle')
 | 
					              .attr('text-anchor', 'middle')
 | 
				
			||||||
 | 
					              .attr('dominant-baseline', 'hanging')
 | 
				
			||||||
              .attr('fill', 'black')
 | 
					              .attr('fill', 'black')
 | 
				
			||||||
              .attr('font-size', `${uniformFontSize}px`)
 | 
					              .attr('font-size', `${uniformFontSize}px`)
 | 
				
			||||||
              .text((data, index) => labelData[index]);
 | 
					              .text((item) => item.label);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user