Skip to content

Commit ac60459

Browse files
committed
fix: improve VariableContext
1 parent 695a37f commit ac60459

5 files changed

Lines changed: 195 additions & 49 deletions

File tree

cpp/HybridStyleRegistry.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "ShadowTreeUpdateManager.hpp"
55
#include "StyledComputedFactory.hpp"
66
#include "Environment.hpp"
7+
#include "VariableContext.hpp"
78

89
#include <regex>
910
#include <string>
@@ -70,6 +71,12 @@ namespace margelo::nitro::cssnitro {
7071
});
7172
}
7273

74+
void HybridStyleRegistry::setRootVariable(const std::string &name,
75+
const std::vector<HybridRootVariableRule> &value) {
76+
// Call setTopLevelVariable with key="root"
77+
VariableContext::setTopLevelVariable("root", name, value);
78+
}
79+
7380
Declarations HybridStyleRegistry::getDeclarations(const std::string &componentId,
7481
const std::string &classNames,
7582
const std::string &variableScope,

cpp/HybridStyleRegistry.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ namespace margelo::nitro::cssnitro {
4343

4444
void addStyleSheet(const HybridStyleSheet &stylesheet) override;
4545

46+
void setRootVariable(const std::string &name,
47+
const std::vector<HybridRootVariableRule> &value) override;
48+
4649
Declarations getDeclarations(const std::string &componentId, const std::string &classNames,
4750
const std::string &variableScope,
4851
const std::string &containerScope) override;

cpp/VariableContext.cpp

Lines changed: 135 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,81 +2,176 @@
22

33
namespace margelo::nitro::cssnitro {
44

5+
using AnyValue = ::margelo::nitro::AnyValue;
6+
57
// Initialize the static contexts map
6-
std::unordered_map<std::string, std::unordered_map<std::string, std::shared_ptr<reactnativecss::Observable<AnyValue>>>> VariableContext::contexts;
8+
std::unordered_map<std::string, VariableContext::Context> VariableContext::contexts;
79

8-
void VariableContext::createContext(const std::string &key) {
9-
// Create a new empty map for this context
10-
contexts[key] = std::unordered_map<std::string, std::shared_ptr<reactnativecss::Observable<AnyValue>>>();
10+
void VariableContext::createContext(const std::string &key, const std::string &parent) {
11+
// Create a new context with the specified parent
12+
Context ctx;
13+
ctx.parent = parent;
14+
ctx.values = std::unordered_map<std::string, VariableValue>();
15+
contexts[key] = ctx;
1116
}
1217

1318
void VariableContext::deleteContext(const std::string &key) {
1419
// Remove the context from the map
1520
contexts.erase(key);
1621
}
1722

18-
const AnyValue &VariableContext::getVariable(const std::string &key, const std::string &name,
19-
reactnativecss::Effect::GetProxy &get) {
20-
// Find the context
21-
auto contextIt = contexts.find(key);
22-
if (contextIt == contexts.end()) {
23-
// Context doesn't exist, throw or return a default value
24-
static AnyValue defaultValue;
25-
return defaultValue;
23+
AnyValue VariableContext::getValue(const VariableValue &varValue,
24+
reactnativecss::Effect::GetProxy &get) {
25+
if (std::holds_alternative<std::shared_ptr<reactnativecss::Observable<AnyValue>>>(
26+
varValue)) {
27+
auto obs = std::get<std::shared_ptr<reactnativecss::Observable<AnyValue>>>(varValue);
28+
return get(*obs);
29+
} else {
30+
auto comp = std::get<std::shared_ptr<reactnativecss::Computed<AnyValue>>>(varValue);
31+
return get(*comp);
32+
}
33+
}
34+
35+
std::optional<AnyValue> VariableContext::checkContext(const std::string &contextKey,
36+
const std::string &name,
37+
reactnativecss::Effect::GetProxy &get) {
38+
auto contextIt = contexts.find(contextKey);
39+
if (contextIt != contexts.end()) {
40+
auto &valueMap = contextIt->second.values;
41+
auto varIt = valueMap.find(name);
42+
if (varIt != valueMap.end()) {
43+
auto result = getValue(varIt->second, get);
44+
// If the value is not nullopt, return it
45+
if (!std::holds_alternative<std::monostate>(result)) {
46+
return result;
47+
}
48+
}
49+
}
50+
return std::nullopt;
51+
}
52+
53+
std::optional<AnyValue>
54+
VariableContext::getVariable(const std::string &key, const std::string &name,
55+
reactnativecss::Effect::GetProxy &get) {
56+
// 1. Check current key
57+
auto result = checkContext(key, name, get);
58+
if (result.has_value()) {
59+
return result;
2660
}
2761

28-
// Find the variable in the context
29-
auto &variableMap = contextIt->second;
30-
auto varIt = variableMap.find(name);
31-
if (varIt == variableMap.end()) {
32-
// Variable doesn't exist, return a default value
33-
static AnyValue defaultValue;
34-
return defaultValue;
62+
// 2. Check "universal" context (if we're not already in it)
63+
if (key != "universal") {
64+
result = checkContext("universal", name, get);
65+
if (result.has_value()) {
66+
return result;
67+
}
68+
69+
// 3. Walk up the parent chain from the original key
70+
std::string currentKey = key;
71+
auto contextIt = contexts.find(currentKey);
72+
if (contextIt != contexts.end()) {
73+
std::string parentKey = contextIt->second.parent;
74+
75+
// Walk up parent chain until we hit root (parent points to itself)
76+
while (parentKey != currentKey && !parentKey.empty()) {
77+
result = checkContext(parentKey, name, get);
78+
if (result.has_value()) {
79+
return result;
80+
}
81+
82+
// Move to next parent
83+
auto parentIt = contexts.find(parentKey);
84+
if (parentIt != contexts.end()) {
85+
currentKey = parentKey;
86+
parentKey = parentIt->second.parent;
87+
} else {
88+
break;
89+
}
90+
}
91+
}
3592
}
3693

37-
// Get the observable and subscribe the effect to it
38-
auto &observable = varIt->second;
39-
return get(*observable);
94+
// Variable doesn't exist in any context
95+
return std::nullopt;
4096
}
4197

4298
void VariableContext::setVariable(const std::string &key, const std::string &name,
4399
const AnyValue &value) {
44100
// Find or create the context
45101
auto contextIt = contexts.find(key);
46102
if (contextIt == contexts.end()) {
47-
// Context doesn't exist, create it
48-
createContext(key);
103+
// Context doesn't exist, create it with empty parent
104+
createContext(key, "");
49105
contextIt = contexts.find(key);
50106
}
51107

52-
auto &variableMap = contextIt->second;
108+
auto &valueMap = contextIt->second.values;
53109

54-
// Find or create the observable for this variable
55-
auto varIt = variableMap.find(name);
56-
if (varIt == variableMap.end()) {
57-
// Variable doesn't exist, create a new Observable with the value
58-
auto observable = reactnativecss::Observable<AnyValue>::create(value);
59-
variableMap[name] = observable;
60-
} else {
61-
// Variable exists, update its value
62-
varIt->second->set(value);
110+
// Find the variable
111+
auto varIt = valueMap.find(name);
112+
if (varIt != valueMap.end()) {
113+
// Variable exists, check if it's an Observable or Computed
114+
if (std::holds_alternative<std::shared_ptr<reactnativecss::Observable<AnyValue>>>(
115+
varIt->second)) {
116+
// It's an Observable, just update its value
117+
auto obs = std::get<std::shared_ptr<reactnativecss::Observable<AnyValue>>>(
118+
varIt->second);
119+
obs->set(value);
120+
return;
121+
} else {
122+
// It's a Computed, dispose it and create a new Observable
123+
auto comp = std::get<std::shared_ptr<reactnativecss::Computed<AnyValue>>>(
124+
varIt->second);
125+
comp->dispose();
126+
}
63127
}
128+
129+
// Create a new Observable with the value
130+
auto observable = reactnativecss::Observable<AnyValue>::create(value);
131+
valueMap[name] = observable;
64132
}
65133

66134
void VariableContext::setVariable(const std::string &key, const std::string &name,
67-
std::shared_ptr<reactnativecss::Observable<AnyValue>> observable) {
135+
std::shared_ptr<reactnativecss::Computed<AnyValue>> computed) {
68136
// Find or create the context
69137
auto contextIt = contexts.find(key);
70138
if (contextIt == contexts.end()) {
71-
// Context doesn't exist, create it
72-
createContext(key);
139+
// Context doesn't exist, create it with empty parent
140+
createContext(key, "");
73141
contextIt = contexts.find(key);
74142
}
75143

76-
auto &variableMap = contextIt->second;
144+
auto &valueMap = contextIt->second.values;
145+
146+
// Check if variable already exists and dispose if it's a Computed
147+
auto varIt = valueMap.find(name);
148+
if (varIt != valueMap.end()) {
149+
if (std::holds_alternative<std::shared_ptr<reactnativecss::Computed<AnyValue>>>(
150+
varIt->second)) {
151+
auto existingComp = std::get<std::shared_ptr<reactnativecss::Computed<AnyValue>>>(
152+
varIt->second);
153+
existingComp->dispose();
154+
}
155+
}
156+
157+
// Set the variable to use the provided Computed directly
158+
valueMap[name] = computed;
159+
}
160+
161+
void VariableContext::setTopLevelVariable(const std::string &key, const std::string &name,
162+
const std::vector<HybridRootVariableRule> &value) {
163+
// Create a new Computed that returns "yellow" for now
164+
auto computed = reactnativecss::Computed<AnyValue>::create(
165+
[](const AnyValue &prev, reactnativecss::Effect::GetProxy &get) -> AnyValue {
166+
(void) prev;
167+
(void) get;
168+
return AnyValue("yellow");
169+
},
170+
AnyValue() // Initial value
171+
);
77172

78-
// Set the variable to use the provided Observable directly
79-
variableMap[name] = observable;
173+
// Use the Computed overload to set the variable
174+
setVariable(key, name, computed);
80175
}
81176

82177
} // namespace margelo::nitro::cssnitro

cpp/VariableContext.hpp

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,70 @@
33
#include <string>
44
#include <unordered_map>
55
#include <memory>
6+
#include <optional>
7+
#include <variant>
68
#include <NitroModules/AnyMap.hpp>
79
#include "Observable.hpp"
10+
#include "Computed.hpp"
811
#include "Effect.hpp"
912

1013
namespace margelo::nitro::cssnitro {
1114

1215
using AnyValue = ::margelo::nitro::AnyValue;
1316

17+
// Forward declaration
18+
struct HybridRootVariableRule;
19+
1420
class VariableContext {
1521
public:
16-
// Static map: context key -> (variable name -> Observable<AnyValue>)
17-
static std::unordered_map<std::string, std::unordered_map<std::string, std::shared_ptr<reactnativecss::Observable<AnyValue>>>> contexts;
22+
// Value can be either Observable or Computed
23+
using VariableValue = std::variant<
24+
std::shared_ptr<reactnativecss::Observable<AnyValue>>,
25+
std::shared_ptr<reactnativecss::Computed<AnyValue>>
26+
>;
27+
28+
struct Context {
29+
std::string parent;
30+
std::unordered_map<std::string, VariableValue> values;
31+
};
32+
33+
// Static map: context key -> Context
34+
static std::unordered_map<std::string, Context> contexts;
1835

19-
// Create a new context with the given key
20-
static void createContext(const std::string &key);
36+
// Create a new context with the given key and parent
37+
static void createContext(const std::string &key, const std::string &parent);
2138

2239
// Delete a context by key
2340
static void deleteContext(const std::string &key);
2441

2542
// Get a variable from a context, subscribing the effect to changes
26-
static const AnyValue &getVariable(const std::string &key, const std::string &name,
27-
reactnativecss::Effect::GetProxy &get);
43+
// Returns std::nullopt if the context or variable doesn't exist
44+
static std::optional<AnyValue> getVariable(const std::string &key, const std::string &name,
45+
reactnativecss::Effect::GetProxy &get);
2846

29-
// Set a variable in a context
47+
// Set a variable in a context (creates an Observable)
3048
static void
3149
setVariable(const std::string &key, const std::string &name, const AnyValue &value);
3250

33-
// Set a variable in a context using an existing Observable
51+
// Set a variable in a context using an existing Computed
3452
static void setVariable(const std::string &key, const std::string &name,
35-
std::shared_ptr<reactnativecss::Observable<AnyValue>> observable);
53+
std::shared_ptr<reactnativecss::Computed<AnyValue>> computed);
54+
55+
// Set a top-level variable (creates a Computed from HybridRootVariableRule)
56+
static void setTopLevelVariable(const std::string &key, const std::string &name,
57+
const std::vector<HybridRootVariableRule> &value);
3658

3759
private:
3860
VariableContext() = delete; // Static-only class
61+
62+
// Helper to get value from a VariableValue variant
63+
static AnyValue
64+
getValue(const VariableValue &varValue, reactnativecss::Effect::GetProxy &get);
65+
66+
// Helper to check a specific context for the variable
67+
static std::optional<AnyValue>
68+
checkContext(const std::string &contextKey, const std::string &name,
69+
reactnativecss::Effect::GetProxy &get);
3970
};
4071

4172
} // namespace margelo::nitro::cssnitro

src/specs/StyleRegistry/HybridStyleRegistry.nitro.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export type HybridStyleRegistry = StyleRegistry & RawStyleRegistry;
77
export interface StyleRegistry
88
extends HybridObject<{ ios: "c++"; android: "c++" }> {
99
setClassname(classname: string, styleRule: HybridStyleRule[]): void;
10+
setRootVariable(name: string, value: HybridRootVariableRule[]): void;
1011
addStyleSheet(stylesheet: HybridStyleSheet): void;
1112
getDeclarations(
1213
componentId: string,
@@ -130,3 +131,12 @@ interface HybridStyleRule {
130131
}
131132

132133
type SpecificityArray = number[];
134+
135+
/****************************** Variables *******************************/
136+
137+
interface HybridRootVariableRule {
138+
d: AnyMap;
139+
140+
/** MediaQuery */
141+
m?: AnyMap;
142+
}

0 commit comments

Comments
 (0)