Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{
presets: [
'es2015',
'react',
],
plugins: [
'transform-es2015-modules-commonjs',
'transform-flow-strip-types',
'transform-class-properties',
['transform-object-rest-spread', { useBuiltIns: true }],
]
Expand Down
3 changes: 3 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ parser: babel-eslint

env:
jest: true

globals:
ReactClass: true
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why is this needed, where do we use such a global?

Copy link
Copy Markdown
Member Author

@lttb lttb Apr 23, 2017

Choose a reason for hiding this comment

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

eslint consider flow types and it works with rules like 'no-undef`, but it dosnt about this flow type. so we need to add it as global.

but maybe there is better solution with some plugin etc., I'll take a look

21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,21 @@ const PrimaryButton = styled(Button, {
Using base Style Sheet we can share classes between styled primitives.

```js
import { Styled } from 'styled-jss'
import injectSheet from 'react-jss'
import { Styled, injectStyled } from 'styled-jss'

// Base styles, like a regular jss object.
const styled = Styled({
root: {
margin: 10
margin: 10,
'& $baseButton': {
fontSize: 16
}
},
baseButton: {
padding: 10
padding: 10,
'& + &': {
marginLeft; 10
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

typo

}
}
})

Expand All @@ -58,20 +63,24 @@ const PrimaryButton = styled(NormalButton, {
// One can use classes AND styled primitives.
const MyComponent = ({classes}) => (
<div className={classes.root}>
<NormalButton>normal button</NormalButton>
<PrimaryButton>primary button</PrimaryButton>
</div>
)

const MyStyledComponent = injectSheet(styled.styles)(MyComponent)
const MyStyledComponent = injectStyled(styled)(MyComponent)
```

### With custom JSS setup:

`styled-jss` use [jss-preset-default](https://github.com/cssinjs/jss-preset-default) by default.
But you can require `createStyled` and provide your custom jss instance.

```js
import { create as createJss } from 'jss'
import vendorPrefixer from 'jss-vendor-prefixer'

import { createStyled } from 'styled-jss'
import createStyled from 'styled-jss/createStyled'

const jss = createJss()
jss.use(vendorPrefixer())
Expand Down
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@
"babel-core": "^6.23.1",
"babel-eslint": "^7.2.2",
"babel-plugin-transform-class-properties": "^6.23.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.23.0",
"babel-plugin-transform-flow-strip-types": "^6.22.0",
"babel-plugin-transform-object-rest-spread": "^6.23.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.23.0",
"eslint": "^3.13.0",
"eslint-config-airbnb": "^14.1.0",
Expand All @@ -78,5 +79,8 @@
"git add"
]
},
"pre-commit": "lint:staged"
"pre-commit": [
"lint:staged",
"test"
]
}
47 changes: 47 additions & 0 deletions src/createStyled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import styled from './styled'

import type {
BaseStylesType,
ComponentStyleType,
StyledType,
StyledElementAttrsType,
StyledElementType,
TagNameOrStyledElementType
} from './types'

const createStyled = (jss: Function) => (
baseStyles: BaseStylesType = {}
): StyledType => {
let staticSheet
let dynamicSheet

const mountSheets = () => {
if (!staticSheet) {
staticSheet = jss.createStyleSheet(baseStyles, {
meta: 'StaticBaseSheet',
}).attach()

dynamicSheet = jss.createStyleSheet({}, {
link: true,
meta: 'DynamicComponentSheet',
}).attach()
}

return {staticSheet, dynamicSheet}
}

return Object.assign((
tagNameOrStyledElement: TagNameOrStyledElementType,
ownStyle: ComponentStyleType
): StyledElementType => {
const {tagName, style}: StyledElementAttrsType = typeof tagNameOrStyledElement === 'string'
? {tagName: tagNameOrStyledElement, style: {}}
: tagNameOrStyledElement

const elementStyle = {...style, ...ownStyle}

return styled({tagName, baseStyles, elementStyle, mountSheets})
}, {mountSheets, styles: baseStyles})
}

export default createStyled
117 changes: 6 additions & 111 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,116 +1,11 @@
import {PureComponent, createElement} from 'react'
import {create as createJss, getDynamicStyles} from 'jss'
import {create as createJss} from 'jss'
import preset from 'jss-preset-default'
import filterProps from './utils/filter-props'

const jssDefault = createJss(preset())
import createStyled from './createStyled'

type StyledElementAttrsType = { tag: string, styles: Object }
type StyledElementType = Function & StyledElementAttrsType
type tagOrStyledElementTypeype = string | StyledElementType
type StyledElementPropsType = {
classes: Object,
children: ?any,
className: ?string,
}
const jss: Function = createJss(preset())

const createStyled = (jss?: Function = jssDefault) => (baseStyles: Object = {}) => {
let sheet
let dynamicSheet
let counter = 0
export const Styled = createStyled(jss)
export default Styled()

const styled = (
tagOrStyledElement: tagOrStyledElementTypeype,
ownStyles: Object
): StyledElementType => {
const {tag, styles}: StyledElementAttrsType = typeof tagOrStyledElement === 'string'
? {tag: tagOrStyledElement, styles: {}}
: tagOrStyledElement

const elementStyles = {...styles, ...ownStyles}
const dynamicStyles = getDynamicStyles(elementStyles)
const staticTag = `${tag}-${++counter}`

return class StyledElement extends PureComponent {
static tag = tag

static styles = elementStyles

props: StyledElementPropsType

tagScoped = ''

constructor(props) {
super(props)
this.tagScoped = `${tag}-${++counter}`
}

componentWillMount() {
if (!sheet) {
sheet = jss.createStyleSheet(baseStyles, {
link: true,
meta: 'StaticBaseSheet',
}).attach()

dynamicSheet = jss.createStyleSheet({}, {
link: true,
meta: 'DynamicComponentSheet',
}).attach()
}

if (!sheet.getRule(staticTag)) {
sheet.addRule(staticTag, elementStyles)
}

if (dynamicStyles && !dynamicSheet.getRule(this.tagScoped)) {
dynamicSheet
.detach()
.addRule(this.tagScoped, dynamicStyles)
dynamicSheet
.update(this.tagScoped, this.props)
.attach()
.link()
}
}

componentWillReceiveProps(nextProps: StyledElementPropsType) {
if (dynamicStyles) {
dynamicSheet.update(this.tagScoped, nextProps)
}
}

componentWillUnmount() {
dynamicSheet.deleteRule(this.tagScoped)
}

render() {
if (!sheet) return null

const {children, className, ...attrs} = this.props

const props = filterProps(attrs)
const tagClass = [
sheet.classes[staticTag],
dynamicSheet.classes[this.tagScoped],
className,
]
.filter(Boolean)
.join(' ')

return createElement(tag, {...props, className: tagClass}, children)
}
}
}

return Object.assign(styled, {styles: baseStyles})
}

const defaultStyledCreator = createStyled()
const defaultStyled = defaultStyledCreator()

export {
createStyled,
defaultStyledCreator as Styled,
}

export default defaultStyled
export {default as injectStyled} from './injectStyled'
20 changes: 20 additions & 0 deletions src/injectStyled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {createElement} from 'react'

import composeClasses from './utils/composeClasses'
import type {StyledType} from './types'

const injectStyled = (styled: StyledType) => (InnerComponent: ReactClass<any>) => {
const {staticSheet, dynamicSheet} = styled.mountSheets()

const classNames = Object.keys({...staticSheet.classes, ...dynamicSheet.classes})

const classes = [...classNames]
.reduce((acc, name) => ({
...acc,
[name]: composeClasses(staticSheet.classes[name], dynamicSheet.classes[name]),
}), {})

return (props: Object) => createElement(InnerComponent, {classes, ...props})
}

export default injectStyled
83 changes: 83 additions & 0 deletions src/styled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {PureComponent, createElement} from 'react'
import {getDynamicStyles} from 'jss'

import filterProps from './utils/filterProps'
import composeClasses from './utils/composeClasses'
import generateTagName from './utils/generateTagName'

import type {
JssStaticSheet,
JssDynamicSheet,
ComponentStyleType,
StyledElementPropsType
} from './types'

type StyledArgs = {
tagName: string,
elementStyle: ComponentStyleType,
mountSheets: Function
}

const styled = ({tagName, elementStyle, mountSheets}: StyledArgs) => {
const dynamicStyle = getDynamicStyles(elementStyle)
const staticTagName = generateTagName(tagName)

return class StyledElement extends PureComponent {
static tagName: string = tagName
static style: ComponentStyleType = elementStyle

props: StyledElementPropsType

dynamicTagName = ''
staticSheet: JssStaticSheet
dynamicSheet: JssDynamicSheet

constructor(props: StyledElementPropsType) {
super(props)
if (!this.dynamicTagName) {
this.dynamicTagName = generateTagName(tagName)
}
}

componentWillMount() {
Object.assign(this, mountSheets())

if (!this.staticSheet.getRule(staticTagName)) {
this.staticSheet.addRule(staticTagName, elementStyle)
}

if (dynamicStyle && !this.dynamicSheet.getRule(this.dynamicTagName)) {
this.dynamicSheet
.detach()
.addRule(this.dynamicTagName, dynamicStyle)
this.dynamicSheet
.update(this.dynamicTagName, this.props)
.attach()
.link()
}
}

componentWillReceiveProps(nextProps: StyledElementPropsType) {
if (dynamicStyle) {
this.dynamicSheet.update(this.dynamicTagName, nextProps)
}
}

render() {
if (!this.staticSheet) return null

const {children, className, ...attrs} = this.props

const props = filterProps(attrs)
const tagClass = composeClasses(
this.staticSheet.classes[staticTagName],
this.dynamicSheet.classes[this.dynamicTagName],
className
)

return createElement(tagName, {...props, className: tagClass}, children)
}
}
}

export default styled
4 changes: 2 additions & 2 deletions src/tests/App.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import type {StyledType} from '../types'


export default (styled: Function) => {
export default (styled: StyledType) => {
const App = styled('div', {
margin: 50,
})
Expand Down
Loading