Initial Commit

This commit is contained in:
NWalker4483 2021-06-03 01:45:51 -04:00
commit fdd6b75c99
21 changed files with 8575 additions and 0 deletions

1
.eslintignore Normal file
View File

@ -0,0 +1 @@
dist/

6
.eslintrc Normal file
View File

@ -0,0 +1,6 @@
{
"extends": "plugin:bpmn-io/es6",
"env": {
"browser": true
}
}

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/node_modules/
dist/

9
.npmignore Normal file
View File

@ -0,0 +1,9 @@
resources/
test/
!dist
lib/
.eslintignore
.eslintrc
karma.conf.js
.travis.yml
rollup.config.js

8
.travis.yml Normal file
View File

@ -0,0 +1,8 @@
language:
- node_js
node_js:
- node
script:
- npm run all

77
CHANGELOG.md Normal file
View File

@ -0,0 +1,77 @@
# Changelog
All notable changes to [diagram-js-minimap](https://github.com/bpmn-io/diagram-js-minimap) are documented here. We use [semantic versioning](http://semver.org/) for releases.
## Unreleased
___Note:__ Yet to be released changes appear here._
## 2.0.4
* `FIX`: translate toggle button content ([#43](https://github.com/bpmn-io/diagram-js-minimap/issues/43))
## 2.0.3
Re-released `v2.0.2`.
## 2.0.2
* `FIX`: do not log graphics not found ([#38](https://github.com/bpmn-io/diagram-js-minimap/issues/38))
## 2.0.1
* `FIX`: prevent minimap from crashing on mouse move ([#36](https://github.com/bpmn-io/diagram-js-minimap/issues/36))
## 2.0.0
* `CHORE`: provide pre-packaged distribution
* `CHORE`: bump to `diagram-js@4`
* `FIX`: only update viewbox on valid bounds
## 1.3.0
* `CHORE`: bump to `diagram-js@3`
## 1.2.2
__Republish with updated changelog.__
## 1.2.0
* `FEAT`: zoom on CTRL key only ([`a1848cf8`](https://github.com/bpmn-io/diagram-js-minimap/commit/a1848cf880478a74fb799422780df10f7e6d7d8f))
* `FEAT`: center & drag on SVG mouse down ([`5585f871`](https://github.com/bpmn-io/diagram-js-minimap/commit/5585f871933f6ec39d964907d6ab1a33d176cf8f))
* `FIX`: change title attribute depending on open/closed ([`5bc0e04a`](https://github.com/bpmn-io/diagram-js-minimap/commit/5bc0e04aedefb46f867b734aa9a303db3ea6c0b7))
## 1.1.2
* `FIX`: use `svgClasses` for IE 11 compatibility ([#25](https://github.com/bpmn-io/diagram-js-minimap/issues/25))
## 1.1.1
* `FIX`: export `Minimap` as ES module
## 1.1.0
* `FEAT`: align minimap to canvas (0, 0) if possible ([#17](https://github.com/bpmn-io/diagram-js-minimap/issues/17))
* `FIX`: make close handle always clickable ([#18](https://github.com/bpmn-io/diagram-js-minimap/issues/18))
* `FIX`: correct stepping when zooming out ([#19](https://github.com/bpmn-io/diagram-js-minimap/issues/19))
* `FIX`: use same zoom directions like diagram-js `ZoomScroll`
## 1.0.0
### Breaking Changes
* `CHORE`: migrate to ES modules
### Other Improvements
* `FEAT`: improved minimap UX ([#4](https://github.com/bpmn-io/diagram-js-minimap/issues/4))
* `FEAT`: add more intuitive open / close controls ([#5](https://github.com/bpmn-io/diagram-js-minimap/issues/5))
* `FIX`: disallow minimap zoom outside of minimap ([`153093be`](https://github.com/bpmn-io/diagram-js-minimap/commit/153093be7f9b3999d2b2653613db427aecb83687))
* `FIX`: ignore canvas.resized events if not present in DOM ([`24614f86`](https://github.com/bpmn-io/diagram-js-minimap/commit/24614f86856a7e1b75950ffbb1a96d2d11541b5c))
* `FIX`: correct wheel / click interaction ([#12](https://github.com/bpmn-io/diagram-js-minimap/issues/12))
* `FIX`: properly cleanup global event listeners ([#16](https://github.com/bpmn-io/diagram-js-minimap/issues/16))
## ...
Check `git log` for earlier history.

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017-present camunda Services GmbH
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.

44
README.md Normal file
View File

@ -0,0 +1,44 @@
# diagram-js Minimap
[![Build Status](https://travis-ci.com/bpmn-io/diagram-js-minimap.svg?branch=master)](https://travis-ci.com/bpmn-io/diagram-js-minimap)
A minimap for diagram-js.
![Minimap](resources/screenshot.png)
## Features
* See the whole diagram in the minimap
* Highlight current viewport
* Click/drag/scroll the minimap to navigate the diagram
## Usage
Extend your diagram-js application with the minimap module. We'll use [bpmn-js](https://github.com/bpmn-io/bpmn-js) as an example:
```javascript
import BpmnModeler from 'bpmn-js/lib/Modeler';
import minimapModule from 'diagram-js-minimap';
var bpmnModeler = new BpmnModeler({
additionalModules: [
minimapModule
]
});
```
For proper styling integrate the embedded style sheet:
```html
<link rel="stylesheet" href="diagram-js-minimap/assets/diagram-js-minimap.css" />
```
Please see [this example](https://github.com/bpmn-io/bpmn-js-examples/tree/master/minimap) for a more detailed instruction.
## License
MIT

View File

@ -0,0 +1,48 @@
.djs-editor {
position: absolute;
top: 90px;
right: 20px;
left: 500px;
/* overflow: hidden; */
background-color: rgba(255, 255, 255, 0.9);
border: solid 1px #CCC;
border-radius: 2px;
box-sizing: border-box;
/* user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none; */
}
.djs-editor:not(.open) {
overflow: hidden;
}
.djs-editor .ide {
display: none;
}
.djs-editor.open .ide {
display: block;
}
.djs-editor .ide {
width: 620px;
height: 280px;
}
.djs-editor:not(.open) .toggle {
padding: 10px;
text-align: center;
}
.djs-editor .toggle:before {
content: attr(title);
}
.djs-editor.open .toggle {
position: absolute;
right: 0;
padding: 6px;
z-index: 1;
}

43
karma.conf.js Normal file
View File

@ -0,0 +1,43 @@
'use strict';
// configures browsers to run test against
// any of [ 'ChromeHeadless', 'Chrome', 'Firefox', 'IE', 'PhantomJS' ]
var browsers = (process.env.TEST_BROWSERS || 'PhantomJS').split(',');
// use puppeteer provided Chrome for testing
process.env.CHROME_BIN = require('puppeteer').executablePath();
module.exports = function(karma) {
karma.set({
frameworks: [
'mocha',
'sinon-chai'
],
files: [
'test/spec/*Spec.js'
],
preprocessors: {
'test/spec/*Spec.js': [ 'webpack' ]
},
browsers,
autoWatch: false,
singleRun: true,
webpack: {
mode: 'development',
module: {
rules: [
{
test: /\.css$/,
use: 'raw-loader'
}
]
}
}
});
};

164
lib/Editor.js Normal file
View File

@ -0,0 +1,164 @@
import {
attr as domAttr,
classes as domClasses,
event as domEvent,
query as domQuery
} from 'min-dom';
import {
append as svgAppend,
attr as svgAttr,
classes as svgClasses,
clone as svgClone,
create as svgCreate,
remove as svgRemove
} from 'tiny-svg';
import {
assign,
every,
isNumber,
isObject
} from 'min-dash';
import CodeMirror from 'codemirror';
import cssEscape from 'css.escape';
import { getVisual } from 'diagram-js/lib/util/GraphicsUtil';
/**
* A code editor that reflects and lets you navigate the diagram.
*/
export default function Editor(
config, injector, eventBus,
canvas, elementRegistry) {
var self = this;
this._canvas = canvas;
this._elementRegistry = elementRegistry;
this._eventBus = eventBus;
this._injector = injector;
this._state = {
isOpen: undefined,
isDragging: false,
initialDragPosition: null,
offsetViewport: null,
cachedViewbox: null,
dragger: null,
svgClientRect: null,
parentClientRect: null,
zoomDelta: 0
};
this._init();
this.toggle((config && config.open) || false);
domEvent.bind(this._toggle, 'click', function(event) {
event.preventDefault();
event.stopPropagation();
self.toggle();
});
}
Editor.$inject = [
'config.editor',
'injector',
'eventBus',
'canvas',
'elementRegistry'
];
Editor.prototype._init = function() {
var canvas = this._canvas,
container = canvas.getContainer();
// create parent div
var parent = this._parent = document.createElement('div');
domClasses(parent).add('djs-editor');
container.appendChild(parent);
// create toggle
var toggle = this._toggle = document.createElement('div');
domClasses(toggle).add('toggle');
parent.appendChild(toggle);
// create ide textarea
var ide = this._ide = document.createElement('textarea');
ide.name = "code";
ide.maxLength = "5000";
ide.cols = "80";
ide.rows = "40";
domClasses(ide).add('ide');
parent.appendChild(ide);
CodeMirror.fromTextArea(ide, {
lineNumbers: true,
mode: "python",
theme: 'monokai'
});
};
Editor.prototype._validate = function() {
// ! This parts gonna be hard
}
Editor.prototype.open = function() {
assign(this._state, { isOpen: true });
domClasses(this._parent).add('open');
var translate = this._injector.get('translate', false) || function(s) { return s; };
domAttr(this._toggle, 'title', translate('Close Editor'));
this._eventBus.fire('editor.toggle', { open: true });
};
Editor.prototype.close = function() {
assign(this._state, { isOpen: false });
domClasses(this._parent).remove('open');
var translate = this._injector.get('translate', false) || function(s) { return s; };
domAttr(this._toggle, 'title', translate('Open Editor'));
this._eventBus.fire('editor.toggle', { open: false });
};
Editor.prototype.toggle = function(open) {
var currentOpen = this.isOpen();
if (typeof open === 'undefined') {
open = !currentOpen;
}
if (open == currentOpen) {
return;
}
if (open) {
this.open();
} else {
this.close();
}
};
Editor.prototype.isOpen = function() {
return this._state.isOpen;
};

8
lib/index.js Normal file
View File

@ -0,0 +1,8 @@
import Editor from './Editor';
import CoreModule from 'diagram-js/lib/core';
export default {
// __depends__: [ CoreModule ], // {2}
__init__: [ 'editor' ], // {3}
editor: [ 'type', Editor ] // {1}
};

7589
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

65
package.json Normal file
View File

@ -0,0 +1,65 @@
{
"name": "diagram-js-code-editor",
"version": "1.0.0",
"description": "A code-editor for diagram-js",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"umd:main": "dist/diagram-code-editor.umd.js",
"source": "lib/index.js",
"scripts": {
"all": "run-s lint test distro",
"lint": "eslint .",
"dev": "npm test -- --auto-watch --no-single-run",
"test": "karma start",
"distro": "run-s build test:build",
"build": "rollup -c",
"test:build": "karma start test/distro/karma.conf.js",
"prepublishOnly": "run-s distro"
},
"repository": {
"type": "git",
"url": "git@github.com:NWalker4483/diagram-js-code-editor.git"
},
"keywords": [
"diagram-js",
"code-editor"
],
"author": "Philipp Fromme",
"license": "MIT",
"bugs": {
"url": "https://github.com/NWalker4483/diagram-js-code-editor/issues"
},
"homepage": "https://github.com/NWalker4483/diagram-js-code-editor#readme",
"devDependencies": {
"chai": "^4.2.0",
"diagram-js": "^5.0.2",
"eslint": "^6.5.0",
"eslint-plugin-bpmn-io": "^0.10.0",
"karma": "^4.3.0",
"karma-chrome-launcher": "^3.1.0",
"karma-firefox-launcher": "^1.2.0",
"karma-mocha": "^1.3.0",
"karma-phantomjs-launcher": "^1.0.4",
"karma-sinon-chai": "^2.0.2",
"karma-webpack": "^4.0.2",
"mocha": "^6.2.1",
"mocha-test-container-support": "^0.2.0",
"npm-run-all": "^4.1.5",
"puppeteer": "^3.0.0",
"raw-loader": "^3.1.0",
"rollup": "^1.22.0",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-terser": "^5.1.2",
"sinon": "^7.5.0",
"sinon-chai": "^3.3.0",
"webpack": "^4.41.0"
},
"dependencies": {
"codemirror": "^5.61.1",
"css.escape": "^1.5.1",
"min-dash": "^3.5.2",
"min-dom": "^3.1.1",
"tiny-svg": "^2.2.2"
}
}

BIN
resources/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

58
rollup.config.js Normal file
View File

@ -0,0 +1,58 @@
import { terser } from 'rollup-plugin-terser';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import pkg from './package.json';
const srcEntry = pkg.source;
const umdDist = pkg[ 'umd:main' ];
const umdName = 'DiagramJSMinimap';
function pgl(plugins=[]) {
return plugins;
}
export default [
// browser-friendly UMD build
{
input: srcEntry,
output: {
file: umdDist.replace(/\.js$/, '.prod.js'),
format: 'umd',
name: umdName
},
plugins: pgl([
resolve(),
commonjs(),
terser()
])
},
{
input: srcEntry,
output: {
file: umdDist,
format: 'umd',
name: umdName
},
plugins: pgl([
resolve(),
commonjs()
])
},
{
input: srcEntry,
output: [
{ file: pkg.main, format: 'cjs' },
{ file: pkg.module, format: 'es' }
],
external: [
'diagram-js/lib/util/GraphicsUtil',
'css.escape',
'min-dash',
'min-dom',
'tiny-svg'
],
plugins: pgl()
}
];

3
test/.eslintrc Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "plugin:bpmn-io/mocha"
}

1
test/TestHelper.js Normal file
View File

@ -0,0 +1 @@
export * from 'diagram-js/test/helper';

16
test/distro/distroSpec.js Normal file
View File

@ -0,0 +1,16 @@
describe('distro', function() {
it('should expose CJS bundle', function() {
const DiagramJSMinimap = require('../..');
expect(DiagramJSMinimap).to.exist;
});
it('should expose UMD bundle', function() {
const DiagramJSMinimap = require('../../dist/diagram-minimap.umd.js');
expect(DiagramJSMinimap).to.exist;
});
});

33
test/distro/karma.conf.js Normal file
View File

@ -0,0 +1,33 @@
'use strict';
// configures browsers to run test against
// any of [ 'ChromeHeadless', 'Chrome', 'Firefox', 'IE', 'PhantomJS' ]
var browsers = (process.env.TEST_BROWSERS || 'PhantomJS').split(',');
// use puppeteer provided Chrome for testing
process.env.CHROME_BIN = require('puppeteer').executablePath();
module.exports = function(karma) {
karma.set({
frameworks: [
'mocha',
'sinon-chai'
],
files: [ '*Spec.js' ],
preprocessors: {
'*Spec.js': [ 'webpack' ]
},
browsers,
autoWatch: false,
singleRun: true,
webpack: {
mode: 'development'
}
});
};

379
test/spec/MinimapSpec.js Normal file
View File

@ -0,0 +1,379 @@
/* global sinon */
import {
attr as svgAttr,
remove as domRemove,
query as domQuery
} from 'min-dom';
import Diagram from 'diagram-js';
import {
bootstrapDiagram,
getDiagramJS,
inject,
insertCSS
} from '../TestHelper';
import minimapModule from '../../lib';
import modelingModule from 'diagram-js/lib/features/modeling';
import moveCanvasModule from 'diagram-js/lib/navigation/movecanvas';
import moveModule from 'diagram-js/lib/features/move';
import zoomScrollModule from 'diagram-js/lib/navigation/zoomscroll';
import minimapCSS from '../../assets/diagram-js-minimap.css';
insertCSS('diagram-js-minimap.css', minimapCSS);
var viewerModules = [
minimapModule,
moveCanvasModule,
zoomScrollModule
];
var modelerModules = viewerModules.concat([
modelingModule,
moveModule
]);
describe('minimap', function() {
this.timeout(20000);
describe('viewer', function() {
beforeEach(bootstrapDiagram({
modules: viewerModules,
minimap: {
open: true
}
}));
it('should show', inject(function(canvas, elementFactory) {
// when
var shapeA = elementFactory.createShape({
id: 'A',
width: 100,
height: 300,
x: 50,
y: 150
});
canvas.addShape(shapeA, canvas.getRootElement());
var shapeB = elementFactory.createShape({
id: 'B',
width: 50,
height: 50,
x: 775,
y: 1175
});
canvas.addShape(shapeB, canvas.getRootElement());
var shapeC = elementFactory.createShape({
id: 'C',
width: 300,
height: 300,
x: 650,
y: -50
});
canvas.addShape(shapeC, canvas.getRootElement());
// then
expectMinimapShape('A');
expectMinimapShape('B');
expectMinimapShape('C');
}));
it('should show single element', inject(function(canvas, elementFactory) {
// when
var shapeA = elementFactory.createShape({
id: 'A',
width: 100,
height: 300,
x: 50,
y: 150
});
canvas.addShape(shapeA, canvas.getRootElement());
// then
expectMinimapShape('A');
}));
});
describe('modeler', function() {
beforeEach(bootstrapDiagram({
modules: modelerModules,
minimap: {
open: true
}
}));
it('should show', inject(function(canvas, modeling, elementFactory) {
// when
var shapeA = elementFactory.createShape({
id: 'A',
width: 100,
height: 300
});
modeling.createShape(shapeA, { x: 100, y: 300 }, canvas.getRootElement());
var shapeB = elementFactory.createShape({
id: 'B',
width: 50,
height: 50
});
modeling.createShape(shapeB, { x: 800, y: 1200 }, canvas.getRootElement());
var shapeC = elementFactory.createShape({
id: 'C',
width: 300,
height: 300
});
modeling.createShape(shapeC, { x: 800, y: 100 }, canvas.getRootElement());
var shapes = generateShapes(200, {
x: -200,
y: -50,
width: 3000,
height: 1000
});
// then
expectMinimapShape('A');
expectMinimapShape('B');
expectMinimapShape('C');
expectMinimapShapes(shapes);
}));
it('should update', inject(function(canvas, elementFactory, modeling) {
// given
var parent = elementFactory.createShape({
id: 'parent',
width: 100,
height: 100,
x: 100,
y: 100
});
var child = elementFactory.createShape({
id: 'child',
parent: parent,
width: 50,
height: 50,
x: 125,
y: 125
});
var rootElement = canvas.getRootElement();
modeling.createElements([ parent, child ], { x: 100, y: 100 }, rootElement);
expectMinimapShape('parent');
expectMinimapShape('child');
// when
modeling.resizeShape(parent, { x: 50, y: 50, width: 200, height: 100 });
// then
expectMinimapShape('parent');
expectMinimapShape('child');
}));
});
describe('canvas.resized', function() {
beforeEach(bootstrapDiagram({
modules: viewerModules,
minimap: {
open: true
}
}));
it('should not update if not present in DOM', inject(
function(canvas, eventBus, minimap) {
// given
var spy = sinon.spy(minimap, '_update');
// when
domRemove(canvas.getContainer());
eventBus.fire('canvas.resized');
// then
expect(spy).to.not.have.been.called;
}
));
});
describe('update', function() {
it('should not error on viewbox changed', function() {
var diagram = new Diagram({
modules: modelerModules
});
var canvas = diagram.get('canvas');
canvas.viewbox({
x: 0,
y: 0,
width: 100,
height: 100
});
});
it('should not error on viewbox changed (malformed values)', function() {
var diagram = new Diagram({
modules: modelerModules
});
var canvas = diagram.get('canvas');
canvas.viewbox({
x: 0,
y: 0,
width: Infinity,
height: Infinity
});
});
});
describe('mousemove', function() {
beforeEach(bootstrapDiagram({
modules: viewerModules,
minimap: {
open: true
}
}));
it('should change viewbox on mousemove', inject(function(eventBus, minimap) {
// given
var svg = minimap._svg;
var listener = sinon.spy();
eventBus.on('canvas.viewbox.changing', listener);
// when
triggerMouseEvent('mousedown', svg);
triggerMouseEvent('mousemove', svg);
triggerMouseEvent('mousemove', svg);
// then
// 1 mousedown + 2 mousemove
expect(listener).to.have.been.calledThrice;
}));
});
});
// helpers /////////////////
function generateShapes(count, viewport) {
return getDiagramJS().invoke(function(canvas, elementFactory, modeling) {
var rootElement = canvas.getRootElement(),
shape;
var shapes = [];
for (var i = 0; i < count; i++) {
shape = elementFactory.createShape({
id: 'shape' + i,
width: random(10, 300),
height: random(10, 200),
});
shapes.push(modeling.createShape(shape, {
x: random(viewport.x, viewport.width),
y: random(viewport.y, viewport.height)
}, rootElement));
}
return shapes;
});
}
function random(start, end) {
return Math.round(Math.random() * (end - start) + start);
}
function triggerMouseEvent(type, gfx) {
var event = document.createEvent('MouseEvent');
event.initMouseEvent(type, true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null);
return gfx.dispatchEvent(event);
}
function expectMinimapShape(id) {
getDiagramJS().invoke(function(elementRegistry, minimap) {
var element = elementRegistry.get(id);
expect(element).to.exist;
var minimapShape = domQuery('g#' + id, minimap._parent);
expect(minimapShape).to.exist;
var transform = svgAttr(minimapShape, 'transform');
var translate = transform.replace('translate(', '').replace(')', '').split(' ');
var x = parseInt(translate[0], 10),
y = parseInt(translate[1], 10);
var parentX = element.parent.x || 0,
parentY = element.parent.y || 0;
expect(x).to.equal(element.x - parentX);
expect(y).to.equal(element.y - parentY);
});
}
function expectMinimapShapes(shapes) {
shapes.forEach(function(shape) {
var id = shape.id;
expectMinimapShape(id);
});
}