Skip to content

Commit

Permalink
Fixing tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mattgperry authored and mergatron[bot] committed Feb 13, 2024
1 parent d3b56fc commit 80d802d
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ test("mixComplex can animate from a value-less prop", () => {
})

test("mixComplex can animate from a value with extra zeros", () => {
expect(mixComplex("#000 #fff 0 0px 0px", "20px 0px")(0.5)).toBe(
"rgba(180, 180, 180, 1) 10px 0px"
expect(mixComplex("#fff 0 0px 0px", "20px 0px #000")(0.5)).toBe(
"10px 0px rgba(180, 180, 180, 1)"
)
})

Expand Down
44 changes: 37 additions & 7 deletions packages/framer-motion/src/utils/mix/complex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { pipe } from "../pipe"
import { warning } from "../errors"
import { HSLA, RGBA } from "../../value/types/types"
import { color } from "../../value/types/color"
import { analyseComplexValue, complex } from "../../value/types/complex"
import {
ComplexValueInfo,
ComplexValues,
analyseComplexValue,
complex,
} from "../../value/types/complex"

type MixableArray = Array<number | RGBA | HSLA | string>
type MixableObject = {
Expand All @@ -23,7 +28,7 @@ export function getMixer<T>(a: T) {
if (typeof a === "number") {
return mixNumber
} else if (typeof a === "string") {
if (a.startsWith("var(") || a.startsWith("url(")) {
if (a.startsWith("var(")) {
return mixImmediate
} else if (color.test(a)) {
return mixColor
Expand Down Expand Up @@ -73,21 +78,46 @@ export function mixObject(a: MixableObject, b: MixableObject) {
}
}

function matchOrder(
origin: ComplexValueInfo,
target: ComplexValueInfo
): ComplexValues {
const orderedOrigin: ComplexValues = []

const pointers = { color: 0, var: 0, number: 0 }

for (let i = 0; i < target.values.length; i++) {
const type = target.types[i]
const originIndex = origin.indexes[type][pointers[type]]
const originValue = origin.values[originIndex] ?? 0

orderedOrigin[i] = originValue

pointers[type]++
}

console.log({ orderedOrigin })

return orderedOrigin
}

export const mixComplex = (
origin: string | number,
target: string | number
) => {
const template = complex.createTransformer(target)
const originStats = analyseComplexValue(origin)
const targetStats = analyseComplexValue(target)

const canInterpolate =
originStats.values &&
targetStats.values &&
originStats.values.length === targetStats.values.length
originStats.indexes.var.length === targetStats.indexes.var.length &&
originStats.indexes.color.length === targetStats.indexes.color.length &&
originStats.indexes.number.length >= targetStats.indexes.number.length

if (canInterpolate) {
return pipe(mixArray(originStats.values, targetStats.values), template)
return pipe(
mixArray(matchOrder(originStats, targetStats), targetStats.values),
template
)
} else {
warning(
true,
Expand Down
36 changes: 18 additions & 18 deletions packages/framer-motion/src/value/types/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,34 +82,34 @@ describe("complex value type", () => {
expect(complex.parse(PATH)).toEqual(PATH_VALUES)
expect(complex.parse(GREYSCALE)).toEqual([100])
expect(complex.parse(MIXED)).toEqual([
{ red: 161, green: 0, blue: 246, alpha: 0 },
0,
0,
0,
{ red: 161, green: 0, blue: 246, alpha: 0 },
])
expect(complex.parse("0px 0px 0px rgba(161 0 246 / 0.5)")).toEqual([
{ red: 161, green: 0, blue: 246, alpha: 0.5 },
0,
0,
0,
{ red: 161, green: 0, blue: 246, alpha: 0.5 },
])
expect(complex.parse("0px 0px 0px #F00")).toEqual([
{ red: 255, green: 0, blue: 0, alpha: 1 },
0,
0,
0,
{ red: 255, green: 0, blue: 0, alpha: 1 },
])
expect(complex.parse("0px 0px 0px #F000")).toEqual([
{ red: 255, green: 0, blue: 0, alpha: 0 },
0,
0,
0,
{ red: 255, green: 0, blue: 0, alpha: 0 },
])
expect(complex.parse("0px 0px 0px #00FF0000")).toEqual([
{ red: 0, green: 255, blue: 0, alpha: 0 },
0,
0,
0,
{ red: 0, green: 255, blue: 0, alpha: 0 },
])
})

Expand All @@ -122,15 +122,15 @@ describe("complex value type", () => {
)
expect(
transformMixedExpo([
0,
1.5999999547489097e-8,
3.199999909497819e-8,
{
red: 161,
green: 0,
blue: 246,
alpha: 6.399999974426862e-10,
},
0,
1.5999999547489097e-8,
3.199999909497819e-8,
])
).toBe("0px 0px 0px rgba(161, 0, 246, 0)")

Expand Down Expand Up @@ -410,59 +410,60 @@ describe("combination values", () => {

it("should parse into an array", () => {
expect(complex.parse("0px 10px #fff")).toEqual([
{ red: 255, green: 255, blue: 255, alpha: 1 },
0,
10,
{ red: 255, green: 255, blue: 255, alpha: 1 },
])
expect(complex.parse("20px 20px 10px inset #fff")).toEqual([
{ red: 255, green: 255, blue: 255, alpha: 1 },
20,
20,
10,
{ red: 255, green: 255, blue: 255, alpha: 1 },
])
expect(
complex.parse("20px 20px 10px inset rgba(255, 255, 255, 1)")
).toEqual([{ red: 255, green: 255, blue: 255, alpha: 1 }, 20, 20, 10])
).toEqual([20, 20, 10, { red: 255, green: 255, blue: 255, alpha: 1 }])
expect(
complex.parse(
"20px 20px 10px inset #fff, 20px 20px 10px inset rgba(255, 255, 255, 1)"
)
).toEqual([
{ red: 255, green: 255, blue: 255, alpha: 1 },
{ red: 255, green: 255, blue: 255, alpha: 1 },
20,
20,
10,
{ red: 255, green: 255, blue: 255, alpha: 1 },
20,
20,
10,
{ red: 255, green: 255, blue: 255, alpha: 1 },
])
expect(complex.parse("linear-gradient(0.25turn, #fff)")).toEqual([
0.25,
{
red: 255,
green: 255,
blue: 255,
alpha: 1,
},
0.25,
])
expect(
complex.parse("linear-gradient(1deg, rgba(255, 255, 255, 1))")
).toEqual([
1,
{
red: 255,
green: 255,
blue: 255,
alpha: 1,
},
1,
])

expect(
complex.parse(
"linear-gradient(217deg, rgba(255,0,0,.8), rgba(255,0,0,0) 70.71%)"
)
).toEqual([
217,
{
red: 255,
green: 0,
Expand All @@ -475,7 +476,6 @@ describe("combination values", () => {
blue: 0,
alpha: 0,
},
217,
70.71,
])

Expand All @@ -484,10 +484,10 @@ describe("combination values", () => {
"radial-gradient(circle at 50% 25%, #e66465, #9198e5)"
)
).toEqual([
{ alpha: 1, blue: 101, green: 100, red: 230 },
{ alpha: 1, blue: 229, green: 152, red: 145 },
50,
25,
{ alpha: 1, blue: 101, green: 100, red: 230 },
{ alpha: 1, blue: 229, green: 152, red: 145 },
])
})

Expand Down
61 changes: 45 additions & 16 deletions packages/framer-motion/src/value/types/complex/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CSSVariableToken } from "../../../render/dom/utils/is-css-variable"
import { isNumericalString } from "../../../utils/is-numerical-string"
import { color } from "../color"
import { Color } from "../types"
import { colorRegex, floatRegex, isString, sanitize } from "../utils"

Expand All @@ -13,11 +13,19 @@ function test(v: any) {
)
}

type ComplexValues = Array<CSSVariableToken | Color | number>
export type ComplexValues = Array<CSSVariableToken | string | number | Color>

export type ValueIndexes = {
color: number[]
number: number[]
var: number[]
}

export interface ComplexValueInfo {
values: ComplexValues
split: string[]
indexes: ValueIndexes
types: Array<keyof ValueIndexes>
}

const complexRegex =
Expand All @@ -28,47 +36,68 @@ const splitToken = "${}"
export function analyseComplexValue(value: string | number): ComplexValueInfo {
const originalValue = value.toString()

const matchedValues = originalValue.match(complexRegex)
const values: ComplexValues =
matchedValues === null ? [] : (matchedValues as ComplexValues)

/**
* match() returns strings - convert numerical strings to actual numbers.
*/
for (let i = 0; i < values?.length; i++) {
if (isNumericalString(values[i] as string)) {
values[i] = parseFloat(values[i] as string)
const matchedValues = originalValue.match(complexRegex) || []
const values: ComplexValues = []
const indexes: ValueIndexes = {
color: [],
number: [],
var: [],
}
const types: Array<keyof ValueIndexes> = []

for (let i = 0; i < matchedValues.length; i++) {
const parsedValue: string | number = matchedValues[i]

if (color.test(parsedValue)) {
indexes.color.push(i)
types.push("color")
values.push(color.parse(parsedValue))
} else if (parsedValue.startsWith("var(")) {
indexes.var.push(i)
types.push("var")
values.push(parsedValue)
} else {
indexes.number.push(i)
types.push("number")
values.push(parseFloat(parsedValue))
}
}

const tokenised = originalValue.replace(complexRegex, splitToken)
const split = tokenised.split(splitToken)

return { values, split }
return { values, split, indexes, types }
}

function parseComplexValue(v: string | number) {
return analyseComplexValue(v).values
}

function createTransformer(source: string | number) {
const { split } = analyseComplexValue(source)
const { split, types } = analyseComplexValue(source)

const numSections = split.length
return (v: Array<CSSVariableToken | Color | number | string>) => {
let output = ""
for (let i = 0; i < numSections; i++) {
output += split[i]
if (v[i] !== undefined) {
output += typeof v === "number" ? sanitize(v[i]) : v[i]
const type = types[i]
if (type === "number") {
output += sanitize(v[i] as number)
} else if (type === "color") {
output += color.transform(v[i] as Color)
} else {
output += v[i]
}
}
}

return output
}
}

const convertNumbersToZero = (v: number | Color) =>
const convertNumbersToZero = (v: number | string) =>
typeof v === "number" ? 0 : v

function getAnimatableNone(v: string | number) {
Expand Down
2 changes: 1 addition & 1 deletion packages/framer-motion/src/value/types/numbers/units.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const createUnitType = (unit: string) => ({
test: (v: string | number) =>
isString(v) && v.endsWith(unit) && v.split(" ").length === 1,
parse: parseFloat,
transform: (v: number | string) => v + unit,
transform: (v: number | string) => `${v}${unit}`,
})

export const degrees = createUnitType("deg")
Expand Down

0 comments on commit 80d802d

Please sign in to comment.