Skip to content

Commit

Permalink
(split)Styled API (cssinjs#1094)
Browse files Browse the repository at this point in the history
* wip styled

* more tests, lint

* add styled system tests

* switch back to style-system v3, until v4 changes the format back

* switch back to style-system v3, until v4 changes the format back

* switch back to style-system v3, until v4 changes the format back, better types

* upgrade flow and react

* upgrade theming

* flow types

* migrate to hooks

* Fix tests

* Update size snapshot

* more tests

* accept multiple static and dynamic style declarations

* implement shouldForwardProp

* merge the styles arguments passed to styled

* add test for empty values returned from function rules

* optimize perf, better types

* support label

* Update changelog.md

* skip the tests for now on the ci
  • Loading branch information
kof committed Jun 12, 2019
1 parent 71537b3 commit 4bf6f40
Show file tree
Hide file tree
Showing 11 changed files with 698 additions and 38 deletions.
30 changes: 15 additions & 15 deletions .size-snapshot.json
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
{
"dist/react-jss.js": {
"bundled": 128515,
"minified": 44371,
"gzipped": 13873
"bundled": 163005,
"minified": 56827,
"gzipped": 18448
},
"dist/react-jss.min.js": {
"bundled": 96128,
"minified": 34478,
"gzipped": 11089
"bundled": 106313,
"minified": 40197,
"gzipped": 13484
},
"dist/react-jss.cjs.js": {
"bundled": 20600,
"minified": 9694,
"gzipped": 3131
"bundled": 23951,
"minified": 10720,
"gzipped": 3514
},
"dist/react-jss.esm.js": {
"bundled": 19744,
"minified": 8949,
"gzipped": 3011,
"bundled": 23029,
"minified": 9919,
"gzipped": 3388,
"treeshaked": {
"rollup": {
"code": 1899,
"import_statements": 491
"code": 1930,
"import_statements": 522
},
"webpack": {
"code": 3363
"code": 3428
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
},
"dependencies": {
"@babel/runtime": "^7.3.1",
"@emotion/is-prop-valid": "^0.7.3",
"hoist-non-react-statics": "^3.2.0",
"is-in-browser": "^1.1.3",
"jss": "10.0.0-alpha.17",
Expand All @@ -52,6 +53,7 @@
"tiny-warning": "^1.0.2"
},
"devDependencies": {
"@types/react": "^16.7.20"
"@types/react": "^16.8.16",
"styled-system": "4.2.0-0"
}
}
3 changes: 2 additions & 1 deletion src/createUseStyles.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ const createUseStyles = <Theme: {}>(styles: Styles<Theme>, options?: HookOptions

useEffectOrLayoutEffect(
() => {
if (state.sheet && state.dynamicRules) {
// We only need to update the rules on a subsequent update and not in the first mount
if (state.sheet && state.dynamicRules && !isFirstMount.current) {
updateDynamicRules(data, state.sheet, state.dynamicRules)
}
},
Expand Down
12 changes: 3 additions & 9 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,9 @@ import {
SheetsRegistry,
Styles,
StyleSheetFactoryOptions,
CreateGenerateIdOptions,
CreateGenerateIdOptions
} from 'jss'
import {
createTheming,
useTheme,
withTheme,
ThemeProvider,
Theming,
} from 'theming'
import {createTheming, useTheme, withTheme, ThemeProvider, Theming} from 'theming'

declare const jss: Jss

Expand Down Expand Up @@ -74,7 +68,7 @@ export {
withTheme,
createTheming,
useTheme,
JssContext,
JssContext
}

export default withStyles
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export {default as JssProvider} from './JssProvider'
export {default as jss} from './jss'
export {SheetsRegistry, createGenerateId} from 'jss'
export {default as JssContext} from './JssContext'
export {default as styled} from './styled'
export {withStyles}

// Kept for backwards compatibility.
Expand Down
111 changes: 111 additions & 0 deletions src/styled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// @flow
/* eslint-disable react/prop-types, react/require-default-props */

import React from 'react'
import isPropValid from '@emotion/is-prop-valid'
import type {StatelessFunctionalComponent, ComponentType} from 'react'
import {ThemeContext as DefaultThemeContext} from 'theming'

import createUseStyles from './createUseStyles'
import type {HookOptions, StaticStyle, DynamicStyle} from './types'

type StyledProps = {
className?: string,
as?: string
}

type StyleArg<Theme> = StaticStyle | DynamicStyle<Theme> | null | void | ''

const parseStyles = <Theme>(args: {[string]: StyleArg<Theme>}) => {
const dynamicStyles = []
let staticStyle
let label = 'css'

// Not using ...rest to optimize perf.
for (const key in args) {
const style = args[key]
if (!style) continue
if (typeof style === 'function') {
dynamicStyles.push(style)
} else {
if (!staticStyle) staticStyle = {}
Object.assign(staticStyle, style)
if ('label' in staticStyle) {
// Label could potentially be defined in each style object,
// so we take the first one and ignore every subsequent one.
if (label === 'css') label = staticStyle.label
// Label should not leak to the core.
delete staticStyle.label
}
}
}

const styles = {}

if (staticStyle) styles[label] = staticStyle

// When there is only one function rule, we don't need to wrap it.
if (dynamicStyles.length === 1) {
styles.cssd = dynamicStyles[0]
}

// We create a new function rule which will call all other function rules
// and merge the styles they return.
if (dynamicStyles.length > 1) {
styles.cssd = props => {
const merged = {}
for (let i = 0; i < dynamicStyles.length; i++) {
const dynamicStyle = dynamicStyles[i](props)
if (dynamicStyle) Object.assign(merged, dynamicStyle)
}
return merged
}
}

return {styles, label}
}

type StyledOptions<Theme> = HookOptions<Theme> & {
shouldForwardProp?: string => boolean
}

export default <Theme: {}>(
type: string | StatelessFunctionalComponent<StyledProps> | ComponentType<StyledProps>,
options?: StyledOptions<Theme> = {}
) => {
const {theming, shouldForwardProp} = options
const isTagName = typeof type === 'string'
const ThemeContext = theming ? theming.context : DefaultThemeContext

return function StyledFactory(/* :: ...args: StyleArg<Theme>[] */): StatelessFunctionalComponent<
StyledProps
> {
// eslint-disable-next-line prefer-rest-params
const {styles, label} = parseStyles(arguments)

const useStyles = createUseStyles(styles, label ? {...options, name: label} : options)

const Styled = (props: StyledProps) => {
const {as, className} = props
// $FlowFixMe theming ThemeContext types need to be fixed.
const theme = React.useContext(ThemeContext)
const classes = useStyles({...props, theme})
const childProps: StyledProps = ({}: any)

for (const prop in props) {
// We don't want to pass non-dom props to the DOM,
// but we still want to forward them to a users component.
if (isTagName && !isPropValid(prop)) continue
if (shouldForwardProp && shouldForwardProp(prop) === false) continue
childProps[prop] = props[prop]
}
// $FlowIgnore we don't care label might not exist in classes.
const classNames = `${classes[label] || classes.css || ''} ${classes.cssd || ''}`.trim()
childProps.className = className ? `${className} ${classNames}` : classNames

return React.createElement(as || type, childProps)
}

return Styled
}
}

0 comments on commit 4bf6f40

Please sign in to comment.