Compare commits

..

107 Commits

Author SHA1 Message Date
Dylan Vorster
d2c5bac65c v6.0.0-beta.0 2019-08-11 00:57:16 +02:00
Dylan Vorster
5fc17b6217 changelog 2019-08-11 00:53:52 +02:00
Dylan Vorster
d2e1cf2dd1 yaml is a piece of s*** 2019-08-11 00:35:56 +02:00
Dylan Vorster
9e2f6ffb0f sigh 2019-08-11 00:35:00 +02:00
Dylan Vorster
cc900b2a90 sigh 2019-08-11 00:32:39 +02:00
Dylan Vorster
51c6bd2391 fix tests 2019-08-11 00:31:49 +02:00
Dylan Vorster
a8c6031166 more restructuring 2019-08-10 22:33:06 +02:00
Dylan Vorster
a6655dbf2d more restructuring 2019-08-10 22:30:01 +02:00
Dylan Vorster
37532edd01 more readme 2019-08-10 22:24:46 +02:00
Dylan Vorster
4538884a96 Readme 2019-08-10 21:59:49 +02:00
Dylan Vorster
c9f7d2c516 ITS DONE 2019-08-10 21:18:29 +02:00
Dylan Vorster
5c66c84a31 I think we done 2019-08-10 16:58:56 +02:00
Dylan Vorster
4a57ecdc97 lik 99% there 2019-08-10 16:37:40 +02:00
Dylan Vorster
9fc65b7d7f added some nice apis to improve rendering and such 2019-08-10 15:04:43 +02:00
Dylan Vorster
2573c5d8cc Fixed up the custom node demo 2019-08-10 14:27:54 +02:00
Dylan Vorster
a46e17306b serialization is almost working again 2019-08-10 14:08:28 +02:00
Dylan Vorster
7045d75ae7 reworked deserialization 2019-08-10 12:46:59 +02:00
Dylan Vorster
47e4a14520 fixing serialization 2019-08-10 12:01:08 +02:00
Dylan Vorster
58eee117e9 we have parity with dragging and creating new links 2019-08-09 00:03:42 +02:00
Dylan Vorster
09d5b65e59 got more parity with moving points and such 2019-08-07 21:50:41 +02:00
Dylan Vorster
ea6adbe61b more parity 2019-08-06 23:56:17 +02:00
Dylan Vorster
5f74600157 some more parity work 2019-08-06 23:50:50 +02:00
Dylan Vorster
3590245f6c moving items works again 2019-08-05 00:24:43 +02:00
Dylan Vorster
46aa99b2ea selections are now event driven 2019-08-04 18:12:24 +02:00
Dylan Vorster
80f86867fd dragging and stuff is working again 2019-08-04 17:51:03 +02:00
Dylan Vorster
52f9105b1b state machines yay 2019-08-04 17:25:56 +02:00
Dylan Vorster
e9558919fb pretty 2019-08-04 13:28:38 +02:00
Dylan Vorster
dd5a0b7d8d break it up further into canvas 2019-08-04 13:27:55 +02:00
Dylan Vorster
0f7930e132 some minor things 2019-08-02 23:55:28 +02:00
Dylan Vorster
3823adc8e8 v6.0.0-alpha.4.2 2019-07-31 23:58:04 +02:00
Dylan Vorster
fce011a11b Merge pull request #379 from dudenamedjune/lerna
fixed path issue in lib-routing/src/link/PathFindingLinkWidget.tsx
2019-07-31 18:39:50 +02:00
dudenamedjune
4962343b54 fixed path issue in lib-routing/src/link/PathFindingLinkWidget.tsx 2019-07-31 11:22:29 -05:00
Dylan Vorster
45649146c9 v6.0.0-alpha.4.1 2019-07-30 22:53:42 +02:00
Dylan Vorster
74e24331ed fix flow demo 2019-07-30 22:48:57 +02:00
Dylan Vorster
0cc469fa44 storybook 2019-07-30 22:43:29 +02:00
Dylan Vorster
0b78ba69c6 fix up the builds 2019-07-30 22:36:44 +02:00
Dylan Vorster
ca301b9d4d v6.0.0-alpha.4.0 2019-07-30 22:23:18 +02:00
Dylan Vorster
ad6c77da28 v6.0.0-alpha.3.0 2019-07-30 22:15:19 +02:00
Dylan Vorster
66b72b4465 v6.0.0-y.0 2019-07-30 22:06:26 +02:00
Dylan Vorster
bb878657ba v6.0.0-alpha.1 2019-07-30 21:58:06 +02:00
Dylan Vorster
1db1e21fc2 hmm 2019-07-30 21:57:27 +02:00
Dylan Vorster
f4920ab4d0 v6.0.0-alpha.0 2019-07-30 20:18:37 +02:00
Dylan Vorster
ccfd8e026c some work 2019-07-30 20:18:21 +02:00
Dylan Vorster
702290d9b3 leaking logs 2019-07-30 01:54:47 +02:00
Dylan Vorster
347377d3b4 fixed a bunch of issues with links, and moved it all to styled components 2019-07-30 01:53:07 +02:00
Dylan Vorster
44fd2ad91b fix type issues 2019-07-30 00:12:30 +02:00
Dylan Vorster
39d49c9ee0 type issue fixes 2019-07-29 23:34:36 +02:00
Dylan Vorster
61c6d4b161 massive performance wins 2019-07-29 00:40:22 +02:00
Dylan Vorster
b324154eff pretty 2019-07-28 22:32:29 +02:00
Dylan Vorster
17350a740a fix points 2019-07-28 22:32:08 +02:00
Dylan Vorster
d7fc5370ab improved routing 2019-07-28 21:52:48 +02:00
Dylan Vorster
5fbe7ce9e4 pretty 2019-07-28 21:29:45 +02:00
Dylan Vorster
3e37e34155 generics 2019-07-28 20:23:15 +02:00
Dylan Vorster
f46680596c Fixed bugs after refactor 2019-07-28 18:28:18 +02:00
Dylan Vorster
3a2d3ccb73 make all the things pluggable 2019-07-28 18:13:03 +02:00
Dylan Vorster
f4aa1852ed improved actions 2019-07-28 16:08:37 +02:00
Dylan Vorster
e167885038 Start improving the action system 2019-07-28 14:56:47 +02:00
Dylan Vorster
619e8a2347 more parity 2019-07-27 23:07:04 +02:00
Dylan Vorster
4c1e67106a make the widgets report their dimensions 2019-07-27 22:53:04 +02:00
Dylan Vorster
d08be537e1 use geometry 2019-07-27 16:12:37 +02:00
Dylan Vorster
8e5243da95 more resize stuffs 2019-07-27 00:31:30 +02:00
Dylan Vorster
22236f8420 oops 2019-07-27 00:25:13 +02:00
Dylan Vorster
99fe94ae29 resize observers 2019-07-27 00:23:19 +02:00
Dylan Vorster
3b0c19d998 fixes 2019-07-26 23:15:24 +02:00
Dylan Vorster
fc67d477a9 start using better geometry 2019-07-26 22:38:21 +02:00
Dylan Vorster
e4d427e0d3 formatting 2019-07-26 17:33:45 +02:00
Dylan Vorster
bc2d444aee dagre stuffs 2019-07-26 17:33:27 +02:00
Dylan Vorster
b15a326dab Fixed labels 2019-07-26 16:17:46 +02:00
Dylan Vorster
b54feb03e4 fixed more stuff 2019-07-26 14:31:18 +02:00
Dylan Vorster
39eddc0a2a Fixed up the demos 2019-07-26 14:11:05 +02:00
Dylan Vorster
85a1fde2e5 fixes and improvements 2019-07-26 13:54:09 +02:00
Dylan Vorster
c0dae87d02 cleanup 2019-07-26 13:38:34 +02:00
Dylan Vorster
fddec93370 more generics 2019-07-26 13:37:30 +02:00
Dylan Vorster
01ffbce479 saftey push 2019-07-25 22:29:47 +02:00
Dylan Vorster
30790682d3 spacing 2019-07-25 15:58:34 +02:00
Dylan Vorster
dc9c2eae66 API improvements and start moving to emotion 2019-07-25 15:57:39 +02:00
Dylan Vorster
969152a814 links 2019-07-25 01:15:01 +02:00
Dylan Vorster
2e38a7cfd5 space 2019-07-25 01:13:04 +02:00
Dylan Vorster
e3fe7319c1 changelog 2019-07-25 01:12:19 +02:00
Dylan Vorster
c3a332ebab fine ill add it again 2019-07-25 01:00:59 +02:00
Dylan Vorster
314bb908f9 sigh 2019-07-25 00:59:04 +02:00
Dylan Vorster
9b16b3f4ed try this out for size 2019-07-25 00:56:13 +02:00
Dylan Vorster
722b6d98f3 tests work globally again 2019-07-25 00:54:48 +02:00
Dylan Vorster
2c6971be9a remove snapshot testing 2019-07-25 00:44:14 +02:00
Dylan Vorster
ea0160cbcf e2e tests 2019-07-25 00:32:56 +02:00
Dylan Vorster
efc53eec9b spelling 2019-07-24 16:40:43 +02:00
Dylan Vorster
b5c86159d1 some docs 2019-07-24 16:37:05 +02:00
Dylan Vorster
ea2070ec98 more cleanup 2019-07-24 16:32:05 +02:00
Dylan Vorster
39c3611686 formatting 2019-07-24 16:26:46 +02:00
Dylan Vorster
1ff63d4117 routing works again :yey: 2019-07-24 16:19:31 +02:00
Dylan Vorster
e80da8853f got the demos working again 2019-07-24 15:31:19 +02:00
Dylan Vorster
a06e23eaf6 more lerna work 2019-07-24 15:17:48 +02:00
Dylan Vorster
4b599fc37f getting started 2019-07-24 13:40:19 +02:00
Dylan Vorster
def92af022 lerna 2019-07-24 00:31:31 +02:00
Dylan Vorster
01e9f5f2db changes 2019-07-22 23:27:55 +02:00
Dylan Vorster
aa1a02774d 5.3.2 2019-07-22 23:26:57 +02:00
Dylan Vorster
e60aac56db 5.3.1 2019-07-22 23:26:54 +02:00
Dylan Vorster
b2e74d8b97 badge 2019-07-22 23:08:24 +02:00
Dylan Vorster
e333a4f168 correct version 2019-07-22 22:59:31 +02:00
Dylan Vorster
1d316409e1 npm 2019-07-22 22:57:10 +02:00
Dylan Vorster
9d947d40b7 5.3.1 2019-07-22 22:55:55 +02:00
Dylan Vorster
7d80f15358 improvements 2019-07-22 22:55:33 +02:00
Dylan Vorster
28844b86eb 5.3.0 2019-07-22 22:48:40 +02:00
Dylan Vorster
70c9aeac66 please 2 2019-07-22 22:44:15 +02:00
Dylan Vorster
12a7bfbda5 please 2019-07-22 21:58:46 +02:00
Dylan Vorster
650061db4a fix jest 2019-07-22 21:05:28 +02:00
Dylan Vorster
48c6d1f038 changelog 2019-07-22 20:42:53 +02:00
293 changed files with 11305 additions and 24306 deletions

