Showing preview only (396K chars total). Download the full file or copy to clipboard to get everything.
Repository: projectstorm/react-diagrams
Branch: master
Commit: cd3bdd119376
Files: 229
Total size: 342.7 KB
Directory structure:
gitextract_auqij3jd/
├── .changeset/
│ ├── README.md
│ ├── cold-drinks-unite.md
│ └── config.json
├── .editorconfig
├── .envrc
├── .gitbook.yaml
├── .github/
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ ├── prettier.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── diagrams-demo-gallery/
│ ├── .babelrc.json
│ ├── .storybook/
│ │ ├── main.js
│ │ ├── manager.js
│ │ ├── preview-head.html
│ │ ├── preview.js
│ │ └── theme.js
│ ├── CHANGELOG.md
│ ├── demos/
│ │ ├── 1_SimpleUsage.stories.tsx
│ │ ├── 2_AdvancedUsage.stories.tsx
│ │ ├── 3_Customization.stories.tsx
│ │ ├── 4_Libraries.stories.tsx
│ │ ├── demo-alternative-linking/
│ │ │ ├── CreateLinkState.ts
│ │ │ ├── DefaultState.ts
│ │ │ └── index.tsx
│ │ ├── demo-animation/
│ │ │ └── index.tsx
│ │ ├── demo-canvas-drag/
│ │ │ └── index.tsx
│ │ ├── demo-cloning/
│ │ │ └── index.tsx
│ │ ├── demo-custom-action/
│ │ │ └── index.tsx
│ │ ├── demo-custom-link-label/
│ │ │ ├── EditableLabelFactory.tsx
│ │ │ ├── EditableLabelModel.ts
│ │ │ ├── EditableLabelWidget.tsx
│ │ │ └── index.tsx
│ │ ├── demo-custom-link1/
│ │ │ └── index.tsx
│ │ ├── demo-custom-link2/
│ │ │ └── index.tsx
│ │ ├── demo-custom-node1/
│ │ │ ├── DiamondNodeFactory.tsx
│ │ │ ├── DiamondNodeModel.ts
│ │ │ ├── DiamondNodeWidget.tsx
│ │ │ ├── DiamondPortModel.ts
│ │ │ ├── SimplePortFactory.ts
│ │ │ └── index.tsx
│ │ ├── demo-custom_delete_keys/
│ │ │ └── index.tsx
│ │ ├── demo-dagre/
│ │ │ └── index.tsx
│ │ ├── demo-drag-and-drop/
│ │ │ ├── Application.ts
│ │ │ ├── components/
│ │ │ │ ├── BodyWidget.tsx
│ │ │ │ ├── TrayItemWidget.tsx
│ │ │ │ └── TrayWidget.tsx
│ │ │ └── index.tsx
│ │ ├── demo-dynamic-ports/
│ │ │ └── index.tsx
│ │ ├── demo-grid/
│ │ │ └── index.tsx
│ │ ├── demo-labelled-links/
│ │ │ └── index.tsx
│ │ ├── demo-listeners/
│ │ │ └── index.tsx
│ │ ├── demo-locks/
│ │ │ └── index.tsx
│ │ ├── demo-mutate-graph/
│ │ │ └── index.tsx
│ │ ├── demo-pan-and-zoom/
│ │ │ └── index.tsx
│ │ ├── demo-performance/
│ │ │ └── index.tsx
│ │ ├── demo-right-angles-routing/
│ │ │ └── index.tsx
│ │ ├── demo-serializing/
│ │ │ └── index.tsx
│ │ ├── demo-simple/
│ │ │ └── index.tsx
│ │ ├── demo-simple-flow/
│ │ │ └── index.tsx
│ │ ├── demo-smart-routing/
│ │ │ └── index.tsx
│ │ ├── demo-zoom-to-fit/
│ │ │ └── index.tsx
│ │ ├── demo-zoom-to-fit-nodes/
│ │ │ └── index.tsx
│ │ └── helpers/
│ │ ├── DemoCanvasWidget.tsx
│ │ ├── DemoWorkspaceWidget.tsx
│ │ ├── Helper.tsx
│ │ └── index.css
│ ├── package.json
│ └── tsconfig.json
├── diagrams-demo-project/
│ ├── .babelrc
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ ├── src/
│ │ ├── BodyWidget.tsx
│ │ ├── custom-node-js/
│ │ │ ├── JSCustomNodeFactory.jsx
│ │ │ ├── JSCustomNodeModel.js
│ │ │ └── JSCustomNodeWidget.jsx
│ │ ├── custom-node-ts/
│ │ │ ├── TSCustomNodeFactory.tsx
│ │ │ ├── TSCustomNodeModel.ts
│ │ │ └── TSCustomNodeWidget.tsx
│ │ ├── main.css
│ │ └── main.tsx
│ ├── tsconfig.json
│ └── webpack.config.js
├── docs/
│ ├── README.md
│ ├── about-the-project/
│ │ ├── architecture-questions.md
│ │ └── testing.md
│ ├── customizing/
│ │ ├── README.md
│ │ ├── extending-default-links.md
│ │ ├── nodes.md
│ │ └── ports.md
│ └── getting-started/
│ ├── README.md
│ └── using-the-library.md
├── package.json
├── packages/
│ ├── geometry/
│ │ ├── .npmignore
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── BezierCurve.ts
│ │ │ ├── Bounds.ts
│ │ │ ├── Matrix.ts
│ │ │ ├── Point.ts
│ │ │ ├── Polygon.ts
│ │ │ ├── Rectangle.ts
│ │ │ ├── index.ts
│ │ │ └── toolkit.ts
│ │ ├── tsconfig.json
│ │ └── webpack.config.js
│ ├── react-canvas-core/
│ │ ├── .npmignore
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── CanvasEngine.ts
│ │ │ ├── Toolkit.ts
│ │ │ ├── actions/
│ │ │ │ ├── DeleteItemsAction.ts
│ │ │ │ ├── PanAndZoomCanvasAction.ts
│ │ │ │ └── ZoomCanvasAction.ts
│ │ │ ├── core/
│ │ │ │ ├── AbstractFactory.ts
│ │ │ │ ├── AbstractModelFactory.ts
│ │ │ │ ├── AbstractReactFactory.tsx
│ │ │ │ ├── BaseObserver.ts
│ │ │ │ ├── FactoryBank.ts
│ │ │ │ └── ModelGeometryInterface.ts
│ │ │ ├── core-actions/
│ │ │ │ ├── Action.ts
│ │ │ │ └── ActionEventBus.ts
│ │ │ ├── core-models/
│ │ │ │ ├── BaseEntity.ts
│ │ │ │ ├── BaseModel.ts
│ │ │ │ └── BasePositionModel.ts
│ │ │ ├── core-state/
│ │ │ │ ├── AbstractDisplacementState.ts
│ │ │ │ ├── State.ts
│ │ │ │ └── StateMachine.ts
│ │ │ ├── entities/
│ │ │ │ ├── canvas/
│ │ │ │ │ ├── CanvasModel.ts
│ │ │ │ │ └── CanvasWidget.tsx
│ │ │ │ ├── layer/
│ │ │ │ │ ├── LayerModel.ts
│ │ │ │ │ ├── SmartLayerWidget.tsx
│ │ │ │ │ └── TransformLayerWidget.tsx
│ │ │ │ └── selection/
│ │ │ │ ├── SelectionBoxLayerFactory.tsx
│ │ │ │ ├── SelectionBoxWidget.tsx
│ │ │ │ └── SelectionLayerModel.ts
│ │ │ ├── index.ts
│ │ │ ├── states/
│ │ │ │ ├── DefaultState.ts
│ │ │ │ ├── DragCanvasState.ts
│ │ │ │ ├── MoveItemsState.ts
│ │ │ │ ├── SelectingState.ts
│ │ │ │ └── SelectionBoxState.ts
│ │ │ └── widgets/
│ │ │ └── PeformanceWidget.tsx
│ │ ├── tsconfig.json
│ │ └── webpack.config.js
│ ├── react-diagrams/
│ │ ├── .npmignore
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ └── index.ts
│ │ ├── tsconfig.json
│ │ └── webpack.config.js
│ ├── react-diagrams-core/
│ │ ├── .npmignore
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── DiagramEngine.ts
│ │ │ ├── entities/
│ │ │ │ ├── label/
│ │ │ │ │ ├── LabelModel.ts
│ │ │ │ │ └── LabelWidget.tsx
│ │ │ │ ├── link/
│ │ │ │ │ ├── LinkModel.ts
│ │ │ │ │ ├── LinkWidget.tsx
│ │ │ │ │ └── PointModel.ts
│ │ │ │ ├── link-layer/
│ │ │ │ │ ├── LinkLayerFactory.tsx
│ │ │ │ │ ├── LinkLayerModel.ts
│ │ │ │ │ └── LinkLayerWidget.tsx
│ │ │ │ ├── node/
│ │ │ │ │ ├── NodeModel.ts
│ │ │ │ │ └── NodeWidget.tsx
│ │ │ │ ├── node-layer/
│ │ │ │ │ ├── NodeLayerFactory.tsx
│ │ │ │ │ ├── NodeLayerModel.ts
│ │ │ │ │ └── NodeLayerWidget.tsx
│ │ │ │ └── port/
│ │ │ │ ├── PortModel.ts
│ │ │ │ └── PortWidget.tsx
│ │ │ ├── index.ts
│ │ │ ├── models/
│ │ │ │ └── DiagramModel.ts
│ │ │ └── states/
│ │ │ ├── DefaultDiagramState.ts
│ │ │ ├── DragDiagramItemsState.ts
│ │ │ └── DragNewLinkState.ts
│ │ ├── tsconfig.json
│ │ └── webpack.config.js
│ ├── react-diagrams-defaults/
│ │ ├── .npmignore
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── label/
│ │ │ │ ├── DefaultLabelFactory.tsx
│ │ │ │ ├── DefaultLabelModel.tsx
│ │ │ │ └── DefaultLabelWidget.tsx
│ │ │ ├── link/
│ │ │ │ ├── DefaultLinkFactory.tsx
│ │ │ │ ├── DefaultLinkModel.ts
│ │ │ │ ├── DefaultLinkPointWidget.tsx
│ │ │ │ ├── DefaultLinkSegmentWidget.tsx
│ │ │ │ └── DefaultLinkWidget.tsx
│ │ │ ├── node/
│ │ │ │ ├── DefaultNodeFactory.tsx
│ │ │ │ ├── DefaultNodeModel.ts
│ │ │ │ └── DefaultNodeWidget.tsx
│ │ │ └── port/
│ │ │ ├── DefaultPortFactory.tsx
│ │ │ ├── DefaultPortLabelWidget.tsx
│ │ │ └── DefaultPortModel.ts
│ │ ├── tsconfig.json
│ │ └── webpack.config.js
│ └── react-diagrams-routing/
│ ├── .npmignore
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── jest.config.js
│ ├── package.json
│ ├── src/
│ │ ├── dagre/
│ │ │ └── DagreEngine.ts
│ │ ├── engine/
│ │ │ └── PathFinding.ts
│ │ ├── index.ts
│ │ └── link/
│ │ ├── PathFindingLinkFactory.tsx
│ │ ├── PathFindingLinkModel.ts
│ │ ├── PathFindingLinkWidget.tsx
│ │ ├── RightAngleLinkFactory.tsx
│ │ ├── RightAngleLinkModel.ts
│ │ └── RightAngleLinkWidget.tsx
│ ├── tests/
│ │ └── PathFinding.test.tsx
│ ├── tsconfig.json
│ └── webpack.config.js
├── pnpm-workspace.yaml
├── tsconfig.base.json
├── tsconfig.json
└── webpack.shared.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .changeset/README.md
================================================
# 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)
================================================
FILE: .changeset/cold-drinks-unite.md
================================================
---
'@projectstorm/react-diagrams-defaults': patch
'@projectstorm/react-diagrams-routing': patch
'@projectstorm/react-diagrams-core': patch
'@projectstorm/react-canvas-core': patch
'@projectstorm/react-diagrams-gallery': patch
'@projectstorm/react-diagrams-demo': patch
'@projectstorm/geometry': patch
---
Updated packages to support React v19
================================================
FILE: .changeset/config.json
================================================
{
"$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": []
}
================================================
FILE: .editorconfig
================================================
[*]
indent_style = tab
indent_size = 2
trim_trailing_whitespace = true
# Some exceptions
[{package.json,*.yml}]
indent_style = space
indent_size = 2
================================================
FILE: .envrc
================================================
PATH_add ./node_modules/.bin
================================================
FILE: .gitbook.yaml
================================================
root: ./docs/
structure:
summary: README.md
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
# 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
## What, why and how?
## Feel good image:

