mirror of
https://github.com/sartography/bpmn-js.git
synced 2025-02-22 05:38:09 +00:00
feat(grid-snapping): snap resizable elements top-left
Related to camunda/camunda-modeler#1344
This commit is contained in:
parent
369209ed59
commit
3b756e0725
25
lib/features/grid-snapping/BpmnGridSnapping.js
Normal file
25
lib/features/grid-snapping/BpmnGridSnapping.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { isAny } from '../modeling/util/ModelingUtil';
|
||||||
|
|
||||||
|
export default function BpmnGridSnapping(eventBus) {
|
||||||
|
eventBus.on([
|
||||||
|
'create.init',
|
||||||
|
'shape.move.init'
|
||||||
|
], function(event) {
|
||||||
|
var context = event.context,
|
||||||
|
shape = event.shape;
|
||||||
|
|
||||||
|
if (isAny(shape, [
|
||||||
|
'bpmn:Participant',
|
||||||
|
'bpmn:SubProcess',
|
||||||
|
'bpmn:TextAnnotation'
|
||||||
|
])) {
|
||||||
|
if (!context.gridSnappingContext) {
|
||||||
|
context.gridSnappingContext = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
context.gridSnappingContext.snapLocation = 'top-left';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
BpmnGridSnapping.$inject = [ 'eventBus' ];
|
8
lib/features/grid-snapping/index.js
Normal file
8
lib/features/grid-snapping/index.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import BpmnGridSnapping from './BpmnGridSnapping';
|
||||||
|
import GridSnappingModule from 'diagram-js/lib/features/grid-snapping';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
__depends__: [ GridSnappingModule ],
|
||||||
|
__init__: [ 'bpmnGridSnapping' ],
|
||||||
|
bpmnGridSnapping: [ 'type', BpmnGridSnapping ]
|
||||||
|
};
|
30
test/spec/features/grid-snapping/BpmnGridSnapping.bpmn
Normal file
30
test/spec/features/grid-snapping/BpmnGridSnapping.bpmn
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1kkksuc" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="2.0.0-0">
|
||||||
|
<bpmn:collaboration id="Collaboration_1">
|
||||||
|
<bpmn:participant id="Participant_1" name="Participant_1" processRef="Process_1" />
|
||||||
|
<bpmn:textAnnotation id="TextAnnotation_1">
|
||||||
|
<bpmn:text>TextAnnotation_1</bpmn:text>
|
||||||
|
</bpmn:textAnnotation>
|
||||||
|
<bpmn:association id="Association_0punwjh" sourceRef="Participant_1" targetRef="TextAnnotation_1" />
|
||||||
|
</bpmn:collaboration>
|
||||||
|
<bpmn:process id="Process_1" isExecutable="true">
|
||||||
|
<bpmn:subProcess id="SubProcess_1" name="SubProcess_1" />
|
||||||
|
</bpmn:process>
|
||||||
|
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||||
|
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration_1">
|
||||||
|
<bpmndi:BPMNShape id="Participant_1brw48p_di" bpmnElement="Participant_1">
|
||||||
|
<dc:Bounds x="100" y="100" width="590" height="250" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="SubProcess_11f0dg1_di" bpmnElement="SubProcess_1" isExpanded="true">
|
||||||
|
<dc:Bounds x="150" y="130" width="350" height="190" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="TextAnnotation_0bnhhhy_di" bpmnElement="TextAnnotation_1">
|
||||||
|
<dc:Bounds x="700" y="20" width="110" height="50" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="Association_0punwjh_di" bpmnElement="Association_0punwjh">
|
||||||
|
<di:waypoint x="646" y="100" />
|
||||||
|
<di:waypoint x="706" y="70" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
</bpmndi:BPMNPlane>
|
||||||
|
</bpmndi:BPMNDiagram>
|
||||||
|
</bpmn:definitions>
|
217
test/spec/features/grid-snapping/BpmnGridSnappingSpec.js
Normal file
217
test/spec/features/grid-snapping/BpmnGridSnappingSpec.js
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
import {
|
||||||
|
bootstrapModeler,
|
||||||
|
inject
|
||||||
|
} from 'test/TestHelper';
|
||||||
|
|
||||||
|
import coreModule from 'lib/core';
|
||||||
|
import createModule from 'diagram-js/lib/features/create';
|
||||||
|
import gridSnappingModule from 'lib/features/grid-snapping';
|
||||||
|
import modelingModule from 'lib/features/modeling';
|
||||||
|
import moveModule from 'diagram-js/lib/features/move';
|
||||||
|
|
||||||
|
import {
|
||||||
|
createCanvasEvent as canvasEvent
|
||||||
|
} from '../../../util/MockEvents';
|
||||||
|
|
||||||
|
import { isString } from 'min-dash';
|
||||||
|
|
||||||
|
var LOW_PRIORITY = 500;
|
||||||
|
|
||||||
|
|
||||||
|
describe('features/grid-snapping', function() {
|
||||||
|
|
||||||
|
var diagramXML = require('./BpmnGridSnapping.bpmn');
|
||||||
|
|
||||||
|
beforeEach(bootstrapModeler(diagramXML, {
|
||||||
|
modules: [
|
||||||
|
coreModule,
|
||||||
|
createModule,
|
||||||
|
gridSnappingModule,
|
||||||
|
modelingModule,
|
||||||
|
moveModule
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
|
||||||
|
var participant,
|
||||||
|
subProcess,
|
||||||
|
textAnnotation;
|
||||||
|
|
||||||
|
beforeEach(inject(function(elementRegistry) {
|
||||||
|
participant = elementRegistry.get('Participant_1');
|
||||||
|
subProcess = elementRegistry.get('SubProcess_1');
|
||||||
|
textAnnotation = elementRegistry.get('TextAnnotation_1');
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
describe('snap top-left', function() {
|
||||||
|
|
||||||
|
it('participant', inject(function(dragging, eventBus, move) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var events = recordEvents(eventBus, [
|
||||||
|
'shape.move.move',
|
||||||
|
'shape.move.end'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// when
|
||||||
|
move.start(canvasEvent({ x: 100, y: 100 }), participant);
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: 106, y: 112 }));
|
||||||
|
dragging.move(canvasEvent({ x: 112, y: 124 }));
|
||||||
|
dragging.move(canvasEvent({ x: 118, y: 136 }));
|
||||||
|
dragging.move(canvasEvent({ x: 124, y: 148 }));
|
||||||
|
dragging.move(canvasEvent({ x: 130, y: 160 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(events.map(position('top-left'))).to.eql([
|
||||||
|
{ x: 110, y: 110 }, // move
|
||||||
|
{ x: 110, y: 120 }, // move
|
||||||
|
{ x: 120, y: 140 }, // move
|
||||||
|
{ x: 120, y: 150 }, // move
|
||||||
|
{ x: 130, y: 160 }, // move
|
||||||
|
{ x: 130, y: 160 } // end
|
||||||
|
]);
|
||||||
|
|
||||||
|
// expect snapped to top-left
|
||||||
|
expect(participant.x).to.equal(130);
|
||||||
|
expect(participant.y).to.equal(160);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('sub process', inject(function(dragging, eventBus, move) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var events = recordEvents(eventBus, [
|
||||||
|
'shape.move.move',
|
||||||
|
'shape.move.end'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// when
|
||||||
|
move.start(canvasEvent({ x: 150, y: 130 }), subProcess);
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: 156, y: 142 }));
|
||||||
|
dragging.move(canvasEvent({ x: 162, y: 154 }));
|
||||||
|
dragging.move(canvasEvent({ x: 168, y: 166 }));
|
||||||
|
dragging.move(canvasEvent({ x: 174, y: 178 }));
|
||||||
|
dragging.move(canvasEvent({ x: 180, y: 190 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(events.map(position('top-left'))).to.eql([
|
||||||
|
{ x: 160, y: 140 }, // move
|
||||||
|
{ x: 160, y: 150 }, // move
|
||||||
|
{ x: 170, y: 170 }, // move
|
||||||
|
{ x: 170, y: 180 }, // move
|
||||||
|
{ x: 180, y: 190 }, // move
|
||||||
|
{ x: 180, y: 190 } // end
|
||||||
|
]);
|
||||||
|
|
||||||
|
// expect snapped to top-left
|
||||||
|
expect(subProcess.x).to.equal(180);
|
||||||
|
expect(subProcess.y).to.equal(190);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('text annotation', inject(function(dragging, eventBus, move) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var events = recordEvents(eventBus, [
|
||||||
|
'shape.move.move',
|
||||||
|
'shape.move.end'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// when
|
||||||
|
move.start(canvasEvent({ x: 700, y: 20 }), textAnnotation);
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: 706, y: 32 }));
|
||||||
|
dragging.move(canvasEvent({ x: 712, y: 44 }));
|
||||||
|
dragging.move(canvasEvent({ x: 718, y: 56 }));
|
||||||
|
dragging.move(canvasEvent({ x: 724, y: 68 }));
|
||||||
|
dragging.move(canvasEvent({ x: 730, y: 80 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(events.map(position('top-left'))).to.eql([
|
||||||
|
{ x: 710, y: 30 }, // move
|
||||||
|
{ x: 710, y: 40 }, // move
|
||||||
|
{ x: 720, y: 60 }, // move
|
||||||
|
{ x: 720, y: 70 }, // move
|
||||||
|
{ x: 730, y: 80 }, // move
|
||||||
|
{ x: 730, y: 80 } // end
|
||||||
|
]);
|
||||||
|
|
||||||
|
// expect snapped to top-left
|
||||||
|
expect(textAnnotation.x).to.equal(730);
|
||||||
|
expect(textAnnotation.y).to.equal(80);
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// helpers //////////
|
||||||
|
|
||||||
|
function recordEvents(eventBus, eventTypes) {
|
||||||
|
var events = [];
|
||||||
|
|
||||||
|
eventTypes.forEach(function(eventType) {
|
||||||
|
eventBus.on(eventType, LOW_PRIORITY, function(event) {
|
||||||
|
events.push(event);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns x and y of an event. If called with string that specifies orientation if will return
|
||||||
|
* x and y of specified orientation.
|
||||||
|
*
|
||||||
|
* @param {Object|string} event - Event or orientation <top|right|bottom|left>
|
||||||
|
*
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
function position(event) {
|
||||||
|
var orientation;
|
||||||
|
|
||||||
|
if (isString(event)) {
|
||||||
|
orientation = event;
|
||||||
|
|
||||||
|
return function(event) {
|
||||||
|
var shape = event.shape;
|
||||||
|
|
||||||
|
var x = event.x,
|
||||||
|
y = event.y;
|
||||||
|
|
||||||
|
if (/top/.test(orientation)) {
|
||||||
|
y -= shape.height / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/right/.test(orientation)) {
|
||||||
|
x += shape.width / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/bottom/.test(orientation)) {
|
||||||
|
y += shape.height / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/left/.test(orientation)) {
|
||||||
|
x -= shape.width / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: x,
|
||||||
|
y: y
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: event.x,
|
||||||
|
y: event.y
|
||||||
|
};
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user