-
Notifications
You must be signed in to change notification settings - Fork 715
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
new(vx-react-spring): add package + AnimatedAxis #779
Conversation
"prop-types": "^15.6.0" | ||
}, | ||
"devDependencies": { | ||
"@vx/scale": "0.0.198" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is now a dep
so don't need to duplicate it
return animatedTicks.map(({ item, key, props }, index) => { | ||
// @ts-ignore react-spring types don't handle fromX, etc. | ||
const { fromX, toX, fromY, toY, opacity } = props; | ||
const tickLabelProps = allTickLabelProps[index] ?? allTickLabelProps[0] ?? {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't come up with a great solution for this. when tick values update, there may not be a tickLabelProps
for that index.
ultimately the weirdness comes from the Axis
component trying to find the max tick label font size to correctly offset the axis label (will comment on this). maybe we should deprecate that logic and clean this up?
index, | ||
from: createPoint({ x: scaledValue, y: 0 }, horizontal), | ||
to: createPoint({ x: scaledValue, y: tickLength * tickSign }, horizontal), | ||
formattedValue: format(value, index, filteredTickValues), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
have been wanting to add this for a while (will update the PR description): we now pass all ticks as a third argument (common d3 pattern) so that users can determine if a tick is first/last, etc. with index
only you couldn't ever determine this because you may not know the exact # ticks unless you specify tickValues
axisClassName, | ||
labelOffset = 8, | ||
tickLabelProps = (/** tickValue, index */) => ({ | ||
export const bottomTickLabelProps = (/** tickValue, index */) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exporting these so that they could be used outside the component. tick label props are the primary differences between axis configurations
let tickLabelFontSize = 10; // track the max tick label size to compute label offset | ||
|
||
// compute the max tick label size to compute label offset | ||
const allTickLabelProps = ticks.map(({ value, index }) => tickLabelProps(value, index)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the jank logic I alluded to above. The label and ticks were previously coupled with this, so I updated the logic to pass an array of tickLabelProps
to ticksComponent
.
Might be worth a breaking change to remove this?
numTicksColumns={numTickColumns} | ||
/> | ||
<AnimatedAxis | ||
orientation={Orientation.bottom} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is all just indented and we use AnimatedAxis
+ specify orientation
I'd like to propose option c): create a new package |
🤯 I dig it! Noting that I think a lot of the Organizing that over time might be tricky, but I think it'll be a good forcing function to keep things modular / might converge on good patterns over time. |
8a196ef
to
ffc5235
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Coverage decreased (-0.5%) to 44.264%
Let's add some tests, could just be simple exports/defined checks.
@hshoff 🎉
I have an |
"@vx/scale": "0.0.198", | ||
"@vx/text": "0.0.198", | ||
"classnames": "^2.2.5", | ||
"prop-types": "^15.6.2" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need prop-types
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, nimbus has a parser that adds propTypes
to components during the build based on the types.
const fromLeave = ({ from, to, value }: ComputedTick<Scale>) => { | ||
const scaledValue = scale(value) ?? 0; | ||
|
||
return { | ||
fromX: horizontal | ||
? // for top/bottom scales, enter from left or right based on value | ||
scaledValue < scaleLength / 2 | ||
? minPosition | ||
: maxPosition | ||
: // for left/right scales, don't animate x | ||
from.x, | ||
// same logic as above for the `to` Point | ||
toX: horizontal ? (scaledValue < scaleLength / 2 ? minPosition : maxPosition) : to.x, | ||
// for top/bottom scales, don't animate y | ||
fromY: horizontal | ||
? // for top/bottom scales, don't animate y | ||
from.y | ||
: // for left/right scales, animate from top or bottom based on value | ||
scaledValue < scaleLength / 2 | ||
? minPosition | ||
: maxPosition, | ||
// same logic as above for the `to` Point | ||
toY: horizontal ? to.y : scaledValue < scaleLength / 2 ? minPosition : maxPosition, | ||
opacity: 0, | ||
}; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would something like this work? would be great to avoid nested ternaries
const fromLeave = ({ from, to, value }: ComputedTick<Scale>) => {
const scaledValue = scale(value) ?? 0;
const lhs = scaledValue < scaleLength / 2;
const opacity = 0;
// vertical
let fromX = from.x;
let toX = to.x;
let fromY = lhs ? minPosition : maxPosition;
let toY = lhs ? minPosition : maxPosition;
if (horizontal) {
fromX = lhs ? minPosition : maxPosition;
toX = lhs ? minPosition : maxPosition;
fromY: from.y;
toY: to.y;
}
return { opacity, fromX, toX, fromY, toY };
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚀 Enhancements
This PR adds a newAnimatedAxis
component to@vx/axis
and the neededreact-spring
dependency. The surface area is pretty minimal because onlyticks
are animated so we can fully re-useAxis/AxisBottom/etc
and override a newticksComponent
prop which defaults to the old behavior.I'm a little conflicted because generallyvx
is not opinionated on animation libraries and we don't want to bloat bundle sizes. However, the incentive for this comes from theXYChart
POC in #745 where we would like to support animation if users opt in for it. So, we could eithera) simply add the neededAxis
hooks (ticksComponent
) to enable this cleanly, and keepAnimatedAxis
inXYChart
. orb) add it to@vx/axis
to let users opt into using it. It shouldn't affect bundle sizes if consumers use tree shaking or deep importsTo make b) a bit cleaner we could consideravoid exportingAnimatedAxis
from theindex
and require a deep importaddreact-spring
as a peer-dep if we want consumers to be able to use an existing version they haveUPDATE based on feedback
@vx/axis
@vx/axis
Axis
is refactored to accept aticksComponent
which allows us to animate themvalues
totickFormat(value, index, values)
so that format logic can more easily leverage all ticks (becausenumTicks
is approximate, lib consumers do not know how many tick values exist a priori)@vx/react-spring
@vx/react-spring
that includesreact-spring
as apeerDep
and can be a home for things that depend onreact-spring
<AnimatedAxis />
and<AnimatedTicksRender />
in@vx/react-spring
vx-demo/axis
demo to use<AnimatedAxis />
Updated
/axis
demo@hshoff @kristw @techniq