diff --git a/packages/shared/__tests__/split-value-test.js b/packages/shared/__tests__/split-value-test.js index ee3f3852..3bf2b1fc 100644 --- a/packages/shared/__tests__/split-value-test.js +++ b/packages/shared/__tests__/split-value-test.js @@ -44,4 +44,28 @@ describe('Ensure CSS values are split correctly', () => { splitValue('calc((100% - 50px) * 0.5) var(--rightpadding, 20px)'), ).toEqual(['calc((100% - 50px) * 0.5)', 'var(--rightpadding,20px)']); }); + + test('Expands a string of values with slash notation appropriately.', () => { + expect(splitValue('1px / 2px 3px 4px 5px', 'borderRadius')).toEqual([ + '1px 2px', + '1px 3px', + '1px 4px', + '1px 5px', + ]); + expect(splitValue('1px 2px / 3px 4px', 'borderRadius')).toEqual([ + '1px 3px', + '2px 4px', + '1px 3px', + '2px 4px', + ]); + expect(splitValue('1px 2px / 3px 4px 5px', 'borderRadius')).toEqual([ + '1px 3px', + '2px 4px', + '1px 5px', + '2px 4px', + ]); + expect( + splitValue('1px 2px 3px 4px / 5px 6px 7px 8px', 'borderRadius'), + ).toEqual(['1px 5px', '2px 6px', '3px 7px', '4px 8px']); + }); }); diff --git a/packages/shared/src/preprocess-rules/legacy-expand-shorthands.js b/packages/shared/src/preprocess-rules/legacy-expand-shorthands.js index 0f677007..33920930 100644 --- a/packages/shared/src/preprocess-rules/legacy-expand-shorthands.js +++ b/packages/shared/src/preprocess-rules/legacy-expand-shorthands.js @@ -162,7 +162,10 @@ const shorthands: $ReadOnly<{ [key: string]: (TStyleValue) => TReturn }> = { ], borderRadius: (rawValue: TStyleValue): TReturn => { - const [top, right = top, bottom = top, left = right] = splitValue(rawValue); + const [top, right = top, bottom = top, left = right] = splitValue( + rawValue, + 'borderRadius', + ); return [ ['borderTopStartRadius', top], diff --git a/packages/shared/src/utils/split-css-value.js b/packages/shared/src/utils/split-css-value.js index f7c731fe..60246050 100644 --- a/packages/shared/src/utils/split-css-value.js +++ b/packages/shared/src/utils/split-css-value.js @@ -23,9 +23,49 @@ function printNode(node: PostCSSValueASTNode): string { } } +// Splits PostCSS value nodes for border-radius into horizontal and vertical groups by slash. +function splitNodesBySlash( + nodes: PostCSSValueASTNode[], +): PostCSSValueASTNode[][] { + const result = []; + let current = []; + + for (const node of nodes) { + const isSeparator = node.type === 'div' && node.value === '/'; + if (isSeparator) { + if (current.length > 0) { + result.push(current); + current = []; + } + } else { + current.push(node); + } + } + + if (current.length > 0) { + result.push(current); + } + + return result; +} + +// Expands a border-radius shorthand value to an array of four values. +function expandBorderRadiusShorthand(group: PostCSSValueASTNode[]) { + if (group.length === 2) return [group[0], group[1], group[0], group[1]]; + if (group.length === 3) return [group[0], group[1], group[2], group[1]]; + if (group.length === 4) return [group[0], group[1], group[2], group[3]]; + return Array(4).fill(group[0]); +} + +// Combines two arrays of border-radius values into a single formatted string. +function combineBorderRadiusValues(verticals: string[], horizontals: string[]) { + return verticals.map((value, i) => `${value} ${horizontals[i]}`); +} + // Using split(' ') Isn't enough because of values like calc. export default function splitValue( str: TStyleValue, + propertyName: string = '', ): $ReadOnlyArray { if (str == null || typeof str === 'number') { return [str]; @@ -38,9 +78,28 @@ export default function splitValue( const parsed = parser(str.trim()); - const nodes = parsed.nodes - .filter((node) => node.type !== 'space' && node.type !== 'div') - .map(printNode); + let nodes: string[] = []; + if (propertyName === 'borderRadius') { + const groups = splitNodesBySlash( + parsed.nodes.filter((node) => node.type !== 'space'), + ); + if (groups.length === 1) { + nodes = parsed.nodes.filter((node) => node.type !== 'div').map(printNode); + } else { + // edge case + const vertical = expandBorderRadiusShorthand( + groups[0].filter((node) => node.type !== 'div'), + ).map(printNode); + const horizontal = expandBorderRadiusShorthand( + groups[1].filter((node) => node.type !== 'div'), + ).map(printNode); + nodes = combineBorderRadiusValues(vertical, horizontal); + } + } else { + nodes = parsed.nodes + .filter((node) => node.type !== 'space' && node.type !== 'div') + .map(printNode); + } if ( nodes.length > 1 &&