callstack / linaria

Zero-runtime CSS in JS library
https://linaria.dev
MIT License
11.68k stars 417 forks source link

how to properly extend linaria? #459

Open dreyks opened 5 years ago

dreyks commented 5 years ago

I'm trying to interbreed linaria with astroturf =) specifically the "static props into classnames" convertion, so that I can write css bound to props combinations like this

So far I've got this working with the following setup

import React from 'react'
import { styled } from 'linaria/react'
import classNames from 'classnames'

const RawComponent = styled.div`
  .size-big {
    font-size: 36px
  }
`

const staticStyled = (Component) => React.forwardRef(({ className, ...props }, ref) => {
  const propClasses = Object.entries(props)
    .filter(([_name, value]) => value && ['string', 'boolean', 'number'].includes(typeof value))
    .map((entry) => entry.join('-'))
  const newClassName = classNames(Component.__linaria.className, className, propClasses)
  return <Component ref={ref} className={newClassName} {...props} />
})

export const StyledComponent = staticStyled(RawComponent)

Obviously, I want to extract the staticStyled helper (preferably to an external library), ideally to the point where I could still import { styled } from 'linaria/react' and things would still work.

I just can't come up with a good strategy on how to achieve this

Any tips?

brandonkal commented 5 years ago

@dreyks see my fork on npm @brandonkal/linaria which achieves what you are looking for among other things.

mattoni commented 5 years ago

@brandonkal tried using the latest release of your fork, and am finding that the type of props is 'any'?

image

brandonkal commented 5 years ago

@mattoni that is expected. Typescript has no mechanism to infer the type there without a bit of extra work:

type ThemeContext = string

interface BadgeProps {
  theme$: ThemeContext
}
const StyledBadge = ((props) => styled.div<BadgeProps>`
  color: ${props.theme$};
`)({} as BadgeProps)

This works because rather than defining a new function for every interpolation, the props object is just available to you. Under the hood, the babel transform essentially transforms it to this:

const StyledBadge = styled.div`
  color: ${props => props.theme$};
`

before doing the other transforms.

Of course you could just do this if you preferred:

type ThemeContext = string

interface BadgeProps {
  theme$: ThemeContext
}
const StyledBadge = styled.div<BadgeProps>`
  color: ${(props: BadgeProps) => props.theme$};
`

but typing the props argument for every single interpolation gets tedious, which is why I made the shortcut.

If you have other questions, feel free to open an issue.

mattoni commented 5 years ago

@brandonkal thanks for the info. Why does it work properly with the vanilla linaria?

P.S. I did want to open an issue, but there seems to be no way to do that on your fork.

brandonkal commented 5 years ago

@mattoni Oh you are right! I just enabled issues now. Please go ahead and open one with a repo that shows the props argument typed with vanilla linaria.