diff --git a/src/main/resources/public/css/app.css b/src/main/resources/public/css/app.css index aaf9a3e..af5e7f1 100644 --- a/src/main/resources/public/css/app.css +++ b/src/main/resources/public/css/app.css @@ -455,3 +455,24 @@ code { #tab-resize-handle:hover { background-color: #666; } + +.pulsate-tasks .btn-group:has(.overlay-button) { + animation: 3s linear 1s infinite pulsate; +} + +@keyframes pulsate { + 25% { + transform: scale(1); + filter: drop-shadow(0 0 0 #000000aa); + } + + 50% { + transform: scale(1.2); + filter: drop-shadow(3px 3px 1px #000000aa); + } + + 75% { + transform: scale(1); + filter: drop-shadow(0 0 0 #000000aa); + } +} diff --git a/src/main/resources/public/js/view-process-instance.js b/src/main/resources/public/js/view-process-instance.js index 949074f..7ff9922 100644 --- a/src/main/resources/public/js/view-process-instance.js +++ b/src/main/resources/public/js/view-process-instance.js @@ -99,6 +99,8 @@ function loadProcessInstanceView() { // wait until BPMN is loaded loadProcessInstanceDetailsViews(); loadElementInfoOfProcessInstance(); + + pulsateTasks(); }); bpmnViewIsLoaded = true; @@ -110,6 +112,34 @@ function loadProcessInstanceView() { } } +function pulsateTasks() { + if ( + !history.filter(({ action }) => + [ + "completeJob", + "timeTravel", + "publishMessage", + "failJob", + "throwJob", + ].includes(action) + ).length + ) { + // if we don't have any completed jobs in the instance history + // we add a class to the canvas that makes the overlay buttons pulsate + const canvas = document.querySelector("#canvas"); + if (!canvas) return; + + canvas.classList.add("pulsate-tasks"); + + canvas.addEventListener("click", (evt) => { + // stop pulsating when the user clicks a button group with the overlay button + if (evt.target.closest(".btn-group")?.querySelector(".overlay-button")) { + canvas.classList.remove("pulsate-tasks"); + } + }); + } +} + function loadProcessInstanceDetailsViews() { loadVariablesOfProcessInstance(); loadParentInstanceOfProcessInstance();