提交 | 用户 | 时间
|
820397
|
1 |
import { assign, forEach, isArray } from 'min-dash' |
H |
2 |
|
|
3 |
import { is } from 'bpmn-js/lib/util/ModelUtil' |
|
4 |
|
|
5 |
import { isExpanded, isEventSubProcess } from 'bpmn-js/lib/util/DiUtil' |
|
6 |
|
|
7 |
import { isAny } from 'bpmn-js/lib/features/modeling/util/ModelingUtil' |
|
8 |
|
|
9 |
import { getChildLanes } from 'bpmn-js/lib/features/modeling/util/LaneUtil' |
|
10 |
|
|
11 |
import { hasPrimaryModifier } from 'diagram-js/lib/util/Mouse' |
|
12 |
|
|
13 |
/** |
|
14 |
* A provider for BPMN 2.0 elements context pad |
|
15 |
*/ |
|
16 |
export default function ContextPadProvider( |
|
17 |
config, |
|
18 |
injector, |
|
19 |
eventBus, |
|
20 |
contextPad, |
|
21 |
modeling, |
|
22 |
elementFactory, |
|
23 |
connect, |
|
24 |
create, |
|
25 |
popupMenu, |
|
26 |
canvas, |
|
27 |
rules, |
|
28 |
translate |
|
29 |
) { |
|
30 |
config = config || {} |
|
31 |
|
|
32 |
contextPad.registerProvider(this) |
|
33 |
|
|
34 |
this._contextPad = contextPad |
|
35 |
|
|
36 |
this._modeling = modeling |
|
37 |
|
|
38 |
this._elementFactory = elementFactory |
|
39 |
this._connect = connect |
|
40 |
this._create = create |
|
41 |
this._popupMenu = popupMenu |
|
42 |
this._canvas = canvas |
|
43 |
this._rules = rules |
|
44 |
this._translate = translate |
|
45 |
|
|
46 |
if (config.autoPlace !== false) { |
|
47 |
this._autoPlace = injector.get('autoPlace', false) |
|
48 |
} |
|
49 |
|
|
50 |
eventBus.on('create.end', 250, function (event) { |
|
51 |
const context = event.context, |
|
52 |
shape = context.shape |
|
53 |
|
|
54 |
if (!hasPrimaryModifier(event) || !contextPad.isOpen(shape)) { |
|
55 |
return |
|
56 |
} |
|
57 |
|
|
58 |
const entries = contextPad.getEntries(shape) |
|
59 |
|
|
60 |
if (entries.replace) { |
|
61 |
entries.replace.action.click(event, shape) |
|
62 |
} |
|
63 |
}) |
|
64 |
} |
|
65 |
|
|
66 |
ContextPadProvider.$inject = [ |
|
67 |
'config.contextPad', |
|
68 |
'injector', |
|
69 |
'eventBus', |
|
70 |
'contextPad', |
|
71 |
'modeling', |
|
72 |
'elementFactory', |
|
73 |
'connect', |
|
74 |
'create', |
|
75 |
'popupMenu', |
|
76 |
'canvas', |
|
77 |
'rules', |
|
78 |
'translate', |
|
79 |
'elementRegistry' |
|
80 |
] |
|
81 |
|
|
82 |
ContextPadProvider.prototype.getContextPadEntries = function (element) { |
|
83 |
const contextPad = this._contextPad, |
|
84 |
modeling = this._modeling, |
|
85 |
elementFactory = this._elementFactory, |
|
86 |
connect = this._connect, |
|
87 |
create = this._create, |
|
88 |
popupMenu = this._popupMenu, |
|
89 |
canvas = this._canvas, |
|
90 |
rules = this._rules, |
|
91 |
autoPlace = this._autoPlace, |
|
92 |
translate = this._translate |
|
93 |
|
|
94 |
const actions = {} |
|
95 |
|
|
96 |
if (element.type === 'label') { |
|
97 |
return actions |
|
98 |
} |
|
99 |
|
|
100 |
const businessObject = element.businessObject |
|
101 |
|
|
102 |
function startConnect(event, element) { |
|
103 |
connect.start(event, element) |
|
104 |
} |
|
105 |
|
|
106 |
function removeElement() { |
|
107 |
modeling.removeElements([element]) |
|
108 |
} |
|
109 |
|
|
110 |
function getReplaceMenuPosition(element) { |
|
111 |
const Y_OFFSET = 5 |
|
112 |
|
|
113 |
const diagramContainer = canvas.getContainer(), |
|
114 |
pad = contextPad.getPad(element).html |
|
115 |
|
|
116 |
const diagramRect = diagramContainer.getBoundingClientRect(), |
|
117 |
padRect = pad.getBoundingClientRect() |
|
118 |
|
|
119 |
const top = padRect.top - diagramRect.top |
|
120 |
const left = padRect.left - diagramRect.left |
|
121 |
|
|
122 |
const pos = { |
|
123 |
x: left, |
|
124 |
y: top + padRect.height + Y_OFFSET |
|
125 |
} |
|
126 |
|
|
127 |
return pos |
|
128 |
} |
|
129 |
|
|
130 |
/** |
|
131 |
* Create an append action |
|
132 |
* |
|
133 |
* @param {string} type |
|
134 |
* @param {string} className |
|
135 |
* @param {string} [title] |
|
136 |
* @param {Object} [options] |
|
137 |
* |
|
138 |
* @return {Object} descriptor |
|
139 |
*/ |
|
140 |
function appendAction(type, className, title, options) { |
|
141 |
if (typeof title !== 'string') { |
|
142 |
options = title |
|
143 |
title = translate('Append {type}', { type: type.replace(/^bpmn:/, '') }) |
|
144 |
} |
|
145 |
|
|
146 |
function appendStart(event, element) { |
|
147 |
const shape = elementFactory.createShape(assign({ type: type }, options)) |
|
148 |
create.start(event, shape, { |
|
149 |
source: element |
|
150 |
}) |
|
151 |
} |
|
152 |
|
|
153 |
const append = autoPlace |
|
154 |
? function (event, element) { |
|
155 |
const shape = elementFactory.createShape(assign({ type: type }, options)) |
|
156 |
|
|
157 |
autoPlace.append(element, shape) |
|
158 |
} |
|
159 |
: appendStart |
|
160 |
|
|
161 |
return { |
|
162 |
group: 'model', |
|
163 |
className: className, |
|
164 |
title: title, |
|
165 |
action: { |
|
166 |
dragstart: appendStart, |
|
167 |
click: append |
|
168 |
} |
|
169 |
} |
|
170 |
} |
|
171 |
|
|
172 |
function splitLaneHandler(count) { |
|
173 |
return function (event, element) { |
|
174 |
// actual split |
|
175 |
modeling.splitLane(element, count) |
|
176 |
|
|
177 |
// refresh context pad after split to |
|
178 |
// get rid of split icons |
|
179 |
contextPad.open(element, true) |
|
180 |
} |
|
181 |
} |
|
182 |
|
|
183 |
if (isAny(businessObject, ['bpmn:Lane', 'bpmn:Participant']) && isExpanded(businessObject)) { |
|
184 |
const childLanes = getChildLanes(element) |
|
185 |
|
|
186 |
assign(actions, { |
|
187 |
'lane-insert-above': { |
|
188 |
group: 'lane-insert-above', |
|
189 |
className: 'bpmn-icon-lane-insert-above', |
|
190 |
title: translate('Add Lane above'), |
|
191 |
action: { |
|
192 |
click: function (event, element) { |
|
193 |
modeling.addLane(element, 'top') |
|
194 |
} |
|
195 |
} |
|
196 |
} |
|
197 |
}) |
|
198 |
|
|
199 |
if (childLanes.length < 2) { |
|
200 |
if (element.height >= 120) { |
|
201 |
assign(actions, { |
|
202 |
'lane-divide-two': { |
|
203 |
group: 'lane-divide', |
|
204 |
className: 'bpmn-icon-lane-divide-two', |
|
205 |
title: translate('Divide into two Lanes'), |
|
206 |
action: { |
|
207 |
click: splitLaneHandler(2) |
|
208 |
} |
|
209 |
} |
|
210 |
}) |
|
211 |
} |
|
212 |
|
|
213 |
if (element.height >= 180) { |
|
214 |
assign(actions, { |
|
215 |
'lane-divide-three': { |
|
216 |
group: 'lane-divide', |
|
217 |
className: 'bpmn-icon-lane-divide-three', |
|
218 |
title: translate('Divide into three Lanes'), |
|
219 |
action: { |
|
220 |
click: splitLaneHandler(3) |
|
221 |
} |
|
222 |
} |
|
223 |
}) |
|
224 |
} |
|
225 |
} |
|
226 |
|
|
227 |
assign(actions, { |
|
228 |
'lane-insert-below': { |
|
229 |
group: 'lane-insert-below', |
|
230 |
className: 'bpmn-icon-lane-insert-below', |
|
231 |
title: translate('Add Lane below'), |
|
232 |
action: { |
|
233 |
click: function (event, element) { |
|
234 |
modeling.addLane(element, 'bottom') |
|
235 |
} |
|
236 |
} |
|
237 |
} |
|
238 |
}) |
|
239 |
} |
|
240 |
|
|
241 |
if (is(businessObject, 'bpmn:FlowNode')) { |
|
242 |
if (is(businessObject, 'bpmn:EventBasedGateway')) { |
|
243 |
assign(actions, { |
|
244 |
'append.receive-task': appendAction( |
|
245 |
'bpmn:ReceiveTask', |
|
246 |
'bpmn-icon-receive-task', |
|
247 |
translate('Append ReceiveTask') |
|
248 |
), |
|
249 |
'append.message-intermediate-event': appendAction( |
|
250 |
'bpmn:IntermediateCatchEvent', |
|
251 |
'bpmn-icon-intermediate-event-catch-message', |
|
252 |
translate('Append MessageIntermediateCatchEvent'), |
|
253 |
{ eventDefinitionType: 'bpmn:MessageEventDefinition' } |
|
254 |
), |
|
255 |
'append.timer-intermediate-event': appendAction( |
|
256 |
'bpmn:IntermediateCatchEvent', |
|
257 |
'bpmn-icon-intermediate-event-catch-timer', |
|
258 |
translate('Append TimerIntermediateCatchEvent'), |
|
259 |
{ eventDefinitionType: 'bpmn:TimerEventDefinition' } |
|
260 |
), |
|
261 |
'append.condition-intermediate-event': appendAction( |
|
262 |
'bpmn:IntermediateCatchEvent', |
|
263 |
'bpmn-icon-intermediate-event-catch-condition', |
|
264 |
translate('Append ConditionIntermediateCatchEvent'), |
|
265 |
{ eventDefinitionType: 'bpmn:ConditionalEventDefinition' } |
|
266 |
), |
|
267 |
'append.signal-intermediate-event': appendAction( |
|
268 |
'bpmn:IntermediateCatchEvent', |
|
269 |
'bpmn-icon-intermediate-event-catch-signal', |
|
270 |
translate('Append SignalIntermediateCatchEvent'), |
|
271 |
{ eventDefinitionType: 'bpmn:SignalEventDefinition' } |
|
272 |
) |
|
273 |
}) |
|
274 |
} else if ( |
|
275 |
isEventType(businessObject, 'bpmn:BoundaryEvent', 'bpmn:CompensateEventDefinition') |
|
276 |
) { |
|
277 |
assign(actions, { |
|
278 |
'append.compensation-activity': appendAction( |
|
279 |
'bpmn:Task', |
|
280 |
'bpmn-icon-task', |
|
281 |
translate('Append compensation activity'), |
|
282 |
{ |
|
283 |
isForCompensation: true |
|
284 |
} |
|
285 |
) |
|
286 |
}) |
|
287 |
} else if ( |
|
288 |
!is(businessObject, 'bpmn:EndEvent') && |
|
289 |
!businessObject.isForCompensation && |
|
290 |
!isEventType(businessObject, 'bpmn:IntermediateThrowEvent', 'bpmn:LinkEventDefinition') && |
|
291 |
!isEventSubProcess(businessObject) |
|
292 |
) { |
|
293 |
assign(actions, { |
|
294 |
'append.end-event': appendAction( |
|
295 |
'bpmn:EndEvent', |
|
296 |
'bpmn-icon-end-event-none', |
|
297 |
translate('Append EndEvent') |
|
298 |
), |
|
299 |
'append.gateway': appendAction( |
|
300 |
'bpmn:ExclusiveGateway', |
|
301 |
'bpmn-icon-gateway-none', |
|
302 |
translate('Append Gateway') |
|
303 |
), |
|
304 |
'append.append-task': appendAction( |
|
305 |
'bpmn:UserTask', |
|
306 |
'bpmn-icon-user-task', |
|
307 |
translate('Append Task') |
|
308 |
), |
|
309 |
'append.intermediate-event': appendAction( |
|
310 |
'bpmn:IntermediateThrowEvent', |
|
311 |
'bpmn-icon-intermediate-event-none', |
|
312 |
translate('Append Intermediate/Boundary Event') |
|
313 |
) |
|
314 |
}) |
|
315 |
} |
|
316 |
} |
|
317 |
|
|
318 |
if (!popupMenu.isEmpty(element, 'bpmn-replace')) { |
|
319 |
// Replace menu entry |
|
320 |
assign(actions, { |
|
321 |
replace: { |
|
322 |
group: 'edit', |
|
323 |
className: 'bpmn-icon-screw-wrench', |
|
324 |
title: '修改类型', |
|
325 |
action: { |
|
326 |
click: function (event, element) { |
|
327 |
const position = assign(getReplaceMenuPosition(element), { |
|
328 |
cursor: { x: event.x, y: event.y } |
|
329 |
}) |
|
330 |
|
|
331 |
popupMenu.open(element, 'bpmn-replace', position) |
|
332 |
} |
|
333 |
} |
|
334 |
} |
|
335 |
}) |
|
336 |
} |
|
337 |
|
|
338 |
if ( |
|
339 |
isAny(businessObject, [ |
|
340 |
'bpmn:FlowNode', |
|
341 |
'bpmn:InteractionNode', |
|
342 |
'bpmn:DataObjectReference', |
|
343 |
'bpmn:DataStoreReference' |
|
344 |
]) |
|
345 |
) { |
|
346 |
assign(actions, { |
|
347 |
'append.text-annotation': appendAction('bpmn:TextAnnotation', 'bpmn-icon-text-annotation'), |
|
348 |
|
|
349 |
connect: { |
|
350 |
group: 'connect', |
|
351 |
className: 'bpmn-icon-connection-multi', |
|
352 |
title: translate( |
|
353 |
'Connect using ' + |
|
354 |
(businessObject.isForCompensation ? '' : 'Sequence/MessageFlow or ') + |
|
355 |
'Association' |
|
356 |
), |
|
357 |
action: { |
|
358 |
click: startConnect, |
|
359 |
dragstart: startConnect |
|
360 |
} |
|
361 |
} |
|
362 |
}) |
|
363 |
} |
|
364 |
|
|
365 |
if (isAny(businessObject, ['bpmn:DataObjectReference', 'bpmn:DataStoreReference'])) { |
|
366 |
assign(actions, { |
|
367 |
connect: { |
|
368 |
group: 'connect', |
|
369 |
className: 'bpmn-icon-connection-multi', |
|
370 |
title: translate('Connect using DataInputAssociation'), |
|
371 |
action: { |
|
372 |
click: startConnect, |
|
373 |
dragstart: startConnect |
|
374 |
} |
|
375 |
} |
|
376 |
}) |
|
377 |
} |
|
378 |
|
|
379 |
if (is(businessObject, 'bpmn:Group')) { |
|
380 |
assign(actions, { |
|
381 |
'append.text-annotation': appendAction('bpmn:TextAnnotation', 'bpmn-icon-text-annotation') |
|
382 |
}) |
|
383 |
} |
|
384 |
|
|
385 |
// delete element entry, only show if allowed by rules |
|
386 |
let deleteAllowed = rules.allowed('elements.delete', { elements: [element] }) |
|
387 |
|
|
388 |
if (isArray(deleteAllowed)) { |
|
389 |
// was the element returned as a deletion candidate? |
|
390 |
deleteAllowed = deleteAllowed[0] === element |
|
391 |
} |
|
392 |
|
|
393 |
if (deleteAllowed) { |
|
394 |
assign(actions, { |
|
395 |
delete: { |
|
396 |
group: 'edit', |
|
397 |
className: 'bpmn-icon-trash', |
|
398 |
title: translate('Remove'), |
|
399 |
action: { |
|
400 |
click: removeElement |
|
401 |
} |
|
402 |
} |
|
403 |
}) |
|
404 |
} |
|
405 |
|
|
406 |
return actions |
|
407 |
} |
|
408 |
|
|
409 |
// helpers ///////// |
|
410 |
|
|
411 |
function isEventType(eventBo, type, definition) { |
|
412 |
const isType = eventBo.$instanceOf(type) |
|
413 |
let isDefinition = false |
|
414 |
|
|
415 |
const definitions = eventBo.eventDefinitions || [] |
|
416 |
forEach(definitions, function (def) { |
|
417 |
if (def.$type === definition) { |
|
418 |
isDefinition = true |
|
419 |
} |
|
420 |
}) |
|
421 |
|
|
422 |
return isType && isDefinition |
|
423 |
} |