UNPKG

@mcdevsl/superset-ui

Version:
107 lines (97 loc) 3.31 kB
import { CSSProperties } from 'react'; import { getTextDimension, Margin, Dimension } from '@superset-ui/core'; import { AxisOrient, ChannelDef, Value } from 'encodable'; import ChannelEncoderAxis from 'encodable/lib/encoders/ChannelEncoderAxis'; export interface AxisLayout { axisWidth: number; labelAngle: number; labelFlush: number | boolean; labelOffset: number; labelOverlap: 'flat' | 'rotate'; minMargin: Partial<Margin>; orient: AxisOrient; tickLabelDimensions: Dimension[]; tickLabels: string[]; tickTextAnchor?: string; } export default function computeAxisLayout<Def extends ChannelDef<Output>, Output extends Value>( axis: ChannelEncoderAxis<Def, Output>, { axisTitleHeight = 20, axisWidth, gapBetweenAxisLabelAndBorder = 4, gapBetweenTickAndTickLabel = 4, defaultTickSize = 8, tickTextStyle = {}, }: { axisTitleHeight?: number; axisWidth: number; gapBetweenAxisLabelAndBorder?: number; gapBetweenTickAndTickLabel?: number; defaultTickSize?: number; tickTextStyle?: CSSProperties; }, ): AxisLayout { const tickLabels = axis.getTickLabels(); const tickLabelDimensions = tickLabels.map((text: string) => getTextDimension({ style: tickTextStyle, text, }), ); const { labelAngle, labelFlush, labelOverlap, labelPadding, orient, tickSize = defaultTickSize, } = axis.config; const maxWidth = Math.max(...tickLabelDimensions.map(d => d.width), 0); // cheap heuristic, can improve const widthPerTick = axisWidth / tickLabels.length; const isLabelOverlap = maxWidth > widthPerTick; const labelAngleIfOverlap = labelOverlap.strategy === 'rotate' ? labelOverlap.labelAngle : 0; const labelAngleAfterOverlapCheck = isLabelOverlap ? labelAngleIfOverlap : 0; const finalLabelAngle = labelAngle === 0 ? labelAngleAfterOverlapCheck : labelAngle; const spaceForAxisTitle = axis.hasTitle() ? labelPadding + axisTitleHeight : 0; let tickTextAnchor = 'middle'; let labelOffset = 0; let requiredMargin = tickSize + gapBetweenTickAndTickLabel + spaceForAxisTitle + gapBetweenAxisLabelAndBorder; if (axis.channelEncoder.isX()) { if (finalLabelAngle === 0) { const labelHeight = tickLabelDimensions.length > 0 ? tickLabelDimensions[0].height : 0; labelOffset = labelHeight + labelPadding; requiredMargin += labelHeight; } else { const labelHeight = Math.ceil( Math.abs(maxWidth * Math.sin((finalLabelAngle * Math.PI) / 180)), ); labelOffset = labelHeight + labelPadding; requiredMargin += labelHeight; tickTextAnchor = (orient === 'top' && finalLabelAngle > 0) || (orient === 'bottom' && finalLabelAngle < 0) ? 'end' : 'start'; } requiredMargin += 8; } else { labelOffset = maxWidth + spaceForAxisTitle; requiredMargin += maxWidth; } return { axisWidth, labelAngle: finalLabelAngle, labelFlush, labelOffset, labelOverlap: isLabelOverlap ? labelOverlap.strategy : 'flat', minMargin: { [orient]: Math.ceil(requiredMargin), }, orient, tickLabelDimensions, tickLabels, tickTextAnchor, }; }