-
Notifications
You must be signed in to change notification settings - Fork 730
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #773 from kristw/kristw--axis
feat: update vx/axis types
- Loading branch information
Showing
36 changed files
with
634 additions
and
620 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,216 +1,86 @@ | ||
import React from 'react'; | ||
import cx from 'classnames'; | ||
import { Line } from '@vx/shape'; | ||
import { Point } from '@vx/point'; | ||
import { Group } from '@vx/group'; | ||
import { Text } from '@vx/text'; | ||
import center from '../utils/center'; | ||
import getLabelTransform from '../utils/labelTransform'; | ||
import ORIENT from '../constants/orientation'; | ||
import toString from '../utils/toString'; | ||
import toNumberOrUndefined from '../utils/toNumberOrUndefined'; | ||
import { SharedAxisProps, AxisOrientation } from '../types'; | ||
import { getTicks, coerceNumber } from '@vx/scale'; | ||
import { SharedAxisProps, AxisScale } from '../types'; | ||
import AxisRenderer from './AxisRenderer'; | ||
import getTickPosition from '../utils/getTickPosition'; | ||
import getTickFormatter from '../utils/getTickFormatter'; | ||
import createPoint from '../utils/createPoint'; | ||
import Orientation from '../constants/orientation'; | ||
|
||
export type AxisProps<ScaleInput> = SharedAxisProps<ScaleInput> & { | ||
orientation?: AxisOrientation; | ||
export type AxisProps<Scale extends AxisScale> = SharedAxisProps<Scale> & { | ||
orientation?: Orientation; | ||
}; | ||
|
||
export default function Axis<ScaleInput>({ | ||
children, | ||
export default function Axis<Scale extends AxisScale>({ | ||
children = AxisRenderer, | ||
axisClassName, | ||
axisLineClassName, | ||
hideAxisLine = false, | ||
hideTicks = false, | ||
hideZero = false, | ||
label = '', | ||
labelClassName, | ||
labelOffset = 14, | ||
labelProps = { | ||
textAnchor: 'middle', | ||
fontFamily: 'Arial', | ||
fontSize: 10, | ||
fill: '#222', | ||
}, | ||
left = 0, | ||
numTicks = 10, | ||
orientation = ORIENT.bottom, | ||
orientation = Orientation.bottom, | ||
rangePadding = 0, | ||
scale, | ||
stroke = '#222', | ||
strokeWidth = 1, | ||
strokeDasharray, | ||
tickClassName, | ||
tickFormat, | ||
tickLabelProps = (/** tickValue, index */) => ({ | ||
textAnchor: 'middle', | ||
fontFamily: 'Arial', | ||
fontSize: 10, | ||
fill: '#222', | ||
}), | ||
tickLength = 8, | ||
tickStroke = '#222', | ||
tickTransform, | ||
tickValues, | ||
tickComponent, | ||
top = 0, | ||
}: AxisProps<ScaleInput>) { | ||
const values = | ||
tickValues || | ||
(scale.ticks | ||
? scale.ticks(numTicks) | ||
: scale | ||
.domain() | ||
.filter( | ||
(_, index, arr) => | ||
numTicks == null || | ||
arr.length <= numTicks || | ||
index % Math.round((arr.length - 1) / numTicks) === 0, | ||
)); | ||
const format = tickFormat || (scale.tickFormat ? scale.tickFormat() : toString); | ||
...restProps | ||
}: AxisProps<Scale>) { | ||
const format = tickFormat ?? getTickFormatter(scale); | ||
|
||
const range = scale.range(); | ||
const range0 = Number(range[0]) + 0.5 - rangePadding; | ||
const range1 = Number(range[range.length - 1]) + 0.5 + rangePadding; | ||
const isLeft = orientation === Orientation.left; | ||
const isTop = orientation === Orientation.top; | ||
const horizontal = isTop || orientation === Orientation.bottom; | ||
|
||
const isLeft = orientation === ORIENT.left; | ||
const isTop = orientation === ORIENT.top; | ||
const axisIsHorizontal = isTop || orientation === ORIENT.bottom; | ||
const tickPosition = getTickPosition(scale); | ||
const tickSign = isLeft || isTop ? -1 : 1; | ||
|
||
const position = center(scale.copy()); | ||
|
||
const axisFromPoint = new Point({ | ||
x: axisIsHorizontal ? range0 : 0, | ||
y: axisIsHorizontal ? 0 : range0, | ||
}); | ||
const axisToPoint = new Point({ | ||
x: axisIsHorizontal ? range1 : 0, | ||
y: axisIsHorizontal ? 0 : range1, | ||
}); | ||
const range = scale.range(); | ||
const axisFromPoint = createPoint({ x: Number(range[0]) + 0.5 - rangePadding, y: 0 }, horizontal); | ||
const axisToPoint = createPoint( | ||
{ x: Number(range[range.length - 1]) + 0.5 + rangePadding, y: 0 }, | ||
horizontal, | ||
); | ||
|
||
let tickLabelFontSize = 10; // track the max tick label size to compute label offset | ||
const ticks = (tickValues ?? getTicks(scale, numTicks)) | ||
.map((value, index) => ({ value, index })) | ||
.filter(({ value }) => !hideZero || (value !== 0 && value !== '0')) | ||
.map(({ value, index }) => { | ||
const scaledValue = coerceNumber(tickPosition(value)); | ||
|
||
if (children) { | ||
return ( | ||
<Group className={cx('vx-axis', axisClassName)} top={top} left={left}> | ||
{children({ | ||
axisFromPoint, | ||
axisToPoint, | ||
horizontal: axisIsHorizontal, | ||
tickSign, | ||
numTicks, | ||
label, | ||
rangePadding, | ||
tickLength, | ||
tickFormat: format, | ||
tickPosition: position, | ||
ticks: values.map((value, index) => { | ||
const scaledValue = toNumberOrUndefined(position(value)); | ||
const from = new Point({ | ||
x: axisIsHorizontal ? scaledValue : 0, | ||
y: axisIsHorizontal ? 0 : scaledValue, | ||
}); | ||
const to = new Point({ | ||
x: axisIsHorizontal ? scaledValue : tickSign * tickLength, | ||
y: axisIsHorizontal ? tickLength * tickSign : scaledValue, | ||
}); | ||
return { | ||
value, | ||
index, | ||
from, | ||
to, | ||
formattedValue: format(value, index), | ||
}; | ||
}), | ||
})} | ||
</Group> | ||
); | ||
} | ||
return { | ||
value, | ||
index, | ||
from: createPoint({ x: scaledValue, y: 0 }, horizontal), | ||
to: createPoint({ x: scaledValue, y: tickLength * tickSign }, horizontal), | ||
formattedValue: format(value, index), | ||
}; | ||
}); | ||
|
||
return ( | ||
<Group className={cx('vx-axis', axisClassName)} top={top} left={left}> | ||
{values.map((val, index) => { | ||
if ( | ||
hideZero && | ||
((typeof val === 'number' && val === 0) || (typeof val === 'string' && val === '0')) | ||
) { | ||
return null; | ||
} | ||
const scaledValue = toNumberOrUndefined(position(val)); | ||
const tickFromPoint = new Point({ | ||
x: axisIsHorizontal ? scaledValue : 0, | ||
y: axisIsHorizontal ? 0 : scaledValue, | ||
}); | ||
const tickToPoint = new Point({ | ||
x: axisIsHorizontal ? scaledValue : tickSign * tickLength, | ||
y: axisIsHorizontal ? tickLength * tickSign : scaledValue, | ||
}); | ||
|
||
const tickLabelPropsObj = tickLabelProps(val, index); | ||
tickLabelFontSize = Math.max( | ||
tickLabelFontSize, | ||
(typeof tickLabelPropsObj.fontSize === 'number' && tickLabelPropsObj.fontSize) || 0, | ||
); | ||
|
||
const tickYCoord = tickToPoint.y + (axisIsHorizontal && !isTop ? tickLabelFontSize : 0); | ||
const formattedValue = format(val, index); | ||
return ( | ||
<Group | ||
key={`vx-tick-${val}-${index}`} | ||
className={cx('vx-axis-tick', tickClassName)} | ||
transform={tickTransform} | ||
> | ||
{!hideTicks && ( | ||
<Line | ||
from={tickFromPoint} | ||
to={tickToPoint} | ||
stroke={tickStroke} | ||
strokeLinecap="square" | ||
/> | ||
)} | ||
{tickComponent ? ( | ||
tickComponent({ | ||
...tickLabelPropsObj, | ||
x: tickToPoint.x, | ||
y: tickYCoord, | ||
formattedValue, | ||
}) | ||
) : ( | ||
<Text x={tickToPoint.x} y={tickYCoord} {...tickLabelPropsObj}> | ||
{formattedValue} | ||
</Text> | ||
)} | ||
</Group> | ||
); | ||
{children({ | ||
...restProps, | ||
axisFromPoint, | ||
axisToPoint, | ||
hideAxisLine, | ||
hideTicks, | ||
hideZero, | ||
horizontal, | ||
numTicks, | ||
orientation, | ||
rangePadding, | ||
scale, | ||
tickFormat: format, | ||
tickLength, | ||
tickPosition, | ||
tickSign, | ||
ticks, | ||
})} | ||
|
||
{!hideAxisLine && ( | ||
<Line | ||
className={cx('vx-axis-line', axisLineClassName)} | ||
from={axisFromPoint} | ||
to={axisToPoint} | ||
stroke={stroke} | ||
strokeWidth={strokeWidth} | ||
strokeDasharray={strokeDasharray} | ||
/> | ||
)} | ||
|
||
{label && ( | ||
<Text | ||
className={cx('vx-axis-label', labelClassName)} | ||
{...getLabelTransform({ | ||
labelOffset, | ||
labelProps, | ||
orientation, | ||
range, | ||
tickLabelFontSize, | ||
tickLength, | ||
})} | ||
{...labelProps} | ||
> | ||
{label} | ||
</Text> | ||
)} | ||
</Group> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.