View File

@@ -2,30 +2,22 @@ version: 2
jobs:
build:
docker:
- image: projectstorm/react-diagrams-ci
- image: buildkite/puppeteer
working_directory: ~/repo
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}
- v1-dependencies-{{ checksum "yarn.lock" }}
- run: yarn install
- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package.json" }}
key: v1-dependencies-{{ checksum "yarn.lock" }}
# test building project
- run: yarn run prepublishOnly
# test building storybook
- run: yarn run storybook:build
- run: yarn run build
# test e2e tests and jest snapshots
- run: yarn run test:ci
- run: cd packages/diagrams-demo-gallery && yarn run test --ci
- run: cd packages/react-diagrams-routing && yarn run test --ci

View File

@@ -4,6 +4,6 @@ indent_size = 2
trim_trailing_whitespace = true
# Some exceptions
[{package.json}]
[{package.json,*.yml}]
indent_style = space
indent_size = 2

View File

@@ -4,21 +4,18 @@
- [ ] 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
- [ ] Had a beer/coffee 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:
## Feel good image:
(Add your own one below :])

200
.gitignore vendored
View File

@@ -1,194 +1,8 @@
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
#!! ERROR: net is undefined. Use list command to see defined gitignore types !!#
### NetBeans ###
nbproject/private/
build/
nbbuild/
nbdist/
nbactions.xml
.nb-gradle/
### SublimeText ###
# cache files for sublime text
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache
# workspace files are user-specific
*.sublime-workspace
# project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using SublimeText
# *.sublime-project
# sftp configuration file
sftp-config.json
# Package control specific files
Package Control.last-run
Package Control.ca-list
Package Control.ca-bundle
Package Control.system-ca-bundle
Package Control.cache/
Package Control.ca-certs/
bh_unicode_properties.cache
# Sublime-github package stores a github token in this file
# https://packagecontrol.io/packages/sublime-github
GitHub.sublime-settings
### PhpStorm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/workspace.xml
.idea/tasks.xml
.idea/dictionaries
.idea/vcs.xml
.idea/jsLibraryMappings.xml
# Sensitive or high-churn files:
.idea/dataSources.ids
.idea/dataSources.xml
.idea/dataSources.local.xml
.idea/sqlDataSources.xml
.idea/dynamic.xml
.idea/uiDesigner.xml
# Gradle:
.idea/gradle.xml
.idea/libraries
# Mongo Explorer plugin:
.idea/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### PhpStorm Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
### Windows ###
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
### OSX ###
*.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Node ###
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
dist
.DS_Store
.idea
.out
*.zip
.env
node_modules
yarn-error.log

View File

@@ -1,194 +0,0 @@
demos
images
docs
.out
.storybook
.circleci
tests
*.md
# Created by https://www.gitignore.io/api/net,netbeans,sublimetext,phpstorm,windows,osx,node
#!! ERROR: net is undefined. Use list command to see defined gitignore types !!#
### NetBeans ###
nbproject/private/
build/
nbbuild/
nbdist/
nbactions.xml
.nb-gradle/
### SublimeText ###
# cache files for sublime text
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache
# workspace files are user-specific
*.sublime-workspace
# project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using SublimeText
# *.sublime-project
# sftp configuration file
sftp-config.json
# Package control specific files
Package Control.last-run
Package Control.ca-list
Package Control.ca-bundle
Package Control.system-ca-bundle
Package Control.cache/
Package Control.ca-certs/
bh_unicode_properties.cache
# Sublime-github package stores a github token in this file
# https://packagecontrol.io/packages/sublime-github
GitHub.sublime-settings
### PhpStorm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/workspace.xml
.idea/tasks.xml
.idea/dictionaries
.idea/vcs.xml
.idea/jsLibraryMappings.xml
# Sensitive or high-churn files:
.idea/dataSources.ids
.idea/dataSources.xml
.idea/dataSources.local.xml
.idea/sqlDataSources.xml
.idea/dynamic.xml
.idea/uiDesigner.xml
# Gradle:
.idea/gradle.xml
.idea/libraries
# Mongo Explorer plugin:
.idea/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### PhpStorm Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
### Windows ###
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
### OSX ###
*.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Node ###
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
.idea

3
.prettierignore Normal file
View File

@@ -0,0 +1,3 @@
node_modules
dist
.out

7
.prettierrc Normal file
View File

@@ -0,0 +1,7 @@
{
"semi": true,
"singleQuote": true,
"jsxBracketSameLine": true,
"useTabs": true,
"printWidth": 120
}

View File

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

View File

