Skip to content

Commit 6036045

Browse files
authored
Component as style source (#3)
1 parent bdb09c4 commit 6036045

File tree

5 files changed

+59
-20
lines changed

5 files changed

+59
-20
lines changed

src/__tests__/mixins.test.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { createStyled } from '../styled'
22
import { buildDynamicStyles } from '../buildDynamicStyles'
3+
import styled from '../..'
34

45
const { css } = createStyled()
56
const props = { theme: {} }
@@ -104,4 +105,20 @@ describe('mixins', () => {
104105
flex: 3,
105106
})
106107
})
108+
109+
it('Should override styled component', () => {
110+
const Component = styled.View`
111+
flex: 1;
112+
`
113+
114+
const styles = css`
115+
${Component};
116+
height: 2px;
117+
`
118+
119+
expect(buildDynamicStyles(props, styles)).toStrictEqual({
120+
flex: 1,
121+
height: 2,
122+
})
123+
})
107124
})

src/parsers.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { getStylesForProperty } from 'css-to-react-native'
22
import { UnknownProps, UnknownStyles } from './types'
33
import { buildDynamicStyles } from './buildDynamicStyles'
4-
import { isFunction } from './utils'
4+
import { isFunction, isStyledComponent } from './utils'
55

66
/**
77
* If at least one of args is a function, return a function that will be called with the props passed to the component.
@@ -78,9 +78,13 @@ export const runtime = (key: string, value: unknown) => {
7878
}
7979

8080
export const mixin = (value: unknown) => {
81+
if (isStyledComponent(value)) {
82+
value = value.styles
83+
}
8184
if (isFunction(value)) {
85+
const fn = value as Function
8286
return dynamic((props, style) => {
83-
const mixinStyles = value(props)
87+
const mixinStyles = fn(props)
8488
if (mixinStyles && typeof mixinStyles === 'object') {
8589
buildDynamicStyles(props, mixinStyles, style)
8690
}

src/styled.tsx

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import React, { PropsWithChildren, createElement, useContext } from 'react'
22
import {
3+
ActivityIndicator,
34
Button,
5+
DrawerLayoutAndroid,
46
FlatList,
57
Image,
68
ImageBackground,
@@ -12,6 +14,7 @@ import {
1214
ScrollView,
1315
SectionList,
1416
StyleProp,
17+
Switch,
1518
Text,
1619
TextInput,
1720
TouchableHighlight,
@@ -33,26 +36,15 @@ import {
3336
AsComponentProps,
3437
UnknownStyles,
3538
StyledObject,
39+
StyledComponent,
40+
AnyStyleProps,
3641
} from './types'
3742
import { buildPropsFromAttrs } from './buildPropsFromAttrs'
3843
import { substitute, runtime, mixin } from './parsers'
3944
import { createTheme } from './theme'
4045
import { splitAttrs } from './splitAttrs'
4146
import { buildDynamicStyles } from './buildDynamicStyles'
42-
import { isFunction } from './utils'
43-
44-
interface AnyStyleProps {
45-
style?: StyleProp<UnknownStyles>
46-
}
47-
48-
type StyledComponent = React.ForwardRefExoticComponent<Omit<React.PropsWithChildren<UnknownProps & AnyStyleProps & AsComponentProps>, "ref"> & React.RefAttributes<unknown>> & {
49-
isStyled?: boolean
50-
initialStyles: UnknownStyles
51-
attrs: InnerAttrs[]
52-
origin: AnyComponent
53-
}
54-
55-
const isStyledComponent = (component: AnyComponent): component is StyledComponent => !!(component as StyledComponent)?.isStyled
47+
import { isFunction, isStyledComponent } from './utils'
5648

5749
const methods = {
5850
substitute,
@@ -70,7 +62,7 @@ export function createStyled<Theme extends AnyTheme>() {
7062
throw new Error('It seems you forgot to add babel plugin.')
7163
}
7264
}
73-
initialStyles = isStyledComponent(Component) ? { ...Component.initialStyles, ...initialStyles } : initialStyles
65+
initialStyles = isStyledComponent(Component) ? { ...Component.styles, ...initialStyles } : initialStyles
7466
attrs = isStyledComponent(Component) ? [...Component.attrs, ...attrs] : attrs
7567
const hasDynamicStyles = Object.keys(initialStyles).some((key) => isFunction(initialStyles[key]))
7668
const fixedStyle = hasDynamicStyles ? undefined : initialStyles
@@ -112,7 +104,7 @@ export function createStyled<Theme extends AnyTheme>() {
112104

113105
StyledComponent.displayName = 'StyledComponent'
114106
StyledComponent.isStyled = true
115-
StyledComponent.initialStyles = initialStyles
107+
StyledComponent.styles = initialStyles
116108
StyledComponent.attrs = attrs
117109
StyledComponent.defaultProps = Object.assign({ style: fixedStyle }, fixedProps)
118110
StyledComponent.origin = origin
@@ -129,10 +121,12 @@ export function createStyled<Theme extends AnyTheme>() {
129121
innerStyled.attrs = attrs(innerStyled)
130122
// We use as unknown as Type constraction here becasue
131123
// it is expected that argument so the styled function is transformed to an Array<Array> type during Babel transpilation.
132-
return innerStyled as Styled<C, Theme>
124+
return innerStyled as unknown as Styled<C, Theme>
133125
}
134126

127+
styled.ActivityIndicator = styled(ActivityIndicator)
135128
styled.Button = styled(Button)
129+
styled.DrawerLayoutAndroid = styled(DrawerLayoutAndroid)
136130
styled.FlatList = styled(FlatList)
137131
styled.Image = styled(Image)
138132
styled.KeyboardAvoidingView = styled(KeyboardAvoidingView)
@@ -142,6 +136,7 @@ export function createStyled<Theme extends AnyTheme>() {
142136
styled.SafeAreaView = styled(SafeAreaView)
143137
styled.ScrollView = styled(ScrollView)
144138
styled.SectionList = styled(SectionList)
139+
styled.Switch = styled(Switch)
145140
styled.Text = styled(Text)
146141
styled.TextInput = styled(TextInput)
147142
styled.TouchableHighlight = styled(TouchableHighlight)

src/types.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { ComponentPropsWithRef, ForwardRefExoticComponent } from 'react'
2+
import { StyleProp } from 'react-native'
23

34
export type AnyComponent<P extends object = any> = React.ComponentType<P>
45

@@ -16,7 +17,14 @@ export interface StyledObject<Props extends object = BaseObject> {
1617
[key: string]: StyledObject<Props> | string | number | StyleFunction<Props> | Array<Interpolation<Props>> | undefined
1718
}
1819

20+
/**
21+
* Use this type to disambiguate between a styled-component instance
22+
* and a StyleFunction or any other type of function.
23+
*/
24+
export type StyledComponentBrand<Props extends object> = { styles: StyledObject<Props>; readonly _sc: symbol }
25+
1926
export type Interpolation<Props extends object> =
27+
| StyledComponentBrand<Props>
2028
| StyleFunction<Props>
2129
| StyledObject<Props>
2230
| TemplateStringsArray
@@ -51,7 +59,7 @@ export interface Styled<
5159
<Props extends object = OwnProps>(
5260
styles: Styles<Themed<OwnProps & Props, Theme>>,
5361
...interpolations: Array<Interpolation<Themed<OwnProps & Props, Theme>>>
54-
): ForwardRefExoticComponent<Substitute<OwnProps, Props & AsComponentProps>>
62+
): ForwardRefExoticComponent<Substitute<OwnProps, Props & AsComponentProps>> & StyledComponentBrand<OwnProps & Props>
5563
attrs<Props extends object, R extends object = Props>(
5664
attrs: ((props: Themed<Props & OwnProps, Theme>) => PickProps<R, OwnProps>) | PickProps<R, OwnProps>
5765
): Styled<C, Theme, FastOmit<OwnProps, keyof R> & Props & Partial<R>>
@@ -61,3 +69,14 @@ export type UnknownProps = Record<string, unknown>
6169
export type UnknownStyles = Record<string, any>
6270
export type AttrsFn = (props: Themed<UnknownProps, AnyTheme>) => UnknownProps
6371
export type InnerAttrs = AttrsFn | UnknownProps
72+
73+
export interface AnyStyleProps {
74+
style?: StyleProp<UnknownStyles>
75+
}
76+
77+
export type StyledComponent = React.ForwardRefExoticComponent<Omit<React.PropsWithChildren<UnknownProps & AnyStyleProps & AsComponentProps>, "ref"> & React.RefAttributes<unknown>> & {
78+
isStyled?: boolean
79+
styles: StyledObject
80+
attrs: InnerAttrs[]
81+
origin: AnyComponent
82+
}

src/utils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1+
import { StyledComponent } from './types'
2+
13
export const isFunction = (fn: any): fn is Function => typeof fn === 'function'
4+
5+
export const isStyledComponent = (component: any): component is StyledComponent => !!(component as StyledComponent)?.isStyled

0 commit comments

Comments
 (0)