Compare commits

..

245 Commits

Author SHA1 Message Date
Dylan Vorster
2c730a9fd1 wip 2018-05-05 12:22:02 +02:00
Dylan Vorster
a086bc785e more wip 2018-05-02 11:31:18 +02:00
Dylan Vorster
a7b6012a50 improved generics 2018-05-02 11:23:56 +02:00
Dylan Vorster
4117af8a33 wip 2018-05-02 11:20:23 +02:00
Dylan Vorster
eb7f0642a5 more moving toward react-canvas 2018-05-01 19:23:46 +02:00
Dylan Vorster
a8c73115ac no longer need these actions 2018-05-01 12:15:20 +02:00
Dylan Vorster
404e12c4e8 more stuff we no longer need 2018-04-27 20:42:38 +02:00
Dylan Vorster
aa6d1c336c wip 2018-04-27 20:28:12 +02:00
Dylan Vorster
204e05a2a1 wip 2018-04-27 19:26:18 +02:00
Dylan Vorster
d12220baa0 more refactoring 2018-04-27 16:17:20 +02:00
Dylan Vorster
91c1ee0169 WIP 2018-04-27 15:54:28 +02:00
Dylan Vorster
4806805037 use base zoom and extend from more react-canvas 2018-04-27 15:24:04 +02:00
Dylan Vorster
22f4062f56 Move to react-canvas ;) 2018-04-27 15:08:40 +02:00
Dylan Vorster
32a3c33916 Merge pull request #230 from ganesh-sankey/master
on deserialization of diagram, labels are not getting rendered bug re…
2018-04-23 17:37:37 +02:00
Ganesh Kakade
75ef02dd4d on deserialization of diagram, labels are not getting rendered bug resolved 2018-04-23 20:44:24 +05:30
Dylan Vorster
665c8b3443 Update README.md 2018-04-15 16:36:51 +02:00
Dylan Vorster
ccf425676f Update README.md 2018-04-15 16:36:39 +02:00
Dylan Vorster
1ff3abf3ef Update README.md 2018-04-15 16:36:26 +02:00
Dylan Vorster
0b1dab0de6 Merge pull request #217 from yngndrw/allow-loose-links-fix-and-refactor
Allow loose links fix and refactor (Fix for #171)
2018-04-02 02:28:00 +02:00
Andrew Young
0988e625b1 Added E2E tests, but could not get them to run ? 2018-04-01 23:14:24 +01:00
Andrew Young
dbaf03662f Added @types directory to the .gitignore file 2018-04-01 20:45:19 +01:00
Andrew Young
55f62587bd Re-write the "allowLooseLinks" validation functionality - Now checks both end points on any selected links that have been moved, resulting in more reliable validation 2018-04-01 20:27:55 +01:00
Andrew Young
98bcd60396 Code consistency change 2018-04-01 20:26:41 +01:00
Andrew Young
8467d8e7ca Fixed a typo 2018-04-01 20:26:04 +01:00
Dylan Vorster
40b4e14f15 Merge pull request #207 from wader/always-remove-link-from-old-source-target-port
Always remove link from old source/target port on port change
2018-03-27 17:16:06 +02:00
Mattias Wadman
7a78aaa9fd Always remove link from old source/target port on port change
Was only removed from old if new port is null
2018-03-23 13:43:58 +01:00
Dylan Vorster
47214df76b Update README.md 2018-03-23 10:11:35 +02:00
Dylan Vorster
cc40c398b7 Merge branch 'master' of github.com:projectstorm/react-diagrams 2018-03-21 15:04:02 +02:00
Dylan Vorster
188f63979b 5.1.1 2018-03-21 15:03:05 +02:00
Dylan Vorster
468bcea8b2 Merge pull request #202 from projectstorm/hotfix
Hotfix
2018-03-21 15:03:02 +02:00
Dylan Vorster
54b9feb62e whoops 2018-03-21 14:58:31 +02:00
Dylan Vorster
9f397b1453 fix types directory 2018-03-21 14:55:51 +02:00
Dylan Vorster
445702dd43 Hotfix 2018-03-21 14:52:13 +02:00
Dylan Vorster
bb6a6227e2 Merge pull request #201 from johnetrent/master
Change UglifyJsPlugin's ECMAScript target version to 5
2018-03-21 12:24:09 +02:00
John Trent
afff63e46a Change UglifyJsPlugin's ECMAScript target version to 5 2018-03-20 16:49:18 -07:00
John Trent
03e8348a14 Run prettier on webpack.config.js 2018-03-20 16:45:07 -07:00
Dylan Vorster
2c6d02f101 5.1.0 2018-03-17 14:44:17 +02:00
Dylan Vorster
327dcc190d general improvements 2018-03-17 14:43:35 +02:00
Dylan Vorster
c40c5c5919 improve spacing 2018-03-17 14:30:01 +02:00
Dylan Vorster
388b9931b1 Merge branch '5.1.0' of github.com:projectstorm/react-diagrams into 5.1.0 2018-03-17 14:26:18 +02:00
Dylan Vorster
ae04b5a07f Updates README.md
Auto commit by GitBook Editor
2018-03-17 12:26:03 +00:00
Dylan Vorster
9d2f450440 upgrade images 2018-03-17 14:25:52 +02:00
Dylan Vorster
45d2ea0c70 Merge branch 'master' of github.com:projectstorm/react-diagrams into 5.1.0 2018-03-17 14:16:47 +02:00
Dylan Vorster
94fe0fc854 Updates docs/Getting Started.md
Auto commit by GitBook Editor
2018-03-17 12:16:26 +00:00
Dylan Vorster
adf268697a Deletes test.md
Auto commit by GitBook Editor
2018-03-17 11:08:00 +00:00
Dylan Vorster
2c478db23f Updates test.md
Auto commit by GitBook Editor
2018-03-17 11:01:44 +00:00
Dylan Vorster
3ea6375011 rather import from storm-react-diagrams to further test the library 2018-03-13 22:46:06 +02:00
Dylan Vorster
64ec2dd0a4 Merge pull request #190 from projectstorm/source_map
sourcemap support
2018-03-13 21:42:50 +02:00
Dylan Vorster
e84c2e4e3e fix source map 2018-03-13 21:36:48 +02:00
Dylan Vorster
5f5f13a818 really really fix source maps -_- 2018-03-10 15:52:30 +02:00
Dylan Vorster
2b1a39f236 fix source maps 2018-03-10 15:44:24 +02:00
Dylan Vorster
4402068a93 sourcemap support 2018-03-10 15:32:24 +02:00
Dylan Vorster
ed50438744 Merge pull request #186 from maxleiko/master
fix main.ts exports
2018-03-08 19:25:33 +02:00
maxleiko
9a651c3ecc fix main.ts exports 2018-03-08 18:16:58 +01:00
Dylan Vorster
1af2f8cbe9 Update README.md 2018-03-03 21:57:47 +02:00
Dylan Vorster
2c74a5cf9a Update CHANGELOG.md 2018-03-03 21:57:17 +02:00
Dylan Vorster
140817f8c6 Merge pull request #179 from maxleiko/master
clean refactor / consistent TSLint / Prettier
2018-03-02 19:04:32 +02:00
maxleiko
1debc9c891 clean refactor / consistent TSLint / Prettier 2018-03-02 16:31:50 +01:00
Dylan Vorster
2ee0211994 Merge pull request #173 from wader/basemodel-extends-t
BaseModel extends use T instead of BaseModelListener
2018-02-28 19:55:25 +02:00
Dylan Vorster
86047f69fd Merge pull request #175 from smeijer/feature/add-cross-platform-support
feat(build): add cross-env to support building on various platforms.
2018-02-28 15:59:01 +02:00
Dylan Vorster
036d8dddcf Merge pull request #174 from smeijer/feature/add-vertical-flow-support
feat(link): improve rendering of vertical links
2018-02-28 15:58:18 +02:00
Stephan Meijer
08b81fff56 feat(build): add cross-env to support building on various platforms. 2018-02-28 10:50:30 +01:00
Stephan Meijer
fce1e0c7fe feat(link): improve rendering of vertical links 2018-02-28 10:21:06 +01:00
Mattias Wadman
d69f61e39d BaseModel extends use T instead of BaseModelListener
Use T instead otherwise LinkModelListener will not be used for LinkModel.
Fixes type warning about link.addListener argument.
Probably fixes same issue with iterateListeners.
2018-02-28 00:06:36 +01:00
Dylan Vorster
eb6fac30e0 update readme 2018-02-25 14:12:22 +02:00
Dylan Vorster
446cc8cdff Merge pull request #145 from projectstorm/links_refactor
5.0.0 (WIP)
2018-02-25 14:09:31 +02:00
Dylan Vorster
b8fd5a0690 5.0.0 2018-02-25 14:05:19 +02:00
Dylan Vorster
30195af8ca fix types add changelog 2018-02-25 14:04:59 +02:00
Dylan Vorster
8566c12ccc fix broken paths 2018-02-25 13:47:22 +02:00
Dylan Vorster
ac741d63a6 add some docs 2018-02-25 13:45:57 +02:00
Dylan Vorster
9b4f7d28d6 make jest a bit more defensive 2018-02-25 11:59:14 +02:00
Dylan Vorster
bee07d1be2 worked out a better way to do this 2018-02-25 11:50:24 +02:00
Dylan Vorster
259782ecfa upgrade all the widgets to use bem in a smart way 2018-02-25 11:21:22 +02:00
Dylan Vorster
ec3e022ca3 deserialize labels correctly 2018-02-25 10:16:04 +02:00
Dylan Vorster
60e620539e new snapshots 2018-02-24 19:07:14 +02:00
Dylan Vorster
06510c208c still trying to fix tests 2018-02-24 19:00:58 +02:00
Dylan Vorster
ff8a9c2e40 storybook should use typescript loader 2018-02-24 18:55:24 +02:00
Dylan Vorster
1854130a99 update lock file 2018-02-24 18:49:40 +02:00
Dylan Vorster
e2285cec16 reformat code 2018-02-24 18:44:28 +02:00
Dylan Vorster
070ed985f6 properly handle tests on CI 2018-02-24 18:44:07 +02:00
Dylan Vorster
3f27f99a8f improve e2e tests 2018-02-24 18:41:52 +02:00
Dylan Vorster
23b4a9cb2d add helper methods for E2E tests 2018-02-24 17:44:31 +02:00
Dylan Vorster
fd33ab44c3 remove this 2018-02-24 15:34:37 +02:00
Dylan Vorster
67ac9d1923 vastly improve tests 2018-02-24 15:34:09 +02:00
Dylan Vorster
4d3ad389df more fixes to testing framework 2018-02-24 13:02:17 +02:00
Dylan Vorster
9b3ad7a47f improve testing frameworks 2018-02-24 12:16:47 +02:00
Dylan Vorster
942a0c3894 some demos were using the wrong ports 2018-02-23 19:34:47 +02:00
Dylan Vorster
6980fa091e super advanced custom link demo 2018-02-23 19:29:51 +02:00
Dylan Vorster
a318b71ef1 fix more bugs 2018-02-23 18:58:34 +02:00
Dylan Vorster
6dea182ab8 fix custom links 2018-02-23 18:27:03 +02:00
Dylan Vorster
20675cbf38 prove that multiple labels work 2018-02-23 18:16:43 +02:00
Dylan Vorster
9f78dbba19 forgot to add keys 2018-02-23 18:11:11 +02:00
Dylan Vorster
1b4bbf6493 fix up the labels 2018-02-23 18:10:08 +02:00
Dylan Vorster
317eabb42a add labels to simple example and enable offsets 2018-02-23 17:59:14 +02:00
Dylan Vorster
32fd0000d2 labels work again :D 2018-02-23 17:51:15 +02:00
Dylan Vorster
29ca66989c Update README.md 2018-02-18 20:33:49 +02:00
Dylan Vorster
76fb35f6d1 Fixed some demos, code cleanup 2018-02-18 20:24:52 +02:00
Dylan Vorster
85f97a63fa Merge branch 'smart-routing' of https://github.com/klauspaiva/react-diagrams into links_refactor 2018-02-18 20:13:10 +02:00
Klaus Paiva
f811f12206 Updating docs 2018-02-14 16:08:48 +11:00
Klaus Paiva
183390ab60 Also testing the new story 2018-02-14 15:51:55 +11:00
Klaus Paiva
b243d661c4 Tests for logic in the smart routing class 2018-02-14 10:53:01 +11:00
Klaus Paiva
724b1d3e56 Code re-factoring 2018-02-14 09:34:51 +11:00
Klaus Paiva
088230d100 Correct calculation when nodes are placed at the very edge of the canvas 2018-02-13 07:58:06 +11:00
Klaus Paiva
afa34ae4cf Doing smart routing when conditions are met 2018-02-13 07:42:51 +11:00
Klaus Paiva
4ea968da26 Allowing new connections to be created 2018-02-12 17:18:04 +11:00
Klaus Paiva
2fdf944e0f Smart routing: flattening changes 2018-02-12 16:58:13 +11:00
Dylan Vorster
de40b3841d upgrade node modules, fix stupid mistake lol 2018-02-11 20:28:18 +02:00
Dylan Vorster
7d867dac97 Merge branch 'master' of github.com:projectstorm/react-diagrams into links_refactor 2018-02-11 20:04:27 +02:00
Dylan Vorster
372af4341d Merge branch 'flow-diagram-changes' of https://github.com/yngndrw/react-diagrams into links_refactor 2018-02-11 19:17:14 +02:00
Dylan Vorster
16523dac85 more refactor, this ones a big one 2018-02-11 19:03:08 +02:00
Dylan Vorster
181b2f8122 Merge pull request #146 from maxleiko/master
fixes #139
2018-01-25 21:58:50 +02:00
Dylan Vorster
a365d9eb23 Merge pull request #149 from tomitrescak/patch-1
Impossible to set safely extra porperties
2018-01-25 21:58:02 +02:00
Tomas Trescak
f66c7a6011 Impossible to set safely extra porperties 2018-01-25 19:26:50 +11:00
Andrew Young
6dc3a45851 Added demo to tests and updated test snapshots 2018-01-24 17:16:54 +00:00
Andrew Young
4d6097a1d0 "yarn run pretty" 2018-01-24 17:05:44 +00:00
Andrew Young
b7698ca53a Added a new demo showing off flow diagram usage:
- PortModel can now decide whether or not a link should be allowed (E.g. only allowing outputs to connect to inputs)
- PortModel now has an optional maximum number of links - When set to 1 an existing link is returned by createLinkModel and when set to another finite number null will be returned when the maximum is reached
- LinkModel has been updated to support the resetting of existing links (I.e. removing ports and removing mid-points)
- DiagramWidget has been updated to handle null being returned by createLinkModel as well as an existing link (this also supports an existing link where the link's target port should now be the source port)
- DiagramWidget has been updated to respect the PortModel's new canLinkToPort method
- DiagramWidget has been updated to disallow duplicate links
2018-01-24 17:02:58 +00:00
Maxime Tricoire
e22d9929e5 fixes #139 2018-01-24 14:45:12 +01:00
Dylan Vorster
f1e5a657ba Fixed some stuff that was broken 2018-01-21 16:56:33 +02:00
Dylan Vorster
c447103f0f Quite the overhaul and improvement 2018-01-21 16:29:06 +02:00
Dylan Vorster
c6ac076204 started splitting out the links 2018-01-20 15:41:17 +02:00
Dylan Vorster
266eb85436 Merge pull request #142 from klauspaiva/labels-to-links
Labels to links
2018-01-20 14:11:41 +02:00
Klaus Paiva
070696724c Merge remote-tracking branch 'library/master' into labels-to-links 2018-01-19 09:48:21 +11:00
Klaus Paiva
929790e807 Added new story to tests 2018-01-19 09:44:20 +11:00
Klaus Paiva
54c4be43d3 More code styling 2018-01-19 09:34:53 +11:00
Klaus Paiva
cebd81f000 Executed prettifier task and added missing types 2018-01-19 09:30:27 +11:00
Klaus Paiva
b92641a113 Better demo 2018-01-19 07:56:03 +11:00
Klaus Paiva
72adca100b Smoother animation 2018-01-19 07:33:45 +11:00
Dylan Vorster
455f46ada0 update readme and release notes 2018-01-18 21:33:12 +02:00
Dylan Vorster
8fadbb5d39 update changelog 2018-01-18 20:52:28 +02:00
Dylan Vorster
dbdcd4a7ce 4.0.0 2018-01-18 20:41:15 +02:00
Dylan Vorster
e6506d673a bump package versions 2018-01-18 20:41:04 +02:00
Klaus Paiva
2c0c5ffaff Rendering a conditional label with static styling 2018-01-18 16:13:15 +11:00
Dylan Vorster
4c7daa223b Merge pull request #137 from projectstorm/factory_refactor
Factory refactor
2018-01-08 21:01:30 +02:00
Dylan Vorster
74d172f904 update snapshot 2018-01-08 20:59:27 +02:00
Dylan Vorster
75412dfb2e run pretty 2018-01-08 20:57:58 +02:00
Dylan Vorster
e103b55e7d Merge branch 'master' of github.com:projectstorm/react-diagrams into factory_refactor 2018-01-08 20:51:08 +02:00
Dylan Vorster
518f481776 complete refactor 2018-01-08 20:50:54 +02:00
Dylan Vorster
e47a929ff6 Merge pull request #136 from dedelp/master
move isLocked logic to models
2018-01-08 17:38:00 +02:00
dandelp
a7ac7d1c1c move isLocked logic to models 2018-01-08 09:16:26 -06:00
Dylan Vorster
6b55811312 Merge branch 'master' of github.com:projectstorm/react-diagrams 2018-01-07 23:02:03 +02:00
Dylan Vorster
3a39137b82 a few more updates 2018-01-07 23:01:50 +02:00
Dylan Vorster
fb5a7f9bf2 Merge pull request #134 from projectstorm/test
testing prs
2018-01-07 22:59:13 +02:00
Dylan Vorster
eebafbef64 fixed PR Guide 2018-01-07 22:58:04 +02:00
Dylan Vorster
16bb31770c testing prs 2018-01-07 22:56:16 +02:00
Dylan Vorster
32738688a6 Demos are a whole lot better now 2018-01-07 22:48:52 +02:00
Dylan Vorster
ecd7202fb5 Merge branch 'cleanup-demos' of github.com:projectstorm/react-diagrams into cleanup-demos 2018-01-07 13:50:17 +02:00
Dylan Vorster
5bb9591478 Now display code side to size 2018-01-07 13:49:18 +02:00
Dylan Vorster
ad9b98516b okay I actually fixed it really really this time i promise promise really 2018-01-05 00:00:22 +02:00
Dylan Vorster
4598855145 Actually fixed jest 2018-01-04 23:56:48 +02:00
Dylan Vorster
2f048d6a94 trying to fix jest 2018-01-04 23:26:54 +02:00
Dylan Vorster
0291bd41fd cleaned up the demos nicely 2018-01-04 23:18:49 +02:00
Dylan Vorster
0be7157cd2 Add some helper widgets 2018-01-04 23:01:30 +02:00
Dylan Vorster
83d5d50a3b started work on docs 2018-01-04 22:45:55 +02:00
Dylan Vorster
d41b523480 fix the snapshot file 2018-01-04 21:47:47 +02:00
Dylan Vorster
bbf88ca951 cleaned up demos 2018-01-04 21:45:23 +02:00
Dylan Vorster
c057168a56 Merge pull request #131 from projectstorm/more_updates
PR #130 Refactor
2018-01-01 18:44:14 +02:00
Dylan Vorster
c9523a107a whoops 2018-01-01 18:41:10 +02:00
Dylan Vorster
3e3b284ed5 it works again, thanks jsdom 2018-01-01 18:38:08 +02:00
Dylan Vorster
f258371bc6 more rework of the cloning 2017-12-28 19:03:32 +02:00
dandelp
ee64e037b1 fix text 2017-12-27 10:00:58 -06:00
dandelp
97a92f6d33 Link updates 2017-12-27 09:52:26 -06:00
dandelp
760c37e20c Merge branch 'master' into link_updates 2017-12-26 16:39:28 -06:00
dandelp
139442cbf3 cleanup 2017-12-26 16:33:53 -06:00
dandelp
b71f82bc73 add entity cloning, fix #123, split link if point dropped on different node 2017-12-26 16:32:15 -06:00
dandelp
0fd20981bf add entity cloning, fix #123, split link if point dropped on different node 2017-12-26 16:28:35 -06:00
Dylan Vorster
a744b26c29 Merge pull request #129 from dedelp/wasMoved_fix
update wasMoved
2017-12-26 16:58:46 +02:00
dandelp
73724ef55a mend 2017-12-26 08:31:54 -06:00
dandelp
bb7bff529f update links 2017-12-26 08:30:20 -06:00
dandelp
f41a842807 update wasMoved 2017-12-22 11:13:23 -06:00
Dylan Vorster
9a90a5e8f5 forgot about this 2017-12-16 23:15:25 +02:00
Dylan Vorster
7a0898f189 remove these docs 2017-12-16 23:15:04 +02:00
Dylan Vorster
c24c05597c update docs 2017-12-16 23:07:56 +02:00
Dylan Vorster
94388f4aef clean up code style 2017-12-16 23:04:01 +02:00
Dylan Vorster
83f130c013 update snapshot 2017-12-16 23:02:43 +02:00
Dylan Vorster
6aced36e5b Merge branch 'master' into events-refactor 2017-12-16 23:02:05 +02:00
Dylan Vorster
a034b5f950 bump NPM versions 2017-12-16 22:56:52 +02:00
Dylan Vorster
a57fa33a24 Merge pull request #121 from kfrajtak/master
Link customisation prototype
2017-11-29 22:14:23 +02:00
Karel Frajtak
8e6ebcf44f changes made based on @dylanvorster comments - undoing changes to the code base 2017-11-29 17:25:37 +01:00
Karel Frajtak
a3ef7a089f New snapshot added 2017-11-29 08:13:40 +01:00
Karel Frajtak
f040d46e53 port creates link with himself as source port 2017-11-29 07:56:37 +01:00
Karel Frajtak
9b047ca4e4 custom link demo 2017-11-28 19:35:52 +01:00
Dylan Vorster
a1369453c1 house keeping 2017-11-24 00:09:26 +02:00
Dylan Vorster
23b5689ef4 oh boy what have I done :O 2017-11-23 23:43:03 +02:00
Dylan Vorster
f7aa626783 update release notes 2017-11-22 22:07:15 +02:00
Dylan Vorster
3d3a977037 3.2.0 2017-11-22 20:36:06 +02:00
Dylan Vorster
dff4ea4c1e changelog updates as well as npmignore 2017-11-22 20:35:54 +02:00
Dylan Vorster
5ba8b42a77 stop talking to the rif raf 2017-11-21 18:06:42 +02:00
Dylan Vorster
985c22df2a Merge pull request #118 from JokerNN/fix-events-demo
fix events demo
2017-11-21 17:59:21 +02:00
Andrey Taktaev
9694f99827 Improve action handler 2017-11-21 16:38:45 +01:00
Andrey Taktaev
ceff0cb74e fix events demo 2017-11-21 15:46:00 +01:00
Dylan Vorster
a92ea0da97 Merge pull request #117 from JokerNN/zoom-to-fit
Add zoom to fit method to DiagramEngine
2017-11-21 13:02:48 +02:00
Andrey Taktaev
a9e47be079 update test snapshot 2017-11-21 12:00:12 +01:00
Andrey Taktaev
0ef0584ed3 fixed demo comment 2017-11-21 11:52:36 +01:00
Andrey Taktaev
45eaafffa2 Add zoom to fit method to DiagramEngine 2017-11-21 11:13:12 +01:00
Dylan Vorster
42642d6070 nvm, remove raf -_- 2017-11-21 00:56:55 +02:00
Dylan Vorster
a131680dc3 fix missing raf polyfill 2017-11-21 00:52:15 +02:00
Dylan Vorster
1fd302a4e1 tests specific to circle ci 2017-11-21 00:48:07 +02:00
Dylan Vorster
7372ad075d add storybook snapshots 2017-11-21 00:43:42 +02:00
Dylan Vorster
9965bd1066 trying out some deterministic stuff 2017-11-21 00:39:19 +02:00
Dylan Vorster
9523d64113 first tests 2017-11-20 23:35:33 +02:00
Dylan Vorster
cd7306e0d8 add required Dockerfiles 2017-11-20 21:47:23 +02:00
Dylan Vorster
e68e1474ea use headless 2017-11-20 21:27:17 +02:00
Dylan Vorster
13d828e18c wip 2017-11-20 21:24:43 +02:00
Dylan Vorster
c1ee66d192 test with jest 2017-11-20 21:10:14 +02:00
Dylan Vorster
75e52e4508 how about now 2017-11-20 20:48:57 +02:00
Dylan Vorster
6cb9181408 added status badge 2017-11-20 20:46:44 +02:00
Dylan Vorster
7f06610356 added circle ci 2017-11-20 20:33:59 +02:00
Dylan Vorster
a8727f7f22 Merge pull request #113 from JokerNN/fix-issue-109
Example fix for "Updating DiagramWidget leads to loss of event listeners" #109
2017-11-20 13:11:50 +02:00
Dylan Vorster
beff431b6a Merge pull request #115 from alex-enchi/rdi-114-grid-size-serialization
rdi-114 serialize gridSize as well
2017-11-20 13:10:59 +02:00
Alex K
e1c5014207 rdi-114 serialize gridSize as well 2017-11-20 13:08:28 +02:00
Andrey Taktaev
bbe1e19710 cleanup 2017-11-20 11:46:03 +01:00
Andrey Taktaev
4e102a109e fix-#109 2017-11-20 11:44:11 +01:00
Dylan Vorster
600a386f32 fix a bunch of stuff 2017-11-20 12:06:20 +02:00
Dylan Vorster
8b7b32f4ed fix for dagre 2017-11-20 11:55:45 +02:00
Dylan Vorster
e93bf76167 Merge branch 'master' into rdi-94-dagre-example 2017-11-20 11:18:29 +02:00
Dylan Vorster
0ac691910a Merge pull request #111 from klauspaiva/extra-stories-and-limit-number-ponts
Two new storybook entries and new functionality: limit to the number of points
2017-11-20 11:16:07 +02:00
Klaus Paiva
0cf0cee501 Using storybook action to log events;
Moved addPointToLink to the class so we don't need to provide the widget argument;
Backwards compatible default for maxNumberPointsPerLink
2017-11-20 19:39:00 +11:00
Alex K
4eca7ad122 rdi-94 added back dagre into dependencies 2017-11-20 10:09:49 +02:00
Alex K
e9307ffe97 Merge branch 'master' into rdi-94-dagre-example
# Conflicts:
#	package.json
2017-11-20 10:08:57 +02:00
Alex K
1a1b47e085 rdi-94 added redistribute button 2017-11-20 09:59:40 +02:00
Klaus Paiva
bf9a20adbc Removing local-only code 2017-11-19 11:13:41 +11:00
Klaus Paiva
55030b9358 Added two new demos.
First simply demonstrates how events can be handled.
Second demonstrates a new functionality: limiting the number of points to be created per link using the new property maxNumberPointsPerLink
2017-11-19 11:06:19 +11:00
Dylan Vorster
a2dc9c77f5 3.1.4 2017-11-15 10:17:53 +02:00
Dylan Vorster
e5f6877a77 refactor dependency 2017-11-15 10:17:46 +02:00
Dylan Vorster
c1a4273302 changelog 2017-11-15 10:11:17 +02:00
Dylan Vorster
5f7f8bcad3 3.1.3 2017-11-15 10:09:43 +02:00
Dylan Vorster
59fa06debf bump package versions 2017-11-15 10:07:56 +02:00
Alex K
47fcae619a rdi-94 example of dagre redistribution 2017-11-14 22:50:42 +02:00
Dylan Vorster
05f7c06227 Merge pull request #99 from Schlesiger/patch-1
Export styles with `.min` extension
2017-10-11 22:26:16 +02:00
Dylan Vorster
3a52d4a852 Merge pull request #92 from DrummerHead/master
Refactor path creation on DefaultLinkWidget.tsx
2017-10-11 22:25:04 +02:00
DrummerHead
ad7ccf07e5 Integrate path generation functions into DefaultLinkWidget class 2017-10-11 17:23:22 -03:00
DrummerHead
c8116e12ca Use existing PointModel instead of new Point interface 2017-10-11 17:11:23 -03:00
DrummerHead
5c07f1cd17 Merge remote-tracking branch 'upstream/master'
* upstream/master:
  Export CanvasActions to library user
  #89 - explanatory comment
  wheel to zoom inverseZoom property and trackpad support for Chrome and Firefox
2017-10-11 17:05:39 -03:00
Connor Schlesiger
a22f8814cf Export styles with .min extension
It is best practice to export compressed styles with a `.min.css` extension. Several tools use the `.min.css` extension to decide whether preprocessing is necessary.
2017-10-11 12:51:14 -04:00
Dylan Vorster
44d46fa029 Merge pull request #98 from JokerNN/master
Export CanvasActions to library user
2017-10-10 21:36:33 +02:00
Andrey Taktaev
f5b2d8662c Export CanvasActions to library user 2017-10-10 17:15:06 +02:00
Dylan Vorster
404b73eeba Merge pull request #95 from JokerNN/wheelzoom-issue89
Wheelzoom - inverseZoom property and trackpad support
2017-09-30 00:26:19 +02:00
Andrey Taktaev
bb1f4e1c4c #89 - explanatory comment 2017-09-29 17:54:10 +02:00
Andrey Taktaev
3db0fcb85c wheel to zoom inverseZoom property and trackpad support for Chrome and Firefox 2017-09-29 17:50:24 +02:00
DrummerHead
4920523c48 Refactor path creation on DefaultLinkWidget.tsx 2017-09-21 20:53:38 -03:00
Dylan Vorster
955f9d8a58 3.1.2 2017-09-15 17:37:31 +02:00
Dylan Vorster
babac6efc0 wow i suck at this 2017-09-15 17:37:20 +02:00
Dylan Vorster
57a0d63d9c Merge branch 'JokerNN-master' 2017-09-15 17:36:56 +02:00
Dylan Vorster
55eb201850 Add link to PR 2017-09-15 17:36:14 +02:00
Andrey Taktaev
6996f8c6cd fix zooming when canvas not in the top left corner 2017-09-15 17:23:00 +02:00
146 changed files with 27103 additions and 4771 deletions

31
.circleci/config.yml Normal file
View File

@@ -0,0 +1,31 @@
version: 2
jobs:
build:
docker:
- image: projectstorm/react-diagrams-ci
working_directory: ~/repo
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}
- run: yarn install
- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package.json" }}
# test building project
- run: yarn run prepublishOnly
# test building storybook
- run: yarn run storybook:build
# test e2e tests and jest snapshots
- run: yarn run test:ci

View File

@@ -0,0 +1,27 @@
FROM node:8-slim
# Install latest chrome dev package.
# Note: this installs the necessary libs to make the bundled version of Chromium that Pupppeteer
# installs, work.
RUN apt-get update && apt-get install -y wget --no-install-recommends \
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
&& apt-get update \
&& apt-get install -y google-chrome-unstable \
--no-install-recommends \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get purge --auto-remove -y curl \
&& rm -rf /src/*.deb
RUN yarn add puppeteer
# Add pptr user.
RUN groupadd -r pptruser && useradd -r -g pptruser -G audio,video pptruser \
&& mkdir -p /home/pptruser/Downloads \
&& chown -R pptruser:pptruser /home/pptruser \
&& chown -R pptruser:pptruser /node_modules
# Run user as non privileged.
USER pptruser
CMD ["google-chrome-unstable"]

1
.envrc Normal file
View File

@@ -0,0 +1 @@
PATH_add ./node_modules/.bin

27
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,27 @@
# Checklist
- [ ] The code has been run through pretty `yarn run pretty`
- [ ] The tests pass on CircleCI
- [ ] You have referenced the issue(s) or other PR(s) this fixes/relates-to
- [ ] The PR Template has been filled out (see below)
- [ ] Had a beer because you are awesome
## What?
(My awesome new feature does this really cool thing.)
## Why?
(Because obviously it could not do it before)
## How?
(Basically I did this and that because im a super 1337 hacker)
## Feel-Good "programming lol" image:
(Add your own one below :])
![LOL](https://i.pinimg.com/originals/7f/1b/c3/7f1bc3fb2e123dd3255a85c04db22f19.jpg)

4
.gitignore vendored
View File

@@ -1,6 +1,10 @@
dist/
dist/main.js
dist/main.js.map
/package
*.tgz
@types/
.out
# Created by https://www.gitignore.io/api/net,netbeans,sublimetext,phpstorm,windows,osx,node

View File

@@ -1,7 +1,11 @@
demos
images
docs/
docs
.out
.storybook
.circleci
tests
*.md
# Created by https://www.gitignore.io/api/net,netbeans,sublimetext,phpstorm,windows,osx,node

14
.storybook/addon-code/react.js vendored Normal file
View File

@@ -0,0 +1,14 @@
import React from 'react';
import addons from '@storybook/addons';
export class WithCode extends React.Component {
render() {
const { children, code } = this.props;
const channel = addons.getChannel();
// send the notes to the channel.
channel.emit('storybook/code/set_code', code);
// return children elements.
return children;
}
}

View File

@@ -0,0 +1,69 @@
import React from 'react';
import addons from '@storybook/addons';
import SyntaxHighlighter from 'react-syntax-highlighter';
import { github } from 'react-syntax-highlighter/styles/hljs';
/**
* @author Dylan
*
* Simple little addon for displaying code, might make this a seperate project at some point
*/
export class CodePreview extends React.Component {
constructor(...args) {
super(...args);
this.state = {
code: ''
};
}
componentDidMount() {
const { channel, api } = this.props;
// Listen to the notes and render it.
channel.on('storybook/code/set_code', (code) => {
this.onAddCode(code);
});
// Clear the current notes on every story change.
this.stopListeningOnStory = api.onStory(() => {
this.onAddCode('');
});
}
// This is some cleanup tasks when the Notes panel is unmounting.
componentWillUnmount() {
if (this.stopListeningOnStory) {
this.stopListeningOnStory();
}
this.unmounted = true;
const { channel } = this.props;
channel.removeListener('storybook/notes/add_notes', this.onAddCode);
}
onAddCode(code) {
this.setState({ code: code });
}
render() {
return (
<SyntaxHighlighter
customStyle={{width: '100%', overflowX:'hidden', tabSize: 4}}
showLineNumbers={true}
language='language-tsx'
style={github}
>
{this.state.code}
</SyntaxHighlighter>
);
}
}
// Register the addon with a unique name.
addons.register('storybook/code', api => {
// Also need to set a unique name to the panel.
addons.addPanel('storybook/code/panel', {
title: 'Code preview',
render: () => <CodePreview channel={addons.getChannel()} api={api} />,
});
});

3
.storybook/addons.js Normal file
View File

@@ -0,0 +1,3 @@
import './addon-code/register';
import '@storybook/addon-actions/register';
import '@storybook/addon-options/register';

View File

@@ -7,18 +7,33 @@ module.exports = {
loaders: ["style-loader", "css-loader", "sass-loader"],
include: path.resolve(__dirname, '../')
},
{
test: /\.css/,
loaders: ["style-loader", "css-loader"],
include: path.resolve(__dirname, '../')
},
{
enforce: 'pre',
test: /\.js$/,
loader: "source-map-loader"
loader: "source-map-loader",
exclude: [
/node_modules\//
]
},
{
test: /\.tsx?$/,
loader: 'awesome-typescript-loader',
loader: 'awesome-typescript-loader?declaration=false',
},
{
test: /\.(woff|woff2|eot|ttf|otf|svg)$/,
loader: "file-loader"
}
]
},
resolve: {
alias: {
'storm-react-diagrams': path.join(__dirname, "..", "src", "main")
},
extensions: [".tsx", ".ts", ".js"]
}
};
};

