const bpmnContainerElt = window.document.getElementById('bpmn-container'); // TODO enable navigation support // There is currently an issue with popover which are wrongly updated during and after pan const bpmnVisualization = new bpmnvisu.BpmnVisualization({ container: bpmnContainerElt, navigation: { enabled: false } }); const diagram = getHorizontalBpmnDiagram(); bpmnVisualization.load(diagram, { fit: {type: 'Center', margin: 10 } }); const registeredBpmnElements = new Map(); const bpmnElementsRegistry = bpmnVisualization.bpmnElementsRegistry; const elementsWithPopover = bpmnElementsRegistry.getElementsByIds([ 'Activity_1j15wcw', // manual task 3 'Activity_0y3sd80', //script task 5 ]); const elementsWithPopup = bpmnElementsRegistry.getElementsByIds([ 'Activity_0kn4d46', // user task 2 'Flow_12yysoe', // sequence flow between 'task 5' and 'end event' ]); // tippy global configuration tippy.setDefaultProps({ content: 'Loading...', allowHTML: true, onShow(instance) { instance.setContent(getBpmnElementInfoAsHtml(instance.reference)); }, onHidden(instance) { instance.setContent('Loading...'); }, // don't consider `data-tippy-*` attributes on the reference element as we fully manage tippy with javascript // and we cannot update the reference here as it is generated by bpmn-visualization ignoreAttributes: true, // https://atomiks.github.io/tippyjs/v6/all-props/#popperoptions // modifiers: [ // { // name: 'computeStyles', // options: { // adaptive: false, // true by default // }, // }, // ], // popperOptions: { // strategy: 'fixed', // }, // https://atomiks.github.io/tippyjs/v6/all-props/#placement // https://atomiks.github.io/tippyjs/v6/all-props/#inlinepositioning // inlinePositioning: true, // https://atomiks.github.io/tippyjs/v6/all-props/#interactive interactive: true, // https://atomiks.github.io/tippyjs/v6/all-props/#movetransition // custom transition --> not needed // moveTransition: 'transform 0.2s ease-out', }); addPopover(elementsWithPopover); addPopup(elementsWithPopup); function addPopover(bpmnElements) { registerBpmnElements(bpmnElements); // Set the cursor to mark the elements as clickable bpmnVisualization.bpmnElementsRegistry.addCssClasses(bpmnElements.map(element => element.bpmnSemantic.id), 'c-hand'); const htmlElements = bpmnElements.map(elt => elt.htmlElement) tippy(htmlElements, { // sticky option behavior with this appendTo // The following is only needed to manage diagram navigation // Current issue while pan, the dimension of the popper changed while dragging which may also wrongly trigger a flip // during the pan and then, an new flip after dimensions are restored // for issue on pan, this may help: https://github.com/atomiks/tippyjs/issues/688 // Notice that we cannot have the same configuration when we trigger on mouseover/focus or on click // When trigger on click // 'reference': work with zoom (do not move the popper), but disappear on pan, mainly vertical pan (translation computation issue) // 'popper': do not move on zoom, move on pan but also change the dimension of the tooltip while pan) appendTo: bpmnContainerElt, // When trigger on click // when using this, no resize issue on pan, but no more flip nor overflow. We can however use sticky: 'reference' with is better // It is almost ok when trigger on mouse over/focus as even if there is still an overflow issue, the tooltip disappear right // after the bpmn element is no more displayed after overflow //appendTo: bpmnContainerElt.parentElement, // https://atomiks.github.io/tippyjs/v6/all-props/#sticky // This has a performance cost since checks are run on every animation frame. Use this only when necessary! // enable it //sticky: true, // only check the "reference" rect for changes sticky: 'reference', // only check the "popper" rect for changes // sticky: 'popper', duration: 400, delay: [200, 400], theme: 'light-border', trigger: 'click', }); } function addPopup(bpmnElements) { registerBpmnElements(bpmnElements); bpmnElements.forEach(bpmnElement => { const htmlElement = bpmnElement.htmlElement; const isEdge = !bpmnElement.bpmnSemantic.isShape; const offset = isEdge? [0, -40] : undefined; // undefined offset for tippyjs default offset tippy(htmlElement, { // work perfectly on hover with or without 'diagram navigation' enable appendTo: bpmnContainerElt.parentElement, // https://atomiks.github.io/tippyjs/v6/all-props/#sticky // This has a performance cost since checks are run on every animation frame. Use this only when necessary! // only check the "reference" rect for changes sticky: 'reference', arrow: false, offset: offset, placement: 'bottom', }); }); } function registerBpmnElements(bpmnElements) { bpmnElements.forEach(elt => registeredBpmnElements.set(elt.htmlElement, elt.bpmnSemantic)); } const headerKeys = ['id', 'name', 'kind', 'isShape']; function getBpmnElementInfoAsHtml(htmlElement) { const bpmnSemantic = registeredBpmnElements.get(htmlElement); // sort the non header keys in alphabetic order (following the browser locale) const secondaryKeys = Object.keys(bpmnSemantic) .filter(key => !headerKeys.includes(key)); return `
BPMN Info
${computeBpmnInfoForPopover(headerKeys, bpmnSemantic)}
${computeBpmnInfoForPopover(secondaryKeys, bpmnSemantic, true, true)}
`; } function computeBpmnInfoForPopover(keys, bpmnSemantic, sort = false, filterUndefinedValue = false) { return keys.map(key => getConvertedBpmnSemanticValue(key, bpmnSemantic)) .sort((a, b) => sort ? a.key.localeCompare(b.key) : 0) .filter(obj => filterUndefinedValue ? obj.value !== 'N/A' : true) .map(obj => `${obj.key}: ${obj.value}`) .join('
\n'); } const bpmnSemanticConversionMap = new Map([['isShape', { transformedKey: 'representation', transformFunction: (bpmnSemantic) => bpmnSemantic.isShape? 'Shape': 'Edge'}]]); function getConvertedBpmnSemanticValue(key, bpmnSemantic) { const convertedBpmnSemantic = bpmnSemanticConversionMap.get(key) || { transformedKey: key, transformFunction: (bpmnSemantic) => bpmnSemantic[key] || 'N/A'}; return {key: convertedBpmnSemantic.transformedKey, value: convertedBpmnSemantic.transformFunction(bpmnSemantic)} }