-
-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
534 additions
and
486 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#define HOST_FN(name, args, fn_body) \ | ||
jsi::Function::createFromHostFunction(rt, \ | ||
jsi::PropNameID::forAscii(rt, name), \ | ||
args, \ | ||
[this, &fnName](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *arguments, size_t count) -> jsi::Value \ | ||
fn_body \ | ||
); \ | ||
|
||
#define BIND_FN(fn) std::bind(&UnistylesRuntime::fn, this, std::placeholders::_1, std::placeholders::_2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
#include "UnistylesRuntime.h" | ||
#include <jsi/jsi.h> | ||
|
||
using namespace facebook; | ||
|
||
jsi::Value UnistylesRuntime::getScreenWidth(jsi::Runtime& rt, std::string fnName) { | ||
return jsi::Value(this->screen.width); | ||
} | ||
|
||
jsi::Value UnistylesRuntime::getScreenHeight(jsi::Runtime& rt, std::string fnName) { | ||
return jsi::Value(this->screen.height); | ||
} | ||
|
||
jsi::Value UnistylesRuntime::getContentSizeCategory(jsi::Runtime & rt, std::string fnName) { | ||
return jsi::Value(jsi::String::createFromUtf8(rt, this->contentSizeCategory)); | ||
} | ||
|
||
jsi::Value UnistylesRuntime::hasEnabledAdaptiveThemes(jsi::Runtime& rt, std::string fnName) { | ||
return jsi::Value(this->hasAdaptiveThemes); | ||
} | ||
|
||
jsi::Value UnistylesRuntime::getThemeName(jsi::Runtime& rt, std::string fnName) { | ||
return !this->themeName.empty() | ||
? jsi::Value(jsi::String::createFromUtf8(rt, this->themeName)) | ||
: this->getThemeOrFail(rt); | ||
} | ||
|
||
jsi::Value UnistylesRuntime::getCurrentBreakpoint(jsi::Runtime& rt, std::string fnName) { | ||
return !this->breakpoint.empty() | ||
? jsi::Value(jsi::String::createFromUtf8(rt, this->breakpoint)) | ||
: jsi::Value::undefined(); | ||
} | ||
|
||
jsi::Value UnistylesRuntime::getColorScheme(jsi::Runtime& rt, std::string fnName) { | ||
return jsi::Value(jsi::String::createFromUtf8(rt, this->colorScheme)); | ||
} | ||
|
||
jsi::Value UnistylesRuntime::getSortedBreakpointPairs(jsi::Runtime& rt, std::string fnName) { | ||
std::unique_ptr<jsi::Array> sortedBreakpointEntriesArray = std::make_unique<jsi::Array>(rt, this->sortedBreakpointPairs.size()); | ||
|
||
for (size_t i = 0; i < this->sortedBreakpointPairs.size(); ++i) { | ||
std::unique_ptr<jsi::Array> pairArray = std::make_unique<jsi::Array>(rt, 2); | ||
jsi::String nameValue = jsi::String::createFromUtf8(rt, this->sortedBreakpointPairs[i].first); | ||
|
||
pairArray->setValueAtIndex(rt, 0, nameValue); | ||
pairArray->setValueAtIndex(rt, 1, jsi::Value(this->sortedBreakpointPairs[i].second)); | ||
sortedBreakpointEntriesArray->setValueAtIndex(rt, i, *pairArray); | ||
} | ||
|
||
return jsi::Value(rt, *sortedBreakpointEntriesArray); | ||
} | ||
|
||
jsi::Value UnistylesRuntime::setBreakpoints(jsi::Runtime& rt, std::string fnName) { | ||
return HOST_FN(fnName, 1, { | ||
jsi::Object breakpointsObj = arguments[0].asObject(rt); | ||
auto sortedBreakpoints = this->toSortedBreakpointPairs(rt, breakpointsObj); | ||
|
||
if (sortedBreakpoints.size() == 0) { | ||
throw jsi::JSError(rt, UnistylesErrorBreakpointsCannotBeEmpty); | ||
} | ||
|
||
if (sortedBreakpoints.at(0).second != 0) { | ||
throw jsi::JSError(rt, UnistylesErrorBreakpointsMustStartFromZero); | ||
} | ||
|
||
this->sortedBreakpointPairs = sortedBreakpoints; | ||
|
||
std::string breakpoint = this->getBreakpointFromScreenWidth(this->screen.width, sortedBreakpoints); | ||
|
||
this->breakpoint = breakpoint; | ||
|
||
return jsi::Value::undefined(); | ||
}); | ||
} | ||
|
||
jsi::Value UnistylesRuntime::setActiveTheme(jsi::Runtime& rt, std::string fnName) { | ||
return HOST_FN(fnName, 1, { | ||
std::string themeName = arguments[0].asString(rt).utf8(rt); | ||
|
||
if (this->themeName != themeName) { | ||
this->themeName = themeName; | ||
this->onThemeChangeCallback(themeName); | ||
} | ||
|
||
return jsi::Value::undefined(); | ||
}); | ||
} | ||
|
||
jsi::Value UnistylesRuntime::updateTheme(jsi::Runtime& rt, std::string fnName) { | ||
return HOST_FN(fnName, 1, { | ||
std::string themeName = arguments[0].asString(rt).utf8(rt); | ||
|
||
if (this->themeName == themeName) { | ||
this->onThemeChangeCallback(themeName); | ||
} | ||
|
||
return jsi::Value::undefined(); | ||
}); | ||
} | ||
|
||
jsi::Value UnistylesRuntime::useAdaptiveThemes(jsi::Runtime& rt, std::string fnName) { | ||
return HOST_FN(fnName, 1, { | ||
bool enableAdaptiveThemes = arguments[0].asBool(); | ||
|
||
if (enableAdaptiveThemes && this->colorScheme == UnistylesUnspecifiedScheme) { | ||
throw jsi::JSError(rt, UnistylesErrorAdaptiveThemesNotSupported); | ||
} | ||
|
||
this->hasAdaptiveThemes = enableAdaptiveThemes; | ||
|
||
if (!enableAdaptiveThemes || !this->supportsAutomaticColorScheme) { | ||
return jsi::Value::undefined(); | ||
} | ||
|
||
if (this->themeName != this->colorScheme) { | ||
this->themeName = this->colorScheme; | ||
this->onThemeChangeCallback(this->themeName); | ||
} | ||
|
||
return jsi::Value::undefined(); | ||
}); | ||
} | ||
|
||
jsi::Value UnistylesRuntime::addPlugin(jsi::Runtime& rt, std::string fnName) { | ||
return HOST_FN(fnName, 1, { | ||
std::string pluginName = arguments[0].asString(rt).utf8(rt); | ||
bool notify = arguments[1].asBool(); | ||
|
||
this->pluginNames.push_back(pluginName); | ||
|
||
// registry enabled plugins won't notify listeners | ||
if (notify) { | ||
this->onPluginChangeCallback(); | ||
} | ||
|
||
return jsi::Value::undefined(); | ||
}); | ||
} | ||
|
||
jsi::Value UnistylesRuntime::removePlugin(jsi::Runtime& rt, std::string fnName) { | ||
return HOST_FN(fnName, 1, { | ||
std::string pluginName = arguments[0].asString(rt).utf8(rt); | ||
|
||
auto it = std::find(this->pluginNames.begin(), this->pluginNames.end(), pluginName); | ||
|
||
if (it != this->pluginNames.end()) { | ||
this->pluginNames.erase(it); | ||
this->onPluginChangeCallback(); | ||
} | ||
|
||
return jsi::Value::undefined(); | ||
}); | ||
} | ||
|
||
jsi::Value UnistylesRuntime::getEnabledPlugins(jsi::Runtime& rt, std::string fnName) { | ||
auto jsiArray = facebook::jsi::Array(rt, this->pluginNames.size()); | ||
|
||
for (size_t i = 0; i < this->pluginNames.size(); i++) { | ||
jsiArray.setValueAtIndex(rt, i, facebook::jsi::String::createFromUtf8(rt, this->pluginNames[i])); | ||
} | ||
|
||
return jsiArray; | ||
} | ||
|
||
jsi::Value UnistylesRuntime::getInsets(jsi::Runtime& rt, std::string fnName) { | ||
auto insets = jsi::Object(rt); | ||
|
||
insets.setProperty(rt, "top", this->insets.top); | ||
insets.setProperty(rt, "bottom", this->insets.bottom); | ||
insets.setProperty(rt, "left", this->insets.left); | ||
insets.setProperty(rt, "right", this->insets.right); | ||
|
||
return insets; | ||
} | ||
|
||
jsi::Value UnistylesRuntime::getStatusBar(jsi::Runtime& rt, std::string fnName) { | ||
auto statusBar = jsi::Object(rt); | ||
auto setStatusBarColorFunction = HOST_FN("setColor", 1, { | ||
std::string color = arguments[0].asString(rt).utf8(rt); | ||
|
||
if (this->onSetStatusBarColorCallback.has_value()) { | ||
this->onSetStatusBarColorCallback.value()(color); | ||
} | ||
|
||
return jsi::Value::undefined(); | ||
}); | ||
|
||
statusBar.setProperty(rt, "width", this->statusBar.width); | ||
statusBar.setProperty(rt, "height", this->statusBar.height); | ||
statusBar.setProperty(rt, "setColor", setStatusBarColorFunction); | ||
|
||
return statusBar; | ||
} | ||
|
||
jsi::Value UnistylesRuntime::getNavigationBar(jsi::Runtime& rt, std::string fnName) { | ||
auto navigationBarValue = jsi::Object(rt); | ||
auto setNavigationBarColorFunction = HOST_FN("setColor", 1, { | ||
std::string color = arguments[0].asString(rt).utf8(rt); | ||
|
||
if (this->onSetStatusBarColorCallback.has_value()) { | ||
this->onSetNavigationBarColorCallback.value()(color); | ||
} | ||
|
||
return jsi::Value::undefined(); | ||
}); | ||
|
||
navigationBarValue.setProperty(rt, "width", this->navigationBar.width); | ||
navigationBarValue.setProperty(rt, "height", this->navigationBar.height); | ||
navigationBarValue.setProperty(rt, "setColor", setNavigationBarColorFunction); | ||
|
||
return navigationBarValue; | ||
} | ||
|
||
std::optional<jsi::Value> UnistylesRuntime::setThemes(jsi::Runtime& rt, const jsi::Value& value) { | ||
jsi::Array themes = value.asObject(rt).asArray(rt); | ||
std::vector<std::string> themesVector; | ||
size_t length = themes.size(rt); | ||
|
||
for (size_t i = 0; i < length; ++i) { | ||
jsi::Value element = themes.getValueAtIndex(rt, i); | ||
|
||
if (element.isString()) { | ||
std::string theme = element.asString(rt).utf8(rt); | ||
themesVector.push_back(theme); | ||
} | ||
} | ||
|
||
if (themesVector.size() == 0) { | ||
throw jsi::JSError(rt, UnistylesErrorThemesCannotBeEmpty); | ||
} | ||
|
||
this->themes = themesVector; | ||
this->themeName = ""; | ||
|
||
bool hasLightTheme = std::find(themesVector.begin(), themesVector.end(), "light") != themesVector.end(); | ||
bool hasDarkTheme = std::find(themesVector.begin(), themesVector.end(), "dark") != themesVector.end(); | ||
|
||
this->supportsAutomaticColorScheme = hasLightTheme && hasDarkTheme; | ||
|
||
return std::nullopt; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
#include "UnistylesModel.h" | ||
|
||
std::string UnistylesModel::getBreakpointFromScreenWidth(int width, const std::vector<std::pair<std::string, double>>& sortedBreakpointPairs) { | ||
for (size_t i = 0; i < sortedBreakpointPairs.size(); ++i) { | ||
const auto& [key, value] = sortedBreakpointPairs[i]; | ||
const double maxVal = (i + 1 < sortedBreakpointPairs.size()) ? sortedBreakpointPairs[i + 1].second : std::numeric_limits<double>::infinity(); | ||
|
||
if (width >= value && width < maxVal) { | ||
return key; | ||
} | ||
} | ||
|
||
return sortedBreakpointPairs.empty() ? "" : sortedBreakpointPairs.back().first; | ||
} | ||
|
||
void UnistylesModel::handleScreenSizeChange(Dimensions& screen, Insets& insets, Dimensions& statusBar, Dimensions& navigationBar) { | ||
std::string breakpoint = this->getBreakpointFromScreenWidth(screen.width, this->sortedBreakpointPairs); | ||
bool hasDifferentBreakpoint = this->breakpoint != breakpoint; | ||
bool hasDifferentScreenDimensions = this->screen.width != screen.width || this->screen.height != screen.height; | ||
bool hasDifferentInsets = this->insets.top != insets.top || this->insets.bottom != insets.bottom || this->insets.left != insets.left || this->insets.right != insets.right; | ||
|
||
// we don't need to check statusBar/navigationBar as they will only change on orientation change witch is equal to hasDifferentScreenDimensions | ||
bool shouldNotify = hasDifferentBreakpoint || hasDifferentScreenDimensions || hasDifferentInsets; | ||
|
||
this->breakpoint = breakpoint; | ||
this->screen = {screen.width, screen.height}; | ||
this->insets = {insets.top, insets.bottom, insets.left, insets.right}; | ||
this->statusBar = {statusBar.width, statusBar.height}; | ||
this->navigationBar = {navigationBar.width, navigationBar.height}; | ||
|
||
std::string orientation = screen.width > screen.height | ||
? UnistylesOrientationLandscape | ||
: UnistylesOrientationPortrait; | ||
|
||
if (shouldNotify) { | ||
this->onLayoutChangeCallback(breakpoint, orientation, screen, statusBar, insets, navigationBar); | ||
} | ||
} | ||
|
||
void UnistylesModel::handleAppearanceChange(std::string colorScheme) { | ||
this->colorScheme = colorScheme; | ||
|
||
if (!this->supportsAutomaticColorScheme || !this->hasAdaptiveThemes) { | ||
return; | ||
} | ||
|
||
if (this->themeName != this->colorScheme) { | ||
this->onThemeChangeCallback(this->colorScheme); | ||
this->themeName = this->colorScheme; | ||
} | ||
} | ||
|
||
void UnistylesModel::handleContentSizeCategoryChange(std::string contentSizeCategory) { | ||
this->contentSizeCategory = contentSizeCategory; | ||
this->onContentSizeCategoryChangeCallback(contentSizeCategory); | ||
} | ||
|
||
jsi::Value UnistylesModel::getThemeOrFail(jsi::Runtime& runtime) { | ||
if (this->themes.size() == 1) { | ||
std::string themeName = this->themes.at(0); | ||
|
||
this->themeName = themeName; | ||
|
||
return jsi::String::createFromUtf8(runtime, themeName); | ||
} | ||
|
||
return jsi::Value().undefined(); | ||
} | ||
|
||
std::vector<std::pair<std::string, double>> UnistylesModel::toSortedBreakpointPairs(jsi::Runtime& rt, jsi::Object& breakpointsObj) { | ||
jsi::Array propertyNames = breakpointsObj.getPropertyNames(rt); | ||
std::vector<std::pair<std::string, double>> sortedBreakpointEntriesVec; | ||
|
||
for (size_t i = 0; i < propertyNames.size(rt); ++i) { | ||
jsi::Value propNameValue = propertyNames.getValueAtIndex(rt, i); | ||
std::string name = propNameValue.asString(rt).utf8(rt); | ||
jsi::PropNameID propNameID = jsi::PropNameID::forUtf8(rt, name); | ||
jsi::Value value = breakpointsObj.getProperty(rt, propNameID); | ||
|
||
if (value.isNumber()) { | ||
double breakpointValue = value.asNumber(); | ||
|
||
sortedBreakpointEntriesVec.push_back(std::make_pair(name, breakpointValue)); | ||
} | ||
} | ||
|
||
std::sort(sortedBreakpointEntriesVec.begin(), sortedBreakpointEntriesVec.end(), [](const std::pair<std::string, double>& a, const std::pair<std::string, double>& b) { | ||
return a.second < b.second; | ||
}); | ||
|
||
return sortedBreakpointEntriesVec; | ||
} |
Oops, something went wrong.