# BoxOButtons

* Drag and drop things into this box and they display as buttons. 

* Box highlights when you drag compatible items over it 
(Use __dataKey__ property to tell it what drag items are compatible).

* Drag and drop buttons within the box to re-order them.

* Click the 'X' on a button to delete it.

* Click a button to select it (for editing details or whatever).
 
* Designed to work with 
[react-drag-drop-container](https://github.com/peterh32/react-drag-drop-container)
component  (though it would be pretty easy to adapt to straight HTML 5 
d-n-d if you had to).

* Callbacks for __buttonAdded__, __buttonDeleted__, 
__buttonsReordered__, and __buttonSelected__ enable the parent
component to approve or refuse any action -- the updated buttons are 
passed in the callback, and the parent can apply them or not.

## Demo & Examples

Live demo: [peterh32.github.io/react-box-o-buttons](http://peterh32.github.io/react-box-o-buttons/)

To build the examples locally, run:

```
npm install
npm start
```

Then open [`localhost:8000`](http://localhost:8000) in a browser.


## Installation


```
npm install react-box-o-buttons --save
```


## Usage

Create a parent component and have it render BoxOButtons:

```
var BoxOButtons = require('react-box-o-buttons');

class DummyParent extends React.Component {
      constructor(props) {
        super(props);
        // add some buttons
        this.state = {
            buttons: ['frog', 'bug', 'newt'].map(
                (label, i)=>{return {'label': label, 'id': i}}
            ),
            selectedButtonIndex: null
        };
    }

    render() {
        return (
            <BoxOButtons
                buttons={this.state.buttons}
                selectedButton={this.state.selectedButtonIndex}
                dataKey="foo"
            />
        );
    }
}

export default DummyParent
```
The __dataKey__ field is how your BoxOButtons will recognize compatible drop objects.

Stick the parent element in a page and you should see three buttons:frog, bug, and newt.
```
<DummyParent />
```

In the parent, add methods to handle the four callbacks from BoxOButtons:
__buttonAdded__, __buttonDeleted__, __buttonsReordered__, 
and __buttonSelected__.
```
handleButtonAdded(updatedButtons, newButton) {
    //... test if newButton is acceptable and then ...
    this.setState({buttons: updatedButtons});
    return true;
}

handleButtonDeleted(updatedButtons, deletedButton, newSelectedButton, force) {
    //... test if it's ok to delete deletedButton, or if 'force' is true, then ...
    this.setState({buttons: updatedButtons, selectedButtonIndex: newSelectedButton});
    return true;
}

handleButtonsReordered(updatedButtons, newSelectedButton) {
    //... if this is ok then ...
    this.setState({buttons: updatedButtons, selectedButtonIndex: newSelectedButton});
    return true;
}

handleButtonSelected(n) {
    //... test if n is ok to highlight then ...
    this.setState({selectedButtonIndex: n});
    return true;
}
```
Wire them up in your render method:
```
  render() {
    return (
      <BoxOButtons
        buttons={this.state.buttons}
        dataKey="foo"
        buttonAdded={this.handleButtonAdded}
        buttonDeleted={this.handleButtonDeleted}
        buttonsReordered={this.handleButtonsReordered}
        buttonSelected={this.handleButtonSelected}
      />
    );
  }

```
Remember to bind them in your constructor:
```
		this.handleButtonAdded = this.handleButtonAdded.bind(this);
		this.handleButtonDeleted = this.handleButtonDeleted.bind(this);
		this.handleButtonsReordered = this.handleButtonsReordered.bind(this);
		this.handleButtonSelected = this.handleButtonSelected.bind(this);
```
Now go back to your page. Click an 'x' on one of the buttons and it should disappear.
You can also drag and drop buttons to reorder them.

In your page, add some items you can drag into the box, using DragDropContainer 
(DragDropContainer is a required dependency):
```
var DragDropContainer = require('react-drag-drop-container');

...


<DragDropContainer dataKey="foo" dragData={{label: 'alligator'}}>
  Gator
</DragDropContainer>

<DragDropContainer dataKey="foo" dragData={{label: 'fish'}}>
  Fish
</DragDropContainer>

<DragDropContainer dataKey="foo" dragData={{label: 'mosquito'}}>
  Mosquito
</DragDropContainer>
```
In your page, drag and drop these into the BoxOButtons and they should display as buttons.

If you have multiple compatible boxes (compatible => matching dataKey properties) then
you can also drag elements between them.

### Properties
#### Callbacks (all required)
BoxOButtons uses callbacks to tell the parent component about changes such as deletions,
additions, and reordering.

The callbacks then must 
1) decide whether to accept the change.
2) make changes in the __buttons__ and __selectedButton__ properties if the change is accepted.
3) return true if the change was accepted, false otherwise.
##### buttonAdded
Callback when item is dropped. Args:
updatedButtons (array) and newButton (object).
##### buttonDeleted
Callback when button is deleted. Args: updatedButtons (array), deletedButton (object), 
newSelectedButton (since deleting a button can change this), and 
force (boolean, set true if you want to skip any confirmations etc.).
##### buttonsReordered
Callback when buttons are re-ordered. Args: updatedButtons (array) and newSelectedButton (object)
(since rearranging the buttons can change the selection)
##### buttonSelected
Callback when a button is selected (highlighted). Arg: button index (int) or null if nothing 
is selected.

#### Other Properties
##### placeholderText
Optional text to display when the box is empty.
##### dataKey
The key to use for retrieving drag data from a dragged element. 
This also determines which dragged elements are considered 'compatible' with this box and
cause it to highlight when dragged over. Defaults to 'data'.
##### labelField
The field to use as the button label; defaults to 'label'.
##### buttons
Array representing the current set of buttons.
##### selectedButton
0-based index of selected button, or null if no button is selected.


### Notes

##### CSS
CSS will be inserted in the `<head>` section of your document in a `<style>` element with 
id="react-box-o-buttons". It should be fairly easy to overwrite styles if you want the buttons
or the box to look different. (I suppose we could also add properties to let you set the 
classnames to something else.)

## Development (`src`, `lib` and the build process)

**NOTE:** The source code for the component is in `src`. A transpiled CommonJS version (generated with Babel) is available in `lib` for use with node.js, browserify and webpack. A UMD bundle is also built to `dist`, which can be included without the need for any build system.

To build, watch and serve the examples (which will also watch the component source), run `npm start`. If you just want to watch changes to `src` and rebuild `lib`, run `npm run watch` (this is useful if you are working with `npm link`).

## License

Proprietary, not licensed for re-use.

Copyright (c) 2017 Medidata.

