<p align="center">
  <img alt="react-theme-provider" src="./assets/theme-provider-logo.png" width="496">
</p>

---

[![Build Status][build-badge]][build]
[![Version][version-badge]][package]
[![MIT License][license-badge]][license]

## About 
`@callstack/react-theme-provider` is a set of utilities that help you create your own theming system in few easy steps.
You can use it to customize colors, fonts, etc.

## Features
 - works in **React** and **React Native**
 - `ThemeProvider` - component
 - `withTheme` - Higher Order Component
 - `createTheming(defaultTheme)` - factory returns `ThemeProvider` component and `withTheme` HOC with default theme injected.

## Examples
 - buildin example for web react - ['/examples/web'](/examples/web)
 - [![Edit v6o562k6l7](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/v6o562k6l7)

## Getting started
### Instalation
```
npm install --save @callstack/react-theme-provider
```
or using yarn
```
yarn add @callstack/react-theme-provider
```

### Usage
To use, simply wrap your code into `ThemeProvider` component and pass your theme as a `theme` prop.

```js
<ThemeProvider theme={{ primaryColor: 'red', background: 'gray'}}>
  <App />
</ThemeProvider>
```

You could access theme data inside every component by wraping it into `withTheme` HOC. Just like this:

```js
class App extends React.Component {
  render() {
    return (
      <div style={{ color: props.theme.primaryColor }}>
        Hello
      </div>
    );
  }
}

export default withTheme(App);
```

## `ThemeProvider`
**type:** 
```js
type ThemeProviderType<T> = React.ComponentType<{
  children?: any,
  theme: T,
}>
```

Component you have to use to provide the theme to any component wrapped in `withTheme` HOC.

### props
 -`theme` - your theme object

## `withTheme`
**type:** 
```js
type WithThemeType<T, S> = <C: React.ComponentType<*>>(
  Comp: C
) => C &
  React.ComponentType<
    $Diff<React.ElementConfig<C>, { theme: T }> & { theme?: S }
  >;
```

Classic Higher Order Component which takes your component as an argument and injects `theme` prop into it.

### Example of usage
```js
const App = ({ theme }) => (
  <div style={{ color: theme.primaryColor }}>
    Hello
  </div>
);

export withTheme(App);
```

### Injected props
It will inject the following props to the component:
 - `theme` - our theme object.
 - `getWrappedInstance` -  exposed by some HOCs like react-redux's `connect`.
 Use it to get the ref of the underlying element.

### Injecting theme by a direct prop
You can also override `theme` provided by `ThemeProvider` by setting `theme` prop on the component wrapped in `withTheme` HOC.

Just like this:
```js
const Button = withTheme(({ theme }) => (
  <div style={{ color: theme.primaryColor }}>
    Click me
  </div>
));

const App = () => (
  <ThemeProvider theme={{ primaryColor: 'red' }}>
    <Button theme= {{ primaryColor: 'green' }}/>
  </ThemeProvider>
)
```
In this example Button will have green text.

## `createTheming`
**type:** 
```js
<T, S>(defaultTheme: T) => {
  ThemeProvider: ThemeProviderType<T>,
  withTheme: WithThemeType<T, S>,
}
```

This is more advanced replacement to classic importing `ThemeProvider` and `withTheme` directly from the library.
Thanks to it you can create your own ThemeProvider with any default theme.

Returns instance of `ThemeProvider` component and `withTheme` HOC. 
You can use this factory to create a singleton with your instances of `ThemeProvider` and `withTheme`.

>**Note:** `ThemeProvider` and `withTheme` generated by `createTheming` always will use different context so make sure you are using matching `withTheme`!   
If you acidentially import `withTheme` from `@callstack/react-theme-provider` instead of your theming instance it won't work.

### Arguments
 - `defaultTheme` - default theme object

### Benefits
 - Possibility to define `flow` types for your theme
 - Possibility to pass default theme
 - You can use multiple `ThemeProvider`s in your app without any conflicts.

### Example of usage
```js
// theming.js
import { createTheming } from '@callstack/react-theme-provider';
const { ThemeProvider, withTheme } = createTheming({
  primaryColor: 'red',
  secondaryColor: 'green',
});
export { ThemeProvider, withTheme };

//App.js
import { ThemeProvider, withTheme } from './theming';
```

## Applying a custom theme to a component
If you want to change the theme for a certain component, you can directly pass the theme prop to the component. The theme passed as the prop is merged with the theme from the Provider.

```js
import * as React from 'react';
import MyButton from './MyButton';

export default function ButtonExample() {
  return (
    <MyButton theme={{ roundness: 3 }}>
      Press me
    </MyButton>
  );
}
```

## Gotchas
The `ThemeProvider` exposes the theme to the components via [React's context API](https://reactjs.org/docs/context.html), 
which means that the component must be in the same tree as the `ThemeProvider`. Some React Native components will render a 
different tree such as a `Modal`, in which case the components inside the `Modal` won't be able to access the theme. The work 
around is to get the theme using the `withTheme` HOC and pass it down to the components as props, or expose it again with the 
exported `ThemeProvider` component.


[build-badge]: https://img.shields.io/circleci/project/github/callstack/react-theme-provider/master.svg?style=flat-square
[build]: https://circleci.com/gh/callstack/react-theme-provider
[version-badge]: https://img.shields.io/npm/v/@callstack/react-theme-provider.svg?style=flat-square
[package]: https://www.npmjs.com/package/@callstack/react-theme-provider
[license-badge]: https://img.shields.io/npm/l/react-theme-provider.svg?style=flat-square
[license]: https://opensource.org/licenses/MIT
[chat-badge]: https://img.shields.io/badge/chat-slack-brightgreen.svg?style=flat-square&colorB=E01563
[chat]: https://slack.callstack.com/