Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Styled API #1094

Merged
merged 25 commits into from
Jun 12, 2019
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@

[options]
include_warnings=true
esproposal.export_star_as=enable
esproposal.export_star_as=enable
suppress_comment= \\(.\\|\n\\)*\\$FlowFixMe
suppress_comment= \\(.\\|\n\\)*\\$FlowIgnore
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be used for things which need to be ignored but need no fixing

2 changes: 2 additions & 0 deletions flow-typed/mocha.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare function describe(string, Function): void
declare function it(string, Function): void
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"eslint-config-jss": "^5.0.1",
"eslint-config-prettier": "^2.9.0",
"expect.js": "^0.3.1",
"flow-bin": "^0.94.0",
"flow-bin": "^0.98.0",
"json-loader": "^0.5.4",
"karma": "^1.3.0",
"karma-benchmark": "^0.6.0",
Expand Down
30 changes: 15 additions & 15 deletions packages/react-jss/.size-snapshot.json
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
{
"dist/react-jss.js": {
"bundled": 128759,
"minified": 44373,
"gzipped": 13874
"bundled": 163249,
"minified": 56829,
"gzipped": 18451
},
"dist/react-jss.min.js": {
"bundled": 96372,
"minified": 34480,
"gzipped": 11091
"bundled": 106557,
"minified": 40199,
"gzipped": 13485
},
"dist/react-jss.cjs.js": {
"bundled": 21204,
"minified": 9854,
"gzipped": 3193
"bundled": 24555,
"minified": 10880,
"gzipped": 3571
},
"dist/react-jss.esm.js": {
"bundled": 20353,
"minified": 9113,
"gzipped": 3072,
"bundled": 23638,
"minified": 10083,
"gzipped": 3445,
"treeshaked": {
"rollup": {
"code": 2040,
"import_statements": 439
"code": 2071,
"import_statements": 470
},
"webpack": {
"code": 3470
"code": 3535
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion packages/react-jss/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 @@ -51,6 +52,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 packages/react-jss/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) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can be in changelog

updateDynamicRules(data, state.sheet, state.dynamicRules)
}
},
Expand Down
1 change: 1 addition & 0 deletions packages/react-jss/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 packages/react-jss/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
}
}