├── .babelrc
├── .eslintrc.json
├── .gitignore
├── .npmignore
├── CONTRIBUTING.md
├── README.md
├── bs-config.json
├── examples
└── test-app
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ └── index.html
│ ├── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── index.css
│ ├── index.js
│ └── logo.svg
│ └── yarn.lock
├── package.json
├── rollup.config.js
├── src
├── __snapshots__
│ └── createStyleHoc.test.js.snap
├── createStyleHoc.js
├── createStyleHoc.test.js
├── helpers
│ ├── generateStyleObject.test.js
│ └── index.js
├── index.js
├── withMargin.js
└── withPadding.js
├── yarn-error.log
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015"],
3 | "plugins": ["transform-object-rest-spread"],
4 | "env": {
5 | "test": {
6 | "plugins": ["istanbul", "transform-object-rest-spread", "rewire"]
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "ecmaVersion": 6,
3 | "env": {
4 | "es6": true,
5 | "mocha": true,
6 | "node": true,
7 | "jest": true
8 | },
9 | "parserOptions": {
10 | "ecmaVersion": 6,
11 | "sourceType": "module",
12 | "ecmaFeatures": {
13 | "jsx": true,
14 | "experimentalObjectRestSpread": true
15 | }
16 | },
17 | "plugins": [
18 | "react"
19 | ],
20 | "rules": {
21 | "react/jsx-uses-react": "error",
22 | "react/jsx-uses-vars": "error",
23 | "brace-style": 1,
24 | "camelcase": [1, {
25 | "properties": "never"
26 | }],
27 | "comma-spacing": [1, {
28 | "before": false,
29 | "after": true
30 | }],
31 | "comma-style": [1, "last"],
32 | "eol-last": 1,
33 | "eqeqeq": [1, "allow-null"],
34 | "func-call-spacing": [1, "never"],
35 | "indent": [1, 2, {
36 | "SwitchCase": 1
37 | }],
38 | "key-spacing": [1, {
39 | "beforeColon": false,
40 | "afterColon": true
41 | }],
42 | "keyword-spacing": [1, {
43 | "before": true,
44 | "after": true
45 | }],
46 | "new-parens": 1,
47 | "no-case-declarations": 1,
48 | "no-cond-assign": 1,
49 | "no-console": 1,
50 | "no-constant-condition": 1,
51 | "no-debugger": 1,
52 | "no-dupe-args": 1,
53 | "no-dupe-keys": 1,
54 | "no-duplicate-case": 1,
55 | "no-empty": 1,
56 | "no-empty-character-class": 1,
57 | "no-ex-assign": 1,
58 | "no-extra-semi": 1,
59 | "no-func-assign": 1,
60 | "no-invalid-regexp": 1,
61 | "no-irregular-whitespace": 1,
62 | "no-mixed-spaces-and-tabs": 1,
63 | "no-new-object": 1,
64 | "no-obj-calls": 1,
65 | "no-plusplus": 1,
66 | "no-redeclare": 1,
67 | "no-regex-spaces": 1,
68 | "no-sparse-arrays": 1,
69 | "no-tabs": 1,
70 | "no-trailing-spaces": 1,
71 | "no-undef": 1,
72 | "no-unexpected-multiline": 1,
73 | "no-unreachable": 1,
74 | "no-unsafe-finally": 1,
75 | "no-unused-vars": [1, {
76 | "vars": "all",
77 | "args": "none"
78 | }],
79 | "no-whitespace-before-property": 1,
80 | "semi": [1, "always"],
81 | "semi-spacing": [1, {
82 | "before": false,
83 | "after": true
84 | }],
85 | "space-before-blocks": [1, "always"],
86 | "space-before-function-paren": [0, {
87 | "anonymous": "always",
88 | "named": "never"
89 | }],
90 | "space-in-parens": [1, "never"],
91 | "space-infix-ops": 1,
92 | "space-unary-ops": [1, {
93 | "words": true,
94 | "nonwords": false
95 | }],
96 | "strict": [1, "safe"],
97 | "unicode-bom": [1, "never"],
98 | "use-isnan": 1,
99 | "valid-typeof": 1
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | dist
3 | node_modules
4 | coverage
5 | .nyc_output
6 | npm-debug.log
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | src
2 | .*
3 | coverage
4 | test
5 | rollup.config.js
6 | bs-config.json
7 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Thanks for wanting to contribute to react-style-hoc!
2 |
3 | Below you can find a list of things we need to decide on, to move forward with this library.
4 |
5 | Should you decide to open a PR, please make sure to run all tests (`npm test`) first, and include any relevant tests for your feature.
6 |
7 |
8 | # to do:
9 | - add more utility functions
10 | - how shall we handle auto prefixing?
11 | - can we extract a CSS file from the final inline style that was generated? Similar to a JSS approach?
12 | - what about performance? -> we need some benchmarking
13 | - How do we want to expose things like align-items for flexbox?
14 | - Do we want to provide default positioning utilities?
15 | - Should everything be a function, or do we allow more configuration as params..?
16 | - ....
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Installation:
2 |
3 | `yarn add react-style-hoc`
4 |
5 | or if you prefer `npm`:
6 |
7 | `npm install --save react-style-hoc`
8 |
9 |
10 | # [motivation] Why would I want to compose styles with functions...?!
11 |
12 | Great question!
13 |
14 | The purpose of this repository is to allow you to declare styles as functions, that are composable, and return a react Higher Order Component.
15 |
16 | The direct benefit of which is that it becomes possible to create reusable HOCs for styling.
17 |
18 | This is probably not very useful for 'looks-and-feel' styles (everything 'inside the component box') - but more for things that have to do with positioning. Separating those two concerns is a good idea in any case, and this provides you with a tool to do so.
19 |
20 |
21 | A simple example would be to define a flex centerer, which would look something like this:
22 |
23 | ```
24 | const MyComponent = ({style}) =>
unstyled
25 |
26 | const isFlexBox = createStyleHoc('display', 'flex');
27 | const centersChidren = createStyleHoc({alignItems: 'center', justifyContent: 'center'}, '');
28 |
29 | const isFlexCenterer = compose(
30 | isFlexBox,
31 | centersChildren
32 | )
33 | ```
34 |
35 | When using `isFlexCenterer` on it's own as a wrapper around `MyComponent`, like this:...
36 |
37 | ```
38 | export isFlexCenterer(MyComponent);
39 | ```
40 |
41 | the `style` prop of `MyComponent` then looks like this:
42 |
43 | ```
44 | {
45 | display: 'flex',
46 | alignItems: 'center',
47 | justifyContent: 'center',
48 | }
49 |
50 | ```
51 |
52 | Of course, the power of composing styles only becomes fully clear once you start composing different helpers.
53 |
54 | You can compare this to using `mixins` in a CSS preprocessor such as SASS or LESS.
55 |
56 | ```
57 | const isFullyStyled = compose(
58 | withMargin(20),
59 | withPadding(40),
60 | isFlexCenterer,
61 | withColor,
62 | ....
63 | )
64 | ```
65 |
66 |
67 | This is a very young library, and contributions very welcome!
68 |
69 | # Development:
70 |
71 | `yarn install`
72 |
73 | `npm run watch:js`
74 |
75 | run tests: `npm test`
76 |
77 | # Example:
78 | run [our example app](https://github.com/ambewas/react-style-hoc/tree/master/examples/test-app)
79 |
80 | # How to use:
81 |
82 | ```
83 | import React, { Component } from 'react';
84 | import { withPadding, withMargin, compose, createStyleHoc } from 'react-style-hoc';
85 | ```
86 |
87 | ## Compose styles with our default provided style HOCs
88 | ```
89 | const withPaddingAndMargin = compose(
90 | withPadding(20),
91 | withMargin(50),
92 | );
93 |
94 | ```
95 |
96 | ## ... or create your own style HOCS using `createStyleHoc`:
97 | Pass a string (style key). Everything here is curried, so if no default value is provided, you still have to call `withColor` with a value -> see usage in `withAllStyles`
98 | ```
99 | const withColor = createStyleHoc('color');
100 | ```
101 |
102 | ## Pass style key & value as arguments:
103 | ```
104 | const withSomeRandomStyleAsArguments = createStyleHoc('display', 'flex');
105 | ```
106 |
107 | ## Pass an object with default styles applied:
108 |
109 | Note: we need that second argument. We still need figure out how to get rid of that. Should we even expose this kind of API? Thoughts are welcome.
110 | ```
111 | const withSomeRandomStyleAsObject = createStyleHoc({
112 | background: 'purple',
113 | }, '');
114 | ```
115 |
116 | ## Compose all previously made styles into one!
117 | ```
118 | const withAllStyles = compose(
119 | withSomeRandomStyleAsArguments,
120 | withSomeRandomStyleAsObject,
121 | withPaddingAndMargin,
122 | withColor('#78a5ff'),
123 | )
124 | ```
125 |
126 |
127 | ## Use the hoc on your stateless components:
128 |
129 | Note: you can still provide some other default style as a prop, the HOC styles will be applied after -- so its possible to override everything. E.g. if we would have provided `{padding: '1px'}`, then `withPadding(20)` from before would not have any effect.
130 | ```
131 | const TestStateless = ({ children, style }) =>
14 | `;
15 |
16 | exports[`createStyleHoc when wrapping a component in a HOC created by createStyleHoc, it should add the correct style prop 1`] = `
17 |
24 | unstyled
25 |
26 | `;
27 |
--------------------------------------------------------------------------------
/src/createStyleHoc.js:
--------------------------------------------------------------------------------
1 | import React from 'react'; // eslint-disable-line
2 |
3 | import { curry } from 'ramda';
4 | import { generateStyleObject } from './helpers/index.js';
5 |
6 | export const createStyleHoc = curry((style, value, WrappedComponentOrStyle) => {
7 | // style -> e.g. { margin: 10, padding: 30 } || ['margin', 'padding'] || 'margin'
8 | // we should support default styles, an array of style values to be added, or one string.
9 |
10 | let newStyleObject = generateStyleObject(style, value);
11 |
12 | /**
13 | |--------------------------------------------------
14 | | how can we detect is a component has been passed through,
15 | | and otherwise simply generate a style object? -- useful for
16 | | composing without having to depend on react.
17 | |--------------------------------------------------
18 | */
19 |
20 | return ({ style, ...props }) => {
21 | return ;
22 | };
23 | });
24 |
--------------------------------------------------------------------------------
/src/createStyleHoc.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'; // eslint-disable-line
2 | import renderer from 'react-test-renderer';
3 | import { createStyleHoc } from './createStyleHoc.js';
4 |
5 | const TestComponent = ({ style }) =>