Skip to content

Commit a78c2a3

Browse files
committed
Add support for setting a custom viewport width
1 parent c5251b8 commit a78c2a3

13 files changed

Lines changed: 302 additions & 15 deletions

packages/react-strict-dom/src/native/index.js

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@ import type {
1818
import typeof * as TStyleX from '@stylexjs/stylex';
1919

2020
import * as React from 'react';
21+
import { useMemo } from 'react';
2122
import * as compat from './compat';
2223
import * as html from './html';
2324
import * as stylex from './stylex';
2425
import { ProvideCustomProperties } from './modules/ContextCustomProperties';
26+
import { ProvideViewportScale } from './modules/ContextViewportScale';
27+
import * as ReactNative from './react-native';
2528

2629
type StyleTheme<V, T> = Theme<V, T>;
2730
type StyleVars<T> = VarGroup<T>;
@@ -35,6 +38,11 @@ type ProviderProps = $ReadOnly<{
3538
customProperties: ProviderValue
3639
}>;
3740

41+
type ViewportProviderProps = $ReadOnly<{
42+
children: React.Node,
43+
viewportWidth: number
44+
}>;
45+
3846
export type { StaticStyles, StyleTheme, StyleVars, Styles, StylesWithout };
3947

4048
function ThemeProvider(props: ProviderProps): React.Node {
@@ -47,8 +55,29 @@ function ThemeProvider(props: ProviderProps): React.Node {
4755
);
4856
}
4957

58+
function ViewportProvider({
59+
viewportWidth: logicalViewportWidth,
60+
children
61+
}: ViewportProviderProps): React.Node {
62+
const { width: viewportWidth } = ReactNative.useWindowDimensions();
63+
64+
const viewportScale = useMemo(
65+
() => ({
66+
scale: viewportWidth / logicalViewportWidth
67+
}),
68+
[logicalViewportWidth, viewportWidth]
69+
);
70+
71+
return (
72+
<ProvideViewportScale value={viewportScale}>
73+
{children}
74+
</ProvideViewportScale>
75+
);
76+
}
77+
5078
const contexts = {
51-
ThemeProvider: ThemeProvider as typeof ThemeProvider
79+
ThemeProvider: ThemeProvider as typeof ThemeProvider,
80+
ViewportProvider: ViewportProvider as typeof ViewportProvider
5281
};
5382

5483
// Export using StyleX types as the shim has divergent types internally.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict
8+
*/
9+
10+
import React from 'react';
11+
12+
type ViewportScale = $ReadOnly<{
13+
scale: number
14+
}>;
15+
16+
const ContextViewportScale: React.Context<ViewportScale> = React.createContext({
17+
scale: 1
18+
});
19+
20+
if (__DEV__) {
21+
ContextViewportScale.displayName = 'ContextViewportScale';
22+
}
23+
24+
export const ProvideViewportScale = ContextViewportScale.Provider;
25+
26+
export function useViewportScale(): ViewportScale {
27+
return React.useContext(ContextViewportScale);
28+
}

packages/react-strict-dom/src/native/modules/useStrictDOMElement.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import * as React from 'react';
1313

1414
import { useElementCallback } from '../../shared/useElementCallback';
1515
import { errorMsg } from '../../shared/logUtils';
16+
import { useViewportScale } from './ContextViewportScale';
1617

1718
function errorUnimplemented(name: string) {
1819
if (__DEV__) {
@@ -33,6 +34,7 @@ type Options = {
3334
};
3435

3536
export function useStrictDOMElement<T>({ tagName }: Options): CallbackRef<T> {
37+
const { scale: viewportScale } = useViewportScale();
3638
const elementCallback = useElementCallback(
3739
React.useCallback(
3840
// $FlowFixMe[unclear-type]
@@ -83,6 +85,20 @@ export function useStrictDOMElement<T>({ tagName }: Options): CallbackRef<T> {
8385
};
8486
}
8587

88+
if (viewportScale !== 1) {
89+
const getBoundingClientRect = node.getBoundingClientRect;
90+
node.getBoundingClientRect = function () {
91+
const rect = getBoundingClientRect.call(node);
92+
93+
return new DOMRect(
94+
rect.x / viewportScale,
95+
rect.y / viewportScale,
96+
rect.width / viewportScale,
97+
rect.height / viewportScale
98+
);
99+
};
100+
}
101+
86102
const { getRootNode } = node;
87103
if (getRootNode == null) {
88104
node.getRootNode = () => errorUnimplemented('getRootNode');
@@ -157,7 +173,7 @@ export function useStrictDOMElement<T>({ tagName }: Options): CallbackRef<T> {
157173
}
158174
}
159175
},
160-
[tagName]
176+
[tagName, viewportScale]
161177
)
162178
);
163179

packages/react-strict-dom/src/native/modules/useStyleProps.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { flattenStyle } from './flattenStyle';
2020
import { useInheritedStyles } from './ContextInheritedStyles';
2121
import { usePseudoStates } from './usePseudoStates';
2222
import { useStyleTransition } from './useStyleTransition';
23+
import { useViewportScale } from './ContextViewportScale';
2324

2425
type StyleOptions = {
2526
customProperties: ?CustomProperties,
@@ -66,6 +67,7 @@ export function useStyleProps(
6667

6768
const { fontScale, height, width } = ReactNative.useWindowDimensions();
6869
const colorScheme = ReactNative.useColorScheme();
70+
const { scale: viewportScale } = useViewportScale();
6971

7072
// These values are already computed
7173
const {
@@ -104,6 +106,7 @@ export function useStyleProps(
104106
inheritedFontSize:
105107
typeof inheritedFontSize === 'number' ? inheritedFontSize : undefined,
106108
viewportHeight: height,
109+
viewportScale,
107110
viewportWidth: width
108111
},
109112
flatStyle as $FlowFixMe

packages/react-strict-dom/src/native/stylex/CSSLengthUnitValue.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type ResolvePixelValueOptions = $ReadOnly<{
1717
fontScale: number | void,
1818
inheritedFontSize: ?number,
1919
viewportHeight: number,
20+
viewportScale: number,
2021
viewportWidth: number
2122
}>;
2223

@@ -58,7 +59,8 @@ export class CSSLengthUnitValue {
5859
viewportWidth,
5960
viewportHeight,
6061
fontScale = 1,
61-
inheritedFontSize
62+
inheritedFontSize,
63+
viewportScale
6264
} = options;
6365
const unit = this.unit;
6466
const value = this.value;
@@ -72,10 +74,10 @@ export class CSSLengthUnitValue {
7274
}
7375
}
7476
case 'px': {
75-
return value;
77+
return value * viewportScale;
7678
}
7779
case 'rem': {
78-
return fontScale * 16 * value;
80+
return fontScale * 16 * value * viewportScale;
7981
}
8082
case 'vh': {
8183
return viewportHeight * valuePercent;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict
8+
*/
9+
10+
import type { ReactNativeTransform } from '../../types/renderer.native';
11+
12+
export class CSSTransformValue {
13+
value: $ReadOnlyArray<ReactNativeTransform>;
14+
cachedScaledTransform: void | {
15+
viewportScale: number,
16+
value: $ReadOnlyArray<ReactNativeTransform>
17+
};
18+
19+
constructor(value: $ReadOnlyArray<ReactNativeTransform>) {
20+
this.value = value;
21+
}
22+
23+
resolveTransformValue(
24+
viewportScale: number
25+
): $ReadOnlyArray<ReactNativeTransform> {
26+
if (viewportScale === 1) {
27+
return this.value;
28+
}
29+
if (
30+
this.cachedScaledTransform != null &&
31+
this.cachedScaledTransform.viewportScale === viewportScale
32+
) {
33+
return this.cachedScaledTransform.value;
34+
}
35+
36+
const scaledTransform = this.value.map((transform) => {
37+
if (
38+
transform.translateX != null &&
39+
typeof transform.translateX === 'number'
40+
) {
41+
return { translateX: transform.translateX * viewportScale };
42+
}
43+
if (
44+
transform.translateY != null &&
45+
typeof transform.translateY === 'number'
46+
) {
47+
return { translateY: transform.translateY * viewportScale };
48+
}
49+
return transform;
50+
});
51+
52+
this.cachedScaledTransform = {
53+
viewportScale,
54+
value: scaledTransform
55+
};
56+
57+
return scaledTransform;
58+
}
59+
}

packages/react-strict-dom/src/native/stylex/__tests__/parseTransform-test.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import { parseTransform } from '../parseTransform';
8+
import { parseTransform as parseTransformImpl } from '../parseTransform';
9+
10+
function parseTransform(transform) {
11+
return parseTransformImpl(transform).resolveTransformValue(1);
12+
}
913

1014
describe('parseTransform', () => {
1115
test('perspective', () => {

packages/react-strict-dom/src/native/stylex/index.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import {
3434
stringContainsVariables
3535
} from './customProperties';
3636
import { version } from '../modules/version';
37+
import { CSSTransformValue } from './CSSTransformValue';
38+
// import { LENGTH_PROPS } from './lengthProps';
3739

3840
type ResolveStyleOptions = $ReadOnly<{
3941
active?: ?boolean,
@@ -44,6 +46,7 @@ type ResolveStyleOptions = $ReadOnly<{
4446
hover?: ?boolean,
4547
inheritedFontSize: ?number,
4648
viewportHeight: number,
49+
viewportScale: number,
4750
viewportWidth: number,
4851
writingDirection?: ?'ltr' | 'rtl'
4952
}>;
@@ -295,6 +298,7 @@ function resolveStyle(
295298
hover,
296299
inheritedFontSize,
297300
viewportHeight,
301+
viewportScale,
298302
viewportWidth
299303
} = options;
300304
const colorScheme = options.colorScheme || 'light';
@@ -331,6 +335,7 @@ function resolveStyle(
331335
fontScale,
332336
inheritedFontSize: inheritedFontSize,
333337
viewportHeight,
338+
viewportScale,
334339
viewportWidth
335340
});
336341
continue;
@@ -343,6 +348,7 @@ function resolveStyle(
343348
fontScale,
344349
inheritedFontSize: fontSize,
345350
viewportHeight,
351+
viewportScale,
346352
viewportWidth
347353
});
348354
} else {
@@ -352,6 +358,19 @@ function resolveStyle(
352358
}
353359
}
354360

361+
if (styleValue instanceof CSSTransformValue) {
362+
result[propName] = styleValue.resolveTransformValue(viewportScale);
363+
continue;
364+
}
365+
// if (
366+
// viewportScale !== 1 &&
367+
// typeof styleValue === 'number' &&
368+
// LENGTH_PROPS.has(propName)
369+
// ) {
370+
// result[propName] = styleValue * viewportScale;
371+
// continue;
372+
// }
373+
355374
// Resolve the stylex object-value syntax
356375
if (
357376
styleValue != null &&
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict
8+
*/
9+
10+
function allSides(fn: (side: 'Top' | 'Right' | 'Bottom' | 'Left') => string) {
11+
return [fn('Top'), fn('Right'), fn('Bottom'), fn('Left')];
12+
}
13+
14+
export const LENGTH_PROPS: Set<string> = new Set([
15+
'margin',
16+
...allSides((prop) => `margin${prop}`),
17+
'padding',
18+
...allSides((prop) => `padding${prop}`),
19+
...allSides((prop) => prop.toLowerCase()),
20+
'maxHeight',
21+
'maxWidth',
22+
'minHeight',
23+
'minWidth',
24+
'height',
25+
'width',
26+
'marginInline',
27+
'marginBlock',
28+
'marginBlockStart',
29+
'marginBlockEnd',
30+
'marginInlineStart',
31+
'marginInlineEnd',
32+
'paddingInline',
33+
'paddingBlock',
34+
'paddingBlockStart',
35+
'paddingBlockEnd',
36+
'paddingInlineStart',
37+
'paddingInlineEnd',
38+
'borderWidth',
39+
...allSides((prop) => `border${prop}Width`),
40+
'borderRadius',
41+
'borderTopLeftRadius',
42+
'borderTopRightRadius',
43+
'borderBottomLeftRadius',
44+
'borderBottomRightRadius',
45+
'outlineWidth',
46+
...allSides((prop) => `outline${prop}Width`),
47+
'outlineOffset',
48+
...allSides((prop) => `${prop}Offset`),
49+
'gap',
50+
'columnGap',
51+
'rowGap'
52+
]);

0 commit comments

Comments
 (0)