Compare commits

..

1 Commits

Author SHA1 Message Date
Dylan Vorster
174ec45f8f initial work on group nodes support 2018-03-21 16:40:25 +02:00
359 changed files with 34582 additions and 23606 deletions

View File

@@ -1,8 +0,0 @@
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)

View File

@@ -1,11 +0,0 @@
{
"$schema": "https://unpkg.com/@changesets/config@2.2.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"baseBranch": "master",
"updateInternalDependencies": "patch",
"ignore": []
}

31
.circleci/config.yml Normal file
View File

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

View File

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

View File

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

View File

@@ -1,4 +0,0 @@
root: ./docs/
structure:
summary: README.md

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 KiB

View File

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

View File

@@ -1,23 +0,0 @@
name: Prettier check
# This action works with pull requests and pushes
on:
pull_request:
jobs:
prettier:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
# Make sure the actual branch is checked out when running on pull requests
ref: ${{ github.head_ref }}
- uses: actions/checkout@v2 # Check out the repository first.
- uses: actionsx/prettier@v2
with:
# prettier CLI arguments.
args: --check --ignore-path .prettierignore --config .prettierrc '**/*.{ts,tsx,js,jsx}'

View File

@@ -1,47 +0,0 @@
name: Release
on:
push:
branches:
- master
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v2
- name: Read .nvmrc
run: echo "##[set-output name=NVMRC;]$(cat .nvmrc)"
id: nvm
- name: Use Node.js (.nvmrc)
uses: actions/setup-node@v2
with:
node-version: "${{ steps.nvm.outputs.NVMRC }}"
- name: Install PNPM
uses: pnpm/action-setup@v2
with:
version: latest
- name: Install Dependencies
run: pnpm install
- name: Create Release Pull Request
uses: changesets/action@v1
id: changesets
with:
# This expects you to have a script called release which does a build for your packages and calls changeset publish
publish: pnpm release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: publish storybook
if: steps.changesets.outputs.published == 'true'
run: pnpm release:storybook

View File

@@ -1,30 +0,0 @@
name: Build and Test
on:
pull_request:
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v2
- name: Read .nvmrc
run: echo "##[set-output name=NVMRC;]$(cat .nvmrc)"
id: nvm
- name: Use Node.js (.nvmrc)
uses: actions/setup-node@v2
with:
node-version: "${{ steps.nvm.outputs.NVMRC }}"
- name: Install PNPM
uses: pnpm/action-setup@v2
with:
version: latest
- name: Install Dependencies
run: pnpm install
- name: Build
run: pnpm build

198
.gitignore vendored
View File

@@ -1,9 +1,193 @@
dist
.DS_Store
.idea
dist/
dist/main.js
dist/main.js.map
/package
*.tgz
.out
*.zip
.env
# 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
tsconfig.tsbuildinfo
.vscode
jspm_packages
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
.idea

194
.npmignore Normal file
View File

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

View File

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

View File

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

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

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

View File

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

3
.storybook/addons.js Normal file
View File

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

8
.storybook/config.js Normal file
View File

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

View File

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

View File

