Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,21 +1,7 @@
{
"ignore": ["node_modules/**"],
"env": {
"development": {
"presets": [
[
"env",
{
"targets": {
"node": 8
},
"debug": false,
"useBuiltIns": true,
"modules": "commonjs"
}
]
]
},
"development": { "presets": [["env"]] },
"production": {
"presets": [
[
Expand Down
89 changes: 82 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,16 +101,43 @@ const MyComponent = ({ option, ...rest }) => <Box {...rest} />

## CSS Modules

`classier-react` supports using CSS Modules through dynamic object keys:
`classier-react` can wrap CSS Modules with `elementModule` and `boxedModule`.

`boxedModule` creates an Element that checks the provided module for a mapping after transformation:

```jsx
import { createModuleElement } from 'classier-react'
import styles from './styles.css'

const MBox = boxedModule(styles)

...

<Box {...{
[styles.fontSize]: 'md'
}} {...rest} />
<MBox someProp='someValue' children={...} />
```

`elementModule` will expose the optionally listed blocks as Elements from the module:

```jsx
import { createModuleElement } from 'classier-react'
import styles from './styles.css'

const Block = elementModule(styles, ['element'])


...

<Block.Element fontSize="md" {...props} />
```

## Passing down styles

`<Comp />` only injects the props it merges, it can't make sure they are rendered. If you're wrapping or writing a component, it's a good idea to pass the `style` and `className` props onward to what is rendered.

Most components are already written this way.

```jsx
const MyComponent = ({ option, ...rest }) => <div {...rest} />
```

## Avoiding mixing domains
Expand All @@ -127,8 +154,10 @@ const containerClassName = cx(props.styled)

## API

### React

```js
import { Box, Text, Comp, cx, configure } from 'classier-react'
import { Box, Text, Comp, createElement } from 'classier-react'
```

---
Expand All @@ -151,7 +180,51 @@ Renders a span with all its props translated to CSS classes.

### `<Comp />`

A _HOC_ for injecting or "composing" style props. Merges its `style`, `className`, and the rest of its props as classes into the props of a child component.
A declarative wrapper for injecting or "composing" style props. Merges its `style`, `className`, and the rest of its props as classes into the props of a child component.

---

### `createElement(name)`

Returns a customized `Box` that nests its CSS classes under the {name} block.

#### members

- **Comp** - a `Comp` with the same customization.

---

### CSS Modules

```js
import { boxedModule, createModuleElement, elementModule } from 'classier-react'
```

---

### `boxedModule(module)`

Returns an `Element` that tries to map its CSS classes to the ones in the provided module.

---

### `createModuleElement(module, name)`

Returns an `Element` that tries to map its CSS classes to the ones in the provided module under the {name} block.

---

### `elementModule(module, pick)`

Returns an object that picks selectors of `module` to `createModuleElement`.

---

### Utility

```js
import { cx, configure } from 'classier-react'
```

---

Expand All @@ -167,7 +240,9 @@ Lets you change the global behavior of `cx`

#### opts

- **kebabCase** - Transform names and values from camelCase. Reverses `style-loader`. (_default: true_)
- **transformFn** - A function mapping an AST Node to a CSS class name. (_default: mappers.tailwind_)

- **kebabCase** - Transform names and values from camelCase. You might want to turn it off with `postcss-modules`. (_default: true_)

- **keepSentence** - When kebabing, lower-case everything but the first word (_default: true_)

Expand Down
40 changes: 23 additions & 17 deletions src/components/Box.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
import React from 'react'
import cx from '../transform'
import { classnames } from '../transform'

/*
Creates an element that uses its unknown props as css class names.
Defaults to a div.
*/
export default function Box({
style,
className,
onClick,
title,
children,
is = 'div',
...propClasses
}) {
const classes = cx(propClasses, className)

const props = {
export const factory = mapper =>
function Box({
title,
style,
onClick,
className,
children,
className: classes,
role: onClick ? 'button' : undefined
is = 'div',
...propClasses
}) {
const classes = mapper(propClasses, className)
const Tag = is

const props = {
title,
style,
onClick,
children,
className: classes,
role: onClick ? 'button' : undefined
}

return <Tag {...props} />
}

return React.createElement(is, props)
}
export const Box = factory(classnames)

export default Box
31 changes: 18 additions & 13 deletions src/components/Comp.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import React from 'react'
import cx from '../transform'
import { classnames } from '../transform'

/*
A wrapper that injects its children with style and className
using shallow merging and classNameTransform
*/
export default function Comp({ style, className, children, ...propClasses }) {
return React.Children.map(
children,
child =>
child
? React.cloneElement(child, {
style: { ...style, ...child.props.style },
className: cx(propClasses, className, child.props.className)
})
: null
)
}
export const factory = mapper =>
function Comp({ style, className, children, ...propClasses }) {
return React.Children.map(
children,
child =>
child
? React.cloneElement(child, {
style: { ...style, ...child.props.style },
className: mapper(propClasses, className, child.props.className)
})
: null
)
}

export const Comp = factory(classnames)

export default Comp
15 changes: 11 additions & 4 deletions src/components/Text.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import React from 'react'
import cx from '../transform'
import { classnames } from '../transform'

/*
Creates a span element that uses its unknown props as css class names.
*/
export default function Text({ className, children, ...propClasses }) {
return <span className={cx(propClasses, className)} children={children} />
}
export const factory = mapper =>
function Text({ className, children, ...propClasses }) {
return (
<span className={mapper(propClasses, className)} children={children} />
)
}

export const Text = factory(classnames)

export default Text
25 changes: 17 additions & 8 deletions src/config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
export const config = {
import tailwind from './mappers/tailwind'

export const config = {}

export function configure(opts) {
Object.assign(config, {
...opts,
mapFn: opts.mapFn ? opts.mapFn(config) : config.mapFn,
join: { ...config.join, ...opts.join }
})
}

// Default configuration
configure({
mapFn: tailwind,
keepSentence: true,
kebabCase: true,
join: {
Expand All @@ -8,11 +22,6 @@ export const config = {
value: '-',
words: '-'
}
}
})

export function configure (opts) {
Object.assign(config, {
...opts,
join: { ...config.join, ...opts.join }
})
}
export default config
5 changes: 3 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as cx } from './transform'
export * from './components'
export * from './config'
export * from './modules'
export * from './transform'
export * from './components'
10 changes: 10 additions & 0 deletions src/mappers/tailwind.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default function tailwind(config) {
return ({ variant, block, modifier, value }) =>
(variant || '') +
((variant && config.join.variant) || '') +
(block || '') +
((block && modifier && config.join.modifier) || '') +
(modifier || '') +
((value && config.join.value) || '') +
(value || '')
}
26 changes: 26 additions & 0 deletions src/modules.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { classnamesWithBase, classnamesWithMapper } from './transform'
import { factory as makeBox } from './components/Box'
import { factory as makeComp } from './components/Comp'

export const makeElement = mapper => {
const El = makeBox(mapper)
El.Comp = makeComp(mapper)
return El
}

export const createElement = block => makeElement(classnamesWithBase({ block }))

export const createModuleElement = (module, key) =>
makeElement(
classnamesWithMapper(name => module[name] || name, { block: key })
)

export const boxedModule = module =>
makeElement(classnamesWithMapper(name => module[name] || name))

export const elementModule = (module, pick) =>
(pick || Object.keys(module)).reduce((res, key) => {
const capped = key.charAt(0).toUpperCase() + key.substring(1)
res[capped] = createModuleElement(module, key)
return res
}, {})
Loading