Skip to content

Commit

Permalink
support sx array
Browse files Browse the repository at this point in the history
  • Loading branch information
siriwatknp committed May 6, 2024
1 parent 3ca5a54 commit fff1f85
Show file tree
Hide file tree
Showing 8 changed files with 335 additions and 11 deletions.
11 changes: 10 additions & 1 deletion packages/pigment-css-react/src/processors/sx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,22 @@ export class SxProcessor extends BaseProcessor {
/**
* Replace the sx prop with runtime sx
*/
let pathToReplace: undefined | NodePath<CallExpression>;
this.replacer((_tagPath) => {
const tagPath = _tagPath as NodePath<CallExpression>;

spreadSxProp(tagPath);
const isArrayArgument = spreadSxProp(tagPath);
if (isArrayArgument) {
pathToReplace = tagPath;
}

return tagPath.node;
}, false);

if (pathToReplace) {
// need to replace outside of `this.replacer` to preserve the import statement
pathToReplace.replaceWith(pathToReplace.node.arguments[0]);
}
}

get asSelector(): string {
Expand Down
12 changes: 7 additions & 5 deletions packages/pigment-css-react/src/sx.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ export default function sx(transformedSx, { className, style }) {
let sxVars = {};

function iterateSx(element) {
sxClass += ` ${typeof element === 'string' ? element : element.className}`;
sxVars = {
...sxVars,
...(transformedSx && typeof transformedSx !== 'string' ? transformedSx.vars : undefined),
};
if (element) {
sxClass += ` ${typeof element === 'string' ? element : element.className}`;
sxVars = {
...sxVars,
...(transformedSx && typeof transformedSx !== 'string' ? transformedSx.vars : undefined),
};
}
}

if (Array.isArray(transformedSx)) {
Expand Down
37 changes: 32 additions & 5 deletions packages/pigment-css-react/src/utils/spreadSxProp.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { NodePath, types as astService } from '@babel/core';
import {
ArrayExpression,
CallExpression,
Expression,
JSXAttribute,
Expand Down Expand Up @@ -100,7 +101,7 @@ export default function spreadSxProp(tagPath: NodePath<CallExpression>) {
| NodePath<ObjectExpression>
| null;
if (!target) {
return;
return false;
}
let paths:
| NodePath<JSXAttribute | JSXSpreadAttribute>[]
Expand All @@ -112,19 +113,45 @@ export default function spreadSxProp(tagPath: NodePath<CallExpression>) {
paths = target.get('properties');
}
const { props, sxPath } = getProps(paths);
let spreadSxNode: undefined | SpreadElement | JSXSpreadAttribute;
if (sxPath) {
const expression = sxPath.get('value');
if ('node' in expression) {
if (target.isObjectExpression()) {
target.node.properties.push(astService.spreadElement(expression.node as Expression));
spreadSxNode = astService.spreadElement(expression.node as Expression);
target.node.properties.push(spreadSxNode);
}
if (target.isJSXOpeningElement() && expression.isJSXExpressionContainer()) {
target.node.attributes.push(
astService.jsxSpreadAttribute(expression.node.expression as Expression),
);
spreadSxNode = astService.jsxSpreadAttribute(expression.node.expression as Expression);
target.node.attributes.push(spreadSxNode);
}
}
sxPath.remove();
}
tagPath.node.arguments.push(astService.objectExpression(props));

if (spreadSxNode?.argument.type === 'ArrayExpression') {
spreadSxNode.argument = astService.callExpression(tagPath.node.callee, [
spreadSxNode.argument,
astService.objectExpression(props),
]);
}

// This step is required to pass information about the array argument to the outer function
// to replace the `tagPath` with its first argument.
//
// Check if the sx value is an array expression
let arrayPath: NodePath<ArrayExpression> | null = null;
if (tagPath.parentPath.isArrayExpression()) {
// sx call is a direct child, e.g. [_sx(...), _sx(...)]
arrayPath = tagPath.parentPath;
} else if (tagPath.parentPath.parentPath?.isArrayExpression()) {
// sx call inside a conditional/logical expression, e.g. [true ? _sx(...) : _sx(...), prop && _sx(...)]
arrayPath = tagPath.parentPath.parentPath;
}
if (arrayPath) {
return true;
}

return false;
}
1 change: 1 addition & 0 deletions packages/pigment-css-react/src/utils/sx-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export const babelPlugin = declare<{
}
const valuePath = path.get('value');
if (
!valuePath.isArrayExpression() &&
!valuePath.isObjectExpression() &&
!valuePath.isArrowFunctionExpression() &&
!valuePath.isConditionalExpression() &&
Expand Down
95 changes: 95 additions & 0 deletions packages/pigment-css-react/tests/sx/fixtures/sx-array.input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { jsx as _jsx } from 'react/jsx-runtime';

<div sx={[{ opacity: 1 }]} />;

<div className="foo" style={{ opacity: 1 }} sx={[{ color: 'red' }, { color: 'green' }]} />;

function App(props) {
return (
<SliderRail
{...props}
sx={[
props.variant === 'secondary'
? { color: props.isRed ? 'red' : 'blue' }
: { backgroundColor: 'blue', color: 'white' },
(theme) => ({
border: `1px solid ${theme.palette.primary.main}`,
}),
]}
/>
);
}

function App2(props) {
return (
<SliderRail
sx={[
{ color: 'green' },
props.variant === 'secondary' && { color: props.isRed ? 'red' : 'blue' },
]}
className={`foo ${props.className}`}
style={{
color: 'red',
...props.style,
}}
/>
);
}

_jsx('div', {
sx: [
{
opacity: 1,
},
],
});

_jsx('div', {
className: 'foo',
style: {
opacity: 1,
},
sx: [
{
color: 'red',
},
{
color: 'green',
},
],
});

function App3(props) {
return _jsx('div', {
sx: [
(theme) => ({
border: `1px solid ${theme.palette.primary.main}`,
}),
props.disabled
? {
opacity: 0.4,
}
: {
color: 'red',
},
],
children: 'test',
...props,
});
}

function App4(props) {
return _jsx('div', {
sx: [
(theme) => ({
border: `1px solid ${theme.palette.primary.main}`,
}),
props.variant === 'secondary' && { color: props.isRed ? 'red' : 'blue' },
],
className: `foo ${props.className}`,
style: {
color: 'red',
...props.style,
},
});
}
49 changes: 49 additions & 0 deletions packages/pigment-css-react/tests/sx/fixtures/sx-array.output.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.dnf03uj {
opacity: 1;
}
.dsvwfmp {
color: red;
}
.d121rcfp {
color: green;
}
.schfkcb {
color: var(--schfkcb-0);
}
.s1cp79ho {
background-color: blue;
color: white;
}
.soyt4ij {
border: 1px solid red;
}
.sr48wd1 {
color: green;
}
.s6s70bv {
color: var(--s6s70bv-0);
}
.s1v8upwb {
opacity: 1;
}
.sjtfjpx {
color: red;
}
.s1r80n7h {
color: green;
}
.s1gu7ed8 {
border: 1px solid red;
}
.s1h4vmh2 {
opacity: 0.4;
}
.s1oy2sl1 {
color: red;
}
.s14d8kn5 {
border: 1px solid red;
}
.s1su4mia {
color: var(--s1su4mia-0);
}
127 changes: 127 additions & 0 deletions packages/pigment-css-react/tests/sx/fixtures/sx-array.output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import {
sx as _sx,
sx as _sx2,
sx as _sx3,
sx as _sx4,
sx as _sx5,
sx as _sx6,
sx as _sx7,
sx as _sx8,
} from '@pigment-css/react';
import { jsx as _jsx } from 'react/jsx-runtime';
<div {..._sx(['dnf03uj'], {})} />;
<div
className="foo"
style={{
opacity: 1,
}}
{..._sx2(['dsvwfmp', 'd121rcfp'], {
className: 'foo',
style: {
opacity: 1,
},
})}
/>;
function App(props) {
return (
<SliderRail
{...props}
{..._sx3(
[
props.variant === 'secondary'
? {
className: 'schfkcb',
vars: {
'schfkcb-0': [props.isRed ? 'red' : 'blue', false],
},
}
: 's1cp79ho',
'soyt4ij',
],
{
...props,
},
)}
/>
);
}
function App2(props) {
return (
<SliderRail
className={`foo ${props.className}`}
style={{
color: 'red',
...props.style,
}}
{..._sx4(
[
'sr48wd1',
props.variant === 'secondary' && {
className: 's6s70bv',
vars: {
's6s70bv-0': [props.isRed ? 'red' : 'blue', false],
},
},
],
{
className: `foo ${props.className}`,
style: {
color: 'red',
...props.style,
},
},
)}
/>
);
}
_jsx('div', {
..._sx5(['s1v8upwb'], {}),
});
_jsx('div', {
className: 'foo',
style: {
opacity: 1,
},
..._sx6(['sjtfjpx', 's1r80n7h'], {
className: 'foo',
style: {
opacity: 1,
},
}),
});
function App3(props) {
return _jsx('div', {
children: 'test',
...props,
..._sx7(['s1gu7ed8', props.disabled ? 's1h4vmh2' : 's1oy2sl1'], {
...props,
}),
});
}
function App4(props) {
return _jsx('div', {
className: `foo ${props.className}`,
style: {
color: 'red',
...props.style,
},
..._sx8(
[
's14d8kn5',
props.variant === 'secondary' && {
className: 's1su4mia',
vars: {
's1su4mia-0': [props.isRed ? 'red' : 'blue', false],
},
},
],
{
className: `foo ${props.className}`,
style: {
color: 'red',
...props.style,
},
},
),
});
}
Loading

0 comments on commit fff1f85

Please sign in to comment.