diff --git a/example/src/App.tsx b/example/src/App.tsx
index d09345f..403794a 100644
--- a/example/src/App.tsx
+++ b/example/src/App.tsx
@@ -1,4 +1,4 @@
-import { Text, View } from "react-native";
+import { Button, Text, View } from "react-native";
import { StatusBar } from "expo-status-bar";
@@ -10,6 +10,7 @@ export default function App() {
Hello world!!!
+
);
diff --git a/src/__tests__/native/container-queries.test.tsx b/src/__tests__/native/container-queries.test.tsx
index 2dd262d..3ea6039 100644
--- a/src/__tests__/native/container-queries.test.tsx
+++ b/src/__tests__/native/container-queries.test.tsx
@@ -107,7 +107,7 @@ test("container query width", () => {
},
});
- expect(parent.props.style).toStrictEqual([{ width: 200 }, { width: 500 }]);
+ expect(parent.props.style).toStrictEqual({ width: 500 });
expect(child.props.style).toStrictEqual({
color: "#00f",
diff --git a/src/__tests__/native/specificity.test.tsx b/src/__tests__/native/specificity.test.tsx
index 3432a2f..b582893 100644
--- a/src/__tests__/native/specificity.test.tsx
+++ b/src/__tests__/native/specificity.test.tsx
@@ -16,10 +16,7 @@ test("inline styles", () => {
/>,
).getByTestId(testID);
- expect(component.props.style).toStrictEqual([
- { backgroundColor: "#f00" },
- { backgroundColor: "blue" },
- ]);
+ expect(component.props.style).toStrictEqual({ backgroundColor: "blue" });
});
test("specificity order", () => {
@@ -60,10 +57,7 @@ test("important - requires sorting", () => {
,
).getByTestId(testID);
- expect(component.props.style).toStrictEqual([
- { color: "#f00" },
- { color: "#00f" },
- ]);
+ expect(component.props.style).toStrictEqual({ color: "#00f" });
});
test("important - inline", () => {
@@ -79,10 +73,7 @@ test("important - inline", () => {
/>,
).getByTestId(testID);
- expect(component.props.style).toStrictEqual([
- { backgroundColor: "red" },
- { backgroundColor: "#00f" },
- ]);
+ expect(component.props.style).toStrictEqual({ backgroundColor: "#00f" });
});
test("important - modifiers", () => {
@@ -96,17 +87,11 @@ test("important - modifiers", () => {
,
).getByTestId(testID);
- expect(component.props.style).toStrictEqual([
- { color: "#f00" },
- { color: "#00f" },
- ]);
+ expect(component.props.style).toStrictEqual({ color: "#00f" });
fireEvent(component, "hoverIn");
- expect(component.props.style).toStrictEqual([
- { color: "#008000" },
- { color: "#00f" },
- ]);
+ expect(component.props.style).toStrictEqual({ color: "#00f" });
});
test("passThrough - inline", () => {
diff --git a/src/native/react/useNativeCss.ts b/src/native/react/useNativeCss.ts
index 05341c3..4386820 100644
--- a/src/native/react/useNativeCss.ts
+++ b/src/native/react/useNativeCss.ts
@@ -172,29 +172,36 @@ export function useNativeCss(
export function mappingToConfig(mapping: StyledConfiguration) {
return Object.entries(mapping).flatMap(([key, value]): Config => {
if (value === true) {
- return { source: key, target: key };
+ return {
+ source: key,
+ target: key,
+ };
} else if (value === false) {
return { source: key, target: false };
} else if (typeof value === "string") {
return { source: key, target: value.split(".") };
} else if (typeof value === "object") {
+ const nativeStyleMapping = value.nativeStyleMapping as
+ | Record
+ | undefined;
+
if (Array.isArray(value)) {
- return { source: key, target: value };
+ return { source: key, target: value, nativeStyleMapping };
}
if ("target" in value) {
if (value.target === false) {
- return { source: key, target: false };
+ return { source: key, target: false, nativeStyleMapping };
} else if (typeof value.target === "string") {
const target = value.target.split(".");
if (target.length === 1) {
- return { source: key, target: target[0]! };
+ return { source: key, target: target[0]!, nativeStyleMapping };
} else {
- return { source: key, target };
+ return { source: key, target, nativeStyleMapping };
}
} else if (Array.isArray(value.target)) {
- return { source: key, target: value.target };
+ return { source: key, target: value.target, nativeStyleMapping };
}
}
}
diff --git a/src/native/styles/index.ts b/src/native/styles/index.ts
index 1ee9b24..09ab073 100644
--- a/src/native/styles/index.ts
+++ b/src/native/styles/index.ts
@@ -49,10 +49,19 @@ export function getStyledProps(
const styledProps = state.stylesObs?.get(state.styleEffect);
for (const config of state.configs) {
- result = deepMergeConfig(config, styledProps?.normal, inline, true);
+ result = deepMergeConfig(
+ config,
+ nativeStyleMapping(config, styledProps?.normal),
+ inline,
+ true,
+ );
if (styledProps?.important) {
- result = deepMergeConfig(config, result, styledProps.important);
+ result = deepMergeConfig(
+ config,
+ result,
+ nativeStyleMapping(config, styledProps.important),
+ );
}
// Apply the handlers
@@ -122,11 +131,11 @@ function deepMergeConfig(
right: Record | undefined | null,
rightIsInline = false,
) {
- if (!config.target || !right) {
+ if (!right) {
return { ...left };
}
- let result = Object.assign({}, left, right);
+ let result = config.target ? Object.assign({}, left, right) : { ...left };
if (
right &&
@@ -140,7 +149,7 @@ function deepMergeConfig(
/**
* If target is a path, deep merge until we get to the last key
*/
- if (Array.isArray(config.target) && config.target.length > 1) {
+ if (Array.isArray(config.target)) {
for (let i = 0; i < config.target.length - 1; i++) {
const key = config.target[i];
@@ -159,11 +168,9 @@ function deepMergeConfig(
return result;
}
- const target = Array.isArray(config.target)
- ? config.target[0]
- : config.target;
+ const target = config.target;
- if (target === undefined) {
+ if (target === undefined || target === false) {
return result;
}
@@ -196,3 +203,59 @@ function deepMergeConfig(
return result;
}
+
+function nativeStyleMapping(
+ config: Config,
+ props: Record | undefined,
+) {
+ if (!config.nativeStyleMapping || !props) {
+ return props;
+ }
+
+ let source: Record | undefined;
+
+ if (typeof config.target === "string") {
+ source = props[config.target];
+ } else if (config.target === false) {
+ source = props["style"];
+ } else {
+ const tokens = [...config.target];
+ const lastToken = tokens.pop()!;
+
+ source = props;
+ for (const token of tokens) {
+ source = source[token];
+ if (!source) {
+ return props;
+ }
+ }
+
+ source = source[lastToken];
+ }
+
+ if (!source) {
+ return props;
+ }
+
+ for (const [key, path] of Object.entries(config.nativeStyleMapping)) {
+ const styleValue = source[key];
+
+ delete source[key];
+
+ if (styleValue === undefined) {
+ continue;
+ }
+
+ let target = props;
+ const tokens = path.split(".");
+ const lastToken = tokens.pop();
+ for (const token of tokens) {
+ target[token] ??= {};
+ target = target[token];
+ }
+
+ target[lastToken!] = styleValue;
+ }
+
+ return props;
+}
diff --git a/types.d.ts b/types.d.ts
index b89adc3..1ba5b85 100644
--- a/types.d.ts
+++ b/types.d.ts
@@ -16,6 +16,9 @@ declare module "@react-native/virtualized-lists" {
}
declare module "react-native" {
+ interface ButtonProps {
+ className?: string;
+ }
interface ScrollViewProps
extends ViewProps,
ScrollViewPropsIOS,