mirror of
https://github.com/projectstorm/react-diagrams.git
synced 2026-03-13 09:50:09 +08:00
Compare commits
42 Commits
react_canv
...
v5.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e333a4f168 | ||
|
|
1d316409e1 | ||
|
|
9d947d40b7 | ||
|
|
7d80f15358 | ||
|
|
28844b86eb | ||
|
|
70c9aeac66 | ||
|
|
12a7bfbda5 | ||
|
|
650061db4a | ||
|
|
48c6d1f038 | ||
|
|
7cea7032f0 | ||
|
|
2b346aa326 | ||
|
|
0f7f8853d1 | ||
|
|
3a57ea711e | ||
|
|
de208e3250 | ||
|
|
2df09edb7e | ||
|
|
ba1401a8f2 | ||
|
|
c6137d03ce | ||
|
|
ead43957e3 | ||
|
|
b7d8ffa793 | ||
|
|
a289c73b68 | ||
|
|
b75976d091 | ||
|
|
a95010652d | ||
|
|
4cd24940c8 | ||
|
|
0f1dec1ebc | ||
|
|
eedc21cc87 | ||
|
|
b719178f44 | ||
|
|
12724748e6 | ||
|
|
0ebd6d918f | ||
|
|
591ec2e76e | ||
|
|
9f95ac8451 | ||
|
|
e3fc996092 | ||
|
|
de9765358f | ||
|
|
fa34f5c98b | ||
|
|
f4ac467056 | ||
|
|
5b142c1c61 | ||
|
|
40219d7a4d | ||
|
|
0eb432b302 | ||
|
|
ad5e7a4b4d | ||
|
|
0583db1c74 | ||
|
|
0b92e939ab | ||
|
|
8997eba4b4 | ||
|
|
3fbbeb82de |
@@ -1,6 +1,6 @@
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# Some exceptions
|
||||
|
||||
200
.gitignore
vendored
200
.gitignore
vendored
@@ -1,194 +1,8 @@
|
||||
dist/
|
||||
dist/main.js
|
||||
dist/main.js.map
|
||||
/package
|
||||
*.tgz
|
||||
@types/
|
||||
|
||||
.out
|
||||
|
||||
# Created by https://www.gitignore.io/api/net,netbeans,sublimetext,phpstorm,windows,osx,node
|
||||
|
||||
#!! ERROR: net is undefined. Use list command to see defined gitignore types !!#
|
||||
|
||||
### NetBeans ###
|
||||
nbproject/private/
|
||||
build/
|
||||
nbbuild/
|
||||
nbdist/
|
||||
nbactions.xml
|
||||
.nb-gradle/
|
||||
|
||||
|
||||
### SublimeText ###
|
||||
# cache files for sublime text
|
||||
*.tmlanguage.cache
|
||||
*.tmPreferences.cache
|
||||
*.stTheme.cache
|
||||
|
||||
# workspace files are user-specific
|
||||
*.sublime-workspace
|
||||
|
||||
# project files should be checked into the repository, unless a significant
|
||||
# proportion of contributors will probably not be using SublimeText
|
||||
# *.sublime-project
|
||||
|
||||
# sftp configuration file
|
||||
sftp-config.json
|
||||
|
||||
# Package control specific files
|
||||
Package Control.last-run
|
||||
Package Control.ca-list
|
||||
Package Control.ca-bundle
|
||||
Package Control.system-ca-bundle
|
||||
Package Control.cache/
|
||||
Package Control.ca-certs/
|
||||
bh_unicode_properties.cache
|
||||
|
||||
# Sublime-github package stores a github token in this file
|
||||
# https://packagecontrol.io/packages/sublime-github
|
||||
GitHub.sublime-settings
|
||||
|
||||
|
||||
### PhpStorm ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
.idea/workspace.xml
|
||||
.idea/tasks.xml
|
||||
.idea/dictionaries
|
||||
.idea/vcs.xml
|
||||
.idea/jsLibraryMappings.xml
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/dataSources.ids
|
||||
.idea/dataSources.xml
|
||||
.idea/dataSources.local.xml
|
||||
.idea/sqlDataSources.xml
|
||||
.idea/dynamic.xml
|
||||
.idea/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/gradle.xml
|
||||
.idea/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
### PhpStorm Patch ###
|
||||
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
||||
|
||||
# *.iml
|
||||
# modules.xml
|
||||
|
||||
|
||||
### Windows ###
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
|
||||
### OSX ###
|
||||
*.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
jspm_packages
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
dist
|
||||
.DS_Store
|
||||
.idea
|
||||
.out
|
||||
*.zip
|
||||
.env
|
||||
node_modules
|
||||
yarn-error.log
|
||||
|
||||
198
.npmignore
198
.npmignore
@@ -1,194 +1,4 @@
|
||||
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
|
||||
*
|
||||
!dist/**/*
|
||||
!package.json
|
||||
!README.md
|
||||
|
||||
14
.storybook/addon-code/react.js
vendored
14
.storybook/addon-code/react.js
vendored
@@ -1,14 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
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} />,
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,2 @@
|
||||
import './addon-code/register';
|
||||
import '@storybook/addon-actions/register';
|
||||
import '@storybook/addon-options/register';
|
||||
|
||||
@@ -1,39 +1,48 @@
|
||||
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"
|
||||
module.exports = async ({config, mode}) => {
|
||||
return {
|
||||
...config,
|
||||
resolve: {
|
||||
...config.resolve,
|
||||
extensions: ['.tsx', '.ts', '.js'],
|
||||
alias: {
|
||||
...config.resolve.alias,
|
||||
'storm-react-diagrams': path.join(__dirname, "..", "src", "main")
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'storm-react-diagrams': path.join(__dirname, "..", "src", "main")
|
||||
},
|
||||
extensions: [".tsx", ".ts", ".js"]
|
||||
}
|
||||
module: {
|
||||
...config.module,
|
||||
rules: [
|
||||
...config.module.rules,
|
||||
...[
|
||||
{
|
||||
test: /\.scss$/,
|
||||
loaders: [
|
||||
'style-loader',
|
||||
'css-loader',
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {config: {path: path.join(__dirname, '..')}}
|
||||
},
|
||||
'sass-loader'
|
||||
]
|
||||
},
|
||||
{
|
||||
enforce: 'pre',
|
||||
test: /\.js$/,
|
||||
loader: 'source-map-loader',
|
||||
exclude: [/node_modules\//]
|
||||
},
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
transpileOnly: true
|
||||
}
|
||||
},
|
||||
]
|
||||
]
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
19
CHANGELOG.md
19
CHANGELOG.md
@@ -1,3 +1,22 @@
|
||||
__5.3.0__
|
||||
|
||||
* [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
|
||||
|
||||
21
README.md
21
README.md
@@ -1,12 +1,14 @@
|
||||
# STORM React Diagrams
|
||||
|
||||
**PSA**: React Diagrams is currently getting a bit of a rewrite to enable much more advanced features. To see the new foundation WIP visit [https://github.com/projectstorm/react-canvas](https://github.com/projectstorm/react-canvas).
|
||||
__PSA 2018__: React Diagrams ~is currently~ was getting a bit of a rewrite to enable much more advanced features. To see the new foundation WIP visit [https://github.com/projectstorm/react-canvas](https://github.com/projectstorm/react-canvas).
|
||||
|
||||
__PSA 2019__: I still want to jump onto the rewrite, but it is a much larger project than anticipated, so going to try maintain this one in the mean time.
|
||||
|
||||
---
|
||||
|
||||
**DEMO**: [http://www.projectstorm.io/react-diagrams](http://www.projectstorm.io/react-diagrams)
|
||||
**DEMO**: [http://projectstorm.cloud/react-diagrams](http://projectstorm.cloud/react-diagrams)
|
||||
|
||||
**DOCS:** [https://projectstorm.gitbooks.io/react-diagrams](https://projectstorm.gitbooks.io/react-diagrams)
|
||||
**(SOME) DOCS:** [https://projectstorm.gitbooks.io/react-diagrams](https://projectstorm.gitbooks.io/react-diagrams)
|
||||
|
||||
**RELEASE NOTES** : [http://dylanv.blog/2018/03/03/storm-react-diagrams-5-0-0/](http://dylanv.blog/2018/03/03/storm-react-diagrams-5-0-0/)
|
||||
|
||||
@@ -14,12 +16,13 @@ A super simple, no-nonsense diagramming library written in React that just works
|
||||
|
||||
[](https://gitter.im/projectstorm/react-diagrams?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://npmjs.org/package/storm-react-diagrams) [](https://npmjs.org/package/storm-react-diagrams) [](http://packagequality.com/#?package=storm-react-diagrams) [](https://circleci.com/gh/projectstorm/react-diagrams/tree/master)
|
||||
|
||||
Example implementation using custom models:
|
||||

|
||||
|
||||

|
||||
|
||||
Get started with the default models right out of the box:
|
||||

|
||||
|
||||
|
||||
## Introduction
|
||||
|
||||
A no-nonsense diagramming library written entirely in React with the help of a few small libraries. It aims to be:
|
||||
@@ -29,7 +32,11 @@ A no-nonsense diagramming library written entirely in React with the help of a f
|
||||
* 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
|
||||
* Perfect for creating declarative systems such as programmatic pipelines and visual programming languages
|
||||
|
||||
#### Installing
|
||||
|
||||
```yarn add @projectstorm/react-diagrams```
|
||||
|
||||
#### Run the demos
|
||||
|
||||
@@ -37,7 +44,7 @@ After running `yarn install` you must then run: `yarn run storybook`
|
||||
|
||||
#### Building from source
|
||||
|
||||
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.
|
||||
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\).
|
||||
|
||||
## [Checkout the docs](https://projectstorm.gitbooks.io/react-diagrams)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import * as React from "react";
|
||||
import { withDocs } from "storybook-readme";
|
||||
import { WithCode } from "../../.storybook/addon-code/react.js";
|
||||
|
||||
export class Helper {
|
||||
/**
|
||||
@@ -22,16 +20,4 @@ export class Helper {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
@import "../../src/sass/main";
|
||||
|
||||
html, body, #root{
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.srd-demo-workspace{
|
||||
background: black;
|
||||
display: flex;
|
||||
@@ -36,15 +42,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
|
||||
@@ -11,8 +11,8 @@ export function distributeElements(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;
|
||||
modelNode.x = node.x - node.width / 2;
|
||||
modelNode.y = node.y - node.height / 2;
|
||||
});
|
||||
return clonedModel;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@ export default () => {
|
||||
|
||||
// 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
|
||||
|
||||
162
demos/index.tsx
162
demos/index.tsx
@@ -1,23 +1,21 @@
|
||||
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";
|
||||
import {storiesOf, addDecorator, addParameters} from "@storybook/react";
|
||||
import {setOptions} from "@storybook/addon-options";
|
||||
import {host} from "storybook-host";
|
||||
import {Helper} from "./.helpers/Helper";
|
||||
import {Toolkit} from "../src/Toolkit";
|
||||
import {themes} from '@storybook/theming';
|
||||
|
||||
Toolkit.TESTING = true;
|
||||
|
||||
addDecorator(
|
||||
host({
|
||||
cropMarks: false,
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
padding: 20
|
||||
})
|
||||
);
|
||||
addParameters({
|
||||
options: {
|
||||
theme: themes.dark,
|
||||
},
|
||||
});
|
||||
|
||||
//include the SCSS for the demo
|
||||
import "./.helpers/demo.scss";
|
||||
|
||||
setOptions({
|
||||
name: "STORM React Diagrams",
|
||||
@@ -25,105 +23,51 @@ setOptions({
|
||||
addonPanelInRight: true
|
||||
});
|
||||
|
||||
import demo_simple from "./demo-simple";
|
||||
import demo_flow from "./demo-simple-flow";
|
||||
import demo_performance from "./demo-performance";
|
||||
import demo_locks from "./demo-locks";
|
||||
import demo_grid from "./demo-grid";
|
||||
import demo_limit_points from "./demo-limit-points";
|
||||
import demo_listeners from "./demo-listeners";
|
||||
import demo_zoom from "./demo-zoom-to-fit";
|
||||
import demo_labels from "./demo-labelled-links";
|
||||
|
||||
storiesOf("Simple Usage", module)
|
||||
.add(
|
||||
"Simple example",
|
||||
Helper.makeDemo(
|
||||
require("./demo-simple/index").default(),
|
||||
require("!!raw-loader!./demo-simple/index"),
|
||||
require("./demo-simple/docs.md")
|
||||
)
|
||||
)
|
||||
.add(
|
||||
"Simple flow example",
|
||||
Helper.makeDemo(require("./demo-simple-flow/index").default(), require("!!raw-loader!./demo-simple-flow/index"))
|
||||
)
|
||||
.add(
|
||||
"Performance demo",
|
||||
Helper.makeDemo(require("./demo-performance/index").default(), require("!!raw-loader!./demo-performance/index"))
|
||||
)
|
||||
.add(
|
||||
"Locked widget",
|
||||
Helper.makeDemo(require("./demo-locks/index").default(), require("!!raw-loader!./demo-locks/index"))
|
||||
)
|
||||
.add(
|
||||
"Canvas grid size",
|
||||
Helper.makeDemo(require("./demo-grid/index").default(), require("!!raw-loader!./demo-grid/index"))
|
||||
)
|
||||
.add(
|
||||
"Limiting link points",
|
||||
Helper.makeDemo(
|
||||
require("./demo-limit-points/index").default(),
|
||||
require("!!raw-loader!./demo-limit-points/index")
|
||||
)
|
||||
)
|
||||
.add(
|
||||
"Events and listeners",
|
||||
Helper.makeDemo(require("./demo-listeners/index").default(), require("!!raw-loader!./demo-listeners/index"))
|
||||
)
|
||||
.add(
|
||||
"Zoom to fit",
|
||||
Helper.makeDemo(require("./demo-zoom-to-fit/index").default(), require("!!raw-loader!./demo-zoom-to-fit/index"))
|
||||
)
|
||||
.add(
|
||||
"Links with labels",
|
||||
Helper.makeDemo(
|
||||
require("./demo-labelled-links/index").default(),
|
||||
require("!!raw-loader!./demo-labelled-links/index")
|
||||
)
|
||||
);
|
||||
.add("Simple example", demo_simple)
|
||||
.add("Simple flow example", demo_flow)
|
||||
.add("Performance demo", demo_performance)
|
||||
.add("Locked widget", demo_locks)
|
||||
.add("Canvas grid size", demo_grid)
|
||||
.add("Limiting link points", demo_limit_points)
|
||||
.add("Events and listeners", demo_listeners)
|
||||
.add("Zoom to fit", demo_zoom)
|
||||
.add("Links with labels", demo_labels);
|
||||
|
||||
import demo_adv_clone_selected from "./demo-cloning";
|
||||
import demo_adv_ser_des from "./demo-serializing";
|
||||
import demo_adv_prog from "./demo-mutate-graph";
|
||||
import demo_adv_dnd from "./demo-drag-and-drop";
|
||||
import demo_smart_routing from "./demo-smart-routing";
|
||||
|
||||
storiesOf("Advanced Techniques", module)
|
||||
.add(
|
||||
"Clone Selected",
|
||||
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")
|
||||
)
|
||||
);
|
||||
.add("Clone Selected", demo_adv_clone_selected)
|
||||
.add("Serializing and de-serializing", demo_adv_ser_des)
|
||||
.add("Programatically modifying graph", demo_adv_prog)
|
||||
.add("Drag and drop", demo_adv_dnd)
|
||||
.add("Smart routing", demo_smart_routing);
|
||||
|
||||
import demo_cust_nodes from "./demo-custom-node1";
|
||||
import demo_cust_links from "./demo-custom-link1";
|
||||
|
||||
storiesOf("Custom Models", module)
|
||||
.add(
|
||||
"Custom diamond node",
|
||||
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")
|
||||
)
|
||||
);
|
||||
.add("Custom diamond node", demo_cust_nodes)
|
||||
.add("Custom animated links", demo_cust_links);
|
||||
|
||||
storiesOf("3rd party libraries", module).add(
|
||||
"Auto Distribute (Dagre)",
|
||||
Helper.makeDemo(require("./demo-dagre/index").default(), require("!!raw-loader!./demo-dagre/index"))
|
||||
);
|
||||
import demo_3rd_dagre from "./demo-dagre";
|
||||
|
||||
storiesOf("3rd party libraries", module)
|
||||
.add("Auto Distribute (Dagre)", demo_3rd_dagre);
|
||||
|
||||
// enable this to log mouse location when writing new puppeteer tests
|
||||
//Helper.logMousePosition()
|
||||
|
||||
@@ -7,7 +7,7 @@ The first thing you need to do, is grab the distribution files on NPM. You can d
|
||||
**Via yarn:**
|
||||
|
||||
```
|
||||
yarn install storm-react-diagrams
|
||||
yarn add storm-react-diagrams
|
||||
```
|
||||
|
||||
**Via npm:**
|
||||
|
||||
@@ -26,8 +26,8 @@ tell the mouse pointer to click and drag on various elements while making assert
|
||||
|
||||
We use Jest for the assertions and the interactivity is handled by puppeteer. Due to the laborious nature
|
||||
of writing e2e tests, there is a helper method that is provided in each test that makes interacting
|
||||
with the diagrams a lot easier. Using this helper, you cna easily tell the mouse to drag links between nodes,
|
||||
with the diagrams a lot easier. Using this helper, you can easily tell the mouse to drag links between nodes,
|
||||
select them and also easily assert information about them. The important thing here, is that this helper
|
||||
does not touch the model in any way, but is purely a helper for writing the tests themselves. Please
|
||||
make use of this helper when writing tests, as it ensure that the tests are defensive in nature, and also
|
||||
reduces the overhead of physically writing them.
|
||||
reduces the overhead of physically writing them.
|
||||
|
||||
7
jest-puppeteer.config.js
Normal file
7
jest-puppeteer.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
launch: {
|
||||
dumpio: true,
|
||||
headless: process.env.CI === 'true',
|
||||
},
|
||||
browserContext: 'default',
|
||||
}
|
||||
@@ -1,27 +1,20 @@
|
||||
const path = require("path");
|
||||
// jest.config.js
|
||||
module.exports = {
|
||||
verbose: true,
|
||||
moduleFileExtensions: [
|
||||
"ts",
|
||||
"tsx",
|
||||
"js",
|
||||
"jsx",
|
||||
"json",
|
||||
"node"
|
||||
],
|
||||
"preset": "jest-puppeteer",
|
||||
transform: {
|
||||
".*test_loader.*": path.join(__dirname, "tests", "helpers", "storybook-loader.js" ),
|
||||
"^.+\\.tsx?$": "ts-jest",
|
||||
'^.+\\.tsx?$': 'ts-jest',
|
||||
'^.+\\.jsx?$': 'babel-jest'
|
||||
},
|
||||
moduleNameMapper:{
|
||||
"\\.(scss|css|png)$": path.join(__dirname,"tests","helpers","css-mock.js"),
|
||||
moduleNameMapper: {
|
||||
"\\.(scss|css|png)$": path.join(__dirname, "tests", "helpers", "css-mock.js"),
|
||||
"storm-react-diagrams": path.join(__dirname, "src", "main")
|
||||
},
|
||||
roots:[
|
||||
__dirname+'/tests'
|
||||
],
|
||||
testMatch: [
|
||||
"**/*\.test\.tsx"
|
||||
"**/*\.test\.ts"
|
||||
]
|
||||
};
|
||||
|
||||
114
package.json
114
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "storm-react-diagrams",
|
||||
"version": "5.1.1",
|
||||
"name": "@projectstorm/react-diagrams",
|
||||
"version": "5.3.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/projectstorm/react-diagrams.git"
|
||||
@@ -17,7 +17,7 @@
|
||||
"nodes"
|
||||
],
|
||||
"main": "./dist/main.js",
|
||||
"typings": "./dist/@types/src/main",
|
||||
"typings": "./dist/@types/main",
|
||||
"author": "dylanvorster",
|
||||
"scripts": {
|
||||
"build:ts": "webpack",
|
||||
@@ -28,61 +28,71 @@
|
||||
"storybook:github": "storybook-to-ghpages",
|
||||
"pretty": "prettier --use-tabs --write \"{src,demos,tests}/**/*.{ts,tsx}\" --print-width 120",
|
||||
"lint": "tslint -p .",
|
||||
"test:ci": "rm -rf ./dist && node ./tests/e2e/generate-e2e.js && jest --no-cache",
|
||||
"test:ci": "rm -rf ./dist && node ./tests/e2e/generate-e2e.js && jest --runInBand --no-cache",
|
||||
"test": "jest --no-cache",
|
||||
"prepublishOnly": "yarn run build:ts:prod && yarn run build:sass:prod"
|
||||
"prepublishOnly": "rm -rf ./dist && yarn run build:ts:prod && yarn run build:sass:prod"
|
||||
},
|
||||
"dependencies": {
|
||||
"peerDependencies": {
|
||||
"closest": "^0.0.1",
|
||||
"lodash": "^4.17.5",
|
||||
"lodash": "4.*",
|
||||
"pathfinding": "^0.4.18",
|
||||
"paths-js": "^0.4.7",
|
||||
"react": "^16.2.0"
|
||||
"paths-js": "^0.4.9",
|
||||
"react": "16.*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/addon-actions": "^3.3.15",
|
||||
"@storybook/addon-options": "^3.3.15",
|
||||
"@storybook/addon-storyshots": "^3.3.15",
|
||||
"@storybook/addons": "^3.3.15",
|
||||
"@storybook/react": "^3.3.15",
|
||||
"@storybook/storybook-deployer": "^2.3.0",
|
||||
"@types/jest": "^22.2.0",
|
||||
"@types/lodash": "^4.14.104",
|
||||
"@types/node": "^9.4.7",
|
||||
"@babel/core": "^7.5.5",
|
||||
"@storybook/addon-actions": "^5.1.9",
|
||||
"@storybook/addon-options": "^5.1.9",
|
||||
"@storybook/addon-storyshots": "^5.1.9",
|
||||
"@storybook/addons": "^5.1.9",
|
||||
"@storybook/react": "^5.1.9",
|
||||
"@storybook/storybook-deployer": "^2.8.1",
|
||||
"@storybook/theming": "^5.1.9",
|
||||
"@types/jest": "^24.0.15",
|
||||
"@types/jest-environment-puppeteer": "^4.0.0",
|
||||
"@types/lodash": "^4.14.136",
|
||||
"@types/node": "^12.6.8",
|
||||
"@types/promise": "^7.1.30",
|
||||
"@types/puppeteer": "^1.1.0",
|
||||
"@types/react": "^16.0.40",
|
||||
"awesome-typescript-loader": "^4.0.1",
|
||||
"copy-webpack-plugin": "^4.5.1",
|
||||
"cross-env": "^5.1.4",
|
||||
"css-loader": "^0.28.10",
|
||||
"dagre": "^0.8.2",
|
||||
"enzyme": "^3.3.0",
|
||||
"file-loader": "^1.1.11",
|
||||
"glob": "^7.1.2",
|
||||
"jest": "^22.4.2",
|
||||
"jest-cli": "^22.4.2",
|
||||
"json-beautify": "^1.0.1",
|
||||
"node-sass": "^4.7.2",
|
||||
"prettier": "^1.11.1",
|
||||
"puppeteer": "^1.1.1",
|
||||
"raf": "^3.4.0",
|
||||
"raw-loader": "^0.5.1",
|
||||
"react-dom": "^16.2.0",
|
||||
"react-syntax-highlighter": "^7.0.2",
|
||||
"react-test-renderer": "^16.2.0",
|
||||
"sass-loader": "^6.0.7",
|
||||
"source-map-loader": "^0.2.3",
|
||||
"storybook-host": "^4.1.5",
|
||||
"storybook-readme": "^3.2.1",
|
||||
"style-loader": "^0.20.3",
|
||||
"ts-jest": "^22.4.1",
|
||||
"tslint": "^5.9.1",
|
||||
"ts-loader": "^4.1.0",
|
||||
"typescript": "^2.7.2",
|
||||
"uglifyjs-webpack-plugin": "^1.2.3",
|
||||
"val-loader": "^1.1.0",
|
||||
"webpack": "^4.1.1",
|
||||
"webpack-cli": "^2.0.11"
|
||||
"@types/puppeteer": "^1.12.4",
|
||||
"@types/react": "^16.8.23",
|
||||
"babel-jest": "^24.8.0",
|
||||
"babel-loader": "^8.0.6",
|
||||
"closest": "^0.0.1",
|
||||
"copy-webpack-plugin": "^5.0.3",
|
||||
"cross-env": "^5.2.0",
|
||||
"css-loader": "^3.1.0",
|
||||
"dagre": "^0.8.4",
|
||||
"enzyme": "^3.10.0",
|
||||
"file-loader": "^4.1.0",
|
||||
"glob": "^7.1.4",
|
||||
"jest": "^24.8.0",
|
||||
"jest-cli": "^24.8.0",
|
||||
"jest-puppeteer": "^4.3.0",
|
||||
"json-beautify": "^1.1.0",
|
||||
"lodash": "4.*",
|
||||
"node-sass": "^4.12.0",
|
||||
"pathfinding": "^0.4.18",
|
||||
"paths-js": "^0.4.9",
|
||||
"prettier": "^1.18.2",
|
||||
"puppeteer": "^1.18.1",
|
||||
"raf": "^3.4.1",
|
||||
"raw-loader": "^3.1.0",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-syntax-highlighter": "^11.0.2",
|
||||
"react-test-renderer": "^16.8.6",
|
||||
"sass-loader": "^7.1.0",
|
||||
"source-map-loader": "^0.2.4",
|
||||
"storybook-host": "^5.1.0",
|
||||
"storybook-readme": "^5.0.5",
|
||||
"style-loader": "^0.23.1",
|
||||
"terser-webpack-plugin": "^1.3.0",
|
||||
"ts-jest": "^24.0.2",
|
||||
"ts-loader": "^6.0.4",
|
||||
"typescript": "^3.5.3",
|
||||
"val-loader": "^1.1.1",
|
||||
"webpack": "^4.36.1",
|
||||
"webpack-cli": "^3.3.6",
|
||||
"webpack-node-externals": "^1.7.2"
|
||||
}
|
||||
}
|
||||
|
||||
3
postcss.config.js
Normal file
3
postcss.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
plugins: [require('autoprefixer')]
|
||||
};
|
||||
@@ -45,8 +45,18 @@ export class Toolkit {
|
||||
|
||||
public static generateCurvePath(firstPoint: PointModel, lastPoint: PointModel, curvy: number = 0): string {
|
||||
var isHorizontal = Math.abs(firstPoint.x - lastPoint.x) > Math.abs(firstPoint.y - lastPoint.y);
|
||||
var curvyX = isHorizontal ? curvy : 0;
|
||||
var curvyY = isHorizontal ? 0 : curvy;
|
||||
|
||||
var xOrY = isHorizontal ? "x" : "y";
|
||||
|
||||
// make sure that smoothening works
|
||||
// without disrupting the line direction
|
||||
let curvyness = curvy;
|
||||
if (firstPoint[xOrY] > firstPoint[xOrY]) {
|
||||
curvyness = -curvy;
|
||||
}
|
||||
|
||||
var curvyX = isHorizontal ? curvyness : 0;
|
||||
var curvyY = isHorizontal ? 0 : curvyness;
|
||||
|
||||
return `M${firstPoint.x},${firstPoint.y} C ${firstPoint.x + curvyX},${firstPoint.y + curvyY}
|
||||
${lastPoint.x - curvyX},${lastPoint.y - curvyY} ${lastPoint.x},${lastPoint.y}`;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { DefaultPortModel } from "./DefaultPortModel";
|
||||
import * as _ from "lodash";
|
||||
|
||||
import { NodeModel } from "../../models/NodeModel";
|
||||
import { NodeModel, NodeModelListener } from "../../models/NodeModel";
|
||||
import { Toolkit } from "../../Toolkit";
|
||||
import { DiagramEngine } from "../../DiagramEngine";
|
||||
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
export class DefaultNodeModel extends NodeModel {
|
||||
export class DefaultNodeModel extends NodeModel<NodeModelListener> {
|
||||
name: string;
|
||||
color: string;
|
||||
ports: { [s: string]: DefaultPortModel };
|
||||
|
||||
@@ -318,13 +318,6 @@ export class DefaultLinkWidget extends BaseWidget<DefaultLinkProps, DefaultLinkS
|
||||
var pointLeft = points[0];
|
||||
var pointRight = points[1];
|
||||
|
||||
//some defensive programming to make sure the smoothing is
|
||||
//always in the right direction
|
||||
if (pointLeft[xOrY] > pointRight[xOrY]) {
|
||||
pointLeft = points[1];
|
||||
pointRight = points[0];
|
||||
}
|
||||
|
||||
paths.push(
|
||||
this.generateLink(
|
||||
Toolkit.generateCurvePath(pointLeft, pointRight, this.props.link.curvyness),
|
||||
|
||||
@@ -2,6 +2,7 @@ import { BaseEntity, BaseListener } from "../BaseEntity";
|
||||
import * as _ from "lodash";
|
||||
import { BaseEvent } from "../BaseEntity";
|
||||
import { DiagramEngine } from "../DiagramEngine";
|
||||
import { PointModel } from "../models/PointModel";
|
||||
|
||||
export interface BaseModelListener extends BaseListener {
|
||||
selectionChanged?(event: BaseEvent<BaseModel> & { isSelected: boolean }): void;
|
||||
@@ -34,7 +35,7 @@ export class BaseModel<
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public getSelectedEntities(): BaseModel<any, T>[] {
|
||||
public getSelectedEntities(): Array<BaseModel<any, T> | PointModel> {
|
||||
if (this.isSelected()) {
|
||||
return [this];
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export class LinkModel<T extends LinkModelListener = LinkModelListener> extends
|
||||
targetPort: PortModel | null;
|
||||
labels: LabelModel[];
|
||||
points: PointModel[];
|
||||
extras: {};
|
||||
extras: any;
|
||||
|
||||
constructor(linkType: string = "default", id?: string) {
|
||||
super(linkType, id);
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import { BaseEvent } from "../BaseEntity";
|
||||
import { BaseModel, BaseModelListener } from "./BaseModel";
|
||||
import { LinkModel, LinkModelListener } from "./LinkModel";
|
||||
import { PortModel } from "./PortModel";
|
||||
import * as _ from "lodash";
|
||||
import { DiagramEngine } from "../DiagramEngine";
|
||||
import { DiagramModel } from "./DiagramModel";
|
||||
import { PointModel } from "./PointModel";
|
||||
|
||||
export class NodeModel extends BaseModel<DiagramModel, BaseModelListener> {
|
||||
export interface NodeModelListener extends BaseModelListener {
|
||||
positionChanged?(event: BaseEvent<NodeModel>): void;
|
||||
}
|
||||
|
||||
export class NodeModel<T extends NodeModelListener = NodeModelListener> extends BaseModel<DiagramModel, T> {
|
||||
x: number;
|
||||
y: number;
|
||||
extras: any;
|
||||
@@ -37,6 +44,12 @@ export class NodeModel extends BaseModel<DiagramModel, BaseModelListener> {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
positionChanged() {
|
||||
this.iterateListeners(
|
||||
(listener: NodeModelListener, event) => listener.positionChanged && listener.positionChanged(event)
|
||||
);
|
||||
}
|
||||
|
||||
getSelectedEntities() {
|
||||
let entities = super.getSelectedEntities();
|
||||
|
||||
|
||||
@@ -255,8 +255,10 @@ export class DiagramWidget extends BaseWidget<DiagramProps, DiagramState> {
|
||||
model.model.x = diagramModel.getGridPosition(model.initialX + amountX / amountZoom);
|
||||
model.model.y = diagramModel.getGridPosition(model.initialY + amountY / amountZoom);
|
||||
|
||||
// update port coordinates as well
|
||||
if (model.model instanceof NodeModel) {
|
||||
model.model.positionChanged();
|
||||
|
||||
// update port coordinates as well
|
||||
_.forEach(model.model.getPorts(), port => {
|
||||
const portCoords = this.props.diagramEngine.getPortCoords(port);
|
||||
port.updateCoords(portCoords);
|
||||
@@ -475,6 +477,7 @@ export class DiagramWidget extends BaseWidget<DiagramProps, DiagramState> {
|
||||
}
|
||||
}}
|
||||
onMouseDown={event => {
|
||||
if (event.nativeEvent.which === 3) return;
|
||||
this.setState({ ...this.state, wasMoved: false });
|
||||
|
||||
diagramEngine.clearRepaintEntities();
|
||||
|
||||
@@ -46,7 +46,7 @@ export class E2EPort extends E2EElement {
|
||||
// click on this port
|
||||
this.page.mouse.move(bounds.x, bounds.y);
|
||||
this.page.mouse.down();
|
||||
|
||||
//
|
||||
let bounds2 = await port.element.boundingBox();
|
||||
|
||||
// drag to other port
|
||||
@@ -72,10 +72,13 @@ export class E2EPort extends E2EElement {
|
||||
this.page.mouse.move(x, y);
|
||||
this.page.mouse.up();
|
||||
|
||||
const link = _.difference(_.flatMap((await this.parent.model()).ports, "links"), currentLinks)[0];
|
||||
if(!link){
|
||||
return null;
|
||||
}
|
||||
|
||||
// get the parent to get the link
|
||||
return await this.helper.link(
|
||||
_.difference(_.flatMap((await this.parent.model()).ports, "links"), currentLinks)[0]
|
||||
);
|
||||
return await this.helper.link(link);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +101,11 @@ export class E2ELink extends E2EElement {
|
||||
async select(): Promise<any> {
|
||||
const point = await this.page.evaluate(id => {
|
||||
const path = document.querySelector(`path[data-linkid="${id}"]`) as SVGPathElement;
|
||||
return path.getPointAtLength(path.getTotalLength() / 2);
|
||||
const point =path.getPointAtLength(path.getTotalLength() / 2);
|
||||
return {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
}
|
||||
}, this.id);
|
||||
await this.page.keyboard.down("Shift");
|
||||
await this.page.mouse.move(point.x, point.y);
|
||||
@@ -115,11 +122,19 @@ export class E2EHelper {
|
||||
}
|
||||
|
||||
async link(id): Promise<E2ELink> {
|
||||
if(!id){
|
||||
throw "Link ID must be valid"
|
||||
}
|
||||
let selector = await this.page.waitForSelector(`path[data-linkid="${id}"]`);
|
||||
return new E2ELink(this, this.page, selector, id);
|
||||
}
|
||||
|
||||
async node(id): Promise<E2ENode> {
|
||||
if(!id){
|
||||
if(!id){
|
||||
throw "Node ID must be valid"
|
||||
}
|
||||
}
|
||||
let selector = await this.page.waitForSelector(`div[data-nodeid="${id}"]`);
|
||||
return new E2ENode(this, this.page, selector, id);
|
||||
}
|
||||
|
||||
@@ -37,8 +37,6 @@ glob.glob(__dirname + "/../../demos/demo-*/index.tsx", {}, (err, files) => {
|
||||
},
|
||||
};
|
||||
|
||||
console.log(config);
|
||||
|
||||
webpack(config, (err, stats) => {
|
||||
if (err || stats.hasErrors()) {
|
||||
// Handle errors here
|
||||
|
||||
@@ -1,60 +1,36 @@
|
||||
import "jest";
|
||||
import * as puppeteer from "puppeteer";
|
||||
import { E2EHelper } from "./E2EHelper";
|
||||
import {E2EHelper} from "./E2EHelper";
|
||||
|
||||
var browser;
|
||||
describe("simple flow test", () => {
|
||||
|
||||
async function itShould(demo: string, directive, test: (page: puppeteer.Page, helper: E2EHelper) => any) {
|
||||
it(directive, async () => {
|
||||
let page = await browser.newPage();
|
||||
await page.goto("file://" + __dirname + "/../../dist/e2e/" + demo + "/index.html");
|
||||
let helper = new E2EHelper(page);
|
||||
await test(page, helper);
|
||||
await page.close();
|
||||
beforeEach(async () => {
|
||||
await page.goto(`file://${__dirname}/../../dist/e2e/demo-simple-flow/index.html`);
|
||||
});
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
if (process.env.CIRCLECI) {
|
||||
console.log("using CircleCI");
|
||||
|
||||
browser = await puppeteer.launch({
|
||||
args: ["--no-sandbox", "--disable-setuid-sandbox"]
|
||||
});
|
||||
} else {
|
||||
browser = await puppeteer.launch({
|
||||
headless: false
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
browser.close();
|
||||
});
|
||||
|
||||
describe("simple flow test", async () => {
|
||||
itShould("demo-simple-flow", "drag link to port adds a link", async (page, helper) => {
|
||||
it("drag link to port adds a link", async () => {
|
||||
// create a new link
|
||||
let node1 = await helper.node("6");
|
||||
let helper = new E2EHelper(page);
|
||||
let node1 = await helper.node("17");
|
||||
let node2 = await helper.node("9");
|
||||
|
||||
let port1 = await node1.port("7");
|
||||
let port1 = await node1.port("18");
|
||||
let port2 = await node2.port("10");
|
||||
|
||||
let newlink = await port1.link(port2);
|
||||
await expect(await newlink.exists()).toBeTruthy();
|
||||
});
|
||||
|
||||
itShould("demo-simple-flow", "drag link to node does not add a link", async (page, helper) => {
|
||||
it("drag link to node does not add a link", async () => {
|
||||
// create a new link
|
||||
let node1 = await helper.node("6");
|
||||
let helper = new E2EHelper(page);
|
||||
let node1 = await helper.node("17");
|
||||
let node2 = await helper.node("9");
|
||||
|
||||
let port1 = await node1.port("7");
|
||||
let port1 = await node1.port("18");
|
||||
|
||||
let node2Bounds = await node2.element.boundingBox();
|
||||
|
||||
let newlink = await port1.linkToPoint(node2Bounds.x, node2Bounds.y);
|
||||
await expect(await newlink.exists()).toBeFalsy();
|
||||
|
||||
await expect(newlink).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,46 +1,21 @@
|
||||
import "jest";
|
||||
import * as puppeteer from "puppeteer";
|
||||
import { E2EHelper } from "./E2EHelper";
|
||||
import {E2EHelper} from "./E2EHelper";
|
||||
|
||||
var browser;
|
||||
describe("simple test", () => {
|
||||
|
||||
async function itShould(demo: string, directive, test: (page: puppeteer.Page, helper: E2EHelper) => any) {
|
||||
it(directive, async () => {
|
||||
let page = await browser.newPage();
|
||||
await page.goto("file://" + __dirname + "/../../dist/e2e/" + demo + "/index.html");
|
||||
let helper = new E2EHelper(page);
|
||||
await test(page, helper);
|
||||
await page.close();
|
||||
beforeAll(async () => {
|
||||
await page.goto(`file://${__dirname}/../../dist/e2e/demo-simple/index.html`)
|
||||
});
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
if (process.env.CIRCLECI) {
|
||||
console.log("using CircleCI");
|
||||
|
||||
browser = await puppeteer.launch({
|
||||
args: ["--no-sandbox", "--disable-setuid-sandbox"]
|
||||
});
|
||||
} else {
|
||||
browser = await puppeteer.launch({
|
||||
headless: false
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
browser.close();
|
||||
});
|
||||
|
||||
describe("simple test", async () => {
|
||||
itShould("demo-simple", "should delete a link and create a new one", async (page, helper) => {
|
||||
it("should delete a link and create a new one", async () => {
|
||||
// get the existing link
|
||||
let helper = new E2EHelper(page);
|
||||
let link = await helper.link("12");
|
||||
await expect(await link.exists()).toBeTruthy();
|
||||
|
||||
// remove it
|
||||
await link.select();
|
||||
await page.keyboard.press("Del");
|
||||
await page.keyboard.press("Delete");
|
||||
|
||||
await expect(await link.exists()).toBeFalsy();
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,6 @@
|
||||
import initStoryshots from "@storybook/addon-storyshots";
|
||||
import "raf/polyfill";
|
||||
|
||||
initStoryshots({ configPath: __dirname });
|
||||
|
||||
|
||||
initStoryshots();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as React from "react";
|
||||
import { storiesOf, addDecorator } from "@storybook/react";
|
||||
import { Toolkit } from "../../src/Toolkit";
|
||||
|
||||
Toolkit.TESTING = true;
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
"compilerOptions": {
|
||||
"suppressExcessPropertyErrors": true,
|
||||
"declaration": true,
|
||||
"outDir": "@types",
|
||||
"target": "es5",
|
||||
"outDir": "dist/@types",
|
||||
"strictNullChecks": false,
|
||||
"sourceMap": true,
|
||||
"skipLibCheck": true,
|
||||
@@ -14,11 +13,16 @@
|
||||
"paths": {
|
||||
"storm-react-diagrams": ["src/main.ts"]
|
||||
},
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015"
|
||||
"es6"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"./src"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"**/*.spec.ts",
|
||||
|
||||
31
tslint.json
31
tslint.json
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"defaultSeverity": "error",
|
||||
"extends": [
|
||||
"tslint:recommended"
|
||||
],
|
||||
"jsRules": {},
|
||||
"rules": {
|
||||
"member-access": false,
|
||||
"comment-format": false,
|
||||
"max-line-length": false,
|
||||
"object-literal-sort-keys": false,
|
||||
"quotemark": [true, "double", "jsx-double"],
|
||||
"arrow-parens": false,
|
||||
"indent": [true, "tabs", 2],
|
||||
"semicolon": false,
|
||||
"object-literal-key-quotes": [true, "as-needed"],
|
||||
"no-var-keyword": false,
|
||||
"jsdoc-format": false,
|
||||
"prefer-const": false,
|
||||
"interface-name": false,
|
||||
"array-type": false,
|
||||
"trailing-comma": false,
|
||||
"one-line": false,
|
||||
"object-literal-shorthand": false,
|
||||
"no-string-literal": false,
|
||||
"ordered-imports": false,
|
||||
"prefer-for-of": false,
|
||||
"no-empty-interface": false
|
||||
},
|
||||
"rulesDirectory": []
|
||||
}
|
||||
@@ -1,22 +1,7 @@
|
||||
const webpack = require("webpack");
|
||||
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
|
||||
var path = require("path");
|
||||
var plugins = [];
|
||||
const production = process.env.NODE_ENV === "production";
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const nodeExternals = require('webpack-node-externals');
|
||||
|
||||
//do we minify it all
|
||||
if (production) {
|
||||
console.log("creating production build");
|
||||
plugins.push(
|
||||
new webpack.DefinePlugin({
|
||||
"process.env.NODE_ENV": '"production"'
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
module.exports =
|
||||
//for building the umd distribution
|
||||
{
|
||||
@@ -27,27 +12,7 @@ module.exports =
|
||||
libraryTarget: "umd",
|
||||
library: "storm-react-diagrams"
|
||||
},
|
||||
externals: {
|
||||
react: {
|
||||
root: "React",
|
||||
commonjs2: "react",
|
||||
commonjs: "react",
|
||||
amd: "react"
|
||||
},
|
||||
"react-dom": {
|
||||
root: "ReactDOM",
|
||||
commonjs2: "react-dom",
|
||||
commonjs: "react-dom",
|
||||
amd: "react-dom"
|
||||
},
|
||||
lodash: {
|
||||
commonjs: "lodash",
|
||||
commonjs2: "lodash",
|
||||
amd: "_",
|
||||
root: "_"
|
||||
}
|
||||
},
|
||||
plugins: plugins,
|
||||
externals: [nodeExternals()],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
@@ -67,16 +32,6 @@ module.exports =
|
||||
devtool: production ? "source-map" : "cheap-module-source-map",
|
||||
mode: production ? "production" : "development",
|
||||
optimization: {
|
||||
minimizer: [
|
||||
// we specify a custom UglifyJsPlugin here to get source maps in production
|
||||
new UglifyJsPlugin({
|
||||
uglifyOptions: {
|
||||
compress: false,
|
||||
ecma: 5,
|
||||
mangle: false
|
||||
},
|
||||
sourceMap: true
|
||||
})
|
||||
]
|
||||
minimizer: [new TerserPlugin()],
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user