================================================
FILE: .github/workflows/prettier.yml
================================================
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}'
================================================
FILE: .github/workflows/release.yml
================================================
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
================================================
FILE: .github/workflows/test.yml
================================================
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
================================================
FILE: .gitignore
================================================
dist
.DS_Store
.idea
.out
*.zip
.env
node_modules
tsconfig.tsbuildinfo
.vscode
================================================
FILE: .prettierignore
================================================
node_modules
dist
.out
================================================
FILE: .prettierrc
================================================
{
"semi": true,
"singleQuote": true,
"useTabs": true,
"printWidth": 120,
"trailingComma": "none"
}
================================================
FILE: CHANGELOG.md
================================================
__V7!__
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:
[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/)
PR: https://github.com/projectstorm/react-diagrams/pull/145
* (refactor) Links completely overhauled
* (feature) Smart Routing
* (feature) Flow support
* (demo) Smart Routing
* (demo) Animated links
* (api) Bootstrapping Improvements
* (feature) add custom properties to all widgets
* (refactor) use BEM for all css
* (feature) Default Link factory hooks
* (tests) e2e tests + helper framework
* (tests) automatically load JEST Snapshots
* (feature) Link labels!
__4.0.0__ [http://dylanv.blog/2018/01/18/storm-react-diagrams-v4-0-0/](https://dylanvorster.com/storm-react-diagrams-v4-0/)
* (refactor) Events system was completely overhauled
* (demo) Custom Link Sizes
* (refactor) Demos are now much more verbose and better managed
* (update) node packages
* (bug) Fix #129
* (feature) Control link creation through ports
* (refactor) Models are now in seperate files
* (refactor) Merged the concept of instance factories and widget factories into one
* (feature) Models can now be cloned at various parts of the model graph
* (demo) Cloning
* (feature) models control isLocked
__3.2.0__ [http://dylanv.blog/2017/11/22/storm-react-diagrams-3-2-0/](https://dylanvorster.com/storm-react-diagrams-3-2-0/)
* (feature) zoom to fit
* added Circle CI tests
* (demo) dagre automatic layouts
* (demo) zoom to fit
* (demo) selection events
* (demo) limit number of points
* (demo) programmatic node updating
* updated dependencies
* (bugs) swapping diagram models in engines
* (bugs) issues with the rendering pipeline #107
* added ci badge to Readme
__3.1.3__
* Refactor links slightly
* use min extension for css
* bump package versions
* export more classes
__3.1.2__
* Hotfix: fix zooming when canvas not in the top left corner
(https://github.com/projectstorm/react-diagrams/pull/88)
__3.1.0__ [http://dylanv.blog/2017/09/15/storm-react-diagrams-3-1-0/](https://dylanvorster.com/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
__3.0.0__ [http://dylanv.blog/2017/09/13/storm-react-diagrams-v3/](https://dylanvorster.com/storm-react-diagrams-3-0/)
* Massive performance updates
* Complete rewrite
* Started a changelog and design documents for each revision
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
See below for guidelines on house keeping:
### Always add a PR
Since the project runs on GitHub, the best way to contribute is to fork and then submit a PR.
You will find a template that you will need to fill out
### Adding new demos
Add a new folder in the ./demos directory and make sure that it is named correctly like the other demos.
A new demo should conform to the standard of either `demo-simple` in which it contains a markdown file that
clearly explains 'whats going on', or the code sample should have very clear comments that almost always should ready
like an instruction manual such as the simple demo.
Finally, you should link up your demo to the __index.tsx__ file in the __demos__ directory. It should be quite
self explanatory on how it works, but ultimately I have a helper method that makes it easy to link source code
to the demo itself, hence the 'require' statements. The third parameter is if you want to place your demo
inside a markdown guide (again: see simple demo for how that is done).
### Make the demo testable
Similar procedure, except link your demo in the __index.tsx__ file sitting in the __tests__ directory.
Running `yarn run test` will fire up jest (hopefully) and then it will render your demo to a snapshot directory
which when run again (for a second time), should compare the output to the newely generated snapshot. Make sure
to commit the updated snapshot file with your PR!
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2016 Storm
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Introduction
[](https://gitter.im/projectstorm/react-diagrams?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://npmjs.org/package/@projectstorm/react-diagrams) [](https://packagequality.com/#?package=storm-react-diagrams)

**DEMO**: [http://projectstorm.cloud/react-diagrams](http://projectstorm.cloud/react-diagrams)
**DOCS \(wip\)** [https://projectstorm.gitbook.io/react-diagrams](https://projectstorm.gitbook.io/react-diagrams)
Docs are currently being worked on, along with a migration path.
## What
A flow & process orientated diagramming library inspired by **Blender**, **Labview** and **Unreal engine**.
* **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.
## Gallery
Example implementation using custom models: \(Dylan's personal code\)


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

## Installing
For all the bells and whistles:
```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.
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.
================================================
FILE: diagrams-demo-gallery/.babelrc.json
================================================
{
"sourceType": "unambiguous",
"presets": [
[
"@babel/preset-env",
{
"targets": {
"chrome": 100,
"safari": 15,
"firefox": 91
}
}
],
"@babel/preset-react",
"@babel/preset-typescript"
],
"plugins": []
}
================================================
FILE: diagrams-demo-gallery/.storybook/main.js
================================================
module.exports = {
stories: ['../demos/*.stories.tsx'],
addons: ['@storybook/addon-actions', '@storybook/addon-webpack5-compiler-babel'],
framework: '@storybook/react-webpack5'
};
================================================
FILE: diagrams-demo-gallery/.storybook/manager.js
================================================
import { addons } from '@storybook/manager-api';
import diagramsTheme from './theme';
addons.setConfig({
theme: diagramsTheme
});
================================================
FILE: diagrams-demo-gallery/.storybook/preview-head.html
================================================
<style>
html, body, #storybook-root {
height: 100%;
}
</style>
================================================
FILE: diagrams-demo-gallery/.storybook/preview.js
================================================
export const parameters = {
layout: 'fullscreen'
};
================================================
FILE: diagrams-demo-gallery/.storybook/theme.js
================================================
import { create } from '@storybook/theming';
export default create({
base: 'dark',
brandTitle: 'STORM React Diagrams',
brandUrl: 'https://github.com/projectstorm/react-diagrams'
});
================================================
FILE: diagrams-demo-gallery/CHANGELOG.md
================================================
# @projectstorm/react-diagrams-gallery
## 7.2.1
### Patch Changes
- adb4415: Fixed the demos
## 7.2.0
### Minor Changes
- 1be4073: Remove setTimeout from demo-dagre to avoid layout breaks
### Patch Changes
- 80285fe: refactor: update lodash imports to use individual functions
- Updated dependencies [09ed60f]
- Updated dependencies [20766f5]
- Updated dependencies [80285fe]
- @projectstorm/react-canvas-core@7.0.3
- @projectstorm/react-diagrams-core@7.0.3
- @projectstorm/react-diagrams-defaults@7.1.3
- @projectstorm/react-diagrams@7.0.4
## 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
================================================
FILE: diagrams-demo-gallery/demos/1_SimpleUsage.stories.tsx
================================================
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;
================================================
FILE: diagrams-demo-gallery/demos/2_AdvancedUsage.stories.tsx
================================================
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;
================================================
FILE: diagrams-demo-gallery/demos/3_Customization.stories.tsx
================================================
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;
================================================
FILE: diagrams-demo-gallery/demos/4_Libraries.stories.tsx
================================================
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;
================================================
FILE: diagrams-demo-gallery/demos/demo-alternative-linking/CreateLinkState.ts
================================================
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;
}
}
================================================
FILE: diagrams-demo-gallery/demos/demo-alternative-linking/DefaultState.ts
================================================
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);
}
})
);
}
}
================================================
FILE: diagrams-demo-gallery/demos/demo-alternative-linking/index.tsx
================================================
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>
);
};
================================================
FILE: diagrams-demo-gallery/demos/demo-animation/index.tsx
================================================
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} />;
};
================================================
FILE: diagrams-demo-gallery/demos/demo-canvas-drag/index.tsx
================================================
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} />;
};
================================================
FILE: diagrams-demo-gallery/demos/demo-cloning/index.tsx
================================================
import createEngine, { DiagramModel, DefaultNodeModel, LinkModel, NodeModel } from '@projectstorm/react-diagrams';
import _forEach from 'lodash/forEach';
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} />;
};
================================================
FILE: diagrams-demo-gallery/demos/demo-custom-action/index.tsx
================================================
import * as React from 'react';
import _forEach from 'lodash/forEach';
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>
);
};
================================================
FILE: diagrams-demo-gallery/demos/demo-custom-link-label/EditableLabelFactory.tsx
================================================
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';
import { JSX } from 'react';
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} />;
}
}
================================================
FILE: diagrams-demo-gallery/demos/demo-custom-link-label/EditableLabelModel.ts
================================================
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;
}
}
================================================
FILE: diagrams-demo-gallery/demos/demo-custom-link-label/EditableLabelWidget.tsx
================================================
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>
);
};
================================================
FILE: diagrams-demo-gallery/demos/demo-custom-link-label/index.tsx
================================================
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>
);
};
================================================
FILE: diagrams-demo-gallery/demos/demo-custom-link1/index.tsx
================================================
import createEngine, {
DiagramModel,
DefaultNodeModel,
DefaultPortModel,
DefaultLinkFactory,
DefaultLinkModel
} from '@projectstorm/react-diagrams';
import * as React from 'react';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
export class AdvancedLinkModel extends DefaultLinkModel {
constructor() {
super({
type: 'advanced',
width: 10
});
}
}
export class AdvancedPortModel extends DefaultPortModel {
createLinkModel(): AdvancedLinkModel | null {
return new AdvancedLinkModel();
}
}
export class AdvancedLinkSegment extends React.Component<{ model: AdvancedLinkModel; path: string }> {
path: SVGPathElement;
circle: SVGCircleElement;
callback: () => any;
percent: number;
handle: any;
mounted: boolean;
constructor(props) {
super(props);
this.percent = 0;
}
componentDidMount() {
this.mounted = true;
this.callback = () => {
if (!this.circle || !this.path) {
return;
}
this.percent += 2;
if (this.percent > 100) {
this.percent = 0;
}
let point = this.path.getPointAtLength(this.path.getTotalLength() * (this.percent / 100.0));
this.circle.setAttribute('cx', '' + point.x);
this.circle.setAttribute('cy', '' + point.y);
if (this.mounted) {
requestAnimationFrame(this.callback);
}
};
requestAnimationFrame(this.callback);
}
componentWillUnmount() {
this.mounted = false;
}
render() {
return (
<>
<path
fill="none"
ref={(ref) => {
this.path = ref;
}}
strokeWidth={this.props.model.getOptions().width}
stroke="rgba(255,0,0,0.5)"
d={this.props.path}
/>
<circle
ref={(ref) => {
this.circle = ref;
}}
r={10}
fill="orange"
/>
</>
);
}
}
export class AdvancedLinkFactory extends DefaultLinkFactory {
constructor() {
super('advanced');
}
generateModel(): AdvancedLinkModel {
return new AdvancedLinkModel();
}
generateLinkSegment(model: AdvancedLinkModel, selected: boolean, path: string) {
return (
<g>
<AdvancedLinkSegment model={model} path={path} />
</g>
);
}
}
/**
*
* Simple link styling demo
*
* @Author kfrajtak
*/
export default () => {
//1) setup the diagram engine
var engine = createEngine();
engine.getLinkFactories().registerFactory(new AdvancedLinkFactory());
// create some nodes
var node1 = new DefaultNodeModel('Source', 'rgb(0,192,255)');
let port1 = node1.addPort(new AdvancedPortModel(false, 'out-1', 'Out thick'));
let port2 = node1.addPort(new DefaultPortModel(false, 'out-2', 'Out default'));
node1.setPosition(100, 100);
var node2 = new DefaultNodeModel('Target', 'rgb(192,255,0)');
var port3 = node2.addPort(new AdvancedPortModel(true, 'in-1', 'In thick'));
var port4 = node2.addPort(new DefaultPortModel(true, 'in-2', 'In default'));
node2.setPosition(300, 100);
var node3 = new DefaultNodeModel('Source', 'rgb(0,192,255)');
node3.addPort(new AdvancedPortModel(false, 'out-1', 'Out thick'));
node3.addPort(new DefaultPortModel(false, 'out-2', 'Out default'));
node3.setPosition(100, 200);
var node4 = new DefaultNodeModel('Target', 'rgb(192,255,0)');
node4.addPort(new AdvancedPortModel(true, 'in-1', 'In thick'));
node4.addPort(new DefaultPortModel(true, 'in-2', 'In default'));
node4.setPosition(300, 200);
var model = new DiagramModel();
model.addAll(port1.link(port3), port2.link(port4));
// add everything else
model.addAll(node1, node2, node3, node4);
// load model into engine
engine.setModel(model);
// render the diagram!
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};
================================================
FILE: diagrams-demo-gallery/demos/demo-custom-link2/index.tsx
================================================
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 { JSX, MouseEvent } from 'react';
import { DefaultLinkPointWidget, DefaultLinkSegmentWidget } from '@projectstorm/react-diagrams-defaults/dist';
import { DiagramEngine } from '@projectstorm/react-diagrams-core/dist';
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 interface AdvancedLinkWWidgetProps {
link: DefaultLinkModel;
diagramEngine: DiagramEngine;
pointAdded?: (point: PointModel, event: MouseEvent) => any;
renderPoints?: boolean;
selected?: (event: MouseEvent) => any;
}
export class AdvancedLinkWidget extends React.Component<AdvancedLinkWWidgetProps> {
generatePoint = (point: PointModel): JSX.Element => {
return (
<DefaultLinkPointWidget
key={point.getID()}
point={point as any}
colorSelected={this.props.link.getOptions().selectedColor ?? ''}
color={this.props.link.getOptions().color}
/>
);
};
generateLink = (path: string, extraProps: any, id: string | number): JSX.Element => {
return (
<DefaultLinkSegmentWidget
key={`link-${id}`}
path={path}
diagramEngine={this.props.diagramEngine}
factory={this.props.diagramEngine.getFactoryForLink(this.props.link)}
link={this.props.link}
extras={extraProps}
/>
);
};
addPointToLink = (event: MouseEvent, index: number) => {
if (
!event.shiftKey &&
!this.props.link.isLocked() &&
this.props.link.getPoints().length - 1 <= this.props.diagramEngine.getMaxNumberPointsPerLink()
) {
const position = this.props.diagramEngine.getRelativeMousePoint(event);
const point = this.props.link.point(position.x, position.y, index);
event.persist();
event.stopPropagation();
this.props.diagramEngine.getActionEventBus().fireAction({
event,
model: point
});
}
};
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 = [];
//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>
);
};
================================================
FILE: diagrams-demo-gallery/demos/demo-custom-node1/DiamondNodeFactory.tsx
================================================
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';
import { JSX } from 'react';
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();
}
}
================================================
FILE: diagrams-demo-gallery/demos/demo-custom-node1/DiamondNodeModel.ts
================================================
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));
}
}
================================================
FILE: diagrams-demo-gallery/demos/demo-custom-node1/DiamondNodeWidget.tsx
================================================
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>
);
}
}
================================================
FILE: diagrams-demo-gallery/demos/demo-custom-node1/DiamondPortModel.ts
================================================
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();
}
}
================================================
FILE: diagrams-demo-gallery/demos/demo-custom-node1/SimplePortFactory.ts
================================================
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);
}
}
================================================
FILE: diagrams-demo-gallery/demos/demo-custom-node1/index.tsx
================================================
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>
);
};
================================================
FILE: diagrams-demo-gallery/demos/demo-custom_delete_keys/index.tsx
================================================
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>
);
};
================================================
FILE: diagrams-demo-gallery/demos/demo-dagre/index.tsx
================================================
import createEngine, {
DiagramModel,
DefaultNodeModel,
DefaultPortModel,
NodeModel,
DagreEngine,
DiagramEngine,
PathFindingLinkFactory
} from '@projectstorm/react-diagrams';
import { useLayoutEffect, useRef } 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
*/
function genDagreEngine() {
return new DagreEngine({
graph: {
rankdir: 'RL',
ranker: 'longest-path',
marginx: 25,
marginy: 25
},
includeLinks: true,
nodeMargin: 25
});
}
function autoDistribute(engine: DiagramEngine) {
const model = engine.getModel();
const dagreEngine = genDagreEngine();
dagreEngine.redistribute(model);
reroute(engine);
engine.repaintCanvas();
}
function autoRefreshLinks(engine: DiagramEngine) {
const model = engine.getModel();
const dagreEngine = genDagreEngine();
dagreEngine.refreshLinks(model);
// only happens if pathfing is enabled (check line 29)
reroute(engine);
engine.repaintCanvas();
}
function reroute(engine: DiagramEngine) {
engine.getLinkFactories().getFactory<PathFindingLinkFactory>(PathFindingLinkFactory.NAME).calculateRoutingMatrix();
}
function DemoWidget(props) {
const engine = props.engine;
useLayoutEffect(() => {
autoDistribute(engine);
}, []);
const redistribute = () => {
autoDistribute(engine);
};
const refreshLinks = () => {
autoRefreshLinks(engine);
};
return (
<DemoWorkspaceWidget
buttons={
<div>
<DemoButton onClick={redistribute}>Re-distribute</DemoButton>
<DemoButton onClick={refreshLinks}>Refresh Links</DemoButton>
</div>
}
>
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
</DemoWorkspaceWidget>
);
}
export default () => {
//1) setup the diagram engine
const engineRef = useRef(createEngine());
let engine = engineRef.current;
//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} />;
};
================================================
FILE: diagrams-demo-gallery/demos/demo-drag-and-drop/Application.ts
================================================
import * as SRD from '@projectstorm/react-diagrams';
/**
* @author Dylan Vorster
*/
export class Application {
protected activeModel: SRD.DiagramModel;
protected diagramEngine: SRD.DiagramEngine;
constructor() {
this.diagramEngine = SRD.default();
this.newModel();
}
public newModel() {
this.activeModel = new SRD.DiagramModel();
this.diagramEngine.setModel(this.activeModel);
//3-A) create a default node
var node1 = new SRD.DefaultNodeModel('Node 1', 'rgb(0,192,255)');
let port = node1.addOutPort('Out');
node1.setPosition(100, 100);
//3-B) create another default node
var node2 = new SRD.DefaultNodeModel('Node 2', 'rgb(192,255,0)');
let port2 = node2.addInPort('In');
node2.setPosition(400, 100);
// link the ports
let link1 = port.link(port2);
this.activeModel.addAll(node1, node2, link1);
}
public getActiveDiagram(): SRD.DiagramModel {
return this.activeModel;
}
public getDiagramEngine(): SRD.DiagramEngine {
return this.diagramEngine;
}
}
================================================
FILE: diagrams-demo-gallery/demos/demo-drag-and-drop/components/BodyWidget.tsx
================================================
import * as React from 'react';
import _keys from 'lodash/keys';
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>
);
}
}
================================================
FILE: diagrams-demo-gallery/demos/demo-drag-and-drop/components/TrayItemWidget.tsx
================================================
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>
);
}
}
================================================
FILE: diagrams-demo-gallery/demos/demo-drag-and-drop/components/TrayWidget.tsx
================================================
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>;
}
}
================================================
FILE: diagrams-demo-gallery/demos/demo-drag-and-drop/index.tsx
================================================
import * as React from 'react';
import { BodyWidget } from './components/BodyWidget';
import { Application } from './Application';
export default () => {
var app = new Application();
return <BodyWidget app={app} />;
};
================================================
FILE: diagrams-demo-gallery/demos/demo-dynamic-ports/index.tsx
================================================
import createEngine, { DiagramModel, DefaultNodeModel, DiagramEngine } from '@projectstorm/react-diagrams';
import _values from 'lodash/values';
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} />;
};
================================================
FILE: diagrams-demo-gallery/demos/demo-grid/index.tsx
================================================
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>
);
};
================================================
FILE: diagrams-demo-gallery/demos/demo-labelled-links/index.tsx
================================================
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>
);
};
================================================
FILE: diagrams-demo-gallery/demos/demo-listeners/index.tsx
================================================
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>
);
};
================================================
FILE: diagrams-demo-gallery/demos/demo-locks/index.tsx
================================================
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>
);
};
================================================
FILE: diagrams-demo-gallery/demos/demo-mutate-graph/index.tsx
================================================
import createEngine, { DiagramModel, DefaultNodeModel, NodeModel } from '@projectstorm/react-diagrams';
import * as React from 'react';
import _values from 'lodash/values';
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} />;
};
================================================
FILE: diagrams-demo-gallery/demos/demo-pan-and-zoom/index.tsx
================================================
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 pan and zoom action, which is intended as a trackpad/mobile
* alternative to the standard ZoomCanvasAction
*/
class CanvasPanAndZoomToggle extends React.Component<any, any> {
render() {
const { engine } = this.props;
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
}
}
export default () => {
/**
* 1) setup the diagram engine
* PandAndZoomCanvasAction and ZoomCanvasAction are mutually exclusive
* If both are enabled, ZoomCanvasAction will override.
*/
var engine = createEngine({
registerDefaultPanAndZoomCanvasAction: true,
registerDefaultZoomCanvasAction: false
});
//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 <CanvasPanAndZoomToggle engine={engine} model={model} />;
};
================================================
FILE: diagrams-demo-gallery/demos/demo-performance/index.tsx
================================================
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';
/**
*
* Simple stress test of the system, shows that it can handle many nodes, and
* retain good performance
*
* @Author Dylan Vorster
*/
export default () => {
//1) setup the diagram engine
var engine = createEngine();
//2) setup the diagram model
var model = new DiagramModel();
for (var i = 0; i < 8; i++) {
for (var j = 0; j < 8; j++) {
generateNodes(model, i * 200, j * 100);
}
}
//5) load model into engine
engine.setModel(model);
//6) render the diagram!
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};
function generateNodes(model: DiagramModel, offsetX: number, offsetY: number) {
//3-A) create a default node
var node1 = new DefaultNodeModel('Node 1', 'rgb(0,192,255)');
var port1 = node1.addOutPort('Out');
node1.setPosition(100 + offsetX, 100 + offsetY);
//3-B) create another default node
var node2 = new DefaultNodeModel('Node 2', 'rgb(192,255,0)');
var port2 = node2.addInPort('In');
node2.setPosition(200 + offsetX, 100 + offsetY);
//3-C) link the 2 nodes together
var link1 = port1.link(port2);
//4) add the models to the root graph
model.addAll(node1, node2, link1);
}
================================================
FILE: diagrams-demo-gallery/demos/demo-right-angles-routing/index.tsx
================================================
import createEngine, {
DiagramModel,
DefaultNodeModel,
DefaultPortModel,
RightAngleLinkFactory,
LinkModel,
RightAngleLinkModel
} from '@projectstorm/react-diagrams';
import * as React from 'react';
import { DemoButton, DemoWorkspaceWidget } from '../helpers/DemoWorkspaceWidget';
import { action } from '@storybook/addon-actions';
import { AbstractModelFactory, CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
// When new link is created by clicking on port the RightAngleLinkModel needs to be returned.
export class RightAnglePortModel extends DefaultPortModel {
createLinkModel(factory?: AbstractModelFactory<LinkModel>) {
return new RightAngleLinkModel();
}
}
export default () => {
// setup the diagram engine
const engine = createEngine();
engine.getLinkFactories().registerFactory(new RightAngleLinkFactory());
// 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 RightAnglePortModel(false, 'out-1', 'Out'));
node1.setPosition(340, 350);
const node2 = new DefaultNodeModel('Node B', 'rgb(255,255,0)');
const port2 = node2.addPort(new RightAnglePortModel(false, 'out-1', 'Out'));
node2.setPosition(240, 80);
const node3 = new DefaultNodeModel('Node C', 'rgb(192,255,255)');
const port3 = node3.addPort(new RightAnglePortModel(true, 'in-1', 'In'));
node3.setPosition(540, 180);
const node4 = new DefaultNodeModel('Node D', 'rgb(192,0,255)');
const port4 = node4.addPort(new RightAnglePortModel(true, 'in-1', 'In'));
node4.setPosition(95, 185);
// 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, link1, link2);
// load model into engine and render
engine.setModel(model);
return (
<DemoWorkspaceWidget
buttons={
<DemoButton
onClick={() => {
action('Serialized Graph')(JSON.stringify(model.serialize(), null, 2));
}}
>
Serialize Graph
</DemoButton>
}
>
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
</DemoWorkspaceWidget>
);
};
================================================
FILE: diagrams-demo-gallery/demos/demo-serializing/index.tsx
================================================
import createEngine, { DiagramModel, DefaultNodeModel, DefaultLabelModel } from '@projectstorm/react-diagrams';
import * as React from 'react';
import { DemoButton, DemoWorkspaceWidget } from '../helpers/DemoWorkspaceWidget';
import { action } from '@storybook/addon-actions';
import * as beautify from 'json-beautify';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
export default () => {
//1) setup the diagram engine
var engine = createEngine();
//2) setup the diagram model
var model = new DiagramModel();
//3-A) create a default node
var node1 = new DefaultNodeModel('Node 1', 'rgb(0,192,255)');
var port1 = node1.addOutPort('Out');
node1.setPosition(100, 100);
//3-B) create another default node
var node2 = new DefaultNodeModel('Node 2', 'rgb(192,255,0)');
var port2 = node2.addInPort('In');
node2.setPosition(400, 100);
//3-C) link the 2 nodes together
var link1 = port1.link(port2);
link1.addLabel(new DefaultLabelModel({ label: 'Label' }));
//4) add the models to the root graph
model.addAll(node1, node2, link1);
//5) load model into engine
engine.setModel(model);
//!------------- SERIALIZING ------------------
var str = JSON.stringify(model.serialize());
//!------------- DESERIALIZING ----------------
var model2 = new DiagramModel();
model2.deserializeModel(JSON.parse(str), engine);
engine.setModel(model2);
return (
<DemoWorkspaceWidget
buttons={
<DemoButton
onClick={() => {
action('Serialized Graph')(beautify(model2.serialize(), null, 2, 80));
}}
>
Serialize Graph
</DemoButton>
}
>
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
</DemoWorkspaceWidget>
);
};
================================================
FILE: diagrams-demo-gallery/demos/demo-simple/index.tsx
================================================
import createEngine, { DiagramModel, DefaultNodeModel, DefaultLinkModel } from '@projectstorm/react-diagrams';
import * as React from 'react';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
export default () => {
//1) setup the diagram engine
var engine = createEngine();
//2) setup the diagram model
var model = new DiagramModel();
//3-A) create a default node
var node1 = new DefaultNodeModel({
name: 'Node 1',
color: 'rgb(0,192,255)'
});
node1.setPosition(100, 100);
let port1 = node1.addOutPort('Out');
//3-B) create another default node
var node2 = new DefaultNodeModel('Node 2', 'rgb(192,255,0)');
let port2 = node2.addInPort('In');
node2.setPosition(400, 100);
// link the ports
let link1 = port1.link<DefaultLinkModel>(port2);
link1.getOptions().testName = 'Test';
link1.addLabel('Hello World!');
//4) add the models to the root graph
model.addAll(node1, node2, link1);
//5) load model into engine
engine.setModel(model);
//6) render the diagram!
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};
================================================
FILE: diagrams-demo-gallery/demos/demo-simple-flow/index.tsx
================================================
import createEngine, { DiagramModel, DefaultNodeModel, DefaultDiagramState } from '@projectstorm/react-diagrams';
import * as React from 'react';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
export default () => {
//1) setup the diagram engine
var engine = createEngine();
// ############################################ MAGIC HAPPENS HERE
const state = engine.getStateMachine().getCurrentState();
if (state instanceof DefaultDiagramState) {
state.dragNewLink.config.allowLooseLinks = false;
}
// ############################################ MAGIC HAPPENS HERE
//2) setup the diagram model
var model = new DiagramModel();
//3-A) create a default node
var node1 = new DefaultNodeModel('Node 1', 'rgb(0,192,255)');
var port1 = node1.addOutPort('Out');
node1.setPosition(100, 100);
//3-B) create another default node
var node2 = new DefaultNodeModel('Node 2', 'rgb(192,255,0)');
var port2 = node2.addInPort('In');
node2.setPosition(400, 100);
//3-C) link the 2 nodes together
var link1 = port1.link(port2);
//3-D) create an orphaned node
var node3 = new DefaultNodeModel('Node 3', 'rgb(0,192,255)');
node3.addOutPort('Out');
node3.setPosition(100, 200);
//4) add the models to the root graph
model.addAll(node1, node2, node3, link1);
//5) load model into engine
engine.setModel(model);
//6) render the diagram!
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};
================================================
FILE: diagrams-demo-gallery/demos/demo-smart-routing/index.tsx
================================================
import createEngine, {
DiagramModel,
DefaultNodeModel,
DefaultPortModel,
PathFindingLinkFactory,
DefaultLabelModel
} from '@projectstorm/react-diagrams';
import * as React from 'react';
import { DemoButton, DemoWorkspaceWidget } from '../helpers/DemoWorkspaceWidget';
import { action } from '@storybook/addon-actions';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
export default () => {
// setup the diagram engine
const engine = createEngine();
// setup the diagram model
const model = new DiagramModel();
// create four nodes in a way that straight links wouldn't work
const node1 = new DefaultNodeModel('Node A', 'rgb(0,192,255)');
const port1 = node1.addPort(new DefaultPortModel(false, 'out-1', 'Out'));
node1.setPosition(340, 350);
const node2 = new DefaultNodeModel('Node B', 'rgb(255,255,0)');
const port2 = node2.addPort(new DefaultPortModel(false, 'out-1', 'Out'));
node2.setPosition(240, 80);
const node3 = new DefaultNodeModel('Node C', 'rgb(192,255,255)');
const port3 = node3.addPort(new DefaultPortModel(true, 'in-1', 'In'));
node3.setPosition(540, 180);
const node4 = new DefaultNodeModel('Node D', 'rgb(192,0,255)');
const port4 = node4.addPort(new DefaultPortModel(true, 'in-1', 'In'));
node4.setPosition(95, 185);
const node5 = new DefaultNodeModel('Node E', 'rgb(192,255,0)');
node5.setPosition(250, 180);
const pathfinding = engine.getLinkFactories().getFactory<PathFindingLinkFactory>(PathFindingLinkFactory.NAME);
// linking things together (specifically using the pathfinding link)
const link1 = port1.link(port4, pathfinding);
const link2 = port2.link(port3, pathfinding);
link1.addLabel(
new DefaultLabelModel({
label: 'I am a label!',
offsetY: 20
})
);
// add all to the main model
model.addAll(node1, node2, node3, node4, node5, link1, link2);
// load model into engine and render
engine.setModel(model);
return (
<DemoWorkspaceWidget
buttons={
<DemoButton
onClick={() => {
action('Serialized Graph')(JSON.stringify(model.serialize(), null, 2));
}}
>
Serialize Graph
</DemoButton>
}
>
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
</DemoWorkspaceWidget>
);
};
================================================
FILE: diagrams-demo-gallery/demos/demo-zoom-to-fit/index.tsx
================================================
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';
/**
*
* Simple stress test of the system plus zoom to fit function
*
* @Author Dylan Vorster
*/
export default () => {
//1) setup the diagram engine
var engine = createEngine();
//2) setup the diagram model
var model = new DiagramModel();
for (var i = 0; i < 8; i++) {
for (var j = 0; j < 8; j++) {
generateNodes(model, i * 200, j * 100);
}
}
//5) load model into engine
engine.setModel(model);
//6) render the diagram!
return (
<DemoWorkspaceWidget buttons={<DemoButton onClick={() => engine.zoomToFit()}>Zoom to fit</DemoButton>}>
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
</DemoWorkspaceWidget>
);
};
function generateNodes(model: DiagramModel, offsetX: number, offsetY: number) {
//3-A) create a default node
var node1 = new DefaultNodeModel('Node 1', 'rgb(0,192,255)');
var port1 = node1.addOutPort('Out');
node1.setPosition(100 + offsetX, 100 + offsetY);
//3-B) create another default node
var node2 = new DefaultNodeModel('Node 2', 'rgb(192,255,0)');
var port2 = node2.addInPort('In');
node2.setPosition(200 + offsetX, 100 + offsetY);
//3-C) link the 2 nodes together
var link1 = port1.link(port2);
//4) add the models to the root graph
model.addAll(node1, node2, link1);
}
================================================
FILE: diagrams-demo-gallery/demos/demo-zoom-to-fit-nodes/index.tsx
================================================
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';
/**
*
* Simple stress test of the system plus zoom to fit function
*
* @Author Dylan Vorster
*/
export default () => {
//1) setup the diagram engine
var engine = createEngine();
//2) setup the diagram model
var model = new DiagramModel();
for (var i = 0; i < 8; i++) {
for (var j = 0; j < 8; j++) {
generateNodes(model, i * 200, j * 100);
}
}
//5) load model into engine
engine.setModel(model);
//6) render the diagram!
return (
<DemoWorkspaceWidget
buttons={<DemoButton onClick={() => engine.zoomToFitSelectedNodes(50)}>Zoom to fit</DemoButton>}
>
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
</DemoWorkspaceWidget>
);
};
function generateNodes(model: DiagramModel, offsetX: number, offsetY: number) {
//3-A) create a default node
var node1 = new DefaultNodeModel('Node 1', 'rgb(0,192,255)');
var port1 = node1.addOutPort('Out');
node1.setPosition(100 + offsetX, 100 + offsetY);
//3-B) create another default node
var node2 = new DefaultNodeModel('Node 2', 'rgb(192,255,0)');
var port2 = node2.addInPort('In');
node2.setPosition(200 + offsetX, 100 + offsetY);
//3-C) link the 2 nodes together
var link1 = port1.link(port2);
//4) add the models to the root graph
model.addAll(node1, node2, link1);
}
================================================
FILE: diagrams-demo-gallery/demos/helpers/DemoCanvasWidget.tsx
================================================
import * as React from 'react';
import styled from '@emotion/styled';
import { css, Global } from '@emotion/react';
export interface DemoCanvasWidgetProps {
color?: string;
background?: string;
}
namespace S {
export const Container = styled.div<{ color: string; background: string }>`
height: 100%;
background-color: ${(p) => p.background};
background-size: 50px 50px;
display: flex;
> * {
height: 100%;
min-height: 100%;
width: 100%;
}
background-image: linear-gradient(
0deg,
transparent 24%,
${(p) => p.color} 25%,
${(p) => p.color} 26%,
transparent 27%,
transparent 74%,
${(p) => p.color} 75%,
${(p) => p.color} 76%,
transparent 77%,
transparent
),
linear-gradient(
90deg,
transparent 24%,
${(p) => p.color} 25%,
${(p) => p.color} 26%,
transparent 27%,
transparent 74%,
${(p) => p.color} 75%,
${(p) => p.color} 76%,
transparent 77%,
transparent
);
`;
export const Expand = css`
html,
body,
#root {
height: 100%;
}
`;
}
export class DemoCanvasWidget extends React.Component<React.PropsWithChildren<DemoCanvasWidgetProps>> {
render() {
return (
<>
<Global styles={S.Expand} />
<S.Container
background={this.props.background || 'rgb(60, 60, 60)'}
color={this.props.color || 'rgba(255,255,255, 0.05)'}
>
{this.props.children}
</S.Container>
</>
);
}
}
================================================
FILE: diagrams-demo-gallery/demos/helpers/DemoWorkspaceWidget.tsx
================================================
import * as React from 'react';
import styled from '@emotion/styled';
export interface DemoWorkspaceWidgetProps {
buttons?: any;
}
namespace S {
export const Toolbar = styled.div`
padding: 5px;
display: flex;
flex-shrink: 0;
`;
export const Content = styled.div`
flex-grow: 1;
height: 100%;
`;
export const Container = styled.div`
background: black;
display: flex;
flex-direction: column;
height: 100%;
border-radius: 5px;
overflow: hidden;
`;
}
export const DemoButton = styled.button`
background: rgb(60, 60, 60);
font-size: 14px;
padding: 5px 10px;
border: none;
color: white;
outline: none;
cursor: pointer;
margin: 2px;
border-radius: 3px;
&:hover {
background: rgb(0, 192, 255);
}
`;
export class DemoWorkspaceWidget extends React.Component<React.PropsWithChildren<DemoWorkspaceWidgetProps>> {
render() {
return (
<S.Container>
<S.Toolbar>{this.props.buttons}</S.Toolbar>
<S.Content>{this.props.children}</S.Content>
</S.Container>
);
}
}
================================================
FILE: diagrams-demo-gallery/demos/helpers/Helper.tsx
================================================
import * as React from 'react';
export class Helper {
/**
* Logs the mouse position in the console, but overlays a div that consumes all events
* since the actual story book stories are rendered as an iFrame.
*/
static logMousePosition() {
let element = window.parent.document.createElement('mouse-position');
element.style.position = 'absolute';
element.style.top = '0px';
element.style.left = '0px';
element.style.bottom = '0px';
element.style.right = '0px';
element.style.zIndex = '10';
window.parent.document.body.appendChild(element);
window.parent.window.addEventListener('mousemove', (event) => {
console.clear();
console.log(event.clientX, event.clientY);
});
}
}
================================================
FILE: diagrams-demo-gallery/demos/helpers/index.css
================================================
html,
body,
#storybook-root {
height: 100%;
padding: 0;
margin: 0;
}
================================================
FILE: diagrams-demo-gallery/package.json
================================================
{
"name": "@projectstorm/react-diagrams-gallery",
"version": "7.2.1",
"author": "dylanvorster",
"license": "MIT",
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/projectstorm/react-diagrams.git"
},
"scripts": {
"start": "pnpm storybook dev",
"storybook:build": "pnpm storybook build -c .storybook -o .out"
},
"keywords": [
"web",
"diagram",
"diagrams",
"react",
"typescript",
"flowchart",
"simple",
"links",
"nodes"
],
"dependencies": {
"@projectstorm/react-canvas-core": "workspace:*",
"@projectstorm/react-diagrams": "workspace:*",
"@projectstorm/react-diagrams-core": "workspace:*",
"@projectstorm/react-diagrams-defaults": "workspace:*",
"gsap": "^3.12.2",
"json-beautify": "^1.1.1",
"lodash": "^4.17.21",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@babel/preset-env": "^7.26.9",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.27.0",
"@storybook/addon-actions": "^8.6.9",
"@storybook/addon-webpack5-compiler-babel": "^3.0.5",
"@storybook/manager-api": "^8.6.10",
"@storybook/preview-api": "^8.6.10",
"@storybook/react": "^8.6.9",
"@storybook/react-webpack5": "^8.6.9",
"@storybook/storybook-deployer": "^2.8.16",
"@storybook/theming": "^8.6.9",
"@types/lodash": "^4.14.200",
"@types/react": "^19.0.12",
"@types/react-dom": "^19.0.4",
"storybook": "^8.6.9"
}
}
================================================
FILE: diagrams-demo-gallery/tsconfig.json
================================================
{
"compileOnSave": false,
"compilerOptions": {
"esModuleInterop": true,
"declaration": true,
"composite": true,
"incremental": true,
"strictNullChecks": false,
"sourceMap": true,
"skipLibCheck": true,
"jsx": "react",
"target": "ES6",
"module": "commonjs",
"strict": false,
"lib": [
"DOM",
"ES6"
]
},
"include": [
"demos"
]
}
================================================
FILE: diagrams-demo-project/.babelrc
================================================
{
"presets": [
["@babel/preset-env",{
"targets": {
"node": true
}
}],
"@babel/preset-react"
]
}
================================================
FILE: diagrams-demo-project/CHANGELOG.md
================================================
# @projectstorm/react-diagrams-demo
## 7.0.4
### Patch Changes
- @projectstorm/react-diagrams@7.0.4
## 7.0.3
### Patch Changes
- 66c687a: Upgrade all dependencies and fix Storybook after upgrade
- @projectstorm/react-diagrams@7.0.3
## 7.0.2
### Patch Changes
- b8a4cbd: Inline sources in sourcemap
- Updated dependencies [b8a4cbd]
- @projectstorm/react-diagrams@7.0.2
## 7.0.1
### Patch Changes
- @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@7.0.0
================================================
FILE: diagrams-demo-project/README.md
================================================
# Project STORM > React diagrams > Demo Project

In this repo you will find a simple webpack-dev-server project
that shows how to get started with the library.
It contains an example of how to implement a custom node in both Vanilla ES6 as-well
as typescript (the recommended way).
Simply run `yarn start` which will also open your browser.
================================================
FILE: diagrams-demo-project/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Project STORM | React Diagrams demo</title>
<script src="bundle.js"></script>
</head>
<body>
<div id="application"/>
</body>
</html>
================================================
FILE: diagrams-demo-project/package.json
================================================
{
"name": "@projectstorm/react-diagrams-demo",
"version": "7.0.4",
"author": "dylanvorster",
"license": "MIT",
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/projectstorm/react-diagrams.git"
},
"scripts": {
"start": "./node_modules/.bin/webpack serve --open"
},
"keywords": [
"web",
"diagram",
"diagrams",
"react",
"typescript",
"flowchart",
"simple",
"links",
"nodes"
],
"main": "./dist/index.js",
"typings": "./dist/@types/index",
"dependencies": {
"@projectstorm/react-diagrams": "workspace:*",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@babel/core": "^7.26.10",
"@babel/preset-react": "^7.26.3",
"@types/react": "^19.0.12",
"@types/react-dom": "^19.0.4",
"babel-loader": "^9.1.3",
"css-loader": "^6.8.1",
"html-webpack-plugin": "^5.5.3",
"source-map-loader": "^4.0.1",
"style-loader": "^3.3.3",
"webpack": "^5.88.2",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
}
}
================================================
FILE: diagrams-demo-project/src/BodyWidget.tsx
================================================
import * as React from 'react';
import { DiagramEngine, CanvasWidget } from '@projectstorm/react-diagrams';
export interface BodyWidgetProps {
engine: DiagramEngine;
}
export class BodyWidget extends React.Component<BodyWidgetProps> {
render() {
return <CanvasWidget className="diagram-container" engine={this.props.engine} />;
}
}
================================================
FILE: diagrams-demo-project/src/custom-node-js/JSCustomNodeFactory.jsx
================================================
import * as React from 'react';
import { JSCustomNodeModel } from './JSCustomNodeModel';
import { JSCustomNodeWidget } from './JSCustomNodeWidget';
import { AbstractReactFactory } from '@projectstorm/react-diagrams';
export class JSCustomNodeFactory extends AbstractReactFactory {
constructor() {
super('js-custom-node');
}
generateModel(event) {
return new JSCustomNodeModel();
}
generateReactWidget(event) {
return <JSCustomNodeWidget engine={this.engine} node={event.model} />;
}
}
================================================
FILE: diagrams-demo-project/src/custom-node-js/JSCustomNodeModel.js
================================================
import { DefaultPortModel, NodeModel } from '@projectstorm/react-diagrams';
/**
* Example of a custom model using pure javascript
*/
export class JSCustomNodeModel extends NodeModel {
constructor(options = {}) {
super({
...options,
type: 'js-custom-node'
});
this.color = options.color || { options: 'red' };
// setup an in and out port
this.addPort(
new DefaultPortModel({
in: true,
name: 'in'
})
);
this.addPort(
new DefaultPortModel({
in: false,
name: 'out'
})
);
}
serialize() {
return {
...super.serialize(),
color: this.color
};
}
deserialize(ob, engine) {
super.deserialize(ob, engine);
this.color = ob.color;
}
}
================================================
FILE: diagrams-demo-project/src/custom-node-js/JSCustomNodeWidget.jsx
================================================
import * as React from 'react';
import { PortWidget } from '@projectstorm/react-diagrams';
export class JSCustomNodeWidget extends React.Component {
render() {
return (
<div className="custom-node">
<PortWidget engine={this.props.engine} port={this.props.node.getPort('in')}>
<div className="circle-port" />
</PortWidget>
<PortWidget engine={this.props.engine} port={this.props.node.getPort('out')}>
<div className="circle-port" />
</PortWidget>
<div className="custom-node-color" style={{ backgroundColor: this.props.node.color }} />
</div>
);
}
}
================================================
FILE: diagrams-demo-project/src/custom-node-ts/TSCustomNodeFactory.tsx
================================================
import * as React from 'react';
import { TSCustomNodeModel } from './TSCustomNodeModel';
import { TSCustomNodeWidget } from './TSCustomNodeWidget';
import { AbstractReactFactory } from '@projectstorm/react-diagrams';
import { DiagramEngine } from '@projectstorm/react-diagrams';
import { JSX } from 'react';
export class TSCustomNodeFactory extends AbstractReactFactory<TSCustomNodeModel, DiagramEngine> {
constructor() {
super('ts-custom-node');
}
generateModel(initialConfig) {
return new TSCustomNodeModel();
}
generateReactWidget(event): JSX.Element {
return <TSCustomNodeWidget engine={this.engine as DiagramEngine} node={event.model} />;
}
}
================================================
FILE: diagrams-demo-project/src/custom-node-ts/TSCustomNodeModel.ts
================================================
import { BaseModelOptions, DefaultPortModel, NodeModel } from '@projectstorm/react-diagrams';
export interface TSCustomNodeModelOptions extends BaseModelOptions {
color?: string;
}
export class TSCustomNodeModel extends NodeModel {
color: string;
constructor(options: TSCustomNodeModelOptions = {}) {
super({
...options,
type: 'ts-custom-node'
});
this.color = options.color || 'red';
// setup an in and out port
this.addPort(
new DefaultPortModel({
in: true,
name: 'in'
})
);
this.addPort(
new DefaultPortModel({
in: false,
name: 'out'
})
);
}
serialize() {
return {
...super.serialize(),
color: this.color
};
}
deserialize(event): void {
super.deserialize(event);
this.color = event.data.color;
}
}
================================================
FILE: diagrams-demo-project/src/custom-node-ts/TSCustomNodeWidget.tsx
================================================
import * as React from 'react';
import { DiagramEngine, PortWidget } from '@projectstorm/react-diagrams';
import { TSCustomNodeModel } from './TSCustomNodeModel';
export interface TSCustomNodeWidgetProps {
node: TSCustomNodeModel;
engine: DiagramEngine;
}
export interface TSCustomNodeWidgetState {}
export class TSCustomNodeWidget extends React.Component<TSCustomNodeWidgetProps, TSCustomNodeWidgetState> {
constructor(props: TSCustomNodeWidgetProps) {
super(props);
this.state = {};
}
render() {
return (
<div className="custom-node">
<PortWidget engine={this.props.engine} port={this.props.node.getPort('in')}>
<div className="circle-port" />
</PortWidget>
<PortWidget engine={this.props.engine} port={this.props.node.getPort('out')}>
<div className="circle-port" />
</PortWidget>
<div className="custom-node-color" style={{ backgroundColor: this.props.node.color }} />
</div>
);
}
}
================================================
FILE: diagrams-demo-project/src/main.css
================================================
*{
margin: 0;
padding: 0;
}
html, body, #application{
height: 100%;
overflow: hidden;
}
.diagram-container{
background: #333333;
width: 100%;
height: 100%;
}
.custom-node{
border: solid 2px gray;
border-radius: 5px;
width: 50px;
height: 50px;
display: flex;
align-items: flex-start;
justify-content: space-between;
position: relative;
}
.custom-node-color{
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 20px;
transform: translate(-50%, -50%);
border-radius: 10px;
}
.circle-port{
width: 12px;
height: 12px;
margin: 2px;
border-radius: 4px;
background: darkgray;
cursor: pointer;
}
.circle-port:hover{
background: mediumpurple;
}
================================================
FILE: diagrams-demo-project/src/main.tsx
================================================
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import './main.css';
import createEngine, { DefaultLinkModel, DiagramModel } from '@projectstorm/react-diagrams';
import { JSCustomNodeFactory } from './custom-node-js/JSCustomNodeFactory';
import { TSCustomNodeFactory } from './custom-node-ts/TSCustomNodeFactory';
import { JSCustomNodeModel } from './custom-node-js/JSCustomNodeModel';
import { TSCustomNodeModel } from './custom-node-ts/TSCustomNodeModel';
import { BodyWidget } from './BodyWidget';
// create an instance of the engine
const engine = createEngine();
// register the two engines
engine.getNodeFactories().registerFactory(new JSCustomNodeFactory() as any);
engine.getNodeFactories().registerFactory(new TSCustomNodeFactory());
// create a diagram model
const model = new DiagramModel();
//####################################################
// now create two nodes of each type, and connect them
const node1 = new JSCustomNodeModel({ color: 'rgb(192,255,0)' });
node1.setPosition(50, 50);
const node2 = new TSCustomNodeModel({ color: 'rgb(0,192,255)' });
node2.setPosition(200, 50);
const link1 = new DefaultLinkModel();
link1.setSourcePort(node1.getPort('out'));
link1.setTargetPort(node2.getPort('in'));
model.addAll(node1, node2, link1);
//####################################################
// install the model into the engine
engine.setModel(model);
document.addEventListener('DOMContentLoaded', () => {
const root = createRoot(document.querySelector('#application'));
root.render(<BodyWidget engine={engine} />);
});
================================================
FILE: diagrams-demo-project/tsconfig.json
================================================
{
"compileOnSave": false,
"compilerOptions": {
"declaration": false,
"jsx": "react",
"allowJs": true,
"target": "es6",
"module": "CommonJS"
},
"include": [
"./src"
]
}
================================================
FILE: diagrams-demo-project/webpack.config.js
================================================
const path = require('path');
const production = process.env.NODE_ENV === 'production';
const TerserPlugin = require('terser-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: production ? 'production' : 'development',
devtool: 'inline-source-map',
entry: './src/main.tsx',
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx']
},
optimization: {
minimizer: [
new TerserPlugin({
parallel: true,
terserOptions: {
ecma: 6
}
})
]
},
plugins: [
new HtmlWebpackPlugin({
template: 'index.html'
})
],
module: {
rules: [
{
enforce: 'pre',
test: /\.js$/,
loader: 'source-map-loader'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.tsx?$/,
loader: 'ts-loader'
}
]
},
devServer: {
client: {
overlay: true
},
hot: false,
compress: true
}
};
================================================
FILE: docs/README.md
================================================
# Table of contents
* [Introduction](README.md)
* [Getting Started](getting-started/README.md)
* [Using the library](getting-started/using-the-library.md)
* [Customizing](customizing/README.md)
* [Extending DefaultLinkModel](customizing/extending-default-links.md)
* [Custom Nodes](customizing/nodes.md)
* [Custom Ports](customizing/ports.md)
* [About the project](about-the-project/README.md)
* [Testing](about-the-project/testing.md)
* [Architecture Questions](about-the-project/architecture-questions.md)
================================================
FILE: docs/about-the-project/architecture-questions.md
================================================
# Architecture Questions
Here I will try to answer any questions relating to the design of the system
## What was the inspiration for this library?
Joint JS \(a fantastic library\) + my need for rich HTML nodes + LabView + Blender Composite sub system
## Why render the nodes as HTML Elements and not SVG's?
My original requirement for this library stemmed from the requirement of wanting HTML nodes that would allow me to embed rich controls such as input fields, dropdowns and have the system treat such nodes as first class citizens. I originally tried to make this work in JointJS, but ran into a number of problems of which this was a relatively big one.
JointJS does allow you to do this, but at the time of writing this library originally, I was having a lot of trouble to make it work exactly like I needed it, and therefore decided from the very beginning that I would attempt this with an HTML first mindset.
## Why Typescript?
Firstly, because it can transpile into any level of ECMAScript. This means that I don't need to break our the refactor tractor every time ECMAScript decides it wants to add features which it should have done years ago.
I also ported it to Typescript to accommodate the heavy architectural changes I was starting to make. Since porting the library to typescript, and seeing the project explode in size and complexity, I consider this the best decision made with regard to this library so far.
Porting to typescript also afforded us a set of powerful features such as generics and static analysis that all the project contributors have made exclusive use of.
Typescript is <3 typescript is life.
## Why not Flow instead of Typescript?
At the time when I first started evaluating languages that could transpile to ECMAScript, I was not so sold on the supporting environment surrounding flow, and found that there was better tooling to support typescript, they are ultimately trying to do the same thing though, and I guess in the end, typescript just made more sense.
## Why React ?
React is really efficient at rendering and managing HTML in a declarative manner. React has also become one of the bigger industry standards and has a rich ecosystem that plays really well with typescript. Apart from these notable points, I am really fond of React and wanted a diagramming library that takes full advantage of it, and makes it easy for engineers to use its power as well, when extending this library.
## Why cant the Default models and widgets do this or that ?
They are intended to illustrate **how** to use this library and act as a good starting point to extend and show the capability. Ultimately I designed this library to be completely pluggable in a way that you can use it as a library and not a framework. If the default widgets are not good enough, then a good place to start is with creating your own models/factories/widgets.
## Model vs Widget
For those that are new to [Scene Graphs](https://en.wikipedia.org/wiki/Scene_graph) or are not familiar with concepts such as [MVC](https://en.wikipedia.org/wiki/Model–view–controller), this library represents your entire graph as a model. The model is a traversable graph that represents the nodes and links between them in a virtual manner. Your program \(aka the business logic/layer\) can mutate this model imperatively or store snapshots decoratively of the complete model \(via serialization\) and then the engine and react widgets will take care of the rendering. For this reason every model in the library is represented by a widget, and the factories glue it all together.
## How do I make my own elements?
Take a look at the **demos** directory, with specific attention to the **DefaultNodeWidget**
That being said, the demos directory is an _example_ of how you can create your own elements. A number of people want to use the defaults as is, which is cool, but is recommended to create your own models/factories/widgets.
## How do I use the library?
Take a look at the demo folders, they have simple and complex examples of the complete usage.
A good example of a real-world example is Demo 5
================================================
FILE: docs/about-the-project/testing.md
================================================
# Testing
## End to end testing
To test the functionality of the library, we make use of e2e tests \(end to end tests\). In this library, we spin up a headless chrome using puppeteer and interactively and programmatically tell the mouse pointer to click and drag on various elements while making assertions along the way.
We use Jest for the assertions and the interactivity is handled by puppeteer. Due to the laborious nature of writing e2e tests, there is a helper method that is provided in each test that makes interacting with the diagrams a lot easier. Using this helper, you 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.
================================================
FILE: docs/customizing/README.md
================================================
# Customizing
Almost all components in react-diagrams are customizable. While some customization is better documented than others, the best way to learn about customization is through the examples in the codebase and by looking at the type annotations that come with the library.
Most UI customization can be done through extending existing base classes. While node, port, and link have different data models, they share the same customization pattern:
- they need a **model factory** extended off `AbstractModelFactory`, and that factory needs to be registered with the engine under a different model type
- optionally, if you data model is different from the default, you can extend existing base classes such as `NodeModel`, `PortModel`, `DefaultLinkModel`, etc.
- they need to have a **custom component** which renders using its default or customized data model. Some component such as the port can also be extended with composition such as port if you want to simply change the appearance.
## Working with custom links
This is the easiest way to get started:
[Extending the default Link](./extending-default-links.md)
## Working with custom nodes
[Working with Nodes](./nodes.md)
[Working with Ports](./ports.md)
================================================
FILE: docs/customizing/extending-default-links.md
================================================
# Custom Links
## Extending the DefaultLinkModel
Much like extending nodes, custom links can also be created.
In the below example, we have created a link that renders a circle animating from the source port to the target port.

In this specific example, we extended the `DefaultLinkModel` because we wanted to retain
a lot of the functionality that it provides in the base class:
```typescript
export class AdvancedLinkModel extends DefaultLinkModel {
constructor() {
super({
type: 'advanced', // <-- here we give it a new type
width: 10 // we specifically want this to also be width 10
});
}
}
```
Now we need to create a new link factory to tell the system how our new link model fits into the core system. We specifically are going to extend the `DefaultLinkFactory` because we still want to render a `DefaultLinkWidget`. The only difference is that we want each __path segment__ to be a red line with an animating circle. Fortunately, the `DefaultLinkWidget` already uses the `generateLinkSegment()` method defined in the `DefaultLinkFactory` to accomplish this. The only thing we need to do, is provide a different type of segment:
```typescript
export class AdvancedLinkFactory extends DefaultLinkFactory {
constructor() {
super('advanced'); // <-- this matches with the link model above
}
generateModel(): AdvancedLinkModel {
return new AdvancedLinkModel(); // <-- this is how we get new instances
}
/**
* @override the DefaultLinkWidget makes use of this, and it normally renders that
* familiar gray line, so in this case we simply make it return a new advanced segment.
*/
generateLinkSegment(model: AdvancedLinkModel, selected: boolean, path: string) {
return (
<g>
<AdvancedLinkSegment model={model} path={path} />
</g>
);
}
}
```
The actual code for the `AdvancedLinkSegment` [can be found here](https://github.com/projectstorm/react-diagrams/tree/master/diagrams-demo-gallery/demos/demo-custom-link1) (it is in the `demo-custom-link1` folder in the demo gallery).
This is the easiest and most simple way to get started with custom links.
================================================
FILE: docs/customizing/nodes.md
================================================
# Nodes
A node contains the node content itself and its ports. Check [NodeModel source code](https://github.com/projectstorm/react-diagrams/blob/master/packages/react-diagrams-core/src/entities/node/NodeModel.ts#L24), if you want to see what class methods can be extended.
## Extending the NodeModel
If you want to create a custom node that looks entirely different, then you need to create a component that renders using its default or customized data mode. In the example below, it uses a customized data model `DiamondNodeModel` to render `DiamondNodeWidget`, and both of them are being created in the model factory `DiamondNodeFactory`.

Because our Diamond node always has four ports, we add four default port models into the `DiamondNodeModel`. Depending on the type of node you are creating, this is basically where you store your vertex data in the graph theory sense.
```typescript
// DiamondNodeModel.ts
import { NodeModel, NodeModelGenerics, PortModelAlignment } from '@projectstorm/react-diagrams';
import { DiamondPortModel } from './DiamondPortModel';
export interface DiamondNodeModelGenerics {
PORT: DiamondPortModel;
}
// this can be further extended for more complicated node types
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));
}
}
```
This is where we create our customized component. This component can be any customized react component as long as they respect the node and engine props. Ports also need to be rendered inside the node component.
```typescript
// DiamondNodeWidget.tsx
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 and engine props are required
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);
}
`;
}
// this can be any customized react component as long as they respect
// the node and engine props
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>
);
}
}
```
Now we need to create a new node factory to tell the system how our new node model fits into the core system. We specifically are going to extend the `DefaultLinkFactory` because we want to render a `DiamondNodeWidget` with data model being `DiamondNodeModel`. To accomplish that, we simply extend `generateReactWidget(event)` to return a `DiamondNodeWidget` and extend `generateModel` to return a `DiamondNodeModel` instance.
```typescript
// DiamondNodeFactory.tsx
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 {
// event.model is basically what's returned from generateModel()
return <DiamondNodeWidget engine={this.engine} size={50} node={event.model} />;
}
generateModel(event) {
return new DiamondNodeModel();
}
}
```
The actual code for the `DiamondNode` [can be found here](https://github.com/projectstorm/react-diagrams/tree/master/diagrams-demo-gallery/demos/demo-custom-node1) (it is in the `demo-custom-node1` folder in the demo gallery).
This is the easiest and most simple way to get started with custom nodes.
================================================
FILE: docs/customizing/ports.md
================================================
# Ports
Ports allow links to connect to your nodes. Each port that is rendered in a node must also have a corresponding PortModel in the corresponding NodeModel (as is the case with essentially all of the models and widgets in this library).
## Custom port widgets
If you want to create a custom port that looks entirely different (much like in the image below), then you simply need to create your own widget and wrap it in a `PortWidget`:
```jsx
<PortWidget
port={this.props.node.getPort("in"}
engine={this.props.engine} >
<div
style={{
width: 40,
height: 40,
background: 'orange'
}}
/>
</PortWidget>
```
Obviously, you can create the React widgets in any way you like. Whether you use __Emotion__, __BEM__ or plain old __CSS__, the only important thing is that your custom port is wrapped inside a `PortWidget`
## Specifying alignment
When links enter ports, depending on the alignment specified, they can help the links render differently. Take the following example:

In the above example, the 4 ports on the diamond node model are setup with different alignment:
```typescript
this.addPort(new DiamondPortModel(PortModelAlignment.TOP));
this.addPort(new DiamondPortModel(PortModelAlignment.LEFT));
this.addPort(new DiamondPortModel(PortModelAlignment.BOTTOM));
this.addPort(new DiamondPortModel(PortModelAlignment.RIGHT));
```
Each of the custom `DiamondPortModel` models forwards this through to the base `PortModel` class:
```typescript
export class DiamondPortModel extends PortModel {
...
constructor(alignment: PortModelAlignment) {
super({
type: 'diamond',
name: alignment,
alignment: alignment // <-- here
});
}
...
}
```
## Specifying if a link can be connected
A port is directly responsible for specifying if a link is allowed to connect to it. When you drag an un-connected link end-point to a target port, the target port lets the link know if it is allowed to connect.
```typescript
class PortModel{
...
canLinkToPort(port: PortModel): boolean;
}
```
In the above definition, the port argument is the source port that the incoming link is connected to. By default, the method returns true, but you can extend this and overide this method to do more advanced checks.
The `DefaultPortModel` provided in the defaults package, makes use of this principle to only allow `Out` ports to connect to `In` ports:
```typescript
class DefaultPortModel extends PortModel{
...
canLinkToPort(port: PortModel): boolean {
if (port instanceof DefaultPortModel) {
return this.options.in !== port.getOptions().in;
}
return true;
}
}
```
## Specifying what type of link is generated from a port
When a user drags on a port to generate a link, the port is also responsible for specifying
what link is created. This happens through the `createLinkModel()` method:
```typescript
class DefaultPortModel extends PortModel{
...
createLinkModel(): LinkModel{
return new DefaultLinkModel(); // <-- here we generate a DefaultLinkModel
}
}
```
================================================
FILE: docs/getting-started/README.md
================================================
# Getting Started
## Get the package
The first thing you need to do, is grab the distribution files on NPM.
**Via yarn:**
```text
yarn add @projectstorm/react-diagrams
```
**Via npm:**
```text
npm install @projectstorm/react-diagrams
```
**Via pnpm:**
```text
pnpm add @projectstorm/react-diagrams
```
When you run this in your project directory, this will install the library into `./node_modules/@projectstorm/react-diagrams`. You will then find a **dist** folder that contains all the minified and production ready code.
================================================
FILE: docs/getting-started/using-the-library.md
================================================
# Using the library
## Using Typescript
If you are using typescript, then you are in luck! The library is built in typescript, and includes advanced types for everything you need right out of the box.
Lets start by including the things we are going to need:
```typescript
import createEngine, {
DefaultLinkModel,
DefaultNodeModel,
DiagramModel
} from '@projectstorm/react-diagrams';
import {
CanvasWidget
} from '@projectstorm/react-canvas-core';
```
Now we call `createEngine` which will bootstrap a **DiagramEngine** for us that contains all the defaults setup.
```typescript
// create an instance of the engine with all the defaults
const engine = createEngine();
```
Next, we create two nodes:
```typescript
// node 1
const node1 = new DefaultNodeModel({
name: 'Node 1',
color: 'rgb(0,192,255)',
});
node1.setPosition(100, 100);
let port1 = node1.addOutPort('Out');
// node 2
const node2 = new DefaultNodeModel({
name: 'Node 1',
color: 'rgb(0,192,255)',
});
node2.setPosition(100, 100);
let port2 = node2.addOutPort('Out');
```
Now we link the two ports of both of the nodes:
```typescript
// link them and add a label to the link
const link = port1.link<DefaultLinkModel>(port2);
link.addLabel('Hello World!');
```
Great! Now we have setup a simple diagram. All thats left to do, is create a **DiagramModel** to contain everything, add all the elements to it, and then add it to the engine.
```typescript
const model = new DiagramModel();
model.addAll(node1, node2, link);
engine.setModel(model);
```
And then we render with **React**!
```jsx
<CanvasWidget engine={engine} />
```
================================================
FILE: package.json
================================================
{
"name": "@projectstorm/react-diagrams",
"author": "dylanvorster",
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/projectstorm/react-diagrams.git"
},
"keywords": [
"web",
"diagram",
"diagrams",
"react",
"typescript",
"flowchart",
"simple",
"links",
"nodes"
],
"scripts": {
"ncu": "ncu -u && pnpm recursive exec -- ncu -u",
"format": "prettier --write \"**/*.{ts,tsx,js,jsx}\"",
"clean": "rm -rf packages/*/dist",
"test": "pnpm run -r test",
"build": "tsc --build && pnpm run -r build",
"build:prod": "NODE_ENV=production pnpm build",
"release": "pnpm build:prod && pnpm changeset publish",
"release:storybook": "tsc --build && cd diagrams-demo-gallery && pnpm storybook:build && ./node_modules/.bin/storybook-to-ghpages --existing-output-dir .out"
},
"devDependencies": {
"@changesets/cli": "^2.26.2",
"@types/jest": "^29.5.5",
"@types/node": "^20.6.3",
"jest": "^29.7.0",
"jest-cli": "^29.7.0",
"prettier": "^3.0.3",
"rimraf": "^5.0.1",
"source-map-loader": "^4.0.1",
"terser-webpack-plugin": "^5.3.9",
"ts-jest": "^29.1.1",
"ts-loader": "^9.4.4",
"typescript": "^5.2.2",
"webpack": "^5.88.2",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1",
"webpack-node-externals": "^3.0.0"
},
"pnpm": {
"overrides": {
"react": "^19.0.0"
}
}
}
================================================
FILE: packages/geometry/.npmignore
================================================
*
!dist/**/*
!package.json
dist/tsconfig.tsbuildinfo
================================================
FILE: packages/geometry/CHANGELOG.md
================================================
# @projectstorm/geometry
## 7.0.3
### Patch Changes
- 80285fe: refactor: update lodash imports to use individual functions
## 7.0.2
### Patch Changes
- 66c687a: Upgrade all dependencies and fix Storybook after upgrade
## 7.0.1
### Patch Changes
- b8a4cbd: Inline sources in sourcemap
## 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
================================================
FILE: packages/geometry/package.json
================================================
{
"name": "@projectstorm/geometry",
"version": "7.0.3",
"author": "dylanvorster",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/projectstorm/react-diagrams.git"
},
"scripts": {
"clean": "rimraf ./dist",
"build": "../../node_modules/.bin/webpack"
},
"publishConfig": {
"access": "public"
},
"keywords": [
"web",
"diagram",
"diagrams",
"react",
"typescript",
"flowchart",
"simple",
"links",
"nodes"
],
"main": "./dist/index.umd.js",
"module": "./dist/index.js",
"typings": "./dist/@types/index",
"dependencies": {
"lodash": "^4.17.21"
},
"devDependencies": {
"@types/lodash": "^4.14.200"
}
}
================================================
FILE: packages/geometry/src/BezierCurve.ts
================================================
import { Point } from './Point';
import { Polygon } from './Polygon';
export enum BezierCurvepPoints {
SOURCE = 0,
SOURCE_CONTROL = 1,
TARGET_CONTROL = 2,
TARGET = 3
}
export class BezierCurve extends Polygon {
constructor() {
super([new Point(0, 0), new Point(0, 0), new Point(0, 0), new Point(0, 0)]);
}
getSVGCurve(): string {
return `M${this.getSource().toSVG()} C${this.getSourceControl().toSVG()}, ${this.getTargetControl().toSVG()}, ${this.getTarget().toSVG()}`;
}
setPoints(points: Point[]) {
if (points.length !== 4) {
throw new Error('BezierCurve must have extactly 4 points');
}
super.setPoints(points);
}
getSource(): Point {
return this.points[BezierCurvepPoints.SOURCE];
}
getSourceControl(): Point {
return this.points[BezierCurvepPoints.SOURCE_CONTROL];
}
getTargetControl(): Point {
return this.points[BezierCurvepPoints.TARGET_CONTROL];
}
getTarget(): Point {
return this.points[BezierCurvepPoints.TARGET];
}
setSource(point: Point) {
this.points[BezierCurvepPoints.SOURCE] = point;
}
setSourceControl(point: Point) {
this.points[BezierCurvepPoints.SOURCE_CONTROL] = point;
}
setTargetControl(point: Point) {
this.points[BezierCurvepPoints.TARGET_CONTROL] = point;
}
setTarget(point: Point) {
this.points[BezierCurvepPoints.TARGET] = point;
}
}
================================================
FILE: packages/geometry/src/Bounds.ts
================================================
import { Point } from './Point';
export enum BoundsCorner {
TOP_LEFT = 'TL',
TOP_RIGHT = 'TR',
BOTTOM_RIGHT = 'BR',
BOTTOM_LEFT = 'BL'
}
export type Bounds = { [k in BoundsCorner]: Point };
export const boundsFromPositionAndSize = (x: number, y: number, width: number, height: number): Bounds => {
return {
[BoundsCorner.TOP_LEFT]: new Point(x, y),
[BoundsCorner.TOP_RIGHT]: new Point(x + width, y),
[BoundsCorner.BOTTOM_RIGHT]: new Point(x + width, y + height),
[BoundsCorner.BOTTOM_LEFT]: new Point(x, y + height)
};
};
export const createEmptyBounds = () => {
return {
[BoundsCorner.TOP_LEFT]: new Point(),
[BoundsCorner.TOP_RIGHT]: new Point(),
[BoundsCorner.BOTTOM_RIGHT]: new Point(),
[BoundsCorner.BOTTOM_LEFT]: new Point()
};
};
================================================
FILE: packages/geometry/src/Matrix.ts
================================================
import { Point } from './Point';
export class Matrix {
matrix: number[][];
constructor(matrix: number[][]) {
this.matrix = matrix;
}
mmul(matrix: Matrix): Matrix {
this.matrix = this.matrix.map((row, i) =>
matrix.asArray()[0].map((_, j) => row.reduce((acc, _, n) => acc + this.matrix[i][n] * matrix.asArray()[n][j], 0))
);
return this;
}
asArray(): number[][] {
return this.matrix;
}
get(rowIndex: number, columnIndex: number): number {
return this.asArray()[rowIndex][columnIndex];
}
public static multiply(...matrices: Matrix[]): Matrix {
let m: Matrix = matrices[0];
for (let i = 1; i < matrices.length; i++) {
m = m.mmul(matrices[i]);
}
return m;
}
public static scaleMatrix(x: number, y: number): Matrix {
return new Matrix([
[x, 0, 0],
[0, y, 0],
[0, 0, 1]
]);
}
public static translateMatrix(x: number, y: number): Matrix {
return new Matrix([
[1, 0, x],
[0, 1, y],
[0, 0, 1]
]);
}
public static rotateMatrix(deg: number): Matrix {
return new Matrix([
[Math.cos(deg), -1 * Math.sin(deg), 0],
[Math.sin(deg), Math.cos(deg), 0],
[0, 0, 1]
]);
}
static createScaleMatrix(x, y, origin: Point): Matrix {
return this.multiply(
Matrix.translateMatrix(origin.x, origin.y),
Matrix.scaleMatrix(x, y),
Matrix.translateMatrix(-origin.x, -origin.y)
);
}
static createRotateMatrix(deg: number, origin: Point): Matrix {
return this.multiply(
Matrix.translateMatrix(origin.x, origin.y),
Matrix.rotateMatrix(deg),
Matrix.translateMatrix(-origin.x, -origin.y)
);
}
}
================================================
FILE: packages/geometry/src/Point.ts
================================================
import { Matrix } from './Matrix';
export class Point {
x: number;
y: number;
constructor(x: number = 0, y: number = 0) {
this.x = x;
this.y = y;
}
translate(x: number, y: number) {
this.x += x;
this.y += y;
}
clone() {
return new Point(this.x, this.y);
}
toSVG() {
return this.x + ' ' + this.y;
}
asMatrix() {
return new Matrix([[this.x], [this.y], [1]]);
}
transform(matrix: Matrix) {
let final: Matrix = matrix.mmul(this.asMatrix());
this.x = final.get(0, 0);
this.y = final.get(1, 0);
}
public static middlePoint(pointA: Point, pointB: Point): Point {
return new Point((pointB.x + pointA.x) / 2, (pointB.y + pointA.y) / 2);
}
}
================================================
FILE: packages/geometry/src/Polygon.ts
================================================
import { Point } from './Point';
import _forEach from 'lodash/forEach';
import _map from 'lodash/map';
import { Matrix } from './Matrix';
import { boundingBoxFromPoints } from './toolkit';
import { Bounds, BoundsCorner } from './Bounds';
export class Polygon {
protected points: Point[];
constructor(points: Point[] = []) {
this.points = points;
}
serialize() {
return _map(this.points, (point) => {
return [point.x, point.y];
});
}
deserialize(data: any) {
this.points = _map(data, (point) => {
return new Point(point[0], point[1]);
});
}
scale(x, y, origin: Point) {
let matrix = Matrix.createScaleMatrix(x, y, origin);
_forEach(this.points, (point) => {
point.transform(matrix);
});
}
transform(matrix: Matrix) {
_forEach(this.points, (point) => {
point.transform(matrix);
});
}
setPoints(points: Point[]) {
this.points = points;
}
getPoints(): Point[] {
return this.points;
}
rotate(degrees: number) {
this.transform(Matrix.createRotateMatrix(degrees / (180 / Math.PI), this.getOrigin()));
}
translate(offsetX: number, offsetY: number) {
_forEach(this.points, (point) => {
point.translate(offsetX, offsetY);
});
}
doClone(ob: this) {
this.points = _map(ob.points, (point) => {
return point.clone();
});
}
clone(): this {
let ob = Object.create(this);
ob.doClone(this);
return ob;
}
getOrigin(): Point {
if (this.points.length === 0) {
return null;
}
let dimensions = boundingBoxFromPoints(this.points);
return Point.middlePoint(dimensions[BoundsCorner.TOP_LEFT], dimensions[BoundsCorner.BOTTOM_RIGHT]);
}
getBoundingBox(): Bounds {
return boundingBoxFromPoints(this.points);
}
}
================================================
FILE: packages/geometry/src/Rectangle.ts
================================================
import { Point } from './Point';
import { Polygon } from './Polygon';
import { Bounds, BoundsCorner, boundsFromPositionAndSize, createEmptyBounds } from './Bounds';
export class Rectangle extends Polygon {
static fromPositionAndSize(x: number, y: number, width: number, height: number) {
return new Rectangle(boundsFromPositionAndSize(x, y, width, height));
}
static fromPointAndSize(position: Point, width: number, height: number) {
return new Rectangle(boundsFromPositionAndSize(position.x, position.y, width, height));
}
constructor(points?: Bounds) {
if (!points) {
points = createEmptyBounds();
}
super([
points[BoundsCorner.TOP_LEFT],
points[BoundsCorner.TOP_RIGHT],
points[BoundsCorner.BOTTOM_RIGHT],
points[BoundsCorner.BOTTOM_LEFT]
]);
}
updateDimensions(x: number, y: number, width: number, height: number) {
const points = boundsFromPositionAndSize(x, y, width, height);
this.setPoints([
points[BoundsCorner.TOP_LEFT],
points[BoundsCorner.TOP_RIGHT],
points[BoundsCorner.BOTTOM_RIGHT],
points[BoundsCorner.BOTTOM_LEFT]
]);
}
setPoints(points: Point[]) {
if (points.length !== 4) {
throw 'Rectangles must always have 4 points';
}
super.setPoints(points);
}
containsPoint(point: Point) {
const tl = this.getTopLeft();
const br = this.getBottomRight();
return point.x >= tl.x && point.x <= br.x && point.y >= tl.y && point.y <= br.y;
}
getWidth(): number {
return Math.sqrt(
Math.pow(this.getTopLeft().x - this.getTopRight().x, 2) + Math.pow(this.getTopLeft().y - this.getTopRight().y, 2)
);
}
getHeight(): number {
return Math.sqrt(
Math.pow(this.getBottomLeft().x - this.getTopLeft().x, 2) +
Math.pow(this.getBottomLeft().y - this.getTopLeft().y, 2)
);
}
getTopMiddle(): Point {
return Point.middlePoint(this.getTopLeft(), this.getTopRight());
}
getBottomMiddle(): Point {
return Point.middlePoint(this.getBottomLeft(), this.getBottomRight());
}
getLeftMiddle(): Point {
return Point.middlePoint(this.getBottomLeft(), this.getTopLeft());
}
getRightMiddle(): Point {
return Point.middlePoint(this.getBottomRight(), this.getTopRight());
}
getTopLeft(): Point {
return this.points[0];
}
getTopRight(): Point {
return this.points[1];
}
getBottomRight(): Point {
return this.points[2];
}
getBottomLeft(): Point {
return this.points[3];
}
}
================================================
FILE: packages/geometry/src/index.ts
================================================
export * from './Point';
export * from './Matrix';
export * from './Polygon';
export * from './Rectangle';
export * from './BezierCurve';
export * from './toolkit';
export * from './Bounds';
================================================
FILE: packages/geometry/src/toolkit.ts
================================================
import { Point } from './Point';
import _flatMap from 'lodash/flatMap';
import { Polygon } from './Polygon';
import { Bounds, BoundsCorner, createEmptyBounds } from './Bounds';
export const boundingBoxFromPoints = (points: Point[]): Bounds => {
if (points.length === 0) {
return createEmptyBounds();
}
let minX = points[0].x;
let maxX = points[0].x;
let minY = points[0].y;
let maxY = points[0].y;
for (let i = 1; i < points.length; i++) {
if (points[i].x < minX) {
minX = points[i].x;
}
if (points[i].x > maxX) {
maxX = points[i].x;
}
if (points[i].y < minY) {
minY = points[i].y;
}
if (points[i].y > maxY) {
maxY = points[i].y;
}
}
return {
[BoundsCorner.TOP_LEFT]: new Point(minX, minY),
[BoundsCorner.TOP_RIGHT]: new Point(maxX, minY),
[BoundsCorner.BOTTOM_RIGHT]: new Point(maxX, maxY),
[BoundsCorner.BOTTOM_LEFT]: new Point(minX, maxY)
};
};
export const boundingBoxFromPolygons = (polygons: Polygon[]): Bounds => {
return boundingBoxFromPoints(
_flatMap(polygons, (polygon) => {
return polygon.getPoints();
})
);
};
================================================
FILE: packages/geometry/tsconfig.json
================================================
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"outDir": "dist",
"rootDir": "src",
"sourceMap": true,
"declarationDir": "dist/@types",
"tsBuildInfoFile": "dist/tsconfig.tsbuildinfo"
},
"include": ["./src"]
}
================================================
FILE: packages/geometry/webpack.config.js
================================================
const config = require('../../webpack.shared')(__dirname);
module.exports = {
...config,
output: {
...config.output,
library: '@projectstorm/react-diagrams-geometry'
}
};
================================================
FILE: packages/react-canvas-core/.npmignore
================================================
*
!dist/**/*
!package.json
dist/tsconfig.tsbuildinfo
================================================
FILE: packages/react-canvas-core/CHANGELOG.md
================================================
# @projectstorm/react-canvas-core
## 7.0.3
### Patch Changes
- 09ed60f: Allow more derived State classes to provide a generic type
- 80285fe: refactor: update lodash imports to use individual functions
- Updated dependencies [80285fe]
- @projectstorm/geometry@7.0.3
## 7.0.2
### Patch Changes
- 66c687a: Upgrade all dependencies and fix Storybook after upgrade
- Updated dependencies [66c687a]
- @projectstorm/geometry@7.0.2
## 7.0.1
### Patch Changes
- b8a4cbd: Inline sources in sourcemap
- Updated dependencies [b8a4cbd]
- @projectstorm/geometry@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/geometry@7.0.0
================================================
FILE: packages/react-canvas-core/package.json
================================================
{
"name": "@projectstorm/react-canvas-core",
"version": "7.0.3",
"author": "dylanvorster",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/projectstorm/react-diagrams.git"
},
"scripts": {
"clean": "rimraf ./dist",
"build": "../../node_modules/.bin/webpack"
},
"publishConfig": {
"access": "public"
},
"keywords": [
"web",
"diagram",
"diagrams",
"react",
"typescript",
"flowchart",
"simple",
"links",
"nodes"
],
"main": "./dist/index.umd.js",
"module": "./dist/index.js",
"typings": "./dist/@types/index",
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.11.0",
"@projectstorm/geometry": "workspace:*",
"lodash": "^4.17.21",
"react": "^19.0.0"
},
"devDependencies": {
"@types/lodash": "^4.14.200",
"@types/react": "^19.0.12"
}
}
================================================
FILE: packages/react-canvas-core/src/CanvasEngine.ts
================================================
import _debounce from 'lodash/debounce';
import { CanvasModel } from './entities/canvas/CanvasModel';
import { FactoryBank } from './core/FactoryBank';
import { AbstractReactFactory } from './core/AbstractReactFactory';
import { LayerModel } from './entities/layer/LayerModel';
import { BaseListener, BaseObserver } from './core/BaseObserver';
import { MouseEvent } from 'react';
import { BaseModel } from './core-models/BaseModel';
import { Point } from '@projectstorm/geometry';
import { ActionEventBus } from './core-actions/ActionEventBus';
import { PanAndZoomCanvasAction } from './actions/PanAndZoomCanvasAction';
import { ZoomCanvasAction } from './actions/ZoomCanvasAction';
import { DeleteItemsAction } from './actions/DeleteItemsAction';
import { StateMachine } from './core-state/StateMachine';
export interface CanvasEngineListener extends BaseListener {
canvasReady?(): void;
repaintCanvas?(): void;
rendered?(): void;
}
/**
* Defines the CanvasEngine options
*/
export interface CanvasEngineOptions {
registerDefaultDeleteItemsAction?: boolean;
registerDefaultPanAndZoomCanvasAction?: boolean;
registerDefaultZoomCanvasAction?: boolean;
/**
* Defines the debounce wait time in milliseconds if > 0
*/
repaintDebounceMs?: number;
}
export class CanvasEngine<
L extends CanvasEngineListener = CanvasEngineListener,
M extends CanvasModel = CanvasModel
> extends BaseObserver<L> {
protected model: M;
protected layerFactories: FactoryBank<AbstractReactFactory<LayerModel>>;
protected canvas: HTMLDivElement;
protected eventBus: ActionEventBus;
protected stateMachine: StateMachine;
protected options: CanvasEngineOptions;
constructor(options: CanvasEngineOptions = {}) {
super();
this.model = null;
this.eventBus = new ActionEventBus(this);
this.stateMachine = new StateMachine(this);
this.layerFactories = new FactoryBank();
this.registerFactoryBank(this.layerFactories);
/**
* Overrides the standard options with the possible given options
*/
this.options = {
registerDefaultDeleteItemsAction: true,
registerDefaultZoomCanvasAction: true,
repaintDebounceMs: 0,
...options
};
if (this.options.registerDefaultZoomCanvasAction === true) {
this.eventBus.registerAction(new ZoomCanvasAction());
} else if (this.options.registerDefaultPanAndZoomCanvasAction === true) {
this.eventBus.registerAction(new PanAndZoomCanvasAction());
}
if (this.options.registerDefaultDeleteItemsAction === true) {
this.eventBus.registerAction(new DeleteItemsAction());
}
}
getStateMachine() {
return this.stateMachine;
}
getRelativeMousePoint(event: { clientX: number; clientY: number }): Point {
const point = this.getRelativePoint(event.clientX, event.clientY);
return new Point(
(point.x - this.model.getOffsetX()) / (this.model.getZoomLevel() / 100.0),
(point.y - this.model.getOffsetY()) / (this.model.getZoomLevel() / 100.0)
);
}
getRelativePoint(x, y): Point {
const canvasRect = this.canvas.getBoundingClientRect();
return new Point(x - canvasRect.left, y - canvasRect.top);
}
registerFactoryBank(factory: FactoryBank) {
factory.registerListener({
factoryAdded: (event) => {
event.factory.setDiagramEngine(this);
},
factoryRemoved: (event) => {
event.factory.setDiagramEngine(null);
}
});
}
getActionEventBus() {
return this.eventBus;
}
getLayerFactories() {
return this.layerFactories;
}
getFactoryForLayer<F extends AbstractReactFactory<LayerModel>>(layer: LayerModel | string) {
if (typeof layer === 'string') {
return this.layerFactories.getFactory(layer);
}
return this.layerFactories.getFactory(layer.getType());
}
setModel(model: M) {
this.model = model;
if (this.canvas) {
requestAnimationFrame(() => {
this.repaintCanvas();
});
}
}
getModel(): M {
return this.model;
}
repaintCanvas(promise: true): Promise<any>;
repaintCanvas(): void;
repaintCanvas(promise?): Promise<any> | void {
const { repaintDebounceMs } = this.options;
/**
* The actual repaint function
*/
const repaint = () => {
this.iterateListeners((listener) => {
if (listener.repaintCanvas) {
listener.repaintCanvas();
}
});
};
// if the `repaintDebounceMs` option is > 0, then apply the debounce
let repaintFn = repaint;
if (repaintDebounceMs > 0) {
repaintFn = _debounce(repaint, repaintDebounceMs);
}
if (promise) {
return new Promise<void>((resolve) => {
const l = this.registerListener({
rendered: () => {
resolve();
l.deregister();
}
} as L);
repaintFn();
});
}
repaintFn();
}
setCanvas(canvas?: HTMLDivElement) {
if (this.canvas !== canvas) {
this.canvas = canvas;
if (canvas) {
this.fireEvent({}, 'canvasReady');
}
}
}
getCanvas() {
return this.canvas;
}
getMouseElement(event: MouseEvent): BaseModel {
return null;
}
zoomToFit() {
const xFactor = this.canvas.clientWidth / this.canvas.scrollWidth;
const yFactor = this.canvas.clientHeight / this.canvas.scrollHeight;
const zoomFactor = xFactor < yFactor ? xFactor : yFactor;
this.model.setZoomLevel(this.model.getZoomLevel() * zoomFactor);
this.model.setOffset(0, 0);
this.repaintCanvas();
}
}
================================================
FILE: packages/react-canvas-core/src/Toolkit.ts
================================================
export class Toolkit {
static TESTING: boolean = false;
static TESTING_UID = 0;
/**
* Generats a unique ID (thanks Stack overflow :3)
* @returns {String}
*/
public static UID(): string {
if (Toolkit.TESTING) {
Toolkit.TESTING_UID++;
return `${Toolkit.TESTING_UID}`;
}
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0;
const v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
public static closest(element: Element, selector: string) {
if (!Element.prototype.closest) {
Element.prototype.closest = function (s) {
var el = this;
do {
if (Element.prototype.matches.call(el, s)) return el;
el = el.parentElement || el.parentNode;
} while (el !== null && el.nodeType === 1);
return null;
};
}
return element.closest(selector);
}
}
================================================
FILE: packages/react-canvas-core/src/actions/DeleteItemsAction.ts
================================================
import { Action, ActionEvent, InputType } from '../core-actions/Action';
import { KeyboardEvent } from 'react';
import _forEach from 'lodash/forEach';
import _isEqual from 'lodash/isEqual';
export interface DeleteItemsActionOptions {
keyCodes?: number[];
modifiers?: {
ctrlKey?: boolean;
shiftKey?: boolean;
altKey?: boolean;
metaKey?: boolean;
};
}
/**
* Deletes all selected items
*/
export class DeleteItemsAction extends Action {
constructor(options: DeleteItemsActionOptions = {}) {
const keyCodes = options.keyCodes || [46, 8];
const modifiers = {
ctrlKey: false,
shiftKey: false,
altKey: false,
metaKey: false,
...options.modifiers
};
super({
type: InputType.KEY_DOWN,
fire: (event: ActionEvent<KeyboardEvent>) => {
const { keyCode, ctrlKey, shiftKey, altKey, metaKey } = event.event;
if (keyCodes.indexOf(keyCode) !== -1 && _isEqual({ ctrlKey, shiftKey, altKey, metaKey }, modifiers)) {
_forEach(this.engine.getModel().getSelectedEntities(), (model) => {
// only delete items which are not locked
if (!model.isLocked()) {
model.remove();
}
});
this.engine.repaintCanvas();
}
}
});
}
}
================================================
FILE: packages/react-canvas-core/src/actions/PanAndZoomCanvasAction.ts
================================================
import { WheelEvent } from 'react';
import { Action, ActionEvent, InputType } from '../core-actions/Action';
export interface PanAndZoomCanvasActionOptions {
inverseZoom?: boolean;
}
export class PanAndZoomCanvasAction extends Action {
constructor(options: PanAndZoomCanvasActionOptions = {}) {
super({
type: InputType.MOUSE_WHEEL,
fire: (actionEvent: ActionEvent<WheelEvent>) => {
const { event } = actionEvent;
// we can block layer rendering because we are only targeting the transforms
for (let layer of this.engine.getModel().getLayers()) {
layer.allowRepaint(false);
}
const model = this.engine.getModel();
event.stopPropagation();
if (event.ctrlKey) {
// Pinch and zoom gesture
const oldZoomFactor = this.engine.getModel().getZoomLevel() / 100;
let scrollDelta = options.inverseZoom ? event.deltaY : -event.deltaY;
scrollDelta /= 3;
if (model.getZoomLevel() + scrollDelta > 10) {
model.setZoomLevel(model.getZoomLevel() + scrollDelta);
}
const zoomFactor = model.getZoomLevel() / 100;
const boundingRect = event.currentTarget.getBoundingClientRect();
const clientWidth = boundingRect.width;
const clientHeight = boundingRect.height;
// compute difference between rect before and after scroll
const widthDiff = clientWidth * zoomFactor - clientWidth * oldZoomFactor;
const heightDiff = clientHeight * zoomFactor - clientHeight * oldZoomFactor;
// compute mouse coords relative to canvas
const clientX = event.clientX - boundingRect.left;
const clientY = event.clientY - boundingRect.top;
// compute width and height increment factor
const xFactor = (clientX - model.getOffsetX()) / oldZoomFactor / clientWidth;
const yFactor = (clientY - model.getOffsetY()) / oldZoomFactor / clientHeight;
model.setOffset(model.getOffsetX() - widthDiff * xFactor, model.getOffsetY() - heightDiff * yFactor);
} else {
// Pan gesture
let yDelta = options.inverseZoom ? -event.deltaY : event.deltaY;
let xDelta = options.inverseZoom ? -event.deltaX : event.deltaX;
model.setOffset(model.getOffsetX() - xDelta, model.getOffsetY() - yDelta);
}
this.engine.repaintCanvas();
// re-enable rendering
for (let layer of this.engine.getModel().getLayers()) {
layer.allowRepaint(true);
}
}
});
}
}
================================================
FILE: packages/react-canvas-core/src/actions/ZoomCanvasAction.ts
================================================
import { WheelEvent } from 'react';
import { Action, ActionEvent, InputType } from '../core-actions/Action';
export interface ZoomCanvasActionOptions {
inverseZoom?: boolean;
}
export class ZoomCanvasAction extends Action {
constructor(options: ZoomCanvasActionOptions = {}) {
super({
type: InputType.MOUSE_WHEEL,
fire: (actionEvent: ActionEvent<WheelEvent>) => {
const { event } = actionEvent;
// we can block layer rendering because we are only targeting the transforms
for (let layer of this.engine.getModel().getLayers()) {
layer.allowRepaint(false);
}
const model = this.engine.getModel();
event.stopPropagation();
const oldZoomFactor = this.engine.getModel().getZoomLevel() / 100;
let scrollDelta = options.inverseZoom ? -event.deltaY : event.deltaY;
//check if it is pinch gesture
if (event.ctrlKey && scrollDelta % 1 !== 0) {
/*
Chrome and Firefox sends wheel event with deltaY that
have fractional part, also `ctrlKey` prop of the event is true
though ctrl isn't pressed
*/
scrollDelta /= 3;
} else {
scrollDelta /= 60;
}
if (model.getZoomLevel() + scrollDelta > 10) {
model.setZoomLevel(model.getZoomLevel() + scrollDelta);
}
const zoomFactor = model.getZoomLevel() / 100;
const boundingRect = event.currentTarget.getBoundingClientRect();
const clientWidth = boundingRect.width;
const clientHeight = boundingRect.height;
// compute difference between rect before and after scroll
const widthDiff = clientWidth * zoomFactor - clientWidth * oldZoomFactor;
const heightDiff = clientHeight * zoomFactor - clientHeight * oldZoomFactor;
// compute mouse coords relative to canvas
const clientX = event.clientX - boundingRect.left;
const clientY = event.clientY - boundingRect.top;
// compute width and height increment factor
const xFactor = (clientX - model.getOffsetX()) / oldZoomFactor / clientWidth;
const yFactor = (clientY - model.getOffsetY()) / oldZoomFactor / clientHeight;
model.setOffset(model.getOffsetX() - widthDiff * xFactor, model.getOffsetY() - heightDiff * yFactor);
this.engine.repaintCanvas();
// re-enable rendering
for (let layer of this.engine.getModel().getLayers()) {
layer.allowRepaint(true);
}
}
});
}
}
================================================
FILE: packages/react-canvas-core/src/core/AbstractFactory.ts
================================================
import { CanvasEngine } from '../CanvasEngine';
import { FactoryBank } from './FactoryBank';
/**
* Base factory for all the different types of entities.
* Gets registered with the engine, and is used to generate models
*/
export abstract class AbstractFactory<E extends CanvasEngine = CanvasEngine> {
/**
* Couples the factory with the models it generates
*/
protected type: string;
/**
* The engine gets injected when the factory is registered
*/
protected engine: E;
protected bank: FactoryBank;
constructor(type: string) {
this.type = type;
}
setDiagramEngine(engine: E) {
this.engine = engine;
}
setFactoryBank(bank: FactoryBank) {
this.bank = bank;
}
getType(): string {
return this.type;
}
}
================================================
FILE: packages/react-canvas-core/src/core/AbstractModelFactory.ts
================================================
import { AbstractFactory } from './AbstractFactory';
import { BaseModel } from '../core-models/BaseModel';
import { CanvasEngine } from '../CanvasEngine';
export interface GenerateModelEvent {
initialConfig?: any;
}
export abstract class AbstractModelFactory<
T extends BaseModel = BaseModel,
E extends CanvasEngine = CanvasEngine
> extends AbstractFactory<E> {
/**
* Generates new models (the core factory pattern)
*/
abstract generateModel(event: GenerateModelEvent): T;
}
================================================
FILE: packages/react-canvas-core/src/core/AbstractReactFactory.tsx
================================================
import { BaseModel } from '../core-models/BaseModel';
import { AbstractModelFactory } from './AbstractModelFactory';
import { CanvasEngine } from '../CanvasEngine';
import { JSX } from 'react';
export interface GenerateWidgetEvent<T extends BaseModel> {
model: T;
}
/**
* Further extends the AbstractFactory to add widget generation capability.
*/
export abstract class AbstractReactFactory<
T extends BaseModel = BaseModel,
E extends CanvasEngine = CanvasEngine
> extends AbstractModelFactory<T, E> {
/**
* Generates React widgets from the model contained in the event object
*/
abstract generateReactWidget(event: GenerateWidgetEvent<T>): JSX.Element;
}
================================================
FILE: packages/react-canvas-core/src/core/BaseObserver.ts
================================================
import { Toolkit } from '../Toolkit';
export interface BaseEvent {
firing: boolean;
stopPropagation: () => any;
}
export interface BaseEventProxy extends BaseEvent {
function: string;
}
/**
* Listeners are always in the form of an object that contains methods that take events
*/
export type BaseListener = {
/**
* Generic event that fires before a specific event was fired
*/
eventWillFire?: (event: BaseEvent & { function: string }) => void;
/**
* Generic event that fires after a specific event was fired (even if it was consumed)
*/
eventDidFire?: (event: BaseEvent & { function: string }) => void;
} & {
/**
* Type for other events that will fire
*/
[key: string]: (event: BaseEvent) => any;
};
export interface ListenerHandle {
/**
* Used to degister the listener
*/
deregister: () => any;
/**
* Original ID of the listener
*/
id: string;
/**
* Original Listener
*/
listener: BaseListener;
}
/**
* Base observer pattern class for working with listeners
*/
export class BaseObserver<L extends BaseListener = BaseListener> {
protected listeners: { [id: string]: L };
constructor() {
this.listeners = {};
}
private fireEventInternal(fire: boolean, k: keyof L, event: BaseEvent) {
this.iterateListeners((listener) => {
// returning false here will instruct itteration to stop
if (!fire && !event.firing) {
return false;
}
// fire selected listener
if (listener[k]) {
listener[k](event as BaseEvent);
}
});
}
fireEvent<K extends keyof L>(event: Partial<Parameters<L[K]>[0]>, k: keyof L) {
event = {
firing: true,
stopPropagation: () => {
event.firing = false;
},
...event
};
// fire pre
this.fireEventInternal(true, 'eventWillFire', {
...event,
function: k
} as BaseEventProxy);
// fire main event
this.fireEventInternal(false, k, event as BaseEvent);
// fire post
this.fireEventInternal(true, 'eventDidFire', {
...event,
function: k
} as BaseEventProxy);
}
iterateListeners(cb: (listener: L) => any) {
for (let id in this.listeners) {
const res = cb(this.listeners[id]);
// cancel itteration on false
if (res === false) {
return;
}
}
}
getListenerHandle(listener: L): ListenerHandle {
for (let id in this.listeners) {
if (this.listeners[id] === listener) {
return {
id: id,
listener: listener,
deregister: () => {
delete this.listeners[id];
}
};
}
}
}
registerListener(listener: L): ListenerHandle {
const id = Toolkit.UID();
this.listeners[id] = listener;
return {
id: id,
listener: listener,
deregister: () => {
delete this.listeners[id];
}
};
}
deregisterListener(listener: L | ListenerHandle) {
if (typeof listener === 'object') {
(listener as ListenerHandle).deregister();
return true;
}
const handle = this.getListenerHandle(listener);
if (handle) {
handle.deregister();
return true;
}
return false;
}
}
================================================
FILE: packages/react-canvas-core/src/core/FactoryBank.ts
================================================
import { BaseEvent, BaseListener, BaseObserver } from './BaseObserver';
import { AbstractFactory } from './AbstractFactory';
import _values from 'lodash/values';
export interface FactoryBankListener<F extends AbstractFactory = AbstractFactory> extends BaseListener {
/**
* Factory as added to rhe bank
*/
factoryAdded?: (event: BaseEvent & { factory: F }) => any;
/**
* Factory was removed from the bank
*/
factoryRemoved?: (event: BaseEvent & { factory: F }) => any;
}
/**
* Store and managed Factories that extend from Abstractfactory
*/
export class FactoryBank<
F extends AbstractFactory = AbstractFactory,
L extends FactoryBankListener<F> = FactoryBankListener<F>
> extends BaseObserver<L> {
protected factories: { [type: string]: F };
constructor() {
super();
this.factories = {};
}
getFactories(): F[] {
return _values(this.factories);
}
clearFactories() {
for (let factory in this.factories) {
this.deregisterFactory(factory);
}
}
getFactory<T extends F = F>(type: string): T {
if (!this.factories[type]) {
throw new Error(`Cannot find factory with type [${type}]`);
}
return this.factories[type] as T;
}
registerFactory(factory: F) {
factory.setFactoryBank(this);
this.factories[factory.getType()] = factory;
// todo fixme
this.fireEvent<'factoryAdded'>({ factory } as any, 'factoryAdded');
}
deregisterFactory(type: string) {
const factory = this.factories[type];
factory.setFactoryBank(null);
delete this.factories[type];
// todo fixme
this.fireEvent<'factoryRemoved'>({ factory } as any, 'factoryRemoved');
}
}
================================================
FILE: packages/react-canvas-core/src/core/ModelGeometryInterface.ts
================================================
import { Rectangle } from '@projectstorm/geometry';
export interface ModelGeometryInterface {
getBoundingBox(): Rectangle;
}
================================================
FILE: packages/react-canvas-core/src/core-actions/Action.ts
================================================
import { MouseEvent, KeyboardEvent, WheelEvent, TouchEvent, SyntheticEvent } from 'react';
import { Toolkit } from '../Toolkit';
import { CanvasEngine } from '../CanvasEngine';
import { BaseModel } from '../core-models/BaseModel';
export enum InputType {
MOUSE_DOWN = 'mouse-down',
MOUSE_UP = 'mouse-up',
MOUSE_MOVE = 'mouse-move',
MOUSE_WHEEL = 'mouse-wheel',
KEY_DOWN = 'key-down',
KEY_UP = 'key-up',
TOUCH_START = 'touch-start',
TOUCH_END = 'touch-end',
TOUCH_MOVE = 'touch-move'
}
export interface Mapping {
[InputType.MOUSE_DOWN]: MouseEvent;
[InputType.MOUSE_UP]: MouseEvent;
[InputType.MOUSE_MOVE]: MouseEvent;
[InputType.MOUSE_WHEEL]: WheelEvent;
[InputType.KEY_DOWN]: KeyboardEvent;
[InputType.KEY_UP]: KeyboardEvent;
[InputType.TOUCH_START]: TouchEvent;
[InputType.TOUCH_END]: TouchEvent;
[InputType.TOUCH_MOVE]: TouchEvent;
}
export interface ActionEvent<Event extends SyntheticEvent = SyntheticEvent, Model extends BaseModel = BaseModel> {
event: Event;
model?: Model;
}
export interface ActionOptions {
type: InputType;
fire: (event: ActionEvent<Mapping[this['type']]>) => void;
}
export class Action<T extends CanvasEngine = CanvasEngine> {
options: ActionOptions;
id: string;
engine: T;
constructor(options: ActionOptions) {
this.options = options;
this.id = Toolkit.UID();
}
setEngine(engine: T) {
this.engine = engine;
}
}
================================================
FILE: packages/react-canvas-core/src/core-actions/ActionEventBus.ts
================================================
import { Action, ActionEvent, InputType } from './Action';
import { KeyboardEvent, MouseEvent } from 'react';
import _filter from 'lodash/filter';
import _keys from 'lodash/keys';
import { CanvasEngine } from '../CanvasEngine';
import { BaseModel } from '../core-models/BaseModel';
export class ActionEventBus {
protected actions: { [id: string]: Action };
protected engine: CanvasEngine;
protected keys: { [key: string]: boolean };
constructor(engine: CanvasEngine) {
this.actions = {};
this.engine = engine;
this.keys = {};
}
getKeys(): string[] {
return _keys(this.keys);
}
registerAction(action: Action): () => void {
action.setEngine(this.engine);
this.actions[action.id] = action;
return () => {
this.deregisterAction(action);
};
}
deregisterAction(action: Action) {
action.setEngine(null);
delete this.actions[action.id];
}
getActionsForType(type: InputType): Action[] {
return _filter(this.actions, (action) => {
return action.options.type === type;
});
}
getModelForEvent(actionEvent: ActionEvent<MouseEvent>): BaseModel {
if (actionEvent.model) {
return actionEvent.model;
}
return this.engine.getMouseElement(actionEvent.event);
}
getActionsForEvent(actionEvent: ActionEvent): Action[] {
const { event } = actionEvent;
if (event.type === 'mousedown') {
return this.getActionsForType(InputType.MOUSE_DOWN);
} else if (event.type === 'mouseup') {
return this.getActionsForType(InputType.MOUSE_UP);
} else if (event.type === 'keydown') {
// store the recorded key
this.keys[(event as KeyboardEvent).key.toLowerCase()] = true;
return this.getActionsForType(InputType.KEY_DOWN);
} else if (event.type === 'keyup') {
// delete the recorded key
delete this.keys[(event as KeyboardEvent).key.toLowerCase()];
return this.getActionsForType(InputType.KEY_UP);
} else if (event.type === 'mousemove') {
return this.getActionsForType(InputType.MOUSE_MOVE);
} else if (event.type === 'wheel') {
return this.getActionsForType(InputType.MOUSE_WHEEL);
} else if (event.type === 'touchstart') {
return this.getActionsForType(InputType.TOUCH_START);
} else if (event.type === 'touchend') {
return this.getActionsForType(InputType.TOUCH_END);
} else if (event.type === 'touchmove') {
return this.getActionsForType(InputType.TOUCH_MOVE);
}
return [];
}
fireAction(actionEvent: ActionEvent) {
const actions = this.getActionsForEvent(actionEvent);
for (let action of actions) {
action.options.fire(actionEvent as any);
}
}
}
================================================
FILE: packages/react-canvas-core/src/core-models/BaseEntity.ts
================================================
import { Toolkit } from '../Toolkit';
import _cloneDeep from 'lodash/cloneDeep';
import { CanvasEngine } from '../CanvasEngine';
import { BaseEvent, BaseListener, BaseObserver } from '../core/BaseObserver';
import { BaseModel } from './BaseModel';
export interface BaseEntityEvent<T extends BaseEntity = BaseEntity> extends BaseEvent {
entity: T;
}
export interface BaseEntityListener<T extends BaseEntity = BaseEntity> extends BaseListener {
lockChanged?(event: BaseEntityEvent<T> & { locked: boolean }): void;
}
/**
* @TODO move to enums
*/
export type BaseEntityType = 'node' | 'link' | 'port' | 'point';
export interface BaseEntityOptions {
id?: string;
locked?: boolean;
}
export type BaseEntityGenerics = {
LISTENER: BaseEntityListener;
OPTIONS: BaseEntityOptions;
};
export interface DeserializeEvent<T extends BaseEntity = BaseEntity> {
engine: CanvasEngine;
data: ReturnType<T['serialize']>;
registerModel(model: BaseModel);
getModel<T extends BaseModel>(id: string): Promise<T>;
}
export class BaseEntity<T extends BaseEntityGenerics = BaseEntityGenerics> extends BaseObserver<T['LISTENER']> {
protected options: T['OPTIONS'];
constructor(options: T['OPTIONS'] = {}) {
super();
this.options = {
id: Toolkit.UID(),
...options
};
}
getOptions() {
return this.options;
}
getID() {
return this.options.id;
}
doClone(lookupTable: { [s: string]: any } = {}, clone: any) {
/*noop*/
}
clone(lookupTable: { [s: string]: any } = {}) {
// try and use an existing clone first
if (lookupTable[this.options.id]) {
return lookupTable[this.options.id];
}
let clone = _cloneDeep(this);
clone.options = {
...this.options,
id: Toolkit.UID()
};
clone.clearListeners();
lookupTable[this.options.id] = clone;
this.doClone(lookupTable, clone);
return clone;
}
clearListeners() {
this.listeners = {};
}
deserialize(event: DeserializeEvent<this>) {
this.options.id = event.data.id;
this.options.locked = event.data.locked;
}
serialize() {
return {
id: this.options.id,
locked: this.options.locked
};
}
fireEvent<L extends Partial<BaseEntityEvent> & object>(event: L, k: keyof T['LISTENER']) {
super.fireEvent(
{
entity: this,
...event
},
k
);
}
public isLocked(): boolean {
return this.options.locked;
}
public setLocked(locked: boolean = true) {
this.options.locked = locked;
this.fireEvent(
{
locked: locked
},
'lockChanged'
);
}
}
================================================
FILE: packages/react-canvas-core/src/core-models/BaseModel.ts
================================================
import {
BaseEntity,
BaseEntityEvent,
BaseEntityGenerics,
BaseEntityListener,
BaseEntityOptions,
DeserializeEvent
} from './BaseEntity';
import { CanvasModel } from '../entities/canvas/CanvasModel';
export interface BaseModelListener extends BaseEntityListener {
selectionChanged?(event: BaseEntityEvent<BaseModel> & { isSelected: boolean }): void;
entityRemoved?(event: BaseEntityEvent<BaseModel>): void;
}
export interface BaseModelOptions extends BaseEntityOptions {
type?: string;
selected?: boolean;
extras?: any;
}
export interface BaseModelGenerics extends BaseEntityGenerics {
LISTENER: BaseModelListener;
PARENT: BaseEntity;
OPTIONS: BaseModelOptions;
}
export class BaseModel<G extends BaseModelGenerics = BaseModelGenerics> extends BaseEntity<G> {
protected parent: G['PARENT'];
constructor(options: G['OPTIONS']) {
super(options);
}
performanceTune() {
return true;
}
getParentCanvasModel(): CanvasModel {
if (!this.parent) {
return null;
}
if (this.parent instanceof CanvasModel) {
return this.parent;
} else if (this.parent instanceof BaseModel) {
return this.parent.getParentCanvasModel();
}
return null;
}
getParent(): G['PARENT'] {
return this.parent;
}
setParent(parent: G['PARENT']) {
this.parent = parent;
}
getSelectionEntities(): Array<BaseModel> {
return [this];
}
serialize() {
return {
...super.serialize(),
type: this.options.type,
selected: this.options.selected,
extras: this.options.extras
};
}
deserialize(event: DeserializeEvent<this>) {
super.deserialize(event);
this.options.extras = event.data.
gitextract_auqij3jd/ ├── .changeset/ │ ├── README.md │ ├── cold-drinks-unite.md │ └── config.json ├── .editorconfig ├── .envrc ├── .gitbook.yaml ├── .github/ │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows/ │ ├── prettier.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── diagrams-demo-gallery/ │ ├── .babelrc.json │ ├── .storybook/ │ │ ├── main.js │ │ ├── manager.js │ │ ├── preview-head.html │ │ ├── preview.js │ │ └── theme.js │ ├── CHANGELOG.md │ ├── demos/ │ │ ├── 1_SimpleUsage.stories.tsx │ │ ├── 2_AdvancedUsage.stories.tsx │ │ ├── 3_Customization.stories.tsx │ │ ├── 4_Libraries.stories.tsx │ │ ├── demo-alternative-linking/ │ │ │ ├── CreateLinkState.ts │ │ │ ├── DefaultState.ts │ │ │ └── index.tsx │ │ ├── demo-animation/ │ │ │ └── index.tsx │ │ ├── demo-canvas-drag/ │ │ │ └── index.tsx │ │ ├── demo-cloning/ │ │ │ └── index.tsx │ │ ├── demo-custom-action/ │ │ │ └── index.tsx │ │ ├── demo-custom-link-label/ │ │ │ ├── EditableLabelFactory.tsx │ │ │ ├── EditableLabelModel.ts │ │ │ ├── EditableLabelWidget.tsx │ │ │ └── index.tsx │ │ ├── demo-custom-link1/ │ │ │ └── index.tsx │ │ ├── demo-custom-link2/ │ │ │ └── index.tsx │ │ ├── demo-custom-node1/ │ │ │ ├── DiamondNodeFactory.tsx │ │ │ ├── DiamondNodeModel.ts │ │ │ ├── DiamondNodeWidget.tsx │ │ │ ├── DiamondPortModel.ts │ │ │ ├── SimplePortFactory.ts │ │ │ └── index.tsx │ │ ├── demo-custom_delete_keys/ │ │ │ └── index.tsx │ │ ├── demo-dagre/ │ │ │ └── index.tsx │ │ ├── demo-drag-and-drop/ │ │ │ ├── Application.ts │ │ │ ├── components/ │ │ │ │ ├── BodyWidget.tsx │ │ │ │ ├── TrayItemWidget.tsx │ │ │ │ └── TrayWidget.tsx │ │ │ └── index.tsx │ │ ├── demo-dynamic-ports/ │ │ │ └── index.tsx │ │ ├── demo-grid/ │ │ │ └── index.tsx │ │ ├── demo-labelled-links/ │ │ │ └── index.tsx │ │ ├── demo-listeners/ │ │ │ └── index.tsx │ │ ├── demo-locks/ │ │ │ └── index.tsx │ │ ├── demo-mutate-graph/ │ │ │ └── index.tsx │ │ ├── demo-pan-and-zoom/ │ │ │ └── index.tsx │ │ ├── demo-performance/ │ │ │ └── index.tsx │ │ ├── demo-right-angles-routing/ │ │ │ └── index.tsx │ │ ├── demo-serializing/ │ │ │ └── index.tsx │ │ ├── demo-simple/ │ │ │ └── index.tsx │ │ ├── demo-simple-flow/ │ │ │ └── index.tsx │ │ ├── demo-smart-routing/ │ │ │ └── index.tsx │ │ ├── demo-zoom-to-fit/ │ │ │ └── index.tsx │ │ ├── demo-zoom-to-fit-nodes/ │ │ │ └── index.tsx │ │ └── helpers/ │ │ ├── DemoCanvasWidget.tsx │ │ ├── DemoWorkspaceWidget.tsx │ │ ├── Helper.tsx │ │ └── index.css │ ├── package.json │ └── tsconfig.json ├── diagrams-demo-project/ │ ├── .babelrc │ ├── CHANGELOG.md │ ├── README.md │ ├── index.html │ ├── package.json │ ├── src/ │ │ ├── BodyWidget.tsx │ │ ├── custom-node-js/ │ │ │ ├── JSCustomNodeFactory.jsx │ │ │ ├── JSCustomNodeModel.js │ │ │ └── JSCustomNodeWidget.jsx │ │ ├── custom-node-ts/ │ │ │ ├── TSCustomNodeFactory.tsx │ │ │ ├── TSCustomNodeModel.ts │ │ │ └── TSCustomNodeWidget.tsx │ │ ├── main.css │ │ └── main.tsx │ ├── tsconfig.json │ └── webpack.config.js ├── docs/ │ ├── README.md │ ├── about-the-project/ │ │ ├── architecture-questions.md │ │ └── testing.md │ ├── customizing/ │ │ ├── README.md │ │ ├── extending-default-links.md │ │ ├── nodes.md │ │ └── ports.md │ └── getting-started/ │ ├── README.md │ └── using-the-library.md ├── package.json ├── packages/ │ ├── geometry/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── BezierCurve.ts │ │ │ ├── Bounds.ts │ │ │ ├── Matrix.ts │ │ │ ├── Point.ts │ │ │ ├── Polygon.ts │ │ │ ├── Rectangle.ts │ │ │ ├── index.ts │ │ │ └── toolkit.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── react-canvas-core/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── CanvasEngine.ts │ │ │ ├── Toolkit.ts │ │ │ ├── actions/ │ │ │ │ ├── DeleteItemsAction.ts │ │ │ │ ├── PanAndZoomCanvasAction.ts │ │ │ │ └── ZoomCanvasAction.ts │ │ │ ├── core/ │ │ │ │ ├── AbstractFactory.ts │ │ │ │ ├── AbstractModelFactory.ts │ │ │ │ ├── AbstractReactFactory.tsx │ │ │ │ ├── BaseObserver.ts │ │ │ │ ├── FactoryBank.ts │ │ │ │ └── ModelGeometryInterface.ts │ │ │ ├── core-actions/ │ │ │ │ ├── Action.ts │ │ │ │ └── ActionEventBus.ts │ │ │ ├── core-models/ │ │ │ │ ├── BaseEntity.ts │ │ │ │ ├── BaseModel.ts │ │ │ │ └── BasePositionModel.ts │ │ │ ├── core-state/ │ │ │ │ ├── AbstractDisplacementState.ts │ │ │ │ ├── State.ts │ │ │ │ └── StateMachine.ts │ │ │ ├── entities/ │ │ │ │ ├── canvas/ │ │ │ │ │ ├── CanvasModel.ts │ │ │ │ │ └── CanvasWidget.tsx │ │ │ │ ├── layer/ │ │ │ │ │ ├── LayerModel.ts │ │ │ │ │ ├── SmartLayerWidget.tsx │ │ │ │ │ └── TransformLayerWidget.tsx │ │ │ │ └── selection/ │ │ │ │ ├── SelectionBoxLayerFactory.tsx │ │ │ │ ├── SelectionBoxWidget.tsx │ │ │ │ └── SelectionLayerModel.ts │ │ │ ├── index.ts │ │ │ ├── states/ │ │ │ │ ├── DefaultState.ts │ │ │ │ ├── DragCanvasState.ts │ │ │ │ ├── MoveItemsState.ts │ │ │ │ ├── SelectingState.ts │ │ │ │ └── SelectionBoxState.ts │ │ │ └── widgets/ │ │ │ └── PeformanceWidget.tsx │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── react-diagrams/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── react-diagrams-core/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── DiagramEngine.ts │ │ │ ├── entities/ │ │ │ │ ├── label/ │ │ │ │ │ ├── LabelModel.ts │ │ │ │ │ └── LabelWidget.tsx │ │ │ │ ├── link/ │ │ │ │ │ ├── LinkModel.ts │ │ │ │ │ ├── LinkWidget.tsx │ │ │ │ │ └── PointModel.ts │ │ │ │ ├── link-layer/ │ │ │ │ │ ├── LinkLayerFactory.tsx │ │ │ │ │ ├── LinkLayerModel.ts │ │ │ │ │ └── LinkLayerWidget.tsx │ │ │ │ ├── node/ │ │ │ │ │ ├── NodeModel.ts │ │ │ │ │ └── NodeWidget.tsx │ │ │ │ ├── node-layer/ │ │ │ │ │ ├── NodeLayerFactory.tsx │ │ │ │ │ ├── NodeLayerModel.ts │ │ │ │ │ └── NodeLayerWidget.tsx │ │ │ │ └── port/ │ │ │ │ ├── PortModel.ts │ │ │ │ └── PortWidget.tsx │ │ │ ├── index.ts │ │ │ ├── models/ │ │ │ │ └── DiagramModel.ts │ │ │ └── states/ │ │ │ ├── DefaultDiagramState.ts │ │ │ ├── DragDiagramItemsState.ts │ │ │ └── DragNewLinkState.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── react-diagrams-defaults/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── label/ │ │ │ │ ├── DefaultLabelFactory.tsx │ │ │ │ ├── DefaultLabelModel.tsx │ │ │ │ └── DefaultLabelWidget.tsx │ │ │ ├── link/ │ │ │ │ ├── DefaultLinkFactory.tsx │ │ │ │ ├── DefaultLinkModel.ts │ │ │ │ ├── DefaultLinkPointWidget.tsx │ │ │ │ ├── DefaultLinkSegmentWidget.tsx │ │ │ │ └── DefaultLinkWidget.tsx │ │ │ ├── node/ │ │ │ │ ├── DefaultNodeFactory.tsx │ │ │ │ ├── DefaultNodeModel.ts │ │ │ │ └── DefaultNodeWidget.tsx │ │ │ └── port/ │ │ │ ├── DefaultPortFactory.tsx │ │ │ ├── DefaultPortLabelWidget.tsx │ │ │ └── DefaultPortModel.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js │ └── react-diagrams-routing/ │ ├── .npmignore │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src/ │ │ ├── dagre/ │ │ │ └── DagreEngine.ts │ │ ├── engine/ │ │ │ └── PathFinding.ts │ │ ├── index.ts │ │ └── link/ │ │ ├── PathFindingLinkFactory.tsx │ │ ├── PathFindingLinkModel.ts │ │ ├── PathFindingLinkWidget.tsx │ │ ├── RightAngleLinkFactory.tsx │ │ ├── RightAngleLinkModel.ts │ │ └── RightAngleLinkWidget.tsx │ ├── tests/ │ │ └── PathFinding.test.tsx │ ├── tsconfig.json │ └── webpack.config.js ├── pnpm-workspace.yaml ├── tsconfig.base.json ├── tsconfig.json └── webpack.shared.js
SYMBOL INDEX (776 symbols across 119 files)
FILE: diagrams-demo-gallery/demos/demo-alternative-linking/CreateLinkState.ts
class CreateLinkState (line 8) | class CreateLinkState extends State<DiagramEngine> {
method constructor (line 12) | constructor() {
method clearState (line 85) | clearState() {
FILE: diagrams-demo-gallery/demos/demo-alternative-linking/DefaultState.ts
class DefaultState (line 13) | class DefaultState extends State<DiagramEngine> {
method constructor (line 18) | constructor() {
FILE: diagrams-demo-gallery/demos/demo-animation/index.tsx
class NodeDelayedPosition (line 11) | class NodeDelayedPosition extends React.Component<any, any> {
method constructor (line 12) | constructor(props) {
method render (line 16) | render() {
FILE: diagrams-demo-gallery/demos/demo-canvas-drag/index.tsx
class CanvasDragToggle (line 10) | class CanvasDragToggle extends React.Component<any, any> {
method render (line 23) | render() {
FILE: diagrams-demo-gallery/demos/demo-cloning/index.tsx
class CloneSelected (line 11) | class CloneSelected extends React.Component<any, any> {
method constructor (line 12) | constructor(props: any) {
method cloneSelected (line 17) | cloneSelected() {
method render (line 43) | render() {
FILE: diagrams-demo-gallery/demos/demo-custom-action/index.tsx
type CustomDeleteItemsActionOptions (line 7) | interface CustomDeleteItemsActionOptions {
class CustomDeleteItemsAction (line 14) | class CustomDeleteItemsAction extends Action {
method constructor (line 15) | constructor(options: CustomDeleteItemsActionOptions = {}) {
FILE: diagrams-demo-gallery/demos/demo-custom-link-label/EditableLabelFactory.tsx
class EditableLabelFactory (line 9) | class EditableLabelFactory extends AbstractReactFactory<EditableLabelMod...
method constructor (line 10) | constructor() {
method generateModel (line 14) | generateModel(): EditableLabelModel {
method generateReactWidget (line 18) | generateReactWidget(event: GenerateWidgetEvent<EditableLabelModel>): J...
FILE: diagrams-demo-gallery/demos/demo-custom-link-label/EditableLabelModel.ts
type EditableLabelOptions (line 4) | interface EditableLabelOptions extends BaseModelOptions {
class EditableLabelModel (line 8) | class EditableLabelModel extends LabelModel {
method constructor (line 11) | constructor(options: EditableLabelOptions = {}) {
method serialize (line 19) | serialize() {
method deserialize (line 26) | deserialize(event: DeserializeEvent<this>): void {
FILE: diagrams-demo-gallery/demos/demo-custom-link-label/EditableLabelWidget.tsx
type FlowAliasLabelWidgetProps (line 7) | interface FlowAliasLabelWidgetProps {
FILE: diagrams-demo-gallery/demos/demo-custom-link1/index.tsx
class AdvancedLinkModel (line 12) | class AdvancedLinkModel extends DefaultLinkModel {
method constructor (line 13) | constructor() {
class AdvancedPortModel (line 21) | class AdvancedPortModel extends DefaultPortModel {
method createLinkModel (line 22) | createLinkModel(): AdvancedLinkModel | null {
class AdvancedLinkSegment (line 27) | class AdvancedLinkSegment extends React.Component<{ model: AdvancedLinkM...
method constructor (line 35) | constructor(props) {
method componentDidMount (line 40) | componentDidMount() {
method componentWillUnmount (line 64) | componentWillUnmount() {
method render (line 68) | render() {
class AdvancedLinkFactory (line 92) | class AdvancedLinkFactory extends DefaultLinkFactory {
method constructor (line 93) | constructor() {
method generateModel (line 97) | generateModel(): AdvancedLinkModel {
method generateLinkSegment (line 101) | generateLinkSegment(model: AdvancedLinkModel, selected: boolean, path:...
FILE: diagrams-demo-gallery/demos/demo-custom-link2/index.tsx
class AdvancedLinkModel (line 17) | class AdvancedLinkModel extends DefaultLinkModel {
method constructor (line 18) | constructor() {
class AdvancedPortModel (line 26) | class AdvancedPortModel extends DefaultPortModel {
method createLinkModel (line 27) | createLinkModel(): AdvancedLinkModel | null {
type AdvancedLinkWWidgetProps (line 61) | interface AdvancedLinkWWidgetProps {
class AdvancedLinkWidget (line 69) | class AdvancedLinkWidget extends React.Component<AdvancedLinkWWidgetProp...
method generateArrow (line 111) | generateArrow(point: PointModel, previousPoint: PointModel): JSX.Eleme...
method render (line 123) | render() {
class AdvancedLinkFactory (line 160) | class AdvancedLinkFactory extends DefaultLinkFactory {
method constructor (line 161) | constructor() {
method generateModel (line 165) | generateModel(): AdvancedLinkModel {
method generateReactWidget (line 169) | generateReactWidget(event): JSX.Element {
FILE: diagrams-demo-gallery/demos/demo-custom-node1/DiamondNodeFactory.tsx
class DiamondNodeFactory (line 8) | class DiamondNodeFactory extends AbstractReactFactory<DiamondNodeModel, ...
method constructor (line 9) | constructor() {
method generateReactWidget (line 13) | generateReactWidget(event): JSX.Element {
method generateModel (line 17) | generateModel(event) {
FILE: diagrams-demo-gallery/demos/demo-custom-node1/DiamondNodeModel.ts
type DiamondNodeModelGenerics (line 4) | interface DiamondNodeModelGenerics {
class DiamondNodeModel (line 8) | class DiamondNodeModel extends NodeModel<NodeModelGenerics & DiamondNode...
method constructor (line 9) | constructor() {
FILE: diagrams-demo-gallery/demos/demo-custom-node1/DiamondNodeWidget.tsx
type DiamondNodeWidgetProps (line 6) | interface DiamondNodeWidgetProps {
class DiamondNodeWidget (line 30) | class DiamondNodeWidget extends React.Component<DiamondNodeWidgetProps> {
method render (line 31) | render() {
FILE: diagrams-demo-gallery/demos/demo-custom-node1/DiamondPortModel.ts
class DiamondPortModel (line 3) | class DiamondPortModel extends PortModel {
method constructor (line 4) | constructor(alignment: PortModelAlignment) {
method createLinkModel (line 12) | createLinkModel(): LinkModel {
FILE: diagrams-demo-gallery/demos/demo-custom-node1/SimplePortFactory.ts
class SimplePortFactory (line 4) | class SimplePortFactory extends AbstractModelFactory<PortModel, DiagramE...
method constructor (line 7) | constructor(type: string, cb: (initialConfig?: any) => PortModel) {
method generateModel (line 12) | generateModel(event): PortModel {
FILE: diagrams-demo-gallery/demos/demo-dagre/index.tsx
function createNode (line 15) | function createNode(name): any {
function connectNodes (line 21) | function connectNodes(nodeFrom, nodeTo, engine: DiagramEngine) {
function genDagreEngine (line 36) | function genDagreEngine() {
function autoDistribute (line 49) | function autoDistribute(engine: DiagramEngine) {
function autoRefreshLinks (line 59) | function autoRefreshLinks(engine: DiagramEngine) {
function reroute (line 70) | function reroute(engine: DiagramEngine) {
function DemoWidget (line 74) | function DemoWidget(props) {
FILE: diagrams-demo-gallery/demos/demo-drag-and-drop/Application.ts
class Application (line 6) | class Application {
method constructor (line 10) | constructor() {
method newModel (line 15) | public newModel() {
method getActiveDiagram (line 35) | public getActiveDiagram(): SRD.DiagramModel {
method getDiagramEngine (line 39) | public getDiagramEngine(): SRD.DiagramEngine {
FILE: diagrams-demo-gallery/demos/demo-drag-and-drop/components/BodyWidget.tsx
type BodyWidgetProps (line 11) | interface BodyWidgetProps {
class BodyWidget (line 45) | class BodyWidget extends React.Component<BodyWidgetProps> {
method render (line 46) | render() {
FILE: diagrams-demo-gallery/demos/demo-drag-and-drop/components/TrayItemWidget.tsx
type TrayItemWidgetProps (line 4) | interface TrayItemWidgetProps {
class TrayItemWidget (line 23) | class TrayItemWidget extends React.Component<TrayItemWidgetProps> {
method render (line 24) | render() {
FILE: diagrams-demo-gallery/demos/demo-drag-and-drop/components/TrayWidget.tsx
class TrayWidget (line 13) | class TrayWidget extends React.Component {
method render (line 14) | render() {
FILE: diagrams-demo-gallery/demos/demo-dynamic-ports/index.tsx
class CloneSelected (line 8) | class CloneSelected extends React.Component<{ model: DiagramModel; engin...
method render (line 21) | render() {
FILE: diagrams-demo-gallery/demos/demo-mutate-graph/index.tsx
class NodeDelayedPosition (line 11) | class NodeDelayedPosition extends React.Component<any, any> {
method constructor (line 12) | constructor(props) {
method updatePosition (line 18) | updatePosition() {
method updatePositionViaSerialize (line 27) | updatePositionViaSerialize() {
method render (line 41) | render() {
FILE: diagrams-demo-gallery/demos/demo-pan-and-zoom/index.tsx
class CanvasPanAndZoomToggle (line 11) | class CanvasPanAndZoomToggle extends React.Component<any, any> {
method render (line 12) | render() {
FILE: diagrams-demo-gallery/demos/demo-performance/index.tsx
function generateNodes (line 37) | function generateNodes(model: DiagramModel, offsetX: number, offsetY: nu...
FILE: diagrams-demo-gallery/demos/demo-right-angles-routing/index.tsx
class RightAnglePortModel (line 16) | class RightAnglePortModel extends DefaultPortModel {
method createLinkModel (line 17) | createLinkModel(factory?: AbstractModelFactory<LinkModel>) {
FILE: diagrams-demo-gallery/demos/demo-zoom-to-fit-nodes/index.tsx
function generateNodes (line 41) | function generateNodes(model: DiagramModel, offsetX: number, offsetY: nu...
FILE: diagrams-demo-gallery/demos/demo-zoom-to-fit/index.tsx
function generateNodes (line 39) | function generateNodes(model: DiagramModel, offsetX: number, offsetY: nu...
FILE: diagrams-demo-gallery/demos/helpers/DemoCanvasWidget.tsx
type DemoCanvasWidgetProps (line 5) | interface DemoCanvasWidgetProps {
class DemoCanvasWidget (line 58) | class DemoCanvasWidget extends React.Component<React.PropsWithChildren<D...
method render (line 59) | render() {
FILE: diagrams-demo-gallery/demos/helpers/DemoWorkspaceWidget.tsx
type DemoWorkspaceWidgetProps (line 4) | interface DemoWorkspaceWidgetProps {
class DemoWorkspaceWidget (line 46) | class DemoWorkspaceWidget extends React.Component<React.PropsWithChildre...
method render (line 47) | render() {
FILE: diagrams-demo-gallery/demos/helpers/Helper.tsx
class Helper (line 3) | class Helper {
method logMousePosition (line 8) | static logMousePosition() {
FILE: diagrams-demo-project/src/BodyWidget.tsx
type BodyWidgetProps (line 4) | interface BodyWidgetProps {
class BodyWidget (line 8) | class BodyWidget extends React.Component<BodyWidgetProps> {
method render (line 9) | render() {
FILE: diagrams-demo-project/src/custom-node-js/JSCustomNodeFactory.jsx
class JSCustomNodeFactory (line 6) | class JSCustomNodeFactory extends AbstractReactFactory {
method constructor (line 7) | constructor() {
method generateModel (line 11) | generateModel(event) {
method generateReactWidget (line 15) | generateReactWidget(event) {
FILE: diagrams-demo-project/src/custom-node-js/JSCustomNodeModel.js
class JSCustomNodeModel (line 6) | class JSCustomNodeModel extends NodeModel {
method constructor (line 7) | constructor(options = {}) {
method serialize (line 29) | serialize() {
method deserialize (line 36) | deserialize(ob, engine) {
FILE: diagrams-demo-project/src/custom-node-js/JSCustomNodeWidget.jsx
class JSCustomNodeWidget (line 4) | class JSCustomNodeWidget extends React.Component {
method render (line 5) | render() {
FILE: diagrams-demo-project/src/custom-node-ts/TSCustomNodeFactory.tsx
class TSCustomNodeFactory (line 8) | class TSCustomNodeFactory extends AbstractReactFactory<TSCustomNodeModel...
method constructor (line 9) | constructor() {
method generateModel (line 13) | generateModel(initialConfig) {
method generateReactWidget (line 17) | generateReactWidget(event): JSX.Element {
FILE: diagrams-demo-project/src/custom-node-ts/TSCustomNodeModel.ts
type TSCustomNodeModelOptions (line 3) | interface TSCustomNodeModelOptions extends BaseModelOptions {
class TSCustomNodeModel (line 7) | class TSCustomNodeModel extends NodeModel {
method constructor (line 10) | constructor(options: TSCustomNodeModelOptions = {}) {
method serialize (line 32) | serialize() {
method deserialize (line 39) | deserialize(event): void {
FILE: diagrams-demo-project/src/custom-node-ts/TSCustomNodeWidget.tsx
type TSCustomNodeWidgetProps (line 5) | interface TSCustomNodeWidgetProps {
type TSCustomNodeWidgetState (line 10) | interface TSCustomNodeWidgetState {}
class TSCustomNodeWidget (line 12) | class TSCustomNodeWidget extends React.Component<TSCustomNodeWidgetProps...
method constructor (line 13) | constructor(props: TSCustomNodeWidgetProps) {
method render (line 18) | render() {
FILE: packages/geometry/src/BezierCurve.ts
type BezierCurvepPoints (line 4) | enum BezierCurvepPoints {
class BezierCurve (line 11) | class BezierCurve extends Polygon {
method constructor (line 12) | constructor() {
method getSVGCurve (line 16) | getSVGCurve(): string {
method setPoints (line 20) | setPoints(points: Point[]) {
method getSource (line 27) | getSource(): Point {
method getSourceControl (line 31) | getSourceControl(): Point {
method getTargetControl (line 35) | getTargetControl(): Point {
method getTarget (line 39) | getTarget(): Point {
method setSource (line 43) | setSource(point: Point) {
method setSourceControl (line 47) | setSourceControl(point: Point) {
method setTargetControl (line 51) | setTargetControl(point: Point) {
method setTarget (line 55) | setTarget(point: Point) {
FILE: packages/geometry/src/Bounds.ts
type BoundsCorner (line 3) | enum BoundsCorner {
type Bounds (line 10) | type Bounds = { [k in BoundsCorner]: Point };
FILE: packages/geometry/src/Matrix.ts
class Matrix (line 3) | class Matrix {
method constructor (line 6) | constructor(matrix: number[][]) {
method mmul (line 10) | mmul(matrix: Matrix): Matrix {
method asArray (line 17) | asArray(): number[][] {
method get (line 21) | get(rowIndex: number, columnIndex: number): number {
method multiply (line 25) | public static multiply(...matrices: Matrix[]): Matrix {
method scaleMatrix (line 33) | public static scaleMatrix(x: number, y: number): Matrix {
method translateMatrix (line 41) | public static translateMatrix(x: number, y: number): Matrix {
method rotateMatrix (line 49) | public static rotateMatrix(deg: number): Matrix {
method createScaleMatrix (line 57) | static createScaleMatrix(x, y, origin: Point): Matrix {
method createRotateMatrix (line 65) | static createRotateMatrix(deg: number, origin: Point): Matrix {
FILE: packages/geometry/src/Point.ts
class Point (line 3) | class Point {
method constructor (line 7) | constructor(x: number = 0, y: number = 0) {
method translate (line 12) | translate(x: number, y: number) {
method clone (line 17) | clone() {
method toSVG (line 21) | toSVG() {
method asMatrix (line 25) | asMatrix() {
method transform (line 29) | transform(matrix: Matrix) {
method middlePoint (line 35) | public static middlePoint(pointA: Point, pointB: Point): Point {
FILE: packages/geometry/src/Polygon.ts
class Polygon (line 8) | class Polygon {
method constructor (line 11) | constructor(points: Point[] = []) {
method serialize (line 15) | serialize() {
method deserialize (line 21) | deserialize(data: any) {
method scale (line 27) | scale(x, y, origin: Point) {
method transform (line 34) | transform(matrix: Matrix) {
method setPoints (line 40) | setPoints(points: Point[]) {
method getPoints (line 44) | getPoints(): Point[] {
method rotate (line 48) | rotate(degrees: number) {
method translate (line 52) | translate(offsetX: number, offsetY: number) {
method doClone (line 58) | doClone(ob: this) {
method clone (line 64) | clone(): this {
method getOrigin (line 70) | getOrigin(): Point {
method getBoundingBox (line 78) | getBoundingBox(): Bounds {
FILE: packages/geometry/src/Rectangle.ts
class Rectangle (line 5) | class Rectangle extends Polygon {
method fromPositionAndSize (line 6) | static fromPositionAndSize(x: number, y: number, width: number, height...
method fromPointAndSize (line 10) | static fromPointAndSize(position: Point, width: number, height: number) {
method constructor (line 14) | constructor(points?: Bounds) {
method updateDimensions (line 27) | updateDimensions(x: number, y: number, width: number, height: number) {
method setPoints (line 37) | setPoints(points: Point[]) {
method containsPoint (line 44) | containsPoint(point: Point) {
method getWidth (line 51) | getWidth(): number {
method getHeight (line 57) | getHeight(): number {
method getTopMiddle (line 64) | getTopMiddle(): Point {
method getBottomMiddle (line 68) | getBottomMiddle(): Point {
method getLeftMiddle (line 72) | getLeftMiddle(): Point {
method getRightMiddle (line 76) | getRightMiddle(): Point {
method getTopLeft (line 80) | getTopLeft(): Point {
method getTopRight (line 84) | getTopRight(): Point {
method getBottomRight (line 88) | getBottomRight(): Point {
method getBottomLeft (line 92) | getBottomLeft(): Point {
FILE: packages/react-canvas-core/src/CanvasEngine.ts
type CanvasEngineListener (line 16) | interface CanvasEngineListener extends BaseListener {
type CanvasEngineOptions (line 27) | interface CanvasEngineOptions {
class CanvasEngine (line 37) | class CanvasEngine<
method constructor (line 48) | constructor(options: CanvasEngineOptions = {}) {
method getStateMachine (line 75) | getStateMachine() {
method getRelativeMousePoint (line 79) | getRelativeMousePoint(event: { clientX: number; clientY: number }): Po...
method getRelativePoint (line 87) | getRelativePoint(x, y): Point {
method registerFactoryBank (line 92) | registerFactoryBank(factory: FactoryBank) {
method getActionEventBus (line 103) | getActionEventBus() {
method getLayerFactories (line 107) | getLayerFactories() {
method getFactoryForLayer (line 111) | getFactoryForLayer<F extends AbstractReactFactory<LayerModel>>(layer: ...
method setModel (line 118) | setModel(model: M) {
method getModel (line 127) | getModel(): M {
method repaintCanvas (line 133) | repaintCanvas(promise?): Promise<any> | void {
method setCanvas (line 169) | setCanvas(canvas?: HTMLDivElement) {
method getCanvas (line 178) | getCanvas() {
method getMouseElement (line 182) | getMouseElement(event: MouseEvent): BaseModel {
method zoomToFit (line 186) | zoomToFit() {
FILE: packages/react-canvas-core/src/Toolkit.ts
class Toolkit (line 1) | class Toolkit {
method UID (line 9) | public static UID(): string {
method closest (line 21) | public static closest(element: Element, selector: string) {
FILE: packages/react-canvas-core/src/actions/DeleteItemsAction.ts
type DeleteItemsActionOptions (line 6) | interface DeleteItemsActionOptions {
class DeleteItemsAction (line 19) | class DeleteItemsAction extends Action {
method constructor (line 20) | constructor(options: DeleteItemsActionOptions = {}) {
FILE: packages/react-canvas-core/src/actions/PanAndZoomCanvasAction.ts
type PanAndZoomCanvasActionOptions (line 4) | interface PanAndZoomCanvasActionOptions {
class PanAndZoomCanvasAction (line 8) | class PanAndZoomCanvasAction extends Action {
method constructor (line 9) | constructor(options: PanAndZoomCanvasActionOptions = {}) {
FILE: packages/react-canvas-core/src/actions/ZoomCanvasAction.ts
type ZoomCanvasActionOptions (line 4) | interface ZoomCanvasActionOptions {
class ZoomCanvasAction (line 8) | class ZoomCanvasAction extends Action {
method constructor (line 9) | constructor(options: ZoomCanvasActionOptions = {}) {
FILE: packages/react-canvas-core/src/core-actions/Action.ts
type InputType (line 6) | enum InputType {
type Mapping (line 18) | interface Mapping {
type ActionEvent (line 30) | interface ActionEvent<Event extends SyntheticEvent = SyntheticEvent, Mod...
type ActionOptions (line 35) | interface ActionOptions {
class Action (line 40) | class Action<T extends CanvasEngine = CanvasEngine> {
method constructor (line 45) | constructor(options: ActionOptions) {
method setEngine (line 50) | setEngine(engine: T) {
FILE: packages/react-canvas-core/src/core-actions/ActionEventBus.ts
class ActionEventBus (line 8) | class ActionEventBus {
method constructor (line 13) | constructor(engine: CanvasEngine) {
method getKeys (line 19) | getKeys(): string[] {
method registerAction (line 23) | registerAction(action: Action): () => void {
method deregisterAction (line 31) | deregisterAction(action: Action) {
method getActionsForType (line 36) | getActionsForType(type: InputType): Action[] {
method getModelForEvent (line 42) | getModelForEvent(actionEvent: ActionEvent<MouseEvent>): BaseModel {
method getActionsForEvent (line 49) | getActionsForEvent(actionEvent: ActionEvent): Action[] {
method fireAction (line 78) | fireAction(actionEvent: ActionEvent) {
FILE: packages/react-canvas-core/src/core-models/BaseEntity.ts
type BaseEntityEvent (line 7) | interface BaseEntityEvent<T extends BaseEntity = BaseEntity> extends Bas...
type BaseEntityListener (line 11) | interface BaseEntityListener<T extends BaseEntity = BaseEntity> extends ...
type BaseEntityType (line 18) | type BaseEntityType = 'node' | 'link' | 'port' | 'point';
type BaseEntityOptions (line 20) | interface BaseEntityOptions {
type BaseEntityGenerics (line 25) | type BaseEntityGenerics = {
type DeserializeEvent (line 30) | interface DeserializeEvent<T extends BaseEntity = BaseEntity> {
class BaseEntity (line 37) | class BaseEntity<T extends BaseEntityGenerics = BaseEntityGenerics> exte...
method constructor (line 40) | constructor(options: T['OPTIONS'] = {}) {
method getOptions (line 48) | getOptions() {
method getID (line 52) | getID() {
method doClone (line 56) | doClone(lookupTable: { [s: string]: any } = {}, clone: any) {
method clone (line 60) | clone(lookupTable: { [s: string]: any } = {}) {
method clearListeners (line 77) | clearListeners() {
method deserialize (line 81) | deserialize(event: DeserializeEvent<this>) {
method serialize (line 86) | serialize() {
method fireEvent (line 93) | fireEvent<L extends Partial<BaseEntityEvent> & object>(event: L, k: ke...
method isLocked (line 103) | public isLocked(): boolean {
method setLocked (line 107) | public setLocked(locked: boolean = true) {
FILE: packages/react-canvas-core/src/core-models/BaseModel.ts
type BaseModelListener (line 11) | interface BaseModelListener extends BaseEntityListener {
type BaseModelOptions (line 17) | interface BaseModelOptions extends BaseEntityOptions {
type BaseModelGenerics (line 23) | interface BaseModelGenerics extends BaseEntityGenerics {
class BaseModel (line 29) | class BaseModel<G extends BaseModelGenerics = BaseModelGenerics> extends...
method constructor (line 32) | constructor(options: G['OPTIONS']) {
method performanceTune (line 36) | performanceTune() {
method getParentCanvasModel (line 40) | getParentCanvasModel(): CanvasModel {
method getParent (line 52) | getParent(): G['PARENT'] {
method setParent (line 56) | setParent(parent: G['PARENT']) {
method getSelectionEntities (line 60) | getSelectionEntities(): Array<BaseModel> {
method serialize (line 64) | serialize() {
method deserialize (line 73) | deserialize(event: DeserializeEvent<this>) {
method getType (line 79) | getType(): string {
method isSelected (line 83) | isSelected(): boolean {
method isLocked (line 87) | isLocked(): boolean {
method setSelected (line 100) | setSelected(selected: boolean = true) {
method remove (line 113) | remove() {
FILE: packages/react-canvas-core/src/core-models/BasePositionModel.ts
type BasePositionModelListener (line 6) | interface BasePositionModelListener extends BaseModelListener {
type BasePositionModelOptions (line 10) | interface BasePositionModelOptions extends BaseModelOptions {
type BasePositionModelGenerics (line 14) | interface BasePositionModelGenerics extends BaseModelGenerics {
class BasePositionModel (line 19) | class BasePositionModel<G extends BasePositionModelGenerics = BasePositi...
method constructor (line 25) | constructor(options: G['OPTIONS']) {
method setPosition (line 32) | setPosition(x: number | Point, y?: number): void {
method getBoundingBox (line 41) | getBoundingBox(): Rectangle {
method deserialize (line 45) | deserialize(event: DeserializeEvent<this>) {
method serialize (line 50) | serialize() {
method getPosition (line 58) | getPosition(): Point {
method getX (line 62) | getX() {
method getY (line 66) | getY() {
FILE: packages/react-canvas-core/src/core-state/AbstractDisplacementState.ts
type AbstractDisplacementStateEvent (line 5) | interface AbstractDisplacementStateEvent {
method constructor (line 19) | constructor(options: StateOptions) {
method handleMoveStart (line 83) | protected handleMoveStart(x: number, y: number): void {
method handleMove (line 91) | protected handleMove(x: number, y: number, event: React.MouseEvent | Rea...
method handleMoveEnd (line 101) | protected handleMoveEnd(): void {
FILE: packages/react-canvas-core/src/core-state/State.ts
type StateOptions (line 6) | interface StateOptions {
method constructor (line 20) | constructor(options: StateOptions) {
method setEngine (line 27) | setEngine(engine: E) {
method getOptions (line 31) | getOptions() {
method eject (line 35) | eject() {
method transitionWithEvent (line 39) | transitionWithEvent(state: State, event: ActionEvent<SyntheticEvent>) {
method registerAction (line 44) | registerAction(action: Action) {
method tryActivateParentState (line 48) | tryActivateParentState(keys: string[]) {
method tryActivateChildState (line 56) | tryActivateChildState(keys: string[]) {
method findStateToActivate (line 65) | findStateToActivate(keys: string[]) {
method isKeysFullfilled (line 75) | isKeysFullfilled(keys: string[]) {
method activated (line 79) | activated(previous: State) {
method deactivated (line 110) | deactivated(next: State) {
FILE: packages/react-canvas-core/src/core-state/StateMachine.ts
type StateMachineListener (line 6) | interface StateMachineListener extends BaseListener {
class StateMachine (line 10) | class StateMachine extends BaseObserver<StateMachineListener> {
method constructor (line 15) | constructor(engine: CanvasEngine) {
method getCurrentState (line 21) | getCurrentState() {
method pushState (line 25) | pushState(state: State) {
method popState (line 30) | popState() {
method setState (line 35) | setState(state: State) {
FILE: packages/react-canvas-core/src/core/AbstractFactory.ts
method constructor (line 19) | constructor(type: string) {
method setDiagramEngine (line 23) | setDiagramEngine(engine: E) {
method setFactoryBank (line 27) | setFactoryBank(bank: FactoryBank) {
method getType (line 31) | getType(): string {
FILE: packages/react-canvas-core/src/core/AbstractModelFactory.ts
type GenerateModelEvent (line 5) | interface GenerateModelEvent {
FILE: packages/react-canvas-core/src/core/AbstractReactFactory.tsx
type GenerateWidgetEvent (line 6) | interface GenerateWidgetEvent<T extends BaseModel> {
FILE: packages/react-canvas-core/src/core/BaseObserver.ts
type BaseEvent (line 3) | interface BaseEvent {
type BaseEventProxy (line 8) | interface BaseEventProxy extends BaseEvent {
type BaseListener (line 15) | type BaseListener = {
type ListenerHandle (line 32) | interface ListenerHandle {
class BaseObserver (line 51) | class BaseObserver<L extends BaseListener = BaseListener> {
method constructor (line 54) | constructor() {
method fireEventInternal (line 58) | private fireEventInternal(fire: boolean, k: keyof L, event: BaseEvent) {
method fireEvent (line 71) | fireEvent<K extends keyof L>(event: Partial<Parameters<L[K]>[0]>, k: k...
method iterateListeners (line 96) | iterateListeners(cb: (listener: L) => any) {
method getListenerHandle (line 106) | getListenerHandle(listener: L): ListenerHandle {
method registerListener (line 120) | registerListener(listener: L): ListenerHandle {
method deregisterListener (line 132) | deregisterListener(listener: L | ListenerHandle) {
FILE: packages/react-canvas-core/src/core/FactoryBank.ts
type FactoryBankListener (line 5) | interface FactoryBankListener<F extends AbstractFactory = AbstractFactor...
class FactoryBank (line 20) | class FactoryBank<
method constructor (line 26) | constructor() {
method getFactories (line 31) | getFactories(): F[] {
method clearFactories (line 35) | clearFactories() {
method getFactory (line 41) | getFactory<T extends F = F>(type: string): T {
method registerFactory (line 48) | registerFactory(factory: F) {
method deregisterFactory (line 55) | deregisterFactory(type: string) {
FILE: packages/react-canvas-core/src/core/ModelGeometryInterface.ts
type ModelGeometryInterface (line 3) | interface ModelGeometryInterface {
FILE: packages/react-canvas-core/src/entities/canvas/CanvasModel.ts
type CanvasModelListener (line 18) | interface CanvasModelListener extends BaseEntityListener {
type CanvasModelOptions (line 26) | interface CanvasModelOptions extends BaseEntityOptions {
type CanvasModelGenerics (line 33) | interface CanvasModelGenerics extends BaseEntityGenerics {
class CanvasModel (line 39) | class CanvasModel<G extends CanvasModelGenerics = CanvasModelGenerics> e...
method constructor (line 42) | constructor(options: G['OPTIONS'] = {}) {
method getSelectionEntities (line 53) | getSelectionEntities(): BaseModel[] {
method getSelectedEntities (line 59) | getSelectedEntities(): BaseModel[] {
method clearSelection (line 65) | clearSelection() {
method getModels (line 71) | getModels(): BaseModel[] {
method addLayer (line 77) | addLayer(layer: LayerModel) {
method removeLayer (line 85) | removeLayer(layer: LayerModel) {
method getLayers (line 94) | getLayers() {
method setGridSize (line 98) | setGridSize(size: number = 0) {
method getGridPosition (line 103) | getGridPosition(pos: number) {
method deserializeModel (line 110) | deserializeModel(data: ReturnType<this['serialize']>, engine: CanvasEn...
method deserialize (line 145) | deserialize(event: DeserializeEvent<this>) {
method serialize (line 163) | serialize() {
method setZoomLevel (line 176) | setZoomLevel(zoom: number) {
method setOffset (line 181) | setOffset(offsetX: number, offsetY: number) {
method setOffsetX (line 187) | setOffsetX(offsetX: number) {
method setOffsetY (line 191) | setOffsetY(offsetY: number) {
method getOffsetY (line 195) | getOffsetY() {
method getOffsetX (line 199) | getOffsetX() {
method getZoomLevel (line 203) | getZoomLevel() {
FILE: packages/react-canvas-core/src/entities/canvas/CanvasWidget.tsx
type DiagramProps (line 7) | interface DiagramProps {
class CanvasWidget (line 20) | class CanvasWidget extends React.Component<DiagramProps> {
method constructor (line 26) | constructor(props: DiagramProps) {
method componentWillUnmount (line 36) | componentWillUnmount() {
method registerCanvas (line 44) | registerCanvas() {
method componentDidUpdate (line 51) | componentDidUpdate() {
method componentDidMount (line 55) | componentDidMount() {
method render (line 74) | render() {
FILE: packages/react-canvas-core/src/entities/layer/LayerModel.ts
type LayerModelOptions (line 11) | interface LayerModelOptions extends BaseModelOptions {
type LayerModelGenerics (line 16) | interface LayerModelGenerics extends BaseModelGenerics {
method constructor (line 27) | constructor(options: G['OPTIONS'] = {}) {
method deserialize (line 38) | deserialize(event: DeserializeEvent<this>) {
method serialize (line 54) | serialize() {
method isRepaintEnabled (line 65) | isRepaintEnabled() {
method allowRepaint (line 69) | allowRepaint(allow: boolean = true) {
method remove (line 73) | remove() {
method addModel (line 80) | addModel(model: G['CHILDREN']) {
method getSelectionEntities (line 85) | getSelectionEntities(): Array<BaseModel> {
method getModels (line 91) | getModels() {
method getModel (line 95) | getModel(id: string) {
method removeModel (line 99) | removeModel(id: string | G['CHILDREN']): boolean {
FILE: packages/react-canvas-core/src/entities/layer/SmartLayerWidget.tsx
type SmartLayerWidgetProps (line 5) | interface SmartLayerWidgetProps {
class SmartLayerWidget (line 10) | class SmartLayerWidget extends React.Component<SmartLayerWidgetProps> {
method shouldComponentUpdate (line 11) | shouldComponentUpdate(): boolean {
method render (line 15) | render() {
FILE: packages/react-canvas-core/src/entities/layer/TransformLayerWidget.tsx
type TransformLayerWidgetProps (line 7) | interface TransformLayerWidgetProps {
class TransformLayerWidget (line 34) | class TransformLayerWidget extends React.Component<React.PropsWithChildr...
method constructor (line 35) | constructor(props: TransformLayerWidgetProps) {
method getTransform (line 40) | getTransform() {
method getTransformStyle (line 52) | getTransformStyle(): CSSProperties {
method render (line 61) | render() {
FILE: packages/react-canvas-core/src/entities/selection/SelectionBoxLayerFactory.tsx
class SelectionBoxLayerFactory (line 8) | class SelectionBoxLayerFactory extends AbstractReactFactory<SelectionLay...
method constructor (line 9) | constructor() {
method generateModel (line 13) | generateModel(event: GenerateModelEvent): SelectionLayerModel {
method generateReactWidget (line 17) | generateReactWidget(event: GenerateWidgetEvent<SelectionLayerModel>): ...
FILE: packages/react-canvas-core/src/entities/selection/SelectionBoxWidget.tsx
type SelectionBoxWidgetProps (line 5) | interface SelectionBoxWidgetProps {
class SelectionBoxWidget (line 17) | class SelectionBoxWidget extends React.Component<SelectionBoxWidgetProps> {
method render (line 18) | render() {
FILE: packages/react-canvas-core/src/entities/selection/SelectionLayerModel.ts
class SelectionLayerModel (line 7) | class SelectionLayerModel extends LayerModel {
method constructor (line 10) | constructor() {
method setBox (line 18) | setBox(rect: SimpleClientRect) {
method getChildModelFactoryBank (line 22) | getChildModelFactoryBank(): FactoryBank<AbstractModelFactory<BaseModel...
FILE: packages/react-canvas-core/src/states/DefaultState.ts
class DefaultState (line 8) | class DefaultState extends State {
method constructor (line 9) | constructor() {
FILE: packages/react-canvas-core/src/states/DragCanvasState.ts
type DragCanvasStateOptions (line 5) | interface DragCanvasStateOptions {
class DragCanvasState (line 12) | class DragCanvasState<E extends CanvasEngine = CanvasEngine> extends Abs...
method constructor (line 18) | constructor(options: DragCanvasStateOptions = {}) {
method activated (line 28) | async activated(prev) {
method deactivated (line 42) | deactivated(next: State) {
method fireMouseMoved (line 49) | fireMouseMoved(event: AbstractDisplacementStateEvent) {
FILE: packages/react-canvas-core/src/states/MoveItemsState.ts
class MoveItemsState (line 8) | class MoveItemsState<E extends CanvasEngine = CanvasEngine> extends Abst...
method constructor (line 16) | constructor() {
method activated (line 38) | activated(previous: State) {
method fireMouseMoved (line 43) | fireMouseMoved(event: AbstractDisplacementStateEvent) {
FILE: packages/react-canvas-core/src/states/SelectingState.ts
class SelectingState (line 7) | class SelectingState<E extends CanvasEngine = CanvasEngine> extends Stat...
method constructor (line 8) | constructor() {
FILE: packages/react-canvas-core/src/states/SelectionBoxState.ts
type SimpleClientRect (line 9) | interface SimpleClientRect {
class SelectionBoxState (line 18) | class SelectionBoxState<E extends CanvasEngine = CanvasEngine> extends A...
method constructor (line 21) | constructor() {
method activated (line 27) | activated(previous: State) {
method deactivated (line 33) | deactivated(next: State) {
method getBoxDimensions (line 39) | getBoxDimensions(event: AbstractDisplacementStateEvent): SimpleClientR...
method fireMouseMoved (line 59) | fireMouseMoved(event: AbstractDisplacementStateEvent) {
FILE: packages/react-canvas-core/src/widgets/PeformanceWidget.tsx
type PeformanceWidgetProps (line 6) | interface PeformanceWidgetProps {
type PeformanceWidgetState (line 12) | interface PeformanceWidgetState {}
class PeformanceWidget (line 14) | class PeformanceWidget extends React.Component<PeformanceWidgetProps, Pe...
method shouldComponentUpdate (line 15) | shouldComponentUpdate(
method render (line 32) | render() {
FILE: packages/react-diagrams-core/src/DiagramEngine.ts
class DiagramEngine (line 22) | class DiagramEngine extends CanvasEngine<CanvasEngineListener, DiagramMo...
method constructor (line 30) | constructor(options: CanvasEngineOptions = {}) {
method getMouseElement (line 60) | getMouseElement(event: MouseEvent): BaseModel {
method getNodeFactories (line 94) | getNodeFactories() {
method getLinkFactories (line 98) | getLinkFactories() {
method getLabelFactories (line 102) | getLabelFactories() {
method getPortFactories (line 106) | getPortFactories() {
method getFactoryForNode (line 110) | getFactoryForNode<F extends AbstractReactFactory<NodeModel, DiagramEng...
method getFactoryForLink (line 117) | getFactoryForLink<F extends AbstractReactFactory<LinkModel, DiagramEng...
method getFactoryForLabel (line 124) | getFactoryForLabel<F extends AbstractReactFactory<LabelModel, DiagramE...
method getFactoryForPort (line 131) | getFactoryForPort<F extends AbstractModelFactory<PortModel, DiagramEng...
method generateWidgetForLink (line 138) | generateWidgetForLink(link: LinkModel): JSX.Element {
method generateWidgetForNode (line 142) | generateWidgetForNode(node: NodeModel): JSX.Element {
method getNodeElement (line 146) | getNodeElement(node: NodeModel): Element {
method getNodePortElement (line 154) | getNodePortElement(port: PortModel): any {
method getPortCenter (line 170) | getPortCenter(port: PortModel): Point {
method getPortCoords (line 177) | getPortCoords(port: PortModel, element?: HTMLDivElement): Rectangle {
method getNodeDimensions (line 197) | getNodeDimensions(node: NodeModel): { width: number; height: number } {
method getBoundingNodesRect (line 214) | getBoundingNodesRect(nodes: NodeModel[]): Rectangle {
method zoomToFitSelectedNodes (line 223) | zoomToFitSelectedNodes(options: { margin?: number; maxZoom?: number }) {
method zoomToFitNodes (line 239) | zoomToFitNodes(options) {
method getMaxNumberPointsPerLink (line 295) | getMaxNumberPointsPerLink(): number {
method setMaxNumberPointsPerLink (line 299) | setMaxNumberPointsPerLink(max: number) {
FILE: packages/react-diagrams-core/src/entities/label/LabelModel.ts
type LabelModelOptions (line 4) | interface LabelModelOptions extends BaseModelOptions {
type LabelModelGenerics (line 9) | interface LabelModelGenerics extends BaseModelGenerics {
class LabelModel (line 14) | class LabelModel<G extends LabelModelGenerics = LabelModelGenerics> exte...
method constructor (line 15) | constructor(options: G['OPTIONS']) {
method deserialize (line 23) | deserialize(event: DeserializeEvent<this>) {
method serialize (line 29) | serialize() {
FILE: packages/react-diagrams-core/src/entities/label/LabelWidget.tsx
type LabelWidgetProps (line 6) | interface LabelWidgetProps {
class LabelWidget (line 24) | class LabelWidget extends React.Component<LabelWidgetProps> {
method constructor (line 27) | constructor(props: LabelWidgetProps) {
method componentDidUpdate (line 32) | componentDidUpdate() {
method componentDidMount (line 36) | componentDidMount() {
method render (line 89) | render() {
FILE: packages/react-diagrams-core/src/entities/link-layer/LinkLayerFactory.tsx
class LinkLayerFactory (line 8) | class LinkLayerFactory extends AbstractReactFactory<LinkLayerModel, Diag...
method constructor (line 9) | constructor() {
method generateModel (line 13) | generateModel(event: GenerateModelEvent): LinkLayerModel {
method generateReactWidget (line 17) | generateReactWidget(event: GenerateWidgetEvent<LinkLayerModel>): JSX.E...
FILE: packages/react-diagrams-core/src/entities/link-layer/LinkLayerModel.ts
type LinkLayerModelGenerics (line 6) | interface LinkLayerModelGenerics extends LayerModelGenerics {
class LinkLayerModel (line 11) | class LinkLayerModel<G extends LinkLayerModelGenerics = LinkLayerModelGe...
method constructor (line 12) | constructor() {
method addModel (line 20) | addModel(model: G['CHILDREN']): void {
method getLinks (line 32) | getLinks() {
method getChildModelFactoryBank (line 36) | getChildModelFactoryBank(engine: G['ENGINE']) {
FILE: packages/react-diagrams-core/src/entities/link-layer/LinkLayerWidget.tsx
type LinkLayerWidgetProps (line 8) | interface LinkLayerWidgetProps {
class LinkLayerWidget (line 17) | class LinkLayerWidget extends React.Component<LinkLayerWidgetProps> {
method render (line 18) | render() {
FILE: packages/react-diagrams-core/src/entities/link/LinkModel.ts
type LinkModelListener (line 19) | interface LinkModelListener extends BaseModelListener {
type LinkModelGenerics (line 25) | interface LinkModelGenerics extends BaseModelGenerics {
class LinkModel (line 30) | class LinkModel<G extends LinkModelGenerics = LinkModelGenerics>
method constructor (line 42) | constructor(options: G['OPTIONS']) {
method getBoundingBox (line 58) | getBoundingBox(): Rectangle {
method getSelectionEntities (line 68) | getSelectionEntities(): Array<BaseModel> {
method deserialize (line 83) | deserialize(event: DeserializeEvent<this>) {
method getRenderedPath (line 121) | getRenderedPath(): SVGPathElement[] {
method setRenderedPaths (line 125) | setRenderedPaths(paths: SVGPathElement[]) {
method serialize (line 129) | serialize() {
method doClone (line 145) | doClone(lookupTable = {}, clone) {
method clearPort (line 159) | clearPort(port: PortModel) {
method remove (line 167) | remove() {
method isLastPoint (line 179) | isLastPoint(point: PointModel) {
method getPointIndex (line 184) | getPointIndex(point: PointModel) {
method getPointModel (line 188) | getPointModel(id: string): PointModel | null {
method getPortForPoint (line 197) | getPortForPoint(point: PointModel): PortModel {
method getPointForPort (line 207) | getPointForPort(port: PortModel): PointModel {
method getFirstPoint (line 217) | getFirstPoint(): PointModel {
method getLastPoint (line 221) | getLastPoint(): PointModel {
method setSourcePort (line 225) | setSourcePort(port: PortModel | null) {
method getSourcePort (line 239) | getSourcePort(): PortModel {
method getTargetPort (line 243) | getTargetPort(): PortModel {
method setTargetPort (line 247) | setTargetPort(port: PortModel | null) {
method point (line 261) | point(x: number, y: number, index: number = 1): PointModel {
method addLabel (line 265) | addLabel(label: LabelModel) {
method getPoints (line 270) | getPoints(): PointModel[] {
method getLabels (line 274) | getLabels() {
method setPoints (line 278) | setPoints(points: PointModel[]) {
method removePoint (line 285) | removePoint(pointModel: PointModel) {
method removePointsBefore (line 290) | removePointsBefore(pointModel: PointModel) {
method removePointsAfter (line 294) | removePointsAfter(pointModel: PointModel) {
method removeMiddlePoints (line 298) | removeMiddlePoints() {
method addPoint (line 304) | addPoint<P extends PointModel>(pointModel: P, index = 1): P {
method generatePoint (line 310) | generatePoint(x: number = 0, y: number = 0): PointModel {
FILE: packages/react-diagrams-core/src/entities/link/LinkWidget.tsx
type LinkProps (line 10) | interface LinkProps {
type LinkState (line 15) | interface LinkState {
class LinkWidget (line 20) | class LinkWidget extends React.Component<LinkProps, LinkState> {
method constructor (line 24) | constructor(props) {
method componentWillUnmount (line 32) | componentWillUnmount(): void {
method getDerivedStateFromProps (line 41) | static getDerivedStateFromProps(nextProps: LinkProps, prevState: LinkS...
method installTarget (line 48) | installTarget() {
method installSource (line 59) | installSource() {
method componentDidUpdate (line 70) | componentDidUpdate(prevProps: Readonly<LinkProps>, prevState: Readonly...
method generateLinePath (line 79) | public static generateLinePath(firstPoint: PointModel, lastPoint: Poin...
method componentDidMount (line 83) | componentDidMount(): void {
method render (line 92) | render() {
FILE: packages/react-diagrams-core/src/entities/link/PointModel.ts
type PointModelOptions (line 9) | interface PointModelOptions extends Omit<BasePositionModelOptions, 'type...
type PointModelGenerics (line 13) | interface PointModelGenerics {
class PointModel (line 19) | class PointModel<G extends PointModelGenerics = PointModelGenerics> exte...
method constructor (line 22) | constructor(options: G['OPTIONS']) {
method isConnectedToPort (line 30) | isConnectedToPort(): boolean {
method getLink (line 34) | getLink(): LinkModel {
method remove (line 38) | remove() {
method isLocked (line 46) | isLocked() {
FILE: packages/react-diagrams-core/src/entities/node-layer/NodeLayerFactory.tsx
class NodeLayerFactory (line 8) | class NodeLayerFactory extends AbstractReactFactory<NodeLayerModel, Diag...
method constructor (line 9) | constructor() {
method generateModel (line 13) | generateModel(event: GenerateModelEvent): NodeLayerModel {
method generateReactWidget (line 17) | generateReactWidget(event: GenerateWidgetEvent<NodeLayerModel>): JSX.E...
FILE: packages/react-diagrams-core/src/entities/node-layer/NodeLayerModel.ts
type NodeLayerModelGenerics (line 6) | interface NodeLayerModelGenerics extends LayerModelGenerics {
class NodeLayerModel (line 11) | class NodeLayerModel<G extends NodeLayerModelGenerics = NodeLayerModelGe...
method constructor (line 12) | constructor() {
method addModel (line 20) | addModel(model: G['CHILDREN']): void {
method getChildModelFactoryBank (line 32) | getChildModelFactoryBank(engine: G['ENGINE']) {
method getNodes (line 36) | getNodes() {
FILE: packages/react-diagrams-core/src/entities/node-layer/NodeLayerWidget.tsx
type NodeLayerWidgetProps (line 8) | interface NodeLayerWidgetProps {
class NodeLayerWidget (line 13) | class NodeLayerWidget extends React.Component<NodeLayerWidgetProps> {
method render (line 14) | render() {
FILE: packages/react-diagrams-core/src/entities/node/NodeModel.ts
type NodeModelListener (line 17) | interface NodeModelListener extends BaseModelListener {
type NodeModelGenerics (line 21) | interface NodeModelGenerics extends BasePositionModelGenerics {
class NodeModel (line 26) | class NodeModel<G extends NodeModelGenerics = NodeModelGenerics> extends...
method constructor (line 33) | constructor(options: G['OPTIONS']) {
method getBoundingBox (line 40) | getBoundingBox(): Rectangle {
method setPosition (line 46) | setPosition(x: number | Point, y?: number): void {
method deserialize (line 61) | deserialize(event: DeserializeEvent<this>) {
method serialize (line 77) | serialize() {
method doClone (line 86) | doClone(lookupTable = {}, clone) {
method remove (line 94) | remove() {
method getPortFromID (line 103) | getPortFromID(id): PortModel | null {
method getLink (line 112) | getLink(id: string): LinkModel {
method getPort (line 121) | getPort(name: string): PortModel | null {
method getPorts (line 125) | getPorts(): { [s: string]: PortModel } {
method removePort (line 129) | removePort(port: PortModel) {
method addPort (line 141) | addPort(port: PortModel): PortModel {
method updateDimensions (line 147) | updateDimensions({ width, height }: { width: number; height: number }) {
FILE: packages/react-diagrams-core/src/entities/node/NodeWidget.tsx
type NodeProps (line 9) | interface NodeProps {
class NodeWidget (line 26) | class NodeWidget extends React.Component<NodeProps> {
method constructor (line 31) | constructor(props: NodeProps) {
method componentWillUnmount (line 36) | componentWillUnmount(): void {
method componentDidUpdate (line 44) | componentDidUpdate(prevProps: Readonly<NodeProps>, prevState: Readonly...
method installSelectionListener (line 51) | installSelectionListener() {
method updateSize (line 59) | updateSize(width: number, height: number) {
method componentDidMount (line 70) | componentDidMount(): void {
method render (line 83) | render() {
FILE: packages/react-diagrams-core/src/entities/port/PortModel.ts
type PortModelAlignment (line 18) | enum PortModelAlignment {
type PortModelListener (line 25) | interface PortModelListener extends BasePositionModelListener {
type PortModelOptions (line 32) | interface PortModelOptions extends BaseModelOptions {
type PortModelGenerics (line 38) | interface PortModelGenerics extends BasePositionModelGenerics {
class PortModel (line 44) | class PortModel<G extends PortModelGenerics = PortModelGenerics> extends...
method constructor (line 52) | constructor(options: G['OPTIONS']) {
method deserialize (line 58) | deserialize(event: DeserializeEvent<this>) {
method serialize (line 65) | serialize() {
method setPosition (line 79) | setPosition(x, y?) {
method doClone (line 88) | doClone(lookupTable = {}, clone: PortModel) {
method getNode (line 93) | getNode(): NodeModel {
method getName (line 97) | getName(): string {
method getMaximumLinks (line 101) | getMaximumLinks(): number {
method setMaximumLinks (line 105) | setMaximumLinks(maximumLinks: number) {
method removeLink (line 109) | removeLink(link: LinkModel) {
method addLink (line 113) | addLink(link: LinkModel) {
method getLinks (line 117) | getLinks(): { [id: string]: LinkModel } {
method createLinkModel (line 121) | public createLinkModel(): LinkModel | null {
method reportPosition (line 133) | reportPosition() {
method getCenter (line 145) | getCenter(): Point {
method getBoundingBox (line 149) | getBoundingBox(): Rectangle {
method updateCoords (line 153) | updateCoords(coords: Rectangle) {
method canLinkToPort (line 161) | canLinkToPort(port: PortModel): boolean {
method isLocked (line 165) | isLocked() {
FILE: packages/react-diagrams-core/src/entities/port/PortWidget.tsx
type PortProps (line 7) | interface PortProps {
class PortWidget (line 14) | class PortWidget extends React.Component<React.PropsWithChildren<PortPro...
method constructor (line 18) | constructor(props: PortProps) {
method report (line 23) | report() {
method componentWillUnmount (line 27) | componentWillUnmount(): void {
method componentDidUpdate (line 31) | componentDidUpdate(prevProps: Readonly<PortProps>, prevState, snapshot...
method componentDidMount (line 37) | componentDidMount(): void {
method getExtraProps (line 48) | getExtraProps() {
method render (line 58) | render() {
FILE: packages/react-diagrams-core/src/models/DiagramModel.ts
type DiagramListener (line 20) | interface DiagramListener extends BaseEntityListener {
type DiagramModelGenerics (line 26) | interface DiagramModelGenerics extends CanvasModelGenerics {
class DiagramModel (line 30) | class DiagramModel<G extends DiagramModelGenerics = DiagramModelGenerics...
method constructor (line 34) | constructor(options: G['OPTIONS'] = {}) {
method deserialize (line 40) | deserialize(event: DeserializeEvent<this>) {
method addLayer (line 45) | addLayer(layer: LayerModel): void {
method getLinkLayers (line 55) | getLinkLayers(): LinkLayerModel[] {
method getNodeLayers (line 61) | getNodeLayers(): NodeLayerModel[] {
method getActiveNodeLayer (line 67) | getActiveNodeLayer(): NodeLayerModel {
method getActiveLinkLayer (line 79) | getActiveLinkLayer(): LinkLayerModel {
method getNode (line 91) | getNode(node: string): NodeModel {
method getLink (line 100) | getLink(link: string): LinkModel {
method addAll (line 109) | addAll(...models: BaseModel[]): BaseModel[] {
method addLink (line 120) | addLink(link: LinkModel): LinkModel {
method addNode (line 132) | addNode(node: NodeModel): NodeModel {
method removeLink (line 138) | removeLink(link: LinkModel) {
method removeNode (line 147) | removeNode(node: NodeModel) {
method getLinks (line 156) | getLinks(): LinkModel[] {
method getNodes (line 162) | getNodes(): NodeModel[] {
FILE: packages/react-diagrams-core/src/states/DefaultDiagramState.ts
class DefaultDiagramState (line 15) | class DefaultDiagramState extends State<DiagramEngine> {
method constructor (line 20) | constructor() {
FILE: packages/react-diagrams-core/src/states/DragDiagramItemsState.ts
class DragDiagramItemsState (line 9) | class DragDiagramItemsState<E extends DiagramEngine = DiagramEngine> ext...
method constructor (line 10) | constructor() {
FILE: packages/react-diagrams-core/src/states/DragNewLinkState.ts
type DragNewLinkStateOptions (line 13) | interface DragNewLinkStateOptions {
class DragNewLinkState (line 25) | class DragNewLinkState<E extends DiagramEngine = DiagramEngine> extends ...
method constructor (line 30) | constructor(options: DragNewLinkStateOptions = {}) {
method fireMouseMoved (line 96) | fireMouseMoved(event: AbstractDisplacementStateEvent): any {
FILE: packages/react-diagrams-defaults/src/label/DefaultLabelFactory.tsx
class DefaultLabelFactory (line 11) | class DefaultLabelFactory extends AbstractReactFactory<DefaultLabelModel...
method constructor (line 12) | constructor() {
method generateReactWidget (line 16) | generateReactWidget(event): JSX.Element {
method generateModel (line 20) | generateModel(event): DefaultLabelModel {
FILE: packages/react-diagrams-defaults/src/label/DefaultLabelModel.tsx
type DefaultLabelModelOptions (line 4) | interface DefaultLabelModelOptions extends LabelModelOptions {
type DefaultLabelModelGenerics (line 8) | interface DefaultLabelModelGenerics extends LabelModelGenerics {
class DefaultLabelModel (line 12) | class DefaultLabelModel extends LabelModel<DefaultLabelModelGenerics> {
method constructor (line 13) | constructor(options: DefaultLabelModelOptions = {}) {
method setLabel (line 21) | setLabel(label: string) {
method deserialize (line 25) | deserialize(event: DeserializeEvent<this>) {
method serialize (line 30) | serialize() {
FILE: packages/react-diagrams-defaults/src/label/DefaultLabelWidget.tsx
type DefaultLabelWidgetProps (line 5) | interface DefaultLabelWidgetProps {
class DefaultLabelWidget (line 21) | class DefaultLabelWidget extends React.Component<DefaultLabelWidgetProps> {
method render (line 22) | render() {
FILE: packages/react-diagrams-defaults/src/link/DefaultLinkFactory.tsx
class DefaultLinkFactory (line 32) | class DefaultLinkFactory<Link extends DefaultLinkModel = DefaultLinkMode...
method constructor (line 36) | constructor(type = 'default') {
method generateReactWidget (line 40) | generateReactWidget(event): JSX.Element {
method generateModel (line 44) | generateModel(event): Link {
method generateLinkSegment (line 48) | generateLinkSegment(model: Link, selected: boolean, path: string) {
FILE: packages/react-diagrams-defaults/src/link/DefaultLinkModel.ts
type DefaultLinkModelListener (line 14) | interface DefaultLinkModelListener extends LinkModelListener {
type DefaultLinkModelOptions (line 20) | interface DefaultLinkModelOptions extends BaseModelOptions {
type DefaultLinkModelGenerics (line 29) | interface DefaultLinkModelGenerics extends LinkModelGenerics {
class DefaultLinkModel (line 34) | class DefaultLinkModel extends LinkModel<DefaultLinkModelGenerics> {
method constructor (line 35) | constructor(options: DefaultLinkModelOptions = {}) {
method calculateControlOffset (line 46) | calculateControlOffset(port: PortModel): [number, number] {
method getSVGPath (line 57) | getSVGPath(): string {
method serialize (line 76) | serialize() {
method deserialize (line 86) | deserialize(event: DeserializeEvent<this>) {
method addLabel (line 94) | addLabel(label: LabelModel | string) {
method setWidth (line 103) | setWidth(width: number) {
method setColor (line 108) | setColor(color: string) {
FILE: packages/react-diagrams-defaults/src/link/DefaultLinkPointWidget.tsx
type DefaultLinkPointWidgetProps (line 5) | interface DefaultLinkPointWidgetProps {
type DefaultLinkPointWidgetState (line 11) | interface DefaultLinkPointWidgetState {
class DefaultLinkPointWidget (line 21) | class DefaultLinkPointWidget extends React.Component<DefaultLinkPointWid...
method constructor (line 22) | constructor(props) {
method render (line 29) | render() {
FILE: packages/react-diagrams-defaults/src/link/DefaultLinkSegmentWidget.tsx
type DefaultLinkSegmentWidgetProps (line 6) | interface DefaultLinkSegmentWidgetProps {
class DefaultLinkSegmentWidget (line 17) | class DefaultLinkSegmentWidget extends React.Component<DefaultLinkSegmen...
method render (line 18) | render() {
FILE: packages/react-diagrams-defaults/src/link/DefaultLinkWidget.tsx
type DefaultLinkProps (line 8) | interface DefaultLinkProps {
FILE: packages/react-diagrams-defaults/src/node/DefaultNodeFactory.tsx
class DefaultNodeFactory (line 8) | class DefaultNodeFactory extends AbstractReactFactory<DefaultNodeModel, ...
method constructor (line 9) | constructor() {
method generateReactWidget (line 13) | generateReactWidget(event): JSX.Element {
method generateModel (line 17) | generateModel(event): DefaultNodeModel {
FILE: packages/react-diagrams-defaults/src/node/DefaultNodeModel.ts
type DefaultNodeModelOptions (line 6) | interface DefaultNodeModelOptions extends BasePositionModelOptions {
type DefaultNodeModelGenerics (line 11) | interface DefaultNodeModelGenerics extends NodeModelGenerics {
class DefaultNodeModel (line 15) | class DefaultNodeModel extends NodeModel<DefaultNodeModelGenerics> {
method constructor (line 21) | constructor(options: any = {}, color?: string) {
method doClone (line 38) | doClone(lookupTable: {}, clone: any): void {
method removePort (line 44) | removePort(port: DefaultPortModel): void {
method addPort (line 53) | addPort<T extends DefaultPortModel>(port: T): T {
method addInPort (line 67) | addInPort(label: string, after = true): DefaultPortModel {
method addOutPort (line 80) | addOutPort(label: string, after = true): DefaultPortModel {
method deserialize (line 93) | deserialize(event: DeserializeEvent<this>) {
method serialize (line 105) | serialize(): any {
method getInPorts (line 119) | getInPorts(): DefaultPortModel[] {
method getOutPorts (line 123) | getOutPorts(): DefaultPortModel[] {
FILE: packages/react-diagrams-defaults/src/node/DefaultNodeWidget.tsx
type DefaultNodeProps (line 52) | interface DefaultNodeProps {
class DefaultNodeWidget (line 61) | class DefaultNodeWidget extends React.Component<DefaultNodeProps> {
method render (line 66) | render() {
FILE: packages/react-diagrams-defaults/src/port/DefaultPortFactory.tsx
class DefaultPortFactory (line 5) | class DefaultPortFactory extends AbstractModelFactory<DefaultPortModel, ...
method constructor (line 6) | constructor() {
method generateModel (line 10) | generateModel(): DefaultPortModel {
FILE: packages/react-diagrams-defaults/src/port/DefaultPortLabelWidget.tsx
type DefaultPortLabelProps (line 6) | interface DefaultPortLabelProps {
class DefaultPortLabel (line 34) | class DefaultPortLabel extends React.Component<DefaultPortLabelProps> {
method render (line 35) | render() {
FILE: packages/react-diagrams-defaults/src/port/DefaultPortModel.ts
type DefaultPortModelOptions (line 11) | interface DefaultPortModelOptions extends PortModelOptions {
type DefaultPortModelGenerics (line 17) | interface DefaultPortModelGenerics extends PortModelGenerics {
class DefaultPortModel (line 21) | class DefaultPortModel extends PortModel<DefaultPortModelGenerics> {
method constructor (line 24) | constructor(options: DefaultPortModelOptions | boolean, name?: string,...
method deserialize (line 41) | deserialize(event: DeserializeEvent<this>) {
method serialize (line 47) | serialize() {
method link (line 55) | link<T extends LinkModel>(port: PortModel, factory?: AbstractModelFact...
method canLinkToPort (line 62) | canLinkToPort(port: PortModel): boolean {
method createLinkModel (line 69) | createLinkModel(factory?: AbstractModelFactory<LinkModel>): LinkModel {
FILE: packages/react-diagrams-routing/src/dagre/DagreEngine.ts
type DagreEngineOptions (line 12) | interface DagreEngineOptions {
class DagreEngine (line 21) | class DagreEngine {
method constructor (line 24) | constructor(options: DagreEngineOptions = {}) {
method redistribute (line 28) | redistribute(model: DiagramModel) {
method refreshLinks (line 81) | public refreshLinks(diagram: DiagramModel) {
FILE: packages/react-diagrams-routing/src/engine/PathFinding.ts
class PathFinding (line 16) | class PathFinding {
method constructor (line 20) | constructor(factory: PathFindingLinkFactory) {
method calculateDirectPath (line 29) | calculateDirectPath(from: PointModel, to: PointModel): number[][] {
method calculateLinkStartEndCoords (line 47) | calculateLinkStartEndCoords(
method calculateDynamicPath (line 104) | calculateDynamicPath(
FILE: packages/react-diagrams-routing/src/link/PathFindingLinkFactory.tsx
class PathFindingLinkFactory (line 27) | class PathFindingLinkFactory extends DefaultLinkFactory<PathFindingLinkM...
method constructor (line 41) | constructor() {
method setDiagramEngine (line 45) | setDiagramEngine(engine: DiagramEngine): void {
method setFactoryBank (line 75) | setFactoryBank(bank: FactoryBank<AbstractFactory>): void {
method generateReactWidget (line 82) | generateReactWidget(event): JSX.Element {
method generateModel (line 86) | generateModel(event): PathFindingLinkModel {
method getCanvasMatrix (line 104) | getCanvasMatrix(): number[][] {
method calculateCanvasMatrix (line 111) | calculateCanvasMatrix() {
method getRoutingMatrix (line 144) | getRoutingMatrix(): number[][] {
method calculateRoutingMatrix (line 151) | calculateRoutingMatrix(): void {
method translateRoutingX (line 167) | translateRoutingX(x: number, reverse: boolean = false) {
method translateRoutingY (line 170) | translateRoutingY(y: number, reverse: boolean = false) {
method generateDynamicPath (line 278) | generateDynamicPath(pathCoords: number[][]) {
FILE: packages/react-diagrams-routing/src/link/PathFindingLinkModel.ts
class PathFindingLinkModel (line 4) | class PathFindingLinkModel extends DefaultLinkModel {
method constructor (line 5) | constructor(options: DefaultLinkModelOptions = {}) {
method performanceTune (line 12) | performanceTune() {
FILE: packages/react-diagrams-routing/src/link/PathFindingLinkWidget.tsx
type PathFindingLinkWidgetProps (line 11) | interface PathFindingLinkWidgetProps {
type PathFindingLinkWidgetState (line 20) | interface PathFindingLinkWidgetState {
class PathFindingLinkWidget (line 24) | class PathFindingLinkWidget extends React.Component<PathFindingLinkWidge...
method constructor (line 28) | constructor(props: PathFindingLinkWidgetProps) {
method componentDidUpdate (line 37) | componentDidUpdate(): void {
method componentDidMount (line 45) | componentDidMount(): void {
method componentWillUnmount (line 53) | componentWillUnmount(): void {
method generateLink (line 57) | generateLink(path: string, id: string | number): JSX.Element {
method render (line 77) | render() {
FILE: packages/react-diagrams-routing/src/link/RightAngleLinkFactory.tsx
class RightAngleLinkFactory (line 10) | class RightAngleLinkFactory extends DefaultLinkFactory<RightAngleLinkMod...
method constructor (line 13) | constructor() {
method generateModel (line 17) | generateModel(event): RightAngleLinkModel {
method generateReactWidget (line 21) | generateReactWidget(event): JSX.Element {
FILE: packages/react-diagrams-routing/src/link/RightAngleLinkModel.ts
class RightAngleLinkModel (line 6) | class RightAngleLinkModel extends DefaultLinkModel {
method constructor (line 10) | constructor(options: DefaultLinkModelOptions = {}) {
method setFirstAndLastPathsDirection (line 20) | setFirstAndLastPathsDirection() {
method addPoint (line 34) | addPoint<P extends PointModel>(pointModel: P, index: number = 1): P {
method deserialize (line 41) | deserialize(event: DeserializeEvent<this>) {
method setManuallyFirstAndLastPathsDirection (line 46) | setManuallyFirstAndLastPathsDirection(first, last) {
method getLastPathXdirection (line 51) | getLastPathXdirection(): boolean {
method getFirstPathXdirection (line 54) | getFirstPathXdirection(): boolean {
method setWidth (line 58) | setWidth(width: number) {
method setColor (line 63) | setColor(color: string) {
FILE: packages/react-diagrams-routing/src/link/RightAngleLinkWidget.tsx
type RightAngleLinkProps (line 9) | interface RightAngleLinkProps {
type RightAngleLinkState (line 18) | interface RightAngleLinkState {
class RightAngleLinkWidget (line 23) | class RightAngleLinkWidget extends React.Component<RightAngleLinkProps, ...
method constructor (line 39) | constructor(props: RightAngleLinkProps) {
method componentDidUpdate (line 51) | componentDidUpdate(): void {
method componentDidMount (line 59) | componentDidMount(): void {
method componentWillUnmount (line 67) | componentWillUnmount(): void {
method generateLink (line 71) | generateLink(path: string, extraProps: any, id: string | number): JSX....
method calculatePositions (line 91) | calculatePositions(points: PointModel[], event: MouseEvent, index: num...
method draggingEvent (line 160) | draggingEvent(event: MouseEvent, index: number) {
method render (line 187) | render() {
Condensed preview — 229 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (401K chars).
[
{
"path": ".changeset/README.md",
"chars": 510,
"preview": "# Changesets\n\nHello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that wo"
},
{
"path": ".changeset/cold-drinks-unite.md",
"chars": 345,
"preview": "---\n'@projectstorm/react-diagrams-defaults': patch\n'@projectstorm/react-diagrams-routing': patch\n'@projectstorm/react-di"
},
{
"path": ".changeset/config.json",
"chars": 268,
"preview": "{\n\t\"$schema\": \"https://unpkg.com/@changesets/config@2.2.0/schema.json\",\n\t\"changelog\": \"@changesets/cli/changelog\",\n\t\"com"
},
{
"path": ".editorconfig",
"chars": 150,
"preview": "[*]\nindent_style = tab\nindent_size = 2\ntrim_trailing_whitespace = true\n\n# Some exceptions\n[{package.json,*.yml}]\nindent_"
},
{
"path": ".envrc",
"chars": 29,
"preview": "PATH_add ./node_modules/.bin\n"
},
{
"path": ".gitbook.yaml",
"chars": 46,
"preview": "root: ./docs/\n\nstructure:\n summary: README.md"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 457,
"preview": "# Checklist\n\n- [ ] The tests pass\n- [ ] I have referenced the issue(s) or other PR(s) this fixes/relates-to\n- [ ] I have"
},
{
"path": ".github/workflows/prettier.yml",
"chars": 596,
"preview": "name: Prettier check\n\n# This action works with pull requests and pushes\non:\n pull_request:\n\njobs:\n prettier:\n runs-"
},
{
"path": ".github/workflows/release.yml",
"chars": 1167,
"preview": "name: Release\n\non:\n push:\n branches:\n - master\n\nconcurrency: ${{ github.workflow }}-${{ github.ref }}\n\njobs:\n "
},
{
"path": ".github/workflows/test.yml",
"chars": 627,
"preview": "name: Build and Test\non:\n pull_request:\njobs:\n build:\n name: Build\n runs-on: ubuntu-latest\n steps:\n - na"
},
{
"path": ".gitignore",
"chars": 78,
"preview": "dist\n.DS_Store\n.idea\n.out\n*.zip\n.env\nnode_modules\ntsconfig.tsbuildinfo\n.vscode"
},
{
"path": ".prettierignore",
"chars": 23,
"preview": "node_modules\ndist\n.out\n"
},
{
"path": ".prettierrc",
"chars": 104,
"preview": "{\n\t\"semi\": true,\n\t\"singleQuote\": true,\n\t\"useTabs\": true,\n\t\"printWidth\": 120,\n\t\"trailingComma\": \"none\"\n}\n"
},
{
"path": "CHANGELOG.md",
"chars": 9491,
"preview": "__V7!__\n\nwe are now using changesets! you can see the changes for individual packages in their corresponding folders.\nHe"
},
{
"path": "CONTRIBUTING.md",
"chars": 1444,
"preview": "# Contributing\n\nSee below for guidelines on house keeping:\n\n### Always add a PR\n\nSince the project runs on GitHub, the b"
},
{
"path": "LICENSE",
"chars": 1072,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2016 Storm\n\nPermission is hereby granted, free of charge, to any person obtaining a"
},
{
"path": "README.md",
"chars": 3907,
"preview": "# Introduction\n\n[\n\nIn this repo you will find a simple webpack-dev-"
},
{
"path": "diagrams-demo-project/index.html",
"chars": 278,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, ini"
},
{
"path": "diagrams-demo-project/package.json",
"chars": 1007,
"preview": "{\n\t\"name\": \"@projectstorm/react-diagrams-demo\",\n\t\"version\": \"7.0.4\",\n\t\"author\": \"dylanvorster\",\n\t\"license\": \"MIT\",\n\t\"pri"
},
{
"path": "diagrams-demo-project/src/BodyWidget.tsx",
"chars": 339,
"preview": "import * as React from 'react';\nimport { DiagramEngine, CanvasWidget } from '@projectstorm/react-diagrams';\n\nexport inte"
},
{
"path": "diagrams-demo-project/src/custom-node-js/JSCustomNodeFactory.jsx",
"chars": 500,
"preview": "import * as React from 'react';\nimport { JSCustomNodeModel } from './JSCustomNodeModel';\nimport { JSCustomNodeWidget } f"
},
{
"path": "diagrams-demo-project/src/custom-node-js/JSCustomNodeModel.js",
"chars": 697,
"preview": "import { DefaultPortModel, NodeModel } from '@projectstorm/react-diagrams';\n\n/**\n * Example of a custom model using pure"
},
{
"path": "diagrams-demo-project/src/custom-node-js/JSCustomNodeWidget.jsx",
"chars": 592,
"preview": "import * as React from 'react';\nimport { PortWidget } from '@projectstorm/react-diagrams';\n\nexport class JSCustomNodeWid"
},
{
"path": "diagrams-demo-project/src/custom-node-ts/TSCustomNodeFactory.tsx",
"chars": 663,
"preview": "import * as React from 'react';\nimport { TSCustomNodeModel } from './TSCustomNodeModel';\nimport { TSCustomNodeWidget } f"
},
{
"path": "diagrams-demo-project/src/custom-node-ts/TSCustomNodeModel.ts",
"chars": 779,
"preview": "import { BaseModelOptions, DefaultPortModel, NodeModel } from '@projectstorm/react-diagrams';\n\nexport interface TSCustom"
},
{
"path": "diagrams-demo-project/src/custom-node-ts/TSCustomNodeWidget.tsx",
"chars": 941,
"preview": "import * as React from 'react';\nimport { DiagramEngine, PortWidget } from '@projectstorm/react-diagrams';\nimport { TSCus"
},
{
"path": "diagrams-demo-project/src/main.css",
"chars": 680,
"preview": "*{\n\tmargin: 0;\n\tpadding: 0;\n}\n\nhtml, body, #application{\n\theight: 100%;\n\toverflow: hidden;\n}\n\n.diagram-container{\n\tbackg"
},
{
"path": "diagrams-demo-project/src/main.tsx",
"chars": 1586,
"preview": "import * as React from 'react';\nimport { createRoot } from 'react-dom/client';\nimport './main.css';\nimport createEngine,"
},
{
"path": "diagrams-demo-project/tsconfig.json",
"chars": 185,
"preview": "{\n\t\"compileOnSave\": false,\n\t\"compilerOptions\": {\n\t\t\"declaration\": false,\n\t\t\"jsx\": \"react\",\n\t\t\"allowJs\": true,\n\t\t\"target\""
},
{
"path": "diagrams-demo-project/webpack.config.js",
"chars": 1077,
"preview": "const path = require('path');\nconst production = process.env.NODE_ENV === 'production';\nconst TerserPlugin = require('te"
},
{
"path": "docs/README.md",
"chars": 522,
"preview": "# Table of contents\n\n* [Introduction](README.md)\n* [Getting Started](getting-started/README.md)\n * [Using the library]("
},
{
"path": "docs/about-the-project/architecture-questions.md",
"chars": 4126,
"preview": "# Architecture Questions\n\nHere I will try to answer any questions relating to the design of the system\n\n## What was the "
},
{
"path": "docs/about-the-project/testing.md",
"chars": 1003,
"preview": "# Testing\n\n## End to end testing\n\nTo test the functionality of the library, we make use of e2e tests \\(end to end tests\\"
},
{
"path": "docs/customizing/README.md",
"chars": 1227,
"preview": "# Customizing\n\nAlmost all components in react-diagrams are customizable. While some customization is better documented t"
},
{
"path": "docs/customizing/extending-default-links.md",
"chars": 2149,
"preview": "# Custom Links\n\n## Extending the DefaultLinkModel\n\nMuch like extending nodes, custom links can also be created.\nIn the b"
},
{
"path": "docs/customizing/nodes.md",
"chars": 6007,
"preview": "# Nodes\n\nA node contains the node content itself and its ports. Check [NodeModel source code](https://github.com/project"
},
{
"path": "docs/customizing/ports.md",
"chars": 3122,
"preview": "# Ports\n\nPorts allow links to connect to your nodes. Each port that is rendered in a node must also have a corresponding"
},
{
"path": "docs/getting-started/README.md",
"chars": 532,
"preview": "# Getting Started\n\n## Get the package\n\nThe first thing you need to do, is grab the distribution files on NPM.\n\n**Via yar"
},
{
"path": "docs/getting-started/using-the-library.md",
"chars": 1625,
"preview": "# Using the library\n\n## Using Typescript\n\nIf you are using typescript, then you are in luck! The library is built in typ"
},
{
"path": "package.json",
"chars": 1369,
"preview": "{\n\t\"name\": \"@projectstorm/react-diagrams\",\n\t\"author\": \"dylanvorster\",\n\t\"private\": true,\n\t\"repository\": {\n\t\t\"type\": \"git\""
},
{
"path": "packages/geometry/.npmignore",
"chars": 53,
"preview": "*\n!dist/**/*\n!package.json\ndist/tsconfig.tsbuildinfo\n"
},
{
"path": "packages/geometry/CHANGELOG.md",
"chars": 1961,
"preview": "# @projectstorm/geometry\n\n## 7.0.3\n\n### Patch Changes\n\n- 80285fe: refactor: update lodash imports to use individual func"
},
{
"path": "packages/geometry/package.json",
"chars": 671,
"preview": "{\n\t\"name\": \"@projectstorm/geometry\",\n\t\"version\": \"7.0.3\",\n\t\"author\": \"dylanvorster\",\n\t\"license\": \"MIT\",\n\t\"repository\": {"
},
{
"path": "packages/geometry/src/BezierCurve.ts",
"chars": 1330,
"preview": "import { Point } from './Point';\nimport { Polygon } from './Polygon';\n\nexport enum BezierCurvepPoints {\n\tSOURCE = 0,\n\tSO"
},
{
"path": "packages/geometry/src/Bounds.ts",
"chars": 765,
"preview": "import { Point } from './Point';\n\nexport enum BoundsCorner {\n\tTOP_LEFT = 'TL',\n\tTOP_RIGHT = 'TR',\n\tBOTTOM_RIGHT = 'BR',\n"
},
{
"path": "packages/geometry/src/Matrix.ts",
"chars": 1579,
"preview": "import { Point } from './Point';\n\nexport class Matrix {\n\tmatrix: number[][];\n\n\tconstructor(matrix: number[][]) {\n\t\tthis."
},
{
"path": "packages/geometry/src/Point.ts",
"chars": 678,
"preview": "import { Matrix } from './Matrix';\n\nexport class Point {\n\tx: number;\n\ty: number;\n\n\tconstructor(x: number = 0, y: number "
},
{
"path": "packages/geometry/src/Polygon.ts",
"chars": 1695,
"preview": "import { Point } from './Point';\nimport _forEach from 'lodash/forEach';\nimport _map from 'lodash/map';\nimport { Matrix }"
},
{
"path": "packages/geometry/src/Rectangle.ts",
"chars": 2389,
"preview": "import { Point } from './Point';\nimport { Polygon } from './Polygon';\nimport { Bounds, BoundsCorner, boundsFromPositionA"
},
{
"path": "packages/geometry/src/index.ts",
"chars": 191,
"preview": "export * from './Point';\nexport * from './Matrix';\nexport * from './Polygon';\nexport * from './Rectangle';\nexport * from"
},
{
"path": "packages/geometry/src/toolkit.ts",
"chars": 1085,
"preview": "import { Point } from './Point';\nimport _flatMap from 'lodash/flatMap';\nimport { Polygon } from './Polygon';\nimport { Bo"
},
{
"path": "packages/geometry/tsconfig.json",
"chars": 277,
"preview": "{\n\t\"extends\": \"../../tsconfig.base.json\",\n\t\"compilerOptions\": {\n\t\t\"allowSyntheticDefaultImports\": true,\n\t\t\"outDir\": \"dis"
},
{
"path": "packages/geometry/webpack.config.js",
"chars": 178,
"preview": "const config = require('../../webpack.shared')(__dirname);\nmodule.exports = {\n\t...config,\n\toutput: {\n\t\t...config.output,"
},
{
"path": "packages/react-canvas-core/.npmignore",
"chars": 53,
"preview": "*\n!dist/**/*\n!package.json\ndist/tsconfig.tsbuildinfo\n"
},
{
"path": "packages/react-canvas-core/CHANGELOG.md",
"chars": 2324,
"preview": "# @projectstorm/react-canvas-core\n\n## 7.0.3\n\n### Patch Changes\n\n- 09ed60f: Allow more derived State classes to provide a"
},
{
"path": "packages/react-canvas-core/package.json",
"chars": 840,
"preview": "{\n\t\"name\": \"@projectstorm/react-canvas-core\",\n\t\"version\": \"7.0.3\",\n\t\"author\": \"dylanvorster\",\n\t\"license\": \"MIT\",\n\t\"repos"
},
{
"path": "packages/react-canvas-core/src/CanvasEngine.ts",
"chars": 5250,
"preview": "import _debounce from 'lodash/debounce';\nimport { CanvasModel } from './entities/canvas/CanvasModel';\nimport { FactoryBa"
},
{
"path": "packages/react-canvas-core/src/Toolkit.ts",
"chars": 876,
"preview": "export class Toolkit {\n\tstatic TESTING: boolean = false;\n\tstatic TESTING_UID = 0;\n\n\t/**\n\t * Generats a unique ID (thanks"
},
{
"path": "packages/react-canvas-core/src/actions/DeleteItemsAction.ts",
"chars": 1198,
"preview": "import { Action, ActionEvent, InputType } from '../core-actions/Action';\nimport { KeyboardEvent } from 'react';\nimport _"
},
{
"path": "packages/react-canvas-core/src/actions/PanAndZoomCanvasAction.ts",
"chars": 2389,
"preview": "import { WheelEvent } from 'react';\nimport { Action, ActionEvent, InputType } from '../core-actions/Action';\n\nexport int"
},
{
"path": "packages/react-canvas-core/src/actions/ZoomCanvasAction.ts",
"chars": 2340,
"preview": "import { WheelEvent } from 'react';\nimport { Action, ActionEvent, InputType } from '../core-actions/Action';\n\nexport int"
},
{
"path": "packages/react-canvas-core/src/core/AbstractFactory.ts",
"chars": 735,
"preview": "import { CanvasEngine } from '../CanvasEngine';\nimport { FactoryBank } from './FactoryBank';\n\n/**\n * Base factory for al"
},
{
"path": "packages/react-canvas-core/src/core/AbstractModelFactory.ts",
"chars": 486,
"preview": "import { AbstractFactory } from './AbstractFactory';\nimport { BaseModel } from '../core-models/BaseModel';\nimport { Canv"
},
{
"path": "packages/react-canvas-core/src/core/AbstractReactFactory.tsx",
"chars": 669,
"preview": "import { BaseModel } from '../core-models/BaseModel';\nimport { AbstractModelFactory } from './AbstractModelFactory';\nimp"
},
{
"path": "packages/react-canvas-core/src/core/BaseObserver.ts",
"chars": 2979,
"preview": "import { Toolkit } from '../Toolkit';\n\nexport interface BaseEvent {\n\tfiring: boolean;\n\tstopPropagation: () => any;\n}\n\nex"
},
{
"path": "packages/react-canvas-core/src/core/FactoryBank.ts",
"chars": 1601,
"preview": "import { BaseEvent, BaseListener, BaseObserver } from './BaseObserver';\nimport { AbstractFactory } from './AbstractFacto"
},
{
"path": "packages/react-canvas-core/src/core/ModelGeometryInterface.ts",
"chars": 127,
"preview": "import { Rectangle } from '@projectstorm/geometry';\n\nexport interface ModelGeometryInterface {\n\tgetBoundingBox(): Rectan"
},
{
"path": "packages/react-canvas-core/src/core-actions/Action.ts",
"chars": 1382,
"preview": "import { MouseEvent, KeyboardEvent, WheelEvent, TouchEvent, SyntheticEvent } from 'react';\nimport { Toolkit } from '../T"
},
{
"path": "packages/react-canvas-core/src/core-actions/ActionEventBus.ts",
"chars": 2552,
"preview": "import { Action, ActionEvent, InputType } from './Action';\nimport { KeyboardEvent, MouseEvent } from 'react';\nimport _fi"
},
{
"path": "packages/react-canvas-core/src/core-models/BaseEntity.ts",
"chars": 2478,
"preview": "import { Toolkit } from '../Toolkit';\nimport _cloneDeep from 'lodash/cloneDeep';\nimport { CanvasEngine } from '../Canvas"
},
{
"path": "packages/react-canvas-core/src/core-models/BaseModel.ts",
"chars": 2284,
"preview": "import {\n\tBaseEntity,\n\tBaseEntityEvent,\n\tBaseEntityGenerics,\n\tBaseEntityListener,\n\tBaseEntityOptions,\n\tDeserializeEvent\n"
},
{
"path": "packages/react-canvas-core/src/core-models/BasePositionModel.ts",
"chars": 1716,
"preview": "import { BaseModel, BaseModelGenerics, BaseModelListener, BaseModelOptions } from './BaseModel';\nimport { BaseEntityEven"
},
{
"path": "packages/react-canvas-core/src/core-state/AbstractDisplacementState.ts",
"chars": 2897,
"preview": "import { State, StateOptions } from './State';\nimport { Action, ActionEvent, InputType } from '../core-actions/Action';\n"
},
{
"path": "packages/react-canvas-core/src/core-state/State.ts",
"chars": 2748,
"preview": "import { CanvasEngine } from '../CanvasEngine';\nimport { Action, ActionEvent, InputType } from '../core-actions/Action';"
},
{
"path": "packages/react-canvas-core/src/core-state/StateMachine.ts",
"chars": 1221,
"preview": "import { State } from './State';\nimport _last from 'lodash/last';\nimport { CanvasEngine } from '../CanvasEngine';\nimport"
},
{
"path": "packages/react-canvas-core/src/entities/canvas/CanvasModel.ts",
"chars": 4849,
"preview": "import _filter from 'lodash/filter';\nimport _flatMap from 'lodash/flatMap';\nimport _forEach from 'lodash/forEach';\nimpor"
},
{
"path": "packages/react-canvas-core/src/entities/canvas/CanvasWidget.tsx",
"chars": 2856,
"preview": "import * as React from 'react';\nimport { CanvasEngine } from '../../CanvasEngine';\nimport { TransformLayerWidget } from "
},
{
"path": "packages/react-canvas-core/src/entities/layer/LayerModel.ts",
"chars": 2667,
"preview": "import { BaseModel, BaseModelGenerics, BaseModelOptions } from '../../core-models/BaseModel';\nimport { CanvasModel } fro"
},
{
"path": "packages/react-canvas-core/src/entities/layer/SmartLayerWidget.tsx",
"chars": 510,
"preview": "import * as React from 'react';\nimport { LayerModel } from './LayerModel';\nimport { CanvasEngine } from '../../CanvasEng"
},
{
"path": "packages/react-canvas-core/src/entities/layer/TransformLayerWidget.tsx",
"chars": 1396,
"preview": "import * as React from 'react';\nimport styled from '@emotion/styled';\nimport { CSSProperties } from 'react';\nimport { La"
},
{
"path": "packages/react-canvas-core/src/entities/selection/SelectionBoxLayerFactory.tsx",
"chars": 729,
"preview": "import * as React from 'react';\nimport { AbstractReactFactory, GenerateWidgetEvent } from '../../core/AbstractReactFacto"
},
{
"path": "packages/react-canvas-core/src/entities/selection/SelectionBoxWidget.tsx",
"chars": 680,
"preview": "import * as React from 'react';\nimport styled from '@emotion/styled';\nimport { SimpleClientRect } from '../../states/Sel"
},
{
"path": "packages/react-canvas-core/src/entities/selection/SelectionLayerModel.ts",
"chars": 671,
"preview": "import { LayerModel } from '../layer/LayerModel';\nimport { FactoryBank } from '../../core/FactoryBank';\nimport { Abstrac"
},
{
"path": "packages/react-canvas-core/src/index.ts",
"chars": 1533,
"preview": "export * from './CanvasEngine';\nexport * from './Toolkit';\nexport * from './entities/canvas/CanvasModel';\n\nexport * from"
},
{
"path": "packages/react-canvas-core/src/states/DefaultState.ts",
"chars": 1184,
"preview": "import { State } from '../core-state/State';\nimport { Action, ActionEvent, InputType } from '../core-actions/Action';\nim"
},
{
"path": "packages/react-canvas-core/src/states/DragCanvasState.ts",
"chars": 1562,
"preview": "import { CanvasEngine } from '../CanvasEngine';\nimport { AbstractDisplacementState, AbstractDisplacementStateEvent } fro"
},
{
"path": "packages/react-canvas-core/src/states/MoveItemsState.ts",
"chars": 1867,
"preview": "import { AbstractDisplacementState, AbstractDisplacementStateEvent } from '../core-state/AbstractDisplacementState';\nimp"
},
{
"path": "packages/react-canvas-core/src/states/SelectingState.ts",
"chars": 872,
"preview": "import { State } from '../core-state/State';\nimport { Action, ActionEvent, InputType } from '../core-actions/Action';\nim"
},
{
"path": "packages/react-canvas-core/src/states/SelectionBoxState.ts",
"chars": 2852,
"preview": "import { AbstractDisplacementState, AbstractDisplacementStateEvent } from '../core-state/AbstractDisplacementState';\nimp"
},
{
"path": "packages/react-canvas-core/src/widgets/PeformanceWidget.tsx",
"chars": 858,
"preview": "import * as React from 'react';\nimport _isEqual from 'lodash/isEqual';\nimport { BaseModel } from '../core-models/BaseMod"
},
{
"path": "packages/react-canvas-core/tsconfig.json",
"chars": 287,
"preview": "{\n\t\"extends\": \"../../tsconfig.base.json\",\n\t\"compilerOptions\": {\n\t\t\"allowSyntheticDefaultImports\": true,\n\t\t\"outDir\": \"dis"
},
{
"path": "packages/react-canvas-core/webpack.config.js",
"chars": 171,
"preview": "const config = require('../../webpack.shared')(__dirname);\nmodule.exports = {\n\t...config,\n\toutput: {\n\t\t...config.output,"
},
{
"path": "packages/react-diagrams/.npmignore",
"chars": 53,
"preview": "*\n!dist/**/*\n!package.json\ndist/tsconfig.tsbuildinfo\n"
},
{
"path": "packages/react-diagrams/CHANGELOG.md",
"chars": 2928,
"preview": "# @projectstorm/react-diagrams\n\n## 7.0.4\n\n### Patch Changes\n\n- Updated dependencies [09ed60f]\n- Updated dependencies [20"
},
{
"path": "packages/react-diagrams/README.md",
"chars": 0,
"preview": ""
},
{
"path": "packages/react-diagrams/package.json",
"chars": 817,
"preview": "{\n\t\"name\": \"@projectstorm/react-diagrams\",\n\t\"version\": \"7.0.4\",\n\t\"author\": \"dylanvorster\",\n\t\"license\": \"MIT\",\n\t\"reposito"
},
{
"path": "packages/react-diagrams/src/index.ts",
"chars": 1589,
"preview": "import {\n\tDefaultDiagramState,\n\tDiagramEngine,\n\tLinkLayerFactory,\n\tNodeLayerFactory\n} from '@projectstorm/react-diagrams"
},
{
"path": "packages/react-diagrams/tsconfig.json",
"chars": 353,
"preview": "{\n\t\"extends\": \"../../tsconfig.base.json\",\n\t\"compilerOptions\": {\n\t\t\"outDir\": \"dist\",\n\t\t\"rootDir\": \"src\",\n\t\t\"declarationDi"
},
{
"path": "packages/react-diagrams/webpack.config.js",
"chars": 168,
"preview": "const config = require('../../webpack.shared')(__dirname);\nmodule.exports = {\n\t...config,\n\toutput: {\n\t\t...config.output,"
},
{
"path": "packages/react-diagrams-core/.npmignore",
"chars": 53,
"preview": "*\n!dist/**/*\n!package.json\ndist/tsconfig.tsbuildinfo\n"
},
{
"path": "packages/react-diagrams-core/CHANGELOG.md",
"chars": 3095,
"preview": "# Changelog\n\n## 7.0.3\n\n### Patch Changes\n\n- 09ed60f: Allow more derived State classes to provide a generic type\n- 80285f"
},
{
"path": "packages/react-diagrams-core/README.md",
"chars": 82,
"preview": "# Project STORM > React Diagrams > Core\n\nThis workspace houses the default models\n"
},
{
"path": "packages/react-diagrams-core/package.json",
"chars": 1070,
"preview": "{\n\t\"name\": \"@projectstorm/react-diagrams-core\",\n\t\"version\": \"7.0.3\",\n\t\"author\": \"dylanvorster\",\n\t\"license\": \"MIT\",\n\t\"rep"
},
{
"path": "packages/react-diagrams-core/src/DiagramEngine.ts",
"chars": 8671,
"preview": "import { NodeModel } from './entities/node/NodeModel';\nimport { PortModel } from './entities/port/PortModel';\nimport { L"
},
{
"path": "packages/react-diagrams-core/src/entities/label/LabelModel.ts",
"chars": 917,
"preview": "import { LinkModel } from '../link/LinkModel';\nimport { BaseModel, BaseModelGenerics, BaseModelOptions, DeserializeEvent"
},
{
"path": "packages/react-diagrams-core/src/entities/label/LabelWidget.tsx",
"chars": 2822,
"preview": "import * as React from 'react';\nimport { DiagramEngine } from '../../DiagramEngine';\nimport { LabelModel } from './Label"
},
{
"path": "packages/react-diagrams-core/src/entities/link/LinkModel.ts",
"chars": 7912,
"preview": "import { PortModel } from '../port/PortModel';\nimport { PointModel } from './PointModel';\nimport _forEach from 'lodash/f"
},
{
"path": "packages/react-diagrams-core/src/entities/link/LinkWidget.tsx",
"chars": 3339,
"preview": "import * as React from 'react';\nimport { DiagramEngine } from '../../DiagramEngine';\nimport { LinkModel } from './LinkMo"
},
{
"path": "packages/react-diagrams-core/src/entities/link/PointModel.ts",
"chars": 1004,
"preview": "import { LinkModel } from './LinkModel';\nimport {\n\tBaseModelListener,\n\tBasePositionModel,\n\tBasePositionModelGenerics,\n\tB"
},
{
"path": "packages/react-diagrams-core/src/entities/link-layer/LinkLayerFactory.tsx",
"chars": 722,
"preview": "import * as React from 'react';\nimport { AbstractReactFactory, GenerateModelEvent, GenerateWidgetEvent } from '@projects"
},
{
"path": "packages/react-diagrams-core/src/entities/link-layer/LinkLayerModel.ts",
"chars": 995,
"preview": "import { LayerModel, LayerModelGenerics } from '@projectstorm/react-canvas-core';\nimport { LinkModel } from '../link/Lin"
},
{
"path": "packages/react-diagrams-core/src/entities/link-layer/LinkLayerWidget.tsx",
"chars": 748,
"preview": "import * as React from 'react';\nimport styled from '@emotion/styled';\nimport _map from 'lodash/map';\nimport { LinkWidget"
},
{
"path": "packages/react-diagrams-core/src/entities/node/NodeModel.ts",
"chars": 3588,
"preview": "import _forEach from 'lodash/forEach';\nimport _map from 'lodash/map';\nimport _values from 'lodash/values';\nimport { Diag"
},
{
"path": "packages/react-diagrams-core/src/entities/node/NodeWidget.tsx",
"chars": 2650,
"preview": "import * as React from 'react';\nimport _forEach from 'lodash/forEach';\nimport { DiagramEngine } from '../../DiagramEngin"
},
{
"path": "packages/react-diagrams-core/src/entities/node-layer/NodeLayerFactory.tsx",
"chars": 722,
"preview": "import * as React from 'react';\nimport { AbstractReactFactory, GenerateModelEvent, GenerateWidgetEvent } from '@projects"
},
{
"path": "packages/react-diagrams-core/src/entities/node-layer/NodeLayerModel.ts",
"chars": 996,
"preview": "import { LayerModel, LayerModelGenerics } from '@projectstorm/react-canvas-core';\nimport { NodeModel } from '../node/Nod"
},
{
"path": "packages/react-diagrams-core/src/entities/node-layer/NodeLayerWidget.tsx",
"chars": 641,
"preview": "import * as React from 'react';\nimport _map from 'lodash/map';\nimport { NodeModel } from '../node/NodeModel';\nimport { N"
},
{
"path": "packages/react-diagrams-core/src/entities/port/PortModel.ts",
"chars": 3950,
"preview": "import { NodeModel } from '../node/NodeModel';\nimport { LinkModel } from '../link/LinkModel';\nimport _forEach from 'loda"
},
{
"path": "packages/react-diagrams-core/src/entities/port/PortWidget.tsx",
"chars": 1691,
"preview": "import * as React from 'react';\nimport _keys from 'lodash/keys';\nimport { PortModel } from './PortModel';\nimport { Diagr"
},
{
"path": "packages/react-diagrams-core/src/index.ts",
"chars": 895,
"preview": "export * from './models/DiagramModel';\nexport * from './entities/label/LabelModel';\n\nexport * from './entities/link/Link"
},
{
"path": "packages/react-diagrams-core/src/models/DiagramModel.ts",
"chars": 4088,
"preview": "import _filter from 'lodash/filter';\nimport _flatMap from 'lodash/flatMap';\nimport _forEach from 'lodash/forEach';\nimpor"
},
{
"path": "packages/react-diagrams-core/src/states/DefaultDiagramState.ts",
"chars": 1717,
"preview": "import { MouseEvent, TouchEvent } from 'react';\nimport {\n\tSelectingState,\n\tState,\n\tAction,\n\tInputType,\n\tActionEvent,\n\tDr"
},
{
"path": "packages/react-diagrams-core/src/states/DragDiagramItemsState.ts",
"chars": 1248,
"preview": "import { Action, ActionEvent, InputType, MoveItemsState } from '@projectstorm/react-canvas-core';\nimport _forEach from '"
},
{
"path": "packages/react-diagrams-core/src/states/DragNewLinkState.ts",
"chars": 3399,
"preview": "import {\n\tAbstractDisplacementState,\n\tAbstractDisplacementStateEvent,\n\tAction,\n\tActionEvent,\n\tInputType\n} from '@project"
},
{
"path": "packages/react-diagrams-core/tsconfig.json",
"chars": 330,
"preview": "{\n\t\"extends\": \"../../tsconfig.base.json\",\n\t\"compilerOptions\": {\n\t\t\"allowSyntheticDefaultImports\": true,\n\t\t\"outDir\": \"dis"
},
{
"path": "packages/react-diagrams-core/webpack.config.js",
"chars": 171,
"preview": "const config = require('../../webpack.shared')(__dirname);\nmodule.exports = {\n\t...config,\n\toutput: {\n\t\t...config.output,"
},
{
"path": "packages/react-diagrams-defaults/.npmignore",
"chars": 53,
"preview": "*\n!dist/**/*\n!package.json\ndist/tsconfig.tsbuildinfo\n"
},
{
"path": "packages/react-diagrams-defaults/CHANGELOG.md",
"chars": 3146,
"preview": "# @projectstorm/react-diagrams-defaults\n\n## 7.1.3\n\n### Patch Changes\n\n- 20766f5: fix default link widget with react stri"
},
{
"path": "packages/react-diagrams-defaults/README.md",
"chars": 86,
"preview": "# Project STORM > React Diagrams > Defaults\n\nThis workspace houses the default models\n"
},
{
"path": "packages/react-diagrams-defaults/package.json",
"chars": 949,
"preview": "{\n\t\"name\": \"@projectstorm/react-diagrams-defaults\",\n\t\"version\": \"7.1.3\",\n\t\"author\": \"dylanvorster\",\n\t\"license\": \"MIT\",\n\t"
},
{
"path": "packages/react-diagrams-defaults/src/index.ts",
"chars": 614,
"preview": "export * from './label/DefaultLabelFactory';\nexport * from './label/DefaultLabelModel';\nexport * from './label/DefaultLa"
},
{
"path": "packages/react-diagrams-defaults/src/label/DefaultLabelFactory.tsx",
"chars": 671,
"preview": "import * as React from 'react';\nimport { DefaultLabelModel } from './DefaultLabelModel';\nimport { DefaultLabelWidget } f"
},
{
"path": "packages/react-diagrams-defaults/src/label/DefaultLabelModel.tsx",
"chars": 892,
"preview": "import { LabelModel, LabelModelGenerics, LabelModelOptions } from '@projectstorm/react-diagrams-core';\nimport { Deserial"
},
{
"path": "packages/react-diagrams-defaults/src/label/DefaultLabelWidget.tsx",
"chars": 582,
"preview": "import * as React from 'react';\nimport { DefaultLabelModel } from './DefaultLabelModel';\nimport styled from '@emotion/st"
},
{
"path": "packages/react-diagrams-defaults/src/link/DefaultLinkFactory.tsx",
"chars": 1432,
"preview": "import * as React from 'react';\nimport { DefaultLinkModel } from './DefaultLinkModel';\nimport { DefaultLinkWidget } from"
},
{
"path": "packages/react-diagrams-defaults/src/link/DefaultLinkModel.ts",
"chars": 3274,
"preview": "import {\n\tDiagramEngine,\n\tLabelModel,\n\tLinkModel,\n\tLinkModelGenerics,\n\tLinkModelListener,\n\tPortModel,\n\tPortModelAlignmen"
},
{
"path": "packages/react-diagrams-defaults/src/link/DefaultLinkPointWidget.tsx",
"chars": 1254,
"preview": "import * as React from 'react';\nimport { PointModel } from '@projectstorm/react-diagrams-core';\nimport styled from '@emo"
},
{
"path": "packages/react-diagrams-defaults/src/link/DefaultLinkSegmentWidget.tsx",
"chars": 1408,
"preview": "import * as React from 'react';\nimport { DefaultLinkFactory } from './DefaultLinkFactory';\nimport { DiagramEngine } from"
},
{
"path": "packages/react-diagrams-defaults/src/link/DefaultLinkWidget.tsx",
"chars": 3542,
"preview": "import { DiagramEngine, LinkWidget, PointModel } from '@projectstorm/react-diagrams-core';\nimport * as React from 'react"
}
]
// ... and 29 more files (download for full content)
About this extraction
This page contains the full source code of the projectstorm/react-diagrams GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 229 files (342.7 KB), approximately 99.8k tokens, and a symbol index with 776 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.