Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/fix-number-regex-negative-decimals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@react-spring/shared': patch
---

fix(shared): parse negative decimals without an integer part in numberRegex

`numberRegex` required at least one digit before the decimal point, so a value like `-.0000298023` (emitted by Chrome for some `lab()`/`oklch()` colors) was split into several tokens instead of one. That inflated a keyframe's number count and threw `The arity of each "output" value must be equal` when interpolating between color formats. The regex now matches a leading-dot fraction as a single token while keeping all previously supported number formats.
2 changes: 1 addition & 1 deletion packages/shared/src/regexs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Problem: https://github.com/animatedjs/animated/pull/102
// Solution: https://stackoverflow.com/questions/638565/parsing-scientific-notation-sensibly/658662
export const numberRegex = /[+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?/g
export const numberRegex = /[+\-]?(?:(?:\d*\.\d+)|\d+)(?:[eE][+\-]?\d+)?/g

// Covers rgb, rgba, hsl, hsla
// Taken from https://gist.github.com/olmokramer/82ccce673f86db7cda5e
Expand Down
25 changes: 25 additions & 0 deletions packages/shared/src/stringInterpolation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,28 @@ it('interpolates a number-less output value instead of throwing on a null match'
expect(interpolate(0)).toBe('none')
expect(interpolate(0.5)).toBe('none')
})

// https://github.com/pmndrs/react-spring/issues/2397
// lab() values like `lab(77.96% -.0000298023 0)` contain decimals with no
// integer part and a leading minus. The old number regex split these into
// multiple tokens (`0`, `0`, `0`, `0`, `298023`) which broke arity
// checks against other keyframes.
it('interpolates colors containing negative decimals without an integer part', () => {
const interpolate = createStringInterpolator({
range: [0, 1],
output: ['lab(77.96% -.0000298023 0)', 'oklch(0.4 0.2639 271.35 / 1)'],
})

expect(() => interpolate(0.5)).not.toThrow()
expect(interpolate(1)).toBe('oklch(0.4 0.2639 271.35 / 1)')
})

it('parses a standalone negative decimal without an integer part', () => {
const interpolate = createStringInterpolator({
range: [0, 1],
output: ['translateY(-.5px)', 'translateY(10px)'],
})

expect(interpolate(0.5)).toBe('translateY(4.75px)')
expect(interpolate(1)).toBe('translateY(10px)')
})
Loading