@@ -1,224 +1,76 @@
__V7!__
__5.1.0__
we are now using changesets! you can see the changes for individual packages in their corresponding folders.
Here is the main changeset for the core package which depends on everything:
* [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
[Changelog for @projectstorm/react-diagrams](./packages/react-diagrams/CHANGELOG.md)
---
__6.7.4__
.0 -> .4 because I messed up the version / publishing
* (upgrade all dependencies, including a move to React 18)
* https://github.com/projectstorm/react-diagrams/pull/947
__6.7.0__
bug fixes:
* https://github.com/projectstorm/react-diagrams/pull/882
* https://github.com/projectstorm/react-diagrams/pull/914
* https://github.com/projectstorm/react-diagrams/pull/875
types
* https://github.com/projectstorm/react-diagrams/pull/906
features:
* https://github.com/projectstorm/react-diagrams/pull/915
* https://github.com/projectstorm/react-diagrams/pull/877
__6.6.1__
bug fixes:
* https://github.com/projectstorm/react-diagrams/pull/861
* https://github.com/projectstorm/react-diagrams/pull/871
* https://github.com/projectstorm/react-diagrams/pull/870
Some maintenance:
* https://github.com/projectstorm/react-diagrams/pull/861
__6.6.0__
* (docs-broken) https://github.com/projectstorm/react-diagrams/pull/834
* (bug) https://github.com/projectstorm/react-diagrams/pull/838
* (docs-broken) https://github.com/projectstorm/react-diagrams/pull/847
* (bug) https://github.com/projectstorm/react-diagrams/pull/852
* (docs-broken) https://github.com/projectstorm/react-diagrams/pull/856
* (improvement) https://github.com/projectstorm/react-diagrams/pull/857
* (bug) https://github.com/projectstorm/react-diagrams/pull/860
Also includes a bump on all packages using `ncu` recursively.
__6.5.2__
https://github.com/projectstorm/react-diagrams/pull/830
* (fix) issue with zoom to fit selected
* (improvement) properly export PathFinding
* (maintenance) bump all dependencies
__6.5.1__
https://github.com/projectstorm/react-diagrams/pull/829
* (improved) zoom to fit now centers correctly
* (fix) remove wrong peer dependency (@emotion/core)
__6.5.0__
https://github.com/projectstorm/react-diagrams/pull/814
* Some rendering fixes
* small api change around `zoomToFit`
* more api options with the `DefaultLink`
__6.4.0__
https://github.com/projectstorm/react-diagrams/pull/813
* Bump all packages and move to Emotion 11 and React 17
* Move to the latest Storybook
__6.2.0__
* (improvement) Move away fromn math-js (https://github.com/projectstorm/react-diagrams/pull/651)
* (fix) https://github.com/projectstorm/react-diagrams/pull/639
* (fix) Fixing link spawning at (0,0) when clicking port once (inspired by https://github.com/projectstorm/react-diagrams/pull/637)
__6.1.1__
* (feature) https://github.com/projectstorm/react-diagrams/pull/576 [Add zoom to fit nodes feature, fixes #568]
* (improvement) https://github.com/projectstorm/react-diagrams/pull/621 [Support deriving from DefaultLabelModel]
* (fix) https://github.com/projectstorm/react-diagrams/pull/603
[Fixes selectionChanged listener not being deregistered on NodeWidget, Fixes unchecked access to this.props.link.getSourcePort() on LinkWidget]
* (maintenance) bump everything
* fix serialize/deserialize issue with example project raw JS node
__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
-> 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
__5.0.0__ [http://dylanv.blog/2018/03/03/storm-react-diagrams-5-0-0/](https://dylanvorster.com/storm-react-diagrams-v5-0/)
__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/](https://dylanvorster.com/storm-react-diagrams-v4-0/)
__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/](https://dylanvorster.com/storm-react-diagrams-3-2-0/)
* (feature) zoom to fit
__3.2.0__ http://dylanv.blog/2017/11/22/storm-react-diagrams-3-2-0/
* [feature] zoom to fit
* added Circle CI tests
* (demo) dagre automatic layouts
* (demo) zoom to fit
* (demo) selection events
* (demo) limit number of points
* (demo) programmatic node updating
* [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__
__3.1.3__
* Refactor links slightly
* use min extension for css
* bump package versions
* export more classes
__3.1.2__
__3.1.2__
* Hotfix: fix zooming when canvas not in the top left corner
(https://github.com/projectstorm/react-diagrams/pull/88)
__3.1.0__ [http://dylanv.blog/2017/09/15/storm-react-diagrams-3-1-0/](https://dylanvorster.com/storm-react-diagrams-3-1-0/)
__3.1.0__ http://dylanv.blog/2017/09/15/storm-react-diagrams-3-1-0/
* Zoom relative to mouse location
* Fixed links not connecting when grid is larger than port size
* Prevented points from dragging when connected to a port and the node itself is not selected
* API fixes
* Code cleanup
* Code cleanup
__3.0.0__ [http://dylanv.blog/2017/09/13/storm-react-diagrams-v3/](https://dylanvorster.com/storm-react-diagrams-3-0/)
__3.0.0__ http://dylanv.blog/2017/09/13/storm-react-diagrams-v3/
* Massive performance updates
* Complete rewrite
* Started a changelog and design documents for each revision

100
README.md
View File

@@ -1,96 +1,42 @@
# Introduction
# STORM React Diagrams
[![Join the chat at https://gitter.im/projectstorm/react-diagrams](https://badges.gitter.im/projectstorm/react-diagrams.svg)](https://gitter.im/projectstorm/react-diagrams?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![NPM](https://img.shields.io/npm/v/@projectstorm/react-diagrams.svg)](https://npmjs.org/package/@projectstorm/react-diagrams) [![Package Quality](https://npm.packagequality.com/shield/storm-react-diagrams.svg)](https://packagequality.com/#?package=storm-react-diagrams)
**DEMO**: [http://www.projectstorm.io/react-diagrams](http://www.projectstorm.io/react-diagrams)
![](.gitbook/assets/logo.jpg)
**DOCS:** [https://projectstorm.gitbooks.io/react-diagrams](https://projectstorm.gitbooks.io/react-diagrams)
**DEMO**: [http://projectstorm.cloud/react-diagrams](http://projectstorm.cloud/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/)
**DOCS \(wip\)** [https://projectstorm.gitbook.io/react-diagrams](https://projectstorm.gitbook.io/react-diagrams)
A super simple, no-nonsense diagramming library written in React that just works.
Docs are currently being worked on, along with a migration path.
[![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) ![](https://www.gitbook.com/button/status/book/projectstorm/react-diagrams) [![CircleCI](https://circleci.com/gh/projectstorm/react-diagrams/tree/master.svg?style=svg)](https://circleci.com/gh/projectstorm/react-diagrams/tree/master)
## What
![Personal Project](./images/example1.jpg)
A flow & process orientated diagramming library inspired by **Blender**, **Labview** and **Unreal engine**.
![](./images/example2.jpg)
* **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 highest priority to editing diagrams as fast as possible.
![](./images/example3.jpg)
## Gallery
## Introduction
Example implementation using custom models: \(Dylan's personal code\)
A no-nonsense diagramming library written entirely in React with the help of a few small libraries. It aims to be:
![Personal Project](.gitbook/assets/example1.jpg)
![](.gitbook/assets/example2.jpg)
* 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
Get started with the default models right out of the box:
#### Run the demos
![](.gitbook/assets/example3.jpg)
After running `yarn install` you must then run: `yarn run storybook`
## Installing
#### Building from source
For all the bells and whistles:
Simply run `webpack` in the root directory \(or `export NODE_ENV=production && webpack` if you want a production build\) and it will spit out the transpiled code and typescript definitions into the dist directory as a single file.
We use webpack for this because TSC cannot compile a single UMD file \(TSC can currently only output multiple UMD files\).
```text
yarn add @projectstorm/react-diagrams
```
This includes all the packages listed below \(and works \(mostly and conceptually\) like it used to in version 5.0\)
### A more modular approach
This library now has a more modular design and you can import just the core \(contains no default factories or routing\)
```text
yarn add @projectstorm/react-diagrams-core
```
this is built ontop of the evolving **react-canvas-core** library
```text
yarn add @projectstorm/react-canvas-core
```
which makes use of
```text
yarn add @projectstorm/geometry
```
and of course, you can add some extras:
```text
yarn add @projectstorm/react-diagrams-defaults
yarn add @projectstorm/react-diagrams-routing
```
## How to use
Before running any of the examples, please run `pnpm build` in the root. This project is a monorepo, and the packages (including the demos) require the packages to first be built.
## [Checkout the docs](https://projectstorm.gitbooks.io/react-diagrams)
Take a look at the [diagram demos](https://github.com/projectstorm/react-diagrams/tree/master/diagrams-demo-gallery/demos)
**or**
Take a look at the [demo project](https://github.com/projectstorm/react-diagrams/tree/master/diagrams-demo-project) which contains an example for ES6 as well as Typescript
**or**
[Checkout the docs](https://projectstorm.gitbook.io/react-diagrams/)
## Run the demos
After running `pnpm install` and `pnpm build`, you must then run: `cd diagrams-demo-gallery && pnpm run start`
## Building from source
Simply run `pnpm` then `pnpm build` or `pnpm build:prod` in the root directory and it will spit out the transpiled code and typescript definitions into the dist directory as a single file.
## Built with react-diagrams
> Do you have an interesting project built with *react-diagrams*? PR it into this section for others to see.

11
SUMMARY.md Normal file
View File

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

View File

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

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

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

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

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

View File

@@ -0,0 +1,88 @@
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
NodeModel,
DiagramWidget,
BaseModel
} from "storm-react-diagrams";
import * as _ from "lodash";
import * as React from "react";
import { DemoWorkspaceWidget } from "../.helpers/DemoWorkspaceWidget";
/**
* Tests cloning
*/
class CloneSelected extends React.Component<any, any> {
constructor(props: any) {
super(props);
this.cloneSelected = this.cloneSelected.bind(this);
}
cloneSelected() {
let { engine } = this.props;
let offset = { x: 100, y: 100 };
let model = engine.getDiagramModel();
let itemMap = {};
_.forEach(model.getSelectedItems(), (item: BaseModel<any>) => {
let newItem = item.clone(itemMap);
// offset the nodes slightly
if (newItem instanceof NodeModel) {
newItem.setPosition(newItem.x + offset.x, newItem.y + offset.y);
model.addNode(newItem);
} else if (newItem instanceof LinkModel) {
// offset the link points
newItem.getPoints().forEach(p => {
p.updateLocation({ x: p.getX() + offset.x, y: p.getY() + offset.y });
});
model.addLink(newItem);
}
newItem.selected = false;
});
this.forceUpdate();
}
render() {
const { engine } = this.props;
return (
<DemoWorkspaceWidget buttons={<button onClick={this.cloneSelected}>Clone Selected</button>}>
<DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />
</DemoWorkspaceWidget>
);
}
}
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.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 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 <CloneSelected engine={engine} model={model} />;
};

View File

@@ -1,20 +1,23 @@
import createEngine, {
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
DefaultPortModel,
DefaultLinkFactory,
DefaultLinkModel
} from '@projectstorm/react-diagrams';
import * as React from 'react';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
DiagramWidget,
LinkWidget,
LinkProps,
DefaultLinkWidget,
DefaultLinkModel,
DefaultLinkFactory
} from "storm-react-diagrams";
import { action } from "@storybook/addon-actions";
import * as React from "react";
export class AdvancedLinkModel extends DefaultLinkModel {
constructor() {
super({
type: 'advanced',
width: 10
});
super("advanced");
this.width = 10;
}
}
@@ -51,8 +54,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);
@@ -69,16 +72,15 @@ export class AdvancedLinkSegment extends React.Component<{ model: AdvancedLinkMo
return (
<>
<path
fill="none"
ref={(ref) => {
ref={ref => {
this.path = ref;
}}
strokeWidth={this.props.model.getOptions().width}
strokeWidth={this.props.model.width}
stroke="rgba(255,0,0,0.5)"
d={this.props.path}
/>
<circle
ref={(ref) => {
ref={ref => {
this.circle = ref;
}}
r={10}
@@ -91,14 +93,15 @@ export class AdvancedLinkSegment extends React.Component<{ model: AdvancedLinkMo
export class AdvancedLinkFactory extends DefaultLinkFactory {
constructor() {
super('advanced');
super();
this.type = "advanced";
}
generateModel(): AdvancedLinkModel {
getNewInstance(initialConfig?: any): AdvancedLinkModel {
return new AdvancedLinkModel();
}
generateLinkSegment(model: AdvancedLinkModel, selected: boolean, path: string) {
generateLinkSegment(model: AdvancedLinkModel, widget: DefaultLinkWidget, selected: boolean, path: string) {
return (
<g>
<AdvancedLinkSegment model={model} path={path} />
@@ -114,28 +117,29 @@ export class AdvancedLinkFactory extends DefaultLinkFactory {
*/
export default () => {
//1) setup the diagram engine
var engine = createEngine();
engine.getLinkFactories().registerFactory(new AdvancedLinkFactory());
var engine = new DiagramEngine();
engine.installDefaultFactories();
engine.registerLinkFactory(new AdvancedLinkFactory());
// create some nodes
var node1 = new DefaultNodeModel('Source', 'rgb(0,192,255)');
let port1 = node1.addPort(new AdvancedPortModel(false, 'out-1', 'Out thick'));
let port2 = node1.addPort(new DefaultPortModel(false, 'out-2', 'Out default'));
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();
@@ -146,12 +150,8 @@ export default () => {
model.addAll(node1, node2, node3, node4);
// load model into engine
engine.setModel(model);
engine.setDiagramModel(model);
// render the diagram!
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
return <DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />;
};

View File

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

View File

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

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

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

View File

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

View File

@@ -0,0 +1,57 @@
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
DefaultPortModel,
DiagramWidget
} from "storm-react-diagrams";
import * as React from "react";
// import the custom models
import { DiamondNodeModel } from "./DiamondNodeModel";
import { DiamondNodeFactory } from "./DiamondNodeFactory";
import { SimplePortFactory } from "./SimplePortFactory";
import { DiamondPortModel } from "./DiamondPortModel";
/**
* @Author Dylan Vorster
*/
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.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

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

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

@@ -0,0 +1,120 @@
import {
DiagramEngine,
DefaultNodeFactory,
DefaultLinkFactory,
DiagramModel,
DefaultNodeModel,
LinkModel,
DefaultPortModel,
DiagramWidget
} from "storm-react-diagrams";
import { distributeElements } from "./dagre-utils";
import * as React from "react";
import { DemoWorkspaceWidget } from "../.helpers/DemoWorkspaceWidget";
function createNode(name) {
return new DefaultNodeModel(name, "rgb(0,192,255)");
}
let count = 0;
function connectNodes(nodeFrom, nodeTo) {
//just to get id-like structure
count++;
const portOut = nodeFrom.addPort(new DefaultPortModel(true, `${nodeFrom.name}-out-${count}`, "Out"));
const portTo = nodeTo.addPort(new DefaultPortModel(false, `${nodeFrom.name}-to-${count}`, "IN"));
return portOut.link(portTo);
}
/**
* Tests auto distribution
*/
class Demo8Widget extends React.Component<any, any> {
constructor(props) {
super(props);
this.state = {};
this.autoDistribute = this.autoDistribute.bind(this);
}
autoDistribute() {
const { engine } = this.props;
const model = engine.getDiagramModel();
let distributedModel = getDistributedModel(engine, model);
engine.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,4 +1,4 @@
import * as SRD from '@projectstorm/react-diagrams';
import * as SRD from "storm-react-diagrams";
/**
* @author Dylan Vorster
@@ -8,22 +8,24 @@ export class Application {
protected diagramEngine: SRD.DiagramEngine;
constructor() {
this.diagramEngine = SRD.default();
this.diagramEngine = new SRD.DiagramEngine();
this.diagramEngine.installDefaultFactories();
this.newModel();
}
public newModel() {
this.activeModel = new SRD.DiagramModel();
this.diagramEngine.setModel(this.activeModel);
this.diagramEngine.setDiagramModel(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,72 @@
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

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

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

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

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

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

@@ -0,0 +1,37 @@
import { DiagramEngine, DiagramModel, DefaultNodeModel, LinkModel, DiagramWidget } from "storm-react-diagrams";
import * as React from "react";
/**
* Tests the grid size
*/
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.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

@@ -0,0 +1,39 @@
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
DiagramWidget,
DefaultGroupNodeModel
} from "storm-react-diagrams";
import * as React from "react";
/**
* Tests the grid size
*/
export default () => {
var engine = new DiagramEngine();
engine.installDefaultFactories();
var model = new DiagramModel();
var node1 = new DefaultNodeModel("Node 1", "rgb(0,192,255)");
let port = node1.addOutPort("Out");
node1.setPosition(100, 100);
var node2 = new DefaultNodeModel("Node 2", "rgb(192,255,0)");
let port2 = node2.addInPort("In");
node2.setPosition(400, 100);
var node3 = new DefaultGroupNodeModel("Group");
node3.setPosition(50, 50);
let link1 = port.link(port2);
model.addAll(node1, node2, link1, node3);
engine.setDiagramModel(model);
//6) render the diagram!
return <DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />;
};

View File

@@ -0,0 +1,73 @@
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
DefaultPortModel,
DiagramWidget,
DefaultLinkModel
} from "storm-react-diagrams";
import * as React from "react";
import { DemoWorkspaceWidget } from "../.helpers/DemoWorkspaceWidget";
import { action } from "@storybook/addon-actions";
export default () => {
// setup the diagram engine
const engine = new DiagramEngine();
engine.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.setTargetPort(port4);
(link3 as DefaultLinkModel).addLabel("Emoji label: 🎉");
// add all to the main model
model.addAll(node1, node2, node3, node4, link1, link2, link3);
// load model into engine and render
engine.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

@@ -0,0 +1,49 @@
import * as React from "react";
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
DiagramWidget,
DiagramProps
} from "storm-react-diagrams";
/**
* Shows that a limit of points can be set for links
*/
export default () => {
// setup the diagram engine
var engine = new DiagramEngine();
engine.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

@@ -0,0 +1,62 @@
import * as React from "react";
import { action } from "@storybook/addon-actions";
import {
DiagramEngine,
DiagramModel,
DiagramProps,
DefaultNodeModel,
LinkModel,
DiagramWidget
} from "storm-react-diagrams";
/**
* Shows some of the events triggered when elements are selected
*/
export default () => {
// setup the diagram engine
var engine = new DiagramEngine();
engine.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

@@ -0,0 +1,69 @@
import * as React from "react";
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
PointModel,
DiagramWidget,
DiagramProps
} from "storm-react-diagrams";
/**
*
* Shows how you can lock down the system so that the entire scene cant be interacted with.
*
* @Author Dylan Vorster
*/
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.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

@@ -0,0 +1,86 @@
import { DiagramEngine, DiagramModel, DefaultNodeModel, LinkModel, DiagramWidget } from "storm-react-diagrams";
import * as React from "react";
import { DemoWorkspaceWidget } from "../.helpers/DemoWorkspaceWidget";
/**
* Tests the grid size
*/
class NodeDelayedPosition extends React.Component<any, any> {
constructor(props) {
super(props);
this.updatePosition = this.updatePosition.bind(this);
this.updatePositionViaSerialize = this.updatePositionViaSerialize.bind(this);
}
updatePosition() {
const { engine } = this.props;
let model = engine.getDiagramModel();
const nodes = model.getNodes();
let node = nodes[Object.keys(nodes)[0]];
node.setPosition(node.x + 30, node.y + 30);
this.forceUpdate();
}
updatePositionViaSerialize() {
let { engine } = this.props;
let model = engine.getDiagramModel();
let str = JSON.stringify(model.serializeDiagram());
let model2 = new DiagramModel();
let obj = JSON.parse(str);
let node = obj.nodes[0];
node.x += 30;
node.y += 30;
model2.deSerializeDiagram(obj, engine);
engine.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,7 +1,5 @@
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';
import { DiagramEngine, DiagramModel, DefaultNodeModel, LinkModel, DiagramWidget } from "storm-react-diagrams";
import * as React from "react";
/**
*
@@ -12,7 +10,8 @@ import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
*/
export default () => {
//1) setup the diagram engine
var engine = createEngine();
var engine = new DiagramEngine();
engine.installDefaultFactories();
//2) setup the diagram model
var model = new DiagramModel();
@@ -24,25 +23,21 @@ export default () => {
}
//5) load model into engine
engine.setModel(model);
engine.setDiagramModel(model);
//6) render the diagram!
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
return <DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />;
};
function generateNodes(model: DiagramModel, offsetX: number, offsetY: number) {
//3-A) create a default node
var node1 = new DefaultNodeModel('Node 1', 'rgb(0,192,255)');
var port1 = node1.addOutPort('Out');
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,59 @@
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

@@ -0,0 +1,38 @@
import { DiagramEngine, DiagramModel, DefaultNodeModel, LinkModel, DiagramWidget } from "storm-react-diagrams";
import * as React from "react";
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.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

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

View File

@@ -0,0 +1,41 @@
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
DiagramWidget,
DefaultLinkModel
} from "storm-react-diagrams";
import * as React from "react";
export default () => {
//1) setup the diagram engine
var engine = new DiagramEngine();
engine.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

@@ -0,0 +1,68 @@
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
DefaultPortModel,
DiagramWidget
} from "storm-react-diagrams";
import * as React from "react";
import { DemoWorkspaceWidget } from "../.helpers/DemoWorkspaceWidget";
import { action } from "@storybook/addon-actions";
export default () => {
// setup the diagram engine
const engine = new DiagramEngine();
engine.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,8 +1,13 @@
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';
import {
DiagramEngine,
DiagramModel,
DefaultNodeModel,
LinkModel,
DefaultPortModel,
DiagramWidget
} from "storm-react-diagrams";
import * as React from "react";
import { DemoWorkspaceWidget } from "../.helpers/DemoWorkspaceWidget";
/**
*
@@ -12,7 +17,8 @@ import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
*/
export default () => {
//1) setup the diagram engine
var engine = createEngine();
var engine = new DiagramEngine();
engine.installDefaultFactories();
//2) setup the diagram model
var model = new DiagramModel();
@@ -24,27 +30,25 @@ export default () => {
}
//5) load model into engine
engine.setModel(model);
engine.setDiagramModel(model);
//6) render the diagram!
return (
<DemoWorkspaceWidget buttons={<DemoButton onClick={() => engine.zoomToFit()}>Zoom to fit</DemoButton>}>
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
<DemoWorkspaceWidget buttons={<button onClick={() => engine.zoomToFit()}>Zoom to fit</button>}>
<DiagramWidget className="srd-demo-canvas" diagramEngine={engine} />
</DemoWorkspaceWidget>
);
};
function generateNodes(model: DiagramModel, offsetX: number, offsetY: number) {
//3-A) create a default node
var node1 = new DefaultNodeModel('Node 1', 'rgb(0,192,255)');
var port1 = node1.addOutPort('Out');
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

136
demos/index.tsx Normal file
View File

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

10
demos/tslint.json Normal file
View File

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

View File

@@ -1,18 +0,0 @@
{
"sourceType": "unambiguous",
"presets": [
[
"@babel/preset-env",
{
"targets": {
"chrome": 100,
"safari": 15,
"firefox": 91
}
}
],
"@babel/preset-react",
"@babel/preset-typescript"
],
"plugins": []
}

View File

@@ -1,8 +0,0 @@
module.exports = {
stories: ['../demos/*.stories.tsx'],
addons: ['@storybook/addon-actions'],
framework: {
name: '@storybook/react-webpack5',
options: {}
}
};

View File

@@ -1,7 +0,0 @@
import { addons } from '@storybook/addons';
import diagramsTheme from './theme';
addons.setConfig({
theme: diagramsTheme
});

View File

@@ -1,5 +0,0 @@
<style>
html, body, #storybook-root {
height: 100%;
}
</style>

View File

@@ -1,3 +0,0 @@
export const parameters = {
layout: 'fullscreen'
};

View File

@@ -1,7 +0,0 @@
import { create } from '@storybook/theming';
export default create({
base: 'dark',
brandTitle: 'STORM React Diagrams',
brandUrl: 'https://github.com/projectstorm/react-diagrams'
});

View File

@@ -1,70 +0,0 @@
# @projectstorm/react-diagrams-gallery
## 7.1.2
### Patch Changes
- 66c687a: Upgrade all dependencies and fix Storybook after upgrade
- Updated dependencies [66c687a]
- @projectstorm/react-diagrams-defaults@7.1.2
- @projectstorm/react-diagrams-core@7.0.2
- @projectstorm/react-canvas-core@7.0.2
- @projectstorm/react-diagrams@7.0.3
## 7.1.1
### Patch Changes
- b8a4cbd: Inline sources in sourcemap
- Updated dependencies [b8a4cbd]
- @projectstorm/react-canvas-core@7.0.1
- @projectstorm/react-diagrams@7.0.2
- @projectstorm/react-diagrams-core@7.0.1
- @projectstorm/react-diagrams-defaults@7.1.1
## 7.1.0
### Minor Changes
- e0d21f1: - [feature] new ability to refresh links in auto distribute system [PR 756](https://github.com/projectstorm/react-diagrams/pull/756)
- [fix] Default link now uses the correct method for creating a point allowing this to be overridden [PR 939](https://github.com/projectstorm/react-diagrams/pull/939)
Big thanks to @ToTheHit and @h0111in for your help on these, even though its very delayed on my part :)
### Patch Changes
- Updated dependencies [e0d21f1]
- @projectstorm/react-diagrams-defaults@7.1.0
- @projectstorm/react-diagrams@7.0.1
## 7.0.0
### Major Changes
- b051697: - [internal] moves to `Pnpm` (instead of yarn -\_-)
- [internal]moves to `Changesets` for releases
- [internal]removes `Lerna`
- [internal] upgrades all dependencies
- [internal] switches to workspace protocol syntax (Changesets will bake in the correct version when a publish occurs)
- [internal] Changesets will open a release PR which can wrap up several changes in 1 go
- [internal] Changesets will run the storybook deploy automatically upon merging the release PR
- [internal] removes a lot of the stuff from the root package.json
- [internal] cleans up the build and clean commands
- [internal] remove E2E tests, they are a nightmare to maintain and the ROI is far too low
- [fix] Wrong type name for react-canvas model listener
- [fix] export more stuff form the main react-diagrams package
- [fix] circular deps with Rectangle and Polygon (turns out this was a problem but only with UMD builds, sorry @everyone who I doubted, but this is also why I could never reproduce the issue)
- [breaking change] compile both ES6 and UMD
- [breaking change] moves dependencies back to each package. (After years of working on libraries, I've come to actually hate peer dependencies, and this is easily solved with build systems / package managers).
- [breaking change] static methods on `Polygon` and `Rectangle` moved to standalone methods
- [breaking change] static construction methods to rather deal with different Rectangle constructor overloads (I now consider this bad design)
- [breaking change] introduce `Bounds` as a simpler point-array type to deal with boundary computation instead
### Patch Changes
- Updated dependencies [b051697]
- @projectstorm/react-diagrams-defaults@7.0.0
- @projectstorm/react-diagrams-core@7.0.0
- @projectstorm/react-canvas-core@7.0.0
- @projectstorm/react-diagrams@7.0.0

View File

@@ -1,32 +0,0 @@
import { Toolkit } from '@projectstorm/react-canvas-core';
Toolkit.TESTING = true;
export default {
title: 'Simple Usage'
};
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_listeners from './demo-listeners';
import demo_zoom from './demo-zoom-to-fit';
import demo_zoom_nodes from './demo-zoom-to-fit-nodes';
import demo_canvas_drag from './demo-canvas-drag';
import demo_pan_and_zoom from './demo-pan-and-zoom';
import demo_dynamic_ports from './demo-dynamic-ports';
import demo_labels from './demo-labelled-links';
export const DemoSimple = demo_simple;
export const SimpleFlowExample = demo_flow;
export const PerformanceDemo = demo_performance;
export const LockedWidget = demo_locks;
export const CanvasGridSize = demo_grid;
export const EventsAndListeners = demo_listeners;
export const ZoomToFit = demo_zoom;
export const ZoomToFitSelectNodes = demo_zoom_nodes;
export const CanvasDrag = demo_canvas_drag;
export const CanvasPanAndZoom = demo_pan_and_zoom;
export const DynamicPorts = demo_dynamic_ports;
export const LinksWithLabels = demo_labels;

View File

@@ -1,24 +0,0 @@
import { Toolkit } from '@projectstorm/react-canvas-core';
Toolkit.TESTING = true;
export default {
title: 'Advanced Usage'
};
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';
import demo_right_angles_routing from './demo-right-angles-routing';
import demo_alternative_linking from './demo-alternative-linking';
import demo_custom_delete_keys from './demo-custom_delete_keys';
export const CloneSelected = demo_adv_clone_selected;
export const SerializingAndDeSerializing = demo_adv_ser_des;
export const ProgramaticallyModifyingGraph = demo_adv_prog;
export const DragAndDrop = demo_adv_dnd;
export const SmartRouting = demo_smart_routing;
export const RightAnglesRouting = demo_right_angles_routing;
export const LinkingByClickingInsteadOfDragging = demo_alternative_linking;
export const SettingCustomDeleteKeys = demo_custom_delete_keys;

View File

@@ -1,18 +0,0 @@
import { Toolkit } from '@projectstorm/react-canvas-core';
Toolkit.TESTING = true;
export default {
title: 'Customization'
};
import demo_custom_link_label from './demo-custom-link-label';
import demo_custom_action from './demo-custom-action';
import demo_cust_nodes from './demo-custom-node1';
import demo_cust_links from './demo-custom-link1';
import demo_cust_links2 from './demo-custom-link2';
export const CustomDiamondNode = demo_cust_nodes;
export const CustomAnimatedLinks = demo_cust_links;
export const CustomLinkEndsWithArrows = demo_cust_links2;
export const CustomLinkLabel = demo_custom_link_label;
export const CustomEvent = demo_custom_action;

View File

@@ -1,12 +0,0 @@
import { Toolkit } from '@projectstorm/react-canvas-core';
Toolkit.TESTING = true;
export default {
title: 'External Libs'
};
import demo_3rd_dagre from './demo-dagre';
import demo_gsap from './demo-animation';
export const DagreDistribute = demo_3rd_dagre;
export const GsapAnimation = demo_gsap;

View File

@@ -1,89 +0,0 @@
import { Action, ActionEvent, InputType, State } from '@projectstorm/react-canvas-core';
import { PortModel, LinkModel, DiagramEngine } from '@projectstorm/react-diagrams-core';
import { MouseEvent, KeyboardEvent } from 'react';
/**
* This state is controlling the creation of a link.
*/
export class CreateLinkState extends State<DiagramEngine> {
sourcePort: PortModel;
link: LinkModel;
constructor() {
super({ name: 'create-new-link' });
this.registerAction(
new Action({
type: InputType.MOUSE_UP,
fire: (actionEvent: ActionEvent<MouseEvent>) => {
const element = this.engine.getActionEventBus().getModelForEvent(actionEvent);
const {
event: { clientX, clientY }
} = actionEvent;
const ox = this.engine.getModel().getOffsetX();
const oy = this.engine.getModel().getOffsetY();
if (element instanceof PortModel && !this.sourcePort) {
this.sourcePort = element;
/* would be cool if link creating could be done somewhat like
const link = createLink({
sourcePort: this.sourcePort,
points: [{ x: clientX, y: clientY }, { x: clientX, y: clientY }]
})
*/
const link = this.sourcePort.createLinkModel();
link.setSourcePort(this.sourcePort);
link.getFirstPoint().setPosition(clientX - ox, clientY - oy);
link.getLastPoint().setPosition(clientX - ox + 20, clientY - oy + 20);
this.link = this.engine.getModel().addLink(link);
} else if (element instanceof PortModel && this.sourcePort && element != this.sourcePort) {
if (this.sourcePort.canLinkToPort(element)) {
this.link.setTargetPort(element);
element.reportPosition();
this.clearState();
this.eject();
}
} else if (element === this.link.getLastPoint()) {
this.link.point(clientX - ox, clientY - oy, -1);
}
this.engine.repaintCanvas();
}
})
);
this.registerAction(
new Action({
type: InputType.MOUSE_MOVE,
fire: (actionEvent: ActionEvent<React.MouseEvent>) => {
if (!this.link) return;
const { event } = actionEvent;
this.link.getLastPoint().setPosition(event.clientX, event.clientY);
this.engine.repaintCanvas();
}
})
);
this.registerAction(
new Action({
type: InputType.KEY_UP,
fire: (actionEvent: ActionEvent<KeyboardEvent>) => {
// on esc press remove any started link and pop back to default state
if (actionEvent.event.keyCode === 27) {
this.link.remove();
this.clearState();
this.eject();
this.engine.repaintCanvas();
}
}
})
);
}
clearState() {
this.link = undefined;
this.sourcePort = undefined;
}
}

View File

@@ -1,69 +0,0 @@
import { MouseEvent, TouchEvent } from 'react';
import {
SelectingState,
State,
Action,
InputType,
ActionEvent,
DragCanvasState
} from '@projectstorm/react-canvas-core';
import { PortModel, DiagramEngine, DragDiagramItemsState } from '@projectstorm/react-diagrams-core';
import { CreateLinkState } from './CreateLinkState';
export class DefaultState extends State<DiagramEngine> {
dragCanvas: DragCanvasState;
createLink: CreateLinkState;
dragItems: DragDiagramItemsState;
constructor() {
super({ name: 'starting-state' });
this.childStates = [new SelectingState()];
this.dragCanvas = new DragCanvasState();
this.createLink = new CreateLinkState();
this.dragItems = new DragDiagramItemsState();
// determine what was clicked on
this.registerAction(
new Action({
type: InputType.MOUSE_DOWN,
fire: (event: ActionEvent<MouseEvent>) => {
const element = this.engine.getActionEventBus().getModelForEvent(event);
// the canvas was clicked on, transition to the dragging canvas state
if (!element) {
this.transitionWithEvent(this.dragCanvas, event);
}
// initiate dragging a new link
else if (element instanceof PortModel) {
return;
}
// move the items (and potentially link points)
else {
this.transitionWithEvent(this.dragItems, event);
}
}
})
);
// touch drags the canvas
this.registerAction(
new Action({
type: InputType.TOUCH_START,
fire: (event: ActionEvent<TouchEvent>) => {
this.transitionWithEvent(new DragCanvasState(), event);
}
})
);
this.registerAction(
new Action({
type: InputType.MOUSE_UP,
fire: (event: ActionEvent<MouseEvent>) => {
const element = this.engine.getActionEventBus().getModelForEvent(event);
if (element instanceof PortModel) this.transitionWithEvent(this.createLink, event);
}
})
);
}
}

View File

@@ -1,31 +0,0 @@
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';
import { DefaultState } from './DefaultState';
export default () => {
const engine = createEngine();
const model = new DiagramModel();
const node1 = new DefaultNodeModel('Node 1', 'rgb(0,192,255)');
node1.addOutPort('Out');
node1.setPosition(100, 100);
const node2 = new DefaultNodeModel('Node 2', 'rgb(192,255,0)');
node2.addInPort('In');
node2.setPosition(400, 100);
model.addAll(node1, node2);
engine.setModel(model);
// Use this custom "DefaultState" instead of the actual default state we get with the engine
engine.getStateMachine().pushState(new DefaultState());
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};

View File

@@ -1,86 +0,0 @@
import createEngine, { DiagramModel, DefaultNodeModel } from '@projectstorm/react-diagrams';
import * as React from 'react';
import gsap from 'gsap';
import { 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);
}
render() {
const { engine } = this.props;
return (
<DemoWorkspaceWidget>
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
</DemoWorkspaceWidget>
);
}
}
export default () => {
//1) setup the diagram engine
var engine = createEngine({ repaintDebounceMs: 12 });
//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) create another default node
var node3 = new DefaultNodeModel('Node 3', 'rgb(192,255,0)');
node2.setPosition(200, 300);
//3-D) create another default node
var node4 = new DefaultNodeModel('Node 4', 'rgb(192,255,0)');
node2.setPosition(400, 400);
//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, node3, node4);
//5) load model into engine
engine.setModel(model);
var interval = setInterval(() => {
[node1, node2, node3, node4].map((node) => {
var obj = { x: 0, y: 0 };
gsap.fromTo(
obj,
{
x: node.getPosition().x,
y: node.getPosition().y
},
{
x: Math.floor(Math.random() * 500),
y: Math.floor(Math.random() * 500),
duration: 0.8,
onUpdate: () => {
node.setPosition(obj.x, obj.y);
engine.repaintCanvas();
}
}
);
});
}, 2000);
//6) render the diagram!
return <NodeDelayedPosition engine={engine} model={model} />;
};

View File

@@ -1,72 +0,0 @@
import * as React from 'react';
import createEngine, { DiagramModel, DefaultNodeModel } from '@projectstorm/react-diagrams';
import { DemoButton, DemoWorkspaceWidget } from '../helpers/DemoWorkspaceWidget';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
/**
* Tests the drag on/off
*/
class CanvasDragToggle extends React.Component<any, any> {
enableDrag = () => {
const { engine } = this.props;
const state = engine.getStateMachine().getCurrentState();
state.dragCanvas.config.allowDrag = true;
};
disableDrag = () => {
const { engine } = this.props;
const state = engine.getStateMachine().getCurrentState();
state.dragCanvas.config.allowDrag = false;
};
render() {
const { engine } = this.props;
return (
<DemoWorkspaceWidget
buttons={[
<DemoButton key={1} onClick={this.enableDrag}>
Enable canvas drag
</DemoButton>,
<DemoButton key={2} onClick={this.disableDrag}>
Disable canvas drag
</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 <CanvasDragToggle engine={engine} model={model} />;
};

View File

@@ -1,83 +0,0 @@
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
*/
class CloneSelected extends React.Component<any, any> {
constructor(props: any) {
super(props);
this.cloneSelected = this.cloneSelected.bind(this);
}
cloneSelected() {
let { engine } = this.props;
let offset = { x: 100, y: 100 };
let model = engine.getModel();
let itemMap = {};
_.forEach(model.getSelectedEntities(), (item: BaseModel<any>) => {
let newItem = item.clone(itemMap);
// offset the nodes slightly
if (newItem instanceof NodeModel) {
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.setPosition(p.getX() + offset.x, p.getY() + offset.y);
});
model.addLink(newItem);
}
(newItem as BaseModel).setSelected(false);
});
this.forceUpdate();
}
render() {
const { engine } = this.props;
return (
<DemoWorkspaceWidget buttons={<DemoButton onClick={this.cloneSelected}>Clone Selected</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)');
let port = node1.addOutPort('Out');
node1.setPosition(100, 100);
//3-B) create another default node
var node2 = new DefaultNodeModel('Node 2', 'rgb(192,255,0)');
let port2 = node2.addInPort('In');
node2.setPosition(400, 100);
// link the ports
let link1 = port.link(port2);
//4) add the models to the root graph
model.addAll(node1, node2, link1);
//5) load model into engine
engine.setModel(model);
//6) render the diagram!
return <CloneSelected engine={engine} model={model} />;
};

View File

@@ -1,73 +0,0 @@
import * as React from 'react';
import * as _ from 'lodash';
import createEngine, { DiagramModel, DefaultNodeModel, DefaultLinkModel } from '@projectstorm/react-diagrams';
import { CanvasWidget, Action, ActionEvent, InputType } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
interface CustomDeleteItemsActionOptions {
keyCodes?: number[];
}
/**
* Deletes all selected items, but asks for confirmation first
*/
class CustomDeleteItemsAction extends Action {
constructor(options: CustomDeleteItemsActionOptions = {}) {
options = {
keyCodes: [46, 8],
...options
};
super({
type: InputType.KEY_DOWN,
fire: (event: ActionEvent<React.KeyboardEvent>) => {
if (options.keyCodes.indexOf(event.event.keyCode) !== -1) {
const selectedEntities = this.engine.getModel().getSelectedEntities();
if (selectedEntities.length > 0) {
const confirm = window.confirm('Are you sure you want to delete?');
if (confirm) {
_.forEach(selectedEntities, (model) => {
// only delete items which are not locked
if (!model.isLocked()) {
model.remove();
}
});
this.engine.repaintCanvas();
}
}
}
}
});
}
}
export default () => {
// create an engine without registering DeleteItemsAction
const engine = createEngine({ registerDefaultDeleteItemsAction: false });
const model = new DiagramModel();
const node1 = new DefaultNodeModel({ name: 'Node 1', color: 'rgb(0,192,255)' });
node1.setPosition(100, 100);
const port1 = node1.addOutPort('Out');
const node2 = new DefaultNodeModel('Node 2', 'rgb(192,255,0)');
const port2 = node2.addInPort('In');
node2.setPosition(400, 100);
const link1 = port1.link<DefaultLinkModel>(port2);
link1.getOptions().testName = 'Test';
link1.addLabel('Hello World!');
model.addAll(node1, node2, link1);
engine.setModel(model);
// register an DeleteItemsAction with custom keyCodes (in this case, only Delete key)
engine.getActionEventBus().registerAction(new CustomDeleteItemsAction());
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};

View File

@@ -1,20 +0,0 @@
import * as React from 'react';
import { AbstractReactFactory, GenerateWidgetEvent } from '@projectstorm/react-canvas-core';
import { DiagramEngine } from '@projectstorm/react-diagrams';
import { EditableLabelModel } from './EditableLabelModel';
import { EditableLabelWidget } from './EditableLabelWidget';
export class EditableLabelFactory extends AbstractReactFactory<EditableLabelModel, DiagramEngine> {
constructor() {
super('editable-label');
}
generateModel(): EditableLabelModel {
return new EditableLabelModel();
}
generateReactWidget(event: GenerateWidgetEvent<EditableLabelModel>): JSX.Element {
return <EditableLabelWidget model={event.model} />;
}
}

View File

@@ -1,30 +0,0 @@
import { LabelModel } from '@projectstorm/react-diagrams';
import { BaseModelOptions, DeserializeEvent } from '@projectstorm/react-canvas-core';
export interface EditableLabelOptions extends BaseModelOptions {
value?: string;
}
export class EditableLabelModel extends LabelModel {
value: string;
constructor(options: EditableLabelOptions = {}) {
super({
...options,
type: 'editable-label'
});
this.value = options.value || '';
}
serialize() {
return {
...super.serialize(),
value: this.value
};
}
deserialize(event: DeserializeEvent<this>): void {
super.deserialize(event);
this.value = event.data.value;
}
}

View File

@@ -1,40 +0,0 @@
import * as React from 'react';
import { EditableLabelModel } from './EditableLabelModel';
import styled from '@emotion/styled';
import { action } from '@storybook/addon-actions';
export interface FlowAliasLabelWidgetProps {
model: EditableLabelModel;
}
namespace S {
// NOTE: this CSS rules allows to interact with elements in label
export const Label = styled.div`
user-select: none;
pointer-events: auto;
`;
}
// now we can render all what we want in the label
export const EditableLabelWidget: React.FunctionComponent<FlowAliasLabelWidgetProps> = (props) => {
const [str, setStr] = React.useState(props.model.value);
return (
<S.Label>
<input
value={str}
onChange={(event) => {
const newVal = event.target.value;
// update value both in internal component state
setStr(newVal);
// and in model object
props.model.value = newVal;
}}
/>
<button onClick={() => action('model eventDidFire')('You clicked the button')}>Click me!</button>
</S.Label>
);
};

View File

@@ -1,55 +0,0 @@
import * as React from 'react';
import createEngine, { DefaultNodeModel, DiagramModel } from '@projectstorm/react-diagrams';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
import { EditableLabelFactory } from './EditableLabelFactory';
import { EditableLabelModel } from './EditableLabelModel';
/**
* @Author Shumaf Lovpache (aka Soarex16)
*/
export default () => {
// engine setup
const engine = createEngine();
// register our label factory
engine.getLabelFactories().registerFactory(new EditableLabelFactory());
// setup diagram model
const model = new DiagramModel();
// create some nodes
const node1 = new DefaultNodeModel('Node1', 'red');
const port1 = node1.addOutPort('out');
node1.setPosition(250, 100);
const node2 = new DefaultNodeModel('Node2', 'green');
const port2 = node2.addInPort('in');
node2.setPosition(800, 300);
// link nodes together
const link1 = port1.link(port2);
// !!!
// add our custom label to link
link1.addLabel(
new EditableLabelModel({
value: 'Hello, I am label!'
})
);
// add models to the root graph
model.addAll(node1, port1, node2, port2, link1);
// load model into engine
engine.setModel(model);
// render diagram
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};

View File

@@ -1,168 +0,0 @@
import createEngine, {
DiagramModel,
DefaultNodeModel,
DefaultPortModel,
DefaultLinkFactory,
DefaultLinkModel,
DefaultLinkWidget
} from '@projectstorm/react-diagrams';
import { LinkWidget, PointModel } from '@projectstorm/react-diagrams-core';
import * as React from 'react';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
import { MouseEvent } from 'react';
export class AdvancedLinkModel extends DefaultLinkModel {
constructor() {
super({
type: 'advanced',
width: 4
});
}
}
export class AdvancedPortModel extends DefaultPortModel {
createLinkModel(): AdvancedLinkModel | null {
return new AdvancedLinkModel();
}
}
const CustomLinkArrowWidget = (props) => {
const { point, previousPoint } = props;
const angle =
90 +
(Math.atan2(
point.getPosition().y - previousPoint.getPosition().y,
point.getPosition().x - previousPoint.getPosition().x
) *
180) /
Math.PI;
//translate(50, -10),
return (
<g className="arrow" transform={'translate(' + point.getPosition().x + ', ' + point.getPosition().y + ')'}>
<g style={{ transform: 'rotate(' + angle + 'deg)' }}>
<g transform={'translate(0, -3)'}>
<polygon
points="0,10 8,30 -8,30"
fill={props.color}
data-id={point.getID()}
data-linkid={point.getLink().getID()}
/>
</g>
</g>
</g>
);
};
export class AdvancedLinkWidget extends DefaultLinkWidget {
generateArrow(point: PointModel, previousPoint: PointModel): JSX.Element {
return (
<CustomLinkArrowWidget
key={point.getID()}
point={point as any}
previousPoint={previousPoint as any}
colorSelected={this.props.link.getOptions().selectedColor}
color={this.props.link.getOptions().color}
/>
);
}
render() {
//ensure id is present for all points on the path
var points = this.props.link.getPoints();
var paths = [];
this.refPaths = [];
//draw the multiple anchors and complex line instead
for (let j = 0; j < points.length - 1; j++) {
paths.push(
this.generateLink(
LinkWidget.generateLinePath(points[j], points[j + 1]),
{
'data-linkid': this.props.link.getID(),
'data-point': j,
onMouseDown: (event: MouseEvent) => {
this.addPointToLink(event, j + 1);
}
},
j
)
);
}
//render the circles
for (let i = 1; i < points.length - 1; i++) {
paths.push(this.generatePoint(points[i]));
}
if (this.props.link.getTargetPort() !== null) {
paths.push(this.generateArrow(points[points.length - 1], points[points.length - 2]));
} else {
paths.push(this.generatePoint(points[points.length - 1]));
}
return <g data-default-link-test={this.props.link.getOptions().testName}>{paths}</g>;
}
}
export class AdvancedLinkFactory extends DefaultLinkFactory {
constructor() {
super('advanced');
}
generateModel(): AdvancedLinkModel {
return new AdvancedLinkModel();
}
generateReactWidget(event): JSX.Element {
return <AdvancedLinkWidget link={event.model} diagramEngine={this.engine} />;
}
}
/**
*
* Simple link styling demo
*
* @Author kfrajtak
*/
export default () => {
//1) setup the diagram engine
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'));
node1.setPosition(100, 100);
var node2 = new DefaultNodeModel('Target', 'rgb(192,255,0)');
var port2 = node2.addPort(new AdvancedPortModel(true, 'in'));
node2.setPosition(500, 350);
var node3 = new DefaultNodeModel('Source', 'rgb(0,192,255)');
let port3 = node3.addPort(new AdvancedPortModel(false, 'out'));
node3.setPosition(100, 500);
var node4 = new DefaultNodeModel('Target', 'rgb(192,255,0)');
var port4 = node4.addPort(new AdvancedPortModel(true, 'in'));
node4.setPosition(500, 450);
var model = new DiagramModel();
model.addAll(port1.link(port2), port3.link(port4));
// add everything else
model.addAll(node1, node2, node3, node4);
// load model into engine
engine.setModel(model);
// render the diagram!
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};

View File

@@ -1,19 +0,0 @@
import { DiamondNodeWidget } 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 <DiamondNodeWidget engine={this.engine} size={50} node={event.model} />;
}
generateModel(event) {
return new DiamondNodeModel();
}
}

View File

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

@@ -1,116 +0,0 @@
import * as React from 'react';
import { DiamondNodeModel } from './DiamondNodeModel';
import { DiagramEngine, PortModelAlignment, PortWidget } from '@projectstorm/react-diagrams';
import styled from '@emotion/styled';
export interface DiamondNodeWidgetProps {
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 DiamondNodeWidget extends React.Component<DiamondNodeWidgetProps> {
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

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

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

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

@@ -1,35 +0,0 @@
import * as React from 'react';
import createEngine, { DiagramModel, DefaultNodeModel, DefaultLinkModel } from '@projectstorm/react-diagrams';
import { CanvasWidget, DeleteItemsAction } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
export default () => {
// create an engine without registering DeleteItemsAction
const engine = createEngine({ registerDefaultDeleteItemsAction: false });
const model = new DiagramModel();
const node1 = new DefaultNodeModel({ name: 'Node 1', color: 'rgb(0,192,255)' });
node1.setPosition(100, 100);
const port1 = node1.addOutPort('Out');
const node2 = new DefaultNodeModel('Node 2', 'rgb(192,255,0)');
const port2 = node2.addInPort('In');
node2.setPosition(400, 100);
const link1 = port1.link<DefaultLinkModel>(port2);
link1.getOptions().testName = 'Test';
link1.addLabel('Hello World!');
model.addAll(node1, node2, link1);
engine.setModel(model);
// register an DeleteItemsAction with custom keyCodes (in this case, only Delete key)
engine.getActionEventBus().registerAction(new DeleteItemsAction({ keyCodes: [46] }));
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};

View File

@@ -1,148 +0,0 @@
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',
marginx: 25,
marginy: 25
},
includeLinks: true,
nodeMargin: 25
});
}
autoDistribute = () => {
this.engine.redistribute(this.props.model);
// only happens if pathfing is enabled (check line 25)
this.reroute();
this.props.engine.repaintCanvas();
};
autoRefreshLinks = () => {
this.engine.refreshLinks(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={
<div>
<DemoButton onClick={this.autoDistribute}>Re-distribute</DemoButton>
<DemoButton onClick={this.autoRefreshLinks}>Refresh Links</DemoButton>
</div>
}
>
<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(nodesTo[0], nodesTo[1], engine));
links.push(connectNodes(nodesTo[1], nodesTo[2], engine));
links.push(connectNodes(nodesTo[0], nodesTo[2], engine));
links.push(connectNodes(nodesFrom[0], nodesFrom[2], engine));
links.push(connectNodes(nodesFrom[0], 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,87 +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 } 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

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

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

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

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

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

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

@@ -1,54 +0,0 @@
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('element eventDidFire')
});
});
model.registerListener({
eventDidFire: action('model eventDidFire')
});
engine.setModel(model);
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};

View File

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

@@ -1,90 +0,0 @@
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} />;
};

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