Skip to content

Commit

Permalink
Cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelnesen committed Dec 10, 2024
1 parent 0fcefb4 commit 0fdfa56
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ export function Chart({
)}: ${yAxisOptions.labelFormatter(dataPoint.value)}`}
barHeight={barHeight}
barWidth={barWidth}
drawableHeight={drawableHeight}
index={index}
isLast={isLast}
onMouseEnter={(index) => setTooltipIndex(index)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {Fragment} from 'react';
import {Fragment, useMemo} from 'react';
import type {ScaleBand} from 'd3-scale';
import {estimateStringWidth, useChartContext} from '@shopify/polaris-viz-core';

import {LINE_HEIGHT} from '../../../constants';
import {estimateStringWidthWithOffset} from '../../../utilities';
Expand All @@ -18,6 +19,8 @@ const VALUE_FONT_SIZE = 11;
const TEXT_COLOR = 'rgba(31, 33, 36, 1)';
const VALUE_COLOR = 'rgba(97, 97, 97, 1)';

const REDUCED_FONT_SIZE = 11;

export interface FunnelChartXAxisLabelsProps {
formattedValues: string[];
labels: string[];
Expand All @@ -35,8 +38,19 @@ export function FunnelChartXAxisLabels({
xScale,
shouldApplyScaling,
}: FunnelChartXAxisLabelsProps) {
const {characterWidths} = useChartContext();
const targetWidth = labelWidth - GROUP_OFFSET * 3;

const labelFontSize = useMemo(() => {
// Find the widest label
const maxLabelWidth = Math.max(
...labels.map((label) => estimateStringWidth(label, characterWidths)),
);

// If any label is too wide, reduce font size for all
return maxLabelWidth > labelWidth ? REDUCED_FONT_SIZE : LABEL_FONT_SIZE;
}, [labels, characterWidths, labelWidth]);

return (
<Fragment>
{labels.map((label, index) => {
Expand All @@ -49,6 +63,15 @@ export function FunnelChartXAxisLabels({
PERCENT_FONT_WEIGHT,
);

const formattedValueWidth = estimateStringWidthWithOffset(
formattedValues[index],
VALUE_FONT_SIZE,
);

const totalPercentAndValueWidth = percentWidth + formattedValueWidth;
const shouldShowFormattedValue =
totalPercentAndValueWidth < targetWidth;

return (
<g
transform={`translate(${
Expand All @@ -66,7 +89,7 @@ export function FunnelChartXAxisLabels({
text={label}
targetWidth={targetWidth}
textAnchor="start"
fontSize={LABEL_FONT_SIZE}
fontSize={labelFontSize}
x={showScaleIcon ? 20 : 0}
/>

Expand All @@ -79,15 +102,17 @@ export function FunnelChartXAxisLabels({
fontSize={PERCENT_FONT_SIZE}
fontWeight={PERCENT_FONT_WEIGHT}
/>
<SingleTextLine
color={VALUE_COLOR}
text={formattedValues[index]}
targetWidth={targetWidth}
x={percentWidth + LINE_PADDING}
y={1}
textAnchor="start"
fontSize={VALUE_FONT_SIZE}
/>
{shouldShowFormattedValue && (
<SingleTextLine
color={VALUE_COLOR}
text={formattedValues[index]}
targetWidth={targetWidth}
x={percentWidth + LINE_PADDING}
y={1}
textAnchor="start"
fontSize={VALUE_FONT_SIZE}
/>
)}
</g>
</g>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import type {ReactNode} from 'react';
import {Fragment, useRef} from 'react';
import type {SpringValue} from '@react-spring/web';
import {useSpring, animated} from '@react-spring/web';
import {getRoundedRectPath} from '@shopify/polaris-viz-core';
import {useSpring} from '@react-spring/web';
import {useChartContext} from '@shopify/polaris-viz-core';

import {useBarSpringConfig} from '../../../hooks/useBarSpringConfig';

import {InteractiveOverlay} from './components/InteractiveOverlay';
import {ScaledSegment} from './components/ScaledSegment';
import {FUNNEL_CHART_SEGMENT_FILL, BORDER_RADIUS} from './constants';
import {AnimatedSegment} from './components/AnimatedSegment';

interface Props {
ariaLabel: string;
barHeight: number;
barWidth: number;
children: ReactNode;
drawableHeight: number;
index: number;
isLast: boolean;
onMouseEnter?: (index: number) => void;
Expand All @@ -29,7 +27,6 @@ export function FunnelChartSegment({
barHeight,
barWidth,
children,
drawableHeight,
index = 0,
isLast,
onMouseEnter,
Expand All @@ -38,7 +35,11 @@ export function FunnelChartSegment({
x,
}: Props) {
const mounted = useRef(false);
const {containerBounds} = useChartContext();
const isFirst = index === 0;
const {height: drawableHeight} = containerBounds ?? {
height: 0,
};

const springConfig = useBarSpringConfig({
animationDelay: index * 150,
Expand All @@ -59,7 +60,6 @@ export function FunnelChartSegment({
<ScaledSegment
barHeight={barHeight}
barWidth={barWidth}
drawableHeight={drawableHeight}
index={index}
isLast={isLast}
onMouseEnter={onMouseEnter}
Expand All @@ -77,7 +77,6 @@ export function FunnelChartSegment({
animatedHeight={animatedHeight}
ariaLabel={ariaLabel}
barWidth={barWidth}
drawableHeight={drawableHeight}
isFirst={isFirst}
isLast={isLast}
x={x}
Expand All @@ -95,47 +94,3 @@ export function FunnelChartSegment({
</Fragment>
);
}

interface AnimatedSegmentProps {
animatedHeight: SpringValue<number>;
ariaLabel: string;
barWidth: number;
drawableHeight: number;
isFirst: boolean;
isLast: boolean;
x: number;
}

function AnimatedSegment({
animatedHeight,
ariaLabel,
barWidth,
drawableHeight,
isFirst,
isLast,
x,
}: AnimatedSegmentProps) {
const borderRadius = `${isFirst ? BORDER_RADIUS : 0} ${
isLast ? BORDER_RADIUS : 0
} 0 0`;

return (
<animated.path
aria-label={ariaLabel}
d={animatedHeight.to((value: number) =>
getRoundedRectPath({
height: value,
width: barWidth,
borderRadius,
}),
)}
fill={FUNNEL_CHART_SEGMENT_FILL}
style={{
transform: animatedHeight.to(
(value: number) => `translate(${x}px, ${drawableHeight - value}px)`,
),
}}
width={barWidth}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {getRoundedRectPath, useChartContext} from '@shopify/polaris-viz-core';
import type {SpringValue} from '@react-spring/web';
import {animated} from '@react-spring/web';

import {FUNNEL_CHART_SEGMENT_FILL, BORDER_RADIUS} from '../constants';

interface AnimatedSegmentProps {
animatedHeight: SpringValue<number>;
ariaLabel: string;
barWidth: number;
isFirst: boolean;
isLast: boolean;
x: number;
}

export function AnimatedSegment({
animatedHeight,
ariaLabel,
barWidth,
isFirst,
isLast,
x,
}: AnimatedSegmentProps) {
const {containerBounds} = useChartContext();
const {height: drawableHeight} = containerBounds ?? {
height: 0,
};
const borderRadius = `${isFirst ? BORDER_RADIUS : 0} ${
isLast ? BORDER_RADIUS : 0
} 0 0`;

return (
<animated.path
aria-label={ariaLabel}
d={animatedHeight.to((value: number) =>
getRoundedRectPath({
height: value,
width: barWidth,
borderRadius,
}),
)}
fill={FUNNEL_CHART_SEGMENT_FILL}
style={{
transform: animatedHeight.to(
(value: number) => `translate(${x}px, ${drawableHeight - value}px)`,
),
}}
width={barWidth}
/>
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {ReactNode} from 'react';
import {Fragment} from 'react';
import {getRoundedRectPath} from '@shopify/polaris-viz-core';
import {getRoundedRectPath, useChartContext} from '@shopify/polaris-viz-core';

import {InteractiveOverlay} from '../components/InteractiveOverlay';
import {
Expand Down Expand Up @@ -40,7 +40,6 @@ interface Props extends InteractionHandlers {
index: number;
isLast: boolean;
x: number;
drawableHeight: number;
children: ReactNode;
}

Expand All @@ -50,11 +49,15 @@ export function ScaledSegment({
index,
isLast,
x,
drawableHeight,
onMouseEnter,
onMouseLeave,
children,
}: Props) {
const {containerBounds} = useChartContext();
const {width: drawableWidth, height: drawableHeight} = containerBounds ?? {
height: 0,
width: 0,
};
const topSegmentHeight = calculateTopSegmentHeight(barHeight);
const isFirst = index === 0;
const dimensions: Dimensions = {
Expand All @@ -77,7 +80,7 @@ export function ScaledSegment({
/>
);

const scaleSize = calculateResponsiveScale(drawableHeight);
const scaleSize = calculateResponsiveScale(drawableHeight, drawableWidth);
const calculateScaleHeight = () =>
scaleSize * FUNNEL_SEGMENT.scaleHeightMultiplier;

Expand Down Expand Up @@ -149,8 +152,12 @@ export function ScaledSegment({
const calculateTopSegmentHeight = (height: number) =>
Math.floor(height * FUNNEL_SEGMENT.topSegmentHeightRatio);

const calculateResponsiveScale = (drawableHeight: number) => {
// Scale will be 1.5% of the drawable height, with a minimum of 2px
const scale = Math.max(drawableHeight * 0.015, 2);
const calculateResponsiveScale = (
drawableHeight: number,
drawableWidth: number,
) => {
const heightScale = drawableHeight * 0.015;
const widthScale = drawableWidth * 0.005;
const scale = Math.max((heightScale + widthScale) / 2, 2);
return scale;
};

0 comments on commit 0fdfa56

Please sign in to comment.