Skip to content

Commit

Permalink
[change] Move all non-standard CSS transforms to 'preprocess' step
Browse files Browse the repository at this point in the history
This patch reorganizes the style compiler so that the 'preprocess' step
is responsible for all the work needed to transform any non-standard CSS
from React Native into a form that can be 'compiled' to rules for the
CSSStyleSheet.

Over time the 'preprocess' step should eventually be unnecessary as
React Native aligns its APIs with CSS APIs. And any external style
compilers should be able to run the 'preprocess' function over the style
input to produce valid CSS as input for the compiler.
  • Loading branch information
necolas committed Mar 20, 2023
1 parent 43b463b commit bc6e02e
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 179 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,6 @@ describe('compiler/createReactDOMStyle', () => {
`);
});

test('aspectRatio', () => {
expect(createReactDOMStyle({ aspectRatio: 9 / 16 })).toEqual({
aspectRatio: '0.5625'
});
});

describe('flexbox styles', () => {
test('flex: -1', () => {
expect(createReactDOMStyle({ flex: -1 })).toEqual({
Expand Down Expand Up @@ -190,70 +184,4 @@ describe('compiler/createReactDOMStyle', () => {
`);
});
});

test('fontVariant', () => {
expect(
createReactDOMStyle({ fontVariant: 'common-ligatures small-caps' })
).toEqual({
fontVariant: 'common-ligatures small-caps'
});

expect(
createReactDOMStyle({ fontVariant: ['common-ligatures', 'small-caps'] })
).toEqual({
fontVariant: 'common-ligatures small-caps'
});
});

test('textAlignVertical', () => {
expect(
createReactDOMStyle({
textAlignVertical: 'center'
})
).toEqual({
verticalAlign: 'middle'
});
});

test('verticalAlign', () => {
expect(
createReactDOMStyle({
verticalAlign: 'top',
textAlignVertical: 'center'
})
).toEqual({
verticalAlign: 'top'
});
});

