Skip to content

Commit

Permalink
feat(children): add children constructors
Browse files Browse the repository at this point in the history
  • Loading branch information
davidbonnet committed Jan 17, 2019
1 parent d959398 commit 33c269d
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 9 deletions.
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ The `realue` module exposes the following functions:
> ⬇️ `{ value? }`
Sets `value` to `defaultValue` if `value` is `null`.
Sets `value` to `defaultValue` if `value` is `nil`.

#### `transformable`

Expand Down Expand Up @@ -196,6 +196,8 @@ Uses `title` as console group (defaults to decorated component name).

Removes provided `propNames`.

### Decorator constructors

#### `onPropsChange(shouldHandleOrKeys, handler, callOnMount = true)`

Similar to `withPropsOnChange`, except that the values of the `handler` are not merged into the props.
Expand Down Expand Up @@ -239,6 +241,24 @@ The return value of the optional parent prop `[onPullName](newValue, previousVal
Injects prop `[onCycleName](payload)` that cycles the value of prop `[name]` through the values found in prop `[valuesName]` which default to `[false, true]`.
Calls `[onChangeName](value, name, payload)` with `name` taken from prop `[nameName]` or `name`.

#### `withChildren(Component, childProps?, shouldUpdateOrKeys?, valueName?)`

> ⬆️ `{ [valueName]? }`
> ⬇️ `{ children }`
Builds an array that maps every item from the `[valueName]` prop with the result of `<Component {...childProps(props)(itemValue, itemIndex)}` and injects it as a `children` prop.
The prop is only updated if `shouldUpdateOrKeys` returns `true` or if a prop whose name is listed in it changes.

#### `withChild(Component, childProps?, shouldUpdateOrKeys?, valueName?)`

> ⬆️ `{ [valueName]? }`
> ⬇️ `{ children }`
Builds an element from the provided `Component` with the props from `childProps(props)` and injects it as a `children` prop.
The prop is only updated if `shouldUpdateOrKeys` returns `true` or if a prop whose name is listed in it changes.

### Type-oriented decorators

#### `object`
Expand Down
12 changes: 8 additions & 4 deletions demo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import {
toggledEditing,
transformable,
syncedFocus,
withChildren,
withChild,
} from '../src'

const Text = compose(
Expand Down Expand Up @@ -105,11 +107,12 @@ const Item = compose(
const Items = compose(
pure,
array,
)(function Items({ value, item, onAddItem }) {
withChildren(Item),
)(function Items({ value, children, onAddItem }) {
return $(
'div',
null,
$('ul', null, map(value, (value, index) => $(Item, item(index)))),
$('ul', null, children),
onAddItem && $(ItemCreator, { onChange: onAddItem, name: value.length }),
)
})
Expand Down Expand Up @@ -176,7 +179,8 @@ const EditedItems = compose(
onToggleEditing()
},
}),
)(function EditedItems({ value, onChange, editing, onToggleEditing }) {
withChild(Items),
)(function EditedItems({ children, editing, onToggleEditing }) {
return $(
'div',
null,
Expand All @@ -185,7 +189,7 @@ const EditedItems = compose(
onChange: onToggleEditing,
label: 'Edit',
}),
$(Items, { value, onChange }),
children,
)
})

Expand Down
56 changes: 52 additions & 4 deletions src/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import {
every,
get,
indexOf,
isFunction,
isString,
keys,
memoize,
omit,
slice,
uniq,
upperFirst,
map,
identity,
} from 'lodash'
import {
branch,
Expand Down Expand Up @@ -199,9 +200,10 @@ export function onPropsChange(shouldHandleOrKeys, handler, callOnMount = true) {
Similar to `withPropsOnChange`, except that the values of the `handler` are not merged into the props.
The `handler` is called when the component is first mounted if `callOnMount` is `true` (default value).
*/
const shouldHandle = isFunction(shouldHandleOrKeys)
? shouldHandleOrKeys
: (props, nextProps) => !same(props, nextProps, shouldHandleOrKeys)
const shouldHandle =
typeof shouldHandleOrKeys === 'function'
? shouldHandleOrKeys
: (props, nextProps) => !same(props, nextProps, shouldHandleOrKeys)
return lifecycle({
componentWillMount() {
if (callOnMount) {
Expand Down Expand Up @@ -361,6 +363,52 @@ export function cycledProp(options) {
})
}

const DEFAULT_KEYS = ['value', 'name', 'onChange']
const DEFAULT_CHILDREN_PROPS = ({ item }) => (value, index) => item(index)

export function withChildren(
Component,
childProps = DEFAULT_CHILDREN_PROPS,
shouldUpdateOrKeys = DEFAULT_KEYS,
valueName = 'value',
) {
/*
Builds an array that maps every item from the `[valueName]` prop with the result of `<Component {...childProps(props)(itemValue, itemIndex)}` and injects it as a `children` prop.
The prop is only updated if `shouldUpdateOrKeys` returns `true` or if a prop whose name is listed in it changes.
Example:
function Item({ value }) {
return $('li', null, value)
}
const List = withChildren(Item, () => value => ({ value }))('ul')
*/
return withPropsOnChange(shouldUpdateOrKeys, props => ({
children: map(
props[valueName],
(childProps => (value, index) =>
$(Component, {
key: index,
...childProps(value, index),
}))(childProps(props)),
),
}))
}

export function withChild(
Component,
childProps = identity,
shouldUpdateOrKeys = DEFAULT_KEYS,
) {
/*
Builds a child from the provided `Component` with the props from `childProps(props)` and injects it as a `children` prop.
The prop is only updated if `shouldUpdateOrKeys` returns `true` or if a prop whose name is listed in it changes.
*/
return withPropsOnChange(shouldUpdateOrKeys, props => ({
children: $(Component, childProps(props)),
}))
}

export function lazyProperty(object, propertyName, valueBuilder) {
/*
Returns `object[propertyName]` if not `nil`, otherwise sets the result of `valueBuilder(object)` to it and returns it.
Expand Down

0 comments on commit 33c269d

Please sign in to comment.