Skip to content

Commit 030e9c7

Browse files
authored
Merge pull request #411 from jpudysz/feature/variants
fix: nested variants in pressables
2 parents 977099c + 119166c commit 030e9c7

File tree

10 files changed

+73
-18
lines changed

10 files changed

+73
-18
lines changed

cxx/common/Helpers.h

+10
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@ inline Variants variantsToPairs(jsi::Runtime& rt, jsi::Object&& variants) {
127127
return pairs;
128128
}
129129

130+
inline jsi::Object pairsToVariantsValue(jsi::Runtime& rt, Variants& pairs) {
131+
auto variantsValue = jsi::Object(rt);
132+
133+
std::for_each(pairs.begin(), pairs.end(), [&rt, &variantsValue](std::pair<std::string, std::string>& pair){
134+
variantsValue.setProperty(rt, jsi::PropNameID::forUtf8(rt, pair.first), jsi::String::createFromUtf8(rt, pair.second));
135+
});
136+
137+
return variantsValue;
138+
}
139+
130140
inline jsi::Object variantsToValue(jsi::Runtime& rt, Variants& variants) {
131141
jsi::Object rawVariants = jsi::Object(rt);
132142

cxx/core/HostStyle.cpp

+12-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,18 @@ jsi::Function HostStyle::createAddVariantsProxyFunction(jsi::Runtime& rt) {
4545
helpers::assertThat(rt, count == 1, "Unistyles: useVariants expected to be called with one argument.");
4646
helpers::assertThat(rt, arguments[0].isObject(), "Unistyles: useVariants expected to be called with object.");
4747

48-
this->_variants = helpers::variantsToPairs(rt, arguments[0].asObject(rt));
48+
auto parser = parser::Parser(this->_unistylesRuntime);
49+
auto pairs = helpers::variantsToPairs(rt, arguments[0].asObject(rt));
50+
51+
if (this->hasVariantsSet && pairs == this->_variants) {
52+
return jsi::Value::undefined();
53+
}
54+
55+
// new variants or empty variants
56+
this->hasVariantsSet = true;
57+
this->_variants = pairs;
58+
59+
parser.rebuildUnistylesWithVariants(rt, this->_styleSheet, this->_variants);
4960

5061
return jsi::Value::undefined();
5162
});

cxx/core/HostStyle.h

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ struct JSI_EXPORT HostStyle : public jsi::HostObject {
2121
jsi::Function createAddVariantsProxyFunction(jsi::Runtime& rt);
2222

2323
private:
24+
// to additionally distinguish between empty variants
25+
// that are set at the beginning
26+
bool hasVariantsSet = false;
27+
2428
std::shared_ptr<StyleSheet> _styleSheet;
2529
std::shared_ptr<HybridUnistylesRuntime> _unistylesRuntime;
2630
std::vector<std::pair<std::string, std::string>> _variants{};

cxx/core/UnistyleWrapper.h

+26-9
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,27 @@ customStyleProp={[styles.container, styles.otherProp]}
5858
Copying a Unistyle style outside of a JSX element will remove its internal C++ state, leading to unexpected behavior.)");
5959
}
6060

