├── .gitattributes ├── .github └── FUNDING.yml ├── LICENSE ├── README.md ├── demo ├── assets │ ├── .DS_Store │ ├── action.svg │ ├── actionblue.svg │ ├── actionorange.svg │ ├── arrow.svg │ ├── checkoff.svg │ ├── checkon.svg │ ├── close.svg │ ├── closeleft.svg │ ├── database.svg │ ├── databaseorange.svg │ ├── dropdown.svg │ ├── error.svg │ ├── errorblue.svg │ ├── errorred.svg │ ├── eye.svg │ ├── eyeblue.svg │ ├── grabme.svg │ ├── heart.svg │ ├── log.svg │ ├── logred.svg │ ├── meta.png │ ├── more.svg │ ├── search.svg │ ├── tile.png │ ├── time.svg │ ├── timeblue.svg │ ├── twitter.svg │ └── twitterorange.svg ├── flowy.min.css ├── flowy.min.js ├── index.html ├── main.js └── styles.css ├── engine ├── flowy.css └── flowy.js ├── flowy.min.css └── flowy.min.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: alyssaxuu 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Alyssa X 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Flowy 3 | 4 | 5 | ![Demo](https://media.giphy.com/media/dv1C56OywrP7Cn20nr/giphy.gif) 6 |
A javascript library to create pretty flowcharts with ease ✨ 7 | 8 | [Dribbble](https://dribbble.com/shots/8576286-Flowy-Flowchart-Engine) | [Twitter](https://twitter.com/alyssaxuu/status/1199724989353730048) | [Live demo](https://alyssax.com/x/flowy) 9 | 10 | 11 | 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. 12 | 13 | > You can support this project (and many others) through [GitHub Sponsors](https://github.com/sponsors/alyssaxuu)! ❤️ 14 | 15 | Made by [Alyssa X](https://alyssax.com) 16 | 17 | ## Table of contents 18 | - [Features](#features) 19 | - [Installation](#installation) 20 | - [Running Flowy](#running-flowy) 21 | - [Initialization](#initialization) 22 | - [Example](#example) 23 | - [Callbacks](#callbacks) 24 | - [On grab](#on-grab) 25 | - [On release](#on-release) 26 | - [On snap](#on-snap) 27 | - [On rearrange](#on-rearrange) 28 | - [Methods](#methods) 29 | - [Get the flowchart data](#get-the-flowchart-data) 30 | - [Import the flowchart data](#import-the-flowchart-data) 31 | - [Delete all blocks](#delete-all-blocks) 32 | 33 | 34 | ## Features 35 | Currently, Flowy supports the following: 36 | 37 | - [x] Responsive drag and drop 38 | - [x] Automatic snapping 39 | - [x] Automatic scrolling 40 | - [x] Block rearrangement 41 | - [x] Delete blocks 42 | - [x] Automatic block centering 43 | - [x] Conditional snapping 44 | - [x] Conditional block removal 45 | - [x] Import saved files 46 | - [x] Mobile support 47 | - [x] Vanilla javascript (no dependencies) 48 | - [ ] [npm install](https://github.com/alyssaxuu/flowy/issues/10) 49 | 50 | You can suggest new features [here](https://github.com/alyssaxuu/flowy/issues) 51 | 52 | 53 | ## Installation 54 | Adding Flowy to your WebApp is incredibly simple: 55 | 56 | 1. Link `flowy.min.js` and `flowy.min.css` to your project. Through jsDelivr: 57 | ```html 58 | 59 | 60 | ``` 61 | 2. Create a canvas element that will contain the flowchart (for example, `
`) 62 | 3. Create the draggable blocks with the `.create-flowy` class (for example, `
Grab me
`) 63 | 64 | ## Running Flowy 65 | 66 | ### Initialization 67 | ```javascript 68 | flowy(canvas, ongrab, onrelease, onsnap, onrearrange, spacing_x, spacing_y); 69 | ``` 70 | 71 | Parameter | Type | Description 72 | --- | --- | --- 73 | `canvas` | *javascript DOM element* | The element that will contain the blocks 74 | `ongrab` | *function* (optional) | Function that gets triggered when a block is dragged 75 | `onrelease` | *function* (optional) | Function that gets triggered when a block is released 76 | `onsnap` | *function* (optional) | Function that gets triggered when a block snaps with another one 77 | `onrearrange` | *function* (optional) | Function that gets triggered when blocks are rearranged 78 | `spacing_x` | *integer* (optional) | Horizontal spacing between blocks (default 20px) 79 | `spacing_y` | *integer* (optional) | Vertical spacing between blocks (default 80px) 80 | 81 | To define the blocks that can be dragged, you need to add the class `.create-flowy` 82 | 83 | ### Example 84 | **HTML** 85 | ```html 86 |
The block to be dragged
87 |
88 | ``` 89 | **Javascript** 90 | ```javascript 91 | var spacing_x = 40; 92 | var spacing_y = 100; 93 | // Initialize Flowy 94 | flowy(document.getElementById("canvas"), onGrab, onRelease, onSnap, onRearrange, spacing_x, spacing_y); 95 | function onGrab(block){ 96 | // When the user grabs a block 97 | } 98 | function onRelease(){ 99 | // When the user releases a block 100 | } 101 | function onSnap(block, first, parent){ 102 | // When a block snaps with another one 103 | } 104 | function onRearrange(block, parent){ 105 | // When a block is rearranged 106 | } 107 | ``` 108 | ## Callbacks 109 | In order to use callbacks, you need to add the functions when initializing Flowy, as explained before. 110 | ### On grab 111 | ```javascript 112 | function onGrab(block){ 113 | // When the user grabs a block 114 | } 115 | ``` 116 | Gets triggered when a user grabs a block with the class `create-flowy` 117 | 118 | Parameter | Type | Description 119 | --- | --- | --- 120 | `block` | *javascript DOM element* | The block that has been grabbed 121 | 122 | ### On release 123 | ```javascript 124 | function onRelease(){ 125 | // When the user lets go of a block 126 | } 127 | ``` 128 | Gets triggered when a user lets go of a block, regardless of whether it attaches or even gets released in the canvas. 129 | 130 | ### On snap 131 | ```javascript 132 | function onSnap(block, first, parent){ 133 | // When a block can attach to a parent 134 | return true; 135 | } 136 | ``` 137 | Gets triggered when a block can attach to another parent block. You can either prevent the attachment, or allow it by using `return true;` 138 | 139 | Parameter | Type | Description 140 | --- | --- | --- 141 | `block` | *javascript DOM element* | The block that has been grabbed 142 | `first` | *boolean* | If true, the block that has been dragged is the first one in the canvas 143 | `parent` | *javascript DOM element* | The parent the block can attach to 144 | 145 | ### On rearrange 146 | ```javascript 147 | function onRearrange(block, parent){ 148 | // When a block is rearranged 149 | return true; 150 | } 151 | ``` 152 | 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;` 153 | 154 | Parameter | Type | Description 155 | --- | --- | --- 156 | `block` | *javascript DOM element* | The block that has been grabbed 157 | `parent` | *javascript DOM element* | The parent the block can attach to 158 | 159 | ## Methods 160 | ### Get the flowchart data 161 | ```javascript 162 | // As an object 163 | flowy.output(); 164 | // As a JSON string 165 | JSON.stringify(flowy.output()); 166 | ``` 167 | The JSON object that gets outputted looks like this: 168 | ```json 169 | { 170 | "html": "", 171 | "blockarr": [], 172 | "blocks": [ 173 | { 174 | "id": 1, 175 | "parent": 0, 176 | "data": [ 177 | { 178 | "name": "blockid", 179 | "value": "1" 180 | } 181 | ], 182 | "attr": [ 183 | { 184 | "id": "block-id", 185 | "class": "block-class" 186 | } 187 | ] 188 | } 189 | ] 190 | } 191 | ``` 192 | 193 | Here's what each property means: 194 | 195 | Key | Value type | Description 196 | --- | --- | --- 197 | `html` | *string* | Contains the canvas data 198 | `blockarr` | *array* | Contains the block array generated by the library (for import purposes) 199 | `blocks` | *array* | Contains the readable block array 200 | `id` | *integer* | Unique value that identifies a block 201 | `parent` | *integer* | The `id` of the parent a block is attached to (-1 means the block has no parent) 202 | `data` | *array of objects* | An array of all the inputs within a certain block 203 | `name` | *string* | The name attribute of the input 204 | `value` | *string* | The value attribute of the input 205 | `attr` | *array of objects* | Contains all the data attributes of a certain block 206 | ### Import the flowchart data 207 | ```javascript 208 | flowy.import(output) 209 | ``` 210 | Allows you to import entire flowcharts initially exported using the previous method, `flowy.output()` 211 | 212 | Parameter | Type | Description 213 | --- | --- | --- 214 | `output` | *javascript DOM element* | The data from `flowy.output()` 215 | 216 | #### Warning 217 | 218 | 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. 219 | 220 | ### Delete all blocks 221 | To remove all blocks at once use: 222 | ```javascript 223 | flowy.deleteBlocks() 224 | ``` 225 | Currently there is no method to individually remove blocks. The only way to go about it is by splitting branches manually. 226 | # 227 | Feel free to reach out to me through email at hi@alyssax.com or [on Twitter](https://twitter.com/alyssaxuu) if you have any questions or feedback! Hope you find this useful 💜 228 | -------------------------------------------------------------------------------- /demo/assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alyssaxuu/flowy/e977380e3a84043aabb32cfd25b813455df5ec6f/demo/assets/.DS_Store -------------------------------------------------------------------------------- /demo/assets/action.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /demo/assets/actionblue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /demo/assets/actionorange.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /demo/assets/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /demo/assets/checkoff.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /demo/assets/checkon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo/assets/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo/assets/closeleft.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo/assets/database.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /demo/assets/databaseorange.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /demo/assets/dropdown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /demo/assets/error.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /demo/assets/errorblue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /demo/assets/errorred.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /demo/assets/eye.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo/assets/eyeblue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo/assets/grabme.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo/assets/heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /demo/assets/log.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo/assets/logred.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo/assets/meta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alyssaxuu/flowy/e977380e3a84043aabb32cfd25b813455df5ec6f/demo/assets/meta.png -------------------------------------------------------------------------------- /demo/assets/more.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /demo/assets/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo/assets/tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alyssaxuu/flowy/e977380e3a84043aabb32cfd25b813455df5ec6f/demo/assets/tile.png -------------------------------------------------------------------------------- /demo/assets/time.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo/assets/timeblue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo/assets/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/assets/twitterorange.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/flowy.min.css: -------------------------------------------------------------------------------- 1 | .dragging{z-index:111!important}.block{position:absolute;z-index:9}.indicator{width:12px;height:12px;border-radius:60px;background-color:#217ce8;margin-top:-5px;opacity:1;transition:all .3s cubic-bezier(.05,.03,.35,1);transform:scale(1);position:absolute;z-index:2}.invisible{opacity:0!important;transform:scale(0)}.indicator:after{content:"";display:block;width:12px;height:12px;background-color:#217ce8;transform:scale(1.7);opacity:.2;border-radius:60px}.arrowblock{position:absolute;width:100%;overflow:visible;pointer-events:none}.arrowblock svg{width: -webkit-fill-available;overflow: visible;} -------------------------------------------------------------------------------- /demo/flowy.min.js: -------------------------------------------------------------------------------- 1 | var flowy=function(e,t,l,i,n,o,r){t||(t=function(){}),l||(l=function(){}),i||(i=function(){return!0}),n||(n=function(){return!1}),o||(o=20),r||(r=80),Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector),Element.prototype.closest||(Element.prototype.closest=function(e){var t=this;do{if(Element.prototype.matches.call(t,e))return t;t=t.parentElement||t.parentNode}while(null!==t&&1===t.nodeType);return null});var d=!1;function c(e,t,l){return i(e,t,l)}function a(e,t){return n(e,t)}flowy.load=function(){if(!d){d=!0;var i=[],n=[],s=e,u=0,p=0;"absolute"!=window.getComputedStyle(s).position&&"fixed"!=window.getComputedStyle(s).position||(u=s.getBoundingClientRect().left,p=s.getBoundingClientRect().top);var g,w,f,h,y,C,v=!1,m=o,B=r,x=0,R=!1,S=!1,b=0,L=document.createElement("DIV");L.classList.add("indicator"),L.classList.add("invisible"),s.appendChild(L),flowy.import=function(e){s.innerHTML=e.html;for(var t=0;t1&&(D(),E())},flowy.output=function(){var e={html:s.innerHTML,blockarr:i,blocks:[]};if(i.length>0){for(var t=0;t