From 5241bcf77200888dcf750d376706e0cc6c2f2960 Mon Sep 17 00:00:00 2001 From: eleven Date: Thu, 13 Feb 2025 10:04:32 +0800 Subject: [PATCH] =?UTF-8?q?feat(rn):=20=E5=A2=9E=E5=8A=A0css=E4=B8=AD?= =?UTF-8?q?=E5=AF=B9=E7=99=BE=E5=88=86=E6=AF=94=E5=8D=95=E4=BD=8D=E7=9A=84?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/index.spec.js | 111 ++++++++++++------ .../transforms/transform.js | 25 ++-- 2 files changed, 88 insertions(+), 48 deletions(-) diff --git a/packages/css-to-react-native/__tests__/index.spec.js b/packages/css-to-react-native/__tests__/index.spec.js index ffb43c35811b..fc354147ae83 100644 --- a/packages/css-to-react-native/__tests__/index.spec.js +++ b/packages/css-to-react-native/__tests__/index.spec.js @@ -1,10 +1,52 @@ -import transform from '../src/index' +import transform from '../src' describe('misc', () => { it('returns empty object when input is empty', () => { expect(transform('')).toEqual({}) }) + it('transform percent', () => { + expect( + transform(` + .test { + transform: translateY(100%) translateX(100%); + } + `) + ).toEqual({ + test: { + transform: [{ translateX: '100%' }, { translateY: '100%' }] + } + }) + }) + + it('transform percent translateY', () => { + expect( + transform(` + .test { + transform: translateY(100%); + } + `) + ).toEqual({ + test: { + transform: [{ translateY: '100%' }] + } + }) + }) + + it('transform percent translateX', () => { + expect( + transform(` + .test { + transform: translateX(100%); + } + `) + ).toEqual({ + test: { + transform: [{ translateX: '100%' }] + } + }) + }) + it('transforms flex', () => { expect( transform(` @@ -1052,7 +1094,8 @@ describe('border', () => { it('should transform border-color with rgb(a) property', () => { expect( - transform(` + transform( + ` .top { border-color: rgba(65, 131, 196, 0.2); } @@ -1065,7 +1108,9 @@ describe('border', () => { .left { border-color: rgb(65, 131, 196); } - `, { scalable: false }) + `, + { scalable: false } + ) ).toEqual({ top: { borderColor: 'rgba(65, 131, 196, 0.2)' @@ -1384,7 +1429,12 @@ describe('margin', () => { } `) ).toEqual({ - test: { marginTop: 'scalePx2dp(1)', marginRight: 'scalePx2dp(1)', marginBottom: 'scalePx2dp(1)', marginLeft: 'scalePx2dp(1)' } + test: { + marginTop: 'scalePx2dp(1)', + marginRight: 'scalePx2dp(1)', + marginBottom: 'scalePx2dp(1)', + marginLeft: 'scalePx2dp(1)' + } }) }) @@ -1646,9 +1696,7 @@ describe('text-decoration', () => { text-decoration: underline red yellow; } `) - ).toThrow( - 'Failed to parse declaration "textDecoration: underline red yellow"' - ) + ).toThrow('Failed to parse declaration "textDecoration: underline red yellow"') }) }) @@ -2174,9 +2222,7 @@ describe('font', () => { font-family: Goudy Bookletter 1911; } `) - }).toThrowError( - 'Failed to parse declaration "fontFamily: Goudy Bookletter 1911"' - ) + }).toThrowError('Failed to parse declaration "fontFamily: Goudy Bookletter 1911"') }) }) @@ -2424,9 +2470,7 @@ describe('box-shadow', () => { box-shadow: 0 0 0 red yellow green blue; } `) - }).toThrowError( - 'Failed to parse declaration "boxShadow: 0 0 0 red yellow green blue"' - ) + }).toThrowError('Failed to parse declaration "boxShadow: 0 0 0 red yellow green blue"') }) it('transforms box-shadow and enforces offset-y if offset-x present', () => { @@ -2446,9 +2490,7 @@ describe('box-shadow', () => { box-shadow: 10 20px 30px #f00; } `) - }).toThrowError( - 'Failed to parse declaration "boxShadow: 10 20px 30px #f00"' - ) + }).toThrowError('Failed to parse declaration "boxShadow: 10 20px 30px #f00"') expect(() => { transform(` .test { @@ -3491,11 +3533,14 @@ describe('ICSS :export pseudo-selector', () => { it('does not transform value to scalePx2dp when option scalable false', () => { expect( - transform(` + transform( + ` .foo { padding: 10px 20px; } - `, { scalable: false }) + `, + { scalable: false } + ) ).toEqual({ foo: { paddingTop: 10, @@ -3508,7 +3553,8 @@ describe('ICSS :export pseudo-selector', () => { it('should transform border-[direction] property', () => { expect( - transform(` + transform( + ` .left { border-left: red 1px solid; } @@ -3521,7 +3567,9 @@ describe('ICSS :export pseudo-selector', () => { .top { border-top: 1px red solid; } - `, { scalable: false }) + `, + { scalable: false } + ) ).toEqual({ top: { borderTopWidth: 1, @@ -3548,11 +3596,14 @@ describe('ICSS :export pseudo-selector', () => { it('should transform propertyValue remove !import key', () => { expect( - transform(` + transform( + ` .foo { color: red !import; } - `, { scalable: false }) + `, + { scalable: false } + ) ).toEqual({ foo: { color: 'red' @@ -3572,9 +3623,7 @@ describe('ICSS :export pseudo-selector', () => { color: blue; } `) - ).toThrow( - 'Failed to parse :export block because a CSS class in the same file is already using the name "bar"' - ) + ).toThrow('Failed to parse :export block because a CSS class in the same file is already using the name "bar"') }) it('should throw an error if exportedKey has the same name as a class', () => { @@ -3588,9 +3637,7 @@ describe('ICSS :export pseudo-selector', () => { foo: 1; } `) - ).toThrow( - 'Failed to parse :export block because a CSS class in the same file is already using the name "foo"' - ) + ).toThrow('Failed to parse :export block because a CSS class in the same file is already using the name "foo"') expect(() => transform(` :export { @@ -3601,9 +3648,7 @@ describe('ICSS :export pseudo-selector', () => { color: red; } `) - ).toThrow( - 'Failed to parse :export block because a CSS class in the same file is already using the name "foo"' - ) + ).toThrow('Failed to parse :export block because a CSS class in the same file is already using the name "foo"') expect(() => transform(` .foo { @@ -3618,9 +3663,7 @@ describe('ICSS :export pseudo-selector', () => { color: red; } `) - ).toThrow( - 'Failed to parse :export block because a CSS class in the same file is already using the name "foo"' - ) + ).toThrow('Failed to parse :export block because a CSS class in the same file is already using the name "foo"') }) it('should throw for :export that is not top level', () => { diff --git a/packages/css-to-react-native/src/css-to-react-native/transforms/transform.js b/packages/css-to-react-native/src/css-to-react-native/transforms/transform.js index 928671cbd5c7..9e891b7f6e0b 100644 --- a/packages/css-to-react-native/src/css-to-react-native/transforms/transform.js +++ b/packages/css-to-react-native/src/css-to-react-native/transforms/transform.js @@ -1,26 +1,23 @@ import { tokens } from '../tokenTypes' -const { SPACE, COMMA, LENGTH, NUMBER, ANGLE } = tokens +const { SPACE, COMMA, LENGTH, NUMBER, ANGLE, PERCENT } = tokens const oneOfType = tokenType => functionStream => { - const value = functionStream.expect(tokenType) + const value = functionStream.expect(...tokenType) functionStream.expectEmpty() return value } -const singleNumber = oneOfType(NUMBER) -const singleLength = oneOfType(LENGTH) -const singleAngle = oneOfType(ANGLE) -const xyTransformFactory = tokenType => ( - key, - valueIfOmitted -) => functionStream => { - const x = functionStream.expect(tokenType) +const singleNumber = oneOfType([NUMBER]) +const singleLength = oneOfType([LENGTH, PERCENT]) +const singleAngle = oneOfType([ANGLE]) +const xyTransformFactory = tokenType => (key, valueIfOmitted) => functionStream => { + const x = functionStream.expect(...tokenType) let y if (functionStream.hasTokens()) { functionStream.expect(COMMA) - y = functionStream.expect(tokenType) + y = functionStream.expect(...tokenType) } else if (typeof valueIfOmitted !== 'undefined') { y = valueIfOmitted } else { @@ -33,9 +30,9 @@ const xyTransformFactory = tokenType => ( return [{ [`${key}Y`]: y }, { [`${key}X`]: x }] } -const xyNumber = xyTransformFactory(NUMBER) -const xyLength = xyTransformFactory(LENGTH) -const xyAngle = xyTransformFactory(ANGLE) +const xyNumber = xyTransformFactory([NUMBER]) +const xyLength = xyTransformFactory([LENGTH, PERCENT]) +const xyAngle = xyTransformFactory([ANGLE]) const partTransforms = { perspective: singleNumber,