Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix laws and simplify implementation #25

Open
wants to merge 16 commits into
base: v1
Choose a base branch
from
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
57 changes: 57 additions & 0 deletions api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
## API options


```javascript
import ReactDream, { ReactBox } from 'react-dream'

ReactDream.Stateless(({ title }) => <h1>{title}</h1>)
.map(element => <div>{element}</div>)
.contramap(({ language }) => ({
title: language === 'en' ? 'Hello' : 'Hola'
}))
.enhance(
withState('updateTitle', 'title', 'Hola')
)
.name('Header')

ReactDream.Stateful(class extends Component {})
.map() // not optimal!

ReactDream.Stateful(class extends Component {})
.toStateless()
.map() // optimal but verbose

const withChildren = (Parent, Up, Down) =>
ReactDream.Stateless(({ parent, up, down }) =>
<Parent.Component {...parent}>
<Up {...up} />
<Down {...down} />
</Parent.Component>
)

withChildren(
Header,
Title,
Tagline
)
.contramap()

// will build a Stateful
ReactDream(class extends Component {})

// will build a Stateless
ReactDream(props => <br />)

ReactDream(props => <hr />)
.asBox()
.map()

ReactBox(props => <hr />)
.asDream()
.map()

// Equivalences
ReactDream(x).enhance(f) == ReactDream(x).asBox().map(f)
ReactBox(x).map(f) == ReactDream(x).enhance(f)
ReactBox.of(f).ap(x) == ReactBox(x).map(f)
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"react": "^16.0.0",
"react-test-renderer": "^16.0.0",
"recompose": "^0.24.0",
"washington": "^2.0.0-rc.3"
"washington": "^2.0.0-rc.5"
},
"peerDependencies": {
"react": "^16.0.0",
Expand Down
170 changes: 129 additions & 41 deletions src/ReactDream.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,77 @@
import React, { Fragment } from 'react'
import compose from 'recompose/compose'
import getDisplayName from 'recompose/getDisplayName'
import setDisplayName from 'recompose/setDisplayName'
import recomposeDefaultProps from 'recompose/defaultProps'
import setPropTypes from 'recompose/setPropTypes'
import withDebugger from '@hocs/with-debugger'
import withLog from '@hocs/with-log'
import doAp from './internals/doAp'
import doConcat from './internals/doConcat'
import doContramap from './internals/doContramap'
import doMap from './internals/doMap'
import doPromap from './internals/doPromap'
import doRotate from './internals/doRotate'
import doTranslate from './internals/doTranslate'
import doScale from './internals/doScale'
import styleFromProps from './styleFromProps'

import isReferentiallyTransparentFunctionComponent from './isReferentiallyTransparentFunctionComponent'

import getRotateStyle from './helpers/getRotateStyle'
import getScaleStyle from './helpers/getScaleStyle'
import getTranslateStyle from './helpers/getTranslateStyle'

// ALGEBRAS
// ////////////////////////////////////////////////////////////////////////// //

// ap : higherOrderComponent -> ReactDream -> ReactDream
const ap = higherOrderComponent => ReactDreamComponent =>
ReactDream(doAp(higherOrderComponent)(ReactDreamComponent))

// chain : Component -> (Component -> ReactDream) -> ReactDream
const chain = Component => kleisliReactDreamComponent => kleisliReactDreamComponent(Component)

// map : Component -> (Component -> Component) -> ReactDream
const map = Component => higherOrderComponent => ReactDream(doMap(higherOrderComponent)(Component))

// concat : Component -> Component -> ReactDream
const concat = Component => OtherComponent =>
ReactDream(doConcat(OtherComponent.Component)(Component))
const chain = Component => kleisliReactDreamComponent =>
kleisliReactDreamComponent(Component)

// concat : ReactComponent a e ->
// ReactComponent b f ->
// ReactDream (ReactComponent c g)
const concat = Component => ReactDreamComponent =>
ReactDream(
setDisplayName(
getDisplayName(Component)
.concat(getDisplayName(ReactDreamComponent.CompComponent))
)(props => <Fragment>
<Component {...props} />
<ReactDreamComponent.Component {...props} />
</Fragment>)
)

// map : ReactComponent e a ->
// (ReactComponent e a -> ReactComponent e b) ->
// ReactDream (ReactComponent e b)
const map = Component => postProcessor =>
ReactDream(x => postProcessor(Component(x)))

// statelessContramap : ReactComponent a e ->
// (a -> b) ->
// ReactDream (ReactComponent b e)
const statelessContramap = Component => propsPreprocessor =>
Stateless(compose(Component, propsPreprocessor))

// statefulContramap : ReactComponent a e ->
// (a -> b) ->
// ReactDream (ReactComponent b e)
const statefulContramap = Component => propsPreprocessor =>
Stateful(props => <Component {...propsPreprocessor(props)} />)

// statelessPromap : ReactComponent b e ->
// ((a -> b), (e -> f)) ->
// ReactDream (ReactComponent a f)
const statelessPromap = Component => (preProcessor, postProcessor) =>
map(
statelessContramap(Component)(preProcessor).Component
)(
postProcessor
)

// promap : ReactComponent b e ->
// ((a -> b), (e -> f)) ->
// ReactDream (ReactComponent a f)
const promap = Component => (preProcessor, postProcessor) =>
map(
contramap(Component)(preProcessor).Component
)(
postProcessor
)

// contramap : Component -> (a -> Props) -> ReactDream
const contramap = Component => propsPreprocessor =>
ReactDream(doContramap(propsPreprocessor)(Component))

// promap : Component -> (a -> Props) -> (Component -> Component) -> ReactDream
const promap = Component => (propsPreprocessor, higherOrderComponent) =>
ReactDream(doPromap(propsPreprocessor, higherOrderComponent)(Component))

// CUSTOM HELPERS
// ////////////////////////////////////////////////////////////////////////// //
Expand All @@ -61,8 +95,9 @@ const defaultProps = Component => props => ReactDream(recomposeDefaultProps(prop
// log : Component -> (Props -> String) -> IO ReactDream
const log = Component => messageFromProps => ReactDream(withLog(messageFromProps)(Component))

// name : Component -> String -> ReactDream
const name = Component => compose(map(Component), setDisplayName)
// name : ReactComponent a e -> String -> ReactDream (ReactComponent a e)
const name = Component => name =>
ReactDream(setDisplayName(name)(Component))

// removeProps : Component -> (...Array) -> ReactDream
const removeProps = Component => (...propsToRemove) =>
Expand All @@ -76,38 +111,86 @@ const removeProps = Component => (...propsToRemove) =>
})

// propTypes : Component -> (PropTypes) -> ReactDream
const propTypes = Component => propTypesToSet => ReactDream(setPropTypes(propTypesToSet)(Component))
const propTypes = Component => propTypesToSet =>
ReactDream(setPropTypes(propTypesToSet)(Component))

// translate : Component -> (Props -> [Number]) -> ReactDream
const translate = Component => getTranslateFromProps =>
ReactDream(doTranslate(getTranslateFromProps)(Component))
contramap(Component)(getTranslateStyle(getTranslateFromProps))

// rotate : Component -> (Props -> Number) -> ReactDream
const rotate = Component => getRotateFromProps =>
ReactDream(doRotate(getRotateFromProps)(Component))
contramap(Component)(getRotateStyle(getRotateFromProps))

// scale : Component -> (Props -> Number) -> ReactDream
const scale = Component => getScaleFromProps => ReactDream(doScale(getScaleFromProps)(Component))
const scale = Component => getScaleFromProps =>
contramap(Component)(getScaleStyle(getScaleFromProps))

// style : Component -> (Props -> Style) -> ReactDream
const style = Component => getStyleFromProps =>
contramap(Component)(styleFromProps(getStyleFromProps))
contramap(Component)(props => ({
...props,
style: {
...getStyleFromProps(props),
...(props.style || {}),
},
}))


// TYPE
// ////////////////////////////////////////////////////////////////////////// //

// ReactDream : Component -> ReactDream
const ReactDream = Component => ({
// Stateless : Component -> ReactDream
export const Stateless = Component => ({
Component,

// Algebras
chain: chain(Component),
concat: concat(Component),
contramap: statelessContramap(Component),
map: map(Component),
promap: statelessPromap(Component),

// Type
constructor: ReactDream,
match: ({ Stateless, _ }) =>
Stateless !== undefined
? Stateless(Component)
: _(),

// Custom helpers
addProps: addProps(Component),
debug: debug(Component),
defaultProps: defaultProps(Component),
fork: fork(Component),
name: name(Component),
log: log(Component),
propTypes: propTypes(Component),
removeProps: removeProps(Component),
rotate: rotate(Component),
scale: scale(Component),
style: style(Component),
translate: translate(Component),
})

// Stateful : Component -> ReactDream
export const Stateful = Component => ({
Component,

// Algebras
ap: ap(Component),
chain: chain(Component),
concat: concat(Component),
contramap: contramap(Component),
contramap: statefulContramap(Component),
map: map(Component),
promap: promap(Component),

// Type
constructor: ReactDream,
match: ({ Stateful, _ }) =>
Stateful !== undefined
? Stateful(Component)
: _(),

// Custom helpers
addProps: addProps(Component),
debug: debug(Component),
Expand All @@ -123,8 +206,13 @@ const ReactDream = Component => ({
translate: translate(Component),
})

ReactDream.of = ReactDream
// LIFTER
// /////////////////////////////////////////////////////////////////////////////////// //

export const of = ReactDream.of
// ReactDream : Component -> ReactDream
const ReactDream = Component =>
isReferentiallyTransparentFunctionComponent(Component)
? Stateless(Component)
: Stateful(Component)

export default ReactDream
17 changes: 17 additions & 0 deletions src/helpers/getRotateStyle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const calculateTransform = oldTransform => rotation =>
oldTransform ? `${oldTransform} rotate(${rotation}deg)` : `rotate(${rotation}deg)`

// getRotateStyle : (Props a -> Number) -> Props a -> Props b
export default getRotateFromProps =>
props => ({
...props,
style: {
...props.style,
transform:
calculateTransform(
props && props.style && props.style.transform
)(
getRotateFromProps(props)
),
},
})
16 changes: 16 additions & 0 deletions src/helpers/getScaleStyle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const calculateTransform = oldTransform => scaling =>
oldTransform ? `${oldTransform} scale(${scaling})` : `scale(${scaling})`

// getScaleStyle : (Props -> Number) -> Component -> Component
export default getScaleFromProps =>
props => ({
...props,
style: {
...props.style,
transform: calculateTransform(
props && props.style && props.style.transform
)(
getScaleFromProps(props)
),
},
})
20 changes: 9 additions & 11 deletions src/internals/doTranslate.js → src/helpers/getTranslateStyle.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import { compose } from 'recompose'
import doContramap from './doContramap'

const calculateTransform = oldTransform => ([x, y, z]) => {
switch (true) {
case x != null && y != null && z != null:
Expand All @@ -24,15 +21,16 @@ const calculateTransform = oldTransform => ([x, y, z]) => {
}
}

// doTranslate : (Props -> [Number]) -> Component -> Component
export default getTranslateFromProps => Component =>
doContramap(props => ({
// getTranslateStyle : (Props a -> [Number]) -> Props a -> Props b
export default getTranslateFromProps =>
props => ({
...props,
style: {
...props.style,
transform: compose(
calculateTransform(props && props.style && props.style.transform),
getTranslateFromProps
)(props),
transform: calculateTransform(
props && props.style && props.style.transform
)(
getTranslateFromProps(props)
),
},
}))(Component)
})
11 changes: 3 additions & 8 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import ReactDream from './ReactDream'
import ReactDream, { Stateless as _Stateless, Stateful as _Stateful } from './ReactDream'

export { default as createElementWithProps } from './createElementWithProps'

export { default as styleFromProps } from './styleFromProps'

export { default as withStyleFromProps } from './withStyleFromProps'

export { default as addProps } from './partialApplication/addProps'
export { default as ap } from './partialApplication/ap'
export { default as chain } from './partialApplication/chain'
export { default as concat } from './partialApplication/concat'
export { default as contramap } from './partialApplication/contramap'
Expand All @@ -25,6 +20,6 @@ export { default as scale } from './partialApplication/scale'
export { default as style } from './partialApplication/style'
export { default as translate } from './partialApplication/translate'

export const of = ReactDream

export const Stateless = _Stateless
export const Stateful = _Stateful
export default ReactDream
2 changes: 0 additions & 2 deletions src/internals/doAp.js

This file was deleted.

Loading