Skip to content
This repository has been archived by the owner on Dec 11, 2023. It is now read-only.

Commit

Permalink
Merge pull request #520 from gluestack/release/@gluestack-style/react…
Browse files Browse the repository at this point in the history
…@1.0.13

Release/@gluestack style/[email protected]
  • Loading branch information
ankit-tailor authored Nov 7, 2023
2 parents ad73835 + ae50c62 commit 0f8c04a
Show file tree
Hide file tree
Showing 9 changed files with 501 additions and 187 deletions.
93 changes: 87 additions & 6 deletions example/storybook/src/api/UtilityProps/index.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,103 @@ import { Canvas, Meta, Story } from '@storybook/addon-docs';

# Overriding Styles (Utility Props)

We understand that writing styles using sx props can be a bit challenging to learn so we offer the option to pass down utility props for any Styled Component, we internally convert these to sx props (We are also working on a babel plugin that does this at build time to save time for this conversion).
We understand that styling components using sx props can be a bit challenging to learn. To simplify this process, we offer the option to pass utility props to any Styled Component. Internally, these utility props are converted into sx props

### How to write utility props?
Let's explore how you can utilize these utility props.

A simple rule for utility props is that they represent the final property you want to apply to the component's style. These utility props will override all other stylings, except for the style prop. Here is a demo of the syntax.
### Basic styling

You can apply aliases and styling props directly to the component.

```jsx
<StyledView borderColor="$grey600" bg="$grey700" borderRadius="$md" />
<StyledButton borderColor="$grey600" bg="$grey700" borderRadius="$md" />
```

In the above example, you can apply alaises or styles directly to the component, and it will work seamlessly.

This will turn into :

```jsx
<StyledView
<StyledButton
sx={{ borderColor: '$grey600', bg: '$grey700', borderRadius: '$md' }}
/>
```

> Warning: Writing inline styles, like all good things, can negatively impact performance. So we highly recommend you keep it to a minimum.
### Complex styling

You can also handle more intricate styling, such as styles for different states, colormode, media queries, and descendants.

```jsx
<StyledButton
flexDirection="column"
$md-flexDirection="row"
$hover-bg="$primary600"
$_text-hover-color="$text800"
/>
```

- To achieve this, you can use the `$` prefix and specify the entire property path, like `$hover-bg` followed by its corresponding value.
- We provide comprehensive TypeScript support for single-level utility props, including aliases and styles in the format `${states/media/colormode/platform/descendants}-{aliases / style}`.
- For something like `$hover-sm-bg`, TypeScript suggestions are not available, but you can write props prefixed with `${states/media/colormode/platform/descendants}-*`.

This will turn into :

```jsx
<StyledButton
sx={{
'flexDirection': 'column',
':hover': {
bg: '$primary600',
},
'_text': {
':hover': {
color: '$text800',
},
},
'@md': {
flexDirection: 'row',
},
}}
/>
```

### More utility props

If your component's props become cluttered when using the above utility props, we offer support for an object-based approach to improve code maintainability.

```jsx
<StyledButton
flexDirection="column"
$md={{ flexDirection: 'row' }}
$hover={{
bg: '$primary600',
_text: {
color: '$text800',
},
}}
/>
```

- In this case, you can specify states, colormode, media queries, and descendants in object form, prefixed with `$`. These objects take sx properties as values.
- We have provided complete TypeScript support for this approach as well.

This will turn into :

```jsx
<StyledButton
sx={{
'flexDirection': 'column',
':hover': {
bg: '$primary600',
},
'_text': {
':hover': {
color: '$text800',
},
},
'@md': {
flexDirection: 'row',
},
}}
/>
```
168 changes: 124 additions & 44 deletions packages/babel-plugin-styled-resolver/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,21 @@ const {
} = require('@gluestack-style/react/lib/commonjs/stableHash');
const {
CSSPropertiesMap,
reservedKeys,
} = require('@gluestack-style/react/lib/commonjs/core/styled-system');
const {
StyleInjector,
} = require('@gluestack-style/react/lib/commonjs/style-sheet/index');
const {
updateOrderUnResolvedMap,
} = require('@gluestack-style/react/lib/commonjs/updateOrderUnResolvedMap');
const { deepMerge } = require('@gluestack-style/react/lib/commonjs/utils');
const {
setObjectKeyValue,
} = require('@gluestack-style/react/lib/commonjs/utils');
} = require('@gluestack-style/react/lib/commonjs/core/utils');
const {
checkAndReturnUtilityProp,
} = require('@gluestack-style/react/lib/commonjs/core/convert-utility-to-sx');

