Skip to content
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
1 change: 1 addition & 0 deletions apps/examples/src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const themedStyles = css.create({
container: {
display: 'flex',
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
backgroundColor: '#bbb',
padding: 8
Expand Down
2 changes: 2 additions & 0 deletions packages/benchmarks/perf/tests/css-props-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ function runSuite(opts) {
hover: true,
inheritedFontSize: 16,
viewportHeight: 600,
viewportScale: 1,
viewportWidth: 1024
};

Expand All @@ -29,6 +30,7 @@ function runSuite(opts) {
hover: true,
inheritedFontSize: 16,
viewportHeight: 600,
viewportScale: 1,
viewportWidth: 1024
};

Expand Down
4 changes: 3 additions & 1 deletion packages/react-strict-dom/src/native/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import * as compat from './compat';
import * as html from './html';
import * as stylex from './stylex';
import { ProvideCustomProperties } from './modules/ContextCustomProperties';
import { ProvideViewportScale } from './modules/ContextViewportScale';

type StyleTheme<V, T> = Theme<V, T>;
type StyleVars<T> = VarGroup<T>;
Expand All @@ -48,7 +49,8 @@ function ThemeProvider(props: ProviderProps): React.Node {
}

const contexts = {
ThemeProvider: ThemeProvider as typeof ThemeProvider
ThemeProvider: ThemeProvider as typeof ThemeProvider,
ViewportProvider: ProvideViewportScale as typeof ProvideViewportScale
};

// Export using StyleX types as the shim has divergent types internally.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
*/

import * as React from 'react';
import * as ReactNative from '../react-native';

type Value = $ReadOnly<{
scale: number
}>;

type ProviderProps = $ReadOnly<{
children: React.Node,
viewportWidth: number
}>;

const defaultContext = { scale: 1 };
const ContextViewportScale: React.Context<Value> =
React.createContext(defaultContext);

if (__DEV__) {
ContextViewportScale.displayName = 'ContextViewportScale';
}

export function ProvideViewportScale({
viewportWidth: logicalViewportWidth,
children
}: ProviderProps): React.Node {
const { width: viewportWidth } = ReactNative.useWindowDimensions();

const viewportScale = React.useMemo(
() => ({
scale: viewportWidth / logicalViewportWidth
}),
[logicalViewportWidth, viewportWidth]
);

return (
<ContextViewportScale.Provider value={viewportScale}>
{children}
</ContextViewportScale.Provider>
);
}

export function useViewportScale(): Value {
return React.useContext(ContextViewportScale);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @flow strict-local
*/

import type { CallbackRef } from '../../types/react';

import * as React from 'react';

import { useElementCallback } from '../../shared/useElementCallback';
import { errorMsg } from '../../shared/logUtils';
import { useElementCallback } from '../../shared/useElementCallback';
import { useViewportScale } from './ContextViewportScale';

function errorUnimplemented(name: string) {
if (__DEV__) {
Expand All @@ -33,6 +34,7 @@ type Options = {
};

export function useStrictDOMElement<T>({ tagName }: Options): CallbackRef<T> {
const { scale: viewportScale } = useViewportScale();
const elementCallback = useElementCallback(
React.useCallback(
// $FlowFixMe[unclear-type]
Expand Down Expand Up @@ -83,6 +85,20 @@ export function useStrictDOMElement<T>({ tagName }: Options): CallbackRef<T> {
};
}

if (viewportScale !== 1) {
const getBoundingClientRect = node.getBoundingClientRect;
node.getBoundingClientRect = function () {
const rect = getBoundingClientRect.call(node);

return new DOMRect(
rect.x / viewportScale,
rect.y / viewportScale,
rect.width / viewportScale,
rect.height / viewportScale
);
};
}

const { getRootNode } = node;
if (getRootNode == null) {
node.getRootNode = () => errorUnimplemented('getRootNode');
Expand Down Expand Up @@ -157,7 +173,7 @@ export function useStrictDOMElement<T>({ tagName }: Options): CallbackRef<T> {
}
}
},
[tagName]
[tagName, viewportScale]
)
);

Expand Down
3 changes: 3 additions & 0 deletions packages/react-strict-dom/src/native/modules/useStyleProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { flattenStyle } from './flattenStyle';
import { useInheritedStyles } from './ContextInheritedStyles';
import { usePseudoStates } from './usePseudoStates';
import { useStyleTransition } from './useStyleTransition';
import { useViewportScale } from './ContextViewportScale';

type StyleOptions = {
customProperties: ?CustomProperties,
Expand Down Expand Up @@ -66,6 +67,7 @@ export function useStyleProps(

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

// These values are already computed
const {
Expand Down Expand Up @@ -104,6 +106,7 @@ export function useStyleProps(
inheritedFontSize:
typeof inheritedFontSize === 'number' ? inheritedFontSize : undefined,
viewportHeight: height,
viewportScale,
viewportWidth: width
},
flatStyle as $FlowFixMe
Expand Down
26 changes: 13 additions & 13 deletions packages/react-strict-dom/src/native/stylex/CSSLengthUnitValue.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,15 @@ type ResolvePixelValueOptions = $ReadOnly<{
fontScale: number | void,
inheritedFontSize: ?number,
viewportHeight: number,
viewportScale: number,
viewportWidth: number
}>;

type ParsedValue = [+value: number, +unit: CSSLengthUnitType] | null;

const memoizedValues = new Map<string, ParsedValue>();
const memoizedValues = new Map<string, CSSLengthUnitValue | null>();

// TODO: this only works on simple values
export class CSSLengthUnitValue {
static parse(input: string): ParsedValue {
static parse(input: string): CSSLengthUnitValue | null {
const memoizedValue = memoizedValues.get(input);
if (memoizedValue !== undefined) {
return memoizedValue;
Expand All @@ -40,9 +39,9 @@ export class CSSLengthUnitValue {
const value = match[1];
const unit: $FlowFixMe = match[2];
const parsedFloat: number = parseFloat(value);
const parsedValue: ParsedValue = [parsedFloat, unit];
memoizedValues.set(input, parsedValue);
return parsedValue;
const cssLengthUnitValue = new CSSLengthUnitValue(parsedFloat, unit);
memoizedValues.set(input, cssLengthUnitValue);
return cssLengthUnitValue;
}

value: number;
Expand All @@ -55,27 +54,28 @@ export class CSSLengthUnitValue {

resolvePixelValue(options: ResolvePixelValueOptions): number {
const {
viewportWidth,
viewportHeight,
fontScale = 1,
inheritedFontSize
inheritedFontSize,
viewportHeight,
viewportScale,
viewportWidth
} = options;
const unit = this.unit;
const value = this.value;
const valuePercent = value / 100;
switch (unit) {
case 'em': {
if (inheritedFontSize == null) {
return fontScale * 16 * value;
return fontScale * 16 * value * viewportScale;
} else {
return inheritedFontSize * value;
}
}
case 'px': {
return value;
return value * viewportScale;
}
case 'rem': {
return fontScale * 16 * value;
return fontScale * 16 * value * viewportScale;
}
case 'vh': {
return viewportHeight * valuePercent;
Expand Down
59 changes: 59 additions & 0 deletions packages/react-strict-dom/src/native/stylex/CSSTransformValue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
*/

import type { ReactNativeTransform } from '../../types/renderer.native';

export class CSSTransformValue {
value: $ReadOnlyArray<ReactNativeTransform>;
cachedScaledTransform: void | {
viewportScale: number,
value: $ReadOnlyArray<ReactNativeTransform>
};

constructor(value: $ReadOnlyArray<ReactNativeTransform>) {
this.value = value;
}

resolveTransformValue(
viewportScale: number
): $ReadOnlyArray<ReactNativeTransform> {
if (viewportScale === 1) {
return this.value;
}
if (
this.cachedScaledTransform != null &&
this.cachedScaledTransform.viewportScale === viewportScale
) {
return this.cachedScaledTransform.value;
}

const scaledTransform = this.value.map((transform) => {
if (
transform.translateX != null &&
typeof transform.translateX === 'number'
) {
return { translateX: transform.translateX * viewportScale };
}
if (
transform.translateY != null &&
typeof transform.translateY === 'number'
) {
return { translateY: transform.translateY * viewportScale };
}
return transform;
});

this.cachedScaledTransform = {
viewportScale,
value: scaledTransform
};

return scaledTransform;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
* LICENSE file in the root directory of this source tree.
*/

import { parseTransform } from '../parseTransform';
import { parseTransform as parseTransformImpl } from '../parseTransform';

function parseTransform(transform) {
return parseTransformImpl(transform).resolveTransformValue(1);
}

describe('parseTransform', () => {
test('perspective', () => {
Expand Down
21 changes: 20 additions & 1 deletion packages/react-strict-dom/src/native/stylex/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ import type {
} from '../../types/styles';

import { CSSLengthUnitValue } from './CSSLengthUnitValue';
import { CSSTransformValue } from './CSSTransformValue';
import { CSSUnparsedValue } from './typed-om/CSSUnparsedValue';
import { errorMsg, warnMsg } from '../../shared/logUtils';
import { fixContentBox } from './fixContentBox';
import { flattenStyle } from './flattenStyleXStyles';
import { isAllowedShortFormValue } from './isAllowedShortFormValue';
import { isAllowedStyleKey } from './isAllowedStyleKey';
import { lengthStyleKeySet } from './isLengthStyleKey';
import { mediaQueryMatches } from './mediaQueryMatches';
import { parseTextShadow } from './parseTextShadow';
import { parseTimeValue } from './parseTimeValue';
Expand All @@ -44,6 +46,7 @@ type ResolveStyleOptions = $ReadOnly<{
hover?: ?boolean,
inheritedFontSize: ?number,
viewportHeight: number,
viewportScale: number,
viewportWidth: number,
writingDirection?: ?'ltr' | 'rtl'
}>;
Expand Down Expand Up @@ -197,7 +200,7 @@ function processStyle(

const maybeLengthUnitValue = CSSLengthUnitValue.parse(styleValue);
if (maybeLengthUnitValue != null) {
result[propName] = new CSSLengthUnitValue(...maybeLengthUnitValue);
result[propName] = maybeLengthUnitValue;
continue;
// React Native doesn't support these keywords or functions
} else if (styleValue === 'inherit' || styleValue === 'unset') {
Expand Down Expand Up @@ -295,6 +298,7 @@ function resolveStyle(
hover,
inheritedFontSize,
viewportHeight,
viewportScale,
viewportWidth
} = options;
const colorScheme = options.colorScheme || 'light';
Expand Down Expand Up @@ -331,6 +335,7 @@ function resolveStyle(
fontScale,
inheritedFontSize: inheritedFontSize,
viewportHeight,
viewportScale,
viewportWidth
});
continue;
Expand All @@ -343,6 +348,7 @@ function resolveStyle(
fontScale,
inheritedFontSize: fontSize,
viewportHeight,
viewportScale,
viewportWidth
});
} else {
Expand All @@ -352,6 +358,19 @@ function resolveStyle(
}
}

if (styleValue instanceof CSSTransformValue) {
result[propName] = styleValue.resolveTransformValue(viewportScale);
continue;
}
if (
viewportScale !== 1 &&
typeof styleValue === 'number' &&
lengthStyleKeySet.has(propName)
) {
result[propName] = styleValue * viewportScale;
continue;
}

// Resolve the stylex object-value syntax
if (
styleValue != null &&
Expand Down
Loading