@@ -1,64 +1,107 @@
__6.0.0__
Note: This is a complete rewrite of the library, a good place to start to see how the new system works
is with the new demo project which illustrates the new capability.
I would also recommend taking a look at the new updated DiamondPort widget which shows more capability.
* Break up library into monorepo
* Introduce react-canvas-core as a new framework
* Use geometry classes instead of raw X and Y primitives so we can do matrix stuff in the future
* move testing framework to a name based system instead of ID's
* Introduce multiple layers (can now have multiple node and link layers)
* Rewrote the deserialization system to be promise based
* Completely overhauled the observer framework on the models
* Moved all the logic in the DiagramWidget into a a new hierarchical state machine
* Introduces new states for editing
* Introduced faster layout rendering when transforming the canvas directly
* Moved all canvas smart routing into its own link-type under routing package
* Broke up link rendering into a much more modular system that is much easier to extend
* Introduced port alignment allowing the developer to specify how enter it
* Improved generics throughout the entire model system with Mapped Types
* Rewrote all the styles using emotion instead of sass
* Fixed up all the demos to use the new API
* Introduced a demo project that illustrates how to use the library with ES6 as well as with Typescript
* Improved the grid rendering system to allow graphical elements to specify how they get transformed
* Introduced a performance widget for improving performance in a more deterministic way by comparing the serialization of the model (with a way of opting out)
* Renamed a bunch of methods to be more consistent and more understandable
* Completely removed the double render state system that required nodes to render before links, this is done when ports report their new positions
* Ports can now dynamically be added and removed without having to tell the system it happeend
* Port widgets are now containers dumb containers for you own ports
* Port widgets report new sizing information to their target links when they change position, you no longer need to invalidate them
__5.3.2__
* (maintenance) Upgrade :allthethings: (all the build tooling was upgrade)
* (api) move to ES6 (JS now contains native classes)
* (api) changed package name to @projectstorm/react-diagrams
* (bug) (PR259)(https://github.com/projectstorm/react-diagrams/pull/259) Fixes #258
* (refactor) (PR 306)(https://github.com/projectstorm/react-diagrams/pull/306) `:any` fix
* (feature) (PR 178)(https://github.com/projectstorm/react-diagrams/pull/178) Trigger a positionChanged event when moving a Node that has the listener assigned.
* (fix) (PR 356)(https://github.com/projectstorm/react-diagrams/pull/356) Fixed Type issue with 'PointModel()'
* (demo) dark mode and upgrade storybook
__5.2.1__
* [fix] Always remove link from old source/target port on port change
* [maintenance] upgrade node modules
* [refactor] https://github.com/projectstorm/react-diagrams/commit/55f62587bd3b12513c7d37eff59edfc8bdb8d6c9
* [bug] https://github.com/projectstorm/react-diagrams/commit/75ef02dd4d131a0e7c08b2680c69efc390e50b84
* (fix) Always remove link from old source/target port on port change
* (maintenance) upgrade node modules
* (refactor) https://github.com/projectstorm/react-diagrams/commit/55f62587bd3b12513c7d37eff59edfc8bdb8d6c9
* (bug) https://github.com/projectstorm/react-diagrams/commit/75ef02dd4d131a0e7c08b2680c69efc390e50b84
-> and other improvements, also checkout the foundation work happening over at https://github.com/projectstorm/react-canvas
__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
* (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!
* (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
* (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
* (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
* (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
* (bugs) swapping diagram models in engines
* (bugs) issues with the rendering pipeline #107
* added ci badge to Readme
__3.1.3__

View File

@@ -1,46 +1,84 @@
# STORM React Diagrams
# STORM React Diagrams __6__ [beta]
__PSA 2018__: React Diagrams ~is currently~ was 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).
[![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/@projectstorm/react-diagrams.svg)](https://npmjs.org/package/@projectstorm/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) [![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lerna.js.org/)
__PSA 2019__: I still want to jump onto the rewrite, but it is a much larger project than anticipated, so going to try maintain this one in the mean time.
![](./docs/images/logo.jpg)
[pssst! Looking for the old version 5?](https://github.com/projectstorm/react-diagrams/tree/v5.3.2)
---
**DEMO**: [http://projectstorm.cloud/react-diagrams](http://projectstorm.cloud/react-diagrams)
**(SOME) DOCS:** [https://projectstorm.gitbooks.io/react-diagrams](https://projectstorm.gitbooks.io/react-diagrams)
**DOCS (incomplete)** [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/)
## What
A super simple, no-nonsense diagramming library written in React that just works.
A flow & process orientated diagramming library inspired by __Blender__, __Labview__ and __Unreal engine__.
[![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)
* __Modern Codebase__ written entirely in Typescript and React, the library makes use of powerful generics, advanced software engineering principles and is broken up into multiple modules.
* __Hackable and extensible__ the entire library including its core can be extended, rewired and re-assembled into fundamentally different software to suit your own software needs.
* __HTML nodes as a first class citizen__ the library was originally written to represent advanced dynamic nodes, that are difficult to represent as SVG's due to complex input requirements ux requirements.
* __Designed for process__ the library is aimed for software engineers that want to rewire their programs at runtime, and that want to make their software more dynamic.
* __Fast diagram editing__ the defaults provided give the heighest priority to editing diagrams as fast as possible.
## Gallery
Example implementation using custom models: (Dylan's personal code)
![Personal Project](./docs/images/example1.jpg)
![](./docs/images/example2.jpg)
Example implementation using custom models:
![Personal Project](./images/example1.jpg)
![](./images/example2.jpg)
Get started with the default models right out of the box:
![](./images/example3.jpg)
![](./docs/images/example3.jpg)
## Introduction
## Installing
A no-nonsense diagramming library written entirely in React with the help of a few small libraries. It aims to be:
For all the bells and whistles:
* Simple, and void of any fuss/complications when implementing it into your own application
* 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
yarn add @projectstorm/react-diagrams@next
#### Run the demos
This includes all the packages listed below (and works (mostly and conceptually) like it used to in version 5.0)
After running `yarn install` you must then run: `yarn run storybook`
### A more modular approach
#### Building from source
This library now has a more modular design and you can import just the core (contains no default factories or routing)
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.
yarn add @projectstorm/react-diagrams-core@next
this is built ontop of the evolving __react-canvas-core__ library
```
yarn add @projectstorm/react-diagrams-core@next
```
which makes use of
```
yarn add @projectstorm/react-geometry@next
```
and of course, you can add some extras:
yarn add @projectstorm/react-diagrams-defaults@next
yarn add @projectstorm/react-diagrams-routing@next
## How to use
Take a look at the [demos](packages/diagrams-demo-gallery)
__or__
Take a look at the [demo project](packages/diagrams-demo-project) which contains an example for ES6 as well as Typescript
## Run the demos
After running `yarn install` you must then run: `cd packages/diagrams-demo-gallery && yarn run start`
## Building from source
Simply run `yarn build` in the root directory \(or `NODE_ENV=production yarn build` 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\).
## [Checkout the docs](https://projectstorm.gitbooks.io/react-diagrams)

View File

@@ -1,23 +0,0 @@
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>
);
}
}

View File

@@ -1,23 +0,0 @@
import * as React from "react";
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);
});
}
}

View File

@@ -1,77 +0,0 @@
@import "../../src/sass/main";
html, body, #root{
height: 100%;
padding: 0;
margin: 0;
}
.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%;
}
}
.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

@@ -1,18 +0,0 @@
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,12 +0,0 @@
import { NodeModel } from "storm-react-diagrams";
import { DiamondPortModel } from "./DiamondPortModel";
export class DiamondNodeModel extends NodeModel {
constructor() {
super("diamond");
this.addPort(new DiamondPortModel("top"));
this.addPort(new DiamondPortModel("left"));
this.addPort(new DiamondPortModel("bottom"));
this.addPort(new DiamondPortModel("right"));
}
}

View File

@@ -1,105 +0,0 @@
import * as React from "react";
import { DiamondNodeModel } from "./DiamondNodeModel";
import { PortWidget } from "storm-react-diagrams";
export interface DiamonNodeWidgetProps {
node: DiamondNodeModel;
size?: number;
}
export interface DiamonNodeWidgetState {}
/**
* @author Dylan Vorster
*/
export class DiamonNodeWidget extends React.Component<DiamonNodeWidgetProps, DiamonNodeWidgetState> {
public static defaultProps: DiamonNodeWidgetProps = {
size: 150,
node: null
};
constructor(props: DiamonNodeWidgetProps) {
super(props);
this.state = {};
}
render() {
return (
<div
className={"diamond-node"}
style={{
position: "relative",
width: this.props.size,
height: this.props.size
}}
>
<svg
width={this.props.size}
height={this.props.size}
dangerouslySetInnerHTML={{
__html:
`
<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 +
`,10 ` +
(this.props.size - 10) +
`,` +
this.props.size / 2 +
` ` +
this.props.size / 2 +
`,` +
(this.props.size - 10) +
` "/>
</g>
`
}}
/>
<div
style={{
position: "absolute",
zIndex: 10,
top: this.props.size / 2 - 8,
left: -8
}}
>
<PortWidget name="left" node={this.props.node} />
</div>
<div
style={{
position: "absolute",
zIndex: 10,
left: this.props.size / 2 - 8,
top: -8
}}
>
<PortWidget name="top" node={this.props.node} />
</div>
<div
style={{
position: "absolute",
zIndex: 10,
left: this.props.size - 8,
top: this.props.size / 2 - 8
}}
>
<PortWidget name="right" node={this.props.node} />
</div>
<div
style={{
position: "absolute",
zIndex: 10,
left: this.props.size / 2 - 8,
top: this.props.size - 8
}}
>
<PortWidget name="bottom" node={this.props.node} />
</div>
</div>
);
}
}

View File

@@ -1,26 +0,0 @@
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

@@ -1,14 +0,0 @@
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

@@ -1,57 +0,0 @@
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.installDefaultFactories();
// 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.setDiagramModel(model);
//6) render the diagram!
return <DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />;
};

View File

@@ -1,56 +0,0 @@
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 - node.width / 2;
modelNode.y = node.y - node.height / 2;
});
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)
);
}

View File

@@ -1,120 +0,0 @@
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.setDiagramModel(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.installDefaultFactories();
//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.setDiagramModel(model2);
return <Demo8Widget engine={engine} />;
};

View File

@@ -1,72 +0,0 @@
import * as React from "react";
import * as _ from "lodash";
import { TrayWidget } from "./TrayWidget";
import { Application } from "../Application";
import { TrayItemWidget } from "./TrayItemWidget";
import { DefaultNodeModel, DiagramWidget } from "storm-react-diagrams";
export interface BodyWidgetProps {
app: Application;
}
export interface BodyWidgetState {}
/**
* @author Dylan Vorster
*/
export class BodyWidget extends React.Component<BodyWidgetProps, BodyWidgetState> {
constructor(props: BodyWidgetProps) {
super(props);
this.state = {};
}
render() {
return (
<div className="body">
<div className="header">
<div className="title">Storm React Diagrams - Demo 5</div>
</div>
<div className="content">
<TrayWidget>
<TrayItemWidget model={{ type: "in" }} name="In Node" color="rgb(192,255,0)" />
<TrayItemWidget model={{ type: "out" }} name="Out Node" color="rgb(0,192,255)" />
</TrayWidget>
<div
className="diagram-layer"
onDrop={event => {
var data = JSON.parse(event.dataTransfer.getData("storm-diagram-node"));
var nodesCount = _.keys(
this.props.app
.getDiagramEngine()
.getDiagramModel()
.getNodes()
).length;
var node = null;
if (data.type === "in") {
node = new DefaultNodeModel("Node " + (nodesCount + 1), "rgb(192,255,0)");
node.addInPort("In");
} else {
node = new DefaultNodeModel("Node " + (nodesCount + 1), "rgb(0,192,255)");
node.addOutPort("Out");
}
var points = this.props.app.getDiagramEngine().getRelativeMousePoint(event);
node.x = points.x;
node.y = points.y;
this.props.app
.getDiagramEngine()
.getDiagramModel()
.addNode(node);
this.forceUpdate();
}}
onDragOver={event => {
event.preventDefault();
}}
>
<DiagramWidget className="srd-demo-canvas" diagramEngine={this.props.app.getDiagramEngine()} />
</div>
</div>
</div>
);
}
}

View File

@@ -1,31 +0,0 @@
import * as React from "react";
export interface TrayItemWidgetProps {
model: any;
color?: string;
name: string;
}
export interface TrayItemWidgetState {}
export class TrayItemWidget extends React.Component<TrayItemWidgetProps, TrayItemWidgetState> {
constructor(props: TrayItemWidgetProps) {
super(props);
this.state = {};
}
render() {
return (
<div
style={{ borderColor: this.props.color }}
draggable={true}
onDragStart={event => {
event.dataTransfer.setData("storm-diagram-node", JSON.stringify(this.props.model));
}}
className="tray-item"
>
{this.props.name}
</div>
);
}
}

View File

@@ -1,21 +0,0 @@
import * as React from "react";
export interface TrayWidgetProps {}
export interface TrayWidgetState {}
/**
* @author Dylan Vorster
*/
export class TrayWidget extends React.Component<TrayWidgetProps, TrayWidgetState> {
public static defaultProps: TrayWidgetProps = {};
constructor(props: TrayWidgetProps) {
super(props);
this.state = {};
}
render() {
return <div className="tray">{this.props.children}</div>;
}
}

View File

@@ -1,12 +0,0 @@
import * as React from "react";
import { BodyWidget } from "./components/BodyWidget";
import { Application } from "./Application";
import "./sass/main.scss";
export default () => {
var app = new Application();
return <BodyWidget app={app} />;
};

View File

@@ -1,47 +0,0 @@
.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;
}
}
}
}

View File

@@ -1,37 +0,0 @@
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.installDefaultFactories();
//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.setDiagramModel(model);
//6) render the diagram!
return <DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />;
};

View File

@@ -1,72 +0,0 @@
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.installDefaultFactories();
// 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 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.setDiagramModel(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

@@ -1,49 +0,0 @@
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.installDefaultFactories();
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.setDiagramModel(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

@@ -1,62 +0,0 @@
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.installDefaultFactories();
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.setDiagramModel(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

@@ -1,69 +0,0 @@
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.installDefaultFactories();
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.setDiagramModel(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

@@ -1,86 +0,0 @@
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.setDiagramModel(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.installDefaultFactories();
//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.setDiagramModel(model);
//6) render the diagram!
return <NodeDelayedPosition engine={engine} model={model} />;
};

View File

@@ -1,59 +0,0 @@
import { DiagramEngine, DiagramModel, DefaultNodeModel, LinkModel, DiagramWidget } 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";
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.installDefaultFactories();
//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.setDiagramModel(model);
//!------------- SERIALIZING ------------------
var str = JSON.stringify(model.serializeDiagram());
//!------------- DESERIALIZING ----------------
var model2 = new DiagramModel();
model2.deSerializeDiagram(JSON.parse(str), engine);
engine.setDiagramModel(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

@@ -1,38 +0,0 @@
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.installDefaultFactories();
//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.setDiagramModel(model);
//6) render the diagram!
return <DiagramWidget className="srd-demo-canvas" diagramEngine={engine} allowLooseLinks={false} />;
};

View File

@@ -1,9 +0,0 @@
# 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

@@ -1,41 +0,0 @@
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.installDefaultFactories();
//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.setDiagramModel(model);
//6) render the diagram!
return <DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />;
};

View File

@@ -1,68 +0,0 @@
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.installDefaultFactories();
// 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.setDiagramModel(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

@@ -1,71 +0,0 @@
import * as React from "react";
import {storiesOf, addDecorator, addParameters} 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 {themes} from '@storybook/theming';
addParameters({
options: {
theme: themes.dark,
},
});
//include the SCSS for the demo
import "./.helpers/demo.scss";
setOptions({
name: "STORM React Diagrams",
url: "https://github.com/projectstorm/react-diagrams",
addonPanelInRight: true
});
import demo_simple from "./demo-simple";
import demo_flow from "./demo-simple-flow";
import demo_performance from "./demo-performance";
import demo_locks from "./demo-locks";
import demo_grid from "./demo-grid";
import demo_limit_points from "./demo-limit-points";
import demo_listeners from "./demo-listeners";
import demo_zoom from "./demo-zoom-to-fit";
import demo_labels from "./demo-labelled-links";
storiesOf("Simple Usage", module)
.add("Simple example", demo_simple)
.add("Simple flow example", demo_flow)
.add("Performance demo", demo_performance)
.add("Locked widget", demo_locks)
.add("Canvas grid size", demo_grid)
.add("Limiting link points", demo_limit_points)
.add("Events and listeners", demo_listeners)
.add("Zoom to fit", demo_zoom)
.add("Links with labels", demo_labels);
import demo_adv_clone_selected from "./demo-cloning";
import demo_adv_ser_des from "./demo-serializing";
import demo_adv_prog from "./demo-mutate-graph";
import demo_adv_dnd from "./demo-drag-and-drop";
import demo_smart_routing from "./demo-smart-routing";
storiesOf("Advanced Techniques", module)
.add("Clone Selected", demo_adv_clone_selected)
.add("Serializing and de-serializing", demo_adv_ser_des)
.add("Programatically modifying graph", demo_adv_prog)
.add("Drag and drop", demo_adv_dnd)
.add("Smart routing", demo_smart_routing);
import demo_cust_nodes from "./demo-custom-node1";
import demo_cust_links from "./demo-custom-link1";
storiesOf("Custom Models", module)
.add("Custom diamond node", demo_cust_nodes)
.add("Custom animated links", demo_cust_links);
import demo_3rd_dagre from "./demo-dagre";
storiesOf("3rd party libraries", module)
.add("Auto Distribute (Dagre)", demo_3rd_dagre);
// enable this to log mouse location when writing new puppeteer tests
//Helper.logMousePosition()

View File

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

View File

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 94 KiB

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

Before

Width:  |  Height:  |  Size: 438 KiB

After

Width:  |  Height:  |  Size: 438 KiB

View File

Before

Width:  |  Height:  |  Size: 313 KiB

After

Width:  |  Height:  |  Size: 313 KiB

View File

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

BIN
docs/images/logo.jpg Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

View File

Before

Width:  |  Height:  |  Size: 354 KiB

After

Width:  |  Height:  |  Size: 354 KiB

View File

Before

Width:  |  Height:  |  Size: 161 KiB

After

Width:  |  Height:  |  Size: 161 KiB

View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

View File

@@ -1,27 +0,0 @@
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"
]
};

5
lerna.json Normal file
View File

@@ -0,0 +1,5 @@
{
"npmClient": "yarn",
"useWorkspaces": true,
"version": "6.0.0-beta.0"
}

View File

@@ -1,10 +1,13 @@
{
"name": "storm-react-diagrams",
"version": "5.2.2-0",
"name": "@projectstorm/react-diagrams",
"version": "5.3.2",
"author": "dylanvorster",
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/projectstorm/react-diagrams.git"
},
"workspaces": ["packages/*"],
"keywords": [
"web",
"diagram",
@@ -16,21 +19,16 @@
"links",
"nodes"
],
"main": "./dist/main.js",
"typings": "./dist/@types/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"
"build": "yarn build:clean && lerna run build --stream",
"build:prod": "yarn build:clean && lerna run build:prod --stream",
"watch": "yarn build:clean && lerna run watch --stream",
"publish:dev": "yarn build:prod && lerna publish --force-publish --dist-tag=next",
"publish:prod": "yarn build:prod && lerna publish --force-publish",
"publish:storybook": "cd packages/diagrams-demo-gallery && yarn storybook:build && ../../node_modules/.bin/storybook-to-ghpages --existing-output-dir .out",
"build:clean": "lerna run clean --stream",
"test:ci": "lerna run test --stream -- --runInBand --ci ",
"pretty": "prettier --write \"packages/**/*.{ts,tsx,scss,js,jsx}\""
},
"peerDependencies": {
"closest": "^0.0.1",
@@ -41,22 +39,27 @@
},
"devDependencies": {
"@babel/core": "^7.5.5",
"@babel/preset-react": "^7.0.0",
"@emotion/core": "^10.0.14",
"@emotion/styled": "^10.0.14",
"@storybook/addon-actions": "^5.1.9",
"@storybook/addon-options": "^5.1.9",
"@storybook/addon-storyshots": "^5.1.9",
"@storybook/addons": "^5.1.9",
"@storybook/react": "^5.1.9",
"@storybook/storybook-deployer": "^2.8.1",
"@storybook/theming": "^5.1.9",
"@types/jest": "^24.0.15",
"@types/dagre": "^0.7.42",
"@types/jest": "^24.0.16",
"@types/jest-environment-puppeteer": "^4.0.0",
"@types/lodash": "^4.14.136",
"@types/mathjs": "^6.0.1",
"@types/node": "^12.6.8",
"@types/promise": "^7.1.30",
"@types/puppeteer": "^1.12.4",
"@types/puppeteer": "^1.19.0",
"@types/react": "^16.8.23",
"babel-loader": "^8.0.6",
"closest": "^0.0.1",
"copy-webpack-plugin": "^5.0.3",
"copy-webpack-plugin": "^5.0.4",
"cross-env": "^5.2.0",
"css-loader": "^3.1.0",
"dagre": "^0.8.4",
@@ -65,20 +68,21 @@
"glob": "^7.1.4",
"jest": "^24.8.0",
"jest-cli": "^24.8.0",
"jest-puppeteer": "^4.3.0",
"json-beautify": "^1.1.0",
"lerna": "^3.16.4",
"lodash": "4.*",
"node-sass": "^4.12.0",
"mathjs": "^6.0.3",
"pathfinding": "^0.4.18",
"paths-js": "^0.4.9",
"prettier": "^1.18.2",
"puppeteer": "^1.18.1",
"puppeteer": "^1.19.0",
"raf": "^3.4.1",
"raw-loader": "^3.1.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-syntax-highlighter": "^11.0.2",
"react-test-renderer": "^16.8.6",
"sass-loader": "^7.1.0",
"source-map-loader": "^0.2.4",
"storybook-host": "^5.1.0",
"storybook-readme": "^5.0.5",
@@ -88,8 +92,9 @@
"ts-loader": "^6.0.4",
"typescript": "^3.5.3",
"val-loader": "^1.1.1",
"webpack": "^4.36.1",
"webpack": "^4.38.0",
"webpack-cli": "^3.3.6",
"webpack-dev-server": "^3.7.2",
"webpack-node-externals": "^1.7.2"
}
}

View File

@@ -1,8 +1,8 @@
import { configure } from '@storybook/react';
function loadStories() {
require('../demos/index.tsx');
require('../index.tsx');
// You can require as many demos as you need.
}
configure(loadStories, module);
configure(loadStories, module);

View File

@@ -1,14 +1,10 @@
const path = require('path');
module.exports = async ({config, mode}) => {
module.exports = async ({ config, mode }) => {
return {
...config,
resolve: {
...config.resolve,
extensions: ['.tsx', '.ts', '.js'],
alias: {
...config.resolve.alias,
'storm-react-diagrams': path.join(__dirname, "..", "src", "main")
}
extensions: ['.tsx', '.ts', '.js']
},
module: {
...config.module,
@@ -22,9 +18,8 @@ module.exports = async ({config, mode}) => {
'css-loader',
{
loader: 'postcss-loader',
options: {config: {path: path.join(__dirname, '..')}}
},
'sass-loader'
options: { config: { path: path.join(__dirname, '..') } }
}
]
},
{
@@ -40,7 +35,7 @@ module.exports = async ({config, mode}) => {
options: {
transpileOnly: true
}
},
}
]
]
}

View File

@@ -1,15 +1,9 @@
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";
import createEngine, { DiagramModel, DefaultNodeModel, LinkModel, NodeModel } from '@projectstorm/react-diagrams';
import * as _ from 'lodash';
import * as React from 'react';
import { DemoButton, DemoWorkspaceWidget } from '../helpers/DemoWorkspaceWidget';
import { BaseModel, CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
/**
* Tests cloning
@@ -23,24 +17,24 @@ class CloneSelected extends React.Component<any, any> {
cloneSelected() {
let { engine } = this.props;
let offset = { x: 100, y: 100 };
let model = engine.getDiagramModel();
let model = engine.getModel();
let itemMap = {};
_.forEach(model.getSelectedItems(), (item: BaseModel<any>) => {
_.forEach(model.getSelectedEntities(), (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);
newItem.setPosition(newItem.getX() + offset.x, newItem.getY() + 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 });
p.setPosition(p.getX() + offset.x, p.getY() + offset.y);
});
model.addLink(newItem);
}
newItem.selected = false;
(newItem as BaseModel).setSelected(false);
});
this.forceUpdate();
@@ -49,8 +43,10 @@ class CloneSelected extends React.Component<any, any> {
render() {
const { engine } = this.props;
return (
<DemoWorkspaceWidget buttons={<button onClick={this.cloneSelected}>Clone Selected</button>}>
<DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />
<DemoWorkspaceWidget buttons={<DemoButton onClick={this.cloneSelected}>Clone Selected</DemoButton>}>
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
</DemoWorkspaceWidget>
);
}
@@ -58,20 +54,19 @@ class CloneSelected extends React.Component<any, any> {
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.installDefaultFactories();
var engine = createEngine();
//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");
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");
var node2 = new DefaultNodeModel('Node 2', 'rgb(192,255,0)');
let port2 = node2.addInPort('In');
node2.setPosition(400, 100);
// link the ports
@@ -81,7 +76,7 @@ export default () => {
model.addAll(node1, node2, link1);
//5) load model into engine
engine.setDiagramModel(model);
engine.setModel(model);
//6) render the diagram!
return <CloneSelected engine={engine} model={model} />;

View File

@@ -1,23 +1,20 @@
import {
DiagramEngine,
import createEngine, {
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";
DefaultLinkFactory,
DefaultLinkModel
} from '@projectstorm/react-diagrams';
import * as React from 'react';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
export class AdvancedLinkModel extends DefaultLinkModel {
constructor() {
super("advanced");
this.width = 10;
super({
type: 'advanced',
width: 10
});
}
}
@@ -54,8 +51,8 @@ export class AdvancedLinkSegment extends React.Component<{ model: AdvancedLinkMo
let point = this.path.getPointAtLength(this.path.getTotalLength() * (this.percent / 100.0));
this.circle.setAttribute("cx", "" + point.x);
this.circle.setAttribute("cy", "" + point.y);
this.circle.setAttribute('cx', '' + point.x);
this.circle.setAttribute('cy', '' + point.y);
if (this.mounted) {
requestAnimationFrame(this.callback);
@@ -72,10 +69,11 @@ export class AdvancedLinkSegment extends React.Component<{ model: AdvancedLinkMo
return (
<>
<path
fill="none"
ref={ref => {
this.path = ref;
}}
strokeWidth={this.props.model.width}
strokeWidth={this.props.model.getOptions().width}
stroke="rgba(255,0,0,0.5)"
d={this.props.path}
/>
@@ -93,15 +91,14 @@ export class AdvancedLinkSegment extends React.Component<{ model: AdvancedLinkMo
export class AdvancedLinkFactory extends DefaultLinkFactory {
constructor() {
super();
this.type = "advanced";
super('advanced');
}
getNewInstance(initialConfig?: any): AdvancedLinkModel {
generateModel(): AdvancedLinkModel {
return new AdvancedLinkModel();
}
generateLinkSegment(model: AdvancedLinkModel, widget: DefaultLinkWidget, selected: boolean, path: string) {
generateLinkSegment(model: AdvancedLinkModel, selected: boolean, path: string) {
return (
<g>
<AdvancedLinkSegment model={model} path={path} />
@@ -117,29 +114,28 @@ export class AdvancedLinkFactory extends DefaultLinkFactory {
*/
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.installDefaultFactories();
engine.registerLinkFactory(new AdvancedLinkFactory());
var engine = createEngine();
engine.getLinkFactories().registerFactory(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"));
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"));
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"));
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"));
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();
@@ -150,8 +146,12 @@ export default () => {
model.addAll(node1, node2, node3, node4);
// load model into engine
engine.setDiagramModel(model);
engine.setModel(model);
// render the diagram!
return <DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />;
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};

View File

@@ -0,0 +1,19 @@
import { DiamonNodeWidget } from './DiamondNodeWidget';
import { DiamondNodeModel } from './DiamondNodeModel';
import * as React from 'react';
import { AbstractReactFactory } from '@projectstorm/react-canvas-core';
import { DiagramEngine } from '@projectstorm/react-diagrams-core';
export class DiamondNodeFactory extends AbstractReactFactory<DiamondNodeModel, DiagramEngine> {
constructor() {
super('diamond');
}
generateReactWidget(event): JSX.Element {
return <DiamonNodeWidget engine={this.engine} size={50} node={event.model} />;
}
generateModel(event) {
return new DiamondNodeModel();
}
}

View File

@@ -0,0 +1,18 @@
import { NodeModel, NodeModelGenerics, PortModelAlignment } from '@projectstorm/react-diagrams';
import { DiamondPortModel } from './DiamondPortModel';
export interface DiamondNodeModelGenerics {
PORT: DiamondPortModel;
}
export class DiamondNodeModel extends NodeModel<NodeModelGenerics & DiamondNodeModelGenerics> {
constructor() {
super({
type: 'diamond'
});
this.addPort(new DiamondPortModel(PortModelAlignment.TOP));
this.addPort(new DiamondPortModel(PortModelAlignment.LEFT));
this.addPort(new DiamondPortModel(PortModelAlignment.BOTTOM));
this.addPort(new DiamondPortModel(PortModelAlignment.RIGHT));
}
}

View File

@@ -0,0 +1,111 @@
import * as React from 'react';
import { DiamondNodeModel } from './DiamondNodeModel';
import { DiagramEngine, PortModelAlignment, PortWidget } from '@projectstorm/react-diagrams';
import styled from '@emotion/styled';
export interface DiamonNodeWidgetProps {
node: DiamondNodeModel;
engine: DiagramEngine;
size?: number;
}
namespace S {
export const Port = styled.div`
width: 16px;
height: 16px;
z-index: 10;
background: rgba(0, 0, 0, 0.5);
border-radius: 8px;
cursor: pointer;
&:hover {
background: rgba(0, 0, 0, 1);
}
`;
}
/**
* @author Dylan Vorster
*/
export class DiamonNodeWidget extends React.Component<DiamonNodeWidgetProps> {
render() {
return (
<div
className={'diamond-node'}
style={{
position: 'relative',
width: this.props.size,
height: this.props.size
}}>
<svg
width={this.props.size}
height={this.props.size}
dangerouslySetInnerHTML={{
__html:
`
<g id="Layer_1">
</g>
<g id="Layer_2">
<polygon fill="mediumpurple" stroke="${
this.props.node.isSelected() ? 'white' : '#000000'
}" stroke-width="3" stroke-miterlimit="10" points="10,` +
this.props.size / 2 +
` ` +
this.props.size / 2 +
`,10 ` +
(this.props.size - 10) +
`,` +
this.props.size / 2 +
` ` +
this.props.size / 2 +
`,` +
(this.props.size - 10) +
` "/>
</g>
`
}}
/>
<PortWidget
style={{
top: this.props.size / 2 - 8,
left: -8,
position: 'absolute'
}}
port={this.props.node.getPort(PortModelAlignment.LEFT)}
engine={this.props.engine}>
<S.Port />
</PortWidget>
<PortWidget
style={{
left: this.props.size / 2 - 8,
top: -8,
position: 'absolute'
}}
port={this.props.node.getPort(PortModelAlignment.TOP)}
engine={this.props.engine}>
<S.Port />
</PortWidget>
<PortWidget
style={{
left: this.props.size - 8,
top: this.props.size / 2 - 8,
position: 'absolute'
}}
port={this.props.node.getPort(PortModelAlignment.RIGHT)}
engine={this.props.engine}>
<S.Port />
</PortWidget>
<PortWidget
style={{
left: this.props.size / 2 - 8,
top: this.props.size - 8,
position: 'absolute'
}}
port={this.props.node.getPort(PortModelAlignment.BOTTOM)}
engine={this.props.engine}>
<S.Port />
</PortWidget>
</div>
);
}
}

View File

@@ -0,0 +1,15 @@
import { LinkModel, PortModel, DefaultLinkModel, PortModelAlignment } from '@projectstorm/react-diagrams';
export class DiamondPortModel extends PortModel {
constructor(alignment: PortModelAlignment) {
super({
type: 'diamond',
name: alignment,
alignment: alignment
});
}
createLinkModel(): LinkModel {
return new DefaultLinkModel();
}
}

View File

@@ -0,0 +1,15 @@
import { DiagramEngine, PortModel } from '@projectstorm/react-diagrams';
import { AbstractModelFactory } from '@projectstorm/react-canvas-core';
export class SimplePortFactory extends AbstractModelFactory<PortModel, DiagramEngine> {
cb: (initialConfig?: any) => PortModel;
constructor(type: string, cb: (initialConfig?: any) => PortModel) {
super(type);
this.cb = cb;
}
generateModel(event): PortModel {
return this.cb(event.initialConfig);
}
}

View File

@@ -0,0 +1,68 @@
import createEngine, { DefaultNodeModel, DiagramModel, PortModelAlignment } from '@projectstorm/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';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
/**
* @Author Dylan Vorster
*/
export default () => {
//1) setup the diagram engine
var engine = createEngine();
// register some other factories as well
engine
.getPortFactories()
.registerFactory(new SimplePortFactory('diamond', config => new DiamondPortModel(PortModelAlignment.LEFT)));
engine.getNodeFactories().registerFactory(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, 200);
//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, 100);
//3-C) link the 2 nodes together
var link1 = port1.link(node2.getPort(PortModelAlignment.LEFT));
var link2 = port3.link(node2.getPort(PortModelAlignment.RIGHT));
var node4 = new DefaultNodeModel('Node 4', 'rgb(0,192,255)');
var port4 = node4.addOutPort('Out');
node4.setPosition(200, 10);
var link3 = port4.link(node2.getPort(PortModelAlignment.TOP));
var node5 = new DefaultNodeModel('Node 5', 'mediumpurple');
var port5 = node5.addInPort('In');
node5.setPosition(400, 300);
var link4 = port5.link(node2.getPort(PortModelAlignment.BOTTOM));
//4) add the models to the root graph
model.addAll(node1, node2, node3, link1, link2, node4, link3, link4, node5);
//5) load model into engine
engine.setModel(model);
//6) render the diagram!
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};

View File

@@ -0,0 +1,129 @@
import createEngine, {
DiagramModel,
DefaultNodeModel,
DefaultPortModel,
NodeModel,
DagreEngine,
DiagramEngine,
PathFindingLinkFactory
} from '@projectstorm/react-diagrams';
import * as React from 'react';
import { DemoButton, DemoWorkspaceWidget } from '../helpers/DemoWorkspaceWidget';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
function createNode(name): any {
return new DefaultNodeModel(name, 'rgb(0,192,255)');
}
let count = 0;
function connectNodes(nodeFrom, nodeTo, engine: DiagramEngine) {
//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);
// ################# UNCOMMENT THIS LINE FOR PATH FINDING #############################
// return portOut.link(portTo, engine.getLinkFactories().getFactory(PathFindingLinkFactory.NAME));
// #####################################################################################
}
/**
* Tests auto distribution
*/
class DemoWidget extends React.Component<{ model: DiagramModel; engine: DiagramEngine }, any> {
engine: DagreEngine;
constructor(props) {
super(props);
this.engine = new DagreEngine({
graph: {
rankdir: 'RL',
ranker: 'longest-path'
},
includeLinks: true
});
}
autoDistribute = () => {
this.engine.redistribute(this.props.model);
// only happens if pathfing is enabled (check line 25)
this.reroute();
this.props.engine.repaintCanvas();
};
componentDidMount(): void {
setTimeout(() => {
this.autoDistribute();
}, 500);
}
reroute() {
this.props.engine
.getLinkFactories()
.getFactory<PathFindingLinkFactory>(PathFindingLinkFactory.NAME)
.calculateRoutingMatrix();
}
render() {
return (
<DemoWorkspaceWidget buttons={<DemoButton onClick={this.autoDistribute}>Re-distribute</DemoButton>}>
<DemoCanvasWidget>
<CanvasWidget engine={this.props.engine} />
</DemoCanvasWidget>
</DemoWorkspaceWidget>
);
}
}
export default () => {
//1) setup the diagram engine
let engine = createEngine();
//2) setup the diagram model
let model = new DiagramModel();
//3) create a default nodes
let nodesFrom: NodeModel[] = [];
let nodesTo: NodeModel[] = [];
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], engine);
});
// more links for more complicated diagram
links.push(connectNodes(nodesFrom[0], nodesTo[1], engine));
links.push(connectNodes(nodesTo[0], nodesFrom[1], engine));
links.push(connectNodes(nodesFrom[1], nodesTo[2], engine));
// initial random position
nodesFrom.forEach((node, index) => {
node.setPosition(index * 70, index * 70);
model.addNode(node);
});
nodesTo.forEach((node, index) => {
node.setPosition(index * 70, 100);
model.addNode(node);
});
links.forEach(link => {
model.addLink(link);
});
engine.setModel(model);
return <DemoWidget model={model} engine={engine} />;
};

View File

@@ -1,4 +1,4 @@
import * as SRD from "storm-react-diagrams";
import * as SRD from '@projectstorm/react-diagrams';
/**
* @author Dylan Vorster
@@ -8,24 +8,22 @@ export class Application {
protected diagramEngine: SRD.DiagramEngine;
constructor() {
this.diagramEngine = new SRD.DiagramEngine();
this.diagramEngine.installDefaultFactories();
this.diagramEngine = SRD.default();
this.newModel();
}
public newModel() {
this.activeModel = new SRD.DiagramModel();
this.diagramEngine.setDiagramModel(this.activeModel);
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");
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");
var node2 = new SRD.DefaultNodeModel('Node 2', 'rgb(192,255,0)');
let port2 = node2.addInPort('In');
node2.setPosition(400, 100);
// link the ports

View File

@@ -0,0 +1,94 @@
import * as React from 'react';
import * as _ from 'lodash';
import { TrayWidget } from './TrayWidget';
import { Application } from '../Application';
import { TrayItemWidget } from './TrayItemWidget';
import { DefaultNodeModel } from '@projectstorm/react-diagrams';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../../helpers/DemoCanvasWidget';
import styled from '@emotion/styled';
export interface BodyWidgetProps {
app: Application;
}
namespace S {
export const Body = styled.div`
flex-grow: 1;
display: flex;
flex-direction: column;
min-height: 100%;
`;
export const Header = styled.div`
display: flex;
background: rgb(30, 30, 30);
flex-grow: 0;
flex-shrink: 0;
color: white;
font-family: Helvetica, Arial, sans-serif;
padding: 10px;
align-items: center;
`;
export const Content = styled.div`
display: flex;
flex-grow: 1;
`;
export const Layer = styled.div`
position: relative;
flex-grow: 1;
`;
}
export class BodyWidget extends React.Component<BodyWidgetProps> {
render() {
return (
<S.Body>
<S.Header>
<div className="title">Storm React Diagrams - DnD demo</div>
</S.Header>
<S.Content>
<TrayWidget>
<TrayItemWidget model={{ type: 'in' }} name="In Node" color="rgb(192,255,0)" />
<TrayItemWidget model={{ type: 'out' }} name="Out Node" color="rgb(0,192,255)" />
</TrayWidget>
<S.Layer
onDrop={event => {
var data = JSON.parse(event.dataTransfer.getData('storm-diagram-node'));
var nodesCount = _.keys(
this.props.app
.getDiagramEngine()
.getModel()
.getNodes()
).length;
var node: DefaultNodeModel = null;
if (data.type === 'in') {
node = new DefaultNodeModel('Node ' + (nodesCount + 1), 'rgb(192,255,0)');
node.addInPort('In');
} else {
node = new DefaultNodeModel('Node ' + (nodesCount + 1), 'rgb(0,192,255)');
node.addOutPort('Out');
}
var point = this.props.app.getDiagramEngine().getRelativeMousePoint(event);
node.setPosition(point);
this.props.app
.getDiagramEngine()
.getModel()
.addNode(node);
this.forceUpdate();
}}
onDragOver={event => {
event.preventDefault();
}}>
<DemoCanvasWidget>
<CanvasWidget engine={this.props.app.getDiagramEngine()} />
</DemoCanvasWidget>
</S.Layer>
</S.Content>
</S.Body>
);
}
}

View File

@@ -0,0 +1,37 @@
import * as React from 'react';
import styled from '@emotion/styled';
export interface TrayItemWidgetProps {
model: any;
color?: string;
name: string;
}
namespace S {
export const Tray = styled.div<{ color: string }>`
color: white;
font-family: Helvetica, Arial;
padding: 5px;
margin: 0px 10px;
border: solid 1px ${p => p.color};
border-radius: 5px;
margin-bottom: 2px;
cursor: pointer;
`;
}
export class TrayItemWidget extends React.Component<TrayItemWidgetProps> {
render() {
return (
<S.Tray
color={this.props.color}
draggable={true}
onDragStart={event => {
event.dataTransfer.setData('storm-diagram-node', JSON.stringify(this.props.model));
}}
className="tray-item">
{this.props.name}
</S.Tray>
);
}
}

View File

@@ -0,0 +1,17 @@
import * as React from 'react';
import styled from '@emotion/styled';
namespace S {
export const Tray = styled.div`
min-width: 200px;
background: rgb(20, 20, 20);
flex-grow: 0;
flex-shrink: 0;
`;
}
export class TrayWidget extends React.Component {
render() {
return <S.Tray>{this.props.children}</S.Tray>;
}
}

View File

@@ -0,0 +1,9 @@
import * as React from 'react';
import { BodyWidget } from './components/BodyWidget';
import { Application } from './Application';
export default () => {
var app = new Application();
return <BodyWidget app={app} />;
};

View File

@@ -0,0 +1,58 @@
import createEngine, { DiagramModel, DefaultNodeModel, DiagramEngine } from '@projectstorm/react-diagrams';
import * as _ from 'lodash';
import * as React from 'react';
import { DemoButton, DemoWorkspaceWidget } from '../helpers/DemoWorkspaceWidget';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
class CloneSelected extends React.Component<{ model: DiagramModel; engine: DiagramEngine }, any> {
addPorts = () => {
const nodes: DefaultNodeModel[] = _.values(this.props.model.getNodes()) as DefaultNodeModel[];
for (let node of nodes) {
if (node.getOptions().name === 'Node 2') {
node.addInPort(`in-${node.getInPorts().length + 1}`, false);
} else {
node.addOutPort(`out-${node.getOutPorts().length + 1}`, false);
}
}
this.props.engine.repaintCanvas();
};
render() {
const { engine } = this.props;
return (
<DemoWorkspaceWidget buttons={<DemoButton onClick={this.addPorts}>Add more ports</DemoButton>}>
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
</DemoWorkspaceWidget>
);
}
}
export default () => {
//1) setup the diagram engine
var engine = createEngine();
//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)');
node1.setPosition(100, 100);
//3-B) create another default node
var node2 = new DefaultNodeModel('Node 2', 'rgb(192,255,0)');
node2.setPosition(400, 100);
// link the ports
//4) add the models to the root graph
model.addAll(node1, node2);
//5) load model into engine
engine.setModel(model);
//6) render the diagram!
return <CloneSelected engine={engine} model={model} />;
};

View File

@@ -0,0 +1,42 @@
import createEngine, { DiagramModel, DefaultNodeModel } from '@projectstorm/react-diagrams';
import * as React from 'react';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
/**
* Tests the grid size
*/
export default () => {
//1) setup the diagram engine
var engine = createEngine();
//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 (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};

View File

@@ -0,0 +1,65 @@
import createEngine, { DiagramModel, DefaultNodeModel, DefaultLinkModel } from '@projectstorm/react-diagrams';
import * as React from 'react';
import { DemoButton, DemoWorkspaceWidget } from '../helpers/DemoWorkspaceWidget';
import { action } from '@storybook/addon-actions';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
export default () => {
// setup the diagram engine
const engine = createEngine();
// 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 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={
<DemoButton
onClick={() => {
action('Serialized Graph')(JSON.stringify(model.serializeDiagram(), null, 2));
}}>
Serialize Graph
</DemoButton>
}>
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
</DemoWorkspaceWidget>
);
};

View File

@@ -0,0 +1,50 @@
import * as React from 'react';
import { action } from '@storybook/addon-actions';
import createEngine, { DiagramModel, DefaultNodeModel } from '@projectstorm/react-diagrams';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
/**
* Shows some of the events triggered when elements are selected
*/
export default () => {
// setup the diagram engine
var engine = createEngine();
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.registerListener({
eventDidFire: action('eventDidFired')
});
});
engine.setModel(model);
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};

View File

@@ -0,0 +1,60 @@
import * as React from 'react';
import createEngine, { DiagramModel, DefaultNodeModel } from '@projectstorm/react-diagrams';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
/**
*
* 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 = createEngine();
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);
//!========================================= <<<<<<<
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};

View File

@@ -0,0 +1,89 @@
import createEngine, { DiagramModel, DefaultNodeModel, NodeModel } from '@projectstorm/react-diagrams';
import * as React from 'react';
import * as _ from 'lodash';
import { DemoButton, DemoWorkspaceWidget } from '../helpers/DemoWorkspaceWidget';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
/**
* 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.getModel();
const nodes = model.getNodes();
let node = nodes[Object.keys(nodes)[0]];
node.setPosition(node.getX() + 30, node.getY() + 30);
engine.repaintCanvas();
}
updatePositionViaSerialize() {
let { engine } = this.props;
let model = engine.getModel();
let str = JSON.stringify(model.serialize());
let model2 = new DiagramModel();
let obj: ReturnType<DiagramModel['serialize']> = JSON.parse(str);
let node: ReturnType<NodeModel['serialize']> = _.values(obj.layers[1].models)[0] as any;
node.x += 30;
node.y += 30;
model2.deserializeModel(obj, engine);
engine.setModel(model2);
}
render() {
const { engine } = this.props;
return (
<DemoWorkspaceWidget
buttons={[
<DemoButton key={1} onClick={this.updatePosition}>
Update position
</DemoButton>,
<DemoButton key={2} onClick={this.updatePositionViaSerialize}>
Update position via serialize
</DemoButton>
]}>
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
</DemoWorkspaceWidget>
);
}
}
export default () => {
//1) setup the diagram engine
var engine = createEngine();
//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

@@ -1,5 +1,7 @@
import { DiagramEngine, DiagramModel, DefaultNodeModel, LinkModel, DiagramWidget } from "storm-react-diagrams";
import * as React from "react";
import createEngine, { DiagramModel, DefaultNodeModel } from '@projectstorm/react-diagrams';
import * as React from 'react';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
/**
*
@@ -10,8 +12,7 @@ import * as React from "react";
*/
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.installDefaultFactories();
var engine = createEngine();
//2) setup the diagram model
var model = new DiagramModel();
@@ -23,21 +24,25 @@ export default () => {
}
//5) load model into engine
engine.setDiagramModel(model);
engine.setModel(model);
//6) render the diagram!
return <DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />;
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};
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");
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");
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

View File

@@ -0,0 +1,60 @@
import createEngine, { DiagramModel, DefaultNodeModel } from '@projectstorm/react-diagrams';
import * as React from 'react';
import { DemoButton, DemoWorkspaceWidget } from '../helpers/DemoWorkspaceWidget';
import { action } from '@storybook/addon-actions';
import * as beautify from 'json-beautify';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
export default () => {
//1) setup the diagram engine
var engine = createEngine();
//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.deserializeModel(JSON.parse(str), engine);
engine.setModel(model2);
return (
<DemoWorkspaceWidget
buttons={
<DemoButton
onClick={() => {
action('Serialized Graph')(beautify(model2.serialize(), null, 2, 80));
}}>
Serialize Graph
</DemoButton>
}>
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
</DemoWorkspaceWidget>
);
};

View File

@@ -0,0 +1,50 @@
import createEngine, { DiagramModel, DefaultNodeModel, DefaultDiagramState } from '@projectstorm/react-diagrams';
import * as React from 'react';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
export default () => {
//1) setup the diagram engine
var engine = createEngine();
// ############################################ MAGIC HAPPENS HERE
const state = engine.getStateMachine().getCurrentState();
if (state instanceof DefaultDiagramState) {
state.dragNewLink.config.allowLooseLinks = false;
}
// ############################################ MAGIC HAPPENS HERE
//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 (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};

View File

@@ -0,0 +1,40 @@
import createEngine, { DiagramModel, DefaultNodeModel, DefaultLinkModel } from '@projectstorm/react-diagrams';
import * as React from 'react';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
export default () => {
//1) setup the diagram engine
var engine = createEngine();
//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<DefaultLinkModel>(port2);
link1.getOptions().testName = 'Test';
link1.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 (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};

View File

@@ -0,0 +1,72 @@
import createEngine, {
DiagramModel,
DefaultNodeModel,
DefaultPortModel,
PathFindingLinkFactory,
DefaultLabelModel
} from '@projectstorm/react-diagrams';
import * as React from 'react';
import { DemoButton, DemoWorkspaceWidget } from '../helpers/DemoWorkspaceWidget';
import { action } from '@storybook/addon-actions';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
export default () => {
// setup the diagram engine
const engine = createEngine();
// 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);
const pathfinding = engine.getLinkFactories().getFactory<PathFindingLinkFactory>(PathFindingLinkFactory.NAME);
// linking things together (specifically using the pathfinding link)
const link1 = port1.link(port4, pathfinding);
const link2 = port2.link(port3, pathfinding);
link1.addLabel(
new DefaultLabelModel({
label: 'I am a label!',
offsetY: 20
})
);
// 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={
<DemoButton
onClick={() => {
action('Serialized Graph')(JSON.stringify(model.serialize(), null, 2));
}}>
Serialize Graph
</DemoButton>
}>
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
</DemoWorkspaceWidget>
);
};

View File

@@ -1,13 +1,8 @@
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
DefaultPortModel,
DiagramWidget
} from "storm-react-diagrams";
import * as React from "react";
import { DemoWorkspaceWidget } from "../.helpers/DemoWorkspaceWidget";
import createEngine, { DiagramModel, DefaultNodeModel } from '@projectstorm/react-diagrams';
import * as React from 'react';
import { DemoWorkspaceWidget, DemoButton } from '../helpers/DemoWorkspaceWidget';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
/**
*
@@ -17,8 +12,7 @@ import { DemoWorkspaceWidget } from "../.helpers/DemoWorkspaceWidget";
*/
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.installDefaultFactories();
var engine = createEngine();
//2) setup the diagram model
var model = new DiagramModel();
@@ -30,25 +24,27 @@ export default () => {
}
//5) load model into engine
engine.setDiagramModel(model);
engine.setModel(model);
//6) render the diagram!
return (
<DemoWorkspaceWidget buttons={<button onClick={() => engine.zoomToFit()}>Zoom to fit</button>}>
<DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />
<DemoWorkspaceWidget buttons={<DemoButton onClick={() => engine.zoomToFit()}>Zoom to fit</DemoButton>}>
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
</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");
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");
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

View File

@@ -0,0 +1,58 @@
import * as React from 'react';
import styled from '@emotion/styled';
export interface DemoCanvasWidgetProps {
color?: string;
background?: string;
}
namespace S {
export const Container = styled.div<{ color: string; background: string }>`
height: 100%;
background-color: rgb(60, 60, 60) !important;
background-size: 50px 50px;
display: flex;
> * {
height: 100%;
min-height: 100%;
width: 100%;
}
background-image: linear-gradient(
0deg,
transparent 24%,
${p => p.color} 25%,
${p => p.color} 26%,
transparent 27%,
transparent 74%,
${p => p.color} 75%,
${p => p.color} 76%,
transparent 77%,
transparent
),
linear-gradient(
90deg,
transparent 24%,
${p => p.color} 25%,
${p => p.color} 26%,
transparent 27%,
transparent 74%,
${p => p.color} 75%,
${p => p.color} 76%,
transparent 77%,
transparent
);
`;
}
export class DemoCanvasWidget extends React.Component<DemoCanvasWidgetProps> {
render() {
return (
<S.Container
background={this.props.background || 'rgb(60, 60, 60)'}
color={this.props.color || 'rgba(255,255,255, 0.05)'}>
{this.props.children}
</S.Container>
);
}
}

View File

@@ -0,0 +1,55 @@
import * as React from 'react';
import styled from '@emotion/styled';
export interface DemoWorkspaceWidgetProps {
buttons?: any;
}
namespace S {
export const Toolbar = styled.div`
padding: 5px;
display: flex;
flex-shrink: 0;
`;
export const Content = styled.div`
flex-grow: 1;
height: 100%;
`;
export const Container = styled.div`
background: black;
display: flex;
flex-direction: column;
height: 100%;
border-radius: 5px;
overflow: hidden;
`;
}
export const DemoButton = styled.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);
}
`;
export class DemoWorkspaceWidget extends React.Component<DemoWorkspaceWidgetProps> {
render() {
return (
<S.Container>
<S.Toolbar>{this.props.buttons}</S.Toolbar>
<S.Content>{this.props.children}</S.Content>
</S.Container>
);
}
}

View File

@@ -0,0 +1,23 @@
import * as React from 'react';
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);
});
}
}

View File

@@ -0,0 +1,7 @@
html,
body,
#root {
height: 100%;
padding: 0;
margin: 0;
}

View File

@@ -0,0 +1,71 @@
import * as React from 'react';
import { storiesOf, addParameters, addDecorator } from '@storybook/react';
import { setOptions } from '@storybook/addon-options';
import { themes } from '@storybook/theming';
import './demos/helpers/index.css';
import { Toolkit } from '@projectstorm/react-canvas-core';
Toolkit.TESTING = true;
addParameters({
options: {
theme: themes.dark
}
});
setOptions({
name: 'STORM React Diagrams',
url: 'https://github.com/projectstorm/react-diagrams',
addonPanelInRight: true
});
addDecorator(fn => {
Toolkit.TESTING_UID = 0;
return fn();
});
import demo_simple from './demos/demo-simple';
import demo_flow from './demos/demo-simple-flow';
import demo_performance from './demos/demo-performance';
import demo_locks from './demos/demo-locks';
import demo_grid from './demos/demo-grid';
import demo_listeners from './demos/demo-listeners';
import demo_zoom from './demos/demo-zoom-to-fit';
import demo_labels from './demos/demo-labelled-links';
import demo_dynamic_ports from './demos/demo-dynamic-ports';
storiesOf('Simple Usage', module)
.add('Simple example', demo_simple)
.add('Simple flow example', demo_flow)
.add('Performance demo', demo_performance)
.add('Locked widget', demo_locks)
.add('Canvas grid size', demo_grid)
.add('Events and listeners', demo_listeners)
.add('Zoom to fit', demo_zoom)
.add('Dynamic ports', demo_dynamic_ports)
.add('Links with labels', demo_labels);
import demo_adv_clone_selected from './demos/demo-cloning';
import demo_adv_ser_des from './demos/demo-serializing';
import demo_adv_prog from './demos/demo-mutate-graph';
import demo_adv_dnd from './demos/demo-drag-and-drop';
import demo_smart_routing from './demos/demo-smart-routing';
storiesOf('Advanced Techniques', module)
.add('Clone Selected', demo_adv_clone_selected)
.add('Serializing and de-serializing', demo_adv_ser_des)
.add('Programatically modifying graph', demo_adv_prog)
.add('Drag and drop', demo_adv_dnd)
.add('Smart routing', demo_smart_routing);
import demo_cust_nodes from './demos/demo-custom-node1';
import demo_cust_links from './demos/demo-custom-link1';
storiesOf('Custom Models', module)
.add('Custom diamond node', demo_cust_nodes)
.add('Custom animated links', demo_cust_links);
import demo_3rd_dagre from './demos/demo-dagre';
import { Toolkit } from '@projectstorm/react-canvas-core';
storiesOf('3rd party libraries', module).add('Auto Distribute (Dagre)', demo_3rd_dagre);

View File

@@ -0,0 +1,7 @@
module.exports = {
launch: {
dumpio: true,
headless: process.env.CI === 'true'
},
browserContext: 'default'
};

View File

@@ -0,0 +1,9 @@
const path = require('path');
module.exports = {
preset: 'jest-puppeteer',
transform: {
'^.+\\.tsx?$': 'ts-jest'
},
roots: [path.join(__dirname, 'tests-e2e')],
testMatch: ['**/*.test.{ts,tsx}']
};

View File

@@ -0,0 +1,29 @@
{
"name": "@projectstorm/react-diagrams-gallery",
"version": "6.0.0-beta.0",
"author": "dylanvorster",
"license": "MIT",
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/projectstorm/react-diagrams.git"
},
"scripts": {
"start": "../../node_modules/.bin/start-storybook",
"storybook:build": "../../node_modules/.bin/build-storybook -c .storybook -o .out",
"github": "../../node_modules/.bin/storybook-to-ghpages",
"test:run": "../../node_modules/.bin/jest --no-cache",
"test": "yarn storybook:build && yarn test:run"
},
"keywords": [
"web",
"diagram",
"diagrams",
"react",
"typescript",
"flowchart",
"simple",
"links",
"nodes"
]
}

