cxs-css / cxs

fast af css-in-js in 0.7kb
MIT License
1.19k stars 68 forks source link

typescript version inside #102

Open jeremy-coleman opened 5 years ago

jeremy-coleman commented 5 years ago

better editor feedback without being aidsy than pretty much every other style library (at least imo)


import React from 'react'
import { HTMLProps, CSSProperties } from 'react'
const h = React.createElement

//new CSSRuleList() //items, length
//let r = new CSSStyleRule

//was using this like this, jus took context out though idk, deal later
//{theme: defined(context.theme, props.theme, pallete: 'none')}
const defined = (...args) => {
  for (var i = 0; i < args.length; i++) {
    if (args[i] !== undefined) return args[i]
  }
}

let cache = {}
let prefix = 'x'
const rules: CSSStyleRule[] = [] //[]

let insert = (rule: CSSStyleRule) => void rules.push(rule as CSSStyleRule)

const hyph = s => s.replace(/[A-Z]|^ms/g, '-$&').toLowerCase()
const mx = (rule, media) => (media ? `${media}{${rule}}` : rule)
const rx = (cn, prop, val) => `.${cn}{${hyph(prop)}:${val}}`
const noAnd = s => s.replace(/&/g, '')

const parse = (obj, child = '', media?) =>
  Object.keys(obj)
    .map(key => {
      const val = obj[key]
      if (val === null) return ''
      if (typeof val === 'object') {
        const m2 = /^@/.test(key) ? key : null
        const c2 = m2 ? child : child + key
        return parse(val, c2, m2 || media)
      }
      const _key = key + val + child + media
      if (cache[_key]) return cache[_key]
      const className = prefix + rules.length.toString(36)
      insert(mx(rx(className + noAnd(child), key, val), media))
      cache[_key] = className
      return className
    })
    .join(' ')

function cxs(...styles) {
  return styles
    .map(style => parse(style))
    .join(' ')
    .trim()
}

cxs.css = () => rules.sort().join('')

cxs.reset = () => {
  cache = {}
  while (rules.length) rules.pop()
}

cxs.prefix = val => (prefix = val)

if (typeof document !== 'undefined') {
  const sheet = document.head.appendChild(document.createElement('style')).sheet as CSSStyleSheet
  insert = (rule: CSSStyleRule & string) => {
    rules.push(rule as CSSStyleRule)
    sheet.insertRule(rule, sheet.cssRules.length)
  }
}

type DomProps = Partial<HTMLProps<any>>
type CSX = { css?: CSSProperties }
type StyledCSX = DomProps & CSX

type Proptional<P> = CSSProperties | ((...args: P[]) => CSSProperties)

function styled<P>(C) {
  return (...args: Proptional<P>[]) => {
    const Comp = (props: P & StyledCSX) => {
      const stylePropKeys = [...Object.keys({}), 'css']
      //const styleProps = Object.assign({ theme: defined(context.theme, props.theme, {}) }, props)

      const next: any = {}
      for (let key in props) {
        if (stylePropKeys.includes(key)) continue
        next[key] = props[key]
      }
      next.className = [
        next.className,
        ...args
          .map(proptional => (typeof proptional === 'function' ? proptional(props) : proptional))
          .filter(s => !!s)
          .map(s => cxs(s)),
        cxs(props.css || {}),
      ]
        .join(' ')
        .trim()

      return h(C, next)
    }

    return Comp
  }
}

styled.css = cxs.css
styled.reset = cxs.reset

export { cxs }
export { styled }
export default styled

// export function styledComponentWithProps<T, U extends HTMLElement = HTMLElement>(styledFunction: StyledFunction<React.HTMLProps<U>>): StyledFunction<T & React.HTMLProps<U>> {
//   return styledFunction as any;
// }

// thanks microsoft, please teach our youth how to read, i think you've got it figured out
// type Unpacked<T> = T extends (infer U)[]
//   ? U
//   : T extends (...args: any[]) => infer U
//   ? U
//   : T extends Promise<infer U>
//   ? U
//   : T

// type T0 = Unpacked<string> // string
// type T1 = Unpacked<string[]> // string
// type T2 = Unpacked<() => string> // string

// type Foo<T> = T extends { a: infer U; b: infer U } ? U : never
// type T10 = Foo<{ a: string; b: string }> // string
// type T11 = Foo<{ a: string; b: number }> // string | number

//type CC = (...args: CSSProperties[]) => infer R ? R : CSSProperties

//type ReturnType<T> = (...args) => T ? T : CSSProperties
// im going to space mountain charley fml

// function styled1(C) {
//   return (...args) => {
//     const Comp = (props, context: any = {}) => {
//       const stylePropKeys = [...Object.keys({}), 'css']
//       const styleProps = Object.assign({ theme: defined(context.theme, props.theme, {}) }, props)

//       const next: any = {}
//       for (let key in props) {
//         if (stylePropKeys.includes(key)) continue
//         next[key] = props[key]
//       }
//       next.className = [
//         next.className,
//         ...args
//           .map(a => (typeof a === 'function' ? a(styleProps) : a))
//           .filter(s => !!s)
//           .map(s => cxs(s)),
//         cxs(props.css || {}),
//       ]
//         .join(' ')
//         .trim()

//       return h(C, next)
//     }

//     return Comp
//   }
// }

usage

import React from 'react'
import { styled } from './styled'

type Props = {
  color: 'green' | 'black'
}

let MyButton = styled<Props>('button')(props => ({
  backgroundColor: (props.color && props.color) || 'green',
}))

let NoPropsButton = styled('button')({
  backgroundColor: 'green',
})

export { MyButton, NoPropsButton }

let Demo = props => <MyButton onClick={e => console.log('hi')} color={'black'}/>