├── .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 | 
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 |
4 |
--------------------------------------------------------------------------------
/demo/assets/actionblue.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/demo/assets/actionorange.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/demo/assets/arrow.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/demo/assets/checkoff.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/demo/assets/checkon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/demo/assets/close.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/demo/assets/closeleft.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/demo/assets/database.svg:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/demo/assets/databaseorange.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/demo/assets/dropdown.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/demo/assets/error.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/demo/assets/errorblue.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/demo/assets/errorred.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/demo/assets/eye.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/demo/assets/eyeblue.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/demo/assets/grabme.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/demo/assets/heart.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/demo/assets/log.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/demo/assets/logred.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/demo/assets/meta.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyssaxuu/flowy/e977380e3a84043aabb32cfd25b813455df5ec6f/demo/assets/meta.png
--------------------------------------------------------------------------------
/demo/assets/more.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/demo/assets/search.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/demo/assets/tile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alyssaxuu/flowy/e977380e3a84043aabb32cfd25b813455df5ec6f/demo/assets/tile.png
--------------------------------------------------------------------------------
/demo/assets/time.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/demo/assets/timeblue.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/demo/assets/twitter.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/demo/assets/twitterorange.svg:
--------------------------------------------------------------------------------
1 |
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"},flowy.beginDrag=function(e){if("absolute"!=window.getComputedStyle(s).position&&"fixed"!=window.getComputedStyle(s).position||(u=s.getBoundingClientRect().left,p=s.getBoundingClientRect().top),e.targetTouches?(y=e.changedTouches[0].clientX,C=e.changedTouches[0].clientY):(y=e.clientX,C=e.clientY),3!=e.which&&e.target.closest(".create-flowy")){h=e.target.closest(".create-flowy");var l=e.target.closest(".create-flowy").cloneNode(!0);e.target.closest(".create-flowy").classList.add("dragnow"),l.classList.add("block"),l.classList.remove("create-flowy"),0===i.length?(l.innerHTML+="",document.body.appendChild(l),g=document.querySelector(".blockid[value='"+i.length+"']").parentNode):(l.innerHTML+="",document.body.appendChild(l),g=document.querySelector(".blockid[value='"+(parseInt(Math.max.apply(Math,i.map(e=>e.id)))+1)+"']").parentNode),n=e.target.closest(".create-flowy"),t(n),g.classList.add("dragging"),v=!0,w=y-e.target.closest(".create-flowy").getBoundingClientRect().left,f=C-e.target.closest(".create-flowy").getBoundingClientRect().top,g.style.left=y-w+"px",g.style.top=C-f+"px"}var n},flowy.endDrag=function(e){if(3!=e.which&&(v||R))if(S=!1,l(),document.querySelector(".indicator").classList.contains("invisible")||document.querySelector(".indicator").classList.add("invisible"),v&&(h.classList.remove("dragnow"),g.classList.remove("dragging")),0===parseInt(g.querySelector(".blockid").value)&&R)X("rearrange");else if(v&&0==i.length&&g.getBoundingClientRect().top+window.scrollY>s.getBoundingClientRect().top+window.scrollY&&g.getBoundingClientRect().left+window.scrollX>s.getBoundingClientRect().left+window.scrollX)X("drop");else if(v&&0==i.length)q();else if(v)for(var t=i.map(e=>e.id),o=0;oe.id),o=0;oe.id==t[o])[0])){v=!1,g.classList.remove("dragging"),T(g,t.indexOf(b),t);break}R=!1,n=[],v=!1,q();break}}},flowy.moveBlock=function(e){if(e.targetTouches?(y=e.targetTouches[0].clientX,C=e.targetTouches[0].clientY):(y=e.clientX,C=e.clientY),S){R=!0,g.classList.add("dragging");var t=parseInt(g.querySelector(".blockid").value);b=i.filter(e=>e.id==t)[0].parent,n.push(i.filter(e=>e.id==t)[0]),i=i.filter(function(e){return e.id!=t}),0!=t&&document.querySelector(".arrowid[value='"+t+"']").parentNode.remove();for(var l=i.filter(e=>e.parent==t),o=!1,r=[],d=[];!o;){for(var c=0;ce.id==l[c].id)[0]);const e=document.querySelector(".blockid[value='"+l[c].id+"']").parentNode,t=document.querySelector(".arrowid[value='"+l[c].id+"']").parentNode;e.style.left=e.getBoundingClientRect().left+window.scrollX-(g.getBoundingClientRect().left+window.scrollX)+"px",e.style.top=e.getBoundingClientRect().top+window.scrollY-(g.getBoundingClientRect().top+window.scrollY)+"px",t.style.left=t.getBoundingClientRect().left+window.scrollX-(g.getBoundingClientRect().left+window.scrollX)+"px",t.style.top=t.getBoundingClientRect().top+window.scrollY-(g.getBoundingClientRect().top+window.scrollY)+"px",g.appendChild(e),g.appendChild(t),r.push(l[c].id),d.push(l[c].id)}0==r.length?o=!0:(l=i.filter(e=>r.includes(e.parent)),r=[])}for(c=0;ce.parent==t).length;c++){var a=i.filter(e=>e.parent==t)[c];i=i.filter(function(e){return e.id!=a})}for(c=0;c1&&D(),S=!1}if(v?(g.style.left=y-w+"px",g.style.top=C-f+"px"):R&&(g.style.left=y-w-(window.scrollX+u)+s.scrollLeft+"px",g.style.top=C-f-(window.scrollY+p)+s.scrollTop+"px",n.filter(e=>e.id==parseInt(g.querySelector(".blockid").value)).x=g.getBoundingClientRect().left+window.scrollX+parseInt(window.getComputedStyle(g).width)/2+s.scrollLeft,n.filter(e=>e.id==parseInt(g.querySelector(".blockid").value)).y=g.getBoundingClientRect().top+window.scrollY+parseInt(window.getComputedStyle(g).height)/2+s.scrollTop),v||R){y>s.getBoundingClientRect().width+s.getBoundingClientRect().left-10&&ys.getBoundingClientRect().left-10?s.scrollLeft-=10:C>s.getBoundingClientRect().height+s.getBoundingClientRect().top-10&&Cs.getBoundingClientRect().top-10&&(s.scrollLeft-=10);g.getBoundingClientRect().left,window.scrollX,parseInt(window.getComputedStyle(g).width),s.scrollLeft,s.getBoundingClientRect().left,g.getBoundingClientRect().top,window.scrollY,s.scrollTop,s.getBoundingClientRect().top;var h=i.map(e=>e.id);for(c=0;c=i.filter(t=>t.id==e)[0].x-i.filter(t=>t.id==e)[0].width/2-m&&t<=i.filter(t=>t.id==e)[0].x+i.filter(t=>t.id==e)[0].width/2+m&&l>=i.filter(t=>t.id==e)[0].y-i.filter(t=>t.id==e)[0].height/2&&l<=i.filter(t=>t.id==e)[0].y+i.filter(t=>t.id==e)[0].height}function q(){s.appendChild(document.querySelector(".indicator")),g.parentNode.removeChild(g)}function X(e){if("drop"==e)c(g,!0,void 0),v=!1,g.style.top=g.getBoundingClientRect().top+window.scrollY-(p+window.scrollY)+s.scrollTop+"px",g.style.left=g.getBoundingClientRect().left+window.scrollX-(u+window.scrollX)+s.scrollLeft+"px",s.appendChild(g),i.push({parent:-1,childwidth:0,id:parseInt(g.querySelector(".blockid").value),x:g.getBoundingClientRect().left+window.scrollX+parseInt(window.getComputedStyle(g).width)/2+s.scrollLeft-s.getBoundingClientRect().left,y:g.getBoundingClientRect().top+window.scrollY+parseInt(window.getComputedStyle(g).height)/2+s.scrollTop-s.getBoundingClientRect().top,width:parseInt(window.getComputedStyle(g).width),height:parseInt(window.getComputedStyle(g).height)});else if("rearrange"==e){g.classList.remove("dragging"),R=!1;for(var t=0;t0==e.id)[0].x=g.getBoundingClientRect().left+window.scrollX+parseInt(window.getComputedStyle(g).width)/2+s.scrollLeft-s.getBoundingClientRect().left,n.filter(e=>0==e.id)[0].y=g.getBoundingClientRect().top+window.scrollY+parseInt(window.getComputedStyle(g).height)/2+s.scrollTop-s.getBoundingClientRect().top,i=i.concat(n),n=[]}}function I(e,t,l,n){t<0?(s.innerHTML+='',document.querySelector('.arrowid[value="'+g.querySelector(".blockid").value+'"]').parentNode.style.left=e.x-5-(u+window.scrollX)+s.scrollLeft+s.getBoundingClientRect().left+"px"):(s.innerHTML+='',document.querySelector('.arrowid[value="'+parseInt(g.querySelector(".blockid").value)+'"]').parentNode.style.left=i.filter(e=>e.id==n)[0].x-20-(u+window.scrollX)+s.scrollLeft+s.getBoundingClientRect().left+"px"),document.querySelector('.arrowid[value="'+parseInt(g.querySelector(".blockid").value)+'"]').parentNode.style.top=i.filter(e=>e.id==n)[0].y+i.filter(e=>e.id==n)[0].height/2+s.getBoundingClientRect().top-p+"px"}function N(e,t,l,n){t<0?(document.querySelector('.arrowid[value="'+n.id+'"]').parentNode.style.left=e.x-5-(u+window.scrollX)+s.getBoundingClientRect().left+"px",document.querySelector('.arrowid[value="'+n.id+'"]').parentNode.innerHTML=''):(document.querySelector('.arrowid[value="'+n.id+'"]').parentNode.style.left=i.filter(e=>e.id==n.parent)[0].x-20-(u+window.scrollX)+s.getBoundingClientRect().left+"px",document.querySelector('.arrowid[value="'+n.id+'"]').parentNode.innerHTML='')}function T(e,t,l){R||s.appendChild(e);for(var o=0,r=0,d=0;de.parent==l[t]).length;d++){(f=i.filter(e=>e.parent==l[t])[d]).childwidth>f.width?o+=f.childwidth+m:o+=f.width+m}o+=parseInt(window.getComputedStyle(e).width);for(d=0;de.parent==l[t]).length;d++){(f=i.filter(e=>e.parent==l[t])[d]).childwidth>f.width?(document.querySelector(".blockid[value='"+f.id+"']").parentNode.style.left=i.filter(e=>e.id==l[t])[0].x-o/2+r+f.childwidth/2-f.width/2+"px",f.x=i.filter(e=>e.parent==l[t])[0].x-o/2+r+f.childwidth/2,r+=f.childwidth+m):(document.querySelector(".blockid[value='"+f.id+"']").parentNode.style.left=i.filter(e=>e.id==l[t])[0].x-o/2+r+"px",f.x=i.filter(e=>e.parent==l[t])[0].x-o/2+r+f.width/2,r+=f.width+m)}if(e.style.left=i.filter(e=>e.id==l[t])[0].x-o/2+r-(window.scrollX+u)+s.scrollLeft+s.getBoundingClientRect().left+"px",e.style.top=i.filter(e=>e.id==l[t])[0].y+i.filter(e=>e.id==l[t])[0].height/2+B-(window.scrollY+p)+s.getBoundingClientRect().top+"px",R){n.filter(t=>t.id==parseInt(e.querySelector(".blockid").value))[0].x=e.getBoundingClientRect().left+window.scrollX+parseInt(window.getComputedStyle(e).width)/2+s.scrollLeft-s.getBoundingClientRect().left,n.filter(t=>t.id==parseInt(e.querySelector(".blockid").value))[0].y=e.getBoundingClientRect().top+window.scrollY+parseInt(window.getComputedStyle(e).height)/2+s.scrollTop-s.getBoundingClientRect().top,n.filter(t=>t.id==e.querySelector(".blockid").value)[0].parent=l[t];for(d=0;dt.id==parseInt(e.querySelector(".blockid").value))[0];if(I(c,c.x-i.filter(e=>e.id==l[t])[0].x+20,B,l[t]),-1!=i.filter(e=>e.id==l[t])[0].parent){for(var a=!1,g=l[t];!a;)if(-1==i.filter(e=>e.id==g)[0].parent)a=!0;else{var w=0;for(d=0;de.parent==g).length;d++){var f;(f=i.filter(e=>e.parent==g)[d]).childwidth>f.width?d==i.filter(e=>e.parent==g).length-1?w+=f.childwidth:w+=f.childwidth+m:d==i.filter(e=>e.parent==g).length-1?w+=f.width:w+=f.width+m}i.filter(e=>e.id==g)[0].childwidth=w,g=i.filter(e=>e.id==g)[0].parent}i.filter(e=>e.id==g)[0].childwidth=o}R&&(R=!1,e.classList.remove("dragging")),D(),E()}function Y(e){if(S=!1,M(e.target,"block")){var t=e.target.closest(".block");e.targetTouches?(y=e.targetTouches[0].clientX,C=e.targetTouches[0].clientY):(y=e.clientX,C=e.clientY),"mouseup"!==e.type&&M(e.target,"block")&&3!=e.which&&(v||R||(S=!0,w=y-((g=t).getBoundingClientRect().left+window.scrollX),f=C-(g.getBoundingClientRect().top+window.scrollY)))}}function M(e,t){return!!(e.className&&e.className.split(" ").indexOf(t)>=0)||e.parentNode&&M(e.parentNode,t)}function E(){x=i.map(e=>e.x);var e=i.map(e=>e.width),t=x.map(function(t,l){return t-e[l]/2});if((x=Math.min.apply(Math,t))e.id),n=0;ne.id==l[n])[0].id+"']").parentNode.style.left=i.filter(e=>e.id==l[n])[0].x-i.filter(e=>e.id==l[n])[0].width/2-x+s.getBoundingClientRect().left-u+20+"px",-1!=i.filter(e=>e.id==l[n])[0].parent){var o=i.filter(e=>e.id==l[n])[0],r=o.x-i.filter(e=>e.id==i.filter(e=>e.id==l[n])[0].parent)[0].x;document.querySelector('.arrowid[value="'+l[n]+'"]').parentNode.style.left=r<0?o.x-x+20-5+s.getBoundingClientRect().left-u+"px":i.filter(e=>e.id==i.filter(e=>e.id==l[n])[0].parent)[0].x-20-x+s.getBoundingClientRect().left-u+20+"px"}for(n=0;ne.parent),t=0;tl.parent==e[t]).length;o++){var r=i.filter(l=>l.parent==e[t])[o];0==i.filter(e=>e.parent==r.id).length&&(r.childwidth=0),r.childwidth>r.width?o==i.filter(l=>l.parent==e[t]).length-1?l+=r.childwidth:l+=r.childwidth+m:o==i.filter(l=>l.parent==e[t]).length-1?l+=r.width:l+=r.width+m}-1!=e[t]&&(i.filter(l=>l.id==e[t])[0].childwidth=l);for(o=0;ol.parent==e[t]).length;o++){r=i.filter(l=>l.parent==e[t])[o];const c=document.querySelector(".blockid[value='"+r.id+"']").parentNode,a=i.filter(l=>l.id==e[t]);c.style.top=a.y+B+s.getBoundingClientRect().top-p+"px",a.y=a.y+B,r.childwidth>r.width?(c.style.left=a[0].x-l/2+n+r.childwidth/2-r.width/2-(u+window.scrollX)+s.getBoundingClientRect().left+"px",r.x=a[0].x-l/2+n+r.childwidth/2,n+=r.childwidth+m):(c.style.left=a[0].x-l/2+n-(u+window.scrollX)+s.getBoundingClientRect().left+"px",r.x=a[0].x-l/2+n+r.width/2,n+=r.width+m);var d=i.filter(e=>e.id==r.id)[0];N(d,d.x-i.filter(e=>e.id==r.parent)[0].x+20,B,r)}}}},flowy.load()};
2 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Flowy - The simple flowchart engine
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
Your automation pipeline
37 |
Marketing automation
38 |
39 |
40 |
41 |
42 |
Diagram view
43 |
Code editor
44 |
45 |
46 |
Discard
47 |
Publish to site
48 |
49 |
50 |
51 |
52 |
53 |
54 |
Blocks
55 |
56 |
57 |
58 |
59 |
60 |
Triggers
61 |
Actions
62 |
Loggers
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
New visitor
77 |
Triggers when somebody visits a specified page
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
Action is performed
93 |
Triggers when somebody performs a specified action