diff --git a/src/__tests__/vendor/tailwind.test.tsx b/src/__tests__/vendor/tailwind.test.tsx index 05cb918..fa9f12d 100644 --- a/src/__tests__/vendor/tailwind.test.tsx +++ b/src/__tests__/vendor/tailwind.test.tsx @@ -87,7 +87,7 @@ test("transition", () => { ["default-transition-duration", [[150]]], [ "default-transition-timing-function", - [[[{}, "cubic-bezier", [0.4, 0, 0.2, 1]]]], + [[[{}, "cubicBezier", [0.4, 0, 0.2, 1]]]], ], ], }); @@ -117,7 +117,7 @@ test("box-shadow", () => { [ [ {}, - "@boxShadow", + "boxShadow", [ [{}, "var", "tw-inset-shadow", 1], [{}, "var", "tw-inset-ring-shadow", 1], @@ -268,7 +268,7 @@ test("filter", () => { "tw-drop-shadow-size", [ {}, - "drop-shadow", + "dropShadow", [ 0, 3, @@ -279,7 +279,7 @@ test("filter", () => { ], [ "tw-drop-shadow", - [{}, "drop-shadow", [{}, "var", "drop-shadow-md", 1]], + [{}, "dropShadow", [{}, "var", "drop-shadow-md", 1]], ], ], }, diff --git a/src/compiler/declarations.ts b/src/compiler/declarations.ts index 2202f5b..9562270 100644 --- a/src/compiler/declarations.ts +++ b/src/compiler/declarations.ts @@ -62,12 +62,16 @@ const propertyRename: Record = { "background-image": "experimental_backgroundImage", }; +// TODO: We need a better way to handle this const unparsedRuntimeParsing = new Set([ "animation", + "border", + "box-shadow", "text-shadow", "transform", - "box-shadow", - "border", + "scale", + "rotate", + "translate", ]); const parsers: { @@ -666,22 +670,22 @@ function parseTransform( ) { builder.addDescriptor("transform", [ {}, - "@transform", + "transform", value.flatMap((t): StyleDescriptor[] => { switch (t.type) { case "perspective": - return [[{}, "@perspective", parseLength(t.value, builder)]]; + return [[{}, "perspective", parseLength(t.value, builder)]]; case "translate": return [ [ {}, - "@translateX", + "translateX", parseLengthOrCoercePercentageToRuntime(t.value[0], builder), ], [ [ {}, - "@translateY", + "translateY", parseLengthOrCoercePercentageToRuntime(t.value[1], builder), ], ], @@ -690,7 +694,7 @@ function parseTransform( return [ [ {}, - "@translateX", + "translateX", parseLengthOrCoercePercentageToRuntime(t.value, builder), ], ]; @@ -698,22 +702,22 @@ function parseTransform( return [ [ {}, - "@translateY", + "translateY", parseLengthOrCoercePercentageToRuntime(t.value, builder), ], ]; case "rotate": - return [[{}, "@rotate", parseAngle(t.value, builder)]]; + return [[{}, "rotate", parseAngle(t.value, builder)]]; case "rotateX": - return [[{}, "@rotateX", parseAngle(t.value, builder)]]; + return [[{}, "rotateX", parseAngle(t.value, builder)]]; case "rotateY": - return [[{}, "@rotateY", parseAngle(t.value, builder)]]; + return [[{}, "rotateY", parseAngle(t.value, builder)]]; case "rotateZ": - return [[{}, "@rotateZ", parseAngle(t.value, builder)]]; + return [[{}, "rotateZ", parseAngle(t.value, builder)]]; case "scale": return [ - [{}, "@scaleX", parseLength(t.value[0], builder)], - [{}, "@scaleY", parseLength(t.value[1], builder)], + [{}, "scaleX", parseLength(t.value[0], builder)], + [{}, "scaleY", parseLength(t.value[1], builder)], ]; case "scaleX": return [[{}, "scaleX", parseLength(t.value, builder)]]; @@ -915,18 +919,9 @@ export function parseUnparsedDeclaration( const args = parseUnparsed(declaration.value.value, builder, property); if (property === "animation") { - builder.addDescriptor("animation", [ - {}, - `@${toRNProperty(property)}`, - args, - ]); + builder.addDescriptor("animation", [{}, property, args]); } else { - builder.addDescriptor(property, [ - {}, - `@${toRNProperty(property)}`, - args, - 1, - ]); + builder.addDescriptor(property, [{}, toRNProperty(property), args, 1]); } } else { const value = parseUnparsed(declaration.value.value, builder, property); @@ -1040,7 +1035,7 @@ export function unparsedFunction( ): StyleFunction { return [ {}, - token.value.name, + toRNProperty(token.value.name), reduceParseUnparsed(token.value.arguments, builder, property, allowAuto), ]; } @@ -1129,7 +1124,6 @@ export function parseUnparsed( case "scaleY": case "translateX": case "translateY": - tokenOrValue.value.name = `@${tokenOrValue.value.name}`; return unparsedFunction(tokenOrValue, builder, property, allowAuto); case "blur": case "brightness": @@ -2959,7 +2953,7 @@ function parseGradient( case "linear": { return [ {}, - "@linear-gradient", + "linear-gradient", [ parseLineDirection(gradient.direction, builder), ...gradient.items.map((item) => parseGradientItem(item, builder)), @@ -3036,6 +3030,16 @@ function parseFilter( [value.type]: parseAngle(value.value, builder), } as unknown as StyleDescriptor; case "drop-shadow": + return [ + {}, + toRNProperty(value.type), + [ + parseLength(value.value.xOffset, builder), + parseLength(value.value.yOffset, builder), + parseLength(value.value.blur, builder), + parseColor(value.value.color, builder), + ], + ] as unknown as StyleDescriptor; case "url": return; } diff --git a/src/compiler/keyframes.ts b/src/compiler/keyframes.ts index bf9e2a6..cfef899 100644 --- a/src/compiler/keyframes.ts +++ b/src/compiler/keyframes.ts @@ -30,7 +30,7 @@ export function parseEasingFunction( case "cubic-bezier": return [ {}, - "cubic-bezier", + "cubicBezier", [value.x1, value.y1, value.x2, value.y2], ] as const; case "steps": diff --git a/src/compiler/stylesheet.ts b/src/compiler/stylesheet.ts index 4f9f0a8..5a2f3aa 100644 --- a/src/compiler/stylesheet.ts +++ b/src/compiler/stylesheet.ts @@ -342,7 +342,10 @@ export class StylesheetBuilder { const [delayed, usesVariables] = postProcessStyleFunction(value); rule.d ??= []; - if (value[1] === "@animation") { + if ( + value[1].startsWith("animation") || + value[1].startsWith("transition") + ) { rule.a ??= true; } diff --git a/src/jest/index.ts b/src/jest/index.ts index 612dc05..4d41ed6 100644 --- a/src/jest/index.ts +++ b/src/jest/index.ts @@ -34,8 +34,9 @@ export function registerCSS( css: string, options: CompilerOptions & { debug?: boolean } = {}, ) { - const { debug } = options; + const { debug = debugDefault } = options; const compiled = compileWithAutoDebug(css, options); + if (debug) { console.log( `Compiled:\n---\n${inspect( diff --git a/src/runtime/native/__tests__/transform.test.tsx b/src/runtime/native/__tests__/transform.test.tsx index 636b5c2..d3fd7cb 100644 --- a/src/runtime/native/__tests__/transform.test.tsx +++ b/src/runtime/native/__tests__/transform.test.tsx @@ -2,14 +2,61 @@ import { render } from "@testing-library/react-native"; import { View } from "react-native-css/components/View"; import { registerCSS, testID } from "react-native-css/jest"; -test("translate", () => { - registerCSS(`.my-class { translate: 10%; }`); - const component = render( - , - ).getByTestId(testID); - - expect(component.props.style).toStrictEqual({ - transform: [{ translateX: "10%" }, { translateY: 0 }], +describe("translate", () => { + test("parsed", () => { + registerCSS(`.my-class { translate: 10%; }`); + const component = render( + , + ).getByTestId(testID); + + expect(component.props.style).toStrictEqual({ + transform: [{ translateX: "10%" }, { translateY: 0 }], + }); + }); + + test("unparsed", () => { + registerCSS(` + :root { + --translate-x: 2; + --translate-y: 3; + } + .my-class { translate: var(--translate-x) var(--translate-y); }`); + const component = render( + , + ).getByTestId(testID); + + expect(component.props.style).toStrictEqual({ + transform: [{ translateX: 2 }, { translateY: 3 }], + }); + }); +}); + +describe("scale", () => { + test("parsed", () => { + registerCSS(`.my-class { scale: 2 3; }`); + const component = render( + , + ).getByTestId(testID); + + expect(component.props.style).toStrictEqual({ + transform: [{ scaleX: 2 }, { scaleY: 3 }], + }); + }); + + test("unparsed", () => { + registerCSS(` + :root { + --scale-x: 2; + --scale-y: 3; + } + .my-class { scale: var(--scale-x) var(--scale-y); }`); + const component = render( + , + ).getByTestId(testID); + + expect(component.props.style).toStrictEqual({ + transform: [{ scaleX: 2 }, { scaleY: 3 }], + }); }); }); @@ -77,4 +124,16 @@ describe("transform", () => { transform: [{ translateX: "20%" }], }); }); + + test("multiple", () => { + registerCSS(`.my-class { transform: translateX(10%) scaleX(2); }`); + + const component = render( + , + ).getByTestId(testID); + + expect(component.props.style).toStrictEqual({ + transform: [{ translateX: "10%" }, { scaleX: 2 }], + }); + }); }); diff --git a/src/runtime/native/react/useNativeCss.ts b/src/runtime/native/react/useNativeCss.ts index da48cf3..9458841 100644 --- a/src/runtime/native/react/useNativeCss.ts +++ b/src/runtime/native/react/useNativeCss.ts @@ -20,8 +20,8 @@ import { type Getter, type VariableContextValue, } from "../reactivity"; +import { animatedComponentFamily } from "../reanimated"; import { getStyledProps, stylesFamily } from "../styles"; -import { animatedComponentFamily } from "../styles/animation"; import { updateRules } from "./rules"; export type Config = { diff --git a/src/runtime/native/reanimated.ts b/src/runtime/native/reanimated.ts new file mode 100644 index 0000000..03a1410 --- /dev/null +++ b/src/runtime/native/reanimated.ts @@ -0,0 +1,22 @@ +import type { ComponentType } from "react"; + +import { weakFamily } from "./reactivity"; + +export const animatedComponentFamily = weakFamily( + (component: ComponentType) => { + if ( + "displayName" in component && + component.displayName?.startsWith("Animated.") + ) { + return component; + } + + const createAnimatedComponent = + // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-member-access + require("react-native-reanimated").createAnimatedComponent as ( + component: ComponentType, + ) => ComponentType; + + return createAnimatedComponent(component); + }, +); diff --git a/src/runtime/native/styles/calculate-props.ts b/src/runtime/native/styles/calculate-props.ts index 2e38fe5..c9c6aa2 100644 --- a/src/runtime/native/styles/calculate-props.ts +++ b/src/runtime/native/styles/calculate-props.ts @@ -4,7 +4,7 @@ import type { StyleDeclaration, StyleRule, } from "../../../compiler"; -import { applyValue, Specificity as S } from "../../utils"; +import { applyValue, getDeepPath, Specificity as S } from "../../utils"; import type { RenderGuard } from "../conditions/guards"; import { VAR_SYMBOL, @@ -147,9 +147,10 @@ export function applyDeclarations( * mutate the props object and not create a new one */ const originalValue = value; - value = {}; + // This needs to be a object with the [prop] so we can discover in transform arrays + value = { [prop]: true }; delayedStyles.push(() => { - if (target[prop] === value) { + if (getDeepPath(target, prop) === value) { delete target[prop]; value = resolveValue(originalValue, get, { inlineVariables, diff --git a/src/runtime/native/styles/defaults.ts b/src/runtime/native/styles/defaults.ts index 109943f..9c65cb6 100644 --- a/src/runtime/native/styles/defaults.ts +++ b/src/runtime/native/styles/defaults.ts @@ -1,5 +1,6 @@ /* eslint-disable */ export const transformKeys = new Set([ + "translate", "translateX", "translateY", "scale", diff --git a/src/runtime/native/styles/functions/animation-timing.ts b/src/runtime/native/styles/functions/animation-timing.ts new file mode 100644 index 0000000..2bd5807 --- /dev/null +++ b/src/runtime/native/styles/functions/animation-timing.ts @@ -0,0 +1,44 @@ +import type { StyleFunctionResolver } from "../resolve"; + +/* eslint-disable @typescript-eslint/no-require-imports */ +const advancedTimingFunctions: Record< + string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + () => (...args: any[]) => unknown +> = { + cubicBezier: () => { + return ( + require("react-native-reanimated") as typeof import("react-native-reanimated") + ).cubicBezier; + }, + steps: () => { + return ( + require("react-native-reanimated") as typeof import("react-native-reanimated") + ).steps; + }, +}; + +const timingFunctionResolver: StyleFunctionResolver = (resolveValue, value) => { + const name = value[1]; + const resolver = advancedTimingFunctions[name]; + + if (!resolver) { + return; + } + + const args = resolveValue(value[2]); + + if (!Array.isArray(args)) { + return; + } + + const fn = resolver(); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + const result = fn(...args); + + return result; +}; + +export const cubicBezier = timingFunctionResolver; +export const steps = timingFunctionResolver; diff --git a/src/runtime/native/styles/calc.ts b/src/runtime/native/styles/functions/calc.ts similarity index 95% rename from src/runtime/native/styles/calc.ts rename to src/runtime/native/styles/functions/calc.ts index 911c760..555679a 100644 --- a/src/runtime/native/styles/calc.ts +++ b/src/runtime/native/styles/functions/calc.ts @@ -1,6 +1,6 @@ /* eslint-disable */ -import { isStyleDescriptorArray } from "../../utils"; -import type { StyleFunctionResolver } from "./resolve"; +import { isStyleDescriptorArray } from "../../../utils"; +import type { StyleFunctionResolver } from "../resolve"; const calcPrecedence: Record = { "+": 1, diff --git a/src/runtime/native/styles/filters.ts b/src/runtime/native/styles/functions/filters.ts similarity index 93% rename from src/runtime/native/styles/filters.ts rename to src/runtime/native/styles/functions/filters.ts index f1bdd8a..672bc5e 100644 --- a/src/runtime/native/styles/filters.ts +++ b/src/runtime/native/styles/functions/filters.ts @@ -1,6 +1,6 @@ -import { isStyleDescriptorArray } from "../../utils"; -import type { StyleFunctionResolver } from "./resolve"; -import { shorthandHandler } from "./shorthand"; +import { isStyleDescriptorArray } from "../../../utils"; +import type { StyleFunctionResolver } from "../resolve"; +import { shorthandHandler } from "../shorthands/_handler"; export const blur: StyleFunctionResolver = (resolveValue, value) => { const args = resolveValue(value[2]); diff --git a/src/runtime/native/styles/functions/index.ts b/src/runtime/native/styles/functions/index.ts new file mode 100644 index 0000000..94ffe90 --- /dev/null +++ b/src/runtime/native/styles/functions/index.ts @@ -0,0 +1,5 @@ +export * from "./animation-timing"; +export * from "./calc"; +export * from "./filters"; +export * from "./platform-functions"; +export * from "./transform-functions"; diff --git a/src/runtime/native/styles/platform-functions.ts b/src/runtime/native/styles/functions/platform-functions.ts similarity index 95% rename from src/runtime/native/styles/platform-functions.ts rename to src/runtime/native/styles/functions/platform-functions.ts index c621518..57f095f 100644 --- a/src/runtime/native/styles/platform-functions.ts +++ b/src/runtime/native/styles/functions/platform-functions.ts @@ -1,6 +1,6 @@ import { PixelRatio, PlatformColor, StyleSheet } from "react-native"; -import type { StyleFunctionResolver } from "./resolve"; +import type { StyleFunctionResolver } from "../resolve"; export const platformColor: StyleFunctionResolver = (resolveValue, value) => { const color: unknown = resolveValue(value[2]); diff --git a/src/runtime/native/styles/functions/transform-functions.ts b/src/runtime/native/styles/functions/transform-functions.ts new file mode 100644 index 0000000..f6802d3 --- /dev/null +++ b/src/runtime/native/styles/functions/transform-functions.ts @@ -0,0 +1,51 @@ +import type { StyleFunctionResolver } from "../resolve"; + +export const scale: StyleFunctionResolver = (resolveValue, descriptor) => { + const values = resolveValue(descriptor[2]); + + if (Array.isArray(values)) { + const [x, y] = values as [string | number][]; + + if (values.length === 2 && x === y) { + return { scale: x }; + } else if (values.length === 2) { + return [{ scaleX: x }, { scaleY: y }]; + } else { + return { scale: x }; + } + } else { + return { scale: values }; + } +}; + +export const rotate: StyleFunctionResolver = (resolveValue, descriptor) => { + const values = resolveValue(descriptor[2]); + + if (Array.isArray(values)) { + const [x, y, z] = values as [string | number][]; + + if (values.length === 3 && x === y && x === z) { + return { rotate: values }; + } else if (values.length === 3) { + return [{ rotateX: x }, { rotateY: y }, { rotateZ: z }]; + } else if (values.length === 2) { + return [{ rotateX: x }, { rotateY: y }]; + } else { + return { rotate: values }; + } + } else { + return { rotate: values }; + } +}; + +export const translate: StyleFunctionResolver = (resolveValue, descriptor) => { + const values = resolveValue(descriptor[2]); + + if (Array.isArray(values)) { + const [x, y] = values as [string | number][]; + + return [{ translateX: x }, { translateY: y }]; + } else { + return { translateX: values }; + } +}; diff --git a/src/runtime/native/styles/resolve.ts b/src/runtime/native/styles/resolve.ts index 7383dce..7723a9e 100644 --- a/src/runtime/native/styles/resolve.ts +++ b/src/runtime/native/styles/resolve.ts @@ -7,34 +7,10 @@ import type { } from "../../../compiler"; import type { RenderGuard } from "../conditions/guards"; import { type Getter, type VariableContextValue } from "../reactivity"; -import { animation, timingFunctionResolver } from "./animation"; -import { border } from "./border"; -import { boxShadow } from "./box-shadow"; -import { calc } from "./calc"; import type { calculateProps } from "./calculate-props"; import { transformKeys } from "./defaults"; -import { - blur, - brightness, - contrast, - dropShadow, - grayscale, - hueRotate, - invert, - opacity, - saturate, - sepia, -} from "./filters"; -import { - fontScale, - getPixelSizeForLayoutSize, - hairlineWidth, - pixelRatio, - platformColor, - roundToNearestPixel, -} from "./platform-functions"; -import { textShadow } from "./text-shadow"; -import { transform } from "./transform"; +import * as functions from "./functions"; +import * as shorthands from "./shorthands"; import { em, rem, vh, vw } from "./units"; import { varResolver } from "./variables"; @@ -57,34 +33,12 @@ export type StyleResolver = ( options: ResolveValueOptions, ) => unknown; -const functions: Record = { - "@animation": animation, - "@border": border, - "@boxShadow": boxShadow, - "@textShadow": textShadow, - "@transform": transform, - "animationName": animation, - "cubic-bezier": timingFunctionResolver, - "steps": timingFunctionResolver, - "hue-rotate": hueRotate, - "drop-shadow": dropShadow, - blur, - brightness, - calc, - contrast, +const functionResolvers = { + ...shorthands, + ...functions, + animationName: shorthands.animation, em, - fontScale, - grayscale, - hairlineWidth, - invert, - opacity, - pixelRatio, - getPixelSizeForLayoutSize, - platformColor, rem, - roundToNearestPixel, - saturate, - sepia, vh, vw, }; @@ -151,8 +105,8 @@ export function resolveValue( if (name === "var") { return varResolver(simpleResolve, value, get, options); - } else if (name in functions) { - const fn = functions[name]; + } else if (name in functionResolvers) { + const fn = functionResolvers[name as keyof typeof functionResolvers]; if (typeof fn !== "function") { throw new Error(`Unknown function: ${name}`); @@ -166,11 +120,7 @@ export function resolveValue( ) as StyleDescriptor; } else if (transformKeys.has(name)) { // translate, rotate, scale, etc. - const args = value[2]; - return simpleResolve(args, castToArray); - } else if (transformKeys.has(name.slice(1))) { - // @translate, @rotate, @scale, etc. - return { [name.slice(1)]: simpleResolve(value[2], castToArray) }; + return { [name]: simpleResolve(value[2], castToArray) }; } else { let args = simpleResolve(value[2], castToArray); diff --git a/src/runtime/native/styles/shorthand.ts b/src/runtime/native/styles/shorthands/_handler.ts similarity index 91% rename from src/runtime/native/styles/shorthand.ts rename to src/runtime/native/styles/shorthands/_handler.ts index ca074a5..48debf3 100644 --- a/src/runtime/native/styles/shorthand.ts +++ b/src/runtime/native/styles/shorthands/_handler.ts @@ -1,9 +1,9 @@ /* eslint-disable */ -import { setDeepPath } from "../../utils/objects"; -import { isStyleDescriptorArray } from "../../utils/style-value"; -import { ShortHandSymbol } from "./constants"; -import { defaultValues } from "./defaults"; -import type { StyleResolver } from "./resolve"; +import { setDeepPath } from "../../../utils/objects"; +import { isStyleDescriptorArray } from "../../../utils/style-value"; +import { ShortHandSymbol } from "../constants"; +import { defaultValues } from "../defaults"; +import type { StyleResolver } from "../resolve"; type ShorthandType = | "string" diff --git a/src/runtime/native/styles/animation.ts b/src/runtime/native/styles/shorthands/animation.ts similarity index 64% rename from src/runtime/native/styles/animation.ts rename to src/runtime/native/styles/shorthands/animation.ts index 8da3f3d..4d7d765 100644 --- a/src/runtime/native/styles/animation.ts +++ b/src/runtime/native/styles/shorthands/animation.ts @@ -1,11 +1,8 @@ /* eslint-disable */ -import type { ComponentType } from "react"; - -import { applyShorthand } from "../../utils"; -import { StyleCollection } from "../injection"; -import { weakFamily } from "../reactivity"; -import type { StyleFunctionResolver } from "./resolve"; -import { shorthandHandler } from "./shorthand"; +import { applyShorthand } from "../../../utils"; +import { StyleCollection } from "../../injection"; +import type { StyleFunctionResolver } from "../resolve"; +import { shorthandHandler } from "./_handler"; const name = ["animationName", "string", "none"] as const; const delay = ["animationDelay", "number", 0] as const; @@ -61,22 +58,6 @@ export const animationShorthand = shorthandHandler( "tuples", ); -export const animatedComponentFamily = weakFamily( - (component: ComponentType) => { - if ( - "displayName" in component && - component.displayName?.startsWith("Animated.") - ) { - return component; - } - - const createAnimatedComponent = - require("react-native-reanimated").createAnimatedComponent; - - return createAnimatedComponent(component); - }, -); - export const animation: StyleFunctionResolver = ( resolveValue, value, @@ -139,43 +120,3 @@ export const animation: StyleFunctionResolver = ( return applyShorthand(animationShortHandTuples); }; - -const advancedTimingFunctions: Record< - string, - () => (...args: any[]) => unknown -> = { - "cubic-bezier": () => { - return ( - require("react-native-reanimated") as typeof import("react-native-reanimated") - ).cubicBezier; - }, - "steps": () => { - return ( - require("react-native-reanimated") as typeof import("react-native-reanimated") - ).steps; - }, -}; - -export const timingFunctionResolver: StyleFunctionResolver = ( - resolveValue, - value, -) => { - const name = value[1]; - const resolver = advancedTimingFunctions[name]; - - if (!resolver) { - return; - } - - const args = resolveValue(value[2]); - - if (!Array.isArray(args)) { - return; - } - - const fn = resolver(); - - const result = fn(...args); - - return result; -}; diff --git a/src/runtime/native/styles/border.ts b/src/runtime/native/styles/shorthands/border.ts similarity index 85% rename from src/runtime/native/styles/border.ts rename to src/runtime/native/styles/shorthands/border.ts index 56f6c7c..cefdb00 100644 --- a/src/runtime/native/styles/border.ts +++ b/src/runtime/native/styles/shorthands/border.ts @@ -1,4 +1,4 @@ -import { shorthandHandler } from "./shorthand"; +import { shorthandHandler } from "./_handler"; const width = ["borderWidth", "number"] as const; const style = ["borderStyle", "string"] as const; diff --git a/src/runtime/native/styles/box-shadow.ts b/src/runtime/native/styles/shorthands/box-shadow.ts similarity index 89% rename from src/runtime/native/styles/box-shadow.ts rename to src/runtime/native/styles/shorthands/box-shadow.ts index e6d0786..5d9f35e 100644 --- a/src/runtime/native/styles/box-shadow.ts +++ b/src/runtime/native/styles/shorthands/box-shadow.ts @@ -1,6 +1,6 @@ -import { isStyleDescriptorArray } from "../../utils"; -import type { StyleFunctionResolver } from "./resolve"; -import { shorthandHandler } from "./shorthand"; +import { isStyleDescriptorArray } from "../../../utils"; +import type { StyleFunctionResolver } from "../resolve"; +import { shorthandHandler } from "./_handler"; const color = ["color", "string"] as const; const offsetX = ["offsetX", "number"] as const; diff --git a/src/runtime/native/styles/shorthands/index.ts b/src/runtime/native/styles/shorthands/index.ts new file mode 100644 index 0000000..c9c8f33 --- /dev/null +++ b/src/runtime/native/styles/shorthands/index.ts @@ -0,0 +1,5 @@ +export * from "./animation"; +export * from "./border"; +export * from "./box-shadow"; +export * from "./text-shadow"; +export * from "./transform"; diff --git a/src/runtime/native/styles/text-shadow.ts b/src/runtime/native/styles/shorthands/text-shadow.ts similarity index 91% rename from src/runtime/native/styles/text-shadow.ts rename to src/runtime/native/styles/shorthands/text-shadow.ts index 52a8f9d..a3eb61c 100644 --- a/src/runtime/native/styles/text-shadow.ts +++ b/src/runtime/native/styles/shorthands/text-shadow.ts @@ -1,4 +1,4 @@ -import { shorthandHandler } from "./shorthand"; +import { shorthandHandler } from "./_handler"; const width = [["textShadowOffset", "width"], "number"] as const; const height = [["textShadowOffset", "height"], "number"] as const; diff --git a/src/runtime/native/styles/transform.ts b/src/runtime/native/styles/shorthands/transform.ts similarity index 91% rename from src/runtime/native/styles/transform.ts rename to src/runtime/native/styles/shorthands/transform.ts index 4fc51cb..f904622 100644 --- a/src/runtime/native/styles/transform.ts +++ b/src/runtime/native/styles/shorthands/transform.ts @@ -1,4 +1,4 @@ -import type { StyleFunctionResolver } from "./resolve"; +import type { StyleFunctionResolver } from "../resolve"; /** * Handle the unparsable transform property by converting its values into StyleDeclarations diff --git a/src/runtime/utils/objects.ts b/src/runtime/utils/objects.ts index ac7e844..6abe711 100644 --- a/src/runtime/utils/objects.ts +++ b/src/runtime/utils/objects.ts @@ -23,6 +23,8 @@ export function getDeepPath(source: any, paths: string | string[] | false) { } return target; + } else if (transformKeys.has(paths)) { + return source?.transform?.find((t: any) => t[paths] !== undefined); } else { return source?.[paths]; } @@ -63,12 +65,26 @@ export function applyValue( target.transform = []; } - const transform = target.transform.find((t: any) => t[prop] !== undefined); + const transformArray: Record[] = target.transform; + const transform = transformArray.find((t: any) => t[prop] !== undefined); + /** + * If our value is an array, this means a shorthand was split into multiple values + * e.g scale -> scaleX, scaleY + */ if (transform) { - transform[prop] = value; + if (Array.isArray(value)) { + target.transform = transformArray.filter((t) => !(prop in t)); + target.transform.push(...value); + } else { + transform[prop] = value; + } } else { - target.transform.push({ [prop]: value }); + if (Array.isArray(value)) { + transformArray.push(...value); + } else { + transformArray.push(value); + } } return; } else if (typeof value === "object" && value && ShortHandSymbol in value) {