-
Notifications
You must be signed in to change notification settings - Fork 24.6k
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: android transform origin #38558
Closed
intergalacticspacehighway
wants to merge
7
commits into
facebook:main
from
intergalacticspacehighway:feat/android-transform-origin
Closed
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
83719e5
feat: android transform origin
intergalacticspacehighway 3ecc8ec
move process transform origin on to js
intergalacticspacehighway 1f4abea
fix: pr feedback
intergalacticspacehighway 71a7be5
fix: pr feedback
intergalacticspacehighway c494b02
fix: handle translate for matrix transform and remove listener if tra…
intergalacticspacehighway 070787f
fix: optimise transform
intergalacticspacehighway bf53d65
reset invalidate_transform view tag
intergalacticspacehighway File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
...t-native/Libraries/StyleSheet/__tests__/__snapshots__/processTransformOrigin-test.js.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`processTransformOrigin validation only accepts three values 1`] = `"Transform origin must have exactly 3 values."`; | ||
|
||
exports[`processTransformOrigin validation only accepts three values 2`] = `"Transform origin must have exactly 3 values."`; |
134 changes: 134 additions & 0 deletions
134
packages/react-native/Libraries/StyleSheet/__tests__/processTransformOrigin-test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
/** | ||
* 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. | ||
* | ||
* @format | ||
* @oncall react_native | ||
*/ | ||
|
||
import processTransformOrigin from '../processTransformOrigin'; | ||
|
||
describe('processTransformOrigin', () => { | ||
describe('validation', () => { | ||
it('only accepts three values', () => { | ||
expect(() => { | ||
processTransformOrigin([]); | ||
}).toThrowErrorMatchingSnapshot(); | ||
expect(() => { | ||
processTransformOrigin(['50%', '50%']); | ||
}).toThrowErrorMatchingSnapshot(); | ||
}); | ||
|
||
it('should transform a string', () => { | ||
expect(processTransformOrigin('50% 50% 5px')).toEqual(['50%', '50%', 5]); | ||
}); | ||
|
||
it('should handle one value', () => { | ||
expect(processTransformOrigin('top')).toEqual(['50%', 0, 0]); | ||
expect(processTransformOrigin('right')).toEqual(['100%', '50%', 0]); | ||
expect(processTransformOrigin('bottom')).toEqual(['50%', '100%', 0]); | ||
expect(processTransformOrigin('left')).toEqual([0, '50%', 0]); | ||
}); | ||
|
||
it('should handle two values', () => { | ||
expect(processTransformOrigin('30% top')).toEqual(['30%', 0, 0]); | ||
expect(processTransformOrigin('right 30%')).toEqual(['100%', '30%', 0]); | ||
expect(processTransformOrigin('30% bottom')).toEqual(['30%', '100%', 0]); | ||
expect(processTransformOrigin('left 30%')).toEqual([0, '30%', 0]); | ||
}); | ||
|
||
it('should handle two keywords in either order', () => { | ||
expect(processTransformOrigin('right bottom')).toEqual([ | ||
'100%', | ||
'100%', | ||
0, | ||
]); | ||
expect(processTransformOrigin('bottom right')).toEqual([ | ||
'100%', | ||
'100%', | ||
0, | ||
]); | ||
expect(processTransformOrigin('right bottom 5px')).toEqual([ | ||
'100%', | ||
'100%', | ||
5, | ||
]); | ||
expect(processTransformOrigin('bottom right 5px')).toEqual([ | ||
'100%', | ||
'100%', | ||
5, | ||
]); | ||
}); | ||
|
||
it('should not allow specifying same position twice', () => { | ||
expect(() => { | ||
processTransformOrigin('top top'); | ||
}).toThrowErrorMatchingInlineSnapshot( | ||
`"Could not parse transform-origin: top top"`, | ||
); | ||
expect(() => { | ||
processTransformOrigin('right right'); | ||
}).toThrowErrorMatchingInlineSnapshot( | ||
`"Transform-origin right can only be used for x-position"`, | ||
); | ||
expect(() => { | ||
processTransformOrigin('bottom bottom'); | ||
}).toThrowErrorMatchingInlineSnapshot( | ||
`"Could not parse transform-origin: bottom bottom"`, | ||
); | ||
expect(() => { | ||
processTransformOrigin('left left'); | ||
}).toThrowErrorMatchingInlineSnapshot( | ||
`"Transform-origin left can only be used for x-position"`, | ||
); | ||
expect(() => { | ||
processTransformOrigin('top bottom'); | ||
}).toThrowErrorMatchingInlineSnapshot( | ||
`"Could not parse transform-origin: top bottom"`, | ||
); | ||
expect(() => { | ||
processTransformOrigin('left right'); | ||
}).toThrowErrorMatchingInlineSnapshot( | ||
`"Transform-origin right can only be used for x-position"`, | ||
); | ||
}); | ||
|
||
it('should handle three values', () => { | ||
expect(processTransformOrigin('30% top 10px')).toEqual(['30%', 0, 10]); | ||
expect(processTransformOrigin('right 30% 10px')).toEqual([ | ||
'100%', | ||
'30%', | ||
10, | ||
]); | ||
expect(processTransformOrigin('30% bottom 10px')).toEqual([ | ||
'30%', | ||
'100%', | ||
10, | ||
]); | ||
expect(processTransformOrigin('left 30% 10px')).toEqual([0, '30%', 10]); | ||
}); | ||
|
||
it('should enforce two value ordering', () => { | ||
expect(() => { | ||
processTransformOrigin('top 30%'); | ||
}).toThrowErrorMatchingInlineSnapshot( | ||
`"Could not parse transform-origin: top 30%"`, | ||
); | ||
}); | ||
|
||
it('should not allow percents for z-position', () => { | ||
expect(() => { | ||
processTransformOrigin('top 30% 30%'); | ||
}).toThrowErrorMatchingInlineSnapshot( | ||
`"Could not parse transform-origin: top 30% 30%"`, | ||
); | ||
expect(() => { | ||
processTransformOrigin('top 30% center'); | ||
}).toThrowErrorMatchingInlineSnapshot( | ||
`"Could not parse transform-origin: top 30% center"`, | ||
); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
136 changes: 136 additions & 0 deletions
136
packages/react-native/Libraries/StyleSheet/processTransformOrigin.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
/** | ||
* 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. | ||
* | ||
* @format | ||
* @flow | ||
*/ | ||
|
||
import invariant from 'invariant'; | ||
|
||
const INDEX_X = 0; | ||
const INDEX_Y = 1; | ||
const INDEX_Z = 2; | ||
|
||
/* eslint-disable no-labels */ | ||
export default function processTransformOrigin( | ||
transformOrigin: Array<string | number> | string, | ||
): Array<string | number> { | ||
if (typeof transformOrigin === 'string') { | ||
const transformOriginString = transformOrigin; | ||
const regex = /(top|bottom|left|right|center|\d+(?:%|px)|0)/gi; | ||
const transformOriginArray: Array<string | number> = ['50%', '50%', 0]; | ||
|
||
let index = INDEX_X; | ||
let matches; | ||
outer: while ((matches = regex.exec(transformOriginString))) { | ||
let nextIndex = index + 1; | ||
|
||
const value = matches[0]; | ||
const valueLower = value.toLowerCase(); | ||
|
||
switch (valueLower) { | ||
case 'left': | ||
case 'right': { | ||
invariant( | ||
index === INDEX_X, | ||
'Transform-origin %s can only be used for x-position', | ||
value, | ||
); | ||
transformOriginArray[INDEX_X] = valueLower === 'left' ? 0 : '100%'; | ||
break; | ||
} | ||
case 'top': | ||
case 'bottom': { | ||
invariant( | ||
index !== INDEX_Z, | ||
'Transform-origin %s can only be used for y-position', | ||
value, | ||
); | ||
transformOriginArray[INDEX_Y] = valueLower === 'top' ? 0 : '100%'; | ||
|
||
// Handle [[ center | left | right ] && [ center | top | bottom ]] <length>? | ||
if (index === INDEX_X) { | ||
const horizontal = regex.exec(transformOriginString); | ||
if (horizontal == null) { | ||
break outer; | ||
} | ||
|
||
switch (horizontal[0].toLowerCase()) { | ||
case 'left': | ||
transformOriginArray[INDEX_X] = 0; | ||
break; | ||
case 'right': | ||
transformOriginArray[INDEX_X] = '100%'; | ||
break; | ||
case 'center': | ||
transformOriginArray[INDEX_X] = '50%'; | ||
break; | ||
default: | ||
invariant( | ||
false, | ||
'Could not parse transform-origin: %s', | ||
transformOriginString, | ||
); | ||
} | ||
nextIndex = INDEX_Z; | ||
} | ||
|
||
break; | ||
} | ||
case 'center': { | ||
invariant( | ||
index !== INDEX_Z, | ||
'Transform-origin value %s cannot be used for z-position', | ||
value, | ||
); | ||
transformOriginArray[index] = '50%'; | ||
break; | ||
} | ||
default: { | ||
if (value.endsWith('%')) { | ||
transformOriginArray[index] = value; | ||
} else { | ||
transformOriginArray[index] = parseFloat(value); // Remove `px` | ||
} | ||
break; | ||
} | ||
} | ||
|
||
index = nextIndex; | ||
} | ||
|
||
transformOrigin = transformOriginArray; | ||
} | ||
|
||
if (__DEV__) { | ||
_validateTransformOrigin(transformOrigin); | ||
} | ||
|
||
return transformOrigin; | ||
} | ||
|
||
function _validateTransformOrigin(transformOrigin: Array<string | number>) { | ||
invariant( | ||
transformOrigin.length === 3, | ||
'Transform origin must have exactly 3 values.', | ||
); | ||
const [x, y, z] = transformOrigin; | ||
invariant( | ||
typeof x === 'number' || (typeof x === 'string' && x.endsWith('%')), | ||
'Transform origin x-position must be a number. Passed value: %s.', | ||
x, | ||
); | ||
invariant( | ||
typeof y === 'number' || (typeof y === 'string' && y.endsWith('%')), | ||
'Transform origin y-position must be a number. Passed value: %s.', | ||
y, | ||
); | ||
invariant( | ||
typeof z === 'number', | ||
'Transform origin z-position must be a number. Passed value: %s.', | ||
z, | ||
); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's subjective, but there was some discussion within the team before on whether we should be permissive of invalid styles during development. E.g. add a warning, instead of throwing an exception. I think we changed some other to be more permissive and just warn and no-op, since unhandled exception is disruptive during development/live reload, but I could see both ways.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc - @jacobp100 need to handle this in all PRs. wdyt? maybe use console.warn?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I can make changes to this. If you let me know what way you want to go I'll update it in my PR (#38626)