View File

@@ -0,0 +1,28 @@
import { ElementHandle, FrameBase } from 'puppeteer';
export abstract class E2EBase {
name: string;
constructor(name: string) {
this.name = name;
}
async getSelector(): Promise<FrameBase> {
return page as FrameBase;
}
async getElement(): Promise<ElementHandle> {
if (!(await this.getSelector())) {
return null;
}
return (await this.getSelector()).$(this.selector());
}
async waitForElement(): Promise<ElementHandle | null> {
return (await this.getSelector()).waitForSelector(this.selector(), {
timeout: 1000
});
}
protected abstract selector(): string;
}

View File

@@ -0,0 +1,27 @@
import { E2EBase } from './E2EBase';
export class E2ELink extends E2EBase {
isID: boolean;
async select(): Promise<any> {
const point = await page.evaluate(id => {
const path = document.querySelector(id) as SVGPathElement;
const point = path.getPointAtLength(path.getTotalLength() / 2);
return {
x: point.x,
y: point.y
};
}, this.selector());
await page.keyboard.down('Shift');
await page.mouse.move(point.x, point.y);
await page.mouse.down();
await page.keyboard.up('Shift');
}
protected selector(): string {
if (this.isID) {
return `[data-linkid="${this.name}"] path`;
}
return `[data-default-link-test="${this.name}"] path`;
}
}

