Introduction
The loading indicator in Spotfire is not so obvious sometimes. A customer came to me with this request and I was happy to create a function to solve this use case. The progress indicator can be seen at the bottom right of Spotfire
Before |
After |
How it works
The JavaScript code on the text area monitors the spinner loader icon from the bottom right of the screen. When the element attribute changes from hidden to visible, a JavaScript function kicks in to show another custom div that is overlaid on top the analysis. This layer can be dismissed by clicking on it. Sometimes process in the background can run while performing other tasks, but at least we have a nice big notification indicating that something is going on. We can place additional information on our div to indicate that the message can be dismissed by clicking on it or when the loading time finishes. The loading indicator will not show again until the current process ends and another separate process runs again.
How to Use
Create a text area and add the following HTML
<div id="javascriptLoader"> <h1>Loading</h1> </div>
Insert a new JavaScript Progress Indicator file with the following code
JavaScript_Loader_V1.0.js
customLoaderId = "javascriptLoader"; target = document.querySelector("[class^='sf-svg-loader']"); loader = document.getElementById(customLoaderId); loader.hidden = true; // Create a new div with class 'box' const boxDiv = document.createElement('div'); boxDiv.className = 'box'; // Move all child nodes of loader to the new div while (loader.firstChild) { boxDiv.appendChild(loader.firstChild); } // Append the new div back to the loader loader.appendChild(boxDiv); trigger = function(x) { loader.hidden = target.style.display == "none"; console.log("loader is now " + (loader.hidden ? "hidden" : "visible")); }; mutationObserver = new MutationObserver(trigger); mutationObserver.observe(target, { attributes: true }); css = ` <style> /* Absolute Center Loader */ #${customLoaderId} { position: fixed; z-index: 999; height: 2em; width: 2em; overflow: visible; margin: auto; top: 0; left: 0; bottom: 0; right: 0; } /* Transparent Overlay */ #${customLoaderId}:before { content: ''; display: block; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.3); } /*default theme*/ #${customLoaderId} .box{ color: gray; background: whitesmoke; text-align: center; transform: translate(-50%,-50%); position: absolute; cursor: pointer; width: 333px; } </style>`; loader.insertAdjacentHTML('beforeend', css);
That's it!
Custom Themes
The default look and feel is simple and sober, but you can get crazy by customizing the look and feel of the loader. To use a theme, just add the theme after the JavaScript_Loader_V1.0.js file
Here are some custom theme examples:
Dark Theme
javascriptLoader_v1.0_dark.css
javascriptLoader = "javascriptLoader" css=` <style> #javascriptLoader .box { color: white; background: #00000090; text-align: center; border: 1px solid #fffbfb; transform: translate(-50%,-50%); position: absolute; } </style>` customLoader = document.getElementById(javascriptLoader) customLoader.insertAdjacentHTML( 'beforeend', css );
Circled Theme
javascriptLoader_v1.0_circlered.css
let javascriptLoader = "javascriptLoader"; let css = ` <style> #${javascriptLoader} .box { color: gray; background: whitesmoke; text-align: center; position: absolute; cursor: pointer; border: 2px solid gray; border-radius: 50%; height: 120px; width: 120px; display: flex; justify-content: center; align-items: center; } </style> `; let customLoader = document.getElementById(javascriptLoader); customLoader.insertAdjacentHTML('beforeend', css);
Insert Theme
Insert Theme
javascriptLoader_v1.0_InsEarth.css
javascriptLoader_v1.0_InsEarth.css
javascriptLoader = "javascriptLoader" css=` <style> #javascriptLoader .box{ color:black; background:whitesmoke; text-align:center; border:0px solid black; box-shadow: inset -1px 5px 12px 0px; transform: translate(-50%,-50%); position: absolute; width:333px; } </style>` customLoader = document.getElementById(javascriptLoader) customLoader.insertAdjacentHTML( 'beforeend', css );
Inserted Circle
Blurred Rings Theme
javascriptLoader_v1.0_SabbleRings.css
javascriptLoader = "javascriptLoader" contents = document.querySelector(`#${javascriptLoader} .box`) html=` <main> <div class="loading_container"> <div class="loading_text"><div class="glow"> ${contents.innerHTML} </div></div> <article> <div class="bar_1"></div> <div class="bar_2"></div> <div class="bar_3"></div> <div class="bar_4"></div> <article> </div> </main> <style> main { font-family: 'Roboto', sans-serif; } /* === Below, the circles main color. if you want different colors for each circle, just replace to 'var(--main-color-circle)' in box-shadow of .bar_1, .bar_2, .bar_3 and .bar_4 to the color you want. === */ :root { --main-color-circle: #555; } .loading_container { height:30vw; } /* === How much blur you want on the circles === */ .loading_container article { width: 100%; height: 100%; filter: blur(1.2vw); } .loading_text { width: 100%; height: 10%; position: absolute; bottom: 50%; } .title { width:100%; position:absolute; top:5vw; text-align:center; color:#aff; font-weight:100; font-size:6vw; } @keyframes center_rotate { 0% { transform:rotateY(0deg); } 100% { transform:rotateY(1080deg); } } .loading_container article div { width:80%; height:80%; position:absolute; right: 10%; left:10%; bottom:10%; top:10%; border-radius:100%; } .bar_1 { box-shadow:inset 0 0 0 2vw #0ff; animation:bar_1_trans 6s ease-in-out infinite; } .bar_2 { box-shadow:inset 0 0 0 2vw #55f; animation:bar_2_trans 6s ease-in-out infinite; } .bar_3 { box-shadow:inset 0 0 0 2vw #a5f; animation:bar_3_trans 6s ease-in-out infinite; } .bar_4 { box-shadow:inset 0 0 0 2vw #aaf; animation:bar_4_trans 6s ease-in-out infinite; } /* === The one in front === */ @keyframes bar_1_trans { 0% { transform:skewY(0deg) rotateY(0deg); } 30% { transform:skewY(20deg) rotateY(-180deg); } 80% { transform:skewY(0deg) rotateY(360deg); } 100% { transform:skewY(0deg) rotateY(360deg); } } @keyframes bar_2_trans { 0% { transform:skewY(0deg) rotateY(0deg); } 40% { transform:skewY(-10deg) rotateY(-180deg); } 70% { transform:skewY(10deg) rotateY(400deg); } 100% { transform:skewY(0deg) rotateY(360deg); } } @keyframes bar_3_trans { 0% { transform:skewX(0deg) rotateY(0deg); } 25% { transform:skewX(-10deg) rotateY(-90deg); } 65% { transform:skewX(10deg) rotateY(360deg); } 100% { transform:skewX(0deg) rotateY(360deg); } } @keyframes bar_4_trans { 0% { transform:skewX(0deg) rotateY(0deg); } 20% { transform:skewX(20deg) rotateY(-90deg); } 70% { transform:skewX(-10deg) rotateY(330deg); } 100% { transform:skewX(0deg) rotateY(360deg); } } .back_buttons { position:absolute; right:0; left:61.8%; padding-right:3%; padding-left:3%; bottom:0; background:#ffffff05; } .back_buttons h3, .back_buttons button p { color:#555; font-weight:400; font-family: 'Roboto', sans-serif; font-size:19px; margin: 8px 0; text-align:center; } .back_buttons button { width:50%; margin:1vw auto; background:#fffa; border:none; display:table; cursor:pointer; } .back_buttons button:focus { outline:none; } .black_back:hover { background:#111; } .black_back:hover p { font-weight:400; } .white_back:hover { background:#eee; } .white_back:hover p { color:black; font-weight:400; } .developed { position:absolute; bottom:5%; left:5%; right:50%; color:#ddd; font-weight:100; } .glow { color: #fff; text-shadow: 0 0 2px #fff, 0 0 10px #fff, 0 0 20px #0ba9ca, 0 0 30px #0ba9ca, 0 0 40px #0ba9ca, 0 0 50px #0ba9ca; animation: blink 1.5s infinite alternate; } @keyframes blink { 100% { text-shadow: 0 0 2px #fff, 0 0 10px #fff, 0 0 20px #fff, 0 0 40px #0ba9ca, 0 0 70px #0ba9ca, 0 0 80px #0ba9ca; } } </style> ` contents.innerHTML = html javascriptLoader = "javascriptLoader" css=` <style> #${javascriptLoader} .box{ xcolor: blue; background: unset; text-align: center; } </style>` customLoader = document.getElementById(javascriptLoader) customLoader.insertAdjacentHTML( 'beforeend', css );
Back to the JavaScript Component Hub for Spotfire
Disclaimer
This method makes assumptions about the inner workings of Spotfire version 12.0.0 LTS Any analyses created using this hack may cause instability and performance issues and are likely to break when Spotfire is upgraded or hotfixed in the future. Any issues resulting from applying this hack are not covered by Spotfire maintenance agreements and Support will not assist in troubleshooting problems with analysis files where this approach is used.
Recommended Comments