1
.yarnrc Normal file
View File

@@ -0,0 +1 @@
--ignore-engines true

View File

@@ -1,5 +1,67 @@
__3.1.1__
__5.1.0__
* [api] Rename XXXFactory into AbstractXXXFactory
* [refactor] tslint and prettier are now the same
* [refactor] Each class now explicitely has its own class file (consistency)
* [feature] Smooth vertical links (no longer limited to horizontal)
* [feature] Dedicated documentation via gitbook
* [bug] forgot to export some
* [refactor] consistently use lodash where possible
* [maintenance] upgrade node modules
__5.0.0__ http://dylanv.blog/2018/03/03/storm-react-diagrams-5-0-0/
PR: https://github.com/projectstorm/react-diagrams/pull/145
* [refactor] Links completely overhauled
* [feature] Smart Routing
* [feature] Flow support
* [demo] Smart Routing
* [demo] Animated links
* [api] Bootstrapping Improvements
* [feature] add custom properties to all widgets
* [refactor] use BEM for all css
* [feature] Default Link factory hooks
* [tests] e2e tests + helper framework
* [tests] automatically load JEST Snapshots
* [feature] Link labels!
__4.0.0__ http://dylanv.blog/2018/01/18/storm-react-diagrams-v4-0-0/
* [refactor] Events system was completely overhauled
* [demo] Custom Link Sizes
* [refactor] Demos are now much more verbose and better managed
* [update] node packages
* [bug] Fix #129
* [feature] Control link creation through ports
* [refactor] Models are now in seperate files
* [refactor] Merged the concept of instance factories and widget factories into one
* [feature] Models can now be cloned at various parts of the model graph
* [demo] Cloning
* [feature] models control isLocked
__3.2.0__ http://dylanv.blog/2017/11/22/storm-react-diagrams-3-2-0/
* [feature] zoom to fit
* added Circle CI tests
* [demo] dagre automatic layouts
* [demo] zoom to fit
* [demo] selection events
* [demo] limit number of points
* [demo] programmatic node updating
* updated dependencies
* [bugs] swapping diagram models in engines
* [bugs] issues with the rendering pipeline #107
* added ci badge to Readme
__3.1.3__
* Refactor links slightly
* use min extension for css
* bump package versions
* export more classes
__3.1.2__
* Hotfix: fix zooming when canvas not in the top left corner
(https://github.com/projectstorm/react-diagrams/pull/88)
__3.1.0__ http://dylanv.blog/2017/09/15/storm-react-diagrams-3-1-0/
* Zoom relative to mouse location

27
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,27 @@
# Contributing
See below for guidelines on house keeping:
### Always add a PR
Since the project runs on GitHub, the best way to contribute is to fork and then submit a PR.
You will find a template that you will need to fill out
### Adding new demos
Add a new folder in the ./demos directory and make sure that it is named correctly like the other demos.
A new demo should conform to the standard of either `demo-simple` in which it contains a markdown file that
clearly explains 'whats going on', or the code sample should have very clear comments that almost always should ready
like an instruction manual such as the simple demo.
Finally, you should link up your demo to the __index.tsx__ file in the __demos__ directory. It should be quite
self explanatory on how it works, but ultimately I have a helper method that makes it easy to link source code
to the demo itself, hence the 'require' statements. The third parameter is if you want to place your demo
inside a markdown guide (again: see simple demo for how that is done).
### Make the demo testable
Similar procedure, except link your demo in the __index.tsx__ file sitting in the __tests__ directory.
Running `yarn run test` will fire up jest (hopefully) and then it will render your demo to a snapshot directory
which when run again (for a second time), should compare the output to the newely generated snapshot. Make sure
to commit the updated snapshot file with your PR!

View File

@@ -1,98 +1,46 @@
# STORM React Diagrams
__DEMO__: http://www.projectstorm.io/react-diagrams
**PSA**: React Diagrams is currently getting a bit of a rewrite to enable much more advanced features. To see the new foundation WIP visit [https://github.com/projectstorm/react-canvas](https://github.com/projectstorm/react-canvas).
__Latest Release Notes__: http://dylanv.blog/2017/09/15/storm-react-diagrams-3-1-0/
---
**DEMO**: [http://www.projectstorm.io/react-diagrams](http://www.projectstorm.io/react-diagrams)
**DOCS:** [https://projectstorm.gitbooks.io/react-diagrams](https://projectstorm.gitbooks.io/react-diagrams)
**RELEASE NOTES** : [http://dylanv.blog/2018/03/03/storm-react-diagrams-5-0-0/](http://dylanv.blog/2018/03/03/storm-react-diagrams-5-0-0/)
A super simple, no-nonsense diagramming library written in React that just works.
[![Join the chat at https://gitter.im/projectstorm/react-diagrams](https://badges.gitter.im/projectstorm/react-diagrams.svg)](https://gitter.im/projectstorm/react-diagrams?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![NPM](https://img.shields.io/npm/v/storm-react-diagrams.svg)](https://npmjs.org/package/storm-react-diagrams)
[![NPM](https://img.shields.io/npm/dt/storm-react-diagrams.svg)](https://npmjs.org/package/storm-react-diagrams)
[![Join the chat at https://gitter.im/projectstorm/react-diagrams](https://badges.gitter.im/projectstorm/react-diagrams.svg)](https://gitter.im/projectstorm/react-diagrams?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![NPM](https://img.shields.io/npm/v/storm-react-diagrams.svg)](https://npmjs.org/package/storm-react-diagrams) [![NPM](https://img.shields.io/npm/dt/storm-react-diagrams.svg)](https://npmjs.org/package/storm-react-diagrams) [![Package Quality](http://npm.packagequality.com/shield/storm-react-diagrams.svg)](http://packagequality.com/#?package=storm-react-diagrams) [![CircleCI](https://circleci.com/gh/projectstorm/react-diagrams/tree/master.svg?style=svg)](https://circleci.com/gh/projectstorm/react-diagrams/tree/master)
![Demo2](./images/example1.png)
![Personal Project](./images/example1.jpg)
![Demo2](./images/example2.png)
![](./images/example2.jpg)
![Demo2](./images/example3.png)
![](./images/example3.jpg)
## Introduction
A no-nonsense diagramming library written entirely in React with the help of Lodash, and a single polyfill. It aims to be:
A no-nonsense diagramming library written entirely in React with the help of a few small libraries. It aims to be:
* Simple, and void of any fuss/complications when implementing it into your own application
* Customizable without having to hack the core (adapters/factories etc..)
* Customizable without having to hack the core \(adapters/factories etc..\)
* Simple to operate and understand without sugar and magic
* Fast and optimized to handle large diagrams with hundreds of nodes/links
* Super easy to use, and should work as you expect it to
* Perfect for creating declarative systems such as programmatic pipelines and visual programming languages
## Roadmap
#### Run the demos
* Move path logic associated with links into a base link class so we can easily extend links
* Add more fault tolerance
After running `yarn install` you must then run: `yarn run storybook`
## How to install
#### Building from source
```
npm install storm-react-diagrams
```
or
```
yarn add storm-react-diagrams
```
Simply run `webpack` in the root directory \(or `export NODE_ENV=production && webpack` if you want a production build\) and it will spit out the transpiled code and typescript definitions into the dist directory as a single file.
We use webpack for this because TSC cannot compile a single UMD file \(TSC can currently only output multiple UMD files\).
## How to build
## [Checkout the docs](https://projectstorm.gitbooks.io/react-diagrams)
Simply run ```webpack``` in the root directory (or ```export NODE_ENV=production && webpack``` if you want a production build) and it will spit out the transpiled code and typescript definitions into the dist directory as a single file. __It will also compile the code for the demos__ .We use webpack for this because TSC cannot compile a single UMD file (TSC can currently only output multiple UMD files).
_NOTE:_ We turn off name mangeling in production builds because we require class names to be left intact when serializing.
## Make your own nodes
To see how to create your own nodes like the one below, take a look at __demo3__:
![Demo2](./images/demo3.png)
## How does it work
The library uses a Model Graph to represent the virtual diagram and then renders the diagram using
2 layers:
* Node Layer -> which is responsible for rendering nodes as HTML components
* Link Layer -> which renders the links as SVG paths
Each node and link is fed into a factory that then generates the corresponding node or link react widget.
Therefore, to create custom nodes and links, register your own factories that return your own widgets.
As long as a node contains at least one port and the corresponding NodeWidget contains at least one PortWidget,
a link can be connected to it.
## Events
[Event System](docs/Events.md)
## Questions
[Questions](docs/Questions.md)
## Usage
__Delete__ removes any selected items
![__Delete__](./images/rjdDelete.gif)
__Shift + Mouse Drag__ triggers a multi-selection box
![Shift + Mouse Drag](./images/mouseDrag.gif)
__Shift + Mouse Click__ selects the item (items can be multi-selected)
![Shift + Mouse Click](./images/shiftClick.gif)
__Mouse Drag__ drags the entire diagram
![Mouse Drag](./images/canvasDrag.gif)
__Mouse Wheel__ zooms the diagram in / out
![Mouse Wheel](./images/mouseWheel.gif)
__Click Link + Drag__ creates a new link point
![Click Link + Drag](./images/createPoint.gif)
__Click Node Port + Drag__ creates a new link
![Click Node Port + Drag](./images/createLink.gif)

11
SUMMARY.md Normal file
View File

@@ -0,0 +1,11 @@
# Summary
* [Introduction](README.md)
* [Interacting with diagrams](/docs/Interactive Usage.md)
* [Getting Started](/docs/Getting Started.md)
* [About the project](about-the-project.md)
* [Testing](/docs/Testing.md)
* [Architecture Questions](/docs/Architecture Questions.md)

0
about-the-project.md Normal file
View File

View File

@@ -0,0 +1,23 @@
import * as React from "react";
export interface DemoWorkspaceWidgetProps {
buttons?: any;
}
export interface DemoWorkspaceWidgetState {}
export class DemoWorkspaceWidget extends React.Component<DemoWorkspaceWidgetProps, DemoWorkspaceWidgetState> {
constructor(props: DemoWorkspaceWidgetProps) {
super(props);
this.state = {};
}
render() {
return (
<div className="srd-demo-workspace">
<div className="srd-demo-workspace__toolbar">{this.props.buttons}</div>
<div className="srd-demo-workspace__content">{this.props.children}</div>
</div>
);
}
}

37
demos/.helpers/Helper.tsx Normal file
View File

@@ -0,0 +1,37 @@
import * as React from "react";
import { withDocs } from "storybook-readme";
import { WithCode } from "../../.storybook/addon-code/react.js";
export class Helper {
/**
* Logs the mouse position in the console, but overlays a div that consumes all events
* since the actual story book stories are rendered as an iFrame.
*/
static logMousePosition() {
let element = window.parent.document.createElement("mouse-position");
element.style.position = "absolute";
element.style.top = "0px";
element.style.left = "0px";
element.style.bottom = "0px";
element.style.right = "0px";
element.style.zIndex = "10";
window.parent.document.body.appendChild(element);
window.parent.window.addEventListener("mousemove", event => {
console.clear();
console.log(event.clientX, event.clientY);
});
}
static makeDemo(widget, code, markdown?) {
let container = () => <WithCode code={code}>{widget}</WithCode>;
if (markdown) {
return withDocs({
PreviewComponent: ({ children }) => {
return <div className="docs-preview-wrapper">{children}</div>;
}
})(markdown, container);
}
return container;
}
}

80
demos/.helpers/demo.scss Normal file
View File

@@ -0,0 +1,80 @@
@import "../../src/sass/main";
.srd-demo-workspace{
background: black;
display: flex;
flex-direction: column;
height: 100%;
border-radius: 5px;
overflow: hidden;
&__toolbar{
padding: 5px;
display: flex;
flex-shrink: 0;
button{
background: rgb(60,60,60);
font-size: 14px;
padding: 5px 10px;
border: none;
color: white;
outline: none;
cursor: pointer;
margin: 2px;
border-radius: 3px;
&:hover{
background: rgb(0,192,255);
}
}
}
&__content{
flex-grow: 1;
height: 100%;
}
}
.docs-preview-wrapper{
background: rgb(60,60,60);
border-radius: 10px;
overflow: hidden;
padding: 10px;
margin-top: 20px;
margin-bottom: 20px;
}
.srd-demo-canvas{
height: 100%;
min-height: 300px;
background-color: rgb(60,60,60) !important;
$color: rgba(white, .05);
background-image:
linear-gradient(0deg,
transparent 24%,
$color 25%,
$color 26%,
transparent 27%,
transparent 74%,
$color 75%,
$color 76%,
transparent 77%,
transparent),
linear-gradient(90deg,
transparent 24%,
$color 25%,
$color 26%,
transparent 27%,
transparent 74%,
$color 75%,
$color 76%,
transparent 77%,
transparent);
background-size:50px 50px;
.pointui{
fill: rgba(white,0.5);
}
}

View File

@@ -0,0 +1,88 @@
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
NodeModel,
DiagramWidget,
BaseModel
} from "storm-react-diagrams";
import * as _ from "lodash";
import * as React from "react";
import { DemoWorkspaceWidget } from "../.helpers/DemoWorkspaceWidget";
/**
* Tests cloning
*/
class CloneSelected extends React.Component<any, any> {
constructor(props: any) {
super(props);
this.cloneSelected = this.cloneSelected.bind(this);
}
cloneSelected() {
let { engine } = this.props;
let offset = { x: 100, y: 100 };
let model = engine.getDiagramModel();
let itemMap = {};
_.forEach(model.getSelectedItems(), (item: BaseModel<any>) => {
let newItem = item.clone(itemMap);
// offset the nodes slightly
if (newItem instanceof NodeModel) {
newItem.setPosition(newItem.x + offset.x, newItem.y + offset.y);
model.addNode(newItem);
} else if (newItem instanceof LinkModel) {
// offset the link points
newItem.getPoints().forEach(p => {
p.updateLocation({ x: p.getX() + offset.x, y: p.getY() + offset.y });
});
model.addLink(newItem);
}
newItem.selected = false;
});
this.forceUpdate();
}
render() {
const { engine } = this.props;
return (
<DemoWorkspaceWidget buttons={<button onClick={this.cloneSelected}>Clone Selected</button>}>
<DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />
</DemoWorkspaceWidget>
);
}
}
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.installDefaults();
//2) setup the diagram model
var model = new DiagramModel();
//3-A) create a default node
var node1 = new DefaultNodeModel("Node 1", "rgb(0,192,255)");
let port = node1.addOutPort("Out");
node1.setPosition(100, 100);
//3-B) create another default node
var node2 = new DefaultNodeModel("Node 2", "rgb(192,255,0)");
let port2 = node2.addInPort("In");
node2.setPosition(400, 100);
// link the ports
let link1 = port.link(port2);
//4) add the models to the root graph
model.addAll(node1, node2, link1);
//5) load model into engine
engine.setModel(model);
//6) render the diagram!
return <CloneSelected engine={engine} model={model} />;
};

View File

@@ -0,0 +1,157 @@
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
DefaultPortModel,
DiagramWidget,
LinkWidget,
LinkProps,
DefaultLinkWidget,
DefaultLinkModel,
DefaultLinkFactory
} from "storm-react-diagrams";
import { action } from "@storybook/addon-actions";
import * as React from "react";
export class AdvancedLinkModel extends DefaultLinkModel {
constructor() {
super("advanced");
this.width = 10;
}
}
export class AdvancedPortModel extends DefaultPortModel {
createLinkModel(): AdvancedLinkModel | null {
return new AdvancedLinkModel();
}
}
export class AdvancedLinkSegment extends React.Component<{ model: AdvancedLinkModel; path: string }> {
path: SVGPathElement;
circle: SVGCircleElement;
callback: () => any;
percent: number;
handle: any;
mounted: boolean;
constructor(props) {
super(props);
this.percent = 0;
}
componentDidMount() {
this.mounted = true;
this.callback = () => {
if (!this.circle || !this.path) {
return;
}
this.percent += 2;
if (this.percent > 100) {
this.percent = 0;
}
let point = this.path.getPointAtLength(this.path.getTotalLength() * (this.percent / 100.0));
this.circle.setAttribute("cx", "" + point.x);
this.circle.setAttribute("cy", "" + point.y);
if (this.mounted) {
requestAnimationFrame(this.callback);
}
};
requestAnimationFrame(this.callback);
}
componentWillUnmount() {
this.mounted = false;
}
render() {
return (
<>
<path
ref={ref => {
this.path = ref;
}}
strokeWidth={this.props.model.width}
stroke="rgba(255,0,0,0.5)"
d={this.props.path}
/>
<circle
ref={ref => {
this.circle = ref;
}}
r={10}
fill="orange"
/>
</>
);
}
}
export class AdvancedLinkFactory extends DefaultLinkFactory {
constructor() {
super();
this.type = "advanced";
}
getNewInstance(initialConfig?: any): AdvancedLinkModel {
return new AdvancedLinkModel();
}
generateLinkSegment(model: AdvancedLinkModel, widget: DefaultLinkWidget, selected: boolean, path: string) {
return (
<g>
<AdvancedLinkSegment model={model} path={path} />
</g>
);
}
}
/**
*
* Simple link styling demo
*
* @Author kfrajtak
*/
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.installDefaults();
engine.registerLinkFactory(new AdvancedLinkFactory());
// create some nodes
var node1 = new DefaultNodeModel("Source", "rgb(0,192,255)");
let port1 = node1.addPort(new AdvancedPortModel(false, "out-1", "Out thick"));
let port2 = node1.addPort(new DefaultPortModel(false, "out-2", "Out default"));
node1.setPosition(100, 100);
var node2 = new DefaultNodeModel("Target", "rgb(192,255,0)");
var port3 = node2.addPort(new AdvancedPortModel(true, "in-1", "In thick"));
var port4 = node2.addPort(new DefaultPortModel(true, "in-2", "In default"));
node2.setPosition(300, 100);
var node3 = new DefaultNodeModel("Source", "rgb(0,192,255)");
node3.addPort(new AdvancedPortModel(false, "out-1", "Out thick"));
node3.addPort(new DefaultPortModel(false, "out-2", "Out default"));
node3.setPosition(100, 200);
var node4 = new DefaultNodeModel("Target", "rgb(192,255,0)");
node4.addPort(new AdvancedPortModel(true, "in-1", "In thick"));
node4.addPort(new DefaultPortModel(true, "in-2", "In default"));
node4.setPosition(300, 200);
var model = new DiagramModel();
model.addAll(port1.link(port3), port2.link(port4));
// add everything else
model.addAll(node1, node2, node3, node4);
// load model into engine
engine.setModel(model);
// render the diagram!
return <DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />;
};

View File

@@ -0,0 +1,18 @@
import * as SRD from "storm-react-diagrams";
import { DiamonNodeWidget } from "./DiamondNodeWidget";
import { DiamondNodeModel } from "./DiamondNodeModel";
import * as React from "react";
export class DiamondNodeFactory extends SRD.AbstractNodeFactory {
constructor() {
super("diamond");
}
generateReactWidget(diagramEngine: SRD.DiagramEngine, node: SRD.NodeModel): JSX.Element {
return <DiamonNodeWidget node={node} />;
}
getNewInstance() {
return new DiamondNodeModel();
}
}

View File

@@ -1,7 +1,7 @@
import * as SRD from "../../src/main";
import { NodeModel } from "storm-react-diagrams";
import { DiamondPortModel } from "./DiamondPortModel";
export class DiamondNodeModel extends SRD.NodeModel {
export class DiamondNodeModel extends NodeModel {
constructor() {
super("diamond");
this.addPort(new DiamondPortModel("top"));

View File

@@ -1,6 +1,6 @@
import * as React from "react";
import { DiamondNodeModel } from "./DiamondNodeModel";
import { PortWidget } from "../../src/main";
import { PortWidget } from "storm-react-diagrams";
export interface DiamonNodeWidgetProps {
node: DiamondNodeModel;
@@ -39,10 +39,10 @@ export class DiamonNodeWidget extends React.Component<DiamonNodeWidgetProps, Dia
dangerouslySetInnerHTML={{
__html:
`
<g id="Layer_1">
</g>
<g id="Layer_2">
<polygon fill="purple" stroke="#000000" stroke-width="3" stroke-miterlimit="10" points="10,` +
<g id="Layer_1">
</g>
<g id="Layer_2">
<polygon fill="purple" stroke="#000000" stroke-width="3" stroke-miterlimit="10" points="10,` +
this.props.size / 2 +
` ` +
this.props.size / 2 +
@@ -55,8 +55,8 @@ export class DiamonNodeWidget extends React.Component<DiamonNodeWidgetProps, Dia
`,` +
(this.props.size - 10) +
` "/>
</g>
`
</g>
`
}}
/>
<div
@@ -103,5 +103,3 @@ export class DiamonNodeWidget extends React.Component<DiamonNodeWidgetProps, Dia
);
}
}
export var DiamonNodeWidgetFactory = React.createFactory(DiamonNodeWidget);

View File

@@ -0,0 +1,26 @@
import * as _ from "lodash";
import { LinkModel, DiagramEngine, PortModel, DefaultLinkModel } from "storm-react-diagrams";
export class DiamondPortModel extends PortModel {
position: string | "top" | "bottom" | "left" | "right";
constructor(pos: string = "top") {
super(pos, "diamond");
this.position = pos;
}
serialize() {
return _.merge(super.serialize(), {
position: this.position
});
}
deSerialize(data: any, engine: DiagramEngine) {
super.deSerialize(data, engine);
this.position = data.position;
}
createLinkModel(): LinkModel {
return new DefaultLinkModel();
}
}

View File

@@ -0,0 +1,14 @@
import { PortModel, AbstractPortFactory } from "storm-react-diagrams";
export class SimplePortFactory extends AbstractPortFactory {
cb: (initialConfig?: any) => PortModel;
constructor(type: string, cb: (initialConfig?: any) => PortModel) {
super(type);
this.cb = cb;
}
getNewInstance(initialConfig?: any): PortModel {
return this.cb(initialConfig);
}
}

View File

@@ -0,0 +1,57 @@
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
DefaultPortModel,
DiagramWidget
} from "storm-react-diagrams";
import * as React from "react";
// import the custom models
import { DiamondNodeModel } from "./DiamondNodeModel";
import { DiamondNodeFactory } from "./DiamondNodeFactory";
import { SimplePortFactory } from "./SimplePortFactory";
import { DiamondPortModel } from "./DiamondPortModel";
/**
* @Author Dylan Vorster
*/
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.installDefaults();
// register some other factories as well
engine.registerPortFactory(new SimplePortFactory("diamond", config => new DiamondPortModel()));
engine.registerNodeFactory(new DiamondNodeFactory());
//2) setup the diagram model
var model = new DiagramModel();
//3-A) create a default node
var node1 = new DefaultNodeModel("Node 1", "rgb(0,192,255)");
var port1 = node1.addOutPort("Out");
node1.setPosition(100, 150);
//3-B) create our new custom node
var node2 = new DiamondNodeModel();
node2.setPosition(250, 108);
var node3 = new DefaultNodeModel("Node 3", "red");
var port3 = node3.addInPort("In");
node3.setPosition(500, 150);
//3-C) link the 2 nodes together
var link1 = port1.link(node2.getPort("left"));
var link2 = port3.link(node2.getPort("right"));
//4) add the models to the root graph
model.addAll(node1, node2, node3, link1, link2);
//5) load model into engine
engine.setModel(model);
//6) render the diagram!
return <DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />;
};

View File

@@ -0,0 +1,56 @@
import * as dagre from "dagre";
import * as _ from "lodash";
const size = {
width: 60,
height: 60
};
export function distributeElements(model) {
let clonedModel = _.cloneDeep(model);
let nodes = distributeGraph(clonedModel);
nodes.forEach(node => {
let modelNode = clonedModel.nodes.find(item => item.id === node.id);
modelNode.x = node.x;
modelNode.y = node.y;
});
return clonedModel;
}
function distributeGraph(model) {
let nodes = mapElements(model);
let edges = mapEdges(model);
let graph = new dagre.graphlib.Graph();
graph.setGraph({});
graph.setDefaultEdgeLabel(() => ({}));
//add elements to dagre graph
nodes.forEach(node => {
graph.setNode(node.id, node.metadata);
});
edges.forEach(edge => {
if (edge.from && edge.to) {
graph.setEdge(edge.from, edge.to);
}
});
//auto-distribute
dagre.layout(graph);
return graph.nodes().map(node => graph.node(node));
}
function mapElements(model) {
// dagre compatible format
return model.nodes.map(node => ({ id: node.id, metadata: { ...size, id: node.id } }));
}
function mapEdges(model) {
// returns links which connects nodes
// we check are there both from and to nodes in the model. Sometimes links can be detached
return model.links
.map(link => ({
from: link.source,
to: link.target
}))
.filter(
item => model.nodes.find(node => node.id === item.from) && model.nodes.find(node => node.id === item.to)
);
}

120
demos/demo-dagre/index.tsx Normal file
View File

@@ -0,0 +1,120 @@
import {
DiagramEngine,
DefaultNodeFactory,
DefaultLinkFactory,
DiagramModel,
DefaultNodeModel,
LinkModel,
DefaultPortModel,
DiagramWidget
} from "storm-react-diagrams";
import { distributeElements } from "./dagre-utils";
import * as React from "react";
import { DemoWorkspaceWidget } from "../.helpers/DemoWorkspaceWidget";
function createNode(name) {
return new DefaultNodeModel(name, "rgb(0,192,255)");
}
let count = 0;
function connectNodes(nodeFrom, nodeTo) {
//just to get id-like structure
count++;
const portOut = nodeFrom.addPort(new DefaultPortModel(true, `${nodeFrom.name}-out-${count}`, "Out"));
const portTo = nodeTo.addPort(new DefaultPortModel(false, `${nodeFrom.name}-to-${count}`, "IN"));
return portOut.link(portTo);
}
/**
* Tests auto distribution
*/
class Demo8Widget extends React.Component<any, any> {
constructor(props) {
super(props);
this.state = {};
this.autoDistribute = this.autoDistribute.bind(this);
}
autoDistribute() {
const { engine } = this.props;
const model = engine.getDiagramModel();
let distributedModel = getDistributedModel(engine, model);
engine.setModel(distributedModel);
this.forceUpdate();
}
render() {
const { engine } = this.props;
return (
<DemoWorkspaceWidget buttons={<button onClick={this.autoDistribute}>Re-distribute</button>}>
<DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />
</DemoWorkspaceWidget>
);
}
}
function getDistributedModel(engine, model) {
const serialized = model.serializeDiagram();
const distributedSerializedDiagram = distributeElements(serialized);
//deserialize the model
let deSerializedModel = new DiagramModel();
deSerializedModel.deSerializeDiagram(distributedSerializedDiagram, engine);
return deSerializedModel;
}
export default () => {
//1) setup the diagram engine
let engine = new DiagramEngine();
engine.installDefaults();
//2) setup the diagram model
let model = new DiagramModel();
//3) create a default nodes
let nodesFrom = [];
let nodesTo = [];
nodesFrom.push(createNode("from-1"));
nodesFrom.push(createNode("from-2"));
nodesFrom.push(createNode("from-3"));
nodesTo.push(createNode("to-1"));
nodesTo.push(createNode("to-2"));
nodesTo.push(createNode("to-3"));
//4) link nodes together
let links = nodesFrom.map((node, index) => {
return connectNodes(node, nodesTo[index]);
});
// more links for more complicated diagram
links.push(connectNodes(nodesFrom[0], nodesTo[1]));
links.push(connectNodes(nodesTo[0], nodesFrom[1]));
links.push(connectNodes(nodesFrom[1], nodesTo[2]));
// initial random position
nodesFrom.forEach((node, index) => {
node.x = index * 70;
model.addNode(node);
});
nodesTo.forEach((node, index) => {
node.x = index * 70;
node.y = 100;
model.addNode(node);
});
links.forEach(link => {
model.addLink(link);
});
//5) load model into engine
let model2 = getDistributedModel(engine, model);
engine.setModel(model2);
return <Demo8Widget engine={engine} />;
};

View File

@@ -0,0 +1,44 @@
import * as SRD from "storm-react-diagrams";
/**
* @author Dylan Vorster
*/
export class Application {
protected activeModel: SRD.DiagramModel;
protected diagramEngine: SRD.DiagramEngine;
constructor() {
this.diagramEngine = new SRD.DiagramEngine();
this.diagramEngine.installDefaults();
this.newModel();
}
public newModel() {
this.activeModel = new SRD.DiagramModel();
this.diagramEngine.setModel(this.activeModel);
//3-A) create a default node
var node1 = new SRD.DefaultNodeModel("Node 1", "rgb(0,192,255)");
let port = node1.addOutPort("Out");
node1.setPosition(100, 100);
//3-B) create another default node
var node2 = new SRD.DefaultNodeModel("Node 2", "rgb(192,255,0)");
let port2 = node2.addInPort("In");
node2.setPosition(400, 100);
// link the ports
let link1 = port.link(port2);
this.activeModel.addAll(node1, node2, link1);
}
public getActiveDiagram(): SRD.DiagramModel {
return this.activeModel;
}
public getDiagramEngine(): SRD.DiagramEngine {
return this.diagramEngine;
}
}

View File

@@ -1,10 +1,9 @@
import * as React from "react";
import * as _ from "lodash";
import { TrayWidget } from "./TrayWidget";
import { DiagramWidget } from "../../../src/main";
import { Application } from "../Application";
import { TrayItemWidget } from "./TrayItemWidget";
import { DefaultNodeModel, DefaultPortModel } from "../../../src/main";
import { DefaultNodeModel, DiagramWidget } from "storm-react-diagrams";
export interface BodyWidgetProps {
app: Application;
@@ -46,10 +45,10 @@ export class BodyWidget extends React.Component<BodyWidgetProps, BodyWidgetState
var node = null;
if (data.type === "in") {
node = new DefaultNodeModel("Node " + (nodesCount + 1), "rgb(192,255,0)");
node.addPort(new DefaultPortModel(true, "in-1", "In"));
node.addInPort("In");
} else {
node = new DefaultNodeModel("Node " + (nodesCount + 1), "rgb(0,192,255)");
node.addPort(new DefaultPortModel(false, "out-1", "Out"));
node.addOutPort("Out");
}
var points = this.props.app.getDiagramEngine().getRelativeMousePoint(event);
node.x = points.x;
@@ -64,7 +63,7 @@ export class BodyWidget extends React.Component<BodyWidgetProps, BodyWidgetState
event.preventDefault();
}}
>
<DiagramWidget diagramEngine={this.props.app.getDiagramEngine()} />
<DiagramWidget className="srd-demo-canvas" diagramEngine={this.props.app.getDiagramEngine()} />
</div>
</div>
</div>

View File

@@ -3,7 +3,7 @@ import * as React from "react";
import { BodyWidget } from "./components/BodyWidget";
import { Application } from "./Application";
require("./sass/main.scss");
import "./sass/main.scss";
export default () => {
var app = new Application();

View File

@@ -0,0 +1,47 @@
.body{
flex-grow: 1;
display: flex;
flex-direction: column;
min-height: 100%;
.header{
display: flex;
background: rgb(30,30,30);
flex-grow: 0;
flex-shrink: 0;
color: white;
font-family: Helvetica, Arial;
padding: 10px;
>*{
align-self:center;
}
}
.content{
display: flex;
flex-grow: 1;
.diagram-layer{
position: relative;
flex-grow: 1;
}
.tray{
min-width: 200px;
background: rgb(20,20,20);
flex-grow: 0;
flex-shrink: 0;
.tray-item{
color: white;
font-family: Helvetica, Arial;
padding: 5px;
margin: 0px 10px;
border: solid 1px;
border-radius: 5px;
margin-bottom: 2px;
cursor: pointer;
}
}
}
}

37
demos/demo-grid/index.tsx Normal file
View File

@@ -0,0 +1,37 @@
import { DiagramEngine, DiagramModel, DefaultNodeModel, LinkModel, DiagramWidget } from "storm-react-diagrams";
import * as React from "react";
/**
* Tests the grid size
*/
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.installDefaults();
//2) setup the diagram model
var model = new DiagramModel();
model.setGridSize(50);
//3-A) create a default node
var node1 = new DefaultNodeModel("Node 1", "rgb(0,192,255)");
let port = node1.addOutPort("Out");
node1.setPosition(100, 100);
//3-B) create another default node
var node2 = new DefaultNodeModel("Node 2", "rgb(192,255,0)");
let port2 = node2.addInPort("In");
node2.setPosition(400, 100);
// link the ports
let link1 = port.link(port2);
//4) add the models to the root graph
model.addAll(node1, node2, link1);
//5) load model into engine
engine.setModel(model);
//6) render the diagram!
return <DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />;
};

View File

@@ -0,0 +1,73 @@
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
DefaultPortModel,
DiagramWidget,
DefaultLinkModel
} from "storm-react-diagrams";
import * as React from "react";
import { DemoWorkspaceWidget } from "../.helpers/DemoWorkspaceWidget";
import { action } from "@storybook/addon-actions";
export default () => {
// setup the diagram engine
const engine = new DiagramEngine();
engine.installDefaults();
// setup the diagram model
const model = new DiagramModel();
// create four nodes
const node1 = new DefaultNodeModel("Node A", "rgb(0,192,255)");
const port1 = node1.addOutPort("Out");
node1.setPosition(100, 100);
const node2 = new DefaultNodeModel("Node B", "rgb(255,255,0)");
const port2 = node2.addInPort("In");
node2.setPosition(400, 50);
const node3 = new DefaultNodeModel("Node C (no label)", "rgb(192,255,255)");
const port3 = node3.addInPort("In");
node3.setPosition(450, 180);
const node4 = new DefaultNodeModel("Node D", "rgb(192,0,255)");
const port4 = node4.addInPort("In");
node4.setPosition(300, 250);
// link node A and B together and give it a label
const link1 = port1.link(port2);
(link1 as DefaultLinkModel).addLabel("Custom label 1");
(link1 as DefaultLinkModel).addLabel("Custom label 2");
// no label for A and C, just a link
const link2 = port1.link(port3);
// also a label for A and D
const link3 = port1.link(port4);
link3.setTargetPort(port4);
(link3 as DefaultLinkModel).addLabel("Emoji label: 🎉");
// add all to the main model
model.addAll(node1, node2, node3, node4, link1, link2, link3);
// load model into engine and render
engine.setModel(model);
return (
<DemoWorkspaceWidget
buttons={
<button
onClick={() => {
action("Serialized Graph")(JSON.stringify(model.serializeDiagram(), null, 2));
}}
>
Serialize Graph
</button>
}
>
<DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />
</DemoWorkspaceWidget>
);
};

View File

@@ -0,0 +1,49 @@
import * as React from "react";
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
DiagramWidget,
DiagramProps
} from "storm-react-diagrams";
/**
* Shows that a limit of points can be set for links
*/
export default () => {
// setup the diagram engine
var engine = new DiagramEngine();
engine.installDefaults();
var model = new DiagramModel();
//3-A) create a default node
var node1 = new DefaultNodeModel("Node 1", "rgb(0,192,255)");
let port = node1.addOutPort("Out");
node1.setPosition(100, 100);
//3-B) create another default node
var node2 = new DefaultNodeModel("Node 2", "rgb(192,255,0)");
let port2 = node2.addInPort("In");
node2.setPosition(400, 100);
// link the ports
let link1 = port.link(port2);
model.addAll(node1, node2, link1);
engine.setModel(model);
var props = {
diagramEngine: engine,
maxNumberPointsPerLink: 5
} as DiagramProps;
return (
<div>
<p>A maximum of 5 points can be created per link.</p>
<DiagramWidget className="srd-demo-canvas" {...props} />
</div>
);
};

View File

@@ -0,0 +1,62 @@
import * as React from "react";
import { action } from "@storybook/addon-actions";
import {
DiagramEngine,
DiagramModel,
DiagramProps,
DefaultNodeModel,
LinkModel,
DiagramWidget
} from "storm-react-diagrams";
/**
* Shows some of the events triggered when elements are selected
*/
export default () => {
// setup the diagram engine
var engine = new DiagramEngine();
engine.installDefaults();
var model = new DiagramModel();
// sample for link with simple line
var node1 = new DefaultNodeModel("Node 1", "rgb(255,99,66)");
var port1 = node1.addOutPort("Out");
node1.setPosition(100, 100);
var node2 = new DefaultNodeModel("Node 2", "rgb(192,255,0)");
var port2 = node2.addInPort("In");
node2.setPosition(400, 40);
var node3 = new DefaultNodeModel("Node 3", "rgb(128,99,255)");
var port3 = node3.addInPort("In");
node3.setPosition(300, 160);
//link the nodes
let link1 = port1.link(port2);
let link2 = port1.link(port3);
// add all the models
let models = model.addAll(node1, node2, node3, link1, link2);
// add a selection listener to each
models.forEach(item => {
item.addListener({
selectionChanged: action("selectionChanged")
});
});
engine.setModel(model);
var props = {
diagramEngine: engine,
maxNumberPointsPerLink: 0 // no extra points so link selection is fired straight away
} as DiagramProps;
return (
<div>
<p>Click the diagram elements to inspect some of the possible events.</p>
<DiagramWidget className="srd-demo-canvas" {...props} />
</div>
);
};

View File

@@ -0,0 +1,69 @@
import * as React from "react";
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
PointModel,
DiagramWidget,
DiagramProps
} from "storm-react-diagrams";
/**
*
* Shows how you can lock down the system so that the entire scene cant be interacted with.
*
* @Author Dylan Vorster
*/
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.installDefaults();
var model = new DiagramModel();
// sample for link with simple line (no additional points)
var node1 = new DefaultNodeModel("Node 1", "rgb(0,192,255)");
var port1 = node1.addOutPort("Out");
node1.setPosition(100, 100);
var node2 = new DefaultNodeModel("Node 2", "rgb(192,255,0)");
var port2 = node2.addInPort("In");
node2.setPosition(400, 100);
let link1 = port1.link(port2);
model.addAll(node1, node2, link1);
// sample for link with complex line (additional points)
var node3 = new DefaultNodeModel("Node 3", "rgb(0,192,255)");
var port3 = node3.addOutPort("Out");
node3.setPosition(100, 250);
var node4 = new DefaultNodeModel("Node 4", "rgb(192,255,0)");
var port4 = node4.addInPort("In");
node4.setPosition(400, 250);
var link2 = port3.link(port4);
link2.point(350, 225);
link2.point(200, 225);
model.addAll(node3, node4, link2);
engine.setModel(model);
//!========================================= <<<<<<<
model.setLocked(true);
var props = {
diagramEngine: engine,
allowLooseLinks: false,
allowCanvasTranslation: false,
allowCanvasZoom: false
} as DiagramProps;
//!========================================= <<<<<<<
return <DiagramWidget className="srd-demo-canvas" {...props} />;
};

View File

@@ -0,0 +1,86 @@
import { DiagramEngine, DiagramModel, DefaultNodeModel, LinkModel, DiagramWidget } from "storm-react-diagrams";
import * as React from "react";
import { DemoWorkspaceWidget } from "../.helpers/DemoWorkspaceWidget";
/**
* Tests the grid size
*/
class NodeDelayedPosition extends React.Component<any, any> {
constructor(props) {
super(props);
this.updatePosition = this.updatePosition.bind(this);
this.updatePositionViaSerialize = this.updatePositionViaSerialize.bind(this);
}
updatePosition() {
const { engine } = this.props;
let model = engine.getDiagramModel();
const nodes = model.getNodes();
let node = nodes[Object.keys(nodes)[0]];
node.setPosition(node.x + 30, node.y + 30);
this.forceUpdate();
}
updatePositionViaSerialize() {
let { engine } = this.props;
let model = engine.getDiagramModel();
let str = JSON.stringify(model.serializeDiagram());
let model2 = new DiagramModel();
let obj = JSON.parse(str);
let node = obj.nodes[0];
node.x += 30;
node.y += 30;
model2.deSerializeDiagram(obj, engine);
engine.setModel(model2);
this.forceUpdate();
}
render() {
const { engine } = this.props;
return (
<DemoWorkspaceWidget
buttons={[
<button key={1} onClick={this.updatePosition}>
Update position
</button>,
<button key={2} onClick={this.updatePositionViaSerialize}>
Update position via serialize
</button>
]}
>
<DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />
</DemoWorkspaceWidget>
);
}
}
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.installDefaults();
//2) setup the diagram model
var model = new DiagramModel();
//3-A) create a default node
var node1 = new DefaultNodeModel("Node 1", "rgb(0,192,255)");
var port1 = node1.addOutPort("Out");
node1.setPosition(100, 100);
//3-B) create another default node
var node2 = new DefaultNodeModel("Node 2", "rgb(192,255,0)");
var port2 = node2.addInPort("In");
node2.setPosition(400, 100);
//3-C) link the 2 nodes together
var link1 = port1.link(port2);
//4) add the models to the root graph
model.addAll(node1, node2, link1);
//5) load model into engine
engine.setModel(model);
//6) render the diagram!
return <NodeDelayedPosition engine={engine} model={model} />;
};

View File

@@ -0,0 +1,48 @@
import { DiagramEngine, DiagramModel, DefaultNodeModel, LinkModel, DiagramWidget } from "storm-react-diagrams";
import * as React from "react";
/**
*
* Simple stress test of the system, shows that it can handle many nodes, and
* retain good performance
*
* @Author Dylan Vorster
*/
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.installDefaults();
//2) setup the diagram model
var model = new DiagramModel();
for (var i = 0; i < 8; i++) {
for (var j = 0; j < 8; j++) {
generateNodes(model, i * 200, j * 100);
}
}
//5) load model into engine
engine.setModel(model);
//6) render the diagram!
return <DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />;
};
function generateNodes(model: DiagramModel, offsetX: number, offsetY: number) {
//3-A) create a default node
var node1 = new DefaultNodeModel("Node 1", "rgb(0,192,255)");
var port1 = node1.addOutPort("Out");
node1.setPosition(100 + offsetX, 100 + offsetY);
//3-B) create another default node
var node2 = new DefaultNodeModel("Node 2", "rgb(192,255,0)");
var port2 = node2.addInPort("In");
node2.setPosition(200 + offsetX, 100 + offsetY);
//3-C) link the 2 nodes together
var link1 = port1.link(port2);
//4) add the models to the root graph
model.addAll(node1, node2, link1);
}

View File

@@ -0,0 +1,60 @@
import { DiagramEngine, DiagramModel, DefaultNodeModel, LinkModel } from "storm-react-diagrams";
import * as React from "react";
import { DemoWorkspaceWidget } from "../.helpers/DemoWorkspaceWidget";
import { action } from "@storybook/addon-actions";
import * as beautify from "json-beautify";
import {DeserializeEvent} from "../../../react-canvas/src/models/BaseModel";
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.installDefaults();
//2) setup the diagram model
var model = new DiagramModel();
//3-A) create a default node
var node1 = new DefaultNodeModel("Node 1", "rgb(0,192,255)");
var port1 = node1.addOutPort("Out");
node1.setPosition(100, 100);
//3-B) create another default node
var node2 = new DefaultNodeModel("Node 2", "rgb(192,255,0)");
var port2 = node2.addInPort("In");
node2.setPosition(400, 100);
//3-C) link the 2 nodes together
var link1 = port1.link(port2);
//4) add the models to the root graph
model.addAll(node1, node2, link1);
//5) load model into engine
engine.setModel(model);
//!------------- SERIALIZING ------------------
var str = JSON.stringify(model.serialize());
//!------------- DESERIALIZING ----------------
var model2 = new DiagramModel();
model2.deSerialize(new DeserializeEvent(JSON.parse(str), engine));
engine.setModel(model2);
return (
<DemoWorkspaceWidget
buttons={
<button
onClick={() => {
action("Serialized Graph")(beautify(model2.serializeDiagram(), null, 2, 80));
}}
>
Serialize Graph
</button>
}
>
<DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />
</DemoWorkspaceWidget>
);
};

View File

@@ -0,0 +1,38 @@
import { DiagramEngine, DiagramModel, DefaultNodeModel, LinkModel, DiagramWidget } from "storm-react-diagrams";
import * as React from "react";
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.installDefaults();
//2) setup the diagram model
var model = new DiagramModel();
//3-A) create a default node
var node1 = new DefaultNodeModel("Node 1", "rgb(0,192,255)");
var port1 = node1.addOutPort("Out");
node1.setPosition(100, 100);
//3-B) create another default node
var node2 = new DefaultNodeModel("Node 2", "rgb(192,255,0)");
var port2 = node2.addInPort("In");
node2.setPosition(400, 100);
//3-C) link the 2 nodes together
var link1 = port1.link(port2);
//3-D) create an orphaned node
var node3 = new DefaultNodeModel("Node 3", "rgb(0,192,255)");
node3.addOutPort("Out");
node3.setPosition(100, 200);
//4) add the models to the root graph
model.addAll(node1, node2, node3, link1);
//5) load model into engine
engine.setModel(model);
//6) render the diagram!
return <DiagramWidget className="srd-demo-canvas" diagramEngine={engine} allowLooseLinks={false} />;
};

View File

@@ -0,0 +1,9 @@
# Simple Usage
Welcome to STORM React Diagrams (SRD). SRD is a no-nonsense easy to use library for creating
flow diagrams in the web that can ultimately represent any type of process/network/graph etc..
<!-- STORY -->
Try moving around one of the nodes or clicking and dragging the links to create new link anchors (points).
You can also zoom the canvas using the mouse wheel / scroll gesture and drag to select multiple entities on the graph by shift + dragging the mouse.

View File

@@ -0,0 +1,41 @@
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
DiagramWidget,
DefaultLinkModel
} from "storm-react-diagrams";
import * as React from "react";
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.installDefaults();
//2) setup the diagram model
var model = new DiagramModel();
//3-A) create a default node
var node1 = new DefaultNodeModel("Node 1", "rgb(0,192,255)");
let port1 = node1.addOutPort("Out");
node1.setPosition(100, 100);
//3-B) create another default node
var node2 = new DefaultNodeModel("Node 2", "rgb(192,255,0)");
let port2 = node2.addInPort("In");
node2.setPosition(400, 100);
// link the ports
let link1 = port1.link(port2);
(link1 as DefaultLinkModel).addLabel("Hello World!");
//4) add the models to the root graph
model.addAll(node1, node2, link1);
//5) load model into engine
engine.setModel(model);
//6) render the diagram!
return <DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />;
};

View File

@@ -0,0 +1,68 @@
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
DefaultPortModel,
DiagramWidget
} from "storm-react-diagrams";
import * as React from "react";
import { DemoWorkspaceWidget } from "../.helpers/DemoWorkspaceWidget";
import { action } from "@storybook/addon-actions";
export default () => {
// setup the diagram engine
const engine = new DiagramEngine();
engine.installDefaults();
// setup the diagram model
const model = new DiagramModel();
// create four nodes in a way that straight links wouldn't work
const node1 = new DefaultNodeModel("Node A", "rgb(0,192,255)");
const port1 = node1.addPort(new DefaultPortModel(false, "out-1", "Out"));
node1.setPosition(340, 350);
const node2 = new DefaultNodeModel("Node B", "rgb(255,255,0)");
const port2 = node2.addPort(new DefaultPortModel(false, "out-1", "Out"));
node2.setPosition(240, 80);
const node3 = new DefaultNodeModel("Node C", "rgb(192,255,255)");
const port3 = node3.addPort(new DefaultPortModel(true, "in-1", "In"));
node3.setPosition(540, 180);
const node4 = new DefaultNodeModel("Node D", "rgb(192,0,255)");
const port4 = node4.addPort(new DefaultPortModel(true, "in-1", "In"));
node4.setPosition(95, 185);
const node5 = new DefaultNodeModel("Node E", "rgb(192,255,0)");
node5.setPosition(250, 180);
// linking things together
const link1 = port1.link(port4);
const link2 = port2.link(port3);
// add all to the main model
model.addAll(node1, node2, node3, node4, node5, link1, link2);
// load model into engine and render
engine.setModel(model);
return (
<DemoWorkspaceWidget
buttons={
<button
onClick={() => {
action("Serialized Graph")(JSON.stringify(model.serializeDiagram(), null, 2));
}}
>
Serialize Graph
</button>
}
>
<DiagramWidget
className="srd-demo-canvas"
diagramEngine={engine}
smartRouting={true}
maxNumberPointsPerLink={0}
/>
</DemoWorkspaceWidget>
);
};

View File

@@ -0,0 +1,61 @@
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
DefaultPortModel,
DiagramWidget
} from "storm-react-diagrams";
import * as React from "react";
import { DemoWorkspaceWidget } from "../.helpers/DemoWorkspaceWidget";
/**
*
* Simple stress test of the system plus zoom to fit function
*
* @Author Dylan Vorster
*/
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.installDefaults();
//2) setup the diagram model
var model = new DiagramModel();
for (var i = 0; i < 8; i++) {
for (var j = 0; j < 8; j++) {
generateNodes(model, i * 200, j * 100);
}
}
//5) load model into engine
engine.setModel(model);
//6) render the diagram!
return (
<DemoWorkspaceWidget
buttons={<button onClick={() => engine.getCanvasWidget().zoomToFit()}>Zoom to fit</button>}
>
<DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />
</DemoWorkspaceWidget>
);
};
function generateNodes(model: DiagramModel, offsetX: number, offsetY: number) {
//3-A) create a default node
var node1 = new DefaultNodeModel("Node 1", "rgb(0,192,255)");
var port1 = node1.addOutPort("Out");
node1.setPosition(100 + offsetX, 100 + offsetY);
//3-B) create another default node
var node2 = new DefaultNodeModel("Node 2", "rgb(192,255,0)");
var port2 = node2.addInPort("In");
node2.setPosition(200 + offsetX, 100 + offsetY);
//3-C) link the 2 nodes together
var link1 = port1.link(port2);
//4) add the models to the root graph
model.addAll(node1, node2, link1);
}

View File

@@ -1,2 +0,0 @@
/// <reference types="react" />
export default function render(): JSX.Element;

View File

@@ -1,49 +0,0 @@
import {
DiagramEngine,
DefaultNodeFactory,
DefaultLinkFactory,
DiagramModel,
DefaultNodeModel,
LinkModel,
DefaultPortModel,
DiagramWidget
} from "../../src/main";
import * as React from "react";
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.registerNodeFactory(new DefaultNodeFactory());
engine.registerLinkFactory(new DefaultLinkFactory());
//2) setup the diagram model
var model = new DiagramModel();
//3-A) create a default node
var node1 = new DefaultNodeModel("Node 1", "rgb(0,192,255)");
var port1 = node1.addPort(new DefaultPortModel(false, "out-1", "Out"));
node1.x = 100;
node1.y = 100;
//3-B) create another default node
var node2 = new DefaultNodeModel("Node 2", "rgb(192,255,0)");
var port2 = node2.addPort(new DefaultPortModel(true, "in-1", "IN"));
node2.x = 400;
node2.y = 100;
//3-C) link the 2 nodes together
var link1 = new LinkModel();
link1.setSourcePort(port1);
link1.setTargetPort(port2);
//4) add the models to the root graph
model.addNode(node1);
model.addNode(node2);
model.addLink(link1);
//5) load model into engine
engine.setDiagramModel(model);
//6) render the diagram!
return <DiagramWidget diagramEngine={engine} />;
};

View File

@@ -1,64 +0,0 @@
import {
DiagramEngine,
DefaultNodeFactory,
DefaultLinkFactory,
DiagramModel,
DefaultNodeModel,
LinkModel,
DefaultPortModel,
DiagramWidget
} from "../../src/main";
import * as React from "react";
/**
*
* Simple stress test of the system, shows that it can handle many nodes, and
* retain good performance
*
* @Author Dylan Vorster
*/
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.registerNodeFactory(new DefaultNodeFactory());
engine.registerLinkFactory(new DefaultLinkFactory());
function generateNodes(model: DiagramModel, offsetX: number, offsetY: number) {
//3-A) create a default node
var node1 = new DefaultNodeModel("Node 1", "rgb(0,192,255)");
var port1 = node1.addPort(new DefaultPortModel(false, "out-1", "Out"));
node1.x = 100 + offsetX;
node1.y = 100 + offsetY;
//3-B) create another default node
var node2 = new DefaultNodeModel("Node 2", "rgb(192,255,0)");
var port2 = node2.addPort(new DefaultPortModel(true, "in-1", "IN"));
node2.x = 200 + offsetX;
node2.y = 100 + offsetY;
//3-C) link the 2 nodes together
var link1 = new LinkModel();
link1.setSourcePort(port1);
link1.setTargetPort(port2);
//4) add the models to the root graph
model.addNode(node1);
model.addNode(node2);
model.addLink(link1);
}
//2) setup the diagram model
var model = new DiagramModel();
for (var i = 0; i < 8; i++) {
for (var j = 0; j < 8; j++) {
generateNodes(model, i * 200, j * 100);
}
}
//5) load model into engine
engine.setDiagramModel(model);
//6) render the diagram!
return <DiagramWidget diagramEngine={engine} />;
};

View File

@@ -1,23 +0,0 @@
import * as SRD from "../../src/main";
import { DiamondNodeModel } from "./DiamondNodeModel";
import { DiamondPortModel } from "./DiamondPortModel";
export class DiamondNodeFactory extends SRD.AbstractInstanceFactory<DiamondNodeModel> {
constructor() {
super("DiamondNodeModel");
}
getInstance() {
return new DiamondNodeModel();
}
}
export class DiamondPortFactory extends SRD.AbstractInstanceFactory<DiamondPortModel> {
constructor() {
super("DiamondPortModel");
}
getInstance() {
return new DiamondPortModel();
}
}

View File

@@ -1,22 +0,0 @@
import * as SRD from "../../src/main";
import * as _ from "lodash";
export class DiamondPortModel extends SRD.PortModel {
position: string | "top" | "bottom" | "left" | "right";
constructor(pos: string = "top") {
super(pos);
this.position = pos;
}
serialize() {
return _.merge(super.serialize(), {
position: this.position
});
}
deSerialize(data: any) {
super.deSerialize(data);
this.position = data.position;
}
}

View File

@@ -1,12 +0,0 @@
import * as SRD from "../../src/main";
import { DiamonNodeWidgetFactory } from "./DiamondNodeWidget";
export class DiamondWidgetFactory extends SRD.NodeWidgetFactory {
constructor() {
super("diamond");
}
generateReactWidget(diagramEngine: SRD.DiagramEngine, node: SRD.NodeModel): JSX.Element {
return DiamonNodeWidgetFactory({ node: node });
}
}

View File

@@ -1,88 +0,0 @@
import {
DiagramEngine,
DefaultNodeFactory,
DefaultLinkFactory,
DiagramModel,
DefaultNodeModel,
LinkModel,
DefaultPortModel,
DiagramWidget
} from "../../src/main";
import * as React from "react";
import { DiamondNodeModel } from "./DiamondNodeModel";
import { DiamondWidgetFactory } from "./DiamondWidgetFactory";
/**
* @Author Dylan Vorster
*/
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.registerNodeFactory(new DefaultNodeFactory());
engine.registerLinkFactory(new DefaultLinkFactory());
engine.registerNodeFactory(new DiamondWidgetFactory());
//2) setup the diagram model
var model = new DiagramModel();
//3-A) create a default node
var node1 = new DefaultNodeModel("Node 1", "rgb(0,192,255)");
var port1 = node1.addPort(new DefaultPortModel(false, "out-1", "Out"));
node1.x = 100;
node1.y = 150;
//3-B) create our new custom node
var node2 = new DiamondNodeModel();
node2.x = 400;
node2.y = 100;
var node3 = new DefaultNodeModel("Node 3", "red");
var port3 = node3.addPort(new DefaultPortModel(true, "in-1", "In"));
node3.x = 800;
node3.y = 150;
//3-C) link the 2 nodes together
var link1 = new LinkModel();
link1.setSourcePort(port1);
link1.setTargetPort(node2.ports["left"]);
var link2 = new LinkModel();
link2.setSourcePort(node2.ports["right"]);
link2.setTargetPort(port3);
//4) add the models to the root graph
model.addNode(node1);
model.addNode(node2);
model.addNode(node3);
model.addLink(link1);
model.addLink(link2);
//5) load model into engine
engine.setDiagramModel(model);
//6) render the diagram!
return <DiagramWidget diagramEngine={engine} />;
// //!------------- SERIALIZING / DESERIALIZING ------------
//
// //we need this to help the system know what models to create form the JSON
// engine.registerInstanceFactory(new SRD.DefaultNodeInstanceFactory());
// engine.registerInstanceFactory(new SRD.DefaultPortInstanceFactory());
// engine.registerInstanceFactory(new SRD.LinkInstanceFactory());
// engine.registerInstanceFactory(new DiamondNodeFactory());
// engine.registerInstanceFactory(new DiamondPortFactory());
//
// //serialize the model
// var str = JSON.stringify(model.serializeDiagram());
// console.log(str);
//
// //deserialize the model
// var model2 = new SRD.DiagramModel();
// model2.deSerializeDiagram(JSON.parse(str),engine);
// engine.setDiagramModel(model2);
// console.log(model2);
//
// //re-render the model
// ReactDOM.render(React.createElement(SRD.DiagramWidget,{diagramEngine: engine}), document.body);
};

View File

@@ -1,77 +0,0 @@
import * as SRD from "../../src/main";
import * as React from "react";
import { DiagramEngine, DiagramModel, DefaultNodeModel, LinkModel, PointModel, DiagramWidget } from "../../src/main";
/**
*
* Shows how you can lock down the system so that the entire scene cant be interacted with.
*
* @Author Dylan Vorster
*/
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.registerNodeFactory(new SRD.DefaultNodeFactory());
engine.registerLinkFactory(new SRD.DefaultLinkFactory());
var model = new DiagramModel();
// sample for link with simple line (no additional points)
var node1 = new DefaultNodeModel("Node 1", "rgb(0,192,255)");
var port1 = node1.addPort(new SRD.DefaultPortModel(false, "out-1", "Out"));
node1.x = 100;
node1.y = 100;
var node2 = new DefaultNodeModel("Node 2", "rgb(192,255,0)");
var port2 = node2.addPort(new SRD.DefaultPortModel(true, "in-1", "IN"));
node2.x = 400;
node2.y = 100;
var link1 = new LinkModel();
link1.setSourcePort(port1);
link1.setTargetPort(port2);
model.addNode(node1);
model.addNode(node2);
model.addLink(link1);
// sample for link with complex line (additional points)
var node3 = new DefaultNodeModel("Node 3", "rgb(0,192,255)");
var port3 = node3.addPort(new SRD.DefaultPortModel(false, "out-2", "Out"));
node3.x = 100;
node3.y = 250;
var node4 = new DefaultNodeModel("Node 4", "rgb(192,255,0)");
var port4 = node4.addPort(new SRD.DefaultPortModel(true, "in-2", "IN"));
node4.x = 400;
node4.y = 250;
var link2 = new LinkModel();
link2.setSourcePort(port3);
link2.setTargetPort(port4);
var additionalPoint1 = new PointModel(link2, { x: 350, y: 225 });
link2.addPoint(additionalPoint1);
var additionalPoint2 = new PointModel(link2, { x: 200, y: 225 });
link2.addPoint(additionalPoint2);
model.addNode(node3);
model.addNode(node4);
model.addLink(link2);
engine.setDiagramModel(model);
//!========================================= <<<<<<<
model.setLocked(true);
var props = {
diagramEngine: engine,
allowLooseLinks: false,
allowCanvasTranslation: false,
allowCanvasZoom: false
} as SRD.DiagramProps;
//!========================================= <<<<<<<
return <DiagramWidget {...props} />;
};

View File

@@ -1,48 +0,0 @@
import * as SRD from "../../src/main";
/**
* @author Dylan Vorster
*/
export class Application {
protected activeModel: SRD.DiagramModel;
protected diagramEngine: SRD.DiagramEngine;
constructor() {
this.diagramEngine = new SRD.DiagramEngine();
this.diagramEngine.registerNodeFactory(new SRD.DefaultNodeFactory());
this.diagramEngine.registerLinkFactory(new SRD.DefaultLinkFactory());
this.newModel();
}
public newModel() {
this.activeModel = new SRD.DiagramModel();
this.diagramEngine.setDiagramModel(this.activeModel);
var node1 = new SRD.DefaultNodeModel("Node 1", "rgb(0,192,255)");
var port1 = node1.addPort(new SRD.DefaultPortModel(false, "out-1", "Out"));
node1.x = 100;
node1.y = 100;
var node2 = new SRD.DefaultNodeModel("Node 2", "rgb(192,255,0)");
var port2 = node2.addPort(new SRD.DefaultPortModel(true, "in-1", "IN"));
node2.x = 400;
node2.y = 100;
var link1 = new SRD.LinkModel();
link1.setSourcePort(port1);
link1.setTargetPort(port2);
this.activeModel.addNode(node1);
this.activeModel.addNode(node2);
this.activeModel.addLink(link1);
}
public getActiveDiagram(): SRD.DiagramModel {
return this.activeModel;
}
public getDiagramEngine(): SRD.DiagramEngine {
return this.diagramEngine;
}
}

View File

@@ -1,80 +0,0 @@
.body{
flex-grow: 1;
display: flex;
flex-direction: column;
.header{
display: flex;
background: rgb(30,30,30);
flex-grow: 0;
flex-shrink: 0;
color: white;
font-family: Helvetica, Arial;
padding: 10px;
>*{
align-self:center;
}
}
.content{
display: flex;
flex-grow: 1;
.diagram-layer{
position: relative;
flex-grow: 1;
}
.tray{
min-width: 200px;
background: rgb(20,20,20);
flex-grow: 0;
flex-shrink: 0;
.tray-item{
color: white;
font-family: Helvetica, Arial;
padding: 5px;
margin: 0px 10px;
border: solid 1px;
border-radius: 5px;
margin-bottom: 2px;
cursor: pointer;
}
}
}
}
.storm-diagrams-canvas{
$color: rgba(white, .05);
background: rgba(black,0.2);
background-image:
linear-gradient(0deg,
transparent 24%,
$color 25%,
$color 26%,
transparent 27%,
transparent 74%,
$color 75%,
$color 76%,
transparent 77%,
transparent),
linear-gradient(90deg,
transparent 24%,
$color 25%,
$color 26%,
transparent 27%,
transparent 74%,
$color 75%,
$color 76%,
transparent 77%,
transparent);
background-size:50px 50px;
path{
stroke: rgba(white,0.5);
}
.pointui{
fill: rgba(white,0.5);
}
}

View File

@@ -1,67 +0,0 @@
import {
DiagramEngine,
DefaultNodeFactory,
DefaultLinkFactory,
DiagramModel,
DefaultNodeModel,
LinkModel,
DefaultPortModel,
DiagramWidget,
DefaultNodeInstanceFactory,
DefaultPortInstanceFactory,
LinkInstanceFactory
} from "../../src/main";
import * as React from "react";
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.registerNodeFactory(new DefaultNodeFactory());
engine.registerLinkFactory(new DefaultLinkFactory());
//2) setup the diagram model
var model = new DiagramModel();
//3-A) create a default node
var node1 = new DefaultNodeModel("Node 1", "rgb(0,192,255)");
var port1 = node1.addPort(new DefaultPortModel(false, "out-1", "Out"));
node1.x = 100;
node1.y = 100;
//3-B) create another default node
var node2 = new DefaultNodeModel("Node 2", "rgb(192,255,0)");
var port2 = node2.addPort(new DefaultPortModel(true, "in-1", "IN"));
node2.x = 400;
node2.y = 100;
//3-C) link the 2 nodes together
var link1 = new LinkModel();
link1.setSourcePort(port1);
link1.setTargetPort(port2);
//4) add the models to the root graph
model.addNode(node1);
model.addNode(node2);
model.addLink(link1);
//5) load model into engine
engine.setDiagramModel(model);
//!------------- SERIALIZING ------------------
var str = JSON.stringify(model.serializeDiagram());
//!------------- DESERIALIZING ----------------
//we need this to help the system know what models to create form the JSON
engine.registerInstanceFactory(new DefaultNodeInstanceFactory());
engine.registerInstanceFactory(new DefaultPortInstanceFactory());
engine.registerInstanceFactory(new LinkInstanceFactory());
//deserialize the model
var model2 = new DiagramModel();
model2.deSerializeDiagram(JSON.parse(str), engine);
engine.setDiagramModel(model2);
return <DiagramWidget diagramEngine={engine} />;
};

View File

@@ -1,53 +0,0 @@
import {
DiagramEngine,
DefaultNodeFactory,
DefaultLinkFactory,
DiagramModel,
DefaultNodeModel,
LinkModel,
DefaultPortModel,
DiagramWidget
} from "../../src/main";
import * as React from "react";
/**
* Tests the grid size
*/
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.registerNodeFactory(new DefaultNodeFactory());
engine.registerLinkFactory(new DefaultLinkFactory());
//2) setup the diagram model
var model = new DiagramModel();
model.setGridSize(50);
//3-A) create a default node
var node1 = new DefaultNodeModel("Node 1", "rgb(0,192,255)");
var port1 = node1.addPort(new DefaultPortModel(false, "out-1", "Out"));
node1.x = 100;
node1.y = 100;
//3-B) create another default node
var node2 = new DefaultNodeModel("Node 2", "rgb(192,255,0)");
var port2 = node2.addPort(new DefaultPortModel(true, "in-1", "IN"));
node2.x = 400;
node2.y = 100;
//3-C) link the 2 nodes together
var link1 = new LinkModel();
link1.setSourcePort(port1);
link1.setTargetPort(port2);
//4) add the models to the root graph
model.addNode(node1);
model.addNode(node2);
model.addLink(link1);
//5) load model into engine
engine.setDiagramModel(model);
//6) render the diagram!
return <DiagramWidget diagramEngine={engine} />;
};

View File

@@ -1,36 +1,129 @@
import * as React from "react";
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import { storiesOf, addDecorator } from "@storybook/react";
import { setOptions } from "@storybook/addon-options";
import { host } from "storybook-host";
import { Helper } from "./.helpers/Helper";
import { Toolkit } from "../src/Toolkit";
import demo1 from "./demo1/index";
import demo2 from "./demo2/index";
import demo3 from "./demo3/index";
import demo4 from "./demo4/index";
import demo5 from "./demo5/index";
import demo6 from "./demo6/index";
import demo7 from "./demo7/index";
//include the SCSS for the demo
import "./.helpers/demo.scss";
require("./test.scss");
Toolkit.TESTING = true;
storiesOf("React Diagrams", module)
.add("Simple Example", () => {
return demo1();
addDecorator(
host({
cropMarks: false,
height: "100%",
width: "100%",
padding: 20
})
.add("Performance Test", () => {
return demo2();
})
.add("Custom Diamond Widget", () => {
return demo3();
})
.add("Locked Widget", () => {
return demo4();
})
.add("Embedded diagram", () => {
return demo5();
})
.add("Serializing and Deserializing", () => {
return demo6();
})
.add("Grid Size", () => {
return demo7();
});
);
setOptions({
name: "STORM React Diagrams",
url: "https://github.com/projectstorm/react-diagrams",
addonPanelInRight: true
});
storiesOf("Simple Usage", module)
.add(
"Simple example",
Helper.makeDemo(
require("./demo-simple/index").default(),
require("!!raw-loader!./demo-simple/index"),
require("./demo-simple/docs.md")
)
)
// .add(
// "Simple flow example",
// Helper.makeDemo(require("./demo-simple-flow/index").default(), require("!!raw-loader!./demo-simple-flow/index"))
// )
// .add(
// "Performance demo",
// Helper.makeDemo(require("./demo-performance/index").default(), require("!!raw-loader!./demo-performance/index"))
// )
// .add(
// "Locked widget",
// Helper.makeDemo(require("./demo-locks/index").default(), require("!!raw-loader!./demo-locks/index"))
// )
// .add(
// "Canvas grid size",
// Helper.makeDemo(require("./demo-grid/index").default(), require("!!raw-loader!./demo-grid/index"))
// )
// .add(
// "Limiting link points",
// Helper.makeDemo(
// require("./demo-limit-points/index").default(),
// require("!!raw-loader!./demo-limit-points/index")
// )
// )
// .add(
// "Events and listeners",
// Helper.makeDemo(require("./demo-listeners/index").default(), require("!!raw-loader!./demo-listeners/index"))
// )
// .add(
// "Zoom to fit",
// Helper.makeDemo(require("./demo-zoom-to-fit/index").default(), require("!!raw-loader!./demo-zoom-to-fit/index"))
// )
// .add(
// "Links with labels",
// Helper.makeDemo(
// require("./demo-labelled-links/index").default(),
// require("!!raw-loader!./demo-labelled-links/index")
// )
// );
//
// storiesOf("Advanced Techniques", module)
// .add(
// "Clone Selected",
// Helper.makeDemo(require("./demo-cloning/index").default(), require("!!raw-loader!./demo-cloning/index"))
// )
// .add(
// "Serializing and de-serializing",
// Helper.makeDemo(require("./demo-serializing/index").default(), require("!!raw-loader!./demo-serializing/index"))
// )
// .add(
// "Programatically modifying graph",
// Helper.makeDemo(
// require("./demo-mutate-graph/index").default(),
// require("!!raw-loader!./demo-mutate-graph/index")
// )
// )
// .add(
// "Large application",
// Helper.makeDemo(
// require("./demo-drag-and-drop/index").default(),
// require("!!raw-loader!./demo-drag-and-drop/components/BodyWidget")
// )
// )
// .add(
// "Smart routing",
// Helper.makeDemo(
// require("./demo-smart-routing/index").default(),
// require("!!raw-loader!./demo-smart-routing/index")
// )
// );
//
// storiesOf("Custom Models", module)
// .add(
// "Custom diamond node",
// Helper.makeDemo(
// require("./demo-custom-node1/index").default(),
// require("!!raw-loader!./demo-custom-node1/index")
// )
// )
// .add(
// "Custom animated links",
// Helper.makeDemo(
// require("./demo-custom-link1/index").default(),
// require("!!raw-loader!./demo-custom-link1/index")
// )
// );
//
// storiesOf("3rd party libraries", module).add(
// "Auto Distribute (Dagre)",
// Helper.makeDemo(require("./demo-dagre/index").default(), require("!!raw-loader!./demo-dagre/index"))
// );
// enable this to log mouse location when writing new puppeteer tests
//Helper.logMousePosition()

View File

@@ -1,37 +0,0 @@
@import "../src/sass";
.storm-diagrams-canvas{
height: 400px;
background-color: rgb(60,60,60);
$color: rgba(white, .05);
background-image:
linear-gradient(0deg,
transparent 24%,
$color 25%,
$color 26%,
transparent 27%,
transparent 74%,
$color 75%,
$color 76%,
transparent 77%,
transparent),
linear-gradient(90deg,
transparent 24%,
$color 25%,
$color 26%,
transparent 27%,
transparent 74%,
$color 75%,
$color 76%,
transparent 77%,
transparent);
background-size:50px 50px;
path{
stroke: rgba(white,0.5);
}
.pointui{
fill: rgba(white,0.5);
}
}

10
demos/tslint.json Normal file
View File

@@ -0,0 +1,10 @@
{
"extends": [
"../tslint.json"
],
"rules": {
"no-console": false,
"max-classes-per-file": false,
"no-var-requires": false
}
}

View File

@@ -0,0 +1,76 @@
# Architecture Questions
Here I will try to answer any questions relating to the design of the system
### What was the inspiration for this library?
Joint JS (a fantastic library) + my need for rich HTML nodes + LabView + Blender Composite sub system
### Why render the nodes as HTML Elements and not SVG's?
My original requirement for this library stemmed from the requirement of wanting HTML nodes that would allow me to
embed rich controls such as input fields, drop downs and have the system treat such nodes as first class citizens.
I originally tried to make this work in JointJS, but ran into a number of problems of which this was a relatively big one.
JointJS does allow you to do this, but at the time of writing this library originally, I was having a lot of trouble
to make it work exactly like I needed it, and therefore decided from the very beginning that I would attempt this
with an HTML first mindset.
### Why Typescript?
Firstly, because it can transpile into any level of ECMAScript. This means that I don't need to break our the refactor tractor
every time ECMAScript decides it wants to add features which it should have done years ago.
I also ported it to Typescript to accommodate the heavy architectural changes I was starting to make. Since porting
the library to typescript, and seeing the project explode in size and complexity, I consider this the best decision
made with regards to this library so far.
Porting to typescript also afforded us a set of powerful features such as generics and static analysis that
all the project contributors have made exclusive use of.
Typescript is <3 typescript is life.
### Why not Flow instead of Typescript?
At the time when I first started evaluating languages that could transpile to ECMAScript, I was not so sold
on the supporting environment surrounding flow, and and found that there was better tooling to support
typescript, they are ultimately trying to do the same thing though, and I guess in the end, typescript just made
more sense.
### Why React ?
React is really efficient at rendering and managing HTML in a declarative manner. React has also become
one of the bigger industry standards and has a rich eco system that plays really well with typescript.
Apart from these notable points, I am really fond of React and wanted a diagramming library that takes full advantage of it,
and makes it easy for engineers to use its power as well, when extending this library.
### Why cant the Default models and widgets do this or that ?
They are intended to illustrate __how__ to use this library and act as a good starting point
to extend and show the capability. Ultimately I designed this library to be completely
pluggable in a way that you can use it as a library and not a framework. If the default widgets
are not good enough, then a good place to start is with creating your own models/factories/widgets.
### Model vs Widget
For those that are new to [Scene Graphs](https://en.wikipedia.org/wiki/Scene_graph) or are not familiar with concepts
such as [MVC](https://en.wikipedia.org/wiki/Modelviewcontroller), this library represents your entire graph as a model.
The model is a traversable graph that represents the nodes and links between them in a virtual manner. Your program (aka the business logic/layer)
can mutate this model imperatively or store snapshots decoratively of the complete model (via serialization) and then the engine and react
widgets will take care of the rendering. For this reason every model in the library is represented by a widget, and the factories glue it all together.
### How do I make my own elements?
Take a look at the __demos__ directory, with specific attention to the __DefaultNodeWidget__
That being said, the demos directory is an _example_ of how you can create your own elements.
A number of people want to use the defaults as is, which is cool,
but is recommended to create your own models/factories/widgets.
### How do I use the library?
Take a look at the demo folders, they have simple and complex examples of the complete usage.
A good example of a real-world example is Demo 5

View File

@@ -1,88 +0,0 @@
# Events
Each model (DiagramModel, NodeModel etc..) are all built ontop of an event system. You can listen for most of these events by registering
an event on the model itself.
## All models
All models will fire these events:
lockChanged?(entity: BaseEntity,locked: boolean)
Fires when the lock state of the entity changes. If an element is locked, it cannot be moved or deletes.
## All Base models excluding DiagramModel
selectionChanged?(item: BaseModel, isSelected:boolean)
When the _selected_ property of a model changes
entityRemoved?(item:any)
When the entity is going to be deleted. The DiagramModel listeners for this event to when to remove the model from itself.
## DiagramModel
nodesUpdated(node: any, isCreated:boolean)
When nodes are added or removed
linksUpdated(link: any, isCreated:boolean)
when links are added or removed
controlsUpdated() [DEPRECIATED]
_depreciated, use offsetUpdated and zoomUpdated instead_
offsetUpdated(model: DiagramModel,offsetX: number, offsetY: number)
to know when the canvas was translated in any direction
zoomUpdated(model: DiagramModel,zoom: number)
to know when the zoom level of the canvas was updated
## DiagramEngine
The diagram engine
nodeFactoriesUpdated
When node factories have been added or removed from the engine
linkFactoriesUpdated
When link factories have been added or removed from the engine
## LinkModel
sourcePortChanged?(item:LinkModel,target: null|PortModel)
targetPortChanged?(item:LinkModel,target: null|PortModel)
# Example of usage
```javascript
let model = new SRD.DiagramModel();
let node1 = new SRD.DefaultNodeModel("default","rgb(0,192,255)");
node1.addListener({
entityRemoved: (node) => {
console.log('Removed', node.id)
},
selectionChanged: (node, isSelected) => {
console.log(isSelected?'Selected':'Unselected', node)
}
});
model.addListener({
linksUpdated:(entity, isAdded) => {
console.log(isAdded?'added':'removed', entity)
},
nodesUpdated: (entity, isAdded) => {
console.log(isAdded?'added':'removed', entity)
}
});
```

142
docs/Getting Started.md Normal file
View File

@@ -0,0 +1,142 @@
# Getting started
## Installation via NPM
The first thing you need to do, is grab the distribution files on NPM. You can do this either using yarn or npm
**Via yarn:**
```
yarn install storm-react-diagrams
```
**Via npm:**
```
npm install storm-react-diagrams
```
When you run this in your project directory, this will install the library into node\_modules/storm-react-diagrams. You will then find a dist folder that contains all the minified and production ready code.
This will also install React and a few other dependencies that you need in order to use this library.
## Including the library
When including the library you will need both the javascript files as well as the raw BEM styles. Both are included in the dist folder and there are numerous ways to integrate them into your project:
#### Getting the javascript files
**Using Typescript / ES6: \(recommended\)**
```js
import * as SRD from "storm-react-diagrams"
```
**Using RequireJS:**
```js
var SRD = require("storm-react-diagrams)
```
**As a script tag \(not recommended\)**
```html
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="node_modules/storm-react-diagrams/dist/main.js">
```
#### Getting the CSS
**Using RequireJS / Typescript/ ES6 \(recommended\)**
Make sure you have the style-loader enabled and then:
```js
require("storm-react-diagrams/dist/style.min.css");
```
or make sure you have the sass-loader enabled and then:
```js
require("storm-react-diagrams/src/sass/main.scss");
```
If you are using typescript and get a "require function not found" then make sure to
```
yarn add @types/node
```
which will give you the typescript definition files for requireJS
**Using SASS:**
setup your include paths on webpack or lib sass using the following option
```
includePaths: ["node_modules"]
```
and then if you want the raw sass source code:
```sass
@import "~storm-react-diagrams/src/sass/main";
```
or if you want the minified css
```sass
@import "~storm-react-diagrams/dist/style.min";
```
**Using a style tag**
or if you want the minified css
```html
<link rel="stylesheet" href="node_modules/dist/style.min.css">
```
## Render your first diagram
In your library code
```js
// 1) setup the diagram engine
var engine = new SRD.DiagramEngine();
engine.installDefaultFactories();
// 2) setup the diagram model
var model = new SRD.DiagramModel();
// 3) create a default node
var node1 = new SRD.DefaultNodeModel("Node 1", "rgb(0,192,255)");
let port1 = node1.addOutPort("Out");
node1.setPosition(100, 100);
// 4) create another default node
var node2 = new SRD.DefaultNodeModel("Node 2", "rgb(192,255,0)");
let port2 = node2.addInPort("In");
node2.setPosition(400, 100);
// 5) link the ports
let link1 = port1.link(port2);
// 6) add the models to the root graph
model.addAll(node1, node2, link1);
// 7) load model into engine
engine.setDiagramModel(model);
```
And then create an instance of the diagram widget. An example of the simplest possible react widget to do this would be:
```jsx
function SimpleDiagramWidget(props) {
return <SRD.DiagramWidget diagramEngine={props.engine} />;
}
```

22
docs/Interactive Usage.md Normal file
View File

@@ -0,0 +1,22 @@
# End user usage
__Delete__ removes any selected items
![__Delete__](../images/rjdDelete.gif)
__Shift + Mouse Drag__ triggers a multi-selection box
![Shift + Mouse Drag](../images/mouseDrag.gif)
__Shift + Mouse Click__ selects the item (items can be multi-selected)
![Shift + Mouse Click](../images/shiftClick.gif)
__Mouse Drag__ drags the entire diagram
![Mouse Drag](../images/canvasDrag.gif)
__Mouse Wheel__ zooms the diagram in / out
![Mouse Wheel](../images/mouseWheel.gif)
__Click Link + Drag__ creates a new link point
![Click Link + Drag](../images/createPoint.gif)
__Click Node Port + Drag__ creates a new link
![Click Node Port + Drag](../images/createLink.gif)

View File

@@ -1,24 +0,0 @@
## Questions
#### Why didnt I render the nodes as SVG's?
Because its vastly better to render nodes as standard HTML so that we can embed input controls and not have
to deal with the complexities of trying to get SVG to work like we want it to. I also created this primarily to embed into
enterprise applications where the nodes themselves are highly interactive with buttons and other controls that cave when I try to use SVG.
#### Why Typescript?
Because it can transpile into any level of ECMA Script, and the library got really complicated, so I ported it to Typescript
to accommodate the heavy architectural changes I was starting to make. <3 Type Script
#### How do I make my own elements?
Take a look at the __defaults__ directory, with specific attention to the __DefaultNodeWidget__
That being said, the defaults directory is an EXAMPLE of how you can create your own elements. A number of people want to use the defaults as is, which is cool, but is recommended to create your own base node like the onw you see
#### How do I use the library?
Take a look at the demo folders, they have simple and complex examples of the complete usage.
A good example of a real-world example is Demo 5

33
docs/Testing.md Normal file
View File

@@ -0,0 +1,33 @@
# Testing
STORM React diagrams is tested two main ways.
## JEST Snapshot testing
With Jest snapshots, we render all the demos in the demo folder by automatically
looking into each `demo-*` folder and searching for an __index.tsx__ file.
For each file we find, we dynamically include it as a storybook story and assemble one big test.
This test then renders each demo in a deterministic way and compares it to the snapshot file
situated in __snapshots__. If the snapshots don't match, then something has changed and either the snapshot
needs to be updated, or the test is failing in which case we need to fix the code.
Snapshot testing does not test the functionality of the program but it is a first important step
to make sure that changes aren't having a drastic effect on the overall system.
In the event that the snapshot needs to be updated, please run `yarn run test -u` to overwrite
the snapshot file, and then make sure to your branch.
## End to end testing
To test the functionality of the library, we make use of e2e tests (end to end tests).
In this library, we spin up a headless chrome using pupeteer and interactively and programmatically
tell the mouse pointer to click and drag on various elements while making assertions along the way.
We use Jest for the assertions and the interactivity is handled by puppeteer. Due to the laborious nature
of writing e2e tests, there is a helper method that is provided in each test that makes interacting
with the diagrams a lot easier. Using this helper, you cna easily tell the mouse to drag links between nodes,
select them and also easily assert information about them. The important thing here, is that this helper
does not touch the model in any way, but is purely a helper for writing the tests themselves. Please
make use of this helper when writing tests, as it ensure that the tests are defensive in nature, and also
reduces the overhead of physically writing them.

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

BIN
images/example1.jpg Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

BIN
images/example2.jpg Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 870 KiB

BIN
images/example3.jpg Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

27
jest.config.js Normal file
View File

@@ -0,0 +1,27 @@
const path = require("path");
// jest.config.js
module.exports = {
verbose: true,
moduleFileExtensions: [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
],
transform: {
".*test_loader.*": path.join(__dirname, "tests", "helpers", "storybook-loader.js" ),
"^.+\\.tsx?$": "ts-jest",
},
moduleNameMapper:{
"\\.(scss|css|png)$": path.join(__dirname,"tests","helpers","css-mock.js"),
"storm-react-diagrams": path.join(__dirname, "src", "main")
},
roots:[
__dirname+'/tests'
],
testMatch: [
"**/*\.test\.tsx"
]
};

View File

@@ -1,53 +1,88 @@
{
"name": "storm-react-diagrams",
"version": "3.1.1",
"repository": {
"type": "git",
"url": "https://github.com/projectstorm/react-diagrams.git"
},
"keywords": [
"web",
"diagram",
"diagrams",
"react",
"typescript",
"flowchart",
"simple",
"links",
"nodes"
],
"main": "./dist/main.js",
"typings": "./dist/src/main",
"author": "dylanvorster",
"scripts": {
"storybook": "start-storybook -p 9001 -c .storybook",
"storybook:build": "build-storybook -c .storybook -o .out",
"storybook:github": "storybook-to-ghpages",
"prepublishOnly": "export NODE_ENV=production && webpack && ./node_modules/node-sass/bin/node-sass --output-style compressed ./src/sass.scss > ./dist/style.css",
"lintjs": "prettier --use-tabs --write \"{src,demos}/**/*.{ts,tsx}\" --print-width 120"
},
"dependencies": {
"@storybook/storybook-deployer": "^2.0.0",
"closest": "^0.0.1",
"lodash": "^4.17.4",
"react": "^15.6.1"
},
"devDependencies": {
"@storybook/addon-notes": "^3.2.7",
"@storybook/react": "^3.2.8",
"@types/lodash": "^4.14.74",
"@types/node": "^8.0.28",
"@types/react": "^16.0.5",
"awesome-typescript-loader": "^3.2.3",
"css-loader": "^0.28.7",
"node-sass": "^4.5.3",
"prettier": "^1.6.1",
"react-dom": "^15.6.1",
"sass-loader": "^6.0.6",
"source-map-loader": "^0.2.1",
"style-loader": "^0.18.2",
"tslint": "^5.7.0",
"typescript": "^2.5.2",
"webpack": "^3.5.6"
}
"name": "storm-react-diagrams",
"version": "5.1.1",
"repository": {
"type": "git",
"url": "https://github.com/projectstorm/react-diagrams.git"
},
"keywords": [
"web",
"diagram",
"diagrams",
"react",
"typescript",
"flowchart",
"simple",
"links",
"nodes"
],
"main": "./dist/main.js",
"typings": "./dist/@types/src/main",
"author": "dylanvorster",
"scripts": {
"build:ts": "webpack",
"build:ts:prod": "cross-env NODE_ENV=production webpack",
"build:sass:prod": "node-sass --output-style compressed ./src/sass/main.scss > ./dist/style.min.css",
"storybook": "start-storybook -p 9001 -c .storybook",
"storybook:build": "build-storybook -c .storybook -o .out",
"storybook:github": "storybook-to-ghpages",
"pretty": "prettier --use-tabs --write \"{src,demos,tests}/**/*.{ts,tsx}\" --print-width 120",
"lint": "tslint -p .",
"test:ci": "rm -rf ./dist && node ./tests/e2e/generate-e2e.js && jest --no-cache",
"test": "jest --no-cache",
"prepublishOnly": "yarn run build:ts:prod && yarn run build:sass:prod"
},
"dependencies": {
"closest": "^0.0.1",
"lodash": "^4.17.5",
"pathfinding": "^0.4.18",
"paths-js": "^0.4.7",
"react": "^16.2.0"
},
"devDependencies": {
"@storybook/addon-actions": "^3.3.15",
"@storybook/addon-options": "^3.3.15",
"@storybook/addon-storyshots": "^3.3.15",
"@storybook/addons": "^3.3.15",
"@storybook/react": "^3.3.15",
"@storybook/storybook-deployer": "^2.3.0",
"@types/jest": "^22.2.0",
"@types/lodash": "^4.14.104",
"@types/node": "^9.4.7",
"@types/promise": "^7.1.30",
"@types/puppeteer": "^1.1.0",
"@types/react": "^16.0.40",
"awesome-typescript-loader": "^4.0.1",
"copy-webpack-plugin": "^4.5.1",
"cross-env": "^5.1.4",
"css-loader": "^0.28.10",
"dagre": "^0.8.2",
"enzyme": "^3.3.0",
"file-loader": "^1.1.11",
"glob": "^7.1.2",
"jest": "^22.4.2",
"jest-cli": "^22.4.2",
"json-beautify": "^1.0.1",
"node-sass": "^4.7.2",
"prettier": "^1.11.1",
"puppeteer": "^1.1.1",
"raf": "^3.4.0",
"raw-loader": "^0.5.1",
"react-dom": "^16.2.0",
"react-syntax-highlighter": "^7.0.2",
"react-test-renderer": "^16.2.0",
"sass-loader": "^6.0.7",
"source-map-loader": "^0.2.3",
"storybook-host": "^4.1.5",
"storybook-readme": "^3.2.1",
"style-loader": "^0.20.3",
"ts-jest": "^22.4.1",
"tslint": "^5.9.1",
"ts-loader": "^4.1.0",
"typescript": "^2.7.2",
"uglifyjs-webpack-plugin": "^1.2.3",
"val-loader": "^1.1.0",
"webpack": "^4.1.1",
"webpack-cli": "^2.0.11"
}
}

View File

@@ -1,17 +0,0 @@
import { BaseEntity, BaseListener } from "./BaseEntity";
/**
* @author Dylan Vorster
*/
export abstract class AbstractInstanceFactory<T extends BaseEntity<BaseListener>> {
className: string;
constructor(className: string) {
this.className = className;
}
getName() {
return this.className;
}
abstract getInstance(initialConfig?: any): T;
}

View File

@@ -1,70 +0,0 @@
import { Toolkit } from "./Toolkit";
/**
* @author Dylan Vorster
*/
export class BaseListener {
lockChanged?(entity: BaseEntity<BaseListener>, locked: boolean): void;
}
export class BaseEntity<T extends BaseListener> {
public listeners: { [s: string]: T };
public id: string;
public locked: boolean;
constructor(id?: string) {
this.listeners = {};
this.id = id || Toolkit.UID();
this.locked = false;
}
getID() {
return this.id;
}
clearListeners() {
this.listeners = {};
}
public deSerialize(data) {
this.id = data.id;
}
public serialize() {
return {
id: this.id
};
}
public iterateListeners(cb: (t: T) => any) {
for (var i in this.listeners) {
cb(this.listeners[i]);
}
}
public removeListener(listener: string) {
if (this.listeners[listener]) {
delete this.listeners[listener];
return true;
}
return false;
}
public addListener(listener: T): string {
var uid = Toolkit.UID();
this.listeners[uid] = listener;
return uid;
}
public isLocked(): boolean {
return this.locked;
}
public setLocked(locked: boolean = true) {
this.locked = locked;
this.iterateListeners(listener => {
if (listener.lockChanged) {
listener.lockChanged(this, locked);
}
});
}
}

View File

@@ -1,86 +0,0 @@
import { DiagramModel } from "./DiagramModel";
import { DiagramEngine } from "./DiagramEngine";
import { NodeModel, PointModel } from "./Common";
import { SelectionModel } from "./widgets/DiagramWidget";
export class BaseAction {
mouseX: number;
mouseY: number;
ms: number;
constructor(mouseX: number, mouseY: number) {
this.mouseX = mouseX;
this.mouseY = mouseY;
this.ms = new Date().getTime();
}
}
export class SelectingAction extends BaseAction {
mouseX2: number;
mouseY2: number;
constructor(mouseX: number, mouseY: number) {
super(mouseX, mouseY);
this.mouseX2 = mouseX;
this.mouseY2 = mouseY;
}
getBoxDimensions() {
return {
left: this.mouseX2 > this.mouseX ? this.mouseX : this.mouseX2,
top: this.mouseY2 > this.mouseY ? this.mouseY : this.mouseY2,
width: Math.abs(this.mouseX2 - this.mouseX),
height: Math.abs(this.mouseY2 - this.mouseY),
right: this.mouseX2 < this.mouseX ? this.mouseX : this.mouseX2,
bottom: this.mouseY2 < this.mouseY ? this.mouseY : this.mouseY2
};
}
containsElement(x: number, y: number, diagramModel: DiagramModel): boolean {
var z = diagramModel.getZoomLevel() / 100.0;
let dimensions = this.getBoxDimensions();
return (
x * z + diagramModel.getOffsetX() > dimensions.left &&
x * z + diagramModel.getOffsetX() < dimensions.right &&
y * z + diagramModel.getOffsetY() > dimensions.top &&
y * z + diagramModel.getOffsetY() < dimensions.bottom
);
}
}
export class MoveCanvasAction extends BaseAction {
initialOffsetX: number;
initialOffsetY: number;
constructor(mouseX: number, mouseY: number, diagramModel: DiagramModel) {
super(mouseX, mouseY);
this.initialOffsetX = diagramModel.getOffsetX();
this.initialOffsetY = diagramModel.getOffsetY();
}
}
export class MoveItemsAction extends BaseAction {
selectionModels: SelectionModel[];
moved: boolean;
constructor(mouseX: number, mouseY: number, diagramEngine: DiagramEngine) {
super(mouseX, mouseY);
this.moved = false;
diagramEngine.enableRepaintEntities(diagramEngine.getDiagramModel().getSelectedItems());
var selectedItems = diagramEngine.getDiagramModel().getSelectedItems();
//dont allow items which are locked to move
selectedItems = selectedItems.filter(item => {
return !diagramEngine.isModelLocked(item);
});
this.selectionModels = selectedItems.map((item: PointModel | NodeModel) => {
return {
model: item,
initialX: item.x,
initialY: item.y
};
});
}
}

View File

@@ -1,424 +0,0 @@
import { BaseEntity, BaseListener } from "./BaseEntity";
import * as _ from "lodash";
import { port } from "_debugger";
export interface BaseModelListener extends BaseListener {
selectionChanged?(item: BaseModel<BaseModelListener>, isSelected: boolean): void;
entityRemoved?(item: any): void;
}
/**
* @author Dylan Vorster
*/
export class BaseModel<T extends BaseModelListener> extends BaseEntity<BaseModelListener> {
selected: boolean;
constructor(id?: string) {
super(id);
this.selected = false;
}
getSelectedEntities(): BaseModel<T>[] {
if (this.isSelected()) {
return [this];
}
return [];
}
deSerialize(ob) {
super.deSerialize(ob);
this.selected = ob.selected;
}
serialize() {
return _.merge(super.serialize(), {
_class: this.constructor.name,
selected: this.selected
});
}
public getID(): string {
return this.id;
}
public isSelected(): boolean {
return this.selected;
}
public setSelected(selected: boolean = true) {
this.selected = selected;
this.iterateListeners(listener => {
if (listener.selectionChanged) {
listener.selectionChanged(this, selected);
}
});
}
remove() {
this.iterateListeners(listener => {
if (listener.entityRemoved) {
listener.entityRemoved(this);
}
});
}
}
export class PointModel extends BaseModel<BaseModelListener> {
x: number;
y: number;
link: LinkModel;
constructor(link: LinkModel, points: { x: number; y: number }) {
super();
this.x = points.x;
this.y = points.y;
this.link = link;
}
getSelectedEntities() {
if (super.isSelected() && !this.isConnectedToPort()) {
return [this];
}
return [];
}
isConnectedToPort(): boolean {
return this.link.getPortForPoint(this) !== null;
}
deSerialize(ob) {
super.deSerialize(ob);
this.x = ob.x;
this.y = ob.y;
}
serialize() {
return _.merge(super.serialize(), {
x: this.x,
y: this.y
});
}
remove() {
//clear references
if (this.link) {
this.link.removePoint(this);
}
super.remove();
}
updateLocation(points: { x: number; y: number }) {
this.x = points.x;
this.y = points.y;
}
getX(): number {
return this.x;
}
getY(): number {
return this.y;
}
getLink(): LinkModel {
return this.link;
}
}
export interface LinkModelListener extends BaseModelListener {
sourcePortChanged?(item: LinkModel, target: null | PortModel): void;
targetPortChanged?(item: LinkModel, target: null | PortModel): void;
}
export class LinkModel extends BaseModel<LinkModelListener> {
linkType: string;
sourcePort: PortModel | null;
targetPort: PortModel | null;
points: PointModel[];
extras: {};
constructor() {
super();
this.linkType = "default";
this.points = [new PointModel(this, { x: 0, y: 0 }), new PointModel(this, { x: 0, y: 0 })];
this.extras = {};
this.sourcePort = null;
this.targetPort = null;
}
deSerialize(ob) {
super.deSerialize(ob);
this.linkType = ob.type;
this.extras = ob.extras;
this.points = _.map(ob.points, (point: { x; y }) => {
var p = new PointModel(this, { x: point.x, y: point.y });
p.deSerialize(point);
return p;
});
}
serialize() {
return _.merge(super.serialize(), {
type: this.linkType,
source: this.sourcePort ? this.sourcePort.getParent().id : null,
sourcePort: this.sourcePort ? this.sourcePort.id : null,
target: this.targetPort ? this.targetPort.getParent().id : null,
targetPort: this.targetPort ? this.targetPort.id : null,
points: _.map(this.points, point => {
return point.serialize();
}),
extras: this.extras
});
}
remove() {
if (this.sourcePort) {
this.sourcePort.removeLink(this);
}
if (this.targetPort) {
this.targetPort.removeLink(this);
}
super.remove();
}
isLastPoint(point: PointModel) {
var index = this.getPointIndex(point);
return index === this.points.length - 1;
}
getPointIndex(point: PointModel) {
return this.points.indexOf(point);
}
getPointModel(id: string): PointModel | null {
for (var i = 0; i < this.points.length; i++) {
if (this.points[i].id === id) {
return this.points[i];
}
}
return null;
}
getPortForPoint(point: PointModel): PortModel {
if (this.sourcePort !== null && this.getFirstPoint().getID() === point.getID()) {
return this.sourcePort;
}
if (this.targetPort !== null && this.getLastPoint().getID() === point.getID()) {
return this.targetPort;
}
return null;
}
getPointForPort(port: PortModel): PointModel {
if (this.sourcePort !== null && this.sourcePort.getID() === port.getID()) {
return this.getFirstPoint();
}
if (this.targetPort !== null && this.targetPort.getID() === port.getID()) {
return this.getLastPoint();
}
return null;
}
getFirstPoint(): PointModel {
return this.points[0];
}
getLastPoint(): PointModel {
return this.points[this.points.length - 1];
}
setSourcePort(port: PortModel) {
port.addLink(this);
this.sourcePort = port;
this.iterateListeners((listener: LinkModelListener) => {
listener.sourcePortChanged && listener.sourcePortChanged(this, port);
});
}
getSourcePort(): PortModel {
return this.sourcePort;
}
getTargetPort(): PortModel {
return this.targetPort;
}
setTargetPort(port: PortModel) {
port.addLink(this);
this.targetPort = port;
this.iterateListeners((listener: LinkModelListener) => {
listener.targetPortChanged && listener.targetPortChanged(this, port);
});
}
getPoints(): PointModel[] {
return this.points;
}
setPoints(points: PointModel[]) {
this.points = points;
}
removePoint(pointModel: PointModel) {
this.points.splice(this.getPointIndex(pointModel), 1);
}
addPoint(pointModel: PointModel, index = 1) {
this.points.splice(index, 0, pointModel);
}
getType(): string {
return this.linkType;
}
}
export class PortModel extends BaseModel<BaseModelListener> {
name: string;
parentNode: NodeModel;
links: { [id: string]: LinkModel };
deSerialize(ob) {
super.deSerialize(ob);
this.name = ob.name;
}
serialize() {
return _.merge(super.serialize(), {
name: this.name,
parentNode: this.parentNode.id,
links: _.map(this.links, link => {
return link.id;
})
});
}
constructor(name: string, id?: string) {
super(id);
this.name = name;
this.links = {};
this.parentNode = null;
}
getName(): string {
return this.name;
}
getParent(): NodeModel {
return this.parentNode;
}
setParentNode(node: NodeModel) {
this.parentNode = node;
}
removeLink(link: LinkModel) {
delete this.links[link.getID()];
}
addLink(link: LinkModel) {
this.links[link.getID()] = link;
}
getLinks(): { [id: string]: LinkModel } {
return this.links;
}
}
export class NodeModel extends BaseModel<BaseModelListener> {
nodeType: string;
x: number;
y: number;
extras: {};
ports: { [s: string]: PortModel };
constructor(nodeType: string = "default", id?: string) {
super(id);
this.nodeType = nodeType;
this.x = 0;
this.y = 0;
this.extras = {};
this.ports = {};
}
getSelectedEntities() {
let entities = super.getSelectedEntities();
// add the points of each link that are selected here
if (this.isSelected()) {
for (let portName in this.ports) {
entities = entities.concat(
_.map(this.ports[portName].getLinks(), link => {
return link.getPointForPort(this.ports[portName]);
})
);
}
}
return entities;
}
deSerialize(ob) {
super.deSerialize(ob);
this.nodeType = ob.type;
this.x = ob.x;
this.y = ob.y;
this.extras = ob.extras;
}
serialize() {
return _.merge(super.serialize(), {
type: this.nodeType,
x: this.x,
y: this.y,
extras: this.extras,
ports: _.map(this.ports, port => {
return port.serialize();
})
});
}
remove() {
super.remove();
for (var i in this.ports) {
_.forEach(this.ports[i].getLinks(), link => {
link.remove();
});
}
}
getPortFromID(id): PortModel | null {
for (var i in this.ports) {
if (this.ports[i].id === id) {
return this.ports[i];
}
}
return null;
}
getPort(name: string): PortModel | null {
return this.ports[name];
}
getPorts(): { [s: string]: PortModel } {
return this.ports;
}
removePort(port: PortModel) {
//clear the parent node reference
if (this.ports[port.name]) {
this.ports[port.name].setParentNode(null);
delete this.ports[port.name];
}
}
addPort(port: PortModel): PortModel {
port.setParentNode(this);
this.ports[port.name] = port;
return port;
}
getType(): string {
return this.nodeType;
}
}

View File

@@ -1,226 +1,41 @@
import { NodeWidgetFactory, LinkWidgetFactory } from "./WidgetFactories";
import { LinkModel, NodeModel, BaseModel, BaseModelListener, PortModel, PointModel } from "./Common";
import { BaseEntity, BaseListener } from "./BaseEntity";
import { DiagramModel } from "./DiagramModel";
import { AbstractInstanceFactory } from "./AbstractInstanceFactory";
import * as _ from "lodash";
/**
* @author Dylan Vorster
*/
export interface DiagramEngineListener extends BaseListener {
nodeFactoriesUpdated?(): void;
import { DiagramModel } from "./models/DiagramModel";
import { CanvasEngine } from "@projectstorm/react-canvas";
import { DefaultLabelFactory, DefaultLinkFactory, DefaultNodeFactory, DefaultPortFactory } from "storm-react-diagrams";
linkFactoriesUpdated?(): void;
repaintCanvas?(): void;
}
/**
* Passed as a parameter to the DiagramWidget
*/
export class DiagramEngine extends BaseEntity<DiagramEngineListener> {
nodeFactories: { [s: string]: NodeWidgetFactory };
linkFactories: { [s: string]: LinkWidgetFactory };
instanceFactories: {
[s: string]: AbstractInstanceFactory<BaseEntity<BaseListener>>;
};
diagramModel: DiagramModel;
canvas: Element;
export class DiagramEngine extends CanvasEngine<DiagramModel> {
paintableWidgets: {};
linksThatHaveInitiallyRendered: {};
maxNumberPointsPerLink: number;
smartRouting: boolean;
constructor() {
super();
this.diagramModel = new DiagramModel();
this.nodeFactories = {};
this.linkFactories = {};
this.instanceFactories = {};
this.canvas = null;
this.paintableWidgets = null;
this.linksThatHaveInitiallyRendered = {};
this.smartRouting = false;
}
repaintCanvas() {
this.iterateListeners(listener => {
listener.repaintCanvas && listener.repaintCanvas();
});
installDefaults() {
super.installDefaults();
this.registerElementFactory(new DefaultLabelFactory());
this.registerElementFactory(new DefaultLinkFactory());
this.registerElementFactory(new DefaultNodeFactory());
this.registerElementFactory(new DefaultPortFactory());
}
clearRepaintEntities() {
this.paintableWidgets = null;
getMaxNumberPointsPerLink(): number {
return this.maxNumberPointsPerLink;
}
enableRepaintEntities(entities: BaseModel<BaseModelListener>[]) {
this.paintableWidgets = {};
entities.forEach(entity => {
//if a node is requested to repaint, add all of its links
if (entity instanceof NodeModel) {
_.forEach(entity.getPorts(), port => {
_.forEach(port.getLinks(), link => {
this.paintableWidgets[link.getID()] = true;
});
});
}
if (entity instanceof PointModel) {
this.paintableWidgets[entity.getLink().getID()] = true;
}
this.paintableWidgets[entity.getID()] = true;
});
setMaxNumberPointsPerLink(max: number) {
this.maxNumberPointsPerLink = max;
}
/**
* Checks to see if a model is locked by running through
* its parents to see if they are locked first
*/
isModelLocked(model: BaseEntity<BaseListener>) {
//always check the diagram model
if (this.diagramModel.isLocked()) {
return true;
}
//a point is locked, if its model is locked
if (model instanceof PortModel) {
if (model.getParent().isLocked()) {
return true;
}
}
//a point is locked, if its model is locked
if (model instanceof PointModel) {
if (model.getLink().isLocked()) {
return true;
}
}
return model.isLocked();
isSmartRoutingEnabled() {
return this.smartRouting;
}
canEntityRepaint(baseModel: BaseModel<BaseModelListener>) {
//no rules applied, allow repaint
if (this.paintableWidgets === null) {
return true;
}
return this.paintableWidgets[baseModel.getID()] !== undefined;
}
setCanvas(canvas: Element | null) {
this.canvas = canvas;
}
setDiagramModel(model: DiagramModel) {
this.diagramModel = model;
}
getDiagramModel(): DiagramModel {
return this.diagramModel;
}
getNodeFactories(): { [s: string]: NodeWidgetFactory } {
return this.nodeFactories;
}
getLinkFactories(): { [s: string]: LinkWidgetFactory } {
return this.linkFactories;
}
getInstanceFactory(className: string): AbstractInstanceFactory<BaseEntity<BaseListener>> {
return this.instanceFactories[className];
}
registerInstanceFactory(factory: AbstractInstanceFactory<BaseEntity<BaseListener>>) {
this.instanceFactories[factory.getName()] = factory;
}
registerNodeFactory(factory: NodeWidgetFactory) {
this.nodeFactories[factory.getType()] = factory;
this.iterateListeners(listener => {
if (listener.nodeFactoriesUpdated) listener.nodeFactoriesUpdated();
});
}
registerLinkFactory(factory: LinkWidgetFactory) {
this.linkFactories[factory.getType()] = factory;
this.iterateListeners(listener => {
if (listener.linkFactoriesUpdated) listener.linkFactoriesUpdated();
});
}
getFactoryForNode(node: NodeModel): NodeWidgetFactory | null {
if (this.nodeFactories[node.getType()]) {
return this.nodeFactories[node.getType()];
}
console.log("cannot find widget factory for node of type: [" + node.getType() + "]");
return null;
}
getFactoryForLink(link: LinkModel): LinkWidgetFactory | null {
if (this.linkFactories[link.getType()]) {
return this.linkFactories[link.getType()];
}
console.log("cannot find widget factory for link of type: [" + link.getType() + "]");
return null;
}
generateWidgetForLink(link: LinkModel): JSX.Element | null {
var linkFactory = this.getFactoryForLink(link);
if (!linkFactory) {
throw "Cannot find link factory for link: " + link.getType();
}
return linkFactory.generateReactWidget(this, link);
}
generateWidgetForNode(node: NodeModel): JSX.Element | null {
var nodeFactory = this.getFactoryForNode(node);
if (!nodeFactory) {
throw "Cannot find widget factory for node: " + node.getType();
}
return nodeFactory.generateReactWidget(this, node);
}
getRelativeMousePoint(event): { x: number; y: number } {
var point = this.getRelativePoint(event.clientX, event.clientY);
return {
x: (point.x - this.diagramModel.getOffsetX()) / (this.diagramModel.getZoomLevel() / 100.0),
y: (point.y - this.diagramModel.getOffsetY()) / (this.diagramModel.getZoomLevel() / 100.0)
};
}
getRelativePoint(x, y) {
var canvasRect = this.canvas.getBoundingClientRect();
return { x: x - canvasRect.left, y: y - canvasRect.top };
}
getNodePortElement(port: PortModel): any {
var selector = this.canvas.querySelector(
'.port[data-name="' + port.getName() + '"][data-nodeid="' + port.getParent().getID() + '"]'
);
if (selector === null) {
throw "Cannot find Node Port element with nodeID: [" +
port.getParent().getID() +
"] and name: [" +
port.getName() +
"]";
}
return selector;
}
getPortCenter(port: PortModel) {
var sourceElement = this.getNodePortElement(port);
var sourceRect = sourceElement.getBoundingClientRect();
var rel = this.getRelativePoint(sourceRect.left, sourceRect.top);
return {
x:
sourceElement.offsetWidth / 2 +
(rel.x - this.diagramModel.getOffsetX()) / (this.diagramModel.getZoomLevel() / 100.0),
y:
sourceElement.offsetHeight / 2 +
(rel.y - this.diagramModel.getOffsetY()) / (this.diagramModel.getZoomLevel() / 100.0)
};
setSmartRoutingStatus(status: boolean) {
this.smartRouting = status;
}
}

View File

@@ -1,289 +0,0 @@
import { LinkModel, NodeModel, BaseModel, BaseModelListener, PortModel } from "./Common";
import { BaseListener, BaseEntity } from "./BaseEntity";
import * as _ from "lodash";
import { DiagramEngine } from "./DiagramEngine";
/**
* @author Dylan Vorster
*
*/
export interface DiagramListener extends BaseListener {
nodesUpdated?(node: any, isCreated: boolean): void;
linksUpdated?(link: any, isCreated: boolean): void;
/**
* @deprecated
*/
controlsUpdated?(): void;
offsetUpdated?(model: DiagramModel, offsetX: number, offsetY: number): void;
zoomUpdated?(model: DiagramModel, zoom: number): void;
gridUpdated?(model: DiagramModel, size: number): void;
}
/**
*
*/
export class DiagramModel extends BaseEntity<DiagramListener> {
//models
links: { [s: string]: LinkModel };
nodes: { [s: string]: NodeModel };
//control variables
offsetX: number;
offsetY: number;
zoom: number;
rendered: boolean;
gridSize: number;
constructor() {
super();
this.links = {};
this.nodes = {};
this.offsetX = 0;
this.offsetY = 0;
this.zoom = 100;
this.rendered = false;
this.gridSize = 0;
}
setGridSize(size: number = 0) {
this.gridSize = size;
this.iterateListeners(listener => {
listener.gridUpdated && listener.gridUpdated(this, size);
});
}
getGridPosition(pos) {
if (this.gridSize === 0) {
return pos;
}
return this.gridSize * Math.floor((pos + this.gridSize / 2) / this.gridSize);
}
deSerializeDiagram(object: any, diagramEngine: DiagramEngine) {
this.deSerialize(object);
this.offsetX = object.offsetX;
this.offsetY = object.offsetY;
this.zoom = object.zoom;
//deserialize nodes
_.forEach(object.nodes, (node: any) => {
let nodeOb = diagramEngine.getInstanceFactory(node._class).getInstance(node) as NodeModel;
nodeOb.deSerialize(node);
//deserialize ports
_.forEach(node.ports, (port: any) => {
let portOb = diagramEngine.getInstanceFactory(port._class).getInstance() as PortModel;
portOb.deSerialize(port);
nodeOb.addPort(portOb);
});
this.addNode(nodeOb);
});
_.forEach(object.links, (link: any) => {
let linkOb = diagramEngine.getInstanceFactory(link._class).getInstance() as LinkModel;
linkOb.deSerialize(link);
if (link.target) {
linkOb.setTargetPort(this.getNode(link.target).getPortFromID(link.targetPort));
}
if (link.source) {
linkOb.setSourcePort(this.getNode(link.source).getPortFromID(link.sourcePort));
}
this.addLink(linkOb);
});
}
serializeDiagram() {
return _.merge(this.serialize(), {
offsetX: this.offsetX,
offsetY: this.offsetY,
zoom: this.zoom,
links: _.map(this.links, link => {
return link.serialize();
}),
nodes: _.map(this.nodes, link => {
return link.serialize();
})
});
}
clearSelection(ignore: BaseModel<BaseModelListener> | null = null) {
_.forEach(this.getSelectedItems(), element => {
if (ignore && ignore.getID() === element.getID()) {
return;
}
element.setSelected(false); //TODO dont fire the listener
});
}
getSelectedItems(): BaseModel<BaseModelListener>[] {
var items = [];
// run through nodes
items = items.concat(
_.flatMap(this.nodes, node => {
return node.getSelectedEntities();
})
);
// find all the links
items = items.concat(
_.flatMap(this.links, link => {
return link.getSelectedEntities();
})
);
//find all points
items = items.concat(
_.flatMap(this.links, link => {
return _.flatMap(link.points, point => {
return point.getSelectedEntities();
});
})
);
return _.uniq(items);
}
setZoomLevel(zoom: number) {
this.zoom = zoom;
this.iterateListeners(listener => {
if (listener.controlsUpdated) listener.controlsUpdated();
});
this.iterateListeners(listener => {
listener.zoomUpdated && listener.zoomUpdated(this, this.zoom);
});
}
setOffset(offsetX: number, offsetY: number) {
this.offsetX = offsetX;
this.offsetY = offsetY;
this.iterateListeners(listener => {
if (listener.controlsUpdated) listener.controlsUpdated();
});
this.iterateListeners(listener => {
listener.offsetUpdated && listener.offsetUpdated(this, this.offsetX, this.offsetY);
});
}
setOffsetX(offsetX: number) {
this.offsetX = offsetX;
this.iterateListeners(listener => {
if (listener.controlsUpdated) listener.controlsUpdated();
});
this.iterateListeners(listener => {
listener.offsetUpdated && listener.offsetUpdated(this, this.offsetX, this.offsetY);
});
}
setOffsetY(offsetY: number) {
this.offsetY = offsetY;
this.iterateListeners(listener => {
if (listener.controlsUpdated) listener.controlsUpdated();
});
this.iterateListeners(listener => {
listener.offsetUpdated && listener.offsetUpdated(this, this.offsetX, this.offsetY);
});
}
getOffsetY() {
return this.offsetY;
}
getOffsetX() {
return this.offsetX;
}
getZoomLevel() {
return this.zoom;
}
getNode(node: string | NodeModel): NodeModel | null {
if (node instanceof NodeModel) {
return node;
}
if (!this.nodes[node]) {
return null;
}
return this.nodes[node];
}
getLink(link: string | LinkModel): LinkModel | null {
if (link instanceof LinkModel) {
return link;
}
if (!this.links[link]) {
return null;
}
return this.links[link];
}
addLink(link: LinkModel): LinkModel {
link.addListener({
entityRemoved: () => {
this.removeLink(link);
}
});
this.links[link.getID()] = link;
this.iterateListeners(listener => {
if (listener.linksUpdated) listener.linksUpdated(link, true);
});
return link;
}
addNode(node: NodeModel): NodeModel {
node.addListener({
entityRemoved: () => {
this.removeNode(node);
}
});
this.nodes[node.getID()] = node;
this.iterateListeners(listener => {
if (listener.nodesUpdated) listener.nodesUpdated(node, true);
});
return node;
}
removeLink(link: LinkModel | string) {
if (link instanceof LinkModel) {
delete this.links[link.getID()];
this.iterateListeners(listener => {
if (listener.linksUpdated) listener.linksUpdated(link, false);
});
return;
}
delete this.links["" + link];
this.iterateListeners(listener => {
if (listener.linksUpdated) listener.linksUpdated(link, false);
});
}
removeNode(node: NodeModel | string) {
if (node instanceof NodeModel) {
delete this.nodes[node.getID()];
this.iterateListeners(listener => {
if (listener.nodesUpdated) listener.nodesUpdated(node, false);
});
return;
}
delete this.nodes["" + node];
this.iterateListeners(listener => {
if (listener.nodesUpdated) listener.nodesUpdated(node, false);
});
}
getLinks(): { [s: string]: LinkModel } {
return this.links;
}
getNodes(): { [s: string]: NodeModel } {
return this.nodes;
}
}

3
src/Function.d.ts vendored
View File

@@ -1,3 +0,0 @@
interface Function {
name: string;
}

View File

@@ -1,15 +0,0 @@
import { LinkModel } from "./Common";
import * as _ from "lodash";
import { AbstractInstanceFactory } from "./AbstractInstanceFactory";
/**
* @author Dylan Vorster
*/
export class LinkInstanceFactory extends AbstractInstanceFactory<LinkModel> {
constructor() {
super("LinkModel");
}
getInstance() {
return new LinkModel();
}
}

View File

@@ -1,20 +1,13 @@
// tslint:disable no-bitwise
import closest = require("closest");
import { PointModel } from "./models/PointModel";
import { ROUTING_SCALING_FACTOR } from "./routing/PathFinding";
import * as Path from "paths-js/path";
import { Toolkit as TK } from "@projectstorm/react-canvas";
/**
* @author Dylan Vorster
*/
export class Toolkit {
/**
* Generats a unique ID (thanks Stack overflow :3)
* @returns {String}
*/
public static UID(): string {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
export class Toolkit extends TK {
/**
* Finds the closest element as a polyfill
*
@@ -27,4 +20,26 @@ export class Toolkit {
}
return closest(element, selector);
}
public static generateLinePath(firstPoint: PointModel, lastPoint: PointModel): string {
return `M${firstPoint.x},${firstPoint.y} L ${lastPoint.x},${lastPoint.y}`;
}
public static generateCurvePath(firstPoint: PointModel, lastPoint: PointModel, curvy: number = 0): string {
var isHorizontal = Math.abs(firstPoint.x - lastPoint.x) > Math.abs(firstPoint.y - lastPoint.y);
var curvyX = isHorizontal ? curvy : 0;
var curvyY = isHorizontal ? 0 : curvy;
return `M${firstPoint.x},${firstPoint.y} C ${firstPoint.x + curvyX},${firstPoint.y + curvyY}
${lastPoint.x - curvyX},${lastPoint.y - curvyY} ${lastPoint.x},${lastPoint.y}`;
}
public static generateDynamicPath(pathCoords: number[][]) {
let path = Path();
path = path.moveto(pathCoords[0][0] * ROUTING_SCALING_FACTOR, pathCoords[0][1] * ROUTING_SCALING_FACTOR);
pathCoords.slice(1).forEach(coords => {
path = path.lineto(coords[0] * ROUTING_SCALING_FACTOR, coords[1] * ROUTING_SCALING_FACTOR);
});
return path.print();
}
}

View File

@@ -1,24 +0,0 @@
import { NodeModel, LinkModel } from "./Common";
import { DiagramEngine } from "./DiagramEngine";
/**
* @author Dylan Vorster
*/
export abstract class WidgetFactory {
type: string;
constructor(name: string) {
this.type = name;
}
getType(): string {
return this.type;
}
}
export abstract class NodeWidgetFactory extends WidgetFactory {
abstract generateReactWidget(diagramEngine: DiagramEngine, node: NodeModel): JSX.Element;
}
export abstract class LinkWidgetFactory extends WidgetFactory {
abstract generateReactWidget(diagramEngine: DiagramEngine, link: LinkModel): JSX.Element;
}

View File

@@ -1,20 +0,0 @@
import { LinkWidgetFactory } from "../WidgetFactories";
import { LinkModel } from "../Common";
import * as React from "react";
import { DefaultLinkWidget } from "./DefaultLinkWidget";
import { DiagramEngine } from "../DiagramEngine";
/**
* @author Dylan Vorster
*/
export class DefaultLinkFactory extends LinkWidgetFactory {
constructor() {
super("default");
}
generateReactWidget(diagramEngine: DiagramEngine, link: LinkModel): JSX.Element {
return React.createElement(DefaultLinkWidget, {
link: link,
diagramEngine: diagramEngine
});
}
}

View File

@@ -1,259 +0,0 @@
import * as React from "react";
import { LinkModel, PointModel } from "../Common";
import * as _ from "lodash";
import { DiagramEngine } from "../DiagramEngine";
export interface DefaultLinkProps {
color?: string;
width?: number;
link: LinkModel;
smooth?: boolean;
diagramEngine: DiagramEngine;
pointAdded?: (point: PointModel, event) => any;
}
export interface DefaultLinkState {
selected: boolean;
}
/**
* @author Dylan Vorster
*/
export class DefaultLinkWidget extends React.Component<DefaultLinkProps, DefaultLinkState> {
public static defaultProps: DefaultLinkProps = {
color: "black",
width: 3,
link: null,
engine: null,
smooth: false,
diagramEngine: null
};
constructor(props: DefaultLinkProps) {
super(props);
this.state = {
selected: false
};
}
generatePoint(pointIndex: number): JSX.Element {
let x = this.props.link.points[pointIndex].x;
let y = this.props.link.points[pointIndex].y;
return (
<g key={"point-" + this.props.link.points[pointIndex].id}>
<circle
cx={x}
cy={y}
r={5}
className={"point pointui" + (this.props.link.points[pointIndex].isSelected() ? " selected" : "")}
/>
<circle
onMouseLeave={() => {
this.setState({ selected: false });
}}
onMouseEnter={() => {
this.setState({ selected: true });
}}
data-id={this.props.link.points[pointIndex].id}
data-linkid={this.props.link.id}
cx={x}
cy={y}
r={15}
opacity={0}
className={"point"}
/>
</g>
);
}
generateLink(extraProps: any, id: string | number): JSX.Element {
var Bottom = (
<path
className={this.state.selected || this.props.link.isSelected() ? "selected" : ""}
strokeWidth={this.props.width}
stroke={this.props.color}
{...extraProps}
/>
);
var Top = (
<path
strokeLinecap="round"
onMouseLeave={() => {
this.setState({ selected: false });
}}
onMouseEnter={() => {
this.setState({ selected: true });
}}
data-linkid={this.props.link.getID()}
stroke={this.props.color}
strokeOpacity={this.state.selected ? 0.1 : 0}
strokeWidth={20}
onContextMenu={() => {
if (!this.props.diagramEngine.isModelLocked(this.props.link)) {
event.preventDefault();
this.props.link.remove();
}
}}
{...extraProps}
/>
);
return (
<g key={"link-" + id}>
{Bottom}
{Top}
</g>
);
}
render() {
//ensure id is present for all points on the path
var points = this.props.link.points;
var paths = [];
let model = this.props.diagramEngine.getDiagramModel();
//draw the smoothing
if (points.length === 2) {
//if the points are too close, just draw a straight line
var margin = 50;
if (Math.abs(points[0].x - points[1].x) < 50) {
margin = 5;
}
var pointLeft = points[0];
var pointRight = points[1];
//some defensive programming to make sure the smoothing is
//always in the right direction
if (pointLeft.x > pointRight.x) {
pointLeft = points[1];
pointRight = points[0];
}
paths.push(
this.generateLink(
{
onMouseDown: event => {
if (!event.shiftKey && !this.props.diagramEngine.isModelLocked(this.props.link)) {
var point = new PointModel(this.props.link, this.props.diagramEngine.getRelativeMousePoint(event));
point.setSelected(true);
this.forceUpdate();
this.props.link.addPoint(point, 1);
this.props.pointAdded(point, event);
}
},
d:
" M" +
pointLeft.x +
" " +
pointLeft.y +
" C" +
(pointLeft.x + margin) +
" " +
pointLeft.y +
" " +
(pointRight.x - margin) +
" " +
pointRight.y +
" " +
pointRight.x +
" " +
pointRight.y
},
"0"
)
);
if (this.props.link.targetPort === null) {
paths.push(this.generatePoint(1));
}
} else {
//draw the multiple anchors and complex line instead
var ds = [];
if (this.props.smooth) {
ds.push(
" M" +
points[0].x +
" " +
points[0].y +
" C " +
(points[0].x + 50) +
" " +
points[0].y +
" " +
points[1].x +
" " +
points[1].y +
" " +
points[1].x +
" " +
points[1].y
);
for (var i = 1; i < points.length - 2; i++) {
ds.push(" M " + points[i].x + " " + points[i].y + " L " + points[i + 1].x + " " + points[i + 1].y);
}
ds.push(
" M" +
points[i].x +
" " +
points[i].y +
" C " +
points[i].x +
" " +
points[i].y +
" " +
(points[i + 1].x - 50) +
" " +
points[i + 1].y +
" " +
points[i + 1].x +
" " +
points[i + 1].y
);
} else {
var ds = [];
for (var i = 0; i < points.length - 1; i++) {
if (i === 0) {
ds.push(" M " + points[i].x + " " + points[i].y + " L " + points[i + 1].x + " " + points[i + 1].y);
} else if (i === points.length - 1) {
ds.push(" M " + points[i].x + " " + points[i].y + " L " + points[i + 1].x + " " + points[i + 1].y);
} else {
ds.push(" M " + points[i].x + " " + points[i].y + " L " + points[i + 1].x + " " + points[i + 1].y);
}
}
}
paths = ds.map((data, index) => {
return this.generateLink(
{
"data-linkid": this.props.link.id,
"data-point": index,
onMouseDown: (event: MouseEvent) => {
if (!event.shiftKey && !this.props.diagramEngine.isModelLocked(this.props.link)) {
var point = new PointModel(this.props.link, this.props.diagramEngine.getRelativeMousePoint(event));
point.setSelected(true);
this.forceUpdate();
this.props.link.addPoint(point, index + 1);
this.props.pointAdded(point, event);
}
},
d: data
},
index
);
});
//render the circles
for (var i = 1; i < points.length - 1; i++) {
paths.push(this.generatePoint(i));
}
if (this.props.link.targetPort === null) {
paths.push(this.generatePoint(points.length - 1));
}
}
return <g>{paths}</g>;
}
}

View File

@@ -1,20 +0,0 @@
import { NodeWidgetFactory } from "../WidgetFactories";
import { DefaultNodeModel } from "./DefaultNodeModel";
import * as React from "react";
import { DefaultNodeWidget } from "./DefaultNodeWidget";
import { DiagramEngine } from "../DiagramEngine";
/**
* @author Dylan Vorster
*/
export class DefaultNodeFactory extends NodeWidgetFactory {
constructor() {
super("default");
}
generateReactWidget(diagramEngine: DiagramEngine, node: DefaultNodeModel): JSX.Element {
return React.createElement(DefaultNodeWidget, {
node: node,
diagramEngine: diagramEngine
});
}
}

View File

@@ -1,55 +0,0 @@
import { NodeModel } from "../Common";
import { DefaultPortModel } from "./DefaultPortModel";
import * as _ from "lodash";
import { AbstractInstanceFactory } from "../AbstractInstanceFactory";
export class DefaultNodeInstanceFactory extends AbstractInstanceFactory<DefaultNodeModel> {
constructor() {
super("DefaultNodeModel");
}
getInstance() {
return new DefaultNodeModel();
}
}
/**
* @author Dylan Vorster
*/
export class DefaultNodeModel extends NodeModel {
name: string;
color: string;
ports: { [s: string]: DefaultPortModel };
constructor(name: string = "Untitled", color: string = "rgb(0,192,255)") {
super("default");
this.name = name;
this.color = color;
}
deSerialize(object) {
super.deSerialize(object);
this.name = object.name;
this.color = object.color;
}
serialize() {
return _.merge(super.serialize(), {
name: this.name,
color: this.color
});
}
getInPorts(): DefaultPortModel[] {
return _.filter(this.ports, portModel => {
return portModel.in;
});
}
getOutPorts(): DefaultPortModel[] {
return _.filter(this.ports, portModel => {
return !portModel.in;
});
}
}

View File

@@ -1,40 +0,0 @@
import * as React from "react";
import * as _ from "lodash";
import { DefaultNodeModel } from "./DefaultNodeModel";
import { DefaultPortLabel } from "./DefaultPortLabelWidget";
import { DiagramEngine } from "../DiagramEngine";
export interface DefaultNodeProps {
node: DefaultNodeModel;
diagramEngine: DiagramEngine;
}
export interface DefaultNodeState {}
/**
* @author Dylan Vorster
*/
export class DefaultNodeWidget extends React.Component<DefaultNodeProps, DefaultNodeState> {
constructor(props: DefaultNodeProps) {
super(props);
this.state = {};
}
generatePort(port) {
return <DefaultPortLabel model={port} key={port.id} />;
}
render() {
return (
<div className="basic-node" style={{ background: this.props.node.color }}>
<div className="title">
<div className="name">{this.props.node.name}</div>
</div>
<div className="ports">
<div className="in">{_.map(this.props.node.getInPorts(), this.generatePort.bind(this))}</div>
<div className="out">{_.map(this.props.node.getOutPorts(), this.generatePort.bind(this))}</div>
</div>
</div>
);
}
}

View File

@@ -1,33 +0,0 @@
import * as React from "react";
import { DefaultPortModel } from "./DefaultPortModel";
import { PortWidget } from "../widgets/PortWidget";
export interface DefaultPortLabelProps {
model?: DefaultPortModel;
in?: boolean;
label?: string;
}
export interface DefaultPortLabelState {}
/**
* @author Dylan Vorster
*/
export class DefaultPortLabel extends React.Component<DefaultPortLabelProps, DefaultPortLabelState> {
public static defaultProps: DefaultPortLabelProps = {
in: true,
label: "port"
};
render() {
var port = <PortWidget node={this.props.model.getParent()} name={this.props.model.name} />;
var label = <div className="name">{this.props.model.label}</div>;
return (
<div className={(this.props.model.in ? "in" : "out") + "-port"}>
{this.props.model.in ? port : label}
{this.props.model.in ? label : port}
</div>
);
}
}

View File

@@ -1,40 +0,0 @@
import { PortModel } from "../Common";
import * as _ from "lodash";
import { AbstractInstanceFactory } from "../AbstractInstanceFactory";
export class DefaultPortInstanceFactory extends AbstractInstanceFactory<DefaultPortModel> {
constructor() {
super("DefaultPortModel");
}
getInstance() {
return new DefaultPortModel(true, "unknown");
}
}
/**
* @author Dylan Vorster
*/
export class DefaultPortModel extends PortModel {
in: boolean;
label: string;
constructor(isInput: boolean, name: string, label: string = null, id?: string) {
super(name, id);
this.in = isInput;
this.label = label || name;
}
deSerialize(object) {
super.deSerialize(object);
this.in = object.in;
this.label = object.label;
}
serialize() {
return _.merge(super.serialize(), {
in: this.in,
label: this.label
});
}
}

View File

@@ -0,0 +1,19 @@
import * as React from "react";
import { DiagramEngine } from "../../DiagramEngine";
import { DefaultLabelModel } from "../models/DefaultLabelModel";
import { DefaultLabelWidget } from "../widgets/DefaultLabelWidget";
import { AbstractElementFactory } from "@projectstorm/react-canvas";
export class DefaultLabelFactory extends AbstractElementFactory<DefaultLabelModel> {
constructor() {
super("default");
}
generateWidget(engine: DiagramEngine, model: DefaultLabelModel): JSX.Element {
return <DefaultLabelWidget model={model} />;
}
generateModel(): DefaultLabelModel {
return new DefaultLabelModel();
}
}

View File

@@ -0,0 +1,33 @@
import * as React from "react";
import { DefaultLinkWidget } from "../widgets/DefaultLinkWidget";
import { DiagramEngine } from "../../DiagramEngine";
import { AbstractElementFactory } from "@projectstorm/react-canvas";
import { DefaultLinkModel } from "../models/DefaultLinkModel";
export class DefaultLinkFactory extends AbstractElementFactory<DefaultLinkModel> {
constructor() {
super("default");
}
generateWidget(engine: DiagramEngine, model: DefaultLinkModel): JSX.Element {
return React.createElement(DefaultLinkWidget, {
link: model,
diagramEngine: engine
});
}
generateModel(): DefaultLinkModel {
return new DefaultLinkModel();
}
generateLinkSegment(model: DefaultLinkModel, widget: DefaultLinkWidget, selected: boolean, path: string) {
return (
<path
className={selected ? widget.bem("--path-selected") : ""}
strokeWidth={model.getWidth()}
stroke={model.getColor()}
d={path}
/>
);
}
}

View File

@@ -0,0 +1,22 @@
import { DefaultNodeModel } from "../models/DefaultNodeModel";
import * as React from "react";
import { DefaultNodeWidget } from "../widgets/DefaultNodeWidget";
import { DiagramEngine } from "../../DiagramEngine";
import { AbstractElementFactory } from "@projectstorm/react-canvas";
export class DefaultNodeFactory extends AbstractElementFactory<DefaultNodeModel> {
constructor() {
super("default");
}
generateWidget(diagramEngine: DiagramEngine, model: DefaultNodeModel): JSX.Element {
return React.createElement(DefaultNodeWidget, {
node: model,
diagramEngine: diagramEngine
});
}
generateModel(): DefaultNodeModel {
return new DefaultNodeModel();
}
}

View File

@@ -0,0 +1,17 @@
import { DefaultPortModel } from "../models/DefaultPortModel";
import { AbstractElementFactory } from "@projectstorm/react-canvas";
import { DiagramEngine } from "storm-react-diagrams";
export class DefaultPortFactory extends AbstractElementFactory<DefaultPortModel> {
constructor() {
super("default");
}
generateWidget(engine: DiagramEngine, model: DefaultPortModel): JSX.Element {
return null;
}
generateModel(): DefaultPortModel {
return new DefaultPortModel(true, "unknown");
}
}

View File

@@ -0,0 +1,27 @@
import { LabelModel } from "../../models/LabelModel";
import * as _ from "lodash";
import { DeserializeEvent } from "@projectstorm/react-canvas";
export class DefaultLabelModel extends LabelModel {
protected label: string;
constructor() {
super("default");
this.offsetY = -23;
}
setLabel(label: string) {
this.label = label;
}
deSerialize(event: DeserializeEvent) {
super.deSerialize(event);
this.label = event.data.label;
}
serialize() {
return _.merge(super.serialize(), {
label: this.label
});
}
}

View File

@@ -0,0 +1,78 @@
import { LinkModel, LinkModelListener } from "../../models/LinkModel";
import * as _ from "lodash";
import { DefaultLabelModel } from "./DefaultLabelModel";
import { LabelModel } from "../../models/LabelModel";
import { BaseEvent, DeserializeEvent } from "@projectstorm/react-canvas";
export interface DefaultLinkModelListener extends LinkModelListener {
colorChanged?(event: BaseEvent<DefaultLinkModel> & { color: null | string }): void;
widthChanged?(event: BaseEvent<DefaultLinkModel> & { width: 0 | number }): void;
}
export class DefaultLinkModel extends LinkModel<DefaultLinkModelListener> {
protected width: number;
protected color: string;
protected curvyness: number;
constructor(type: string = "default") {
super(type);
this.color = "rgba(255,255,255,0.5)";
this.width = 3;
this.curvyness = 50;
}
serialize() {
return _.merge(super.serialize(), {
width: this.width,
color: this.color,
curvyness: this.curvyness
});
}
deSerialize(event: DeserializeEvent) {
super.deSerialize(event);
this.color = event.data.color;
this.width = event.data.width;
this.curvyness = event.data.curvyness;
}
addLabel(label: LabelModel | string) {
if (label instanceof LabelModel) {
return super.addLabel(label);
}
let labelOb = new DefaultLabelModel();
labelOb.setLabel(label);
return super.addLabel(labelOb);
}
setWidth(width: number) {
this.width = width;
this.iterateListeners("width changed", (listener: DefaultLinkModelListener, event: BaseEvent) => {
if (listener.widthChanged) {
listener.widthChanged({ ...event, width: width });
}
});
}
setColor(color: string) {
this.color = color;
this.iterateListeners("color changed", (listener: DefaultLinkModelListener, event: BaseEvent) => {
if (listener.colorChanged) {
listener.colorChanged({ ...event, color: color });
}
});
}
getWidth() {
return this.width;
}
getColor() {
return this.color;
}
getCurvyness() {
return this.curvyness;
}
}

Some files were not shown because too many files have changed in this diff Show More