1.1.2 • Published 4 years ago

view-components v1.1.2

Weekly downloads
1
License
MIT
Repository
github
Last release
4 years ago

View Components

Table of contents

Motivation

This library has been inspired by styled-components and classnames

styled-components is an awesome library, it's very convenient if you are writing your styles manually. Nevertheless, it is harder to deal with it when you want to use a CSS framework, such as Bootstrap or Tailwind. This small library with one little dependency provides an easy way to create small stateless view components by assigning the CSS classNames conditionally.

Installation

Install the library using

npm install --save view-components

or

yarn add view-components

Usage

You probably wrote some components like this one day:

import React from 'react'
import classNames from 'classnames'

const Button = ({ className, ...props }) => (
  <button
    className={classNames(className, 'standard class-names for-every button')}
    {...props}
  />
)

This library basically does the same, but it makes your code much cleaner. The syntax is very similar to styled-components (If you are not familiar with the tagged templates syntax, check the MDN article):

import React from 'react'
import view from 'view-components'

const Button = view.button`
  standard class-names for-every button
`

This will create a <Button/> component, which will render a <button/> element. All props, including the className, will be forwarded. This means that

<Button className={'disabled'} type={'submit'}>
  foobar
</Button>

will result in

<button class="standard class-names for-every button disabled" type="submit">
  foobar
</button>

Tagged templates allow you to use any values, including functions, inside template strings. Here we can use this fact

Primitives

You can insert any primitives, they will just be converted into strings

const padding = 'px-3 py-1'
const margin = 3

const Button = view.button`
  ${padding} m-${margin}
`

If you use this component, you will get

<button class="px-3 py-1 m-3"></button>

Functions

Functions are the main way to make your components dynamic. If you pass a function when creating a component, it will be called every time the component is rendered. Whatever the function returns will be used as className. The function will receive component's props as its only argument, so that you can decide, what classNames to apply:

const Button = view.button`
  btn
  ${props => (props.disabled ? 'btn-disabled' : 'btn-enabled')}
`
<Button />
// becomes
<button class="btn btn-enabled"></button>

<Button disabled />
// becomes
<button class="btn btn-disabled"></button>

Functions may also return arrays or objects (but not other functions)

Objects

Objects use the same syntax as in classnames package. Object's keys are classNames and values are booleans, indicating whether the className should be applied or not. You can also pass a function as a value. In this case it will be called during every render with the component's props and it must return a boolean to decide whether the className should be applier or not.

const Button = view.button`
  ${{
    foo: true,
    bar: false,
    large: props => props.size === 'lg',
  }}
`

Here foo className will always be applied, bar will always be ignored and large will be used only if the size prop is equal to 'lg'

Arrays

Any arrays you pass will be flattened and used as classNames. You can pass anything inside of an array: primitives, functions, objects or other arrays - the array will be flattened. It means that

const classes = ['btn', 'btn-lg']
const Button = view.button`
  ${classes}
  ${[
    273,
    ['foo', ['bar']],
    props => props.disabled && 'disabled',
    {
      foobar: true,
      baz: false,
    },
  ]}
`

is basically the same as

const Button = view.button`
  ${'btn'} ${'btn-lg'}
  ${273}
  ${'foo'} ${'bar'}
  ${props => props.disabled && 'disabled'}
  ${{
    foobar: true,
    baz: false,
  }}
`

Using with other components

When you do

import view from 'view-components'

You actually get a function. It accepts the only argument - the component it should render. It may be a string for HTML elements or a function or class for React components. It will work fine as long as the component you pass accepts the className prop:

const Container = view('article')`
  ${/* ... */}
`
function SuperComplicatedSwitch({ className, onClick, ...props }) {
  /* Super complicated and important stuff */

  return (
    // className may be applied anywhere in the components tree
    <div className={className}>
      {/* Other components */}
    </div>
  )
}

function FancySwitch = view(SuperComplicatedSwitch)`
  ${/* ... */}
`

This view function has some nice shortcuts for most HTML elements, exported as its properties. So view.img is the same as view('img').

Styled-components

If you want to use this library along with the styled-components, then you can use some shortcuts. Basically you could do something like

import styled from 'styled-components'
import view from 'view-components'

const Button = styled(
  view`
    px-4 py-2 rounded
    ${props => props.disabled && 'bg-secondary'}
  `
)`
  transition: width 200ms;
  width: ${props => (props.disabled ? '10%' : '70%')};
`

That works, but it's not very convenient. There are is a nice function in the view-components/styled module, that makes it look better:

import styledView from 'view-components/styled'

const Button = styledView.button`
  px-4 py-2 rounded
  ${props => props.disabled && 'bg-secondary'}
``
  transition: width 200ms;
  width: ${props => (props.disabled ? '10%' : '70%')};
`

Notice, that you must have styled-components installed manually in order for this to work This form is also a little bit faster (comparing to using styled-components and view-components separately), because it makes use of the attrs method, provided by styled-components. Calling styledView(...)`...` is the same as calling styled(...), it just wraps styled component with the view component logic. After calling this, you can use it as if it was a normal styled component:

styledView.button`
  className1 className2
`.attrs(props => {
  animationDuration: getAnimationDuration(props)
})`
  animation-duration: ${props => props.animationDuration}s;
`

Types

The functions returned by view(...) and styledView(...) are generics. You can define the props you need by passing an interface:

import view from 'view-components'

const Button = view.button<{ disabled?: boolean }>`
  ${props => props.disabled && 'disabled'}
`

For styledView the interface you pass will also be forwarded to styled-components (because view components pass all their props to the wrapped styled components):

import styledView from 'view-components/styled'

const Button = styledView.button<{ disabled?: boolean }>`
  ${props => props.disabled && 'disabled'} // props.disabled is typed
``
  // props.disabled is typed as well
  background: ${props.disabled ? 'grey' : 'palevioletred'};
`
1.1.2

4 years ago

1.1.1

5 years ago

1.1.0

5 years ago

1.0.8

5 years ago

1.0.7

5 years ago

1.0.6

5 years ago

1.0.5

5 years ago

1.0.4

5 years ago

1.0.3

5 years ago

1.0.2

5 years ago

1.0.0

5 years ago