Skip to content

Commit

Permalink
Add support for attrs method in styled
Browse files Browse the repository at this point in the history
  • Loading branch information
Temzasse committed Jan 8, 2022
1 parent e0c7ecb commit f2631b9
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 11 deletions.
72 changes: 68 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Below you can see a list of all supported and unsupported features of Stitches N

| Feature | Supported |
| --------------------- | ---------------------------------------- |
| `styled` | |
| `styled` |(with additional `.attrs` support) |
| `createStitches` ||
| `defaultThemeMap` ||
| `css` |_(Simplified version)_ |
Expand Down Expand Up @@ -173,7 +173,7 @@ const SomeComp = styled(
Stitches Native handles theming differently than Stitches. Since there are no CSS Variables in React Native theming is handled via React Context in a similar way as other CSS-in-JS libraries such as [styled-components](https://styled-components.com/docs/advanced#theming) handle theming.

```tsx
const { createTheme, ThemeProvider } = createStitches({
const { theme, createTheme, ThemeProvider } = createStitches({
colors: {
background: '#fff',
text: '#000',
Expand All @@ -187,12 +187,12 @@ const darkTheme = createTheme({
},
});

export default function App() {
function App() {
// In a real world scenario this value should probably live in React Context
const [darkMode, setDarkMode] = useState(false);

return (
<ThemeProvider theme={darkMode ? darkTheme : undefined}>
<ThemeProvider theme={darkMode ? darkTheme : theme}>
{/*...*/}
</ThemeProvider>
);
Expand Down Expand Up @@ -299,3 +299,67 @@ const ButtonText = styled('Text', {
Hello
</ButtonText>;
```

### Additional props with `.attrs`

In React Native it is quite common that a component exposes props (other than `style`) that accept a style object - a good example of this is the `ScrollView` component that has `contentContainerStyle` prop. Using theme tokens with these kind of props can be accomplished with the `useTheme` hook:

```tsx
function Comp() {
const theme = useTheme();

return (
<ScrollView contentContainerStyle={{ padding: theme.space[2] }}>
{/* ... */}
</ScrollView>
);
}

const ScrollView = styled('ScrollView', {
flex: 1,
});
```

This approach is fine but a bit convoluted since you have to import a hook just to access the theme tokens. There is a better way with the chainable `.attrs` method which can be used to attach additional props to a Stitches styled component (this method was popularized by [styled-components](https://styled-components.com/docs/api#attrs)).

> ⚠️ NOTE: this method does not exist in the original Web version of Stitches.
```tsx
function Example() {
return <ScrollView>{/*...*/}</ScrollView>;
}

const ScrollView = styled('ScrollView', {
flex: 1,
}).attrs((props) => ({
contentContainerStyle: {
padding: props.theme.space[2],
},
}));
```

It is also possible to access the variants of the component within `.attrs`:

```tsx
function Example() {
return <ScrollView spacious>{/*...*/}</ScrollView>;
}

const ScrollView = styled('ScrollView', {
flex: 1,
variants: {
spacious: {
true: {
// some styles...
},
false: {
// some styles...
},
},
},
}).attrs((props) => ({
contentContainerStyle: {
padding: props.theme.space[props.spacious ? 4 : 2],
},
}));
```
11 changes: 7 additions & 4 deletions example/src/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ import { Switch } from 'react-native';
import { StatusBar } from 'expo-status-bar';

import { Stack, Text, useColorMode } from './components';
import { styled, useTheme } from './styles';
import { styled } from './styles';

export default function Example() {
const theme = useTheme();
const { toggleColorMode, colorMode } = useColorMode();

return (
<Wrapper>
<Content contentContainerStyle={{ padding: theme.space[2] }}>
<Content>
<Stack axis="y" space="4">
<Text variant="title1">Example app</Text>

Expand Down Expand Up @@ -55,7 +54,11 @@ const Wrapper = styled('SafeAreaView', {

const Content = styled('ScrollView', {
flex: 1,
});
}).attrs((p) => ({
contentContainerStyle: {
padding: p.theme.space[2],
},
}));

const Box = styled('View', {
minHeight: 100,
Expand Down
20 changes: 18 additions & 2 deletions src/internals/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ export function createStitches(config = {}) {
compoundVariants,
});

const Comp = forwardRef((props, ref) => {
let attrsFn;

let Comp = forwardRef((props, ref) => {
const theme = useThemeInternal();
const styleSheet = styleSheets[theme.definition.__ID__];
const { width: windowWidth } = useWindowDimensions();
Expand Down Expand Up @@ -222,7 +224,14 @@ export function createStitches(config = {}) {
[props.style(...rest), ...stitchesStyles].filter(Boolean)
: [...stitchesStyles, props.style].filter(Boolean);

let attrsProps = {};

if (typeof attrsFn === 'function') {
attrsProps = attrsFn({ ...props, theme: theme.values });
}

const componentProps = {
...attrsProps,
...props,
style: allStyles,
ref,
Expand All @@ -240,7 +249,14 @@ export function createStitches(config = {}) {
return null;
});

return memo(Comp);
Comp = memo(Comp);

Comp.attrs = (cb) => {
attrsFn = cb;
return Comp;
};

return Comp;
}

/** @type {Stitches['css']} */
Expand Down
20 changes: 19 additions & 1 deletion src/types/stitches.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* eslint-disable */
import type * as React from 'react';
import type * as CSSUtil from './css-util';
import type * as StyledComponent from './styled-component';
import type * as Native from './react-native';
Expand Down Expand Up @@ -194,7 +195,24 @@ export default interface Stitches<
StyledComponent.StyledComponentProps<Composers>,
Media,
CSSUtil.CSS<Media, Theme, ThemeMap, Utils>
>;
> & {
attrs: (
cb: (
props: {
theme: Theme;
} & StyledComponent.StyledComponentProps<Composers>
) => Type extends
| Native.ReactNativeElementsKeys
| React.ComponentType<any>
? Native.ReactNativeComponentPropsWithRef<Type>
: {}
) => StyledComponent.StyledComponent<
Type,
StyledComponent.StyledComponentProps<Composers>,
Media,
CSSUtil.CSS<Media, Theme, ThemeMap, Utils>
>;
};
};
}

Expand Down

0 comments on commit f2631b9

Please sign in to comment.