61-
inline static jsi::Object generateUnistylesPrototype(jsi::Runtime& rt, Unistyle::Shared unistyle) {
61+
inline static jsi::Object generateUnistylesPrototype(
62+
jsi::Runtime& rt,
63+
std::shared_ptr<HybridUnistylesRuntime> unistylesRuntime,
64+
Unistyle::Shared unistyle,
65+
std::optional<Variants> variants,
66+
std::optional<jsi::Array> arguments
67+
) {
6268
// add prototype metadata for createUnistylesComponent
6369
auto proto = jsi::Object(rt);
70+
auto hostFn = jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forUtf8(rt, "getStyle"), 0, [unistyle, unistylesRuntime](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count){
71+
auto variants = helpers::variantsToPairs(rt, thisValue.asObject(rt).getProperty(rt, "variants").asObject(rt));
72+
auto arguments = helpers::parseDynamicFunctionArguments(rt, thisValue.asObject(rt).getProperty(rt, "arguments").asObject(rt).asArray(rt));
6473

74+
parser::Parser(unistylesRuntime).rebuildUnistyle(rt, unistyle->parent, unistyle, variants, std::make_optional<std::vector<folly::dynamic>>(arguments));
75+
76+
return jsi::Value(rt, unistyle->parsedStyle.value()).asObject(rt);
77+
});
78+
79+
proto.setProperty(rt, "getStyle", std::move(hostFn));
80+
proto.setProperty(rt, "arguments", arguments.has_value() ? std::move(arguments.value()) : jsi::Array(rt, 0));
81+
proto.setProperty(rt, "variants", variants.has_value() ? helpers::pairsToVariantsValue(rt, variants.value()) : jsi::Object(rt));
6582
proto.setProperty(rt, helpers::STYLE_DEPENDENCIES.c_str(), helpers::dependenciesToJSIArray(rt, unistyle->dependencies));
6683

6784
return proto;
@@ -71,31 +88,31 @@ inline static std::vector<Unistyle::Shared> unistyleFromValue(jsi::Runtime& rt,
7188
if (value.isNull() || !value.isObject()) {
7289
return {};
7390
}
74-
91+
7592
auto maybeArray = value.asObject(rt);
76-
93+
7794
helpers::assertThat(rt, maybeArray.isArray(rt), "Unistyles: can't retrieve Unistyle state from node as it's not an array.");
78-
95+
7996
std::vector<Unistyle::Shared> unistyles;
8097
jsi::Array unistylesArray = maybeArray.asArray(rt);
81-
98+
8299
helpers::iterateJSIArray(rt, unistylesArray, [&rt, &unistyles](size_t index, jsi::Value& value){
83100
auto obj = value.getObject(rt);
84101

85102
// possible if user used React Native styles or inline styles or did spread styles
86103
if (!obj.hasNativeState(rt)) {
87104
auto exoticUnistyles = unistylesFromNonExistentNativeState(rt, obj);
88-
105+
89106
for (auto& exoticUnistyle: exoticUnistyles) {
90107
unistyles.emplace_back(exoticUnistyle);
91108
}
92-
109+
93110
return;
94111
}
95112

96113
unistyles.emplace_back(value.getObject(rt).getNativeState<UnistyleWrapper>(rt)->unistyle);
97114
});
98-
115+
99116
return unistyles;
100117
}
101118

@@ -111,7 +128,7 @@ inline static jsi::Value valueFromUnistyle(jsi::Runtime& rt, std::shared_ptr<Hyb
111128
helpers::defineHiddenProperty(rt, obj, helpers::STYLE_DEPENDENCIES.c_str(), helpers::dependenciesToJSIArray(rt, unistyle->dependencies));
112129
helpers::mergeJSIObjects(rt, obj, unistyle->parsedStyle.value());
113130

114-
obj.setProperty(rt, "__proto__", generateUnistylesPrototype(rt, unistyle));
131+
obj.setProperty(rt, "__proto__", generateUnistylesPrototype(rt, unistylesRuntime, unistyle, std::nullopt, std::nullopt));
115132

116133
return obj;
117134
}

cxx/parser/Parser.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ jsi::Function parser::Parser::createDynamicFunctionProxy(jsi::Runtime& rt, Unist
491491
jsi::Object style = jsi::Value(rt, unistyleFn->parsedStyle.value()).asObject(rt);
492492

493493
// include dependencies for createUnistylesComponent
494-
style.setProperty(rt, "__proto__", generateUnistylesPrototype(rt, unistyle));
494+
style.setProperty(rt, "__proto__", generateUnistylesPrototype(rt, unistylesRuntime, unistyle, variants, helpers::functionArgumentsToArray(rt, args, count)));
495495

496496
jsi::Object secrets = jsi::Object(rt);
497497

example/babel.config.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ module.exports = api => {
88
presets: ['module:@react-native/babel-preset'],
99
plugins: [
1010
[path.join(__dirname, '../plugin'), {
11-
debug: true
11+
debug: true,
12+
isLocal: true
1213
}],
1314
[
1415
'module-resolver',

expo-example/babel.config.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ module.exports = function (api) {
88
presets: ['babel-preset-expo'],
99
plugins: [
1010
[path.join(__dirname, '../plugin'), {
11-
debug: true
11+
debug: true,
12+
isLocal: true
1213
}],
1314
[
1415
'module-resolver',

plugin/import.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ function addUnistylesImport(t, path, state) {
2929
pairs.forEach(([localName, name]) => {
3030
const newImport = t.importDeclaration(
3131
[t.importSpecifier(t.identifier(localName), t.identifier(name))],
32-
t.stringLiteral(`react-native-unistyles/src/components/native/${name}`)
32+
t.stringLiteral(state.opts.isLocal
33+
? `react-native-unistyles/../components/native/${name}`
34+
: `react-native-unistyles/src/components/native/${name}`
35+
)
3336
)
3437

3538
path.node.body.unshift(newImport)

src/core/withUnistyles/useDependencies.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,12 @@ type ListenerProps = {
3131
dependencies: Array<UnistyleDependency>
3232
}
3333

34-
export const useDependencies = (listener: (props: ListenerProps) => VoidFunction) => {
34+
export const useDependencies = (
35+
listener: (props: ListenerProps) => VoidFunction,
36+
stylesDependencies: Array<UnistyleDependency> = []
37+
) => {
3538
const scopedTheme = UnistylesShadowRegistry.getScopedTheme() as UnistylesTheme
36-
const [dependencies] = useState(() => new Set<number>())
39+
const [dependencies] = useState(() => new Set<number>(stylesDependencies))
3740
const [theme, setTheme] = useState(UnistylesRuntime.getTheme(scopedTheme))
3841
const [_, runtimeChanged] = useReducer(() => ({}), {})
3942

src/core/withUnistyles/withUnistyles.native.tsx

+7-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ export const withUnistyles = <TProps extends Record<string, any>, TMappings exte
2323
console.error(`🦄 Unistyles: createUnistylesComponent requires ${propName} to be an object. Please check props for component: ${Component.displayName}`)
2424
}
2525

26+
// @ts-expect-error - this is hidden from TS
27+
if (props[propName].__unistyles_name && !props[propName].__proto__?.getStyle) {
28+
console.error(`🦄 Unistyles: createUnistylesComponent received style that is not bound. You likely used the spread operator on a Unistyle style. Please check props for component: ${Component.displayName}`)
29+
}
30+
2631
stylesRef.current = {
2732
...stylesRef.current,
2833
[propName]: narrowedProps[propName]
@@ -41,7 +46,7 @@ export const withUnistyles = <TProps extends Record<string, any>, TMappings exte
4146
stylesRef.current = {
4247
...stylesRef.current,
4348
// @ts-expect-error - this is hidden from TS
44-
[propName]: props[propName]
49+
[propName]: props[propName].__proto__?.getStyle?.() || props[propName]
4550
}
4651
}
4752
})
@@ -55,7 +60,7 @@ export const withUnistyles = <TProps extends Record<string, any>, TMappings exte
5560
})
5661

5762
return () => dispose()
58-
})
63+
}, narrowedProps.style?.__proto__.uni__dependencies)
5964

6065
const mappingProps = mappings ? mappingsCallback(mappings) : {}
6166
const unistyleProps = narrowedProps.uniProps ? mappingsCallback(narrowedProps.uniProps) : {}

0 commit comments

Comments
 (0)