Skip to content

Commit 7cd11a8

Browse files
marklawlorCopilot
andauthored
fix: nativeStyleMapping (#214)
* fix: nativeStyleMapping incorrectly working * fix: add warning for [email protected] * Update src/native/styles/index.ts Co-authored-by: Copilot <[email protected]> * Apply suggestion from @Copilot Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
1 parent 53249fe commit 7cd11a8

File tree

6 files changed

+96
-37
lines changed

6 files changed

+96
-37
lines changed

example/src/App.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Text, View } from "react-native";
1+
import { Button, Text, View } from "react-native";
22

33
import { StatusBar } from "expo-status-bar";
44

@@ -10,6 +10,7 @@ export default function App() {
1010
<Text className="text-red-800 text-2xl font-bold animate-bounce">
1111
Hello world!!!
1212
</Text>
13+
<Button title="test2" className="text-red-500" />
1314
<StatusBar style="auto" />
1415
</View>
1516
);

src/__tests__/native/container-queries.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ test("container query width", () => {
107107
},
108108
});
109109

110-
expect(parent.props.style).toStrictEqual([{ width: 200 }, { width: 500 }]);
110+
expect(parent.props.style).toStrictEqual({ width: 500 });
111111

112112
expect(child.props.style).toStrictEqual({
113113
color: "#00f",

src/__tests__/native/specificity.test.tsx

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ test("inline styles", () => {
1616
/>,
1717
).getByTestId(testID);
1818

19-
expect(component.props.style).toStrictEqual([
20-
{ backgroundColor: "#f00" },
21-
{ backgroundColor: "blue" },
22-
]);
19+
expect(component.props.style).toStrictEqual({ backgroundColor: "blue" });
2320
});
2421

2522
test("specificity order", () => {
@@ -60,10 +57,7 @@ test("important - requires sorting", () => {
6057
<Text testID={testID} className="blue red" />,
6158
).getByTestId(testID);
6259

63-
expect(component.props.style).toStrictEqual([
64-
{ color: "#f00" },
65-
{ color: "#00f" },
66-
]);
60+
expect(component.props.style).toStrictEqual({ color: "#00f" });
6761
});
6862

6963
test("important - inline", () => {
@@ -79,10 +73,7 @@ test("important - inline", () => {
7973
/>,
8074
).getByTestId(testID);
8175

82-
expect(component.props.style).toStrictEqual([
83-
{ backgroundColor: "red" },
84-
{ backgroundColor: "#00f" },
85-
]);
76+
expect(component.props.style).toStrictEqual({ backgroundColor: "#00f" });
8677
});
8778

8879
test("important - modifiers", () => {
@@ -96,17 +87,11 @@ test("important - modifiers", () => {
9687
<Text testID={testID} className="blue red" />,
9788
).getByTestId(testID);
9889

99-
expect(component.props.style).toStrictEqual([
100-
{ color: "#f00" },
101-
{ color: "#00f" },
102-
]);
90+
expect(component.props.style).toStrictEqual({ color: "#00f" });
10391

10492
fireEvent(component, "hoverIn");
10593

106-
expect(component.props.style).toStrictEqual([
107-
{ color: "#008000" },
108-
{ color: "#00f" },
109-
]);
94+
expect(component.props.style).toStrictEqual({ color: "#00f" });
11095
});
11196

11297
test("passThrough - inline", () => {

src/native/react/useNativeCss.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,29 +172,36 @@ export function useNativeCss(
172172
export function mappingToConfig(mapping: StyledConfiguration<any>) {
173173
return Object.entries(mapping).flatMap(([key, value]): Config => {
174174
if (value === true) {
175-
return { source: key, target: key };
175+
return {
176+
source: key,
177+
target: key,
178+
};
176179
} else if (value === false) {
177180
return { source: key, target: false };
178181
} else if (typeof value === "string") {
179182
return { source: key, target: value.split(".") };
180183
} else if (typeof value === "object") {
184+
const nativeStyleMapping = value.nativeStyleMapping as
185+
| Record<string, string>
186+
| undefined;
187+
181188
if (Array.isArray(value)) {
182-
return { source: key, target: value };
189+
return { source: key, target: value, nativeStyleMapping };
183190
}
184191

185192
if ("target" in value) {
186193
if (value.target === false) {
187-
return { source: key, target: false };
194+
return { source: key, target: false, nativeStyleMapping };
188195
} else if (typeof value.target === "string") {
189196
const target = value.target.split(".");
190197

191198
if (target.length === 1) {
192-
return { source: key, target: target[0]! };
199+
return { source: key, target: target[0]!, nativeStyleMapping };
193200
} else {
194-
return { source: key, target };
201+
return { source: key, target, nativeStyleMapping };
195202
}
196203
} else if (Array.isArray(value.target)) {
197-
return { source: key, target: value.target };
204+
return { source: key, target: value.target, nativeStyleMapping };
198205
}
199206
}
200207
}

src/native/styles/index.ts

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,19 @@ export function getStyledProps(
4949
const styledProps = state.stylesObs?.get(state.styleEffect);
5050

5151
for (const config of state.configs) {
52-
result = deepMergeConfig(config, styledProps?.normal, inline, true);
52+
result = deepMergeConfig(
53+
config,
54+
nativeStyleMapping(config, styledProps?.normal),
55+
inline,
56+
true,
57+
);
5358

5459
if (styledProps?.important) {
55-
result = deepMergeConfig(config, result, styledProps.important);
60+
result = deepMergeConfig(
61+
config,
62+
result,
63+
nativeStyleMapping(config, styledProps.important),
64+
);
5665
}
5766

5867
// Apply the handlers
@@ -122,11 +131,11 @@ function deepMergeConfig(
122131
right: Record<string, any> | undefined | null,
123132
rightIsInline = false,
124133
) {
125-
if (!config.target || !right) {
134+
if (!right) {
126135
return { ...left };
127136
}
128137

129-
let result = Object.assign({}, left, right);
138+
let result = config.target ? Object.assign({}, left, right) : { ...left };
130139

131140
if (
132141
right &&
@@ -140,7 +149,7 @@ function deepMergeConfig(
140149
/**
141150
* If target is a path, deep merge until we get to the last key
142151
*/
143-
if (Array.isArray(config.target) && config.target.length > 1) {
152+
if (Array.isArray(config.target)) {
144153
for (let i = 0; i < config.target.length - 1; i++) {
145154
const key = config.target[i];
146155

@@ -159,11 +168,9 @@ function deepMergeConfig(
159168
return result;
160169
}
161170

162-
const target = Array.isArray(config.target)
163-
? config.target[0]
164-
: config.target;
171+
const target = config.target;
165172

166-
if (target === undefined) {
173+
if (target === undefined || target === false) {
167174
return result;
168175
}
169176

@@ -196,3 +203,59 @@ function deepMergeConfig(
196203

197204
return result;
198205
}
206+
207+
function nativeStyleMapping(
208+
config: Config,
209+
props: Record<string, any> | undefined,
210+
) {
211+
if (!config.nativeStyleMapping || !props) {
212+
return props;
213+
}
214+
215+
let source: Record<string, any> | undefined;
216+
217+
if (typeof config.target === "string") {
218+
source = props[config.target];
219+
} else if (config.target === false) {
220+
source = props["style"];
221+
} else {
222+
const tokens = [...config.target];
223+
const lastToken = tokens.pop()!;
224+
225+
source = props;
226+
for (const token of tokens) {
227+
source = source[token];
228+
if (!source) {
229+
return props;
230+
}
231+
}
232+
233+
source = source[lastToken];
234+
}
235+
236+
if (!source) {
237+
return props;
238+
}
239+
240+
for (const [key, path] of Object.entries(config.nativeStyleMapping)) {
241+
const styleValue = source[key];
242+
243+
delete source[key];
244+
245+
if (styleValue === undefined) {
246+
continue;
247+
}
248+
249+
let target = props;
250+
const tokens = path.split(".");
251+
const lastToken = tokens.pop();
252+
for (const token of tokens) {
253+
target[token] ??= {};
254+
target = target[token];
255+
}
256+
257+
target[lastToken!] = styleValue;
258+
}
259+
260+
return props;
261+
}

types.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ declare module "@react-native/virtualized-lists" {
1616
}
1717

1818
declare module "react-native" {
19+
interface ButtonProps {
20+
className?: string;
21+
}
1922
interface ScrollViewProps
2023
extends ViewProps,
2124
ScrollViewPropsIOS,

0 commit comments

Comments
 (0)