├── .editorconfig ├── .eslintrc ├── .gitignore ├── .jsbeautifyrc ├── README.md ├── gulpfile.js ├── package.json └── src ├── __tests__ ├── CreateClassComponent.jsx ├── ES6ClassComponent.jsx ├── ES6ClassDecorator.jsx ├── ES6ClassDecoratorWithPrefix.jsx ├── Keyframes.jsx └── index.jsx └── index.jsx /.editorconfig: -------------------------------------------------------------------------------- 1 | root=true 2 | [*] 3 | end_of_line = lf 4 | insert_final_newline = true 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "ecmaFeatures": { 3 | "binaryLiterals": false, 4 | "blockBindings": true, 5 | "defaultParams": true, 6 | "forOf": true, 7 | "generators": true, 8 | "objectLiteralComputedProperties": true, 9 | "objectLiteralDuplicateProperties": false, 10 | "objectLiteralShorthandMethods": true, 11 | "objectLiteralShorthandProperties": true, 12 | "octalLiterals": false, 13 | "regexUFlag": false, 14 | "regexYFlag": false, 15 | "templateStrings": true, 16 | "unicodePointEscapes": true, 17 | "jsx": true 18 | }, 19 | "env": { 20 | "browser": true, 21 | "node": true 22 | }, 23 | "parser": "babel-eslint", 24 | "plugins": [ 25 | "react" 26 | ], 27 | "rules": { 28 | 29 | /*Possible Errors */ 30 | "comma-dangle": [1, "always-multiline"], 31 | "no-cond-assign": [1, "except-parens"], 32 | "no-console": 0, 33 | "no-constant-condition": 1, 34 | "no-control-regex": 1, 35 | "no-debugger": 1, 36 | "no-dupe-args": 1, 37 | "no-dupe-keys": 1, 38 | "no-duplicate-case": 0, 39 | "no-empty-character-class": 1, 40 | "no-empty": 1, 41 | "no-ex-assign": 1, 42 | "no-extra-boolean-cast": 1, 43 | "no-extra-parens": 0, 44 | "no-extra-semi": 1, 45 | "no-func-assign": 1, 46 | "no-inner-declarations": [1, "functions"], 47 | "no-invalid-regexp": 1, 48 | "no-irregular-whitespace": 1, 49 | "no-negated-in-lhs": 1, 50 | "no-obj-calls": 1, 51 | "no-regex-spaces": 1, 52 | "no-reserved-keys": 0, 53 | "no-sparse-arrays": 1, 54 | "no-unreachable": 1, 55 | "use-isnan": 1, 56 | "valid-jsdoc": 1, 57 | "valid-typeof": 1, 58 | 59 | /* Best Practices */ 60 | "accessor-pairs": 0, 61 | "block-scoped-var": 1, 62 | "complexity": 0, 63 | "consistent-return": 1, 64 | "curly": [1, "all"], 65 | "default-case": 0, 66 | "dot-notation": [1, { "allowKeywords": true, "allowPattern": "" }], 67 | "dot-location": [1, "property"], 68 | "eqeqeq": 1, 69 | "guard-for-in": 0, 70 | "no-alert": 1, 71 | "no-caller": 1, 72 | "no-div-regex": 1, 73 | "no-else-return": 1, 74 | "no-empty-label": 1, 75 | "no-eq-null": 0, 76 | "no-eval": 1, 77 | "no-extend-native": 1, 78 | "no-extra-bind": 1, 79 | "no-fallthrough": 0, 80 | "no-floating-decimal": 1, 81 | "no-implied-eval": 1, 82 | "no-iterator": 1, 83 | "no-labels": 1, 84 | "no-lone-blocks": 1, 85 | "no-loop-func": 1, 86 | "no-multi-spaces": 1, 87 | "no-multi-str": 1, 88 | "no-native-reassign": 1, 89 | "no-new-func": 1, 90 | "no-new-wrappers": 1, 91 | "no-new": 1, 92 | "no-octal-escape": 1, 93 | "no-octal": 1, 94 | "no-param-reassign": 0, 95 | "no-process-env": 0, 96 | "no-proto": 1, 97 | "no-redeclare": 1, 98 | "no-return-assign": 1, 99 | "no-script-url": 1, 100 | "no-self-compare": 1, 101 | "no-sequences": 1, 102 | "no-throw-literal": 1, 103 | "no-unused-expressions": 0, 104 | "no-void": 0, 105 | "no-warning-comments": [1, { "terms": ["todo", "tofix"], "location": "start" }], 106 | "no-with": 1, 107 | "radix": 1, 108 | "vars-on-top": 1, 109 | "wrap-iife": [1, "inside"], 110 | "yoda": [1, "never"], 111 | 112 | /* Strict Mode */ 113 | "strict": [1, "never"], 114 | 115 | /* Variables */ 116 | "no-catch-shadow": 0, 117 | "no-delete-var": 1, 118 | "no-label-var": 1, 119 | "no-shadow-restricted-names": 1, 120 | "no-shadow": 1, 121 | "no-undef-init": 1, 122 | "no-undef": 1, 123 | "no-undefined": 1, 124 | "no-unused-vars": [1, { "vars": "local", "args": "after-used" }], 125 | "no-use-before-define": 1, 126 | 127 | /* Node.js */ 128 | "handle-callback-err": 1, 129 | "no-mixed-requires": 1, 130 | "no-new-require": 1, 131 | "no-path-concat": 1, 132 | "no-process-exit": 1, 133 | "no-restricted-modules": [1, ""], // add any unwanted Node.js core modules 134 | "no-sync": 1, 135 | 136 | /* Stylistic Issues */ 137 | "brace-style": [1, "stroustrup", { "allowSingleLine": true }], 138 | "camelcase": [1, { "properties": "always" }], 139 | "comma-spacing": [1, { "before": false, "after": true }], 140 | "comma-style": [1, "last"], 141 | "computed-property-spacing": 0, 142 | "consistent-this": 0, 143 | "eol-last": 1, 144 | "func-names": 1, 145 | "func-style": 0, 146 | "indent": [1, 2], 147 | "key-spacing": [1, { "beforeColon": false, "afterColon": true }], 148 | "linebreak-style": 0, 149 | "max-nested-callbacks": [0, 3], 150 | "new-cap": 1, 151 | "new-parens": 1, 152 | "newline-after-var": 0, 153 | "no-array-constructor": 1, 154 | "no-continue": 1, 155 | "no-inline-comments": 0, 156 | "no-lonely-if": 1, 157 | "no-mixed-spaces-and-tabs": 1, 158 | "no-multiple-empty-lines": [1, { "max": 1 }], 159 | "no-nested-ternary": 0, 160 | "no-new-object": 1, 161 | "no-spaced-func": 1, 162 | "no-ternary": 0, 163 | "no-trailing-spaces": 1, 164 | "no-underscore-dangle": 0, 165 | "no-unneeded-ternary": 1, 166 | "no-wrap-func": 1, 167 | "one-var": [1, "never"], 168 | "operator-assignment": [1, "never"], 169 | "padded-blocks": [0, "never"], 170 | "quote-props": [0, "as-needed"], 171 | "quotes": [1, "single"], 172 | "semi-spacing": [1, { "before": false, "after": true }], 173 | "semi": [1, "always"], 174 | "sort-vars": 0, 175 | "space-after-keywords": 0, 176 | "space-before-blocks": [1, "always"], 177 | "space-before-function-paren": [1, "never"], 178 | "space-in-brackets": 0, 179 | "space-in-parens": [1, "never"], 180 | "space-infix-ops": 1, 181 | "space-return-throw-case": 1, 182 | "space-unary-ops": 0, 183 | "spaced-comment": [1, "always"], 184 | "wrap-regex": 1, 185 | 186 | /* ECMAScript 6 */ 187 | "generator-star-spacing": [1, "after"], 188 | "no-var": 1, 189 | "object-shorthand": [1, "always"], 190 | "prefer-const": 1, 191 | 192 | /* Legacy */ 193 | "max-depth": [0, 3], 194 | "max-len": [1, 125, 4], 195 | "max-params": 0, 196 | "max-statements": 0, 197 | "no-bitwise": 1, 198 | "no-plusplus": 1, 199 | 200 | /* React */ 201 | "react/display-name": 1, 202 | "react/jsx-boolean-value": 1, 203 | "react/jsx-quotes": [1, "single"], 204 | "react/jsx-no-undef": 1, 205 | "react/jsx-sort-props": 0, 206 | "react/jsx-sort-prop-types": 1, 207 | "react/jsx-uses-react": 1, 208 | "react/jsx-uses-vars": 1, 209 | "react/no-did-mount-set-state": 1, 210 | "react/no-did-update-set-state": 1, 211 | "react/no-multi-comp": 1, 212 | "react/no-unknown-property": 1, 213 | "react/prop-types": 1, 214 | "react/react-in-jsx-scope": 1, 215 | "react/self-closing-comp": 1, 216 | "react/sort-comp": 1, 217 | "react/wrap-multilines": 0 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | tmp 3 | *.pid 4 | *.lock 5 | npm-debug.log 6 | -------------------------------------------------------------------------------- /.jsbeautifyrc: -------------------------------------------------------------------------------- 1 | { 2 | "html": { 3 | "allowed_file_extensions": ["htm", "html", "xhtml", "shtml", "xml", "svg"], 4 | "brace_style": "collapse", 5 | "end_with_newline": false, 6 | "indent_char": " ", 7 | "indent_handlebars": false, 8 | "indent_inner_html": false, 9 | "indent_size": 2, 10 | "indent_scripts": "keep", 11 | "max_preserve_newlines": 4, 12 | "preserve_newlines": true, 13 | "unformatted": ["a", "span", "img", "code", "pre", "sub", "sup", "em", "strong", "b", "i", "u", "strike", "big", 14 | "small", "pre", "h1", "h2", "h3", "h4", "h5", "h6" 15 | ], 16 | "wrap_line_length": 124 17 | }, 18 | "css": { 19 | "allowed_file_extensions": ["css", "scss", "sass", "less"], 20 | "end_with_newline": false, 21 | "newline_between_rules": true, 22 | "indent_char": " ", 23 | "indent_size": 2, 24 | "selector_separator_newline": true 25 | }, 26 | "js": { 27 | "allowed_file_extensions": ["eslintrc", "js", "jsx", "json", "jshintrc", "jsbeautifyrc"], 28 | "brace_style": "collapse", 29 | "break_chained_methods": false, 30 | "comma_first": false, 31 | "e4x": false, 32 | "end_with_newline": false, 33 | "eval_code": false, 34 | "indent_char": " ", 35 | "indent_level": 0, 36 | "indent_size": 2, 37 | "jslint_happy": false, 38 | "keep_array_indentation": false, 39 | "keep_function_indentation": false, 40 | "max_preserve_newlines": 4, 41 | "preserve_newlines": true, 42 | "space_after_anon_function": false, 43 | "space_before_conditional": false, 44 | "space_in_empty_paren": false, 45 | "space_in_paren": false, 46 | "unescape_strings": false, 47 | "wrap_line_length": 124 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | React Statics Styles 2 | ==================== 3 | 4 | Ultra-light utility to help writing CSS inside your JS React components declarations. 5 | 6 | Statically extracts CSS styles declared at the component class level and outputs a nicely formatted CSS string. It is then easing to pipe it to your CSS postprocessor of choice (eg. `postcss`). 7 | 8 | Best friends with [`gulp-react-statics-styles`](https://github.com/elierotenberg/gulp-react-statics-styles) to make it work with your building pipeline. 9 | 10 | Usage (without gulp) 11 | ==================== 12 | 13 | Declare `styles` as a `static` property of your component class (or using `statics` if you use vanilla `React.createClass`): 14 | ```js 15 | class MyComponent extends React.Component { 16 | static styles = { 17 | '.MyComponent .MyComponent-item': { 18 | // you can put build-time calculations here 19 | fontSize: 0.8 * readFontSizeFromConfig() + 'px', 20 | } 21 | }; 22 | 23 | render: function() { 24 | return
25 |
This is smaller that usual.
26 |
; 27 | }, 28 | }; 29 | 30 | // alternate syntax using the decorator 31 | import { styles } from 'react-statics-styles'; 32 | 33 | @styles({ 34 | '.MyComponent .MyComponent-item': { 35 | fontSize: 0.8 * readFontSizeFromConfig() + 'px', 36 | } 37 | }) 38 | class MyComponent extends React.Component { ... } 39 | ``` 40 | 41 | Then pass one or more class definition(s) to `extractStyles(class)` or `extractAllStyles(array of class)`: 42 | ```js 43 | import { extractStyles, extractAllStyles } from 'react-statics-styles'; 44 | extractStyles(MyComponent); // returns a CSS string 45 | extractAllStyles([MyComponent1, MyComponent2, ...]); 46 | ``` 47 | 48 | Assuming that `readFontSizeFromConfig()` returns `10`, then the first line returns the string: 49 | 50 | ```css 51 | /* @react-nexus-style MyComponent */ 52 | .MyComponent .MyComponent-item { 53 | font-size: 8px; 54 | } 55 | ``` 56 | 57 | The decorator form supports passing additional options. 58 | 59 | The only currently supported option is `prefix`, a static string which will be preprended to all the generated selectors. 60 | 61 | ```js 62 | @styles({ 63 | '.ES6ClassDecoratorWithPrefix': { 64 | minWidth: '334px', 65 | }, 66 | }, { prefix: '.MyApp '}) 67 | class ES6ClassDecoratorWithPrefix extends React.Component { 68 | static displayName = 'ES6ClassDecoratorWithPrefix'; 69 | 70 | render() { 71 | return
; 72 | } 73 | } 74 | ``` 75 | 76 | will yield 77 | 78 | ```css 79 | /* @react-statics-styles ES6ClassDecoratorWithPrefix */ 80 | .MyApp .ES6ClassDecoratorWithPrefix { 81 | min-width: 334px; 82 | } 83 | ``` 84 | 85 | Usage (with gulp) 86 | ================= 87 | 88 | See [`gulp-react-statics-styles`](https://github.com/elierotenberg/gulp-react-statics-styles). 89 | 90 | 91 | ### Installation 92 | 93 | This module is written in ES6/7. You will need `babel` to run it. 94 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | require('babel/register')({ 2 | only: /\.jsx$/, 3 | optional: [ 4 | 'runtime', 5 | 'es7.classProperties', 6 | 'es7.decorators', 7 | ], 8 | }); 9 | 10 | var eslint = require('gulp-eslint'); 11 | var gulp = require('gulp'); 12 | var plumber = require('gulp-plumber'); 13 | var mocha = require('gulp-mocha'); 14 | 15 | function lint() { 16 | return gulp.src('src/**/*.js') 17 | .pipe(plumber()) 18 | .pipe(eslint()) 19 | .pipe(eslint.format()); 20 | } 21 | 22 | function test() { 23 | return gulp.src('src/__tests__/**/*.jsx') 24 | .pipe(mocha()); 25 | } 26 | 27 | gulp.task('lint', lint); 28 | gulp.task('test', ['lint'], test); 29 | gulp.task('default', ['test']); 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-statics-styles", 3 | "version": "3.1.0", 4 | "description": "", 5 | "license": "MIT", 6 | "author": "Elie Rotenberg ", 7 | "main": "src/index.jsx", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/elierotenberg/react-statics-styles.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/elierotenberg/react-statics-styles/issues" 14 | }, 15 | "homepage": "https://github.com/elierotenberg/react-statics-styles", 16 | "scripts": { 17 | "test": "gulp test" 18 | }, 19 | "devDependencies": { 20 | "babel": "^5.5.8", 21 | "babel-eslint": "^3.1.15", 22 | "babel-runtime": "^5.5.8", 23 | "eslint": "^0.23.0", 24 | "eslint-plugin-react": "^2.5.2", 25 | "gulp": "^3.9.0", 26 | "gulp-eslint": "^0.14.0", 27 | "gulp-mocha": "^2.1.1", 28 | "gulp-plumber": "^1.0.1", 29 | "react": "^0.13.3" 30 | }, 31 | "dependencies": { 32 | "bluebird": "^2.9.30", 33 | "change-case": "^2.3.0", 34 | "lodash": "^3.9.3", 35 | "should": "^6.0.3" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/__tests__/CreateClassComponent.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const CreateClassComponent = React.createClass({ 4 | displayName: 'CreateClassComponent', 5 | statics: { 6 | styles: { 7 | '.CreateClassComponent': { 8 | minWidth: '1337px', 9 | }, 10 | }, 11 | }, 12 | 13 | render() { 14 | return
; 15 | }, 16 | }); 17 | 18 | export default CreateClassComponent; 19 | -------------------------------------------------------------------------------- /src/__tests__/ES6ClassComponent.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | class ES6ClassComponent extends React.Component { 4 | static displayName = 'ES6ClassComponent'; 5 | 6 | static styles = { 7 | '.ES6ClassComponent': { 8 | minWidth: '42px', 9 | }, 10 | }; 11 | 12 | render() { 13 | return
; 14 | } 15 | } 16 | 17 | export default ES6ClassComponent; 18 | -------------------------------------------------------------------------------- /src/__tests__/ES6ClassDecorator.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { styles } from '../'; 3 | 4 | @styles({ 5 | '.ES6ClassDecorator': { 6 | minWidth: '33px', 7 | }, 8 | }) 9 | class ES6ClassDecorator extends React.Component { 10 | static displayName = 'ES6ClassDecorator'; 11 | 12 | render() { 13 | return
; 14 | } 15 | } 16 | 17 | export default ES6ClassDecorator; 18 | -------------------------------------------------------------------------------- /src/__tests__/ES6ClassDecoratorWithPrefix.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { styles } from '../'; 3 | 4 | @styles({ 5 | '.ES6ClassDecoratorWithPrefix': { 6 | minWidth: '334px', 7 | }, 8 | }, { prefix: '.MyApp '}) 9 | class ES6ClassDecoratorWithPrefix extends React.Component { 10 | static displayName = 'ES6ClassDecoratorWithPrefix'; 11 | 12 | render() { 13 | return
; 14 | } 15 | } 16 | 17 | export default ES6ClassDecoratorWithPrefix; 18 | -------------------------------------------------------------------------------- /src/__tests__/Keyframes.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const CreateClassComponent = React.createClass({ 4 | displayName: 'Keyframes', 5 | statics: { 6 | stylesOpts: '-webkit-', 7 | styles: { 8 | '@keyframes animationFromTo': { 9 | from: { 10 | transform: 'rotate(0deg)', 11 | top: '0px', 12 | }, 13 | to: { 14 | transform: 'rotate(360deg)', 15 | top: '100px', 16 | }, 17 | }, 18 | '@keyframes animationPercent': { 19 | '0%': { opacity: 0 }, 20 | '100%': { opacity: 1 }, 21 | }, 22 | }, 23 | }, 24 | 25 | render() { 26 | return
; 27 | }, 28 | }); 29 | 30 | export default CreateClassComponent; 31 | -------------------------------------------------------------------------------- /src/__tests__/index.jsx: -------------------------------------------------------------------------------- 1 | const { describe, it } = global; 2 | import 'should'; 3 | import { extractStyles } from '../'; 4 | 5 | import CreateClassComponent from './CreateClassComponent'; 6 | import ES6ClassComponent from './ES6ClassComponent'; 7 | import ES6ClassDecorator from './ES6ClassDecorator'; 8 | import ES6ClassDecoratorWithPrefix from './ES6ClassDecoratorWithPrefix'; 9 | import Keyframes from './Keyframes'; 10 | 11 | describe('CreateClassComponent', () => 12 | it('should extract the correct CSS', () => 13 | extractStyles(CreateClassComponent).should.be.exactly([ 14 | '/* @react-statics-styles CreateClassComponent */', 15 | '.CreateClassComponent {', 16 | ' min-width: 1337px;', 17 | '}', 18 | '', 19 | ].join('\n')) 20 | ) 21 | ); 22 | 23 | describe('ES6ClassComponent', () => 24 | it('should extract the correct CSS', () => 25 | extractStyles(ES6ClassComponent).should.be.exactly([ 26 | '/* @react-statics-styles ES6ClassComponent */', 27 | '.ES6ClassComponent {', 28 | ' min-width: 42px;', 29 | '}', 30 | '', 31 | ].join('\n')) 32 | ) 33 | ); 34 | 35 | describe('ES6ClassDecorator', () => 36 | it('should extract the correct CSS', () => 37 | extractStyles(ES6ClassDecorator).should.be.exactly([ 38 | '/* @react-statics-styles ES6ClassDecorator */', 39 | '.ES6ClassDecorator {', 40 | ' min-width: 33px;', 41 | '}', 42 | '', 43 | ].join('\n')) 44 | ) 45 | ); 46 | 47 | describe('ES6ClassDecoratorWithPrefix', () => 48 | it('should extract the correct CSS', () => 49 | extractStyles(ES6ClassDecoratorWithPrefix).should.be.exactly([ 50 | '/* @react-statics-styles ES6ClassDecoratorWithPrefix */', 51 | '.MyApp .ES6ClassDecoratorWithPrefix {', 52 | ' min-width: 334px;', 53 | '}', 54 | '', 55 | ].join('\n')) 56 | ) 57 | ); 58 | 59 | describe('Keyframes', () => 60 | it('should extract the correct CSS', () => 61 | extractStyles(Keyframes).should.be.exactly([ 62 | '/* @react-statics-styles Keyframes */', 63 | '@keyframes animationFromTo {', 64 | ' from {', 65 | ' transform: rotate(0deg);', 66 | ' top: 0px;', 67 | ' }', 68 | ' to {', 69 | ' transform: rotate(360deg);', 70 | ' top: 100px;', 71 | ' }', 72 | '}', 73 | '@keyframes animationPercent {', 74 | ' 0% {', 75 | ' opacity: 0;', 76 | ' }', 77 | ' 100% {', 78 | ' opacity: 1;', 79 | ' }', 80 | '}', 81 | '', 82 | ].join('\n')) 83 | ) 84 | ); 85 | -------------------------------------------------------------------------------- /src/index.jsx: -------------------------------------------------------------------------------- 1 | import recase from 'change-case'; 2 | import _ from 'lodash'; 3 | 4 | const stylesOpts = Symbol('stylesOpts'); 5 | 6 | function extractKeyframes(keyframe, prefix) { 7 | const rulesKeyframe = Object.keys(keyframe).map((attr) => { 8 | return ` ${prefix}${attr}: ${keyframe[attr]};`; 9 | }).join('\n'); 10 | 11 | return `{\n${rulesKeyframe}\n }`; 12 | } 13 | 14 | function extractStyle(selector, reactStyle, { prefix = '' } = {}) { 15 | const rules = Object.keys(reactStyle).map((attr) => { 16 | return selector.indexOf('@keyframes') > -1 17 | ? ` ${prefix}${attr} ${extractKeyframes(reactStyle[attr], prefix)}` 18 | : ` ${recase.paramCase(attr)}: ${reactStyle[attr]};`; 19 | }).join('\n'); 20 | return `${prefix}${selector} {\n${rules}\n}`; 21 | } 22 | 23 | function extractStyles(Component) { 24 | if(!_.isObject(Component) || 25 | !Component.styles || 26 | !_.isObject(Component.styles)) { 27 | return null; 28 | } 29 | return `/* @react-statics-styles ${Component.displayName} */\n${Object.keys(Component.styles) 30 | .map((selector) => extractStyle(selector, Component.styles[selector], Component[stylesOpts])) 31 | .join('\n')}\n`; 32 | } 33 | 34 | function extractAllStyles(Components) { 35 | return _.without(_.map(Components, extractStyles), null).join('\n'); 36 | } 37 | 38 | function styles(newStyles, opts) { 39 | return (Component) => Object.assign(class extends Component { 40 | static styles = Object.assign({}, Component.styles || {}, newStyles); 41 | }, { [stylesOpts]: opts }); 42 | } 43 | 44 | export default { extractStyle, extractStyles, extractAllStyles, styles, stylesOpts }; 45 | --------------------------------------------------------------------------------