feat(grid-snapping): snap resizable elements top-left
Related to camunda/camunda-modeler#1344
This commit is contained in:
parent
369209ed59
commit
3b756e0725
|
@ -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' ];
|
|
@ -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 ]
|
||||
};
|
|
@ -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>
|
|
@ -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…
Reference in New Issue