parent
60720c8ae7
commit
bff19786b4
|
@ -169,6 +169,7 @@ Modeler.prototype._interactionModules = [
|
|||
// non-modeling components
|
||||
require('./features/label-editing'),
|
||||
require('./features/auto-resize'),
|
||||
require('./features/search'),
|
||||
require('diagram-js/lib/navigation/zoomscroll'),
|
||||
require('diagram-js/lib/navigation/movecanvas'),
|
||||
require('diagram-js/lib/navigation/touch')
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
function BpmnKeyBindings(keyboard, spaceTool, lassoTool, handTool, directEditing,
|
||||
selection, canvas, elementRegistry, editorActions) {
|
||||
searchPad, selection, canvas, elementRegistry, editorActions) {
|
||||
|
||||
var actions = {
|
||||
selectElements: function() {
|
||||
|
@ -30,6 +30,9 @@ function BpmnKeyBindings(keyboard, spaceTool, lassoTool, handTool, directEditing
|
|||
if (currentSelection.length) {
|
||||
directEditing.activate(currentSelection[0]);
|
||||
}
|
||||
},
|
||||
find: function() {
|
||||
searchPad.toggle();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -44,6 +47,13 @@ function BpmnKeyBindings(keyboard, spaceTool, lassoTool, handTool, directEditing
|
|||
return true;
|
||||
}
|
||||
|
||||
// ctrl + f -> search labels
|
||||
if (key === 70 && keyboard.isCmd(modifiers)) {
|
||||
editorActions.trigger('find');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyboard.hasModifier(modifiers)) {
|
||||
return;
|
||||
}
|
||||
|
@ -84,6 +94,7 @@ BpmnKeyBindings.$inject = [
|
|||
'lassoTool',
|
||||
'handTool',
|
||||
'directEditing',
|
||||
'searchPad',
|
||||
'selection',
|
||||
'canvas',
|
||||
'elementRegistry',
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
'use strict';
|
||||
|
||||
var map = require('lodash/collection/map'),
|
||||
filter = require('lodash/collection/filter'),
|
||||
sortBy = require('lodash/collection/sortBy');
|
||||
|
||||
var labelUtil = require('../label-editing/LabelUtil');
|
||||
|
||||
|
||||
/**
|
||||
* Provides ability to search through BPMN elements
|
||||
*/
|
||||
function Search(elementRegistry, searchPad) {
|
||||
|
||||
this._elementRegistry = elementRegistry;
|
||||
|
||||
searchPad.registerProvider(this);
|
||||
}
|
||||
|
||||
module.exports = Search;
|
||||
|
||||
Search._inject = [
|
||||
'elementRegistry',
|
||||
'searchPad'
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Finds all elements that match given pattern
|
||||
*
|
||||
* <Result> :
|
||||
* {
|
||||
* primaryTokens: <Array<Token>>,
|
||||
* secondaryTokens: <Array<Token>>,
|
||||
* element: <Element>
|
||||
* }
|
||||
*
|
||||
* <Token> :
|
||||
* {
|
||||
* normal|matched: <String>
|
||||
* }
|
||||
*
|
||||
* @param {String} pattern
|
||||
* @return {Array<Result>}
|
||||
*/
|
||||
Search.prototype.find = function(pattern) {
|
||||
var elements = this._elementRegistry.filter(function(element) {
|
||||
if (element.labelTarget) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
elements = map(elements, function(element) {
|
||||
return {
|
||||
primaryTokens: matchAndSplit(labelUtil.getLabel(element), pattern),
|
||||
secondaryTokens: matchAndSplit(element.id, pattern),
|
||||
element: element
|
||||
};
|
||||
});
|
||||
|
||||
// exclude non-matched elements
|
||||
elements = filter(elements, function(element) {
|
||||
return hasMatched(element.primaryTokens) || hasMatched(element.secondaryTokens);
|
||||
});
|
||||
|
||||
elements = sortBy(elements, function(element) {
|
||||
return labelUtil.getLabel(element.element) + element.element.id;
|
||||
});
|
||||
|
||||
return elements;
|
||||
};
|
||||
|
||||
|
||||
function hasMatched(tokens) {
|
||||
var matched = filter(tokens, function(t){
|
||||
return !!t.matched;
|
||||
});
|
||||
|
||||
return matched.length > 0;
|
||||
}
|
||||
|
||||
|
||||
function matchAndSplit(text, pattern) {
|
||||
var tokens = [],
|
||||
originalText = text;
|
||||
|
||||
if (!text) {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
text = text.toLowerCase();
|
||||
pattern = pattern.toLowerCase();
|
||||
|
||||
var i = text.indexOf(pattern);
|
||||
|
||||
if (i > -1) {
|
||||
if (i !== 0) {
|
||||
tokens.push({
|
||||
normal: originalText.substr(0, i)
|
||||
});
|
||||
}
|
||||
|
||||
tokens.push({
|
||||
matched: originalText.substr(i, pattern.length)
|
||||
});
|
||||
|
||||
if (pattern.length + i < text.length) {
|
||||
tokens.push({
|
||||
normal: originalText.substr(pattern.length + i, text.length)
|
||||
});
|
||||
}
|
||||
} else {
|
||||
tokens.push({
|
||||
normal: originalText
|
||||
});
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
module.exports = {
|
||||
__depends__: [
|
||||
require('diagram-js/lib/features/search-pad')
|
||||
],
|
||||
__init__: [ 'bpmnSearch'],
|
||||
bpmnSearch: [ 'type', require('./BpmnSearchProvider') ]
|
||||
};
|
|
@ -1,12 +1,11 @@
|
|||
'use strict';
|
||||
|
||||
var TestHelper = require('../../../TestHelper');
|
||||
|
||||
var TestContainer = require('mocha-test-container-support');
|
||||
|
||||
var coreModule = require('../../../../lib/core'),
|
||||
modelingModule = require('../../../../lib/features/modeling'),
|
||||
keyboardModule = require('../../../../lib/features/keyboard'),
|
||||
bpmnSearchModule = require('../../../../lib/features/search'),
|
||||
selectionModule = require('diagram-js/lib/features/selection'),
|
||||
spaceToolModule = require('diagram-js/lib/features/space-tool'),
|
||||
lassoToolModule = require('diagram-js/lib/features/lasso-tool'),
|
||||
|
@ -27,6 +26,7 @@ describe('features - keyboard', function() {
|
|||
modelingModule,
|
||||
selectionModule,
|
||||
spaceToolModule,
|
||||
bpmnSearchModule,
|
||||
lassoToolModule,
|
||||
handToolModule,
|
||||
keyboardModule,
|
||||
|
@ -46,7 +46,7 @@ describe('features - keyboard', function() {
|
|||
|
||||
it('should include triggers inside editorActions', inject(function(editorActions) {
|
||||
// then
|
||||
expect(editorActions.length()).to.equal(11);
|
||||
expect(editorActions.length()).to.equal(12);
|
||||
}));
|
||||
|
||||
|
||||
|
@ -117,6 +117,21 @@ describe('features - keyboard', function() {
|
|||
expect(selectedElements).not.to.contain(rootElement);
|
||||
}));
|
||||
|
||||
|
||||
it('should trigger search for labels', inject(function(canvas, keyboard, searchPad, elementRegistry) {
|
||||
|
||||
sinon.spy(searchPad, 'toggle');
|
||||
|
||||
// given
|
||||
var e = createKeyEvent(container, 70, true);
|
||||
|
||||
// when
|
||||
keyboard._keyHandler(e);
|
||||
|
||||
// then
|
||||
expect(searchPad.toggle).calledOnce;
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
'use strict';
|
||||
|
||||
var coreModule = require('../../../../lib/core'),
|
||||
modelingModule = require('../../../../lib/features/modeling'),
|
||||
bpmnSearchModule = require('../../../../lib/features/search');
|
||||
|
||||
/* global bootstrapViewer, inject */
|
||||
|
||||
describe('features - BPMN search provider', function() {
|
||||
|
||||
var diagramXML = require('./bpmn-search.bpmn');
|
||||
|
||||
var testModules = [
|
||||
coreModule,
|
||||
modelingModule,
|
||||
bpmnSearchModule
|
||||
];
|
||||
|
||||
beforeEach(bootstrapViewer(diagramXML, { modules: testModules }));
|
||||
|
||||
|
||||
it('find should return all elements that match label or ID', inject(function(bpmnSearch) {
|
||||
// given
|
||||
var pattern = '123456';
|
||||
|
||||
// when
|
||||
var elements = bpmnSearch.find(pattern);
|
||||
|
||||
// then
|
||||
expect(elements).length(3);
|
||||
elements.forEach(function(e) {
|
||||
expect(e).to.have.property('element');
|
||||
expect(e).to.have.property('primaryTokens');
|
||||
expect(e).to.have.property('secondaryTokens');
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('matches IDs', inject(function(bpmnSearch) {
|
||||
// given
|
||||
var pattern = 'datastore';
|
||||
|
||||
// when
|
||||
var elements = bpmnSearch.find(pattern);
|
||||
|
||||
// then
|
||||
expect(elements[0].primaryTokens).to.eql([
|
||||
{ normal: 'has matched ID'}
|
||||
]);
|
||||
expect(elements[0].secondaryTokens).to.eql([
|
||||
{ normal: 'some_'},
|
||||
{ matched: 'DataStore'},
|
||||
{ normal: '_123456_id'},
|
||||
]);
|
||||
}));
|
||||
|
||||
|
||||
describe('should split result into matched and non matched tokens', function() {
|
||||
|
||||
it('matched all', inject(function(bpmnSearch) {
|
||||
// given
|
||||
var pattern = 'all matched';
|
||||
|
||||
// when
|
||||
var elements = bpmnSearch.find(pattern);
|
||||
|
||||
// then
|
||||
expect(elements[0].primaryTokens).to.eql([
|
||||
{ matched: 'all matched'}
|
||||
]);
|
||||
}));
|
||||
|
||||
|
||||
it('matched start', inject(function(bpmnSearch) {
|
||||
// given
|
||||
var pattern = 'before';
|
||||
|
||||
// when
|
||||
var elements = bpmnSearch.find(pattern);
|
||||
|
||||
// then
|
||||
expect(elements[0].primaryTokens).to.eql([
|
||||
{ matched: 'before'},
|
||||
{ normal: ' 321'}
|
||||
]);
|
||||
}));
|
||||
|
||||
|
||||
it('matched middle', inject(function(bpmnSearch) {
|
||||
// given
|
||||
var pattern = 'middle';
|
||||
|
||||
// when
|
||||
var elements = bpmnSearch.find(pattern);
|
||||
|
||||
// then
|
||||
expect(elements[0].primaryTokens).to.eql([
|
||||
{ normal: '123 '},
|
||||
{ matched: 'middle'},
|
||||
{ normal: ' 321'}
|
||||
]);
|
||||
}));
|
||||
|
||||
|
||||
it('matched end', inject(function(bpmnSearch) {
|
||||
// given
|
||||
var pattern = 'after';
|
||||
|
||||
// when
|
||||
var elements = bpmnSearch.find(pattern);
|
||||
|
||||
// then
|
||||
expect(elements[0].primaryTokens).to.eql([
|
||||
{ normal: '123 '},
|
||||
{ matched: 'after'}
|
||||
]);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,100 @@
|
|||
<?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:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn">
|
||||
<bpmn:process id="Process_1" isExecutable="false">
|
||||
<bpmn:task id="Task_1j5i0e6" name="Second 123456 task here">
|
||||
<bpmn:outgoing>SequenceFlow_0wgiusn</bpmn:outgoing>
|
||||
<bpmn:dataOutputAssociation id="DataOutputAssociation_1jomsz7">
|
||||
<bpmn:targetRef>some_DataStore_123456_id</bpmn:targetRef>
|
||||
</bpmn:dataOutputAssociation>
|
||||
</bpmn:task>
|
||||
<bpmn:intermediateThrowEvent id="IntermediateThrowEvent_1lhurmj" name="Third 123456">
|
||||
<bpmn:incoming>SequenceFlow_0wgiusn</bpmn:incoming>
|
||||
</bpmn:intermediateThrowEvent>
|
||||
<bpmn:sequenceFlow id="SequenceFlow_0wgiusn" sourceRef="Task_1j5i0e6" targetRef="IntermediateThrowEvent_1lhurmj" />
|
||||
<bpmn:dataStoreReference id="some_DataStore_123456_id" name="has matched ID" />
|
||||
<bpmn:task id="Task_0dso4ju" name="UNIQUE ELEMENT" />
|
||||
<bpmn:task id="Task_asdfasd" name="before 321">
|
||||
<bpmn:incoming>SequenceFlow_1bhe9h2</bpmn:incoming>
|
||||
<bpmn:outgoing>SequenceFlow_02ymelh</bpmn:outgoing>
|
||||
</bpmn:task>
|
||||
<bpmn:task id="Task_asdfasddgg" name="123 middle 321">
|
||||
<bpmn:incoming>SequenceFlow_02ymelh</bpmn:incoming>
|
||||
<bpmn:outgoing>SequenceFlow_0ugwp0d</bpmn:outgoing>
|
||||
</bpmn:task>
|
||||
<bpmn:sequenceFlow id="SequenceFlow_02ymelh" sourceRef="Task_asdfasd" targetRef="Task_asdfasddgg" />
|
||||
<bpmn:task id="Task_asdfasdsdfgg" name="123 after">
|
||||
<bpmn:incoming>SequenceFlow_0ugwp0d</bpmn:incoming>
|
||||
</bpmn:task>
|
||||
<bpmn:sequenceFlow id="SequenceFlow_0ugwp0d" sourceRef="Task_asdfasddgg" targetRef="Task_asdfasdsdfgg" />
|
||||
<bpmn:task id="Task_0vuhy0s" name="all matched">
|
||||
<bpmn:outgoing>SequenceFlow_1bhe9h2</bpmn:outgoing>
|
||||
</bpmn:task>
|
||||
<bpmn:sequenceFlow id="SequenceFlow_1bhe9h2" sourceRef="Task_0vuhy0s" targetRef="Task_asdfasd" />
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
|
||||
<bpmndi:BPMNShape id="Task_1j5i0e6_di" bpmnElement="Task_1j5i0e6">
|
||||
<dc:Bounds x="195" y="106" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="IntermediateThrowEvent_1lhurmj_di" bpmnElement="IntermediateThrowEvent_1lhurmj">
|
||||
<dc:Bounds x="227" y="299" width="36" height="36" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="200" y="335" width="90" height="20" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_0wgiusn_di" bpmnElement="SequenceFlow_0wgiusn">
|
||||
<di:waypoint xsi:type="dc:Point" x="245" y="186" />
|
||||
<di:waypoint xsi:type="dc:Point" x="245" y="299" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="339" y="265" width="90" height="20" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="DataStoreReference_093mpev_di" bpmnElement="some_DataStore_123456_id">
|
||||
<dc:Bounds x="371" y="121" width="50" height="50" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="351" y="186" width="90" height="20" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_0dso4ju_di" bpmnElement="Task_0dso4ju">
|
||||
<dc:Bounds x="0" y="0" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_0vyzyuo_di" bpmnElement="Task_asdfasd">
|
||||
<dc:Bounds x="622.5883069427528" y="246.96102314250913" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_1wkhcs9_di" bpmnElement="Task_asdfasddgg">
|
||||
<dc:Bounds x="783" y="247" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_02ymelh_di" bpmnElement="SequenceFlow_02ymelh">
|
||||
<di:waypoint xsi:type="dc:Point" x="723" y="287" />
|
||||
<di:waypoint xsi:type="dc:Point" x="783" y="287" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="708" y="277" width="90" height="20" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="Task_1m7fa4o_di" bpmnElement="Task_asdfasdsdfgg">
|
||||
<dc:Bounds x="939" y="247" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_0ugwp0d_di" bpmnElement="SequenceFlow_0ugwp0d">
|
||||
<di:waypoint xsi:type="dc:Point" x="883" y="287" />
|
||||
<di:waypoint xsi:type="dc:Point" x="939" y="287" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="866" y="277" width="90" height="20" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="Task_0vuhy0s_di" bpmnElement="Task_0vuhy0s">
|
||||
<dc:Bounds x="471" y="247" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_1bhe9h2_di" bpmnElement="SequenceFlow_1bhe9h2">
|
||||
<di:waypoint xsi:type="dc:Point" x="571" y="287" />
|
||||
<di:waypoint xsi:type="dc:Point" x="623" y="287" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="552" y="277" width="90" height="20" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="DataOutputAssociation_1jomsz7_di" bpmnElement="DataOutputAssociation_1jomsz7">
|
||||
<di:waypoint xsi:type="dc:Point" x="295" y="146" />
|
||||
<di:waypoint xsi:type="dc:Point" x="371" y="146" />
|
||||
</bpmndi:BPMNEdge>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
Loading…
Reference in New Issue