describe('transform', () => {
// passthrough if transform value is ever a string
test('string', () => {
const transform =
'perspective(50px) scaleX(20) translateX(20px) rotate(20deg)';
const style = { transform };
const resolved = createReactDOMStyle(style);

expect(resolved).toEqual({ transform });
});

test('array', () => {
const style = {
transform: [
{ perspective: 50 },
{ scaleX: 20 },
{ translateX: 20 },
{ rotate: '20deg' },
{ matrix: [1, 2, 3, 4, 5, 6] },
{ matrix3d: [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4] }
]
};
const resolved = createReactDOMStyle(style);

expect(resolved).toEqual({
transform:
'perspective(50px) scaleX(20) translateX(20px) rotate(20deg) matrix(1,2,3,4,5,6) matrix3d(1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4)'
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('StyleSheet/compile', () => {
describe('atomic', () => {
test('converts style to atomic CSS', () => {
const result = atomic({
animationDirection: ['alternate', 'alternate-reverse'],
animationDirection: 'alternate,alternate-reverse',
animationKeyframes: [
{ '0%': { top: 0 }, '50%': { top: 5 }, '100%': { top: 10 } },
{ from: { left: 0 }, to: { left: 10 } }
Expand All @@ -34,7 +34,7 @@ describe('StyleSheet/compile', () => {
{
"$$css": true,
"$$css$localize": true,
"animationDirection": "r-animationDirection-1kmv48j",
"animationDirection": "r-animationDirection-1wgwto7",
"animationKeyframes": "r-animationKeyframes-zacbmr",
"fontFamily": "r-fontFamily-1qd0xha",
"insetInlineStart": [
Expand Down Expand Up @@ -63,7 +63,7 @@ describe('StyleSheet/compile', () => {
[
[
[
".r-animationDirection-1kmv48j{animation-direction:alternate,alternate-reverse;}",
".r-animationDirection-1wgwto7{animation-direction:alternate,alternate-reverse;}",
],
3,
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,76 @@ describe('StyleSheet/preprocess', () => {
paddingInlineStart: 2
});
});

test('converts non-standard textAlignVertical', () => {
expect(
preprocess({
textAlignVertical: 'center'
})
).toEqual({
verticalAlign: 'middle'
});

expect(
preprocess({
verticalAlign: 'top',
textAlignVertical: 'center'
})
).toEqual({
verticalAlign: 'top'
});
});

test('aspectRatio', () => {
expect(preprocess({ aspectRatio: 9 / 16 })).toEqual({
aspectRatio: '0.5625'
});
});

test('fontVariant', () => {
expect(
preprocess({ fontVariant: 'common-ligatures small-caps' })
).toEqual({
fontVariant: 'common-ligatures small-caps'
});

expect(
preprocess({ fontVariant: ['common-ligatures', 'small-caps'] })
).toEqual({
fontVariant: 'common-ligatures small-caps'
});
});

describe('transform', () => {
// passthrough if transform value is ever a string
test('string', () => {
const transform =
'perspective(50px) scaleX(20) translateX(20px) rotate(20deg)';
const style = { transform };
const resolved = preprocess(style);

expect(resolved).toEqual({ transform });
});

test('array', () => {
const style = {
transform: [
{ perspective: 50 },
{ scaleX: 20 },
{ translateX: 20 },
{ rotate: '20deg' },
{ matrix: [1, 2, 3, 4, 5, 6] },
{ matrix3d: [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4] }
]
};
const resolved = preprocess(style);

expect(resolved).toEqual({
transform:
'perspective(50px) scaleX(20) translateX(20px) rotate(20deg) matrix(1,2,3,4,5,6) matrix3d(1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4)'
});
});
});
});

describe('preprocesses multiple shadow styles into a single declaration', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

import normalizeValueWithProperty from './normalizeValueWithProperty';
import canUseDOM from '../../../modules/canUseDom';
import { warnOnce } from '../../../modules/warnOnce';

type Style = { [key: string]: any };

Expand All @@ -33,13 +32,6 @@ const supportsCSS3TextDecoration =
(window.CSS.supports('text-decoration-line', 'none') ||
window.CSS.supports('-webkit-text-decoration-line', 'none')));

const ignoredProps = {
elevation: true,
overlayColor: true,
resizeMode: true,
tintColor: true
};

const MONOSPACE_FONT_STACK = 'monospace,monospace';

const SYSTEM_FONT_STACK =
Expand Down Expand Up @@ -114,36 +106,6 @@ const STYLE_SHORT_FORM_EXPANSIONS = {
//paddingInlineEnd: ['marginRight'],
};

/**
* Transform
*/

// { scale: 2 } => 'scale(2)'
// { translateX: 20 } => 'translateX(20px)'
// { matrix: [1,2,3,4,5,6] } => 'matrix(1,2,3,4,5,6)'
const mapTransform = (transform: Object): string => {
const type = Object.keys(transform)[0];
const value = transform[type];
if (type === 'matrix' || type === 'matrix3d') {
return `${type}(${value.join(',')})`;
} else {
const normalizedValue = normalizeValueWithProperty(value, type);
return `${type}(${normalizedValue})`;
}
};

export const createTransformValue = (style: Style): string => {
let transform = style.transform;
if (Array.isArray(style.transform)) {
warnOnce(
'transform',
'"transform" style array value is deprecated. Use space-separated string functions, e.g., "scaleX(2) rotateX(15deg)".'
);
transform = style.transform.map(mapTransform).join(' ');
}
return transform;
};

/**
* Reducer
*/
Expand All @@ -160,16 +122,12 @@ const createReactDOMStyle = (style: Style, isInline?: boolean): Style => {

if (
// Ignore everything with a null value
value == null ||
// Ignore some React Native styles
ignoredProps[prop]
value == null
) {
continue;
}

if (prop === 'aspectRatio') {
resolvedStyle[prop] = value.toString();
} else if (prop === 'backgroundClip') {
if (prop === 'backgroundClip') {
// TODO: remove once this issue is fixed
// https://github.com/rofrischmann/inline-style-prefixer/issues/159
if (value === 'text') {
Expand All @@ -196,24 +154,6 @@ const createReactDOMStyle = (style: Style, isInline?: boolean): Style => {
} else {
resolvedStyle[prop] = value;
}
} else if (prop === 'fontVariant') {
if (Array.isArray(value) && value.length > 0) {
warnOnce(
'fontVariant',
'"fontVariant" style array value is deprecated. Use space-separated values.'
);
resolvedStyle.fontVariant = value.join(' ');
} else {
resolvedStyle[prop] = value;
}
} else if (prop === 'textAlignVertical') {
warnOnce(
'textAlignVertical',
'"textAlignVertical" style is deprecated. Use "verticalAlign".'
);
if (resolvedStyle.verticalAlign == null) {
resolvedStyle.verticalAlign = value === 'center' ? 'middle' : value;
}
} else if (prop === 'textDecorationLine') {
// use 'text-decoration' for browsers that only support CSS2
// text-decoration (e.g., IE, Edge)
Expand All @@ -222,8 +162,6 @@ const createReactDOMStyle = (style: Style, isInline?: boolean): Style => {
} else {
resolvedStyle.textDecorationLine = value;
}
} else if (prop === 'transform' || prop === 'transformMatrix') {
resolvedStyle.transform = createTransformValue(style);
} else if (prop === 'writingDirection') {
resolvedStyle.direction = value;
} else {
Expand Down Expand Up @@ -265,7 +203,7 @@ const createReactDOMStyle = (style: Style, isInline?: boolean): Style => {
}
});
} else {
resolvedStyle[prop] = Array.isArray(value) ? value.join(',') : value;
resolvedStyle[prop] = value;
}
}
}
Expand Down
Loading

0 comments on commit bc6e02e

Please sign in to comment.