Skip to content

Latest commit

 

History

History
263 lines (192 loc) · 11.3 KB

README.md

File metadata and controls

263 lines (192 loc) · 11.3 KB

Layit - Layout library for react-native

npm version Build Status codecov

This library is inspired by styled-components and react-native-row, so thank you to the contributors and maintainers of those libraries.

Layit is a layout library for react native to help position components cleanly and in an expressive way.

Layit offers many of the powerful layout properties which react-native-row offers while additionally allowing the flexibility of applying these properties onto your predefined components.

Layout should be expressive and clear at a glance, it doesn't help that most of the time layout is mixed with other styles like colour and animations making discovery for them non-obvious.

Following the philosophy of react-native-row, this library helps keep unnecessary stylesheet declarations that are involved with layout and position.

Installation

npm install react-native-layit

or

yarn add react-native-layit

Basic Usage

import React from 'react';
import { View, Text } from 'react-native';
import Layit from 'react-native-layit';

const Box = (props) => (
  <Layit width="50" height="50" {...props} />
);

export default () => (
  <Layit>
    <Box style={{ backgroundColor: 'red' }} />
    <Box style={{ backgroundColor: 'blue' }} />
    <Box style={{ backgroundColor: 'green' }} />
  </Layit>
);

But wait... couldn't I get the same by importing View from react-native? I don't see anything different.

Why yes you can! The Layit component acts as wrapper for View and tries to be as un-intrusive as possible. There is also a Higher-order component available for wrapping other component types.

Now, let's get to what this library is for.

Say we wanted things to align horizontally instead of vertically, we can do this without Layit.

const Box = ({ style, ...props }) => (
  <View style={[{ width: 50, height: 50 }, style]} {...props}>
);

export default () => (
  <View style={{ flexDirection: 'row', marginTop: 20, marginLeft: 10, marginBottom: 5, marginRight: 20 }}>
    <Box style={{ backgroundColor: 'red' }} />
    <Box style={{ backgroundColor: 'blue' }} />
    <Box style={{ backgroundColor: 'green' }} />
  </View>
);

or with Layit.

export default () => (
  <Layit row margin={[20, 20, 5, 10]}>
    <Box style={{ backgroundColor: 'red' }} />
    <Box style={{ backgroundColor: 'blue' }} />
    <Box style={{ backgroundColor: 'green' }} />
  </Layit>
);

The layout styles are props and can be utilised cleanly without the need to mangle with the style prop.

The style prop will pass through and applied normally, so you can use as much of the API available as you feel comfortable. With that said though, the style prop will override the layout props, if both are defined. So becareful.

export default () => (
  <View style={{ flexDirection: 'column' }} row margin={[20, 20, 5, 10]}>
    <Box style={{ backgroundColor: 'red' }} />
    <Box style={{ backgroundColor: 'blue' }} />
    <Box style={{ backgroundColor: 'green' }} />
  </View>
);

The above will use column instead of row for flexDirection in the end. If you'd like style prop to be overridden by the layout props continue reading.

Applying to other components

As mentioned earlier, there is also a Higher-order component available, that means it's easy to use another component instead of View.

Perhaps you have a button that has been styled with styled-components, or a panel which has some default padding, use provideLayout() to wrap those components.

import { provideLayout } from 'react-native-layit';

export default provideLayout(SuccessButtonComponent);

Any style or layout props used will be passed to SuccessButtonComponent through the style prop. You'll need to make sure it is continued down the child component you wish the layout props to be applied to.

For example below.

const SuccessButtonComponent = ({ style, ...props }) => {
  const myStyles = {
    // style logic here
    flexDirection: 'column',
    backgroundColor: 'green',
  };
  return (
    <View style={[myStyles, style]} {...props}>
      <Text>It's a success!</Text>
      <Text>Lets have fun.</Text>
    </View>
  );
};

The example has the style prop provided take precendence over its own defined style, that means the flexDirection: 'column' could be changed to to flexDirection: 'row' by using the row property.

You could have it the other way round, but only it cases where you don't want any specific property to change, so if it was flipped in the example to [props.style, myStyles] then SuccessButtonComponent will always have flexDirection: 'column' even if the row property was used.

