base on The minimal javascript library to create flowcharts ✨
# Flowy
![Demo](https://media.giphy.com/media/dv1C56OywrP7Cn20nr/giphy.gif)
<br>A javascript library to create pretty flowcharts with ease ✨
[Dribbble](https://dribbble.com/shots/8576286-Flowy-Flowchart-Engine) | [Twitter](https://twitter.com/alyssaxuu/status/1199724989353730048) | [Live demo](https://alyssax.com/x/flowy)
Flowy makes creating WebApps with flowchart functionality an incredibly simple task. Build automation software, mind mapping tools, or simple programming platforms in minutes by implementing the library into your project.
> You can support this project (and many others) through [GitHub Sponsors](https://github.com/sponsors/alyssaxuu)! ❤️
Made by [Alyssa X](https://alyssax.com)
## Table of contents
- [Features](#features)
- [Installation](#installation)
- [Running Flowy](#running-flowy)
- [Initialization](#initialization)
- [Example](#example)
- [Callbacks](#callbacks)
- [On grab](#on-grab)
- [On release](#on-release)
- [On snap](#on-snap)
- [On rearrange](#on-rearrange)
- [Methods](#methods)
- [Get the flowchart data](#get-the-flowchart-data)
- [Import the flowchart data](#import-the-flowchart-data)
- [Delete all blocks](#delete-all-blocks)
## Features
Currently, Flowy supports the following:
- [x] Responsive drag and drop
- [x] Automatic snapping
- [x] Automatic scrolling
- [x] Block rearrangement
- [x] Delete blocks
- [x] Automatic block centering
- [x] Conditional snapping
- [x] Conditional block removal
- [x] Import saved files
- [x] Mobile support
- [x] Vanilla javascript (no dependencies)
- [ ] [npm install](https://github.com/alyssaxuu/flowy/issues/10)
You can suggest new features [here](https://github.com/alyssaxuu/flowy/issues)
## Installation
Adding Flowy to your WebApp is incredibly simple:
1. Link `flowy.min.js` and `flowy.min.css` to your project. Through jsDelivr:
```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/alyssaxuu/flowy/flowy.min.css">
<script src="https://cdn.jsdelivr.net/gh/alyssaxuu/flowy/flowy.min.js"></script>
```
2. Create a canvas element that will contain the flowchart (for example, `<div id="canvas"></div>`)
3. Create the draggable blocks with the `.create-flowy` class (for example, `<div class="create-flowy">Grab me</div>`)
## Running Flowy
### Initialization
```javascript
flowy(canvas, ongrab, onrelease, onsnap, onrearrange, spacing_x, spacing_y);
```
Parameter | Type | Description
--- | --- | ---
`canvas` | *javascript DOM element* | The element that will contain the blocks
`ongrab` | *function* (optional) | Function that gets triggered when a block is dragged
`onrelease` | *function* (optional) | Function that gets triggered when a block is released
`onsnap` | *function* (optional) | Function that gets triggered when a block snaps with another one
`onrearrange` | *function* (optional) | Function that gets triggered when blocks are rearranged
`spacing_x` | *integer* (optional) | Horizontal spacing between blocks (default 20px)
`spacing_y` | *integer* (optional) | Vertical spacing between blocks (default 80px)
To define the blocks that can be dragged, you need to add the class `.create-flowy`
### Example
**HTML**
```html
<div class="create-flowy">The block to be dragged</div>
<div id="canvas"></div>
```
**Javascript**
```javascript
var spacing_x = 40;
var spacing_y = 100;
// Initialize Flowy
flowy(document.getElementById("canvas"), onGrab, onRelease, onSnap, onRearrange, spacing_x, spacing_y);
function onGrab(block){
// When the user grabs a block
}
function onRelease(){
// When the user releases a block
}
function onSnap(block, first, parent){
// When a block snaps with another one
}
function onRearrange(block, parent){
// When a block is rearranged
}
```
## Callbacks
In order to use callbacks, you need to add the functions when initializing Flowy, as explained before.
### On grab
```javascript
function onGrab(block){
// When the user grabs a block
}
```
Gets triggered when a user grabs a block with the class `create-flowy`
Parameter | Type | Description
--- | --- | ---
`block` | *javascript DOM element* | The block that has been grabbed
### On release
```javascript
function onRelease(){
// When the user lets go of a block
}
```
Gets triggered when a user lets go of a block, regardless of whether it attaches or even gets released in the canvas.
### On snap
```javascript
function onSnap(block, first, parent){
// When a block can attach to a parent
return true;
}
```
Gets triggered when a block can attach to another parent block. You can either prevent the attachment, or allow it by using `return true;`
Parameter | Type | Description
--- | --- | ---
`block` | *javascript DOM element* | The block that has been grabbed
`first` | *boolean* | If true, the block that has been dragged is the first one in the canvas
`parent` | *javascript DOM element* | The parent the block can attach to
### On rearrange
```javascript
function onRearrange(block, parent){
// When a block is rearranged
return true;
}
```
Gets triggered when blocks are rearranged and are dropped anywhere in the canvas, without a parent to attach to. You can either allow the blocks to be deleted, or prevent it and thus have them re-attach to their previous parent using `return true;`
Parameter | Type | Description
--- | --- | ---
`block` | *javascript DOM element* | The block that has been grabbed
`parent` | *javascript DOM element* | The parent the block can attach to
## Methods
### Get the flowchart data
```javascript
// As an object
flowy.output();
// As a JSON string
JSON.stringify(flowy.output());
```
The JSON object that gets outputted looks like this:
```json
{
"html": "",
"blockarr": [],
"blocks": [
{
"id": 1,
"parent": 0,
"data": [
{
"name": "blockid",
"value": "1"
}
],
"attr": [
{
"id": "block-id",
"class": "block-class"
}
]
}
]
}
```
Here's what each property means:
Key | Value type | Description
--- | --- | ---
`html` | *string* | Contains the canvas data
`blockarr` | *array* | Contains the block array generated by the library (for import purposes)
`blocks` | *array* | Contains the readable block array
`id` | *integer* | Unique value that identifies a block
`parent` | *integer* | The `id` of the parent a block is attached to (-1 means the block has no parent)
`data` | *array of objects* | An array of all the inputs within a certain block
`name` | *string* | The name attribute of the input
`value` | *string* | The value attribute of the input
`attr` | *array of objects* | Contains all the data attributes of a certain block
### Import the flowchart data
```javascript
flowy.import(output)
```
Allows you to import entire flowcharts initially exported using the previous method, `flowy.output()`
Parameter | Type | Description
--- | --- | ---
`output` | *javascript DOM element* | The data from `flowy.output()`
#### Warning
This method accepts raw HTML and does **not** sanitize it, therefore this method is vulnerable to [XSS](https://owasp.org/www-community/attacks/DOM_Based_XSS). The _only_ safe use for this method is when the input is **absolutely** trusted, if the input is _not_ to be trusted the use this method can introduce a vulnerability in your system.
### Delete all blocks
To remove all blocks at once use:
```javascript
flowy.deleteBlocks()
```
Currently there is no method to individually remove blocks. The only way to go about it is by splitting branches manually.
#
Feel free to reach out to me through email at
[email protected] or [on Twitter](https://twitter.com/alyssaxuu) if you have any questions or feedback! Hope you find this useful 💜
", Assign "at most 3 tags" to the expected json: {"id":"9621","tags":[]} "only from the tags list I provide: [{"id":77,"name":"3d"},{"id":89,"name":"agent"},{"id":17,"name":"ai"},{"id":54,"name":"algorithm"},{"id":24,"name":"api"},{"id":44,"name":"authentication"},{"id":3,"name":"aws"},{"id":27,"name":"backend"},{"id":60,"name":"benchmark"},{"id":72,"name":"best-practices"},{"id":39,"name":"bitcoin"},{"id":37,"name":"blockchain"},{"id":1,"name":"blog"},{"id":45,"name":"bundler"},{"id":58,"name":"cache"},{"id":21,"name":"chat"},{"id":49,"name":"cicd"},{"id":4,"name":"cli"},{"id":64,"name":"cloud-native"},{"id":48,"name":"cms"},{"id":61,"name":"compiler"},{"id":68,"name":"containerization"},{"id":92,"name":"crm"},{"id":34,"name":"data"},{"id":47,"name":"database"},{"id":8,"name":"declarative-gui "},{"id":9,"name":"deploy-tool"},{"id":53,"name":"desktop-app"},{"id":6,"name":"dev-exp-lib"},{"id":59,"name":"dev-tool"},{"id":13,"name":"ecommerce"},{"id":26,"name":"editor"},{"id":66,"name":"emulator"},{"id":62,"name":"filesystem"},{"id":80,"name":"finance"},{"id":15,"name":"firmware"},{"id":73,"name":"for-fun"},{"id":2,"name":"framework"},{"id":11,"name":"frontend"},{"id":22,"name":"game"},{"id":81,"name":"game-engine "},{"id":23,"name":"graphql"},{"id":84,"name":"gui"},{"id":91,"name":"http"},{"id":5,"name":"http-client"},{"id":51,"name":"iac"},{"id":30,"name":"ide"},{"id":78,"name":"iot"},{"id":40,"name":"json"},{"id":83,"name":"julian"},{"id":38,"name":"k8s"},{"id":31,"name":"language"},{"id":10,"name":"learning-resource"},{"id":33,"name":"lib"},{"id":41,"name":"linter"},{"id":28,"name":"lms"},{"id":16,"name":"logging"},{"id":76,"name":"low-code"},{"id":90,"name":"message-queue"},{"id":42,"name":"mobile-app"},{"id":18,"name":"monitoring"},{"id":36,"name":"networking"},{"id":7,"name":"node-version"},{"id":55,"name":"nosql"},{"id":57,"name":"observability"},{"id":46,"name":"orm"},{"id":52,"name":"os"},{"id":14,"name":"parser"},{"id":74,"name":"react"},{"id":82,"name":"real-time"},{"id":56,"name":"robot"},{"id":65,"name":"runtime"},{"id":32,"name":"sdk"},{"id":71,"name":"search"},{"id":63,"name":"secrets"},{"id":25,"name":"security"},{"id":85,"name":"server"},{"id":86,"name":"serverless"},{"id":70,"name":"storage"},{"id":75,"name":"system-design"},{"id":79,"name":"terminal"},{"id":29,"name":"testing"},{"id":12,"name":"ui"},{"id":50,"name":"ux"},{"id":88,"name":"video"},{"id":20,"name":"web-app"},{"id":35,"name":"web-server"},{"id":43,"name":"webassembly"},{"id":69,"name":"workflow"},{"id":87,"name":"yaml"}]" returns me the "expected json"