react-native-stylizer

2.0.1 • Public • Published

react-native-stylizer

Stateful styling for React Native components.

Installation

NPM: npm install --save react-native-stylizer

Yarn: yarn add react-native-stylizer

Usage

The following example of a Button component will be explained:

import React from "react";
import PropTypes from "prop-types";
import { TouchableOpacity, Text } from "react-native";
import Stylizer from "react-native-stylizer";
 
export default class Button extends React.Component {
    static propTypes = {
        label: PropTypes.string.isRequired,
        appearance: PropTypes.string,
        backgroundColor: PropTypes.string,
        borderColor: PropTypes.string,
        labelColor: PropTypes.string,
        filled: PropTypes.bool.isRequired,
        border: PropTypes.bool.isRequired,
        onPress: PropTypes.func,
    }
    static defaultProps = {
        filled: true,
        border: true,
    }
 
    stylesRef = stylizers.Button
 
    render() {
        const { props, stylesRef } = this;
        const { label, appearance, backgroundColor, borderColor, labelColor, filled, onPress } = props;
        const styles = stylesRef.parseWithState({
            [appearance]: !!appearance,
            "filled": filled,
            "border": !filled && border,
            "customBackgroundColor": !!backgroundColor,
            "customBorderColor": !!borderColor,
            "customLabelColor": !!labelColor,
        }, props);
 
        return (
            <TouchableOpacity style={ styles.container } activeOpacity={ 0.75 } onPress={ onPress }>
                <Text style={ styles.label }>{ label }</Text>
            </TouchableOpacity>
        );
    }
}
 
// Styling
const colors = {
    default: "#cccccc",
    primary: "#808080",
};
 
const stylizers = {
    "Button": Stylizer.create({
        "container": {
            width: "100%",
            height: 44,
            paddingHorizontal: 20,
            justifyContent: "center",
            alignItems: "center",
            borderRadius: 6,
            
            "@states": [
                { "filled": {
                    backgroundColor: colors.default,
 
                    // Nested states
                    "@states": [
                        { "primary": { backgroundColor: colors.primary }},
                    ],
                }},
 
                { "border": {
                    borderWidth: 1,
                    borderColor: colors.default,
                    
                    // Nested states
                    "@states": [
                        { "primary": { borderColor: colors.primary }},
                    ],
                }},
 
                { "customBackgroundColor": ({ backgroundColor }) => ({
                    backgroundColor,
                })},
 
                { "customBorderColor": ({ borderColor }) => ({
                    borderColor,
                })},
            ],
        },
 
        "label": {
            color: colors.default,
            fontSize: 15,
            fontWeight: "800",
            textAlign: "center",
 
            "@states": [
                { "primary": { color: colors.primary }},
                { "filled": { color: "#ffffff" }},
 
                { "customLabelColor": ({ labelColor }) => ({
                    color: labelColor,
                })},
            ],
        },
    }),
};

We will create three different buttons. A regular, a primary and a custom:

<Button
    label="Regular button"
    onPress={ () => alert("Pressed regular button!") }
/>
 
<Button
    label="Primary button"
    appearance="primary"
    onPress={ () => alert("Pressed primary button!") }
/>
 
<Button
    label="Custom button"
    backgroundColor="#0000ff"
    borderColor="#ff0000"
    labelColor="#ffffff"
    filled={ false }
    onPress={ () => alert("Pressed custom button!") }
/>

Stylizer.parseWithState

parseWithState is a method that allows us to pass in a state of conditions. Each property in our "state" object (not to be confused with a component's state object) that returns a value of true will be passed down, while false properties are ignored. Our primary button is essentially passing this state:

{
    "primary": true,
    "filled": true,
    "border": false,
    "customBackgroundColor": false,
    "customBorderColor": false,
    "customLabelColor": false
}

Defining a Stylizer instance

When creating a Stylizer instance, just like a normal React Native StyleSheet, we first define our base styles. In this case; container and label. Both base styles have access to the "true" conditions ("primary" and "filled" for our regular button). In a Stylizer, we define a "@states" or "state" array. This is where we put our state rules as single key objects ("filled" being one of them).

{
    "container": {
        ...
 
        "@states" [ // Can also be "state"
            { "filled": { ... }},
        ],
        ...
    }
    ...
}

In CSS, this would translate to: .container.filled

Just like in SASS, we can nest our states to define more strict ruling.

{
    "container": {
        ...
 
        "@states": [
            { "filled": {
                ...
 
                // Nested states
                "@states": [
                    { "@primary": { ... }},
                ],
            }},
        ],
        ...
    }
    ...
}

In CSS, this would translate to: .container.filled.primary

We also have the ability to create "dynamic" state rules to our Stylizer:

{
    "container": {
        ...
 
        "@states": [
            ...
            { "customBackgroundColor": ({ backgroundColor }) => ({
                backgroundColor,
            })},
        ],
    }
    ...
}

A dynamic state is when a state property is a function instead of an object. In our parseWithState call, we passed down our props as a second argument. This argument will be accessable when defining dynamic states (as seen above).

Why do we need dynamic states?

In short, all properties that are defined as regular objects will be merged into a React Native StyleSheet instance directly when constructing a new Stylizer instance. Dynamic states, however, become objects of their own. parseWithState will return object(s) that may look like this:

{
    container: [24, { backgroundColor: "#0000ff" }, 83],
    label: [52]
}

Any number in each array is a static state that refers to a StyleSheet style, while objects are dynamic states that were created upon parseWithState call (usually in a component's render method).

Parse only a certain style

With the method Stylizer.parseWithState, we apply a state to all styles within the Stylizer instance ("container" and "label"). We can also apply a state to only a certain style, by first calling Stylizer.ref:

render() {
    const { props, stylesRef } = this;
    const { label, onPress } = props;
    const containerStyle = stylesRef.ref("container").parseWithState({
        "stateNameExclusiveToContainer": true,
    }, props);
    const labelStyle = stylesRef.ref("label").parseWithState({
        "stateNameExclusiveToLabel": true,
    });
 
    return (
        <TouchableOpacity style={ containerStyle } activeOpacity={ 0.75 } onPress={ onPress }>
            <Text style={ labelStyle }>{ label }</Text>
        </TouchableOpacity>
    );
}

Methods

Stylizer

Method Description
ref(styleName: string) : StylizerStyle Gets a reference to a style.
parse(...args: any[]) : object Returns an array of styles without any state conditions.
parseWithState(state: object, ...args: any[]) : object Returns an array of styles by state conditions.

StylizerStyle

Method Description
parse(...args: any[]) : object Returns an array of styles without any state conditions.
parseWithState(state: object, ...args: any[]) : object Returns an array of styles by state conditions.

Known issues

There are currently no known issues.

History

Version 2.0.1 (July 9, 2018)

  • Removed react-native as dependency as it's not needed and gave several warnings.

Version 2.0.0 (July 9, 2018)

  • Revamped code base due to inconsistent behavior on different devices. Please re-read the "Usage" section if you are having trouble with the new update.

Version 1.0.0 (July 5, 2018)

  • First release!

Credits

License

MIT

Package Sidebar

Install

npm i react-native-stylizer

Weekly Downloads

0

Version

2.0.1

License

MIT

Unpacked Size

16.6 kB

Total Files

5

Last publish

Collaborators

  • nordlingart