View File

@@ -0,0 +1,12 @@
import { E2EBase } from './E2EBase';
import { E2EPort } from './E2EPort';
export class E2ENode extends E2EBase {
async port(id: string): Promise<E2EPort> {
return new E2EPort(id, this);
}
selector(): string {
return `[data-default-node-name="${this.name}"]`;
}
}

View File

@@ -0,0 +1,83 @@
import * as _ from 'lodash';
import { FrameBase } from 'puppeteer';
import { E2EBase } from './E2EBase';
import { E2ENode } from './E2ENode';
import { E2ELink } from './E2ELink';
export class E2EPort extends E2EBase {
parent: E2ENode;
constructor(name: string, parent: E2ENode) {
super(name);
this.parent = parent;
}
async getLinks(): Promise<E2ELink[]> {
const attribute = await page.evaluate(id => {
return document.querySelector(id).getAttribute('data-links');
}, this.selector());
if (attribute.trim() === '') {
return [];
}
return _.map(attribute.split(','), id => {
const e = new E2ELink(id);
e.isID = true;
return e;
});
}
async link(port: E2EPort): Promise<E2ELink> {
let currentLinks = _.map(await this.getLinks(), 'name');
let bounds = await (await this.getElement()).boundingBox();
// click on this port
page.mouse.move(bounds.x, bounds.y);
page.mouse.down();
//
let bounds2 = await (await port.getElement()).boundingBox();
// drag to other port
page.mouse.move(bounds2.x, bounds2.y);
page.mouse.up();
let newLinks = _.map(await this.getLinks(), 'name');
const s = new E2ELink(_.difference(newLinks, currentLinks)[0]);
s.isID = true;
return s;
}
async linkToPoint(x: number, y: number): Promise<E2ELink> {
let currentLinks = _.map(await this.getLinks(), 'id');
let bounds = await (await this.getElement()).boundingBox();
// click on this port
page.mouse.move(bounds.x, bounds.y);
page.mouse.down();
// drag to point
page.mouse.move(x, y);
page.mouse.up();
let newLinks = _.map(await this.getLinks(), 'id');
const link = _.difference(newLinks, currentLinks)[0];
if (!link) {
return null;
}
// get the parent to get the link
return new E2ELink(link);
}
async getSelector(): Promise<FrameBase> {
return (await this.parent.getElement()) as any;
}
protected selector(): string {
return `${this.parent.selector()} .port[data-name="${this.name}"]`;
}
}

