├── .editorconfig
├── .eslintrc
├── .github
├── ISSUE_TEMPLATE.md
└── stale.yml
├── .gitignore
├── .npmignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── examples
├── README.md
├── hmr
│ ├── app.js
│ └── component.js
├── jest-testing
│ ├── App.js
│ ├── __mocks__
│ │ └── react-native-extended-stylesheet.js
│ ├── __tests__
│ │ └── app-test.js
│ └── index.js
├── media-queries
│ ├── app.js
│ └── component.js
├── readme
│ ├── app.js
│ └── component.js
├── rem
│ ├── app.js
│ └── component.js
├── simple
│ ├── app.js
│ └── component.js
└── theming
│ ├── README.md
│ ├── app.js
│ ├── component.js
│ ├── dark.js
│ ├── demo.gif
│ └── light.js
├── package-lock.json
├── package.json
├── src
├── .babelrc
├── __mocks__
│ └── react-native.js
├── __tests__
│ ├── api.test.js
│ ├── child.test.js
│ ├── sheet.test.js
│ ├── style.test.js
│ ├── utils.test.js
│ └── value.test.js
├── api.js
├── child.js
├── index.js
├── replacers
│ ├── __tests__
│ │ ├── media-queries.test.js
│ │ ├── operation.test.js
│ │ ├── percent.test.js
│ │ ├── rem.test.js
│ │ ├── scale.test.js
│ │ └── vars.test.js
│ ├── media-queries.js
│ ├── operation.js
│ ├── percent.js
│ ├── rem.js
│ ├── scale.js
│ └── vars.js
├── sheet.js
├── style.js
├── utils.js
└── value.js
├── tsconfig.json
├── types
├── index.d.ts
└── test.ts
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | charset = utf-8
6 | insert_final_newline = true
7 |
8 | [*.{js,json}]
9 | indent_style = space
10 | indent_size = 2
11 | trim_trailing_whitespace = true
12 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "eslint:recommended",
3 | "parser": "babel-eslint",
4 |
5 | "env": {
6 | "es6": true,
7 | "node": true,
8 | "jest": true
9 | },
10 |
11 | "plugins": [
12 | "react",
13 | "react-native",
14 | "import"
15 | ],
16 |
17 | "rules": {
18 | "quotes": ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": true }],
19 | "comma-dangle": ["error", "only-multiline"],
20 | "no-multi-spaces": 2,
21 | "space-before-function-paren": [2, {"anonymous": "always", "named": "never"}],
22 | "react/display-name": 0,
23 | "react/forbid-prop-types": 1,
24 | "react/jsx-boolean-value": 1,
25 | "react/jsx-closing-bracket-location": 1,
26 | "react/jsx-curly-spacing": 1,
27 | "react/jsx-handler-names": 1,
28 | "react/jsx-indent-props": [1, 2],
29 | "react/jsx-indent": [0, 2],
30 | "react/jsx-key": 1,
31 | "react/jsx-max-props-per-line": 0,
32 | "react/jsx-no-bind": 0,
33 | "react/jsx-no-duplicate-props": 1,
34 | "react/jsx-no-literals": 0,
35 | "react/jsx-no-undef": 1,
36 | "react/jsx-pascal-case": 1,
37 | "jsx-quotes": 1,
38 | "react/sort-prop-types": 1,
39 | "react/jsx-sort-props": 0,
40 | "react/jsx-uses-react": 1,
41 | "react/jsx-uses-vars": 1,
42 | "react/no-danger": 1,
43 | "react/no-deprecated": 1,
44 | "react/no-did-mount-set-state": 1,
45 | "react/no-did-update-set-state": 1,
46 | "react/no-direct-mutation-state": 1,
47 | "react/no-is-mounted": 1,
48 | "react/no-multi-comp": 1,
49 | "react/no-set-state": 0,
50 | "react/no-string-refs": 1,
51 | "react/no-unknown-property": 1,
52 | "react/prefer-es6-class": 1,
53 | "react/prop-types": [0, {"ignore": ["children"]}],
54 | "react/react-in-jsx-scope": 1,
55 | "import/extensions": 1,
56 | "react/self-closing-comp": 1,
57 | "react/sort-comp": 0,
58 | "react/jsx-wrap-multilines": 1,
59 | "react-native/no-unused-styles": 2,
60 | "react-native/split-platform-components": 2
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #### Steps to Reproduce
5 |
10 |
11 | #### Try in Expo
12 |
17 |
18 | #### Expected Behavior
19 |
20 |
21 | #### Actual Behavior
22 |
23 |
24 | #### Show the code
25 |
26 |
27 | #### Environment
28 |
29 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Configuration for probot-stale - https://github.com/probot/stale
2 | # A .github/stale.yml file is required to enable the plugin.
3 | # The file can be empty, or it can override any of these default settings
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | .DS_Store
3 |
4 | # node.js
5 | node_modules/
6 | npm-debug.log
7 | .idea
8 |
9 | # coverage
10 | /coverage
11 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # .babelrc in root breaks react-native packager see issue #1
2 | .babelrc
3 | .editorconfig
4 | .eslintrc
5 | .jscsrc
6 | .idea
7 | .travis.yml
8 | .github
9 | /coverage
10 | /hooks
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - '8'
5 | - '10'
6 |
7 | script: npm run ci
8 |
9 | after_success:
10 | # send coverage for badge
11 | - npm run coveralls
12 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.12.0 (Jul 5, 2019)
2 |
3 | * Fix `.subscribe()` method and add `.unsubscribe()` ([#109])
4 |
5 | ## 0.11.1 (Dec 17, 2018)
6 |
7 | * Fix array props ([#103])
8 |
9 | ## 0.11.0 (Dec 11, 2018)
10 |
11 | * Inherit styles from `Object` instead of `null` ([#101])
12 | * Support nested style props ([#99])
13 |
14 | ## 0.10.0 (Sep 24, 2018)
15 |
16 | * Add Typescript declarations ([#56])
17 |
18 | ## 0.9.0 (Aug 10, 2018)
19 |
20 | * Add (text) direction support ([@achipa] in [#90])
21 |
22 | ## 0.8.1 (Jan 27, 2018)
23 |
24 | * Calc percents for variable inside operation ([#77])
25 |
26 | ## 0.8.0 (Nov 1, 2017)
27 |
28 | * Style as a function ([#62])
29 | * Support [hot module reload] ([#16])
30 |
31 | ## 0.7.0 (Oct 9, 2017)
32 |
33 | * Allow create-react-app compile without plugins ([@grantbi] in [#54])
34 | * Support dynamic theme change ([#47], [#53])
35 | * Support media queries for global variables ([#25])
36 |
37 | ## 0.6.0 (Jun 3, 2017)
38 |
39 | * Remove `EStyleSheet.memoize()` in favor of [lodash.memoize](https://www.npmjs.com/package/lodash.memoize) ([#45])
40 |
41 | ## 0.5.0 (Jun 5, 2017)
42 |
43 | * Add [babel-plugin-runtyper](https://github.com/vitalets/babel-plugin-runtyper)
44 | * Use native percents when possible ([#32])
45 | * Forward original StyleSheet properties ([#33])
46 |
47 | ## 0.4.0 (Apr 13, 2017)
48 |
49 | * Add division operator ([@joemckie] in [#38])
50 | * Improve Readme
51 |
52 | ## 0.3.2 (Mar 5, 2017)
53 |
54 | * Check for non-object when creating Style ([#14])
55 | * Update examples
56 |
57 | ## 0.3.0 (Jul 4, 2016)
58 |
59 | * Remove unneeded rounding ([@Kerumen] in [#18])
60 |
61 | ## 0.2.0 (Apr 25, 2016)
62 |
63 | * Support media-queries ([#8])
64 |
65 | ## 0.1.3 (Feb 4, 2016)
66 |
67 | * Public release
68 |
69 | [@joemckie]: https://github.com/joemckie
70 | [@Kerumen]: https://github.com/Kerumen
71 | [@grantbi]: https://github.com/grantbi
72 | [@achipa]: https://github.com/achipa
73 |
74 | [hot module reload]: https://facebook.github.io/react-native/blog/2016/03/24/introducing-hot-reloading.html
75 |
76 | [#8]: https://github.com/vitalets/react-native-extended-stylesheet/pull/8
77 | [#14]: https://github.com/vitalets/react-native-extended-stylesheet/pull/14
78 | [#16]: https://github.com/vitalets/react-native-extended-stylesheet/pull/16
79 | [#18]: https://github.com/vitalets/react-native-extended-stylesheet/pull/18
80 | [#25]: https://github.com/vitalets/react-native-extended-stylesheet/pull/25
81 | [#38]: https://github.com/vitalets/react-native-extended-stylesheet/pull/38
82 | [#32]: https://github.com/vitalets/react-native-extended-stylesheet/pull/32
83 | [#33]: https://github.com/vitalets/react-native-extended-stylesheet/pull/33
84 | [#45]: https://github.com/vitalets/react-native-extended-stylesheet/pull/45
85 | [#47]: https://github.com/vitalets/react-native-extended-stylesheet/pull/47
86 | [#53]: https://github.com/vitalets/react-native-extended-stylesheet/pull/53
87 | [#54]: https://github.com/vitalets/react-native-extended-stylesheet/pull/54
88 | [#62]: https://github.com/vitalets/react-native-extended-stylesheet/pull/62
89 | [#77]: https://github.com/vitalets/react-native-extended-stylesheet/pull/77
90 | [#90]: https://github.com/vitalets/react-native-extended-stylesheet/pull/90
91 | [#56]: https://github.com/vitalets/react-native-extended-stylesheet/pull/56
92 | [#99]: https://github.com/vitalets/react-native-extended-stylesheet/pull/99
93 | [#103]: https://github.com/vitalets/react-native-extended-stylesheet/pull/103
94 | [#109]: https://github.com/vitalets/react-native-extended-stylesheet/pull/109
95 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Vitaliy Potapov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Native Extended StyleSheet
2 |
3 | [](https://travis-ci.org/vitalets/react-native-extended-stylesheet)
4 | [](https://coveralls.io/github/vitalets/react-native-extended-stylesheet?branch=master)
5 | [](https://www.npmjs.com/package/react-native-extended-stylesheet)
6 | [](https://www.npmjs.com/package/react-native-extended-stylesheet)
7 |
8 | > ### :warning: Deprecation notice
9 | >
10 | > This library is **deprecated** and will no longer receive any updates or support.
11 | > Please consider migrating to alternative solutions. See [#154](https://github.com/vitalets/react-native-extended-stylesheet/issues/154) for details.
12 |
13 | Drop-in replacement of [React Native StyleSheet](https://facebook.github.io/react-native/docs/stylesheet.html) with media-queries, variables, dynamic themes,
14 | relative units, percents, math operations, scaling and other styling stuff.
15 |
16 |
17 |
18 | - [Demo](#demo)
19 | - [Installation](#installation)
20 | - [Usage](#usage)
21 | - [Features](#features)
22 | - [global variables](#global-variables)
23 | - [local variables](#local-variables)
24 | - [theming](#theming)
25 | - [media queries](#media-queries)
26 | - [math operations](#math-operations)
27 | - [rem units](#rem-units)
28 | - [percents](#percents)
29 | - [scaling](#scaling)
30 | - [underscored styles](#underscored-styles)
31 | - [pseudo classes (:nth-child)](#pseudo-classes-nth-child)
32 | - [value as a function](#value-as-a-function)
33 | - [caching](#caching)
34 | - [outline for debug](#outline-for-debug)
35 | - [hot module reload](#hot-module-reload)
36 | - [API](#api)
37 | - [.create()](#create)
38 | - [.build()](#build)
39 | - [.value()](#value)
40 | - [.child()](#child)
41 | - [.subscribe()](#subscribe)
42 | - [.unsubscribe()](#unsubscribe)
43 | - [Caveats](#caveats)
44 | - [FAQ](#faq)
45 | - [Changelog](#changelog)
46 | - [Feedback](#feedback)
47 | - [License](#license)
48 |
49 | ## Demo
50 | Use this [Expo snack](https://snack.expo.io/@vitalets/extended-stylesheet-simple) to play with Extended StyleSheets
51 | right in the browser or in [Expo app](https://expo.io/tools#client).
52 |
53 | ## Installation
54 | ```
55 | npm i react-native-extended-stylesheet --save
56 | ```
57 |
58 | ## Usage
59 | 1. Define styles using `EStyleSheet.create()` instead of `StyleSheet.create()`:
60 |
61 | ```js
62 | /* component.js */
63 | import EStyleSheet from 'react-native-extended-stylesheet';
64 |
65 | // define extended styles
66 | const styles = EStyleSheet.create({
67 | column: {
68 | width: '80%' // 80% of screen width
69 | },
70 | text: {
71 | color: '$textColor', // global variable $textColor
72 | fontSize: '1.5rem' // relative REM unit
73 | },
74 | '@media (min-width: 350) and (max-width: 500)': { // media queries
75 | text: {
76 | fontSize: '2rem',
77 | }
78 | }
79 | });
80 |
81 | // use styles as usual
82 | class MyComponent extends React.Component {
83 | render() {
84 | return (
85 |
86 | Hello
87 |
88 | );
89 | }
90 | }
91 | ```
92 |
93 | 2. In app entry point call `EStyleSheet.build()` to actually calculate styles:
94 |
95 | ```js
96 | /* app.js */
97 | import EStyleSheet from 'react-native-extended-stylesheet';
98 |
99 | EStyleSheet.build({ // always call EStyleSheet.build() even if you don't use global variables!
100 | $textColor: '#0275d8'
101 | });
102 | ```
103 |
104 | \[[top](#react-native-extended-stylesheet)\]
105 |
106 | ## Features
107 | ### Global variables
108 | Global variables are passed to `EStyleSheet.build()` and available in all stylesheets.
109 | ```js
110 | // app entry: set global variables and calc styles
111 | EStyleSheet.build({
112 | $textColor: '#0275d8'
113 | });
114 |
115 | // component: use global variables
116 | const styles = EStyleSheet.create({
117 | text: {
118 | color: '$textColor'
119 | }
120 | });
121 |
122 | // global variable as inline style or as props to components
123 |
126 | ...
127 |
128 | ```
129 | \[[top](#react-native-extended-stylesheet)\]
130 |
131 | ### Local variables
132 | Local variables can be defined directly in sylesheet and have priority over global variables.
133 | To define local variable just start it with `$`:
134 | ```js
135 | const styles = EStyleSheet.create({
136 | $textColor: '#0275d8',
137 | text: {
138 | color: '$textColor'
139 | },
140 | icon: {
141 | color: '$textColor'
142 | },
143 | });
144 | ```
145 | Local variables are also available in result style: `styles.$textColor`.
146 | \[[top](#react-native-extended-stylesheet)\]
147 |
148 | ### Theming
149 | Changing app theme contains two steps:
150 | 1. re-build app styles
151 | 2. re-render components tree with new styles
152 |
153 | To re-build app styles you can call `EStyleSheet.build()` with new set of global variables:
154 | ```js
155 | EStyleSheet.build({
156 | $theme: 'light', // required variable for caching!
157 | $bgColor: 'white',
158 | });
159 | ```
160 | > Please note that special variable **`$theme` is required** for proper caching of calculated styles.
161 |
162 | Re-rendering whole component tree is currently a bit tricky in React.
163 | One option is to wrap app into component and re-mount it on theme change:
164 | ```js
165 | toggleTheme() {
166 | const theme = EStyleSheet.value('$theme') === 'light' ? darkTheme : lightTheme;
167 | EStyleSheet.build(theme);
168 | this.setState({render: false}, () => this.setState({render: true}));
169 | }
170 | render() {
171 | return this.state.render ? : null;
172 | }
173 | ```
174 | The caveat is that all components loss their state.
175 | In the future it may be possible with `forceDeepUpdate()` method (see [facebook/react#7759](https://github.com/facebook/react/issues/7759)).
176 | The approach is open for discusison, feel free to share your ideas in [#22](https://github.com/vitalets/react-native-extended-stylesheet/issues/22),
177 | [#47](https://github.com/vitalets/react-native-extended-stylesheet/issues/47).
178 |
179 | You can check out full theming code in [examples/theming](examples/theming) or in [Expo snack](https://snack.expo.io/@vitalets/dynamic-themes-with-extended-stylesheets).
180 | \[[top](#react-native-extended-stylesheet)\]
181 |
182 | ### Media queries
183 | Media queries allows to have different styles for different screens, platform, direction and orientation.
184 | They are supported as properties with `@media` prefix (thanks for idea to [@grabbou](https://github.com/grabbou),
185 | [#5](https://github.com/vitalets/react-native-extended-stylesheet/issues/5)).
186 |
187 | Media queries can operate with the following values:
188 |
189 | * media type: `ios|android`
190 | * `width`, `min-width`, `max-width`
191 | * `height`, `min-height`, `max-height`
192 | * `orientation` (`landscape|portrait`)
193 | * `aspect-ratio`
194 | * `direction` (`ltr|rtl`)
195 |
196 | You can use media queries on:
197 | * global level
198 | * sheet level
199 | * style level
200 |
201 | Examples:
202 | ```js
203 | // global level
204 | EStyleSheet.build({
205 | '@media ios': {
206 | $fontSize: 12,
207 | },
208 | '@media android': {
209 | $fontSize: 16,
210 | },
211 | });
212 |
213 | // sheet level
214 | const styles = EStyleSheet.create({
215 | column: {
216 | width: '80%',
217 | },
218 | '@media (min-width: 350) and (max-width: 500)': {
219 | column: {
220 | width: '90%',
221 | }
222 | }
223 | });
224 |
225 | // style level
226 | const styles = EStyleSheet.create({
227 | header: {
228 | '@media ios': {
229 | color: 'green',
230 | },
231 | '@media android': {
232 | color: 'blue',
233 | },
234 | }
235 | });
236 | ```
237 | You can check out full example code in [examples/media-queries](examples/media-queries) or in [Expo snack](https://snack.expo.io/@gbhasha/media-queries-using-extended-stylesheets).
238 | \[[top](#react-native-extended-stylesheet)\]
239 |
240 | ### Math operations
241 | Any value can contain **one** of following math operations: `*`, `/`, `+`, `-`. Operands can be numbers, variables and percents.
242 | For example, to render circle you may create style:
243 | ```js
244 | const styles = EStyleSheet.create({
245 | $size: 20,
246 | circle: {
247 | width: '$size',
248 | height: '$size',
249 | borderRadius: '0.5 * $size'
250 | }
251 | });
252 | ```
253 | \[[top](#react-native-extended-stylesheet)\]
254 |
255 | ### REM units
256 | Similar to [CSS3 rem unit](http://snook.ca/archives/html_and_css/font-size-with-rem) it allows to define any integer value as relative to the root element. In our case root value is special `rem` global variable that can be set in `EStyleSheet.build()`. It makes easy to scale app depending on screen size and other conditions. Default rem is `16`.
257 | ```js
258 | // component
259 | const styles = EStyleSheet.create({
260 | text: {
261 | fontSize: '1.5rem',
262 | marginHorizontal: '2rem'
263 | }
264 | });
265 | // app entry
266 | let {height, width} = Dimensions.get('window');
267 | EStyleSheet.build({
268 | $rem: width > 340 ? 18 : 16
269 | });
270 | ```
271 | You can check out full example code in [examples/rem](examples/rem) or in [Expo snack](https://snack.expo.io/@gbhasha/using-rem-units-with-extended-stylesheet).
272 | \[[top](#react-native-extended-stylesheet)\]
273 |
274 | ### Percents
275 | Percent values are supported natively since React Native 0.43.
276 | EStyleSheet passes them through to original StyleSheet except cases, when you use calculations with percents,
277 | e.g. `"100% - 20"`. Percents are calculated relative to **screen width/height** on application launch.
278 | ```js
279 | const styles = EStyleSheet.create({
280 | column: {
281 | width: '100% - 20'
282 | }
283 | });
284 | ```
285 |
286 | **Percents in nested components**
287 | If you need sub-component with percent operations relative to parent component - you can achieve that with variables.
288 | For example, to render 2 sub-columns with 30%/70% width of parent column:
289 | ```js
290 | render() {
291 | return (
292 |
293 |
294 |
295 |
296 | );
297 | }
298 |
299 | ...
300 |
301 | const styles = EStyleSheet.create({
302 | $columnWidth: '80%',
303 | column: {
304 | width: '$columnWidth',
305 | flexDirection: 'row'
306 | },
307 | subColumnLeft: {
308 | width: '0.3 * $columnWidth'
309 | },
310 | subColumnRight: {
311 | width: '0.7 * $columnWidth'
312 | }
313 | });
314 | ```
315 | \[[top](#react-native-extended-stylesheet)\]
316 |
317 | ### Scaling
318 | You can apply scale to components by setting special `$scale` variable.
319 | ```js
320 | const styles = EStyleSheet.create({
321 | $scale: 1.5,
322 | button: {
323 | width: 100,
324 | height: 20,
325 | marginLeft: 10
326 | }
327 | });
328 | ```
329 | This helps to create reusable components that could be scaled depending on prop:
330 | ```js
331 | class Button extends React.Component {
332 | static propTypes = {
333 | scale: React.PropTypes.number
334 | };
335 | render() {
336 | let style = getStyle(this.props.scale)
337 | return (
338 |
339 |
340 | );
341 | }
342 | }
343 |
344 | let getStyle = function (scale = 1) {
345 | return EStyleSheet.create({
346 | $scale: scale,
347 | button: {
348 | width: 100,
349 | height: 20,
350 | marginLeft: 10
351 | }
352 | });
353 | }
354 | ```
355 | To cache calculated styles please have a look on [caching](#caching) section.
356 | \[[top](#react-native-extended-stylesheet)\]
357 |
358 | ### Underscored styles
359 | Original react-native stylesheets are calculated to integer numbers and original values are unavailable.
360 | But sometimes they are needed. Let's take an example:
361 | You want to render text and icon with the same size and color.
362 | You can take this [awesome icon library](https://github.com/oblador/react-native-vector-icons)
363 | and see that `` component has `size` and `color` props.
364 | It would be convenient to define style for text and keep icon's size/color in sync.
365 | ```js
366 | const styles = EStyleSheet.create({
367 | text: {
368 | fontSize: '1rem',
369 | color: 'gray'
370 | }
371 | });
372 | ```
373 | In runtime `styles` created with original react's `StyleSheet` will look like:
374 | ```js
375 | styles = {
376 | text: 0
377 | }
378 | ```
379 | But extended stylesheet saves calculated values under `_text` property:
380 | ```js
381 | styles = {
382 | text: 0,
383 | _text: {
384 | fontSize: 16,
385 | color: 'gray'
386 | }
387 | }
388 | ```
389 | To render icon we just take styles from `_text`:
390 | ```js
391 | return (
392 |
393 |
394 | Hello
395 |
396 | );
397 | ```
398 | \[[top](#react-native-extended-stylesheet)\]
399 |
400 | ### Pseudo classes (:nth-child)
401 | Extended stylesheet supports 4 pseudo classes: `:first-child`, `:nth-child-even`, `:nth-child-odd`, `:last-child`. As well as in traditional CSS it allows to apply special styling for first/last items or render stripped rows.
402 | To get style for appropriate index you should use `EStyleSheet.child()` method.
403 | It's signature: `EStyleSheet.child(stylesObj, styleName, index, count)`.
404 | ```js
405 | const styles = EStyleSheet.create({
406 | row: {
407 | fontSize: '1.5rem',
408 | borderTopWidth: 1
409 | },
410 | 'row:nth-child-even': {
411 | backgroundColor: 'gray' // make stripped
412 | },
413 | 'row:last-child': {
414 | borderBottomWidth: 1 // render bottom edge for last row
415 | }
416 | });
417 | ...
418 | render() {
419 | return (
420 |
421 | {items.map((item, index) => {
422 | return (
423 |
424 | );
425 | })}
426 |
427 | );
428 | }
429 | ```
430 | \[[top](#react-native-extended-stylesheet)\]
431 |
432 | ### Value as a function
433 | For the deepest customization you can specify any value as a function that will be executed on EStyleSheet build.
434 | For example, you may *darken* or *lighten* color of variable via [npm color package](https://www.npmjs.com/package/color):
435 | ```js
436 | import Color from 'color';
437 | import EStyleSheet from 'react-native-extended-stylesheet';
438 |
439 | const styles = EStyleSheet.create({
440 | button: {
441 | backgroundColor: () => Color('green').darken(0.1).hexString() // <-- value as a function
442 | }
443 | });
444 |
445 | render() {
446 | return (
447 |
448 | ...
449 |
450 | );
451 | }
452 | ```
453 |
454 | The common pattern is to use [EStyleSheet.value()](#value) inside the function to get access to global variables:
455 | ```js
456 |
457 | EStyleSheet.build({
458 | $prmaryColor: 'green'
459 | });
460 |
461 | const styles = EStyleSheet.create({
462 | button: {
463 | backgroundColor: () => Color(EStyleSheet.value('$prmaryColor')).darken(0.1).hexString()
464 | }
465 | });
466 | ```
467 |
468 | \[[top](#react-native-extended-stylesheet)\]
469 |
470 | ### Caching
471 | If you use dynamic styles depending on runtime prop or you are making reusable component with dynamic styling
472 | you may need stylesheet creation in every `render()` call. Let's take example from [scaling](#scaling) section:
473 | ```js
474 | class Button extends React.Component {
475 | static propTypes = {
476 | scale: React.PropTypes.number
477 | };
478 | render() {
479 | let style = getStyle(this.props.scale)
480 | return (
481 |
482 |
483 | );
484 | }
485 | }
486 |
487 | let getStyle = function (scale = 1) {
488 | return EStyleSheet.create({
489 | $scale: scale,
490 | button: {
491 | width: 100,
492 | height: 20,
493 | marginLeft: 10
494 | }
495 | });
496 | }
497 | ```
498 | To avoid creating styles on every render you can use [lodash.memoize](https://www.npmjs.com/package/lodash.memoize):
499 | store result for particular parameters and returns it from cache when called with the same parameters.
500 | Updated example:
501 | ```js
502 | import memoize from 'lodash.memoize';
503 |
504 | let getStyle = memoize(function (scale = 1) {
505 | return EStyleSheet.create({
506 | $scale: scale,
507 | button: {
508 | width: 100,
509 | height: 20,
510 | marginLeft: 10
511 | }
512 | });
513 | });
514 | ```
515 | Now if you call `getStyle(1.5)` 3 times actually style will be created on the first call
516 | and two other calls will get it from cache.
517 | \[[top](#react-native-extended-stylesheet)\]
518 |
519 | ### Outline for debug
520 | It is possible to outline all components that are using EStyleSheet. For that set global `$outline` variable:
521 | ```js
522 | EStyleSheet.build({$outline: 1});
523 | ```
524 | > Note that components without styles will not be outlined,
525 | because RN [does not support](https://github.com/facebook/react-native/issues/1768) default component styling yet.
526 |
527 | To outline particular component set local `$outline` variable:
528 | ```js
529 | const styles = EStyleSheet.create({
530 | $outline: 1,
531 | column: {
532 | width: '80%',
533 | flexDirection: 'row'
534 | },
535 | ...
536 | });
537 | ```
538 | \[[top](#react-native-extended-stylesheet)\]
539 |
540 | ### Hot module reload
541 | [Hot module reload (HMR)](https://facebook.github.io/react-native/blog/2016/03/24/introducing-hot-reloading.html)
542 | allows you to change code and see live updates without loosing app state. It is very handy for tuning styles.
543 | EStyleSheet supports HMR with the following options:
544 |
545 | 1. When you change style of component - the component is updated by HMR automatically without any effort from your side.
546 | 2. When you change global variable or theme - you should use [HMR API](https://facebook.github.io/react-native/releases/next/#hmr-api)
547 | to force style re-calculation:
548 | ```js
549 | // app.js
550 | EStyleSheet.build({
551 | $fontColor: 'black'
552 | });
553 |
554 | ...
555 |
556 | module.hot.accept(() => {
557 | EStyleSheet.clearCache();
558 | EStyleSheet.build(); // force style re-calculation
559 | });
560 | ```
561 | See full example of HMR [here](examples/hmr).
562 | \[[top](#react-native-extended-stylesheet)\]
563 |
564 | ## EStyleSheet API
565 | ### .create()
566 | ```js
567 | /**
568 | * Creates extended stylesheet object
569 | *
570 | * @param {Object} source style
571 | * @returns {Object} extended stylesheet object
572 | */
573 | create (source) {...}
574 | ```
575 | \[[top](#react-native-extended-stylesheet)\]
576 |
577 | ### .build()
578 | ```js
579 | /**
580 | * Calculates all stylesheets
581 | *
582 | * @param {Object} [globalVars] global variables for all stylesheets
583 | */
584 | build (globalVars) {...}
585 | ```
586 | \[[top](#react-native-extended-stylesheet)\]
587 |
588 | ### .value()
589 | ```js
590 | /**
591 | * Calculates particular expression.
592 | *
593 | * @param {*} value
594 | * @param {String} [prop] property for which value is calculated. For example, to calculate percent values
595 | * the function should know is it 'width' or 'height' to use proper reference value.
596 | * @returns {*} calculated result
597 | */
598 | value (value, prop) {...}
599 | ```
600 | **Please note** that in most cases `EStyleSheet.value()` should be used inside function, not directly:
601 | ```js
602 | const styles = EStyleSheet.create({
603 | button1: {
604 | width: () => EStyleSheet.value('$contentWidth') + 10 // <-- Correct!
605 | },
606 | button2: {
607 | width: EStyleSheet.value('$contentWidth') + 10 // <-- Incorrect. Because EStyleSheet.build() may occur later and $contentWidth will be undefined at this moment.
608 | }
609 | });
610 | ```
611 | \[[top](#react-native-extended-stylesheet)\]
612 |
613 | ### .child()
614 | ```js
615 | /**
616 | * Returns styles with pseudo classes :first-child, :nth-child-even, :last-child according to index and count
617 | *
618 | * @param {Object} stylesheet
619 | * @param {String} styleName
620 | * @param {Number} index index of item for style
621 | * @param {Number} count total count of items
622 | * @returns {Object|Array} styles
623 | */
624 | child (styles, styleName, index, count) {...}
625 | ```
626 | \[[top](#react-native-extended-stylesheet)\]
627 |
628 | ### .subscribe()
629 | ```js
630 | /**
631 | * Subscribe to event. Currently only 'build' event is supported.
632 | *
633 | * @param {String} event
634 | * @param {Function} listener
635 | */
636 | subscribe (event, listener) {...}
637 |
638 | ```
639 | This method is useful when you want to pre-render some component on init.
640 | As extended style is calculated after call of `EStyleSheet.build()`,
641 | it is not available instantly after creation so you should wrap pre-render
642 | info listener to `build` event:
643 | ```js
644 | const styles = EStyleSheet.create({
645 | button: {
646 | width: '80%',
647 | }
648 | });
649 |
650 | // this will NOT work as styles.button is not calculated yet
651 | let Button = ;
652 |
653 | // but this will work
654 | let Button;
655 | EStyleSheet.subscribe('build', () => {
656 | Button = ;
657 | });
658 | ```
659 | \[[top](#react-native-extended-stylesheet)\]
660 |
661 | ### .unsubscribe()
662 | ```js
663 | /**
664 | * Unsubscribe from event. Currently only 'build' event is supported.
665 | *
666 | * @param {String} event
667 | * @param {Function} listener
668 | */
669 | unsubscribe (event, listener) {...}
670 |
671 | ```
672 | Unsubscribe from event.
673 | \[[top](#react-native-extended-stylesheet)\]
674 |
675 | ## Caveats
676 | 1. **Dynamic theme change is possible only with loosing components local state**
677 | When theme styles are re-calculated - all components should be re-rendered.
678 | Currently it can be done via re-mounting components tree, please see [#47].
679 | > Note: it is not issue if you are using state container like [Redux](https://github.com/reactjs/redux)
680 | and can easily re-render app in the same state
681 |
682 | 2. **Dynamic orientation change is not supported**
683 | Please see [#9] for more details.
684 |
685 | 3. **Old RN versions (< 0.43) can crash the app with percent values**
686 | RN >= 0.43 supports percent values natively ([#32]) and EStyleSheet since 0.5.0 just proxy percent values to RN as is ([#77]) to keep things simple.
687 | Older RN versions (< 0.43) can't process percents and EStyleSheet process such values.
688 | So if you are using RN < 0.43, you should stick to EStyleSheet@0.4.0.
689 |
690 | ## FAQ
691 | 1. **I'm getting error: `"Unresolved variable: ..."`**
692 | - Ensure that you call `EStyleSheet.build()` in entry point of your app.
693 | - Ensure that `$variable` name without typos.
694 | - Ensure that you are not using `EStyleSheet.value()` before the styles are built. See [#50](https://github.com/vitalets/react-native-extended-stylesheet/issues/50) for details.
695 |
696 | ## Changelog
697 | Please see [CHANGELOG.md](CHANGELOG.md)
698 |
699 | ## Feedback
700 | If you have any ideas or something goes wrong feel free to
701 | [open new issue](https://github.com/vitalets/react-native-extended-stylesheet/issues/new).
702 |
703 | ## License
704 | [MIT](LICENSE.md) @ [Vitaliy Potapov](https://github.com/vitalets)
705 |
706 | \[[top](#react-native-extended-stylesheet)\]
707 |
708 |
709 | * * *
710 | If you love :heart: JavaScript and would like to track new trending repositories,
711 | have a look on vitalets/github-trending-repos.
712 |