Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: native breakpoints - add config to use points instead of pixels #541

Merged
merged 4 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ Then follow [installation guides](https://unistyl.es/v3/start/getting-started) f
<a href="https://galaxies.dev">
<img src="https://avatars.githubusercontent.com/u/118431096?s=200&v=4" height="70px" width="70px" alt="galaxies-dev" />
</a>
<a href="https://github.com/FTCHD">
<img src="https://avatars.githubusercontent.com/u/144691102?v=4" height="70px" width="70px" alt="FTCHD" />
</a>
<a href="https://github.com/mobily">
<img src="https://avatars.githubusercontent.com/u/1467712?v=4" height="70px" width="70px" alt="mobily" />
</a>
Expand All @@ -62,8 +65,14 @@ Then follow [installation guides](https://unistyl.es/v3/start/getting-started) f
<a href="https://github.com/oscklm">
<img src="https://avatars.githubusercontent.com/u/22825865?v=4" height="70px" width="70px" alt="oscklm" />
</a>
<a href="https://github.com/giovannilondero">
<img src="https://avatars.githubusercontent.com/u/10998991?v=4" height="70px" width="70px" alt="giovannilondero" />
<a href="https://github.com/guillaumehcht">
<img src="https://avatars.githubusercontent.com/u/80776475?v=4" height="70px" width="70px" alt="guillaumehcht" />
</a>
<a href="https://github.com/FilipiRafael">
<img src="https://avatars.githubusercontent.com/u/61629642?v=4" height="70px" width="70px" alt="FilipiRafael" />
</a>
<a href="https://github.com/4cc3ssX">
<img src="https://avatars.githubusercontent.com/u/57473799?v=4" height="70px" width="70px" alt="4cc3ssX" />
</a>

## Past sponsors
Expand All @@ -89,6 +98,9 @@ Then follow [installation guides](https://unistyl.es/v3/start/getting-started) f
<a href="https://github.com/hyoban">
<img src="https://avatars.githubusercontent.com/u/38493346?v=4" height="70px" width="70px" alt="hyoban" />
</a>
<a href="https://github.com/giovannilondero">
<img src="https://avatars.githubusercontent.com/u/10998991?v=4" height="70px" width="70px" alt="giovannilondero" />
</a>

## Sponsor my work

Expand Down
2 changes: 2 additions & 0 deletions cxx/core/UnistylesRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ struct UnistylesRegistry: public StyleSheetRegistry {

UnistylesRegistry(const UnistylesRegistry&) = delete;
UnistylesRegistry(const UnistylesRegistry&&) = delete;

bool shouldUsePointsForBreakpoints = false;

void registerTheme(jsi::Runtime& rt, std::string name, jsi::Value& theme);
void registerBreakpoints(jsi::Runtime& rt, std::vector<std::pair<std::string, double>>& sortedBreakpoints);
Expand Down
29 changes: 27 additions & 2 deletions cxx/hybridObjects/HybridStyleSheet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,20 @@ void HybridStyleSheet::parseSettings(jsi::Runtime &rt, jsi::Object settings) {
if (propertyName == "CSSVars") {
return;
}

if (propertyName == "nativeBreakpointsMode") {
helpers::assertThat(rt, propertyValue.isString(), "StyleSheet.configure's nativeBreakpointsMode must be a string");

auto mode = propertyValue.asString(rt).utf8(rt);

helpers::assertThat(rt, mode == "pixels" || mode == "points", "StyleSheet.configure's nativeBreakpointsMode must be one of: pixels or points");

if (mode == "points") {
registry.shouldUsePointsForBreakpoints = true;
}

return;
}

helpers::assertThat(rt, false, "StyleSheet.configure's settings received unexpected key: '" + std::string(propertyName) + "'");
});
Expand All @@ -142,7 +156,13 @@ void HybridStyleSheet::parseBreakpoints(jsi::Runtime &rt, jsi::Object breakpoint
auto& state = registry.getState(rt);

registry.registerBreakpoints(rt, sortedBreakpoints);
state.computeCurrentBreakpoint(this->_unistylesRuntime->getScreen().width);

auto rawWidth = this->_unistylesRuntime->getScreen().width;
auto width = registry.shouldUsePointsForBreakpoints
? rawWidth / this->_unistylesRuntime->getPixelRatio()
: rawWidth;

state.computeCurrentBreakpoint(width);
}

void HybridStyleSheet::parseThemes(jsi::Runtime &rt, jsi::Object themes) {
Expand Down Expand Up @@ -293,7 +313,12 @@ void HybridStyleSheet::onPlatformNativeDependenciesChange(std::vector<UnistyleDe
auto dimensionsIt = std::find(dependencies.begin(), dependencies.end(), UnistyleDependency::DIMENSIONS);

if (dimensionsIt != dependencies.end()) {
registry.getState(rt).computeCurrentBreakpoint(this->_unistylesRuntime->getScreen().width);
auto rawWidth = this->_unistylesRuntime->getScreen().width;
auto width = registry.shouldUsePointsForBreakpoints
? rawWidth / this->_unistylesRuntime->getPixelRatio()
: rawWidth;

registry.getState(rt).computeCurrentBreakpoint(width);
}

// check if color scheme changed and then if Unistyles state depend on it (adaptive themes)
Expand Down
6 changes: 5 additions & 1 deletion cxx/parser/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,11 @@ jsi::Value parser::Parser::getValueFromBreakpoints(jsi::Runtime& rt, Unistyle::S
auto sortedBreakpoints = state.getSortedBreakpointPairs();
auto hasBreakpoints = !sortedBreakpoints.empty();
auto currentBreakpoint = state.getCurrentBreakpointName();
auto dimensions = this->_unistylesRuntime->getScreen();
auto rawDimensions = this->_unistylesRuntime->getScreen();
auto pixelRatio = this->_unistylesRuntime->getPixelRatio();
auto dimensions = registry.shouldUsePointsForBreakpoints
? Dimensions(rawDimensions.width / pixelRatio, rawDimensions.height / pixelRatio)
: rawDimensions;
auto currentOrientation = dimensions.width > dimensions.height
? "landscape"
: "portrait";
Expand Down
2 changes: 1 addition & 1 deletion docs/.astro/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"_variables": {
"lastUpdateCheck": 1737126711469
"lastUpdateCheck": 1738238865697
}
}
12 changes: 6 additions & 6 deletions docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export default defineConfig({
items: [
{ label: 'Introduction', slug: 'v3/start/introduction' },
{ label: 'Getting started', slug: 'v3/start/getting-started' },
{ label: 'Configuration', slug: 'v3/start/configuration' },
{ label: 'Configuration', slug: 'v3/start/configuration', badge: 'Updated' },
{ label: 'New features', slug: 'v3/start/new-features' },
{ label: 'Look under the hood', slug: 'v3/start/how-unistyles-works' },
{ label: 'Migration guide', slug: 'v3/start/migration-guide' }
Expand All @@ -82,12 +82,12 @@ export default defineConfig({
{
label: 'Guides',
items: [
{ label: 'Merging styles', slug: 'v3/guides/merging-styles', badge: 'New!' },
{ label: 'Merging styles', slug: 'v3/guides/merging-styles', badge: 'Updated' },
{ label: 'Theming', slug: 'v3/guides/theming' },
{ label: 'Avoiding Keyboard', slug: 'v3/guides/avoiding-keyboard' },
{ label: 'Expo Router', slug: 'v3/guides/expo-router' },
{ label: 'Custom web', slug: 'v3/guides/custom-web', badge: 'WIP' },
{ label: 'Server side rendering', slug: 'v3/guides/server-side-rendering', badge: 'New!' },
{ label: 'Server side rendering', slug: 'v3/guides/server-side-rendering' },
]
},
{
Expand All @@ -102,11 +102,11 @@ export default defineConfig({
{ label: 'Variants', slug: 'v3/references/variants' },
{ label: 'Compound Variants', slug: 'v3/references/compound-variants' },
{ label: 'Web styles', slug: 'v3/references/web-styles' },
{ label: 'Web Only Features', slug: 'v3/references/web-only', badge: 'Updated' },
{ label: 'Web Only Features', slug: 'v3/references/web-only' },
{ label: 'Scoped theme', slug: 'v3/references/scoped-theme' },
{ label: 'Update 3rd party views', slug: 'v3/references/3rd-party-views', badge: 'New!' },
{ label: 'Update 3rd party views', slug: 'v3/references/3rd-party-views' },
{ label: 'withUnistyles', slug: 'v3/references/with-unistyles' },
{ label: 'useUnistyles', slug: 'v3/references/use-unistyles', badge: 'New!' },
{ label: 'useUnistyles', slug: 'v3/references/use-unistyles' },
{ label: 'Display and Hide', slug: 'v3/references/display-hide' },
{ label: 'Edge to edge', slug: 'v3/references/edge-to-edge' },
{ label: 'Dimensions', slug: 'v3/references/dimensions' },
Expand Down
4 changes: 4 additions & 0 deletions docs/src/content/docs/v3/guides/merging-styles.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ When you see this warning, your component will render correctly, but any new eve

It's critical to ship Unistyles 3.0 apps without this warning, as it can cause unexpected behavior.

<Aside title="Reanimated" type="caution">
By default `Animated` components will flatten your styles array. This means that you can only pass single unistyle to `Animated` components, otherwise you'll get a warning from above.
</Aside>

### Spreading a single Unistyle

Another problematic case is spreading a single Unistyle and merging it, e.g., with inline styles:
Expand Down
50 changes: 49 additions & 1 deletion docs/src/content/docs/v3/other/babel-plugin.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,57 @@ It can be useful for monorepos that use Unistyles with absolute paths, such as `
}
```

#### `autoRemapImports`

This is the most powerful option, but most likely you won't need to use it.
It allows you to remap uncommon imports to Unistyles components.

This may happen if a 3rd library does not import `react-native` components directly, but instead uses its own factory or a relative path.
Unistyles uses it internally to support the following imports from `react-native` internals:

```js
import { NativeText } from "react-native/Libraries/Text/TextNativeComponent"
import View from "react-native/Libraries/Components/View/ViewNativeComponent"
```

Let's say you have a library called `custom-library` that imports `react-native` raw components directly:

```js title="node_modules/custom-library/components/index.js"
import { NativeText } from "react-native/Libraries/Text/TextNativeComponent"
import View from "react-native/Libraries/Components/View/ViewNativeComponent"
```

To convert it to Unistyles, you can use following configuration:

```ts
{
autoRemapImports: [
path: 'node_modules/custom-library/components', // <- must be path from node_modules
imports: [
{
isDefault: false, // <- is default import?
name: 'NativeText', // <- if not, what's the import name?
path: 'react-native/Libraries/Text/TextNativeComponent' // <- what's the import source?
mapTo: 'NativeText' // <- which Unistyles component should be used? Check react-native-unistyles/src/components/native
},
{
isDefault: true,
path: 'react-native/Libraries/Components/View/ViewNativeComponent',
mapTo: 'NativeView'
}
]
]
}
```

:::caution
If you use raw `react-native` imports within your code, Unistyles will auto map it to `react-native-unistyles` factories.
This options should be only used for 3rd party libraries from `node_modules`.
:::

#### `autoProcessPaths`

This configuration is unrelated to the `autoProcessRoot` and `autoProcessImports` options and can be used alongside them.
This configuration is unrelated to the `autoProcessRoot`, `autoProcessImports` and `autoRemapImports` options and can be used alongside them.
By default, the Babel plugin ignores `node_modules`. However, you can extend these paths to attempt converting 3rd components into Unistyles compatible ones.
Within these paths, we will replace `react-native` imports with `react-native-unistyles` factories that borrow component refs. [Read more](/v3/other/babel-plugin#3-component-factory-borrowing-ref).

Expand Down
8 changes: 8 additions & 0 deletions docs/src/content/docs/v3/references/breakpoints.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ const styles = StyleSheet.create(theme => ({
}))
```

### Pixel/Point mode for native breakpoints

By default, Unistyles will use `pixels` for native breakpoints. This means that the breakpoints and [mq](/v3/references/media-queries) will be computed based on mobile screen pixels.
You can change this behavior by setting `nativeBreakpointsMode` to `points` in your [configuration](/v3/start/configuration#settings-optional).

If `nativeBreakpointsMode` is set to `points` all breakpoints and `mq` will be computed based on mobile screen points (screen in pixels divided by pixel ratio).


### Show/Hide your components based on breakpoints

In order to show or hide your components based on the screen size, you can leverage the `mq` utility and one of the two built-in components: `Display` and `Hide`.
Expand Down
3 changes: 2 additions & 1 deletion docs/src/content/docs/v3/start/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,12 @@ const breakpoints = {

### Settings (Optional)

The `Settings` object has been simplified, and in the most recent version, it supports only three properties:
The `Settings` object has been simplified, and in the most recent version, it supports only four properties:

- **`adaptiveThemes`** – a boolean that enables or disables adaptive themes [learn more](/v3/guides/theming#adaptive-themes)
- **`initialTheme`** – a string or a synchronous function that sets the initial theme
- **`CSSVars`** – a boolean that enables or disables CSS variables (defaults to `true`) [learn more](/v3/references/web-only#css-variables)
- **`nativeBreakpointsMode`** - iOS/Android only. User preferred mode for breakpoints. Can be either `points` or `pixels` (defaults to `pixels`) [learn more](/v3/references/breakpoints#pixelpoint-mode-for-native-breakpoints)

```tsx title="unistyles.ts"
const settings = {
Expand Down
2 changes: 1 addition & 1 deletion expo-example/unistyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ declare module 'react-native-unistyles' {

StyleSheet.configure({
settings: {
adaptiveThemes: true,
adaptiveThemes: true
},
breakpoints,
themes: {
Expand Down
112 changes: 79 additions & 33 deletions plugin/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,89 @@
type RemapImport = {
name?: string,
isDefault: boolean,
path: string,
mapTo: string
}

type RemapConfig = {
path: string,
imports: Array<RemapImport>
}

export interface UnistylesPluginOptions {
/**
* Example: "src" or "apps/mobile"
* Add this option if some of your components don't have `react-native-unistyles` import.
* Babel plugin will automatically process all files under this root.
*/
autoProcessRoot?: string

/**
* Example: ['@codemask/styles']
* Enable this option if you want to process only files containing specific imports.
*/
autoProcessImports?: string[]

/**
* Example: ['external-library/components']
* Enable this option to process some 3rd party components under `node_modules`.
* Under these paths we will replace `react-native` imports with `react-native-unistyles` factories that will borrow components refs [read more](https://www.unistyl.es/v3/other/babel-plugin#3-component-factory-borrowing-ref).
*
* Defaults to:
*
* ```ts
* ['react-native-reanimated/src/component', 'react-native-gesture-handler/src/components']
* ```
*/
autoProcessPaths?: string[];

/**
* In order to list detected dependencies by the Babel plugin you can enable the `debug` flag.
* It will `console.log` name of the file and component with Unistyles dependencies.
*/
debug?: boolean;

/**
* Only applicable for Unistyles monorepo for
* path resolution, don't use it!
*/
isLocal?: boolean;
autoProcessRoot?: string,

/**
* Example: ['@codemask/styles']
* Enable this option if you want to process only files containing specific imports.
*/
autoProcessImports?: Array<string>,

/**
* Example: [{
* path: "node_modules/custom-library/components",
* imports: [
* {
* name: "NativeText",
* isDefault: false,
* path: "react-native/Libraries/Text/TextNativeComponent",
* mapTo: "NativeText"
* },
* {
* isDefault: true,
* path: "react-native/Libraries/Components/View/ViewNativeComponent",
* mapTo: "NativeView"
* }
* ]
* }]
*
* Will map:
* import { NativeText } from "react-native/Libraries/Text/TextNativeComponent"
* to Unistyles "NativeText"
*
* import View from "react-native/Libraries/Components/View/ViewNativeComponent"
* to Unistyles "NativeView"
*
* This is the most powerful way of remapping imports. If 3rd party library uses imports different from `react-native` we can remap them to `react-native-unistyles` factories.
* Internally we do that for raw RCTView and RCTText components.
*
* path -> must be within node_modules folder
* imports.name is Optional if library used export default
* imports.mapTo - name of the component from react-native-unistyles/src/components/native
*/
autoRemapImports?: Array<RemapConfig>,

/**
* Example: ['external-library/components']
* Enable this option to process some 3rd party components under `node_modules`.
* Under these paths we will replace `react-native` imports with `react-native-unistyles` factories that will borrow components refs [read more](https://www.unistyl.es/v3/other/babel-plugin#3-component-factory-borrowing-ref).
*
* Defaults to:
*
* ```ts
* ['react-native-reanimated/src/component', 'react-native-gesture-handler/src/components']
* ```
*/
autoProcessPaths?: Array<string>,

/**
* In order to list detected dependencies by the Babel plugin you can enable the `debug` flag.
* It will `console.log` name of the file and component with Unistyles dependencies.
*/
debug?: boolean,

/**
* Only applicable for Unistyles monorepo for
* path resolution, don't use it!
*/
isLocal?: boolean
}

export interface UnistylesPluginPass {
opts: UnistylesPluginOptions;
opts: UnistylesPluginOptions
}
1 change: 1 addition & 0 deletions plugin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ module.exports = function ({ types: t }) {
/** @param {import('./index').UnistylesPluginPass} state */
ImportDeclaration(path, state) {
const exoticImport = REPLACE_WITH_UNISTYLES_EXOTIC_PATHS
.concat(state.opts.autoRemapImports ?? [])
.find(exotic => state.filename.includes(exotic.path))

if (exoticImport) {
Expand Down
Loading