View File

@@ -0,0 +1,33 @@
import 'jest';
import { E2ENode } from './helpers/E2ENode';
describe('simple flow test', () => {
beforeEach(async () => {
await page.goto(`file://${__dirname}/../.out/iframe.html?path=/story/simple-usage--simple-flow-example`);
});
it('drag link to port adds a link', async () => {
// create a new link
let node1 = new E2ENode('Node 3');
let node2 = new E2ENode('Node 2');
let port1 = await node1.port('Out');
let port2 = await node2.port('In');
let newlink = await port1.link(port2);
await expect(await newlink.waitForElement()).toBeTruthy();
});
it('drag link to node does not add a link', async () => {
// create a new link
let node1 = new E2ENode('Node 3');
let node2 = new E2ENode('Node 2');
let port1 = await node1.port('Out');
let node2Bounds = await (await node2.waitForElement()).boundingBox();
let newlink = await port1.linkToPoint(node2Bounds.x, node2Bounds.y);
await expect(newlink).toBeFalsy();
});
});

View File

@@ -0,0 +1,31 @@
import 'jest';
import { E2ELink } from './helpers/E2ELink';
import { E2ENode } from './helpers/E2ENode';
describe('simple test', () => {
beforeAll(async () => {
await page.goto(`file://${__dirname}/../.out/iframe.html?path=/story/simple-usage--simple-example`);
});
it('should delete a link and create a new one', async () => {
// get the existing link
let link = new E2ELink('Test');
await expect(await link.waitForElement()).toBeTruthy();
// remove it
await link.select();
await page.keyboard.press('Delete');
await expect(await link.getElement()).toBeFalsy();
// create a new link
let node1 = new E2ENode('Node 1');
let node2 = new E2ENode('Node 2');
let port1 = await node1.port('Out');
let port2 = await node2.port('In');
let newlink = await port1.link(port2);
await expect(await newlink.waitForElement()).toBeTruthy();
});
});

View File

@@ -0,0 +1,6 @@
{
"extends": "../../tsconfig",
"include": [
"./demos"
]
}

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