Layout properties

Here is a list of available props for the layout

Prop Type Default Description
flex boolean/number Sets style flex: ${number}, will convert false to 0 and true to 1
row boolean/number false Sets style flexDirection: 'row', this take precedence over the col prop if both are true
col boolean/number false Sets style flexDirection: 'column'
reverse boolean false Reverses the direction of row or col making the style flexDirection: 'row-reverse' or flexDirection: 'column-reverse' respectively, so will need one of those enabled to work
alignX enum Sets either justifyContent for row or alignItems for col with the enum value. If neither row or col is defined this prop will not take effect
alignY enum Same with alignX but sets justifyContent for col or alignItems for row
width number/array[number] Set the width, could interfer with flex behaviours if used together. More details about min and max value below
height number/array[number] Set the height, could interfer with flex behaviours if used together. More details about min and max value below
margin number/array[number] Sets margin for top, bottom, left and right. Follows CSS rules for margin
padding number/array[number] Sets padding for top, bottom, left and right. Follows CSS rules for padding
viewProps object {} A set of props to pass on to the Component (default View), the layout passes through most props except ones listed in this table. So if you require any props already used by the layout, this prop is the way to declare them. Props declared here take precedence over "pass through" props
cacheStyles boolean true Disables style cache, this could improve performance if you're doing rapid style changes through Layit, although it really depends on whether hashing an object or sending manual styles through is more performant, and also if you're going to re-use any of the styles that you're rapidly changing again. Your mileage may vary

Using alignX and alignY

Here is a table for reference when an enum value is invalid in which case center is used instead.

Important to note again that if neither row or col is defined these props will not take effect, that's because the library cannot tell which direction the flex is running otherwise.

enum values row+alignX row+alignY col+alignX col+alignY
flex-start
center
flex-end
space-around 🚫 🚫
space-between 🚫 🚫
stretch 🚫 🚫

Using height and width

Setting width and height is quite straight forward, setting them directly with width="20" and height="10".

If you want to set min and/or max for height or width, in stylesheet you'd need to define each separately.

I had found that quite verbose for this library and pollutes the property list a fair amount, so instead I've extended the width and height property to cleanly allow handling min and max easily.

To define minWidth, width and maxWidth you'll need to define an array for the width property in the format: [minWidth, width, maxWidth], and the same applies for height.

The values provided will need to be a valid number or string (can be a percent), otherwise it will error or be ignored altogether.

An example of the feature:

<Layit width={['30', "60%", 80]} />

This means the following styles are applied:

{
  minWidth: 30,
  width: "60%",
  maxWidth: 80,
}

Using margin and padding

Here is a table for what styles are applied when you use the margin and/or padding.

This behaves like the CSS rules, so if you're familiar with that, you probably already understand how it works.

values top bottom left right
12 12 12 12 12
[12] 12 12 12 12
[12, 24] 12 12 24 24
[12, 24, 36] 12 36 24 24
[12, 24, 36, 48] 12 36 48 24

Styles take precedence

If there's a style prop which has a rule conflicting with a layout prop, the style will take precendence. This also applies for viewProps, if viewProps contains a style property and there's also a style prop, the viewProps style will take precedence.

So the hierarchy goes like this

 viewProps.style > style > layoutProps

In order to not have styles overwriting the layoutProps, you should look at shifting those styles into another component which is wrapped by provideLayout(). Try to keep provideLayout() at the very top of the stack of wrappers, if possible.

This behaviour is to allow switching between different stylesheets seemlessly for a component without unexpected "it's not changing" scenarios and worrying about overwrites from this library.

For example

const props = {
  margin: [1, 1, 1, 1],
  style: {
    marginBottom: 2,
    marginLeft: 2,
  },
  viewProps: {
    style: {
      marginLeft: 3,
      marginRight: 4,
    },
  },
};

Will produce the following styles

const flattenedStyles = {
  marginTop: 1,
  marginBottom: 2,
  marginLeft: 3,
  marginRight: 4,
};

Pipeline

  • Positioning props
  • Maybe a modal provider?

License

MIT