const IMPORT_NAME = '@gluestack-style/react';
let configThemePath = [];
Expand Down Expand Up @@ -902,7 +907,6 @@ module.exports = function (b) {
) {
let propsToBePersist = [];
let sxPropsWithIdentifier = {};
let utilityPropsWithIdentifier = {};

let mergedPropertyConfig = {
...ConfigDefault?.propertyTokenMap,
Expand All @@ -914,6 +918,29 @@ module.exports = function (b) {
...ConfigDefault?.aliases,
};

const componentExtendedConfig = merge(
{},
{
...ConfigDefault,
propertyTokenMap: { ...mergedPropertyConfig },
}
);

let sxPropsConvertedUtilityProps = {};

const prefixedMediaQueries = {};

Object.keys(componentExtendedConfig?.tokens?.mediaQueries).forEach(
(key) => {
prefixedMediaQueries[key] = {
key: `@${key}`,
isMediaQuery: true,
};
}
);

Object.assign(reservedKeys, { ...prefixedMediaQueries });

const attr = jsxOpeningElementPath.node.attributes;
attr.forEach((attribute, index) => {
if (t.isJSXAttribute(attribute)) {
Expand All @@ -926,6 +953,53 @@ module.exports = function (b) {
t.isConditionalExpression(propValue.expression)
) {
propsToBePersist.push(attribute);
} else if (
t.isObjectExpression(propValue.expression) &&
propName !== 'sx'
) {
const utilityPropsWithIdentifier =
getIdentifiersObjectFromAstNode(propValue.expression);

const objectProperties = propValue.expression.properties;
const { result: objectValue } =
convertExpressionContainerToStaticObject(objectProperties);

const {
prop: propString,
propPath,
value: utilityPropValue,
} = checkAndReturnUtilityProp(
propName,
objectValue,
styledSystemProps,
[],
reservedKeys
);

if (propString) {
propsToBePersist.push(attribute);
} else {
if (propPath && propPath.length > 0) {
setObjectKeyValue(
sxPropsConvertedUtilityProps,
propPath,
utilityPropValue
);

if (
utilityPropsWithIdentifier &&
utilityPropsWithIdentifier.properties &&
utilityPropsWithIdentifier.properties.length > 0
) {
propsToBePersist.push(
t.jsxAttribute(
t.jsxIdentifier(propName),
t.jsxExpressionContainer(utilityPropsWithIdentifier)
)
);
}
}
}
} else {
if (propName === 'sx') {
const objectProperties = propValue.expression.properties;
Expand All @@ -938,42 +1012,61 @@ module.exports = function (b) {
convertExpressionContainerToStaticObject(
objectProperties
);

// jsxOpeningElementPath.traverse({
// JSXExpressionContainer(jsxPath) {
// jsxPath.traverse({
// ObjectExpression(path) {
// removeEmptyProperties(path.node);
// },
// });
// },
// });
componentSXProp = sxPropsObject;
} else if (
t.isStringLiteral(propValue.expression) ||
t.isNumericLiteral(propValue.expression)
) {
if (styledSystemProps[propName]) {
componentUtilityProps = Object.assign(
componentUtilityProps ?? {},
{
[propName]: propValue.expression.value,
}
);
const {
prop: propString,
propPath,
value: utilityPropValue,
} = checkAndReturnUtilityProp(
propName,
propValue.expression.value,
styledSystemProps,
[],
reservedKeys
);

if (propString) {
propsToBePersist.push(attribute);
} else {
if (propPath && propPath.length > 0) {
setObjectKeyValue(
sxPropsConvertedUtilityProps,
propPath,
utilityPropValue
);
}
}
} else {
propsToBePersist.push(attribute);
}
}
} else if (styledSystemProps[propName]) {
componentUtilityProps = Object.assign(
componentUtilityProps ?? {},
{
[propName]: propValue.value,
}
);
} else {
propsToBePersist.push(attribute);
const {
prop: propString,
propPath,
value: utilityPropValue,
} = checkAndReturnUtilityProp(
propName,
propValue.value,
styledSystemProps,
[],
reservedKeys
);
if (propString) {
propsToBePersist.push(attribute);
} else {
if (propPath && propPath.length > 0) {
setObjectKeyValue(
sxPropsConvertedUtilityProps,
propPath,
utilityPropValue
);
}
}
}
}
});
Expand All @@ -983,32 +1076,19 @@ module.exports = function (b) {
jsxOpeningElementPath.node.attributes.length
);

for (const key in utilityPropsWithIdentifier) {
if (componentSXProp[key]) delete utilityPropsWithIdentifier[key];
}

jsxOpeningElementPath.node.attributes = propsToBePersist;

const sx = {
...componentUtilityProps,
...componentSXProp,
};

const sx = deepMerge(
{ ...sxPropsConvertedUtilityProps },
{ ...componentSXProp }
);
if (Object.keys(sx).length > 0) {
const verbosedSx = convertSxToSxVerbosed(sx);

const inlineSxTheme = {
baseStyle: verbosedSx,
};

let componentExtendedConfig = merge(
{},
{
...ConfigDefault,
propertyTokenMap: { ...mergedPropertyConfig },
}
);

let sxHash = stableHash(sx);

if (outputLibrary) {
Expand Down
6 changes: 6 additions & 0 deletions packages/react/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @gluestack-style/react

## 1.0.13

### Patch Changes

- Added Utility props support [PR](https://github.com/gluestack/gluestack-style/pull/519)

## 1.0.12

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/react/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@gluestack-style/react",
"description": "A universal & performant styling library for React Native, Next.js & React",
"version": "1.0.12",
"version": "1.0.13",
"keywords": [
"React Native",
"Next.js",
Expand Down
Loading

0 comments on commit 0f8c04a

Please sign in to comment.