4 changed files with 679 additions and 3 deletions
@ -0,0 +1,524 @@ |
|||||
|
import { |
||||
|
assign, |
||||
|
forEach, |
||||
|
isArray, |
||||
|
every |
||||
|
} from 'min-dash'; |
||||
|
|
||||
|
import { |
||||
|
is |
||||
|
} from 'bpmn-js/lib/util/ModelUtil'; |
||||
|
|
||||
|
import { |
||||
|
isExpanded, |
||||
|
isEventSubProcess |
||||
|
} from 'bpmn-js/lib/util/DiUtil'; |
||||
|
|
||||
|
import { |
||||
|
isAny |
||||
|
} from 'bpmn-js/lib/features/modeling/util/ModelingUtil'; |
||||
|
|
||||
|
import { |
||||
|
getChildLanes |
||||
|
} from 'bpmn-js/lib/features/modeling/util/LaneUtil'; |
||||
|
|
||||
|
import { |
||||
|
hasPrimaryModifier |
||||
|
} from 'diagram-js/lib/util/Mouse'; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* A provider for BPMN 2.0 elements context pad |
||||
|
*/ |
||||
|
export default function ContextPadProvider( |
||||
|
config, injector, eventBus, |
||||
|
contextPad, modeling, elementFactory, |
||||
|
connect, create, popupMenu, |
||||
|
canvas, rules, translate) { |
||||
|
|
||||
|
config = config || {}; |
||||
|
|
||||
|
contextPad.registerProvider(this); |
||||
|
|
||||
|
this._contextPad = contextPad; |
||||
|
|
||||
|
this._modeling = modeling; |
||||
|
|
||||
|
this._elementFactory = elementFactory; |
||||
|
this._connect = connect; |
||||
|
this._create = create; |
||||
|
this._popupMenu = popupMenu; |
||||
|
this._canvas = canvas; |
||||
|
this._rules = rules; |
||||
|
this._translate = translate; |
||||
|
|
||||
|
if (config.autoPlace !== false) { |
||||
|
this._autoPlace = injector.get('autoPlace', false); |
||||
|
} |
||||
|
|
||||
|
eventBus.on('create.end', 250, function(event) { |
||||
|
var context = event.context, |
||||
|
shape = context.shape; |
||||
|
|
||||
|
if (!hasPrimaryModifier(event) || !contextPad.isOpen(shape)) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var entries = contextPad.getEntries(shape); |
||||
|
|
||||
|
if (entries.replace) { |
||||
|
entries.replace.action.click(event, shape); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
ContextPadProvider.$inject = [ |
||||
|
'config.contextPad', |
||||
|
'injector', |
||||
|
'eventBus', |
||||
|
'contextPad', |
||||
|
'modeling', |
||||
|
'elementFactory', |
||||
|
'connect', |
||||
|
'create', |
||||
|
'popupMenu', |
||||
|
'canvas', |
||||
|
'rules', |
||||
|
'translate' |
||||
|
]; |
||||
|
|
||||
|
ContextPadProvider.prototype.getMultiElementContextPadEntries = function(elements) { |
||||
|
var modeling = this._modeling; |
||||
|
|
||||
|
var actions = {}; |
||||
|
|
||||
|
if (this._isDeleteAllowed(elements)) { |
||||
|
assign(actions, { |
||||
|
'delete': { |
||||
|
group: 'edit', |
||||
|
className: 'bpmn-icon-trash', |
||||
|
title: this._translate('Remove'), |
||||
|
action: { |
||||
|
click: function(event, elements) { |
||||
|
modeling.removeElements(elements.slice()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return actions; |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* @param {djs.model.Base[]} elements |
||||
|
* @return {boolean} |
||||
|
*/ |
||||
|
ContextPadProvider.prototype._isDeleteAllowed = function(elements) { |
||||
|
|
||||
|
var baseAllowed = this._rules.allowed('elements.delete', { |
||||
|
elements: elements |
||||
|
}); |
||||
|
|
||||
|
if (isArray(baseAllowed)) { |
||||
|
return every(baseAllowed, function(element) { |
||||
|
return includes(baseAllowed, element); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return baseAllowed; |
||||
|
}; |
||||
|
|
||||
|
ContextPadProvider.prototype.getContextPadEntries = function(element) { |
||||
|
var contextPad = this._contextPad, |
||||
|
modeling = this._modeling, |
||||
|
|
||||
|
elementFactory = this._elementFactory, |
||||
|
connect = this._connect, |
||||
|
create = this._create, |
||||
|
popupMenu = this._popupMenu, |
||||
|
rules = this._rules, |
||||
|
autoPlace = this._autoPlace, |
||||
|
translate = this._translate; |
||||
|
|
||||
|
var actions = {}; |
||||
|
|
||||
|
if (element.type === 'label') { |
||||
|
return actions; |
||||
|
} |
||||
|
|
||||
|
var businessObject = element.businessObject; |
||||
|
|
||||
|
function startConnect(event, element) { |
||||
|
connect.start(event, element); |
||||
|
} |
||||
|
|
||||
|
function removeElement(e, element) { |
||||
|
modeling.removeElements([ element ]); |
||||
|
} |
||||
|
|
||||
|
function getReplaceMenuPosition(element) { |
||||
|
|
||||
|
var Y_OFFSET = 5; |
||||
|
|
||||
|
var pad = contextPad.getPad(element).html; |
||||
|
|
||||
|
var padRect = pad.getBoundingClientRect(); |
||||
|
|
||||
|
var pos = { |
||||
|
x: padRect.left, |
||||
|
y: padRect.bottom + Y_OFFSET |
||||
|
}; |
||||
|
|
||||
|
return pos; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* Create an append action |
||||
|
* |
||||
|
* @param {string} type |
||||
|
* @param {string} className |
||||
|
* @param {string} [title] |
||||
|
* @param {Object} [options] |
||||
|
* |
||||
|
* @return {Object} descriptor |
||||
|
*/ |
||||
|
function appendAction(type, className, title, options) { |
||||
|
|
||||
|
if (typeof title !== 'string') { |
||||
|
options = title; |
||||
|
title = translate('Append {type}', { type: type.replace(/^bpmn:/, '') }); |
||||
|
} |
||||
|
|
||||
|
function appendStart(event, element) { |
||||
|
|
||||
|
var shape = elementFactory.createShape(assign({ type: type }, options)); |
||||
|
create.start(event, shape, { |
||||
|
source: element |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
var append = autoPlace ? function(event, element) { |
||||
|
var shape = elementFactory.createShape(assign({ type: type }, options)); |
||||
|
|
||||
|
autoPlace.append(element, shape); |
||||
|
} : appendStart; |
||||
|
|
||||
|
|
||||
|
return { |
||||
|
group: 'model', |
||||
|
className: className, |
||||
|
title: title, |
||||
|
action: { |
||||
|
dragstart: appendStart, |
||||
|
click: append |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
function splitLaneHandler(count) { |
||||
|
|
||||
|
return function(event, element) { |
||||
|
|
||||
|
// actual split
|
||||
|
modeling.splitLane(element, count); |
||||
|
|
||||
|
// refresh context pad after split to
|
||||
|
// get rid of split icons
|
||||
|
contextPad.open(element, true); |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
if (isAny(businessObject, [ 'bpmn:Lane', 'bpmn:Participant' ]) && isExpanded(element)) { |
||||
|
|
||||
|
var childLanes = getChildLanes(element); |
||||
|
|
||||
|
assign(actions, { |
||||
|
'lane-insert-above': { |
||||
|
group: 'lane-insert-above', |
||||
|
className: 'bpmn-icon-lane-insert-above', |
||||
|
title: translate('Add Lane above'), |
||||
|
action: { |
||||
|
click: function(event, element) { |
||||
|
modeling.addLane(element, 'top'); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
if (childLanes.length < 2) { |
||||
|
|
||||
|
if (element.height >= 120) { |
||||
|
assign(actions, { |
||||
|
'lane-divide-two': { |
||||
|
group: 'lane-divide', |
||||
|
className: 'bpmn-icon-lane-divide-two', |
||||
|
title: translate('Divide into two Lanes'), |
||||
|
action: { |
||||
|
click: splitLaneHandler(2) |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
if (element.height >= 180) { |
||||
|
assign(actions, { |
||||
|
'lane-divide-three': { |
||||
|
group: 'lane-divide', |
||||
|
className: 'bpmn-icon-lane-divide-three', |
||||
|
title: translate('Divide into three Lanes'), |
||||
|
action: { |
||||
|
click: splitLaneHandler(3) |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
assign(actions, { |
||||
|
'lane-insert-below': { |
||||
|
group: 'lane-insert-below', |
||||
|
className: 'bpmn-icon-lane-insert-below', |
||||
|
title: translate('Add Lane below'), |
||||
|
action: { |
||||
|
click: function(event, element) { |
||||
|
modeling.addLane(element, 'bottom'); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
if (is(businessObject, 'bpmn:FlowNode')) { |
||||
|
|
||||
|
if (is(businessObject, 'bpmn:EventBasedGateway')) { |
||||
|
|
||||
|
assign(actions, { |
||||
|
'append.receive-task': appendAction( |
||||
|
'bpmn:ReceiveTask', |
||||
|
'bpmn-icon-receive-task', |
||||
|
translate('Append ReceiveTask') |
||||
|
), |
||||
|
'append.message-intermediate-event': appendAction( |
||||
|
'bpmn:IntermediateCatchEvent', |
||||
|
'bpmn-icon-intermediate-event-catch-message', |
||||
|
translate('Append MessageIntermediateCatchEvent'), |
||||
|
{ eventDefinitionType: 'bpmn:MessageEventDefinition' } |
||||
|
), |
||||
|
'append.timer-intermediate-event': appendAction( |
||||
|
'bpmn:IntermediateCatchEvent', |
||||
|
'bpmn-icon-intermediate-event-catch-timer', |
||||
|
translate('Append TimerIntermediateCatchEvent'), |
||||
|
{ eventDefinitionType: 'bpmn:TimerEventDefinition' } |
||||
|
), |
||||
|
'append.condition-intermediate-event': appendAction( |
||||
|
'bpmn:IntermediateCatchEvent', |
||||
|
'bpmn-icon-intermediate-event-catch-condition', |
||||
|
translate('Append ConditionIntermediateCatchEvent'), |
||||
|
{ eventDefinitionType: 'bpmn:ConditionalEventDefinition' } |
||||
|
), |
||||
|
'append.signal-intermediate-event': appendAction( |
||||
|
'bpmn:IntermediateCatchEvent', |
||||
|
'bpmn-icon-intermediate-event-catch-signal', |
||||
|
translate('Append SignalIntermediateCatchEvent'), |
||||
|
{ eventDefinitionType: 'bpmn:SignalEventDefinition' } |
||||
|
) |
||||
|
}); |
||||
|
} else |
||||
|
|
||||
|
if (isEventType(businessObject, 'bpmn:BoundaryEvent', 'bpmn:CompensateEventDefinition')) { |
||||
|
|
||||
|
assign(actions, { |
||||
|
'append.compensation-activity': |
||||
|
appendAction( |
||||
|
'bpmn:Task', |
||||
|
'bpmn-icon-task', |
||||
|
translate('Append compensation activity'), |
||||
|
{ |
||||
|
isForCompensation: true |
||||
|
} |
||||
|
) |
||||
|
}); |
||||
|
} else |
||||
|
|
||||
|
if (!is(businessObject, 'bpmn:EndEvent') && |
||||
|
!businessObject.isForCompensation && |
||||
|
!isEventType(businessObject, 'bpmn:IntermediateThrowEvent', 'bpmn:LinkEventDefinition') && |
||||
|
!isEventSubProcess(businessObject)) { |
||||
|
|
||||
|
assign(actions, { |
||||
|
'append.end-event': appendAction( |
||||
|
'bpmn:EndEvent', |
||||
|
'bpmn-icon-end-event-none', |
||||
|
translate('Append EndEvent') |
||||
|
), |
||||
|
'append.gateway': appendAction( |
||||
|
'bpmn:ExclusiveGateway', |
||||
|
'bpmn-icon-gateway-none', |
||||
|
translate('Append Gateway') |
||||
|
), |
||||
|
'append.append-user-task': appendAction( |
||||
|
'bpmn:Task', |
||||
|
'bpmn-icon-user-task', |
||||
|
translate('添加用户任务') |
||||
|
), |
||||
|
'append.intermediate-event': appendAction( |
||||
|
'bpmn:IntermediateThrowEvent', |
||||
|
'bpmn-icon-intermediate-event-none', |
||||
|
translate('Append Intermediate/Boundary Event') |
||||
|
) |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (!popupMenu.isEmpty(element, 'bpmn-replace')) { |
||||
|
|
||||
|
// Replace menu entry
|
||||
|
assign(actions, { |
||||
|
'replace': { |
||||
|
group: 'edit', |
||||
|
className: 'bpmn-icon-screw-wrench', |
||||
|
title: translate('Change type'), |
||||
|
action: { |
||||
|
click: function(event, element) { |
||||
|
|
||||
|
var position = assign(getReplaceMenuPosition(element), { |
||||
|
cursor: { x: event.x, y: event.y } |
||||
|
}); |
||||
|
|
||||
|
popupMenu.open(element, 'bpmn-replace', position, { |
||||
|
title: translate('Change element'), |
||||
|
width: 300, |
||||
|
search: true |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
if (is(businessObject, 'bpmn:SequenceFlow')) { |
||||
|
assign(actions, { |
||||
|
'append.text-annotation': appendAction( |
||||
|
'bpmn:TextAnnotation', |
||||
|
'bpmn-icon-text-annotation' |
||||
|
) |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
if ( |
||||
|
isAny(businessObject, [ |
||||
|
'bpmn:FlowNode', |
||||
|
'bpmn:InteractionNode', |
||||
|
'bpmn:DataObjectReference', |
||||
|
'bpmn:DataStoreReference', |
||||
|
]) |
||||
|
) { |
||||
|
assign(actions, { |
||||
|
'append.text-annotation': appendAction( |
||||
|
'bpmn:TextAnnotation', |
||||
|
'bpmn-icon-text-annotation' |
||||
|
), |
||||
|
|
||||
|
'connect': { |
||||
|
group: 'connect', |
||||
|
className: 'bpmn-icon-connection-multi', |
||||
|
title: translate( |
||||
|
'Connect using ' + |
||||
|
(businessObject.isForCompensation |
||||
|
? '' |
||||
|
: 'Sequence/MessageFlow or ') + |
||||
|
'Association' |
||||
|
), |
||||
|
action: { |
||||
|
click: startConnect, |
||||
|
dragstart: startConnect, |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
if (is(businessObject, 'bpmn:TextAnnotation')) { |
||||
|
assign(actions, { |
||||
|
'connect': { |
||||
|
group: 'connect', |
||||
|
className: 'bpmn-icon-connection-multi', |
||||
|
title: translate('Connect using Association'), |
||||
|
action: { |
||||
|
click: startConnect, |
||||
|
dragstart: startConnect, |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
if (isAny(businessObject, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ])) { |
||||
|
assign(actions, { |
||||
|
'connect': { |
||||
|
group: 'connect', |
||||
|
className: 'bpmn-icon-connection-multi', |
||||
|
title: translate('Connect using DataInputAssociation'), |
||||
|
action: { |
||||
|
click: startConnect, |
||||
|
dragstart: startConnect |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
if (is(businessObject, 'bpmn:Group')) { |
||||
|
assign(actions, { |
||||
|
'append.text-annotation': appendAction('bpmn:TextAnnotation', 'bpmn-icon-text-annotation') |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// delete element entry, only show if allowed by rules
|
||||
|
var deleteAllowed = rules.allowed('elements.delete', { elements: [ element ] }); |
||||
|
|
||||
|
if (isArray(deleteAllowed)) { |
||||
|
|
||||
|
// was the element returned as a deletion candidate?
|
||||
|
deleteAllowed = deleteAllowed[0] === element; |
||||
|
} |
||||
|
|
||||
|
if (deleteAllowed) { |
||||
|
assign(actions, { |
||||
|
'delete': { |
||||
|
group: 'edit', |
||||
|
className: 'bpmn-icon-trash', |
||||
|
title: translate('Remove'), |
||||
|
action: { |
||||
|
click: removeElement |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return actions; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// helpers /////////
|
||||
|
|
||||
|
function isEventType(eventBo, type, definition) { |
||||
|
|
||||
|
var isType = eventBo.$instanceOf(type); |
||||
|
var isDefinition = false; |
||||
|
|
||||
|
var definitions = eventBo.eventDefinitions || []; |
||||
|
forEach(definitions, function(def) { |
||||
|
if (def.$type === definition) { |
||||
|
isDefinition = true; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return isType && isDefinition; |
||||
|
} |
||||
|
|
||||
|
function includes(array, item) { |
||||
|
return array.indexOf(item) !== -1; |
||||
|
} |
@ -0,0 +1,142 @@ |
|||||
|
import { assign } from "min-dash"; |
||||
|
|
||||
|
export default function CustomPalette( |
||||
|
palette, |
||||
|
create, |
||||
|
elementFactory, |
||||
|
handTool, |
||||
|
lassoTool, |
||||
|
spaceTool, |
||||
|
globalConnect, |
||||
|
translate |
||||
|
) { |
||||
|
this.create = create; |
||||
|
this.elementFactory = elementFactory; |
||||
|
this.handTool = handTool; |
||||
|
this.lassoTool = lassoTool; |
||||
|
this.spaceTool = spaceTool; |
||||
|
this.globalConnect = globalConnect; |
||||
|
this.translate = translate; |
||||
|
|
||||
|
palette.registerProvider(this); |
||||
|
} |
||||
|
|
||||
|
CustomPalette.$inject = [ |
||||
|
"palette", |
||||
|
"create", |
||||
|
"elementFactory", |
||||
|
"handTool", |
||||
|
"lassoTool", |
||||
|
"spaceTool", |
||||
|
"globalConnect", |
||||
|
"translate" |
||||
|
]; |
||||
|
|
||||
|
CustomPalette.prototype.getPaletteEntries = function (element) { |
||||
|
const { |
||||
|
create, |
||||
|
elementFactory, |
||||
|
handTool, |
||||
|
lassoTool, |
||||
|
spaceTool, |
||||
|
globalConnect, |
||||
|
translate |
||||
|
} = this; |
||||
|
|
||||
|
function createAction(type, group, className, title, options) { |
||||
|
function createListener(event) { |
||||
|
var shape = elementFactory.createShape(assign({ type: type }, options)); |
||||
|
|
||||
|
if (options) { |
||||
|
shape.businessObject.di.isExpanded = options.isExpanded; |
||||
|
} |
||||
|
|
||||
|
create.start(event, shape); |
||||
|
} |
||||
|
|
||||
|
var shortType = type.replace(/^bpmn:/, ""); |
||||
|
|
||||
|
return { |
||||
|
group: group, |
||||
|
className: className, |
||||
|
title: title || translate("Create {type}", { type: shortType }), |
||||
|
action: { |
||||
|
dragstart: createListener, |
||||
|
click: createListener |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
'hand-tool': { |
||||
|
group: 'tools', |
||||
|
className: 'bpmn-icon-hand-tool', |
||||
|
title: '激活抓手工具', |
||||
|
action: { |
||||
|
click: function(event) { |
||||
|
handTool.activateHand(event); |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"lasso-tool": { |
||||
|
group: "tools", |
||||
|
className: "bpmn-icon-lasso-tool", |
||||
|
title: "激活套索工具", |
||||
|
action: { |
||||
|
click: function (event) { |
||||
|
lassoTool.activateSelection(event); |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
'space-tool': { |
||||
|
group: 'tools', |
||||
|
className: 'bpmn-icon-space-tool', |
||||
|
title: translate('Activate the create/remove space tool'), |
||||
|
action: { |
||||
|
click: function(event) { |
||||
|
spaceTool.activateSelection(event); |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
'global-connect-tool': { |
||||
|
group: 'tools', |
||||
|
className: 'bpmn-icon-connection-multi', |
||||
|
title: translate('Activate the global connect tool'), |
||||
|
action: { |
||||
|
click: function(event) { |
||||
|
globalConnect.start(event); |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
"tool-separator": { |
||||
|
group: "tools", |
||||
|
separator: true |
||||
|
}, |
||||
|
|
||||
|
"create.start-event": createAction( |
||||
|
"bpmn:StartEvent", |
||||
|
"event", |
||||
|
"bpmn-icon-start-event-none", |
||||
|
"创建开始节点" |
||||
|
), |
||||
|
"create.end-event": createAction( |
||||
|
"bpmn:EndEvent", |
||||
|
"event", |
||||
|
"bpmn-icon-end-event-none", |
||||
|
"创建结束节点" |
||||
|
), |
||||
|
"create.user-task": createAction( |
||||
|
"bpmn:UserTask", |
||||
|
"activity", |
||||
|
"bpmn-icon-user-task", |
||||
|
"创建用户任务" |
||||
|
), |
||||
|
"create.exclusive-gateway": createAction( |
||||
|
"bpmn:ExclusiveGateway", |
||||
|
"gateway", |
||||
|
"bpmn-icon-gateway-xor", |
||||
|
"创建排他网关" |
||||
|
) |
||||
|
}; |
||||
|
}; |
@ -0,0 +1,8 @@ |
|||||
|
import CustomContextPad from './CustomContextPad'; |
||||
|
import CustomPalette from "./CustomPalette"; |
||||
|
|
||||
|
export default { |
||||
|
__init__: [ 'paletteProvider','contextPadProvider'], |
||||
|
paletteProvider: [ 'type', CustomPalette ], |
||||
|
contextPadProvider: [ 'type', CustomContextPad ], |
||||
|
}; |
Loading…
Reference in new issue