npm package: @seracio/thirdact
Description: > A set of react components that encapsulate d3-selection api
Author: nmondon
Latest version: 0.9.0

# thirdAct
> A set of react components that encapsulate d3-selection api

## Disclaimer

`thirdact` is still on a very early stage. It is born of our frustration of using the different workflows that mix `React` and `d3` together.

d3's [general update pattern](https://bl.ocks.org/mbostock/3808218) is quite incompatible with React's data binding as React only keeps track of the current data. 

This is an experimental attempt to express the `d3-selection` API in a jsx style. This way, we can mix d3 and React into the `render` method, without using React hooks such as `componentDidUpdate` to manage d3 instructions.

* It uses the `d3-selection` API under the hood, we are just transforming jsx components to d3 instructions
* React's Virtual DOM is not involved or aware of the generated DOM, but we isolate it into an empty React element


## A first overview

you can find a full example [here](https://github.com/seracio/thirdact-example)

```javascript
import { scaleLinear } from 'd3-scale';
import React from 'react';
import {
    SelectAll,
    Transition,
    Selection,
    Append,
    Remove
} from '@seracio/thirdact';

const App = ({ data }) => {
    const size = 500;

    const scaleX = scaleLinear()
        .domain([0, data.length - 1])
        .range([100, 400]);

    return (
        <svg
            preserveAspectRatio="xMinYMin meet"
            viewBox={`0 0 ${size} ${size}`}
        >
            <text
                x={size / 2}
                y={50}
                style={{
                    alignmentBaseline: 'middle',
                    textAnchor: 'middle',
                    fontFamily: 'sans-serif'
                }}
            >
                Hello thirdact
            </text>
            <SelectAll data={data} selector={'.circle'} root={<g />}>                
                <Selection type="enter">
                    <Append>
                        {/* use react conventions to specify your data */}
                        <circle
                            className="circle"
                            cx={(d, i) => scaleX(i)}
                            cy={100}
                            r={10}
                            style={{ fill: 'transparent' }}
                        />
                    </Append>
                    {/* on a transition, only specify the attr, styles or events you want to update */}
                    <Transition duration={1000}>
                        <circle cy={200} style={{ fill: d => d }} />
                    </Transition>
                </Selection>
                <Selection type="update">
                    <Transition duration={1000}>
                        <circle cx={(d, i) => scaleX(i)} />
                    </Transition>
                </Selection>
                <Selection type="exit">
                    {/* you can chain transitions */}
                    <Transition duration={500}>
                        <circle cy={250} r={5} />
                    </Transition>
                    <Transition duration={500}>
                        <circle cy={400} style={{ fill: 'transparent' }} />
                    </Transition>
                    <Remove />
                </Selection>
            </SelectAll>        
        </svg>
    );
};
```

## Install

```bash
yarn add react react-dom d3-selection d3-selection-multi d3-transition lodash.kebabcase
yarn add @seracio/thirdact
```

## Usage

First of all - and if you use d3 modules - you'll need to patch `d3-selection-multi` and `d3-transition` to d3 in your root file: 

```javascript
import * as d3 from 'd3-selection';
import 'd3-selection-multi';
import 'd3-transition';
```

Then you can import the different components of `thirdact`: 

```javascript
import {
    SelectAll,
    Transition,
    Selection,
    Append,
    Remove
} from '@seracio/thirdact';
```

## API

### SelectAll

**SelectAll** is the root component, it takes several props: 

| name    | type    | description    |
|:-------:|:-------:|:--------------:|
| data | Array<any> |   |
| selector | string | the equivalent of the d3's selector in d3.selectAll(selector) |
| getKey | (d,i) => string | an optional function to get the key of the current datum |
| root | React$Element | the react element where the DOM managed by d3 will be |

### Selection

**Selection** is the only component allowed as direct child of **SelectAll**.  
It allows you to use d3's data join  by specifying a type props:

| name   | type    | description    |
|:------:|:-------:|:--------------:|
|type|string|*enter* or *update* or *exit* (and soon *enter+exit*)|

----

Once you have split your data into selections thanks to the general update pattern, you can finally 
do the mapping between your data and the DOM thans to modifiers: 

* **Append** to create items
* **Remove** to remove them
* **Transition** for updating them
* **Call** to apply them a function

You can chain modifiers.

### Append

**Append** is used to add new tags to the DOM, define their attributes and styles or attach events.
`thirdact`'s API is here quite different to its d3 counterpart:

```javascript
<Append>
    <rect className="toto" 
        x={0} 
        y={(d,i) => i*10} 
        width={(d,i) => d} 
        height={10} 
        styles={{strokeWidth: 2}}
        onClick={(d,i) => console.log(d)}
        />
</Append>
```
will be transcripted on:

```javascript
selection.append('rect')
    .attr('class', 'toto')
    .attr('x', 0)
    .attr('y', i*10)
    .attr('width', (d,i) => d)
    .attr('height', 10)
    .style('stroke-width', 2)
    .on('click', (d,i) => console.log(d));
```

### Transitions

**Transition** currently support these props:

| name   | type    | description    |
|:------:|:-------:|:--------------:|
|duration|number|   |

You just have to specify the attributes or styles you want to update:

```javascript
<Transition duration={500}>
    <path
        style={{fill: 'blue'}}
        tween-d={tween}
        />
</Transition>
```

> Note the equivalent of **d3's attrTween**: **tween-[attr name]**


### Remove

**Remove** has no props

### Call

**Call** uses [a function as a children](https://reactjs.org/docs/jsx-in-depth.html#functions-as-children)

```javascript
<Selection type="enter">
    <Append>
        {/**/}
    </Append>
    <Call>
    {function(selection){
        // ...
    }}
    </Call>
</Selection>
```

### Chaining transformations

```javascript
<Selection type="enter">
    <Append>
        <circle
            className="circle"
            cx={(d, i) => scaleX(i)}
            cy={100}
            r={10}
            style={{ fill: 'transparent' }}
        />
    </Append>
    <Transition duration={1000}>
        <circle cy={200} style={{ fill: d => d }} />
    </Transition>
</Selection>
```

## License

MIT License

Copyright (c) 2017 serac.io 

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.